Changeset 472


Ignore:
Timestamp:
Apr 9, 2015, 9:58:36 PM (9 years ago)
Author:
chronos
Message:
  • Fixed: Use csOpaque control style also to Image, PaintBox and OpenGLControl.
  • Modified: Change size of test frame with SpinEdits as delayed using timer.
  • Updated: BRGABitmap package to version 8.1.
Location:
GraphicTest
Files:
36 added
1 deleted
60 edited

Legend:

Unmodified
Added
Removed
  • GraphicTest/GraphicTest.lpi

    r470 r472  
    128128        <Filename Value="Methods/ULazIntfImageColorsCopy.pas"/>
    129129        <IsPartOfProject Value="True"/>
     130        <UnitName Value="ULazIntfImageColorsCopy"/>
    130131      </Unit8>
    131132      <Unit9>
     
    163164        <Filename Value="Methods/UOpenGLPBOMethod.pas"/>
    164165        <IsPartOfProject Value="True"/>
     166        <UnitName Value="UOpenGLPBOMethod"/>
    165167      </Unit16>
    166168      <Unit17>
     
    201203    </CodeGeneration>
    202204    <Linking>
    203       <Debugging>
    204         <UseExternalDbgSyms Value="True"/>
    205       </Debugging>
    206205      <Options>
    207206        <Win32>
  • GraphicTest/Packages/bgrabitmap/bgraanimatedgif.pas

    r452 r472  
    3434    FTimeAccumulator: double;
    3535    FCurrentImage, FWantedImage: integer;
     36    FFullAnimationTime: double;
    3637    FPreviousDisposeMode: TDisposeMode;
    3738
     
    4142
    4243    function GetCount: integer;
     44    function GetTimeUntilNextImage: integer;
    4345    procedure Render(StretchWidth, StretchHeight: integer);
    4446    procedure UpdateSimple(Canvas: TCanvas; ARect: TRect;
     
    7072    BackgroundMode: TGifBackgroundMode;
    7173
    72     constructor Create(filename: string);
     74    constructor Create(filenameUTF8: string);
    7375    constructor Create(stream: TStream);
    7476    constructor Create; override;
     
    7880    procedure LoadFromStream(Stream: TStream); override;
    7981    procedure SaveToStream(Stream: TStream); override;
     82    procedure LoadFromFile(const AFilenameUTF8: string); override;
     83    procedure SaveToFile(const AFilenameUTF8: string); override;
    8084    class function GetFileExtensions: string; override;
    8185
     
    97101    property MemBitmap: TBGRABitmap Read GetMemBitmap;
    98102    property CurrentImage: integer Read FCurrentImage Write SetCurrentImage;
     103    property TimeUntilNextImageMs: integer read GetTimeUntilNextImage;
    99104  end;
    100105
     
    114119implementation
    115120
    116 uses BGRABlend;
     121uses BGRABlend, lazutf8classes;
    117122
    118123const
     124  {$IFDEF ENDIAN_LITTLE}
    119125  AlphaMask = $FF000000;
     126  {$ELSE}
     127  AlphaMask = $000000FF;
     128  {$ENDIF}
    120129
    121130type
     
    188197    if not FPaused then
    189198      FTimeAccumulator += (curDate - FPrevDate) * 24 * 60 * 60 * 1000;
     199    if FFullAnimationTime > 0 then FTimeAccumulator:= frac(FTimeAccumulator/FFullAnimationTime)*FFullAnimationTime;
    190200    nextImage := FCurrentImage;
    191201    while FTimeAccumulator > FImages[nextImage].Delay do
     
    279289end;
    280290
    281 constructor TBGRAAnimatedGif.Create(filename: string);
     291function TBGRAAnimatedGif.GetTimeUntilNextImage: integer;
    282292var
    283   Stream: TFileStream;
     293  acc: double;
     294begin
     295  if Count <= 1 then result := 60*1000 else
     296  if (FWantedImage <> -1) or (FCurrentImage = -1) then
     297    result := 0
     298  else
     299  begin
     300    acc := FTimeAccumulator;
     301    if not FPaused then acc += (Now- FPrevDate) * 24 * 60 * 60 * 1000;
     302    if acc >= FImages[FCurrentImage].Delay then
     303      result := 0
     304    else
     305      result := round(FImages[FCurrentImage].Delay-FTimeAccumulator);
     306  end;
     307end;
     308
     309constructor TBGRAAnimatedGif.Create(filenameUTF8: string);
     310var
     311  Stream: TFileStreamUTF8;
    284312begin
    285313  inherited Create;
    286314  Init;
    287   Stream := TFileStream.Create(filename, fmOpenRead);
     315  Stream := TFileStreamUTF8.Create(filenameUTF8, fmOpenRead or fmShareDenyWrite);
    288316  LoadFromStream(Stream);
    289317  Stream.Free;
     
    355383end;
    356384
     385procedure TBGRAAnimatedGif.LoadFromFile(const AFilenameUTF8: string);
     386var stream: TFileStreamUTF8;
     387begin
     388  stream := TFileStreamUTF8.Create(AFilenameUTF8,fmOpenRead or fmShareDenyWrite);
     389  try
     390    LoadFromStream(Stream);
     391  finally
     392    Stream.Free;
     393  end;
     394end;
     395
     396procedure TBGRAAnimatedGif.SaveToFile(const AFilenameUTF8: string);
     397var
     398  Stream: TFileStreamUTF8;
     399begin
     400  Stream := TFileStreamUTF8.Create(AFilenameUTF8, fmCreate);
     401  try
     402    SaveToStream(Stream);
     403  finally
     404    Stream.Free;
     405  end;
     406end;
     407
    357408{$HINTS OFF}
    358409procedure TBGRAAnimatedGif.LoadImages(stream: TStream);
     
    458509    stridx:   longint;
    459510    bitbuf, bitsinbuf: longint;
    460     bytbuf:   array[0..255] of byte;
     511    bytbuf:   packed array[0..255] of byte;
    461512    bytinbuf, bytbufidx: byte;
    462513    endofsrc: boolean;
     
    684735  begin
    685736    stream.Read(GIFImageDescriptor, sizeof(GIFImageDescriptor));
     737    GIFImageDescriptor.Width := LEtoN(GIFImageDescriptor.Width);
     738    GIFImageDescriptor.Height := LEtoN(GIFImageDescriptor.Height);
     739    GIFImageDescriptor.x := LEtoN(GIFImageDescriptor.x);
     740    GIFImageDescriptor.y := LEtoN(GIFImageDescriptor.y);
    686741    if (GIFImageDescriptor.flags and GIFImageDescriptor_LocalColorTableFlag =
    687742      GIFImageDescriptor_LocalColorTableFlag) then
     
    724779    Clear;
    725780    SetLength(FImages, NbImages);
     781    FFullAnimationTime:= 0;
    726782    for i := 0 to Count - 1 do
     783    begin
    727784      FImages[i] := NewImages[i];
     785      FFullAnimationTime += NewImages[i].Delay;
     786    end;
    728787  end;
    729788
     
    746805          mincount := sizeof(GIFGraphicControlExtension);
    747806          stream.Read(GIFGraphicControlExtension, mincount);
     807          GIFGraphicControlExtension.delaytime := LEtoN(GIFGraphicControlExtension.delaytime);
    748808
    749809          if GIFGraphicControlExtension.flags and
     
    781841  begin
    782842    stream.Read(GIFScreenDescriptor, sizeof(GIFScreenDescriptor));
     843    GIFScreenDescriptor.Width := LEtoN(GIFScreenDescriptor.Width);
     844    GIFScreenDescriptor.Height := LEtoN(GIFScreenDescriptor.Height);
    783845    FWidth  := GIFScreenDescriptor.Width;
    784846    FHeight := GIFScreenDescriptor.Height;
  • GraphicTest/Packages/bgrabitmap/bgrabitmap.pas

    r452 r472  
    113113implementation
    114114
    115 uses GraphType, BGRABitmapTypes;
     115uses GraphType, BGRABitmapTypes, BGRAReadBMP, BGRAReadGif,
     116  BGRAReadIco, bgrareadjpeg, BGRAReadLzp, BGRAReadPCX,
     117  BGRAReadPng, BGRAReadPSD, BGRAReadTGA, BGRAReadXPM,
     118  BGRAWriteLzp;
    116119
    117120var
  • GraphicTest/Packages/bgrabitmap/bgrabitmappack.lpk

    r452 r472  
    1717        </SyntaxOptions>
    1818      </Parsing>
     19      <CodeGeneration>
     20        <Optimizations>
     21          <VariablesInRegisters Value="True"/>
     22          <OptimizationLevel Value="3"/>
     23        </Optimizations>
     24      </CodeGeneration>
    1925      <Linking>
    2026        <Debugging>
    21           <DebugInfoType Value="dsStabs"/>
     27          <GenerateDebugInfo Value="False"/>
    2228        </Debugging>
    2329      </Linking>
     
    3137    <Description Value="Drawing routines with alpha blending and antialiasing"/>
    3238    <License Value="modified LGPL"/>
    33     <Version Major="6" Minor="2"/>
    34     <Files Count="56">
     39    <Version Major="8" Minor="1"/>
     40    <Files Count="91">
    3541      <Item1>
    3642        <Filename Value="bgraanimatedgif.pas"/>
     
    5056      </Item4>
    5157      <Item5>
     58        <Filename Value="bgracanvas.pas"/>
     59        <UnitName Value="BGRACanvas"/>
     60      </Item5>
     61      <Item6>
     62        <Filename Value="bgracanvas2d.pas"/>
     63        <UnitName Value="BGRACanvas2D"/>
     64      </Item6>
     65      <Item7>
     66        <Filename Value="bgracolorint.pas"/>
     67        <UnitName Value="BGRAColorInt"/>
     68      </Item7>
     69      <Item8>
    5270        <Filename Value="bgracompressablebitmap.pas"/>
    5371        <UnitName Value="BGRACompressableBitmap"/>
    54       </Item5>
    55       <Item6>
     72      </Item8>
     73      <Item9>
     74        <Filename Value="bgracoordpool3d.pas"/>
     75        <UnitName Value="BGRACoordPool3D"/>
     76      </Item9>
     77      <Item10>
    5678        <Filename Value="bgradefaultbitmap.pas"/>
    5779        <UnitName Value="BGRADefaultBitmap"/>
    58       </Item6>
    59       <Item7>
     80      </Item10>
     81      <Item11>
    6082        <Filename Value="bgradnetdeserial.pas"/>
    6183        <UnitName Value="BGRADNetDeserial"/>
    62       </Item7>
    63       <Item8>
     84      </Item11>
     85      <Item12>
     86        <Filename Value="bgrafillinfo.pas"/>
     87        <UnitName Value="BGRAFillInfo"/>
     88      </Item12>
     89      <Item13>
    6490        <Filename Value="bgrafilters.pas"/>
    6591        <UnitName Value="BGRAFilters"/>
    66       </Item8>
    67       <Item9>
     92      </Item13>
     93      <Item14>
     94        <Filename Value="bgrafreetype.pas"/>
     95        <UnitName Value="BGRAFreeType"/>
     96      </Item14>
     97      <Item15>
     98        <Filename Value="bgragradients.pas"/>
     99        <UnitName Value="BGRAGradients"/>
     100      </Item15>
     101      <Item16>
     102        <Filename Value="bgragradientscanner.pas"/>
     103        <UnitName Value="BGRAGradientScanner"/>
     104      </Item16>
     105      <Item17>
     106        <Filename Value="bgralayers.pas"/>
     107        <UnitName Value="BGRALayers"/>
     108      </Item17>
     109      <Item18>
     110        <Filename Value="bgramatrix3d.pas"/>
     111        <UnitName Value="BGRAMatrix3D"/>
     112      </Item18>
     113      <Item19>
     114        <Filename Value="bgraopenraster.pas"/>
     115        <UnitName Value="BGRAOpenRaster"/>
     116      </Item19>
     117      <Item20>
    68118        <Filename Value="bgrapaintnet.pas"/>
    69119        <UnitName Value="BGRAPaintNet"/>
    70       </Item9>
    71       <Item10>
     120      </Item20>
     121      <Item21>
     122        <Filename Value="bgrapath.pas"/>
     123        <UnitName Value="BGRAPath"/>
     124      </Item21>
     125      <Item22>
     126        <Filename Value="bgrapen.pas"/>
     127        <UnitName Value="BGRAPen"/>
     128      </Item22>
     129      <Item23>
     130        <Filename Value="bgraphongtypes.pas"/>
     131        <UnitName Value="BGRAPhongTypes"/>
     132      </Item23>
     133      <Item24>
    72134        <Filename Value="bgrapolygon.pas"/>
    73135        <UnitName Value="BGRAPolygon"/>
    74       </Item10>
    75       <Item11>
     136      </Item24>
     137      <Item25>
     138        <Filename Value="bgrapolygonaliased.pas"/>
     139        <UnitName Value="BGRAPolygonAliased"/>
     140      </Item25>
     141      <Item26>
    76142        <Filename Value="bgraresample.pas"/>
    77143        <UnitName Value="BGRAResample"/>
    78       </Item11>
    79       <Item12>
    80         <Filename Value="bgrapen.pas"/>
    81         <UnitName Value="BGRAPen"/>
    82       </Item12>
    83       <Item13>
     144      </Item26>
     145      <Item27>
     146        <Filename Value="bgrascene3d.pas"/>
     147        <UnitName Value="BGRAScene3D"/>
     148      </Item27>
     149      <Item28>
     150        <Filename Value="bgrascene3dinterface.inc"/>
     151        <Type Value="Binary"/>
     152      </Item28>
     153      <Item29>
     154        <Filename Value="bgraslicescaling.pas"/>
     155        <UnitName Value="BGRASliceScaling"/>
     156      </Item29>
     157      <Item30>
     158        <Filename Value="bgrasse.pas"/>
     159        <UnitName Value="BGRASSE"/>
     160      </Item30>
     161      <Item31>
     162        <Filename Value="bgrastreamlayers.pas"/>
     163        <UnitName Value="BGRAStreamLayers"/>
     164      </Item31>
     165      <Item32>
     166        <Filename Value="bgratext.pas"/>
     167        <UnitName Value="BGRAText"/>
     168      </Item32>
     169      <Item33>
     170        <Filename Value="bgratextfx.pas"/>
     171        <UnitName Value="BGRATextFX"/>
     172      </Item33>
     173      <Item34>
    84174        <Filename Value="bgratransform.pas"/>
    85175        <UnitName Value="BGRATransform"/>
    86       </Item13>
    87       <Item14>
    88         <Filename Value="bgragradientscanner.pas"/>
    89         <UnitName Value="BGRAGradientScanner"/>
    90       </Item14>
    91       <Item15>
    92         <Filename Value="bgratext.pas"/>
    93         <UnitName Value="BGRAText"/>
    94       </Item15>
    95       <Item16>
    96         <Filename Value="bgrapolygonaliased.pas"/>
    97         <UnitName Value="BGRAPolygonAliased"/>
    98       </Item16>
    99       <Item17>
    100         <Filename Value="blurfast.inc"/>
    101         <Type Value="Binary"/>
    102       </Item17>
    103       <Item18>
    104         <Filename Value="blurnormal.inc"/>
    105         <Type Value="Binary"/>
    106       </Item18>
    107       <Item19>
    108         <Filename Value="phongdraw.inc"/>
    109         <Type Value="Binary"/>
    110       </Item19>
    111       <Item20>
    112         <Filename Value="bgracanvas.pas"/>
    113         <UnitName Value="BGRACanvas"/>
    114       </Item20>
    115       <Item21>
    116         <Filename Value="filldensity256.inc"/>
    117         <Type Value="Binary"/>
    118       </Item21>
    119       <Item22>
    120         <Filename Value="perspectivescan.inc"/>
    121         <Type Value="Binary"/>
    122       </Item22>
    123       <Item23>
    124         <Filename Value="lineartexscan.inc"/>
    125         <Type Value="Binary"/>
    126       </Item23>
    127       <Item24>
    128         <Filename Value="bgrafillinfo.pas"/>
    129         <UnitName Value="BGRAFillInfo"/>
    130       </Item24>
    131       <Item25>
    132         <Filename Value="filldensitysegment256.inc"/>
    133         <Type Value="Binary"/>
    134       </Item25>
    135       <Item26>
    136         <Filename Value="renderdensity256.inc"/>
    137         <Type Value="Binary"/>
    138       </Item26>
    139       <Item27>
    140         <Filename Value="multishapeline.inc"/>
    141         <Type Value="Binary"/>
    142       </Item27>
    143       <Item28>
    144         <Filename Value="bgrapath.pas"/>
    145         <UnitName Value="BGRAPath"/>
    146       </Item28>
    147       <Item29>
    148         <Filename Value="bgracanvas2d.pas"/>
    149         <UnitName Value="BGRACanvas2D"/>
    150       </Item29>
    151       <Item30>
    152         <Filename Value="bgrascene3d.pas"/>
    153         <UnitName Value="BGRAScene3D"/>
    154       </Item30>
    155       <Item31>
    156         <Filename Value="bgratextfx.pas"/>
    157         <UnitName Value="BGRATextFX"/>
    158       </Item31>
    159       <Item32>
    160         <Filename Value="bgraphongtypes.pas"/>
    161         <UnitName Value="BGRAPhongTypes"/>
    162       </Item32>
    163       <Item33>
    164         <Filename Value="bgralayers.pas"/>
    165         <UnitName Value="BGRALayers"/>
    166       </Item33>
    167       <Item34>
    168         <Filename Value="bgrasse.pas"/>
    169         <UnitName Value="BGRASSE"/>
    170176      </Item34>
    171177      <Item35>
    172         <Filename Value="perspectivescan2.inc"/>
    173         <Type Value="Binary"/>
     178        <Filename Value="bgratypewriter.pas"/>
     179        <UnitName Value="BGRATypewriter"/>
    174180      </Item35>
    175181      <Item36>
    176         <Filename Value="shape3D.inc"/>
    177         <Type Value="Binary"/>
     182        <Filename Value="bgravectorize.pas"/>
     183        <UnitName Value="BGRAVectorize"/>
    178184      </Item36>
    179185      <Item37>
    180         <Filename Value="bgrascene3dinterface.inc"/>
     186        <Filename Value="blendpixelinline.inc"/>
    181187        <Type Value="Binary"/>
    182188      </Item37>
    183189      <Item38>
    184         <Filename Value="bgramatrix3d.pas"/>
    185         <UnitName Value="BGRAMatrix3D"/>
     190        <Filename Value="blendpixels.inc"/>
     191        <Type Value="Binary"/>
    186192      </Item38>
    187193      <Item39>
    188         <Filename Value="csscolorconst.inc"/>
     194        <Filename Value="blendpixelsover.inc"/>
    189195        <Type Value="Binary"/>
    190196      </Item39>
    191197      <Item40>
    192         <Filename Value="lightingclasses3d.inc"/>
     198        <Filename Value="blurfast.inc"/>
    193199        <Type Value="Binary"/>
    194200      </Item40>
    195201      <Item41>
    196         <Filename Value="phonglight.inc"/>
     202        <Filename Value="blurnormal.inc"/>
    197203        <Type Value="Binary"/>
    198204      </Item41>
    199205      <Item42>
    200         <Filename Value="polyaliaspersp.inc"/>
     206        <Filename Value="csscolorconst.inc"/>
    201207        <Type Value="Binary"/>
    202208      </Item42>
    203209      <Item43>
    204         <Filename Value="lineartexscan2.inc"/>
     210        <Filename Value="filldensity256.inc"/>
    205211        <Type Value="Binary"/>
    206212      </Item43>
    207213      <Item44>
    208         <Filename Value="perspectivecolorscan.inc"/>
     214        <Filename Value="filldensitysegment256.inc"/>
    209215        <Type Value="Binary"/>
    210216      </Item44>
    211217      <Item45>
    212         <Filename Value="phongdrawsse.inc"/>
     218        <Filename Value="lightingclasses3d.inc"/>
    213219        <Type Value="Binary"/>
    214220      </Item45>
    215221      <Item46>
    216         <Filename Value="phonglightsse.inc"/>
     222        <Filename Value="lineartexscan.inc"/>
    217223        <Type Value="Binary"/>
    218224      </Item46>
    219225      <Item47>
    220         <Filename Value="bgracoordpool3d.pas"/>
    221         <UnitName Value="BGRACoordPool3D"/>
     226        <Filename Value="lineartexscan2.inc"/>
     227        <Type Value="Binary"/>
    222228      </Item47>
    223229      <Item48>
    224         <Filename Value="bgraopenraster.pas"/>
    225         <UnitName Value="BGRAOpenRaster"/>
     230        <Filename Value="multishapeline.inc"/>
     231        <Type Value="Binary"/>
    226232      </Item48>
    227233      <Item49>
    228         <Filename Value="blendpixels.inc"/>
     234        <Filename Value="perspectivecolorscan.inc"/>
    229235        <Type Value="Binary"/>
    230236      </Item49>
    231237      <Item50>
    232         <Filename Value="blendpixelinline.inc"/>
     238        <Filename Value="perspectivescan.inc"/>
    233239        <Type Value="Binary"/>
    234240      </Item50>
    235241      <Item51>
    236         <Filename Value="blendpixelsover.inc"/>
     242        <Filename Value="perspectivescan2.inc"/>
    237243        <Type Value="Binary"/>
    238244      </Item51>
    239245      <Item52>
    240         <Filename Value="bgrafreetype.pas"/>
    241         <UnitName Value="BGRAFreeType"/>
     246        <Filename Value="phongdraw.inc"/>
     247        <Type Value="Binary"/>
    242248      </Item52>
    243249      <Item53>
    244         <Filename Value="bgragradients.pas"/>
    245         <UnitName Value="BGRAGradients"/>
     250        <Filename Value="phongdrawsse.inc"/>
     251        <Type Value="Binary"/>
    246252      </Item53>
    247253      <Item54>
    248         <Filename Value="bgraslicescaling.pas"/>
    249         <UnitName Value="BGRASliceScaling"/>
     254        <Filename Value="phonglight.inc"/>
     255        <Type Value="Binary"/>
    250256      </Item54>
    251257      <Item55>
    252         <Filename Value="bgravectorize.pas"/>
    253         <UnitName Value="BGRAVectorize"/>
     258        <Filename Value="phonglightsse.inc"/>
     259        <Type Value="Binary"/>
    254260      </Item55>
    255261      <Item56>
    256         <Filename Value="bgratypewriter.pas"/>
    257         <UnitName Value="BGRATypewriter"/>
     262        <Filename Value="polyaliaspersp.inc"/>
     263        <Type Value="Binary"/>
    258264      </Item56>
     265      <Item57>
     266        <Filename Value="renderdensity256.inc"/>
     267        <Type Value="Binary"/>
     268      </Item57>
     269      <Item58>
     270        <Filename Value="shapes3d.inc"/>
     271        <Type Value="Binary"/>
     272      </Item58>
     273      <Item59>
     274        <Filename Value="winstream.inc"/>
     275        <Type Value="Binary"/>
     276      </Item59>
     277      <Item60>
     278        <Filename Value="bgrasse.inc"/>
     279        <UnitName Value="bgrasse"/>
     280      </Item60>
     281      <Item61>
     282        <Filename Value="sseloadv.inc"/>
     283        <UnitName Value="sseloadv"/>
     284      </Item61>
     285      <Item62>
     286        <Filename Value="ssesavev.inc"/>
     287        <UnitName Value="ssesavev"/>
     288      </Item62>
     289      <Item63>
     290        <Filename Value="bgragrayscalemask.pas"/>
     291        <UnitName Value="BGRAGrayscaleMask"/>
     292      </Item63>
     293      <Item64>
     294        <Filename Value="bgrareadbmp.pas"/>
     295        <UnitName Value="BGRAReadBMP"/>
     296      </Item64>
     297      <Item65>
     298        <Filename Value="bgrareadgif.pas"/>
     299        <UnitName Value="BGRAReadGif"/>
     300      </Item65>
     301      <Item66>
     302        <Filename Value="bgrareadpcx.pas"/>
     303        <UnitName Value="BGRAReadPCX"/>
     304      </Item66>
     305      <Item67>
     306        <Filename Value="bgrareadpng.pas"/>
     307        <UnitName Value="BGRAReadPng"/>
     308      </Item67>
     309      <Item68>
     310        <Filename Value="bgrareadpsd.pas"/>
     311        <UnitName Value="BGRAReadPSD"/>
     312      </Item68>
     313      <Item69>
     314        <Filename Value="bgrathumbnail.pas"/>
     315        <UnitName Value="BGRAThumbnail"/>
     316      </Item69>
     317      <Item70>
     318        <Filename Value="bgrareadtga.pas"/>
     319        <UnitName Value="BGRAReadTGA"/>
     320      </Item70>
     321      <Item71>
     322        <Filename Value="bgrareadico.pas"/>
     323        <UnitName Value="BGRAReadIco"/>
     324      </Item71>
     325      <Item72>
     326        <Filename Value="bgrareadjpeg.pas"/>
     327        <UnitName Value="bgrareadjpeg"/>
     328      </Item72>
     329      <Item73>
     330        <Filename Value="bgrareadlzp.pas"/>
     331        <UnitName Value="BGRAReadLzp"/>
     332      </Item73>
     333      <Item74>
     334        <Filename Value="unzipperext.pas"/>
     335        <UnitName Value="UnzipperExt"/>
     336      </Item74>
     337      <Item75>
     338        <Filename Value="bgralzpcommon.pas"/>
     339        <UnitName Value="BGRALzpCommon"/>
     340      </Item75>
     341      <Item76>
     342        <Filename Value="bgrawritelzp.pas"/>
     343        <UnitName Value="BGRAWriteLzp"/>
     344      </Item76>
     345      <Item77>
     346        <Filename Value="bgrareadxpm.pas"/>
     347        <UnitName Value="BGRAReadXPM"/>
     348      </Item77>
     349      <Item78>
     350        <Filename Value="bgrasvg.pas"/>
     351        <UnitName Value="BGRASVG"/>
     352      </Item78>
     353      <Item79>
     354        <Filename Value="bgraunits.pas"/>
     355        <UnitName Value="BGRAUnits"/>
     356      </Item79>
     357      <Item80>
     358        <Filename Value="bgrasvgshapes.pas"/>
     359        <UnitName Value="BGRASVGShapes"/>
     360      </Item80>
     361      <Item81>
     362        <Filename Value="bgrasvgtype.pas"/>
     363        <UnitName Value="BGRASVGType"/>
     364      </Item81>
     365      <Item82>
     366        <Filename Value="bgrareadbmpmiomap.pas"/>
     367        <UnitName Value="BGRAReadBmpMioMap"/>
     368      </Item82>
     369      <Item83>
     370        <Filename Value="bgraarrow.pas"/>
     371        <UnitName Value="BGRAArrow"/>
     372      </Item83>
     373      <Item84>
     374        <Filename Value="vertex3d.inc"/>
     375        <Type Value="Binary"/>
     376      </Item84>
     377      <Item85>
     378        <Filename Value="face3d.inc"/>
     379        <Type Value="Binary"/>
     380      </Item85>
     381      <Item86>
     382        <Filename Value="part3d.inc"/>
     383        <Type Value="Binary"/>
     384      </Item86>
     385      <Item87>
     386        <Filename Value="object3d.inc"/>
     387        <Type Value="Binary"/>
     388      </Item87>
     389      <Item88>
     390        <Filename Value="bgrapalette.pas"/>
     391        <UnitName Value="BGRAPalette"/>
     392      </Item88>
     393      <Item89>
     394        <Filename Value="bgracolorquantization.pas"/>
     395        <UnitName Value="BGRAColorQuantization"/>
     396      </Item89>
     397      <Item90>
     398        <Filename Value="bgradithering.pas"/>
     399        <UnitName Value="BGRADithering"/>
     400      </Item90>
     401      <Item91>
     402        <Filename Value="paletteformats.inc"/>
     403        <Type Value="Binary"/>
     404      </Item91>
    259405    </Files>
    260     <Type Value="RunAndDesignTime"/>
    261406    <RequiredPkgs Count="2">
    262407      <Item1>
  • GraphicTest/Packages/bgrabitmap/bgrabitmappack.pas

    r452 r472  
    88
    99uses
    10   BGRAAnimatedGif, BGRABitmap, BGRABitmapTypes, BGRABlend,
    11   BGRACompressableBitmap, BGRADefaultBitmap, BGRADNetDeserial, BGRAFilters,
    12   BGRAPaintNet, BGRAPolygon, BGRAResample, BGRAPen, BGRATransform,
    13   BGRAGradientScanner, BGRAText, BGRAPolygonAliased, BGRACanvas, BGRAFillInfo,
    14   BGRAPath, BGRACanvas2D, BGRAScene3D, BGRATextFX, BGRAPhongTypes, BGRALayers,
    15   BGRASSE, BGRAMatrix3D, BGRACoordPool3D, BGRAOpenRaster, BGRAFreeType,
    16   BGRAGradients, BGRASliceScaling, BGRAVectorize, BGRATypewriter,
    17   LazarusPackageIntf;
     10  BGRAAnimatedGif, BGRABitmap, BGRABitmapTypes, BGRABlend, BGRACanvas,
     11  BGRACanvas2D, BGRAColorInt, BGRACompressableBitmap, BGRACoordPool3D,
     12  BGRADefaultBitmap, BGRADNetDeserial, BGRAFillInfo, BGRAFilters,
     13  BGRAFreeType, BGRAGradients, BGRAGradientScanner, BGRALayers, BGRAMatrix3D,
     14  BGRAOpenRaster, BGRAPaintNet, BGRAPath, BGRAPen, BGRAPhongTypes,
     15  BGRAPolygon, BGRAPolygonAliased, BGRAResample, BGRAScene3D,
     16  BGRASliceScaling, BGRASSE, BGRAStreamLayers, BGRAText, BGRATextFX,
     17  BGRATransform, BGRATypewriter, BGRAVectorize, BGRAGrayscaleMask,
     18  BGRAReadBMP, BGRAReadGif, BGRAReadPCX, BGRAReadPng, BGRAReadPSD,
     19  BGRAThumbnail, BGRAReadTGA, BGRAReadIco, bgrareadjpeg, BGRAReadLzp,
     20  UnzipperExt, BGRALzpCommon, BGRAWriteLzp, BGRAReadXPM, BGRASVG, BGRAUnits,
     21  BGRASVGShapes, BGRASVGType, BGRAReadBmpMioMap, BGRAArrow, BGRAPalette,
     22  BGRAColorQuantization, BGRADithering;
    1823
    1924implementation
    2025
    21 procedure Register;
    22 begin
    23 end;
    24 
    25 initialization
    26   RegisterPackage('BGRABitmapPack', @Register);
    2726end.
  • GraphicTest/Packages/bgrabitmap/bgrabitmaptypes.pas

    r452 r472  
    3636  PBGRAPixel = ^TBGRAPixel;
    3737
    38   //pixel structure
     38  Int32or64 = {$IFDEF CPU64}Int64{$ELSE}LongInt{$ENDIF};
     39  UInt32or64 = {$IFDEF CPU64}UInt64{$ELSE}LongWord{$ENDIF};
     40
     41  //Each pixel is a sequence of 4 bytes containing blue, green, red and alpha channel.
    3942  TBGRAPixel = packed record
    4043    blue, green, red, alpha: byte;
    4144  end;
     45
     46  ArrayOfTBGRAPixel = array of TBGRAPixel;
    4247
    4348  //gamma expanded values
     
    5055    hue, saturation, lightness, alpha: word;
    5156  end;
     57  TGSBAPixel = THSLAPixel;
    5258
    5359  //general purpose color variable with floating point values
     
    7076
    7177  TResampleMode = (rmSimpleStretch,   //low quality resample
    72                    rmFineResample);   //use resample filters
    73   TResampleFilter = (rfLinear,        //linear interpolation
     78                   rmFineResample);   //use resample filters and pixel-centered coordinates
     79  TResampleFilter = (rfBox,           //equivalent of stretch with high quality
     80                     rfLinear,        //linear interpolation
    7481                     rfHalfCosine,    //mix of rfLinear and rfCosine
    7582                     rfCosine,        //cosine-like interpolation
     
    7784                     rfMitchell,      //downsizing interpolation
    7885                     rfSpline,        //upsizing interpolation
     86                     rfLanczos2,      //Lanczos with radius 2
     87                     rfLanczos3,      //Lanczos with radius 3
     88                     rfLanczos4,      //Lanczos with radius 4
    7989                     rfBestQuality);  //mix of rfMitchell and rfSpline
    8090
     91  TDitheringAlgorithm = (daNearestNeighbor, daFloydSteinberg);
     92  TAlphaChannelPaletteOption = (acIgnore, acTransparentEntry, acFullChannelInPalette);
     93
     94const
     95  ResampleFilterStr : array[TResampleFilter] of string =
     96   ('Box','Linear','HalfCosine','Cosine','Bicubic','Mitchell','Spline',
     97    'Lanczos2','Lanczos3','Lanczos4','BestQuality');
     98
     99function StrToResampleFilter(str: string): TResampleFilter;
     100
     101type
     102  TBGRAImageFormat = (ifUnknown, ifJpeg, ifPng, ifGif, ifBmp, ifIco, ifPcx, ifPaintDotNet, ifLazPaint, ifOpenRaster,
     103    ifPsd, ifTarga, ifTiff, ifXwd, ifXPixMap, ifBmpMioMap);
     104
     105var
     106  DefaultBGRAImageReader: array[TBGRAImageFormat] of TFPCustomImageReaderClass;
     107  DefaultBGRAImageWriter: array[TBGRAImageFormat] of TFPCustomImageWriterClass;
     108
     109type
    81110  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.
    82116
    83117  TMedianOption = (moNone, moLowSmooth, moMediumSmooth, moHighSmooth);
    84   TRadialBlurType = (rbNormal, rbDisk, rbCorona, rbPrecise, rbFast);
    85   TSplineStyle = (ssInside, ssInsideWithEnds, ssCrossing, ssCrossingWithEnds, ssOutside, ssRoundOutside, ssVertexToSide);
     118  TRadialBlurType = (rbNormal, rbDisk, rbCorona, rbPrecise, rbFast, rbBox);
     119  TSplineStyle = (ssInside, ssInsideWithEnds, ssCrossing, ssCrossingWithEnds,
     120    ssOutside, ssRoundOutside, ssVertexToSide);
    86121 
    87   //Advanced blending modes
    88   //see : http://www.brighthub.com/multimedia/photography/articles/18301.aspx
    89   //and : http://www.pegtop.net/delphi/articles/blendmodes/ 
     122  { Advanced blending modes
     123    see : http://www.brighthub.com/multimedia/photography/articles/18301.aspx
     124    and : http://www.pegtop.net/delphi/articles/blendmodes/ }
    90125  TBlendOperation = (boLinearBlend, boTransparent,                                  //blending
    91126    boLighten, boScreen, boAdditive, boLinearAdd, boColorDodge, boDivide, boNiceGlow, boSoftLight, boHardLight, //lighting
     
    146181  end;
    147182
     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
    148191  TPoint3D = record
    149192    x,y,z: single;
    150193  end;
     194
     195  TBGRAArrowStyle = (asNone, asNormal, asCut, asTriangle, asHollowTriangle, asFlipped, asFlippedCut, asTail, asTailRepeat);
    151196
    152197  TBGRATypeWriterAlignment = (twaTopLeft, twaTop, twaTopRight,
     
    169214function BezierCurve(origin, control1, control2, destination: TPointF) : TCubicBezierCurve; overload;
    170215function BezierCurve(origin, control, destination: TPointF) : TQuadraticBezierCurve; overload;
     216function BezierCurve(origin, destination: TPointF) : TQuadraticBezierCurve; overload;
     217function ArcDef(cx, cy, rx,ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single; anticlockwise: boolean) : TArcDef;
    171218
    172219{ Useful constants }
     
    184231  clBlackOpaque = TColor($010000);
    185232
     233{$DEFINE INCLUDE_COLOR_CONST}
    186234{$i csscolorconst.inc}
    187235
     
    204252  public
    205253    constructor Create;
    206     procedure Add(Name: string; Color: TBGRAPixel);
     254    procedure Add(Name: string; const Color: TBGRAPixel);
    207255    procedure Finished;
    208256    function IndexOf(Name: string): integer;
     257    function IndexOfColor(const AColor: TBGRAPixel; AMaxDiff: Word = 0): integer;
    209258
    210259    property ByName[Name: string]: TBGRAPixel read GetByName;
     
    215264
    216265var
    217   CSSColors: TBGRAColorList;
     266  VGAColors, CSSColors: TBGRAColorList;
    218267
    219268function isEmptyPointF(pt: TPointF): boolean;
     
    236285  end;
    237286
     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
    238300  TScanAtFunction = function (X,Y: Single): TBGRAPixel of object;
    239301  TScanAtIntegerFunction = function (X,Y: Integer): TBGRAPixel of object;
    240302  TScanNextPixelFunction = function: TBGRAPixel of object;
    241303  TBGRACustomGradient = class;
     304
     305  TBGRACustomFillInfo = class;
     306  TBGRACustomFontRenderer = class;
    242307
    243308  { TBGRACustomBitmap }
     
    249314  protected
    250315     { 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;
    251332     function GetHeight: integer; virtual; abstract;
    252333     function GetWidth: integer; virtual; abstract;
     
    280361     procedure SetClipRect(const AValue: TRect); virtual; abstract;
    281362     function GetFontPixelMetric: TFontPixelMetric; virtual; abstract;
    282      function LoadAsBmp32(Str: TStream): boolean; 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;
    283366
    284367  public
    285368     Caption:   string;  //user defined caption
    286369
    287      //font style
    288      FontName:  string;
    289      FontStyle: TFontStyles;
    290      FontQuality : TBGRAFontQuality;
    291      FontOrientation: integer;
     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.
    292382
    293383     //line style
    294      LineCap:   TPenEndCap;
    295384     JoinStyle: TPenJoinStyle;
    296385     JoinMiterLimit: single;
    297386
    298387     FillMode:  TFillMode;  //winding or alternate
     388     LinearAntialiasing: boolean;
    299389
    300390     { The resample filter is used when resizing the bitmap, and
     
    310400     constructor Create(AWidth, AHeight: integer; Color: TBGRAPixel); virtual; abstract; overload;
    311401     constructor Create(AFilename: string); virtual; abstract; overload;
     402     constructor Create(AFilename: string; AIsUtf8Filename: boolean); virtual; abstract; overload;
    312403     constructor Create(AStream: TStream); virtual; abstract; overload;
    313404
     
    315406     function NewBitmap(AWidth, AHeight: integer; Color: TBGRAPixel): TBGRACustomBitmap; virtual; abstract; overload;
    316407     function NewBitmap(Filename: string): TBGRACustomBitmap; virtual; abstract; overload;
    317 
     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
    318412     procedure LoadFromFile(const filename: string); virtual;
    319      procedure LoadFromStream(Str: TStream); virtual;
    320      procedure LoadFromStream(Str: TStream; Handler: TFPCustomImageReader); virtual;
    321      procedure SaveToFile(const filename: string); virtual;
    322      procedure SaveToFile(const filename: string; Handler:TFPCustomImageWriter); 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;
    323421     procedure SaveToStreamAsPng(Str: TStream); virtual; abstract;
    324      procedure Assign(ABitmap: TBitmap); virtual; abstract; overload;
     422     procedure SaveToStreamAs(Str: TStream; AFormat: TBGRAImageFormat); virtual;
     423     procedure Assign(ARaster: TRasterImage); virtual; abstract; overload;
    325424     procedure Assign(MemBitmap: TBGRACustomBitmap); virtual; abstract; overload;
    326425     procedure Serialize(AStream: TStream); virtual; abstract;
     
    328427
    329428     {Pixel functions}
    330      procedure SetPixel(x, y: integer; c: TColor); virtual; abstract; overload;
    331      procedure XorPixel(x, y: integer; c: TBGRAPixel); virtual; abstract; overload;
    332      procedure SetPixel(x, y: integer; c: TBGRAPixel); virtual; abstract; overload;
    333      procedure DrawPixel(x, y: integer; c: TBGRAPixel); virtual; abstract; overload;
    334      procedure DrawPixel(x, y: integer; ec: TExpandedPixel); virtual; abstract; overload;
    335      procedure FastBlendPixel(x, y: integer; c: TBGRAPixel); virtual; abstract;
    336      procedure ErasePixel(x, y: integer; alpha: byte); virtual; abstract;
    337      procedure AlphaPixel(x, y: integer; alpha: byte); virtual; abstract;
    338      function GetPixel(x, y: integer): TBGRAPixel; virtual; abstract;
    339      function GetPixel(x, y: single; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel; virtual; abstract; overload;
    340      function GetPixelCycle(x, y: integer): TBGRAPixel; virtual;
     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;
    341442     function GetPixelCycle(x, y: single; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel; virtual; abstract; overload;
    342443     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;
    343446
    344447     {Line primitives}
    345      procedure SetHorizLine(x, y, x2: integer; c: TBGRAPixel); virtual; abstract;
    346      procedure XorHorizLine(x, y, x2: integer; c: TBGRAPixel); virtual; abstract;
    347      procedure DrawHorizLine(x, y, x2: integer; c: TBGRAPixel); virtual; abstract; overload;
    348      procedure DrawHorizLine(x, y, x2: integer; ec: TExpandedPixel); virtual; abstract; overload;
    349      procedure DrawHorizLine(x, y, x2: integer; texture: IBGRAScanner); virtual; abstract; overload;
    350      procedure FastBlendHorizLine(x, y, x2: integer; c: TBGRAPixel); virtual; abstract;
    351      procedure AlphaHorizLine(x, y, x2: integer; alpha: byte); virtual; abstract;
    352      procedure SetVertLine(x, y, y2: integer; c: TBGRAPixel); virtual; abstract;
    353      procedure XorVertLine(x, y, y2: integer; c: TBGRAPixel); virtual; abstract;
    354      procedure DrawVertLine(x, y, y2: integer; c: TBGRAPixel); virtual; abstract;
    355      procedure AlphaVertLine(x, y, y2: integer; alpha: byte); virtual; abstract;
    356      procedure FastBlendVertLine(x, y, y2: integer; c: TBGRAPixel); virtual; abstract;
    357      procedure DrawHorizLineDiff(x, y, x2: integer; c, compare: TBGRAPixel;
    358        maxDiff: byte); virtual; abstract;
     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;
    359464
    360465     {Shapes}
    361      procedure DrawLine(x1, y1, x2, y2: integer; c: TBGRAPixel; DrawLastPixel: boolean); virtual; abstract;
     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;
    362480     procedure DrawLineAntialias(x1, y1, x2, y2: integer; c: TBGRAPixel; DrawLastPixel: boolean); virtual; abstract; overload;
    363481     procedure DrawLineAntialias(x1, y1, x2, y2: integer; c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean); virtual; abstract; overload;
     
    368486     procedure DrawLineAntialias(x1, y1, x2, y2: single; texture: IBGRAScanner; w: single; Closed: boolean); virtual; abstract; overload;
    369487
     488     procedure DrawPolyLine(const points: array of TPoint; c: TBGRAPixel; DrawLastPixel: boolean; ADrawMode: TDrawMode=dmDrawWithTransparency);
    370489     procedure DrawPolyLineAntialias(const points: array of TPoint; c: TBGRAPixel; DrawLastPixel: boolean); virtual; overload;
    371490     procedure DrawPolyLineAntialias(const points: array of TPoint; c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean); virtual; overload;
     
    373492     procedure DrawPolyLineAntialias(const points: array of TPointF; texture: IBGRAScanner; w: single); virtual; abstract; overload;
    374493     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;
    375497     procedure DrawPolygonAntialias(const points: array of TPointF; c: TBGRAPixel; w: single); virtual; abstract; overload;
    376498     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;
    377500
    378501     procedure EraseLine(x1, y1, x2, y2: integer; alpha: byte; DrawLastPixel: boolean); virtual; abstract;
     
    380503     procedure EraseLineAntialias(x1, y1, x2, y2: single; alpha: byte; w: single); virtual; abstract; overload;
    381504     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;
    382507     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;
    383513
    384514     procedure FillTriangleLinearColor(pt1,pt2,pt3: TPointF; c1,c2,c3: TBGRAPixel); virtual; abstract; overload;
     
    394524     procedure FillQuadLinearMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); virtual; abstract; overload;
    395525     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;
    396527     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;
    397529
    398530     procedure FillPolyLinearColor(const points: array of TPointF; AColors: array of TBGRAPixel);  virtual; abstract; overload;
     
    408540     procedure ErasePoly(const points: array of TPointF; alpha: byte); virtual; abstract;
    409541     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;
    410549
    411550     procedure EllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel; w: single); virtual; abstract;
     
    427566     procedure RectangleAntialias(x, y, x2, y2: single; texture: IBGRAScanner; w: single); virtual; abstract; overload;
    428567
    429      procedure RoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer; BorderColor, FillColor: TBGRAPixel); virtual; abstract;
     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;
    430570     procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; c: TBGRAPixel; w: single; options: TRoundRectangleOptions = []); virtual; abstract;
    431571     procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; pencolor: TBGRAPixel; w: single; fillcolor: TBGRAPixel; options: TRoundRectangleOptions = []); virtual; abstract;
    432572     procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; penTexture: IBGRAScanner; w: single; fillTexture: IBGRAScanner; options: TRoundRectangleOptions = []); virtual; abstract;
    433573     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;
    434575     procedure FillRoundRectAntialias(x,y,x2,y2,rx,ry: single; c: TBGRAPixel; options: TRoundRectangleOptions = []); virtual; abstract;
    435576     procedure FillRoundRectAntialias(x,y,x2,y2,rx,ry: single; texture: IBGRAScanner; options: TRoundRectangleOptions = []); virtual; abstract;
    436577     procedure EraseRoundRectAntialias(x,y,x2,y2,rx,ry: single; alpha: byte; options: TRoundRectangleOptions = []); virtual; abstract;
    437578
     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
    438583     procedure FillRect(r: TRect; c: TColor); virtual; overload;
    439584     procedure FillRect(r: TRect; c: TBGRAPixel; mode: TDrawMode); virtual; overload;
     585     procedure FillRect(r: TRect; texture: IBGRAScanner; mode: TDrawMode); virtual; overload;
    440586     procedure FillRect(x, y, x2, y2: integer; c: TColor); virtual; overload;
    441587     procedure FillRect(x, y, x2, y2: integer; c: TBGRAPixel; mode: TDrawMode); virtual; abstract; overload;
    442      procedure FillRect(x, y, x2, y2: integer; texture: IBGRAScanner; mode: TDrawMode); virtual; abstract;
     588     procedure FillRect(x, y, x2, y2: integer; texture: IBGRAScanner; mode: TDrawMode); virtual; abstract; overload;
    443589     procedure FillRectAntialias(x, y, x2, y2: single; c: TBGRAPixel); virtual; abstract;
    444590     procedure FillRectAntialias(x, y, x2, y2: single; texture: IBGRAScanner); virtual; abstract;
     
    446592     procedure AlphaFillRect(x, y, x2, y2: integer; alpha: byte); virtual; abstract;
    447593
    448      procedure TextOut(x, y: single; s: string; c: TBGRAPixel; align: TAlignment); virtual; abstract; overload;
    449      procedure TextOut(x, y: single; s: string; texture: IBGRAScanner; align: TAlignment); virtual; abstract; overload;
    450      procedure TextOutAngle(x, y: single; orientation: integer; s: string; c: TBGRAPixel; align: TAlignment); virtual; abstract;
    451      procedure TextOutAngle(x, y: single; orientation: integer; s: string; texture: IBGRAScanner; align: TAlignment); virtual; abstract;
    452      procedure TextOut(x, y: single; s: string; c: TBGRAPixel); virtual; overload;
    453      procedure TextOut(x, y: single; s: string; c: TColor); virtual; overload;
    454      procedure TextOut(x, y: single; s: string; texture: IBGRAScanner); virtual; overload;
    455      procedure TextRect(ARect: TRect; x, y: integer; s: string; style: TTextStyle; c: TBGRAPixel); virtual; abstract; overload;
    456      procedure TextRect(ARect: TRect; x, y: integer; s: string; style: TTextStyle; texture: IBGRAScanner); virtual; abstract; overload;
    457      procedure TextRect(ARect: TRect; s: string; halign: TAlignment; valign: TTextLayout; c: TBGRAPixel); virtual; overload;
    458      procedure TextRect(ARect: TRect; s: string; halign: TAlignment; valign: TTextLayout; texture: IBGRAScanner); virtual; overload;
    459      function TextSize(s: string): TSize; virtual; abstract;
     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;
    460613
    461614     {Spline}
     
    494647     procedure AlphaFill(alpha: byte); virtual; overload;
    495648     procedure AlphaFill(alpha: byte; start, Count: integer); virtual; abstract; overload;
    496      procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; color: TBGRAPixel); virtual; abstract; overload;
    497      procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner); 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;
    498653     procedure FillClearTypeMask(x,y: integer; xThird: integer; AMask: TBGRACustomBitmap; color: TBGRAPixel; ARGBOrder: boolean = true); virtual; abstract; overload;
    499654     procedure FillClearTypeMask(x,y: integer; xThird: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner; ARGBOrder: boolean = true); virtual; abstract; overload;
     
    529684
    530685     {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;
    531688     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;
    532690     procedure PutImageSubpixel(x, y: single; Source: TBGRACustomBitmap);
    533691     procedure PutImagePart(x,y: integer; Source: TBGRACustomBitmap; SourceRect: TRect; mode: TDrawMode; AOpacity: byte = 255);
    534      procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AOpacity: Byte=255); virtual; abstract;
    535      procedure PutImageAngle(x,y: single; Source: TBGRACustomBitmap; angle: single; imageCenterX: single = 0; imageCenterY: single = 0; AOpacity: Byte=255; ARestoreOffsetAfterRotation: boolean = false); virtual; abstract;
     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;
    536704     procedure BlendImage(x, y: integer; Source: TBGRACustomBitmap; operation: TBlendOperation); virtual; abstract;
    537705     procedure BlendImageOver(x, y: integer; Source: TBGRACustomBitmap; operation: TBlendOperation; AOpacity: byte = 255;
     
    542710     function Resample(newWidth, newHeight: integer;
    543711       mode: TResampleMode = rmFineResample): TBGRACustomBitmap; virtual; abstract;
    544      procedure VerticalFlip; virtual; abstract;
    545      procedure HorizontalFlip; 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;
    546716     function RotateCW: TBGRACustomBitmap; virtual; abstract;
    547717     function RotateCCW: TBGRACustomBitmap; virtual; abstract;
    548718     procedure Negative; virtual; abstract;
     719     procedure NegativeRect(ABounds: TRect); virtual; abstract;
    549720     procedure LinearNegative; virtual; abstract;
     721     procedure LinearNegativeRect(ABounds: TRect); virtual; abstract;
     722     procedure InplaceGrayscale; virtual; abstract;
     723     procedure InplaceGrayscale(ABounds: TRect); virtual; abstract;
    550724     procedure ConvertToLinearRGB; virtual; abstract;
    551725     procedure ConvertFromLinearRGB; virtual; abstract;
     
    553727     procedure GrayscaleToAlpha; virtual; abstract;
    554728     procedure AlphaToGrayscale; virtual; abstract;
    555      procedure ApplyMask(mask: TBGRACustomBitmap); 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;
    556732     function GetImageBounds(Channel: TChannel = cAlpha; ANothingValue: Byte = 0): TRect; virtual; abstract;
    557      function GetImageBounds(Channels: TChannels): TRect; virtual; abstract;
     733     function GetImageBounds(Channels: TChannels; ANothingValue: Byte = 0): TRect; virtual; abstract;
    558734     function GetDifferenceBounds(ABitmap: TBGRACustomBitmap): TRect; virtual; abstract;
    559735     function MakeBitmapCopy(BackgroundColor: TColor): TBitmap; virtual; abstract;
     
    563739     function FilterMedian(Option: TMedianOption): TBGRACustomBitmap; virtual; abstract;
    564740     function FilterSmooth: TBGRACustomBitmap; virtual; abstract;
    565      function FilterSharpen: TBGRACustomBitmap; virtual; abstract;
     741     function FilterSharpen(Amount: single = 1): TBGRACustomBitmap; virtual; abstract;
     742     function FilterSharpen(ABounds: TRect; Amount: single = 1): TBGRACustomBitmap; virtual; abstract;
    566743     function FilterContour: TBGRACustomBitmap; virtual; abstract;
     744     function FilterPixelate(pixelSize: integer; useResample: boolean; filter: TResampleFilter = rfLinear): TBGRACustomBitmap; virtual; abstract;
    567745     function FilterBlurRadial(radius: integer;
    568746       blurType: TRadialBlurType): TBGRACustomBitmap; virtual; abstract;
    569      function FilterPixelate(pixelSize: integer; useResample: boolean; filter: TResampleFilter = rfLinear): TBGRACustomBitmap; virtual; abstract;
     747     function FilterBlurRadial(ABounds: TRect; radius: integer;
     748       blurType: TRadialBlurType): TBGRACustomBitmap; virtual; abstract;
    570749     function FilterBlurMotion(distance: integer; angle: single;
    571750       oriented: boolean): TBGRACustomBitmap; virtual; abstract;
     751     function FilterBlurMotion(ABounds: TRect; distance: integer; angle: single;
     752       oriented: boolean): TBGRACustomBitmap; virtual; abstract;
    572753     function FilterCustomBlur(mask: TBGRACustomBitmap): TBGRACustomBitmap; virtual; abstract;
     754     function FilterCustomBlur(ABounds: TRect; mask: TBGRACustomBitmap): TBGRACustomBitmap; virtual; abstract;
    573755     function FilterEmboss(angle: single): TBGRACustomBitmap; virtual; abstract;
     756     function FilterEmboss(angle: single; ABounds: TRect): TBGRACustomBitmap; virtual; abstract;
    574757     function FilterEmbossHighlight(FillSelection: boolean): TBGRACustomBitmap; virtual; abstract;
    575758     function FilterEmbossHighlight(FillSelection: boolean; BorderColor: TBGRAPixel): TBGRACustomBitmap; virtual; abstract;
    576759     function FilterEmbossHighlight(FillSelection: boolean; BorderColor: TBGRAPixel; var Offset: TPoint): TBGRACustomBitmap; virtual; abstract;
    577760     function FilterGrayscale: TBGRACustomBitmap; virtual; abstract;
     761     function FilterGrayscale(ABounds: TRect): TBGRACustomBitmap; virtual; abstract;
    578762     function FilterNormalize(eachChannel: boolean = True): TBGRACustomBitmap; virtual; abstract;
    579      function FilterRotate(origin: TPointF; angle: single): 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;
    580765     function FilterSphere: TBGRACustomBitmap; virtual; abstract;
    581766     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;
    582768     function FilterCylinder: TBGRACustomBitmap; virtual; abstract;
    583769     function FilterPlane: TBGRACustomBitmap; virtual; abstract;
    584770
    585      property Data: PBGRAPixel Read GetDataPtr;
    586      property Width: integer Read GetWidth;
    587      property Height: integer Read GetHeight;
    588      property NbPixels: integer Read GetNbPixels;
    589      property Empty: boolean Read CheckEmpty;
    590 
    591      property ScanLine[y: integer]: PBGRAPixel Read GetScanLine;
     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
    592791     property RefCount: integer Read GetRefCount;
    593792     property Bitmap: TBitmap Read GetBitmap; //don't forget to call InvalidateBitmap before if you changed something with Scanline
    594      property HasTransparentPixels: boolean Read GetHasTransparentPixels;
    595793     property AverageColor: TColor Read GetAverageColor;
    596794     property AveragePixel: TBGRAPixel Read GetAveragePixel;
    597      property LineOrder: TRawImageLineOrder Read GetLineOrder;
    598795     property CanvasFP: TFPImageCanvas read GetCanvasFP;
    599796     property CanvasDrawModeFP: TDrawMode read GetCanvasDrawModeFP write SetCanvasDrawModeFP;
     
    603800       Read GetCanvasAlphaCorrection Write SetCanvasAlphaCorrection;
    604801
    605      property FontHeight: integer Read GetFontHeight Write SetFontHeight;
    606802     property PenStyle: TPenStyle read GetPenStyle Write SetPenStyle;
    607803     property CustomPenStyle: TBGRAPenStyle read GetCustomPenStyle write SetCustomPenStyle;
    608804     property ClipRect: TRect read GetClipRect write SetClipRect;
    609      property FontAntialias: Boolean read GetFontAntialias write SetFontAntialias; //antialiasing (it's different from TFont antialiasing mode)
     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. }
    610811     property FontFullHeight: integer read GetFontFullHeight write SetFontFullHeight;
    611      property FontPixelMetric: TFontPixelMetric read GetFontPixelMetric;
    612 
    613      //interface
    614      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};
    615      function _AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    616      function _Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
     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;
    617829
    618830     //IBGRAScanner
     
    623835     procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); virtual;
    624836     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
    625844  end;
    626845
     
    637856    procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); virtual;
    638857    function IsScanPutPixelsDefined: boolean; virtual;
     858  protected
    639859    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};
    640860    function _AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
     
    653873  end;
    654874
     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
    655964type
    656965  TBGRABitmapAny = class of TBGRACustomBitmap;  //used to create instances of the same type (see NewBitmap)
     966  TBGRATextOutImproveReadabilityMode = (irMask, irNormal, irClearTypeRGB, irClearTypeBGR);
    657967
    658968var
    659969  BGRABitmapFactory : TBGRABitmapAny;
     970  BGRATextOutImproveReadabilityProc : procedure (bmp: TBGRACustomBitmap; AFont: TFont; xf,yf: single; text: string; color: TBGRAPixel; tex: IBGRAScanner; align: TAlignment; mode : TBGRATextOutImproveReadabilityMode);
     971
     972function CheckPutImageBounds(x, y, tx, ty: integer; out minxb, minyb, maxxb, maxyb, ignoreleft: integer; const cliprect: TRect): boolean; inline;
    660973
    661974{ Color functions }
    662 function GetIntensity(c: TExpandedPixel): word; inline;
    663 function SetIntensity(c: TExpandedPixel; intensity: word): TExpandedPixel;
    664 function GetLightness(c: TExpandedPixel): word; inline;
    665 function SetLightness(c: TExpandedPixel; lightness: word): TExpandedPixel;
     975function GetIntensity(const c: TExpandedPixel): word; inline;
     976function GetIntensity(c: TBGRAPixel): word; inline;
     977function SetIntensity(const c: TExpandedPixel; intensity: word): TExpandedPixel;
     978function SetIntensity(c: TBGRAPixel; intensity: word): TBGRAPixel;
     979function GetLightness(c: TBGRAPixel): word;
     980function GetLightness(const c: TExpandedPixel): word; inline;
     981function SetLightness(const c: TExpandedPixel; lightness: word): TExpandedPixel;
     982function SetLightness(c: TBGRAPixel; lightness: word): TBGRAPixel;
     983function SetLightness(const c: TExpandedPixel; lightness: word; curLightness: word): TExpandedPixel; //if you already know the current lightness of the color
    666984function ApplyLightnessFast(color: TBGRAPixel; lightness: word): TBGRAPixel; inline;
    667985function ApplyIntensityFast(color: TBGRAPixel; lightness: longword): TBGRAPixel;
    668 function CombineLightness(lightness1,lightness2: integer): integer;
     986function CombineLightness(lightness1,lightness2: Int32or64): Int32or64;
    669987function BGRAToHSLA(c: TBGRAPixel): THSLAPixel;
    670 function ExpandedToHSLA(ec: TExpandedPixel): THSLAPixel; inline;
    671 function BGRAToGSBA(c: TBGRAPixel): THSLAPixel;
    672 function HSLAToExpanded(c: THSLAPixel): TExpandedPixel;
    673 function HSLAToBGRA(c: THSLAPixel): TBGRAPixel;
     988function ExpandedToHSLA(const ec: TExpandedPixel): THSLAPixel;
     989function ExpandedToGSBA(ec: TExpandedPixel): TGSBAPixel;
     990function BGRAToGSBA(c: TBGRAPixel): TGSBAPixel;
     991function HSLAToExpanded(const c: THSLAPixel): TExpandedPixel;
     992function HSLAToBGRA(const c: THSLAPixel): TBGRAPixel;
    674993function GtoH(ghue: word): word;
    675994function HtoG(hue: word): word;
     
    677996function GetHue(ec: TExpandedPixel): word;
    678997function ColorImportance(ec: TExpandedPixel): word;
    679 function GSBAToBGRA(c: THSLAPixel): TBGRAPixel;
    680 function GSBAToHSLA(c: THSLAPixel): THSLAPixel;
     998function GSBAToBGRA(c: TGSBAPixel): TBGRAPixel;
     999function GSBAToExpanded(c: TGSBAPixel): TExpandedPixel;
     1000function GSBAToHSLA(c: TGSBAPixel): THSLAPixel;
    6811001function GammaExpansion(c: TBGRAPixel): TExpandedPixel; inline;
    682 function GammaCompression(ec: TExpandedPixel): TBGRAPixel; inline;
     1002function GammaCompression(const ec: TExpandedPixel): TBGRAPixel; inline;
    6831003function GammaCompression(red,green,blue,alpha: word): TBGRAPixel; inline;
    6841004function BGRAToGrayscale(c: TBGRAPixel): TBGRAPixel;
     
    7071027operator * (const c1: TColorF; factor: single): TColorF; inline;
    7081028function ColorF(red,green,blue,alpha: single): TColorF;
    709 function BGRAToStr(c: TBGRAPixel): string;
    710 function StrToBGRA(str: string): TBGRAPixel;
    711 function StrToBGRA(str: string; DefaultColor: TBGRAPixel): TBGRAPixel;
     1029function BGRAToStr(c: TBGRAPixel; AColorList: TBGRAColorList = nil; AMaxDiff: Word= 0): string;
     1030function StrToBGRA(str: string): TBGRAPixel; //full parse
     1031function StrToBGRA(str: string; const DefaultColor: TBGRAPixel): TBGRAPixel; //full parse with default when error or missing values
     1032function PartialStrToBGRA(str: string; const fallbackValues: TBGRAPixel; out error: boolean): TBGRAPixel; //partial parse allowed
     1033procedure TryStrToBGRA(str: string; var parsedValue: TBGRAPixel; out missingValues: boolean; out error: boolean);
    7121034
    7131035{ Get height [0..1] stored in a TBGRAPixel }
     
    7361058operator * (const pt1: TPointF; factor: single): TPointF; inline;
    7371059operator * (factor: single; const pt1: TPointF): TPointF; inline;
    738 function PtInRect(pt: TPoint; r: TRect): boolean;
     1060function PtInRect(const pt: TPoint; r: TRect): boolean; overload;
     1061function RectWithSize(left,top,width,height: integer): TRect;
    7391062function VectLen(dx,dy: single): single; overload;
    7401063function VectLen(v: TPointF): single; overload;
     
    7531076
    7541077{ Cyclic functions }
    755 function PositiveMod(value, cycle: integer): integer; inline;
     1078function PositiveMod(value, cycle: Int32or64): Int32or64; inline; overload;
    7561079
    7571080{ Sin65536 and Cos65536 are fast routines to compute sine and cosine as integer values.
     
    7611084  without applying a modulo. }
    7621085procedure PrecalcSin65536; // compute all values now
    763 function Sin65536(value: word): integer; inline;
    764 function Cos65536(value: word): integer; inline;
     1086function Sin65536(value: word): Int32or64; inline;
     1087function Cos65536(value: word): Int32or64; inline;
    7651088function ByteSqrt(value: byte): byte; inline;
    7661089
     1090function DetectFileFormat(AFilenameUTF8: string): TBGRAImageFormat;
     1091function DetectFileFormat(AStream: TStream; ASuggestedExtensionUTF8: string = ''): TBGRAImageFormat;
     1092function SuggestImageFormat(AFilenameOrExtensionUTF8: string): TBGRAImageFormat;
     1093function CreateBGRAImageReader(AFormat: TBGRAImageFormat): TFPCustomImageReader;
     1094function CreateBGRAImageWriter(AFormat: TBGRAImageFormat; AHasTransparentPixels: boolean): TFPCustomImageWriter;
     1095
    7671096implementation
    7681097
    769 uses Math, SysUtils;
     1098uses Math, SysUtils, FileUtil, lazutf8classes, LCLProc,
     1099  FPReadTiff, FPReadXwd, FPReadXPM,
     1100  FPWriteTiff, FPWriteJPEG, FPWritePNG, FPWriteBMP, FPWritePCX,
     1101  FPWriteTGA, FPWriteXPM;
     1102
     1103function StrToResampleFilter(str: string): TResampleFilter;
     1104var f: TResampleFilter;
     1105begin
     1106  result := rfLinear;
     1107  str := LowerCase(str);
     1108  for f := low(TResampleFilter) to high(TResampleFilter) do
     1109    if CompareText(str,ResampleFilterStr[f])=0 then
     1110    begin
     1111      result := f;
     1112      exit;
     1113    end;
     1114end;
    7701115
    7711116function StrToBlendOperation(str: string): TBlendOperation;
     
    9371282end;
    9381283
     1284//straight line
     1285function BezierCurve(origin, destination: TPointF): TQuadraticBezierCurve;
     1286begin
     1287  result.p1 := origin;
     1288  result.c := (origin+destination)*0.5;
     1289  result.p2 := destination;
     1290end;
     1291
     1292function ArcDef(cx, cy, rx, ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single;
     1293  anticlockwise: boolean): TArcDef;
     1294begin
     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;
     1301end;
     1302
    9391303{ Check if a PointF structure is empty or should be treated as a list separator }
    9401304function isEmptyPointF(pt: TPointF): boolean;
    9411305begin
    9421306  Result := (pt.x = EmptySingle) and (pt.y = EmptySingle);
     1307end;
     1308
     1309{ TBGRACustomFontRenderer }
     1310
     1311procedure TBGRACustomFontRenderer.CopyTextPathTo(ADest: IBGRAPath; x, y: single; s: string; align: TAlignment);
     1312begin
     1313end;
     1314
     1315{ TIntersectionInfo }
     1316
     1317procedure TIntersectionInfo.SetValues(AInterX: Single; AWinding,
     1318  ANumSegment: integer);
     1319begin
     1320  interX := AInterX;
     1321  winding := AWinding;
     1322  numSegment := ANumSegment;
    9431323end;
    9441324
     
    9911371end;
    9921372
    993 procedure TBGRAColorList.Add(Name: string; Color: TBGRAPixel);
     1373procedure TBGRAColorList.Add(Name: string; const Color: TBGRAPixel);
    9941374begin
    9951375  if FFinished then
     
    10211401end;
    10221402
     1403function TBGRAColorList.IndexOfColor(const AColor: TBGRAPixel; AMaxDiff: Word = 0): integer;
     1404var i: integer;
     1405  MinDiff,CurDiff: Word;
     1406begin
     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;
     1431end;
     1432
    10231433{ TBGRACustomBitmap }
    10241434
     
    10391449procedure TBGRACustomBitmap.LoadFromFile(const filename: string);
    10401450begin
    1041   inherited LoadFromFile(filename);
     1451  LoadFromFileUTF8(SysToUtf8(filename));
     1452end;
     1453
     1454procedure TBGRACustomBitmap.LoadFromFileUTF8(const filenameUTF8: string);
     1455var
     1456  Stream: TStream;
     1457  format: TBGRAImageFormat;
     1458  reader: TFPCustomImageReader;
     1459begin
     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;
     1473end;
     1474
     1475procedure TBGRACustomBitmap.LoadFromFileUTF8(const filenameUTF8: string;
     1476  AHandler: TFPCustomImageReader);
     1477var
     1478  Stream: TStream;
     1479begin
     1480  stream := TFileStreamUTF8.Create(filenameUTF8,fmOpenRead or fmShareDenyWrite);
     1481  try
     1482    LoadFromStream(stream, AHandler);
     1483  finally
     1484    ClearTransparentPixels;
     1485    stream.Free;
     1486  end;
    10421487end;
    10431488
    10441489procedure TBGRACustomBitmap.SaveToFile(const filename: string);
    10451490begin
    1046   inherited SaveToFile(filename);
     1491  SaveToFileUTF8(SysToUtf8(filename));
     1492end;
     1493
     1494procedure TBGRACustomBitmap.SaveToFileUTF8(const filenameUTF8: string);
     1495var
     1496  writer: TFPCustomImageWriter;
     1497  format: TBGRAImageFormat;
     1498begin
     1499  format := SuggestImageFormat(filenameUTF8);
     1500  writer := CreateBGRAImageWriter(Format, HasTransparentPixels);
     1501  try
     1502    SaveToFileUTF8(filenameUTF8, writer);
     1503  finally
     1504    writer.free;
     1505  end;
    10471506end;
    10481507
     
    10501509  Handler: TFPCustomImageWriter);
    10511510begin
    1052   inherited SaveToFile(filename, Handler);
     1511  SaveToFileUTF8(SysToUtf8(filename),Handler);
     1512end;
     1513
     1514procedure TBGRACustomBitmap.SaveToFileUTF8(const filenameUTF8: string;
     1515  Handler: TFPCustomImageWriter);
     1516var
     1517  stream: TFileStreamUTF8;
     1518begin
     1519   stream := TFileStreamUTF8.Create(filenameUTF8,fmCreate);
     1520   try
     1521     SaveToStream(stream, Handler);
     1522   finally
     1523     stream.Free;
     1524   end;
     1525end;
     1526
     1527procedure TBGRACustomBitmap.SaveToStreamAs(Str: TStream;
     1528  AFormat: TBGRAImageFormat);
     1529var handler: TFPCustomImageWriter;
     1530begin
     1531  handler := CreateBGRAImageWriter(AFormat, HasTransparentPixels);
     1532  try
     1533    SaveToStream(Str, handler)
     1534  finally
     1535    handler.Free;
     1536  end;
     1537end;
     1538
     1539procedure TBGRACustomBitmap.DrawPixel(x, y: int32or64; c: TBGRAPixel;
     1540  ADrawMode: TDrawMode);
     1541begin
     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;
     1549end;
     1550
     1551procedure TBGRACustomBitmap.LoadFromStream(Str: TStream);
     1552var
     1553  format: TBGRAImageFormat;
     1554  reader: TFPCustomImageReader;
     1555begin
     1556  format := DetectFileFormat(Str);
     1557  reader := CreateBGRAImageReader(format);
     1558  try
     1559    LoadFromStream(Str,reader);
     1560  finally
     1561    reader.Free;
     1562  end;
    10531563end;
    10541564
     
    10571567  FP drawing mode is temporarily changed to load
    10581568  bitmaps properly }
    1059 procedure TBGRACustomBitmap.LoadFromStream(Str: TStream);
    1060 var
    1061   OldDrawMode: TDrawMode;
    1062 begin
    1063   OldDrawMode := CanvasDrawModeFP;
    1064   CanvasDrawModeFP := dmSet;
    1065   try
    1066     if not LoadAsBmp32(Str) then
    1067       inherited LoadFromStream(Str);
    1068   finally
    1069     CanvasDrawModeFP := OldDrawMode;
    1070   end;
    1071 end;
    1072 
    1073 { See above }
    10741569procedure TBGRACustomBitmap.LoadFromStream(Str: TStream;
    10751570  Handler: TFPCustomImageReader);
     
    10871582
    10881583{ Look for a pixel considering the bitmap is repeated in both directions }
    1089 function TBGRACustomBitmap.GetPixelCycle(x, y: integer): TBGRAPixel;
     1584function TBGRACustomBitmap.GetPixelCycle(x, y: int32or64): TBGRAPixel;
    10901585begin
    10911586  if (Width = 0) or (Height = 0) then
     
    10931588  else
    10941589    Result := (Scanline[PositiveMod(y,Height)] + PositiveMod(x,Width))^;
     1590end;
     1591
     1592procedure TBGRACustomBitmap.DrawHorizLine(x, y, x2: int32or64;
     1593  texture: IBGRAScanner);
     1594begin
     1595  HorizLine(x,y,x2,texture,dmDrawWithTransparency);
     1596end;
     1597
     1598procedure TBGRACustomBitmap.HorizLine(x, y, x2: Int32or64; c: TBGRAPixel;
     1599  ADrawMode: TDrawMode);
     1600begin
     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;
     1608end;
     1609
     1610procedure TBGRACustomBitmap.VertLine(x, y, y2: Int32or64; c: TBGRAPixel;
     1611  ADrawMode: TDrawMode);
     1612begin
     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;
     1620end;
     1621
     1622procedure TBGRACustomBitmap.ArrowStartAsNone;
     1623begin
     1624  SetArrowStart(asNone);
     1625end;
     1626
     1627procedure TBGRACustomBitmap.ArrowStartAsClassic(AFlipped: boolean; ACut: boolean; ARelativePenWidth: single);
     1628var join: TPenJoinStyle;
     1629begin
     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;
     1645end;
     1646
     1647procedure TBGRACustomBitmap.ArrowStartAsTriangle(ABackOffset: single; ARounded: boolean; AHollow: boolean;
     1648  AHollowPenWidth: single);
     1649var join: TPenJoinStyle;
     1650begin
     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);
     1656end;
     1657
     1658procedure TBGRACustomBitmap.ArrowStartAsTail;
     1659begin
     1660  SetArrowStart(asTail);
     1661end;
     1662
     1663procedure TBGRACustomBitmap.ArrowEndAsNone;
     1664begin
     1665  SetArrowEnd(asNone);
     1666end;
     1667
     1668procedure TBGRACustomBitmap.ArrowEndAsClassic(AFlipped: boolean; ACut: boolean; ARelativePenWidth: single);
     1669var join: TPenJoinStyle;
     1670begin
     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;
     1686end;
     1687
     1688procedure TBGRACustomBitmap.ArrowEndAsTriangle(ABackOffset: single; ARounded: boolean; AHollow: boolean;
     1689  AHollowPenWidth: single);
     1690var join: TPenJoinStyle;
     1691begin
     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);
     1697end;
     1698
     1699procedure TBGRACustomBitmap.ArrowEndAsTail;
     1700begin
     1701  SetArrowEnd(asTail);
     1702end;
     1703
     1704procedure TBGRACustomBitmap.DrawPolyLine(const points: array of TPoint;
     1705  c: TBGRAPixel; DrawLastPixel: boolean; ADrawMode: TDrawMode);
     1706var i: integer;
     1707begin
     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);
    10951715end;
    10961716
     
    11021722   if length(points) = 1 then
    11031723   begin
    1104      if DrawLastPixel then DrawPixel(points[0].x,points[0].y,c);
     1724     if DrawLastPixel then DrawLineAntialias(points[0].x,points[0].y,points[0].x,points[0].y,c,true);
    11051725   end
    11061726   else
     
    11241744end;
    11251745
     1746procedure TBGRACustomBitmap.DrawPolygon(const points: array of TPoint;
     1747  c: TBGRAPixel; ADrawMode: TDrawMode);
     1748var i: integer;
     1749begin
     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;
     1760end;
     1761
     1762procedure TBGRACustomBitmap.DrawPolygonAntialias(const points: array of TPoint;
     1763  c: TBGRAPixel);
     1764var i: integer;
     1765begin
     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;
     1776end;
     1777
     1778procedure TBGRACustomBitmap.ErasePolyLine(const points: array of TPoint; alpha: byte;
     1779  DrawLastPixel: boolean);
     1780var i: integer;
     1781begin
     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));
     1789end;
     1790
     1791procedure TBGRACustomBitmap.ErasePolyLineAntialias(
     1792  const points: array of TPoint; alpha: byte; DrawLastPixel: boolean);
     1793var i: integer;
     1794begin
     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));
     1802end;
     1803
     1804procedure TBGRACustomBitmap.ErasePolygonOutline(const points: array of TPoint;
     1805  alpha: byte);
     1806var i: integer;
     1807begin
     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;
     1818end;
     1819
     1820procedure TBGRACustomBitmap.ErasePolygonOutlineAntialias(
     1821  const points: array of TPoint; alpha: byte);
     1822var i: integer;
     1823begin
     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;
     1834end;
     1835
    11261836{ Following functions are defined for convenience }
    11271837procedure TBGRACustomBitmap.Rectangle(x, y, x2, y2: integer; c: TColor);
     
    11531863end;
    11541864
     1865procedure TBGRACustomBitmap.FillRoundRect(X1, Y1, X2, Y2: integer; DX,
     1866  DY: integer; FillColor: TBGRAPixel; ADrawMode: TDrawMode);
     1867begin
     1868  RoundRect(X1,Y1,X2,Y2,DX,DY,FillColor,FillColor,ADrawMode);
     1869end;
     1870
     1871procedure TBGRACustomBitmap.EllipseInRect(r: TRect; BorderColor: TBGRAPixel;
     1872  ADrawMode: TDrawMode);
     1873begin
     1874  RoundRect(r.left,r.top,r.right,r.bottom,abs(r.right-r.left),abs(r.bottom-r.top),BorderColor,ADrawMode);
     1875end;
     1876
     1877procedure TBGRACustomBitmap.EllipseInRect(r: TRect; BorderColor,
     1878  FillColor: TBGRAPixel; ADrawMode: TDrawMode);
     1879begin
     1880  RoundRect(r.left,r.top,r.right,r.bottom,abs(r.right-r.left),abs(r.bottom-r.top),BorderColor,FillColor,ADrawMode);
     1881end;
     1882
     1883procedure TBGRACustomBitmap.FillEllipseInRect(r: TRect; FillColor: TBGRAPixel;
     1884  ADrawMode: TDrawMode);
     1885begin
     1886  FillRoundRect(r.left,r.top,r.right,r.bottom,abs(r.right-r.left),abs(r.bottom-r.top),FillColor,ADrawMode);
     1887end;
     1888
    11551889procedure TBGRACustomBitmap.FillRect(r: TRect; c: TColor);
    11561890begin
     
    11631897end;
    11641898
     1899procedure TBGRACustomBitmap.FillRect(r: TRect; texture: IBGRAScanner;
     1900  mode: TDrawMode);
     1901begin
     1902  FillRect(r.Left, r.top, r.right, r.bottom, texture, mode);
     1903end;
     1904
    11651905procedure TBGRACustomBitmap.FillRect(x, y, x2, y2: integer; c: TColor);
    11661906begin
     
    11681908end;
    11691909
    1170 procedure TBGRACustomBitmap.TextOut(x, y: single; s: string; c: TBGRAPixel);
    1171 begin
    1172   TextOut(x, y, s, c, taLeftJustify);
    1173 end;
    1174 
    1175 procedure TBGRACustomBitmap.TextOut(x, y: single; s: string; c: TColor);
    1176 begin
    1177   TextOut(x, y, s, ColorToBGRA(c));
    1178 end;
    1179 
    1180 procedure TBGRACustomBitmap.TextOut(x, y: single; s: string;
     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. }
     1912procedure TBGRACustomBitmap.TextOut(x, y: single; sUTF8: string; c: TBGRAPixel);
     1913begin
     1914  TextOut(x, y, sUTF8, c, taLeftJustify);
     1915end;
     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. }
     1919procedure TBGRACustomBitmap.TextOut(x, y: single; sUTF8: string; c: TColor);
     1920begin
     1921  TextOut(x, y, sUTF8, ColorToBGRA(c));
     1922end;
     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. }
     1926procedure TBGRACustomBitmap.TextOut(x, y: single; sUTF8: string;
    11811927  texture: IBGRAScanner);
    11821928begin
    1183   TextOut(x, y, s, texture, taLeftJustify);
    1184 end;
    1185 
    1186 procedure TBGRACustomBitmap.TextRect(ARect: TRect; s: string;
     1929  TextOut(x, y, sUTF8, texture, taLeftJustify);
     1930end;
     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. }
     1935procedure TBGRACustomBitmap.TextRect(ARect: TRect; sUTF8: string;
    11871936  halign: TAlignment; valign: TTextLayout; c: TBGRAPixel);
    11881937var
     
    11971946  style.ShowPrefix := false;
    11981947  style.Clipping := false;
    1199   TextRect(ARect,ARect.Left,ARect.Top,s,style,c);
    1200 end;
    1201 
    1202 procedure TBGRACustomBitmap.TextRect(ARect: TRect; s: string;
     1948  TextRect(ARect,ARect.Left,ARect.Top,sUTF8,style,c);
     1949end;
     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. }
     1954procedure TBGRACustomBitmap.TextRect(ARect: TRect; sUTF8: string;
    12031955  halign: TAlignment; valign: TTextLayout; texture: IBGRAScanner);
    12041956var
     
    12131965  style.ShowPrefix := false;
    12141966  style.Clipping := false;
    1215   TextRect(ARect,ARect.Left,ARect.Top,s,style,texture);
     1967  TextRect(ARect,ARect.Left,ARect.Top,sUTF8,style,texture);
    12161968end;
    12171969
     
    12451997begin
    12461998  AlphaFill(alpha, 0, NbPixels);
     1999end;
     2000
     2001procedure TBGRACustomBitmap.FillMask(x, y: integer; AMask: TBGRACustomBitmap;
     2002  color: TBGRAPixel);
     2003begin
     2004  FillMask(x,y, AMask, color, dmDrawWithTransparency);
     2005end;
     2006
     2007procedure TBGRACustomBitmap.FillMask(x, y: integer; AMask: TBGRACustomBitmap;
     2008  texture: IBGRAScanner);
     2009begin
     2010  FillMask(x,y, AMask, texture, dmDrawWithTransparency);
    12472011end;
    12482012
     
    12762040    oldClip,newClip: TRect;
    12772041begin
    1278   if Source = nil then exit;
     2042  if (Source = nil) or (AOpacity = 0) then exit;
    12792043  w := SourceRect.Right-SourceRect.Left;
    12802044  h := SourceRect.Bottom-SourceRect.Top;
     
    13042068
    13052069  ClipRect := oldClip;
     2070end;
     2071
     2072procedure TBGRACustomBitmap.PutImageAffine(Origin, HAxis, VAxis: TPointF;
     2073  Source: TBGRACustomBitmap; AOpacity: Byte; ACorrectBlur: Boolean);
     2074begin
     2075  if ACorrectBlur then
     2076    PutImageAffine(Origin,HAxis,VAxis,Source,rfCosine,AOpacity)
     2077  else
     2078    PutImageAffine(Origin,HAxis,VAxis,Source,rfLinear,AOpacity);
     2079end;
     2080
     2081procedure TBGRACustomBitmap.PutImageAffine(Origin, HAxis, VAxis: TPointF;
     2082  Source: TBGRACustomBitmap; AResampleFilter: TResampleFilter; AOpacity: Byte);
     2083var outputBounds: TRect;
     2084begin
     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);
     2095end;
     2096
     2097procedure TBGRACustomBitmap.PutImageAffine(Origin, HAxis, VAxis: TPointF;
     2098  Source: TBGRACustomBitmap; AOutputBounds: TRect; AOpacity: Byte;
     2099  ACorrectBlur: Boolean);
     2100begin
     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);
     2105end;
     2106
     2107{ Returns the area that contains the affine transformed image }
     2108function TBGRACustomBitmap.GetImageAffineBounds(Origin, HAxis, VAxis: TPointF;
     2109  Source: TBGRACustomBitmap): TRect;
     2110var 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
     2123begin
     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);
     2153end;
     2154
     2155procedure TBGRACustomBitmap.PutImageAngle(x, y: single;
     2156  Source: TBGRACustomBitmap; angle: single; AOutputBounds: TRect;
     2157  imageCenterX: single; imageCenterY: single; AOpacity: Byte;
     2158  ARestoreOffsetAfterRotation: boolean; ACorrectBlur: Boolean);
     2159begin
     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);
     2164end;
     2165
     2166procedure TBGRACustomBitmap.PutImageAngle(x, y: single;
     2167  Source: TBGRACustomBitmap; angle: single; imageCenterX: single;
     2168  imageCenterY: single; AOpacity: Byte; ARestoreOffsetAfterRotation: boolean; ACorrectBlur: Boolean);
     2169begin
     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);
     2174end;
     2175
     2176procedure TBGRACustomBitmap.PutImageAngle(x, y: single;
     2177  Source: TBGRACustomBitmap; angle: single; AOutputBounds: TRect;
     2178  AResampleFilter: TResampleFilter; imageCenterX: single; imageCenterY: single; AOpacity: Byte;
     2179  ARestoreOffsetAfterRotation: boolean);
     2180var
     2181  Origin,HAxis,VAxis: TPointF;
     2182begin
     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);
     2187end;
     2188
     2189procedure TBGRACustomBitmap.PutImageAngle(x, y: single;
     2190  Source: TBGRACustomBitmap; angle: single; AResampleFilter: TResampleFilter;
     2191  imageCenterX: single; imageCenterY: single; AOpacity: Byte;
     2192  ARestoreOffsetAfterRotation: boolean);
     2193var
     2194  Origin,HAxis,VAxis: TPointF;
     2195begin
     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);
     2200end;
     2201
     2202procedure TBGRACustomBitmap.ComputeImageAngleAxes(x, y, w, h,
     2203  angle: single; imageCenterX, imageCenterY: single;
     2204  ARestoreOffsetAfterRotation: boolean; out Origin, HAxis, VAxis: TPointF);
     2205var
     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
     2222begin
     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);
     2228end;
     2229
     2230function TBGRACustomBitmap.GetImageAngleBounds(x, y: single;
     2231  Source: TBGRACustomBitmap; angle: single; imageCenterX: single;
     2232  imageCenterY: single; ARestoreOffsetAfterRotation: boolean): TRect;
     2233var
     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
     2250begin
     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);
     2259end;
     2260
     2261procedure TBGRACustomBitmap.VerticalFlip;
     2262begin
     2263  VerticalFlip(rect(0,0,Width,Height));
     2264end;
     2265
     2266procedure TBGRACustomBitmap.HorizontalFlip;
     2267begin
     2268  HorizontalFlip(rect(0,0,Width,Height));
     2269end;
     2270
     2271procedure TBGRACustomBitmap.ApplyMask(mask: TBGRACustomBitmap);
     2272begin
     2273  ApplyMask(mask, Rect(0,0,Width,Height), Point(0,0));
     2274end;
     2275
     2276procedure TBGRACustomBitmap.ApplyMask(mask: TBGRACustomBitmap; ARect: TRect);
     2277begin
     2278  ApplyMask(mask, ARect, ARect.TopLeft);
    13062279end;
    13072280
     
    14482421{************************** Color functions **************************}
    14492422
     2423function CheckPutImageBounds(x, y, tx, ty: integer; out minxb, minyb, maxxb,
     2424  maxyb, ignoreleft: integer; const cliprect: TRect): boolean;
     2425var x2,y2: integer;
     2426begin
     2427  if (x >= cliprect.Right) or (y >= cliprect.Bottom) or (x <= cliprect.Left-tx) or
     2428    (y <= cliprect.Top-ty) or (ty <= 0) or (tx <= 0) then
     2429  begin
     2430    result := false;
     2431    exit;
     2432  end;
     2433
     2434  x2 := x + tx - 1;
     2435  y2 := y + ty - 1;
     2436
     2437  if y < cliprect.Top then
     2438    minyb := cliprect.Top
     2439  else
     2440    minyb := y;
     2441  if y2 >= cliprect.Bottom then
     2442    maxyb := cliprect.Bottom - 1
     2443  else
     2444    maxyb := y2;
     2445
     2446  if x < cliprect.Left then
     2447  begin
     2448    ignoreleft := cliprect.Left-x;
     2449    minxb      := cliprect.Left;
     2450  end
     2451  else
     2452  begin
     2453    ignoreleft := 0;
     2454    minxb      := x;
     2455  end;
     2456  if x2 >= cliprect.Right then
     2457    maxxb := cliprect.Right - 1
     2458  else
     2459    maxxb := x2;
     2460
     2461  result := true;
     2462end;
     2463
    14502464{ The intensity is defined here as the maximum value of any color component }
    1451 function GetIntensity(c: TExpandedPixel): word; inline;
     2465function GetIntensity(const c: TExpandedPixel): word; inline;
    14522466begin
    14532467  Result := c.red;
     
    14582472end;
    14592473
    1460 function SetIntensity(c: TExpandedPixel; intensity: word): TExpandedPixel;
     2474function GetIntensity(c: TBGRAPixel): word;
     2475begin
     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];
     2482end;
     2483
     2484function SetIntensity(const c: TExpandedPixel; intensity: word): TExpandedPixel;
    14612485var
    14622486  curIntensity: word;
     
    14642488  curIntensity := GetIntensity(c);
    14652489  if curIntensity = 0 then //suppose it's gray if there is no color information
    1466     Result := c
     2490  begin
     2491    Result.red := intensity;
     2492    Result.green := intensity;
     2493    Result.blue := intensity;
     2494    result.alpha := c.alpha;
     2495  end
    14672496  else
    14682497  begin
     
    14752504end;
    14762505
     2506function SetIntensity(c: TBGRAPixel; intensity: word): TBGRAPixel;
     2507begin
     2508  result := GammaCompression(SetIntensity(GammaExpansion(c),intensity));
     2509end;
     2510
     2511function GetLightness(c: TBGRAPixel): word;
     2512begin
     2513  result := GetLightness(GammaExpansion(c));
     2514end;
     2515
    14772516{ The lightness here is defined as the subjective sensation of luminosity, where
    14782517  blue is the darkest component and green the lightest }
    1479 function GetLightness(c: TExpandedPixel): word; inline;
     2518function GetLightness(const c: TExpandedPixel): word; inline;
    14802519begin
    14812520  Result := (c.red * redWeightShl10 + c.green * greenWeightShl10 +
     
    14832522end;
    14842523
    1485 function SetLightness(c: TExpandedPixel; lightness: word): TExpandedPixel;
     2524function SetLightness(const c: TExpandedPixel; lightness: word): TExpandedPixel;
    14862525var
    14872526  curLightness: word;
    1488   AddedWhiteness, maxBeforeWhite: word;
    1489   clip: boolean;
    14902527begin
    14912528  curLightness := GetLightness(c);
     
    14952532    exit;
    14962533  end;
     2534  result := SetLightness(c, lightness, curLightness);
     2535end;
     2536
     2537function SetLightness(c: TBGRAPixel; lightness: word): TBGRAPixel;
     2538begin
     2539  result := GammaCompression(SetLightness(GammaExpansion(c),lightness));
     2540end;
     2541
     2542function SetLightness(const c: TExpandedPixel; lightness: word; curLightness: word): TExpandedPixel;
     2543var
     2544  AddedWhiteness, maxBeforeWhite: word;
     2545  clip: boolean;
     2546begin
     2547  if lightness = curLightness then
     2548  begin //no change
     2549    Result := c;
     2550    exit;
     2551  end;
    14972552  if lightness = 65535 then //set to white
    14982553  begin
     
    15212576  if lightness < curLightness then //darker is easy
    15222577  begin
    1523     Result := SetIntensity(c, (GetIntensity(c) * lightness + (curLightness shr 1)) div
    1524       curLightness);
     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;
    15252582    exit;
    15262583  end;
     
    15972654end;
    15982655
    1599 function CombineLightness(lightness1,lightness2: integer): integer;
     2656function CombineLightness(lightness1,lightness2: Int32or64): Int32or64;
    16002657{$ifdef CPUI386} {$asmmode intel} assembler;
    16012658  asm
     
    16632720end;
    16642721
    1665 function ExpandedToHSLA(ec: TExpandedPixel): THSLAPixel;
     2722procedure ExpandedToHSLAInline(r,g,b: Int32Or64; var dest: THSLAPixel); inline;
    16662723const
    1667   deg60  = 8192;
    1668   deg120 = deg60 * 2;
    1669   deg240 = deg60 * 4;
    1670   deg360 = deg60 * 6;
     2724  deg60  = 10922;
     2725  deg120 = 21845;
     2726  deg240 = 43690;
    16712727var
    1672   min, max, minMax: integer;
    1673   twiceLightness: integer;
    1674   r,g,b: integer;
    1675 begin
    1676   r := ec.red;
    1677   g := ec.green;
    1678   b := ec.blue;
    1679   min := r;
    1680   max := r;
    1681   if g > max then
    1682     max := g
    1683   else
    1684   if g < min then
     2728  min, max, minMax: Int32or64;
     2729  UMinMax,UTwiceLightness: UInt32or64;
     2730begin
     2731  if g > r then
     2732  begin
     2733    max := g;
     2734    min := r;
     2735  end
     2736  else
     2737  begin
     2738    max := r;
    16852739    min := g;
     2740  end;
    16862741  if b > max then
    16872742    max := b
     
    16922747
    16932748  if minMax = 0 then
    1694     Result.hue := 0
     2749    dest.hue := 0
    16952750  else
    16962751  if max = r then
    1697     Result.hue := (((g - b) * deg60) div
    1698       minMax + deg360) mod deg360
     2752    {$PUSH}{$RANGECHECKS OFF}
     2753    dest.hue := ((g - b) * deg60) div minMax
     2754    {$POP}
    16992755  else
    17002756  if max = g then
    1701     Result.hue := ((b - r) * deg60) div minMax + deg120
    1702   else
    1703     {max = b} Result.hue :=
    1704       ((r - g) * deg60) div minMax + deg240;
    1705   twiceLightness := max + min;
     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;
    17062761  if min = max then
    1707     Result.saturation := 0
    1708   else
    1709   {$hints off}
    1710   if twiceLightness < 65536 then
    1711     Result.saturation := (int64(minMax) shl 16) div (twiceLightness + 1)
    1712   else
    1713     Result.saturation := (int64(minMax) shl 16) div (131072 - twiceLightness);
    1714   {$hints on}
    1715   Result.lightness := twiceLightness shr 1;
    1716   Result.alpha := ec.alpha;
    1717   Result.hue   := (Result.hue shl 16) div deg360;
     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;
     2772end;
     2773
     2774function ExpandedToHSLA(const ec: TExpandedPixel): THSLAPixel;
     2775begin
     2776  result.alpha := ec.alpha;
     2777  ExpandedToHSLAInline(ec.red,ec.green,ec.blue,result);
    17182778end;
    17192779
    17202780function HtoG(hue: word): word;
    17212781const
    1722   segmentDest: array[0..5] of word =
     2782  segmentDest: array[0..5] of NativeUInt =
    17232783     (13653, 10923, 8192, 13653, 10923, 8192);
    1724   segmentSrc: array[0..5] of word =
     2784  segmentSrc: array[0..5] of NativeUInt =
    17252785     (10923, 10922, 10923, 10923, 10922, 10923);
    1726 begin
    1727   if hue < segmentSrc[0] then
    1728     result := hue * segmentDest[0] div segmentSrc[0]
    1729   else
    1730   begin
    1731     result := segmentDest[0];
    1732     hue -= segmentSrc[0];
    1733     if hue < segmentSrc[1] then
    1734       result += hue * segmentDest[1] div segmentSrc[1]
     2786var
     2787  h,g: NativeUInt;
     2788begin
     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]
    17352798    else
    17362799    begin
    1737       result += segmentDest[1];
    1738       hue -= segmentSrc[1];
    1739       if hue < segmentSrc[2] then
    1740         result += hue * segmentDest[2] div segmentSrc[2]
     2800      g += segmentDest[1];
     2801      h -= segmentSrc[1];
     2802      if h < segmentSrc[2] then
     2803        g += h * segmentDest[2] div segmentSrc[2]
    17412804      else
    17422805      begin
    1743         result += segmentDest[2];
    1744         hue -= segmentSrc[2];
    1745         if hue < segmentSrc[3] then
    1746           result += hue * segmentDest[3] div segmentSrc[3]
     2806        g += segmentDest[2];
     2807        h -= segmentSrc[2];
     2808        if h < segmentSrc[3] then
     2809          g += h * segmentDest[3] div segmentSrc[3]
    17472810        else
    17482811        begin
    1749           result += segmentDest[3];
    1750           hue -= segmentSrc[3];
    1751           if hue < segmentSrc[4] then
    1752             result += hue * segmentDest[4] div segmentSrc[4]
     2812          g += segmentDest[3];
     2813          h -= segmentSrc[3];
     2814          if h < segmentSrc[4] then
     2815            g += h * segmentDest[4] div segmentSrc[4]
    17532816          else
    17542817          begin
    1755             result += segmentDest[4];
    1756             hue -= segmentSrc[4];
    1757             result += hue * segmentDest[5] div segmentSrc[5];
     2818            g += segmentDest[4];
     2819            h -= segmentSrc[4];
     2820            g += h * segmentDest[5] div segmentSrc[5];
    17582821          end;
    17592822        end;
     
    17612824    end;
    17622825  end;
     2826  result := g;
    17632827end;
    17642828
    17652829function GtoH(ghue: word): word;
    17662830const
    1767   segment: array[0..5] of word =
     2831  segment: array[0..5] of NativeUInt =
    17682832     (13653, 10923, 8192, 13653, 10923, 8192);
    1769 begin
    1770   if ghue < segment[0] then
    1771     result := ghue * 10923 div segment[0]
    1772   else
    1773   begin
    1774     ghue -= segment[0];
    1775     if ghue < segment[1] then
    1776       result := ghue * (21845-10923) div segment[1] + 10923
     2833var g: NativeUint;
     2834begin
     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
    17772843    else
    17782844    begin
    1779       ghue -= segment[1];
    1780       if ghue < segment[2] then
    1781         result := ghue * (32768-21845) div segment[2] + 21845
     2845      g -= segment[1];
     2846      if g < segment[2] then
     2847        result := g * (32768-21845) div segment[2] + 21845
    17822848      else
    17832849      begin
    1784         ghue -= segment[2];
    1785         if ghue < segment[3] then
    1786           result := ghue * (43691-32768) div segment[3] + 32768
     2850        g -= segment[2];
     2851        if g < segment[3] then
     2852          result := g * (43691-32768) div segment[3] + 32768
    17872853        else
    17882854        begin
    1789           ghue -= segment[3];
    1790           if ghue < segment[4] then
    1791             result := ghue * (54613-43691) div segment[4] + 43691
     2855          g -= segment[3];
     2856          if g < segment[4] then
     2857            result := g * (54613-43691) div segment[4] + 43691
    17922858          else
    17932859          begin
    1794             ghue -= segment[4];
    1795             result := ghue * (65536-54613) div segment[5] + 54613;
     2860            g -= segment[4];
     2861            result := g * (65536-54613) div segment[5] + 54613;
    17962862          end;
    17972863        end;
     
    18012867end;
    18022868
    1803 function BGRAToGSBA(c: TBGRAPixel): THSLAPixel;
    1804 var ec: TExpandedPixel;
    1805     lightness: word;
    1806 begin
    1807   ec := GammaExpansion(c);
    1808   lightness := GetLightness(ec);
    1809 
    1810   result := ExpandedToHSLA(ec);
     2869function BGRAToGSBA(c: TBGRAPixel): TGSBAPixel;
     2870var lightness: UInt32Or64;
     2871    red,green,blue: Int32or64;
     2872begin
     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);
    18112882  if result.lightness > 32768 then
    1812     result.saturation := result.saturation* word(65535-result.lightness) div 32767;
     2883    result.saturation := result.saturation* UInt32or64(not result.lightness) div 32767;
    18132884  result.lightness := lightness;
    18142885  result.hue := HtoG(result.hue);
    18152886end;
    18162887
    1817 function HSLAToExpanded(c: THSLAPixel): TExpandedPixel;
     2888function ExpandedToGSBA(ec: TExpandedPixel): TGSBAPixel;
     2889var lightness: UInt32Or64;
     2890    red,green,blue: Int32or64;
     2891begin
     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);
     2905end;
     2906
     2907function HSLAToExpanded(const c: THSLAPixel): TExpandedPixel;
    18182908const
    18192909  deg30  = 4096;
     
    18242914  deg360 = deg60 * 6;
    18252915
    1826   function ComputeColor(p, q: integer; h: integer): word; inline;
    1827   begin
    1828     if h > deg360 then
    1829       Dec(h, deg360);
    1830     if h < deg60 then
    1831       Result := p + ((q - p) * h + deg30) div deg60
    1832     else
     2916  function ComputeColor(p, q: Int32or64; h: Int32or64): Int32or64; inline;
     2917  begin
    18332918    if h < deg180 then
    1834       Result := q
    1835     else
    1836     if h < deg240 then
    1837       Result := p + ((q - p) * (deg240 - h) + deg30) div deg60
    1838     else
    1839       Result := p;
     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;
    18402931  end;
    18412932
    18422933var
    1843   q, p: integer;
    1844 begin
    1845   c.hue := c.hue * deg360 shr 16;
    1846   if c.saturation = 0 then  //gray
    1847   begin
    1848     result.red   := c.lightness;
    1849     result.green := c.lightness;
    1850     result.blue  := c.lightness;
     2934  q, p, L, S, H: Int32or64;
     2935begin
     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;
    18512943    result.alpha := c.alpha;
    18522944    exit;
    18532945  end;
    18542946  {$hints off}
    1855   if c.lightness < 32768 then
    1856     q := (c.lightness shr 1) * ((65535 + c.saturation) shr 1) shr 14
    1857   else
    1858     q := c.lightness + c.saturation - ((c.lightness shr 1) *
    1859       (c.saturation shr 1) shr 14);
     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);
    18602952  {$hints on}
    1861   if q > 65535 then
    1862     q := 65535;
    1863   p   := c.lightness * 2 - q;
    1864   if p > 65535 then
    1865     p      := 65535;
    1866   result.red   := ComputeColor(p, q, c.hue + deg120);
    1867   result.green := ComputeColor(p, q, c.hue);
    1868   result.blue  := ComputeColor(p, q, c.hue + deg240);
     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);
    18692964  result.alpha := c.alpha;
    18702965end;
    18712966
    18722967{ Conversion from HSL colorspace to RGB. See : http://en.wikipedia.org/wiki/HSL_color_space }
    1873 function HSLAToBGRA(c: THSLAPixel): TBGRAPixel;
     2968function HSLAToBGRA(const c: THSLAPixel): TBGRAPixel;
    18742969var ec: TExpandedPixel;
    18752970begin
     
    19453040end;
    19463041
    1947 function GSBAToBGRA(c: THSLAPixel): TBGRAPixel;
     3042function GSBAToBGRA(c: TGSBAPixel): TBGRAPixel;
    19483043var ec: TExpandedPixel;
    19493044    lightness: word;
     
    19563051end;
    19573052
    1958 function GSBAToHSLA(c: THSLAPixel): THSLAPixel;
     3053function GSBAToExpanded(c: TGSBAPixel): TExpandedPixel;
     3054var lightness: word;
     3055begin
     3056  c.hue := GtoH(c.hue);
     3057  lightness := c.lightness;
     3058  c.lightness := 32768;
     3059  result := SetLightness(HSLAToExpanded(c),lightness);
     3060end;
     3061
     3062function GSBAToHSLA(c: TGSBAPixel): THSLAPixel;
    19593063begin
    19603064  result := BGRAToHSLA(GSBAToBGRA(c));
     
    19703074end;
    19713075
    1972 function GammaCompression(ec: TExpandedPixel): TBGRAPixel;
     3076function GammaCompression(const ec: TExpandedPixel): TBGRAPixel;
    19733077begin
    19743078  Result.red   := GammaCompressionTab[ec.red];
     
    19943098  cgray: byte;
    19953099begin
     3100  if c.alpha = 0 then
     3101  begin
     3102    result := BGRAPixelTransparent;
     3103    exit;
     3104  end;
    19963105  //gamma expansion
    19973106  ec    := GammaExpansion(c);
     
    20173126function MergeBGRA(const colors: array of TBGRAPixel): TBGRAPixel;
    20183127var
    2019   sumR,sumG,sumB,sumA: longword;
     3128  sumR,sumG,sumB,sumA: NativeUInt;
    20203129  i: integer;
    20213130begin
     
    21073216  weight2: byte): TBGRAPixel;
    21083217var
    2109     f1,f2: word;
    2110     f12: longword;
    2111 begin
    2112   if (weight1 = 0) then
    2113   begin
    2114     if (weight2 = 0) then
     3218    w1,w2,f1,f2,f12,a: UInt32or64;
     3219begin
     3220  w1 := weight1;
     3221  w2 := weight2;
     3222  if (w1 = 0) then
     3223  begin
     3224    if (w2 = 0) then
    21153225      result := BGRAPixelTransparent
    21163226    else
     
    21183228  end
    21193229  else
    2120   if (weight2 = 0) then
     3230  if (w2 = 0) then
    21213231    Result := c1
    21223232  else
    21233233  begin
    2124     f1 := c1.alpha*weight1 shr 1;
    2125     f2 := c2.alpha*weight2 shr 1;
     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}
    21263250    f12 := f1+f2;
    2127     if f12 = 0 then
    2128       result := BGRAPixelTransparent
    2129     else
    2130     begin
    2131       Result.red   := GammaCompressionTab[(GammaExpansionTab[c1.red] * f1 + GammaExpansionTab[c2.red] * f2) div f12];
    2132       Result.green := GammaCompressionTab[(GammaExpansionTab[c1.green] * f1 + GammaExpansionTab[c2.green] * f2) div f12];
    2133       Result.blue  := GammaCompressionTab[(GammaExpansionTab[c1.blue] * f1 + GammaExpansionTab[c2.blue] * f2) div f12];
    2134       Result.alpha := (c1.alpha*weight1+c2.alpha*weight2 + ((weight1+weight2) shr 1)) div (weight1+weight2);
    2135     end;
     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];
    21363254  end;
    21373255end;
     
    23293447end;
    23303448
    2331 { Write a color in hexadecimal format RRGGBBAA }
    2332 function BGRAToStr(c: TBGRAPixel): string;
    2333 begin
     3449{ Write a color in hexadecimal format RRGGBBAA or using the name in a color list }
     3450function BGRAToStr(c: TBGRAPixel; AColorList: TBGRAColorList = nil; AMaxDiff: Word= 0): string;
     3451var idx: integer;
     3452begin
     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;
    23343462  result := IntToHex(c.red,2)+IntToHex(c.green,2)+IntToHex(c.Blue,2)+IntToHex(c.Alpha,2);
    23353463end;
     
    23383466    arrayOfString = array of string;
    23393467
    2340 function SimpleParseFuncParam(str: string): arrayOfString;
     3468function SimpleParseFuncParam(str: string; var flagError: boolean): arrayOfString;
    23413469var idxOpen,start,cur: integer;
    23423470begin
    23433471    result := nil;
    23443472    idxOpen := pos('(',str);
    2345     if idxOpen = 0 then exit;
    2346     start := idxOpen+1;
     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;
    23473480    cur := start;
    23483481    while cur <= length(str) do
     
    23513484       begin
    23523485         setlength(result,length(result)+1);
    2353          result[high(result)] := copy(str,start,cur-start);
     3486         result[high(result)] := trim(copy(str,start,cur-start));
    23543487         start := cur+1;
     3488         if str[cur] = ')' then exit;
    23553489       end;
    23563490       inc(cur);
    23573491    end;
     3492    if idxOpen <> 0 then flagError := true; //should exit on ')'
    23583493    if start <= length(str) then
    23593494    begin
     
    23633498end;
    23643499
    2365 function ParseColorValue(str: string): byte;
     3500function ParseColorValue(str: string; var flagError: boolean): byte;
    23663501var pourcent,unclipped,{%H-}errPos: integer;
    23673502begin
     
    23713506    begin
    23723507      val(copy(str,1,length(str)-1),pourcent,errPos);
     3508      if errPos <> 0 then flagError := true;
    23733509      if pourcent < 0 then result := 0 else
    23743510      if pourcent > 100 then result := 255 else
     
    23773513    begin
    23783514      val(str,unclipped,errPos);
     3515      if errPos <> 0 then flagError := true;
    23793516      if unclipped < 0 then result := 0 else
    23803517      if unclipped > 255 then result := 255 else
     
    23843521end;
    23853522
     3523//this function returns the parsed value only if it contains no error nor missing values, otherwise
     3524//it returns BGRAPixelTransparent
    23863525function StrToBGRA(str: string): TBGRAPixel;
    2387 begin
    2388   result := StrToBGRA(str, BGRAPixelTransparent);
    2389 end;
    2390 
    2391 { Read a color in hexadecimal format RRGGBB(AA) or RGB(A) }
    2392 function StrToBGRA(str: string; DefaultColor: TBGRAPixel): TBGRAPixel;
     3526var missingValues, error: boolean;
     3527begin
     3528  result := BGRABlack;
     3529  TryStrToBGRA(str, result, missingValues, error);
     3530  if missingValues or error then result := BGRAPixelTransparent;
     3531end;
     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.
     3538procedure TryStrToBGRA(str: string; var parsedValue: TBGRAPixel; out missingValues: boolean; out error: boolean);
    23933539var errPos: integer;
    23943540    values: array of string;
     
    23963542    idx: integer;
    23973543begin
    2398   if str = '' then
    2399   begin
    2400     result := DefaultColor;
     3544  str := Trim(str);
     3545  error := false;
     3546  if (str = '') or (str = '?') then
     3547  begin
     3548    missingValues := true;
    24013549    exit;
    2402   end;
    2403   str := lowerCase(str);
     3550  end else
     3551    missingValues := false;
     3552  str := StringReplace(lowerCase(str),'grey','gray',[]);
    24043553
    24053554  //VGA color names
    2406   if str='black' then result := BGRA(0,0,0) else
    2407   if str='silver' then result := BGRA(192,192,192) else
    2408   if str='gray' then result := BGRA(128,128,128) else
    2409   if str='grey' then result := BGRA(128,128,128) else
    2410   if str='white' then result := BGRA(255,255,255) else
    2411   if str='maroon' then result := BGRA(128,0,0) else
    2412   if str='red' then result := BGRA(255,0,0) else
    2413   if str='purple' then result := BGRA(128,0,128) else
    2414   if str='fuchsia' then result := BGRA(255,0,255) else
    2415   if str='green' then result := BGRA(0,128,0) else
    2416   if str='lime' then result := BGRA(0,255,0) else
    2417   if str='olive' then result := BGRA(128,128,0) else
    2418   if str='yellow' then result := BGRA(255,255,0) else
    2419   if str='navy' then result := BGRA(0,0,128) else
    2420   if str='blue' then result := BGRA(0,0,255) else
    2421   if str='teal' then result := BGRA(0,128,128) else
    2422   if str='aqua' then result := BGRA(0,255,255) else
    2423   if str='transparent' then result := DefaultColor else
     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
    24243562  begin
    24253563    //check CSS color
     
    24273565    if idx <> -1 then
    24283566    begin
    2429       result := CSSColors[idx];
     3567      parsedValue := CSSColors[idx];
    24303568      exit;
    24313569    end;
    24323570
    24333571    //CSS RGB notation
    2434     if (copy(str,1,4)='rgb(') or (copy(str,1,5)='rgba(') then
     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
    24353574    begin
    2436       values := SimpleParseFuncParam(str);
     3575      values := SimpleParseFuncParam(str,error);
    24373576      if (length(values)=3) or (length(values)=4) then
    24383577      begin
    2439         result.red := ParseColorValue(values[0]);
    2440         result.green := ParseColorValue(values[1]);
    2441         result.blue := ParseColorValue(values[2]);
     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;
    24423590        if length(values)=4 then
    24433591        begin
    2444           val(values[3],alphaF,errPos);
    2445           if alphaF < 0 then
    2446             result.alpha := 0 else
    2447           if alphaF > 1 then
    2448             result.alpha := 255
    2449           else
    2450             result.alpha := round(alphaF*255);
     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;
    24513611        end else
    2452           result.alpha := 255;
     3612          parsedValue.alpha := 255;
    24533613      end else
    2454         result := DefaultColor;
     3614        error := true;
    24553615      exit;
    24563616    end;
     
    24593619    if str[1]='#' then delete(str,1,1);
    24603620
    2461     //add alpha if missing
     3621    //add alpha if missing (if you want an undefined alpha use '??' or '?')
    24623622    if length(str)=6 then str += 'FF';
    24633623    if length(str)=3 then str += 'F';
     
    24663626    if length(str)=8 then
    24673627    begin
    2468       val('$'+copy(str,1,2),result.red,errPos);
    2469       if errPos <> 0 then
     3628      if copy(str,1,2) <> '??' then
    24703629      begin
    2471         result := DefaultColor;
    2472         exit;
    2473       end;
    2474       val('$'+copy(str,3,2),result.green,errPos);
    2475       if errPos <> 0 then
     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
    24763634      begin
    2477         result := DefaultColor;
    2478         exit;
    2479       end;
    2480       val('$'+copy(str,5,2),result.blue,errPos);
    2481       if errPos <> 0 then
     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
    24823639      begin
    2483         result := DefaultColor;
    2484         exit;
    2485       end;
    2486       val('$'+copy(str,7,2),result.alpha,errPos);
    2487       if errPos <> 0 then
     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
    24883644      begin
    2489         result := DefaultColor;
    2490         exit;
    2491       end;
     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;
    24923652    end else
    24933653    if length(str)=4 then
    24943654    begin
    2495       val('$'+copy(str,1,1),result.red,errPos);
    2496       if errPos <> 0 then
     3655      if str[1] <> '?' then
    24973656      begin
    2498         result := DefaultColor;
    2499         exit;
    2500       end;
    2501       val('$'+copy(str,2,1),result.green,errPos);
    2502       if errPos <> 0 then
     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
    25033662      begin
    2504         result := DefaultColor;
    2505         exit;
    2506       end;
    2507       val('$'+copy(str,3,1),result.blue,errPos);
    2508       if errPos <> 0 then
     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
    25093668      begin
    2510         result := DefaultColor;
    2511         exit;
    2512       end;
    2513       val('$'+copy(str,4,1),result.alpha,errPos);
    2514       if errPos <> 0 then
     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
    25153674      begin
    2516         result := DefaultColor;
    2517         exit;
    2518       end;
    2519       result.red *= $11;
    2520       result.green *= $11;
    2521       result.blue *= $11;
    2522       result.alpha *= $11;
     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;
    25233683    end else
    2524       result := DefaultColor;
    2525   end;
    2526 
     3684      error := true; //string format not recognised
     3685  end;
     3686
     3687end;
     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.
     3692function PartialStrToBGRA(str: string; const fallbackValues: TBGRAPixel; out
     3693  error: boolean): TBGRAPixel;
     3694var missingValues: boolean;
     3695begin
     3696  result := fallbackValues;
     3697  TryStrToBGRA(str, result, missingValues, error);
     3698end;
     3699
     3700{ Read a color, for example in hexadecimal format RRGGBB(AA) or RGB(A). Partial colors are not accepted by this function. }
     3701function StrToBGRA(str: string; const DefaultColor: TBGRAPixel): TBGRAPixel;
     3702var missingValues, error: boolean;
     3703begin
     3704  result := BGRABlack;
     3705  TryStrToBGRA(str, result, missingValues, error);
     3706  if missingValues or error then result := DefaultColor;
    25273707end;
    25283708
     
    25313711begin
    25323712  intval := color.Green shl 16 + color.red shl 8 + color.blue;
    2533   result := intval/16777215;
     3713  result := intval*5.960464832810452e-8;
    25343714end;
    25353715
     
    26013781end;
    26023782
    2603 function PtInRect(pt: TPoint; r: TRect): boolean;
     3783function PtInRect(const pt: TPoint; r: TRect): boolean;
    26043784var
    26053785  temp: integer;
     
    26213801end;
    26223802
     3803function RectWithSize(left, top, width, height: integer): TRect;
     3804begin
     3805  result.left := left;
     3806  result.top := top;
     3807  result.right := left+width;
     3808  result.bottom := top+height;
     3809end;
     3810
    26233811function VectLen(dx, dy: single): single;
    26243812begin
     
    26303818  result := sqrt(v.x*v.x+v.y*v.y);
    26313819end;
    2632 
     3820{$OPTIMIZATION OFF}  // Modif J.P  5/2013
    26333821function IntersectLine(line1, line2: TLineDef): TPointF;
    26343822var parallel: boolean;
     
    26363824  result := IntersectLine(line1,line2,parallel);
    26373825end;
     3826{$OPTIMIZATION ON}
    26383827
    26393828function IntersectLine(line1, line2: TLineDef; out parallel: boolean): TPointF;
     
    27873976
    27883977// Get the cyclic value in the range [0..cycle-1]
    2789 function PositiveMod(value, cycle: integer): integer; inline;
     3978function PositiveMod(value, cycle: Int32or64): Int32or64; inline;
    27903979begin
    27913980  result := value mod cycle;
     
    28013990  byteSqrtTab: packed array of word;
    28023991
    2803 function Sin65536(value: word): integer;
     3992function Sin65536(value: word): Int32or64;
    28043993var b: integer;
    28053994begin
     
    28254014end;
    28264015
    2827 function Cos65536(value: word): integer;
    2828 begin
     4016function Cos65536(value: word): Int32or64;
     4017begin
     4018  {$PUSH}{$R-}
    28294019  result := Sin65536(value+16384); //cosine is translated
     4020  {$POP}
    28304021end;
    28314022
     
    28544045end;
    28554046
     4047function DetectFileFormat(AFilenameUTF8: string): TBGRAImageFormat;
     4048var stream: TFileStreamUTF8;
     4049begin
     4050  try
     4051    stream := TFileStreamUTF8.Create(AFilenameUTF8,fmOpenRead or fmShareDenyWrite);
     4052  except
     4053    result := ifUnknown;
     4054    exit;
     4055  end;
     4056  try
     4057    result := DetectFileFormat(stream, ExtractFileExt(AFilenameUTF8));
     4058  finally
     4059    stream.Free;
     4060  end;
     4061end;
     4062
     4063function DetectFileFormat(AStream: TStream; ASuggestedExtensionUTF8: string
     4064  ): TBGRAImageFormat;
     4065var
     4066  scores: array[TBGRAImageFormat] of integer;
     4067  imageFormat,bestImageFormat: TBGRAImageFormat;
     4068  bestScore: integer;
     4069
     4070  procedure DetectFromStream;
     4071  var
     4072    {%H-}magic: packed array[0..7] of byte;
     4073    {%H-}dwords: packed array[0..9] of DWORD;
     4074    magicAsText: string;
     4075
     4076    streamStartPos, maxFileSize: Int64;
     4077    expectedFileSize: DWord;
     4078
     4079    procedure DetectTarga;
     4080    var
     4081      paletteCount: integer;
     4082      {%H-}targaPixelFormat: packed record pixelDepth: byte; imgDescriptor: byte; end;
     4083    begin
     4084      if (magic[1] in[$00,$01]) and (magic[2] in[0,1,2,3,9,10,11]) and (maxFileSize >= 18) then
     4085      begin
     4086        paletteCount:= magic[5] + magic[6] shl 8;
     4087        if ((paletteCount = 0) and (magic[7] = 0)) or
     4088          (magic[7] in [16,24,32]) then //check palette bit count
     4089        begin
     4090          AStream.Position:= streamStartPos+16;
     4091          if AStream.Read({%H-}targaPixelFormat,2) = 2 then
     4092          begin
     4093            if (targaPixelFormat.pixelDepth in [8,16,24,32]) and
     4094              (targaPixelFormat.imgDescriptor and 15 < targaPixelFormat.pixelDepth) then
     4095                inc(scores[ifTarga],2);
     4096          end;
     4097        end;
     4098      end;
     4099    end;
     4100
     4101    procedure DetectLazPaint;
     4102    var
     4103      w,h: dword;
     4104      i: integer;
     4105    begin
     4106      if (copy(magicAsText,1,8) = 'LazPaint') then //with header
     4107      begin
     4108        AStream.Position:= streamStartPos+8;
     4109        if AStream.Read(dwords,10*4) = 10*4 then
     4110        begin
     4111          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
     4114            (dwords[6] = 0) then inc(scores[ifLazPaint],2);
     4115        end;
     4116      end else //without header
     4117      if ((magic[0] <> 0) or (magic[1] <> 0)) and (magic[2] = 0) and (magic[3] = 0) and
     4118         ((magic[4] <> 0) or (magic[5] <> 0)) and (magic[6] = 0) and (magic[7] = 0) then
     4119      begin
     4120        w := magic[0] + (magic[1] shl 8);
     4121        h := magic[4] + (magic[5] shl 8);
     4122        AStream.Position:= streamStartPos+8;
     4123        if AStream.Read(dwords,4) = 4 then
     4124        begin
     4125          dwords[0] := LEtoN(dwords[0]);
     4126          if (dwords[0] > 0) and (dwords[0] < 65536) then
     4127          begin
     4128            if 12+dwords[0] < expectedFileSize then
     4129            begin
     4130              AStream.Position:= streamStartPos+12+dwords[0];
     4131              if AStream.Read(dwords,6*4) = 6*4 then
     4132              begin
     4133                for i := 0 to 5 do dwords[i] := LEtoN(dwords[i]);
     4134                if (dwords[0] <= w) and (dwords[1] <= h) and
     4135                  (dwords[2] <= w) and (dwords[3] <= h) and
     4136                  (dwords[2] >= dwords[0]) and (dwords[3] >= dwords[1]) and
     4137                  ((dwords[4] = 0) or (dwords[4] = 1)) and
     4138                  (dwords[5] > 0) then inc(scores[ifLazPaint],1);
     4139              end;
     4140            end;
     4141          end;
     4142        end;
     4143      end;
     4144    end;
     4145
     4146  begin
     4147    fillchar({%H-}magic, sizeof(magic), 0);
     4148    fillchar({%H-}dwords, sizeof(dwords), 0);
     4149
     4150    streamStartPos:= AStream.Position;
     4151    maxFileSize:= AStream.Size - streamStartPos;
     4152    if maxFileSize < 8 then exit;
     4153    if AStream.Read(magic,sizeof(magic)) <> sizeof(magic) then
     4154    begin
     4155      fillchar(scores,sizeof(scores),0);
     4156      exit;
     4157    end;
     4158    setlength(magicAsText,sizeof(magic));
     4159    move(magic[0],magicAsText[1],sizeof(magic));
     4160
     4161    if (magic[0] = $ff) and (magic[1] = $d8) then
     4162    begin
     4163         inc(scores[ifJpeg]);
     4164         if (magic[2] = $ff) and (magic[3] >= $c0) then inc(scores[ifJpeg]);
     4165    end;
     4166
     4167    if (magic[0] = $89) and (magic[1] = $50) and (magic[2] = $4e) and
     4168      (magic[3] = $47) and (magic[4] = $0d) and (magic[5] = $0a) and
     4169      (magic[6] = $1a) and (magic[7] = $0a) then inc(scores[ifPng],2);
     4170
     4171    if (copy(magicAsText,1,6)='GIF87a') or (copy(magicAsText,1,6)='GIF89a') then inc(scores[ifGif],2);
     4172
     4173    if (magic[0] = $0a) and (magic[1] in [0,2,3,4,5]) and (magic[2] in[0,1]) and (magic[3] in[1,2,4,8]) then
     4174      inc(scores[ifPcx],2);
     4175
     4176    if (copy(magicAsText,1,2)='BM') then
     4177    begin
     4178      inc(scores[ifBmp]);
     4179      expectedFileSize:= magic[2] + (magic[3] shl 8) + (magic[4] shl 16) + (magic[5] shl 24);
     4180      if expectedFileSize = maxFileSize then inc(scores[ifBmp]);
     4181    end else
     4182    if (copy(magicAsText,1,2)='RL') then
     4183    begin
     4184      inc(scores[ifBmpMioMap]);
     4185      if (magic[2] in[0,1]) and (magic[3] = 0) then inc(scores[ifBmpMioMap]);
     4186    end;
     4187
     4188    if (magic[0] = $00) and (magic[1] = $00) and (magic[2] in[$01,$02]) and (magic[3] = $00) and
     4189      (magic[4] + (magic[5] shl 8) > 0) then inc(scores[ifIco]);
     4190
     4191    if (copy(magicAsText,1,4) = 'PDN3') then
     4192    begin
     4193      expectedFileSize:= 6 + (magic[4] + (magic[5] shl 8) + (magic[6] shl 16)) + 2;
     4194      if expectedFileSize <= maxFileSize then
     4195      begin
     4196        inc(scores[ifPaintDotNet]);
     4197        if magic[7] = $3c then inc(scores[ifPaintDotNet]);
     4198      end;
     4199    end;
     4200
     4201    DetectLazPaint;
     4202
     4203    if (magic[0] = $50) and (magic[1] = $4b) and (magic[2] = $03) and (magic[3] = $04) then
     4204    begin
     4205      if DefaultBGRAImageReader[ifOpenRaster] = nil then inc(scores[ifOpenRaster]) else
     4206      with CreateBGRAImageReader(ifOpenRaster) do
     4207        try
     4208          if CheckContents(AStream) then inc(scores[ifOpenRaster],2);
     4209        finally
     4210          Free;
     4211        end;
     4212    end;
     4213
     4214    if (copy(magicAsText,1,4) = '8BPS') and (magic[4] = $00) and (magic[5] = $01) then inc(scores[ifPsd],2);
     4215
     4216    DetectTarga;
     4217
     4218    if (copy(magicAsText,1,2)='II') and (magic[2] = 42) and (magic[3]=0) then inc(scores[ifTiff]) else
     4219    if (copy(magicAsText,1,2)='MM') and (magic[2] = 0) and (magic[3]=42) then inc(scores[ifTiff]);
     4220
     4221    if (copy(magicAsText,1,8) = '/* XPM *') or (copy(magicAsText,1,6) = '! XPM2') then inc(scores[ifXPixMap]);
     4222
     4223    AStream.Position := streamStartPos;
     4224  end;
     4225
     4226var
     4227  extFormat: TBGRAImageFormat;
     4228
     4229begin
     4230  result := ifUnknown;
     4231  for imageFormat:= low(TBGRAImageFormat) to high(TBGRAImageFormat) do
     4232    scores[imageFormat] := 0;
     4233
     4234  ASuggestedExtensionUTF8:= UTF8LowerCase(ASuggestedExtensionUTF8);
     4235  if (ASuggestedExtensionUTF8 <> '') and (UTF8Copy(ASuggestedExtensionUTF8,1,1) <> '.') then
     4236    ASuggestedExtensionUTF8 := '.'+ASuggestedExtensionUTF8;
     4237
     4238  extFormat:= SuggestImageFormat(ASuggestedExtensionUTF8);
     4239  if extFormat <> ifUnknown then inc(scores[extFormat]);
     4240
     4241  If AStream <> nil then DetectFromStream;
     4242
     4243  bestScore := 0;
     4244  bestImageFormat:= ifUnknown;
     4245  for imageFormat:=low(TBGRAImageFormat) to high(TBGRAImageFormat) do
     4246    if scores[imageFormat] > bestScore then
     4247    begin
     4248      bestScore:= scores[imageFormat];
     4249      bestImageFormat:= imageFormat;
     4250    end;
     4251  result := bestImageFormat;
     4252end;
     4253
     4254function SuggestImageFormat(AFilenameOrExtensionUTF8: string): TBGRAImageFormat;
     4255var ext: string;
     4256begin
     4257  result := ifUnknown;
     4258
     4259  ext := ExtractFileName(AFilenameOrExtensionUTF8);
     4260  if pos('.', ext) <> 0 then ext := ExtractFileExt(ext) else ext := '.'+ext;
     4261  ext := UTF8LowerCase(ext);
     4262
     4263  if (ext = '.jpg') or (ext = '.jpeg') then result := ifJpeg else
     4264  if (ext = '.png') then result := ifPng else
     4265  if (ext = '.gif') then result := ifGif else
     4266  if (ext = '.pcx') then result := ifPcx else
     4267  if (ext = '.bmp') then result := ifBmp else
     4268  if (ext = '.ico') or (ext = '.cur') then result := ifIco else
     4269  if (ext = '.pdn') then result := ifPaintDotNet else
     4270  if (ext = '.lzp') then result := ifLazPaint else
     4271  if (ext = '.ora') then result := ifOpenRaster else
     4272  if (ext = '.psd') then result := ifPsd else
     4273  if (ext = '.tga') then result := ifTarga else
     4274  if (ext = '.tif') or (ext = '.tiff') then result := ifTiff else
     4275  if (ext = '.xwd') then result := ifXwd else
     4276  if (ext = '.xpm') then result := ifXPixMap;
     4277end;
     4278
     4279function CreateBGRAImageReader(AFormat: TBGRAImageFormat): TFPCustomImageReader;
     4280begin
     4281  if DefaultBGRAImageReader[AFormat] = nil then
     4282  begin
     4283    case AFormat of
     4284      ifUnknown: raise exception.Create('The image format is unknown.');
     4285      ifOpenRaster: raise exception.Create('You need to call BGRAOpenRaster.RegisterOpenRasterFormat to read this image.');
     4286      ifPaintDotNet: raise exception.Create('You need to call BGRAPaintNet.RegisterPaintNetFormat to read this image.');
     4287    else
     4288      raise exception.Create('The image reader is not registered for this image format.');
     4289    end;
     4290  end;
     4291  result := DefaultBGRAImageReader[AFormat].Create;
     4292end;
     4293
     4294function CreateBGRAImageWriter(AFormat: TBGRAImageFormat; AHasTransparentPixels: boolean): TFPCustomImageWriter;
     4295begin
     4296  if DefaultBGRAImageWriter[AFormat] = nil then
     4297  begin
     4298    case AFormat of
     4299      ifUnknown: raise exception.Create('The image format is unknown');
     4300      ifOpenRaster: raise exception.Create('You need to call BGRAOpenRaster.RegisterOpenRasterFormat to write with this image format.');
     4301    else
     4302      raise exception.Create('The image writer is not registered for this image format.');
     4303    end;
     4304  end;
     4305
     4306  if AFormat = ifPng then
     4307  begin
     4308    result := TFPWriterPNG.Create;
     4309    TFPWriterPNG(result).Indexed := false;
     4310    TFPWriterPNG(result).WordSized := false;
     4311    TFPWriterPNG(result).UseAlpha := AHasTransparentPixels;
     4312  end else
     4313  if AFormat = ifBmp then
     4314  begin
     4315    result := TFPWriterBMP.Create;
     4316    if AHasTransparentPixels then
     4317      TFPWriterBMP(result).BitsPerPixel := 32 else
     4318      TFPWriterBMP(result).BitsPerPixel := 24;
     4319  end else
     4320  if AFormat = ifXPixMap then
     4321  begin
     4322    result := TFPWriterXPM.Create;
     4323    TFPWriterXPM(result).ColorCharSize := 2;
     4324  end else
     4325    result := DefaultBGRAImageWriter[AFormat].Create;
     4326end;
     4327
    28564328initialization
    28574329
    28584330  InitGamma;
    2859   CSSColors := TBGRAColorList.Create;
    2860   CSSColors.Add('AliceBlue',CSSAliceBlue);
    2861   CSSColors.Add('AntiqueWhite',CSSAntiqueWhite);
    2862   CSSColors.Add('Aqua',CSSAqua);
    2863   CSSColors.Add('Aquamarine',CSSAquamarine);
    2864   CSSColors.Add('Azure',CSSAzure);
    2865   CSSColors.Add('Beige',CSSBeige);
    2866   CSSColors.Add('Bisque',CSSBisque);
    2867   CSSColors.Add('Black',CSSBlack);
    2868   CSSColors.Add('BlanchedAlmond',CSSBlanchedAlmond);
    2869   CSSColors.Add('Blue',CSSBlue);
    2870   CSSColors.Add('BlueViolet',CSSBlueViolet);
    2871   CSSColors.Add('Brown',CSSBrown);
    2872   CSSColors.Add('BurlyWood',CSSBurlyWood);
    2873   CSSColors.Add('CadetBlue',CSSCadetBlue);
    2874   CSSColors.Add('Chartreuse',CSSChartreuse);
    2875   CSSColors.Add('Chocolate',CSSChocolate);
    2876   CSSColors.Add('Coral',CSSCoral);
    2877   CSSColors.Add('CornflowerBlue',CSSCornflowerBlue);
    2878   CSSColors.Add('Cornsilk',CSSCornsilk);
    2879   CSSColors.Add('Crimson',CSSCrimson);
    2880   CSSColors.Add('Cyan',CSSCyan);
    2881   CSSColors.Add('DarkBlue',CSSDarkBlue);
    2882   CSSColors.Add('DarkCyan',CSSDarkCyan);
    2883   CSSColors.Add('DarkGoldenrod',CSSDarkGoldenrod);
    2884   CSSColors.Add('DarkGray',CSSDarkGray);
    2885   CSSColors.Add('DarkGreen',CSSDarkGreen);
    2886   CSSColors.Add('DarkKhaki',CSSDarkKhaki);
    2887   CSSColors.Add('DarkMagenta',CSSDarkMagenta);
    2888   CSSColors.Add('DarkOliveGreen',CSSDarkOliveGreen);
    2889   CSSColors.Add('DarkOrange',CSSDarkOrange);
    2890   CSSColors.Add('DarkOrchid',CSSDarkOrchid);
    2891   CSSColors.Add('DarkRed',CSSDarkRed);
    2892   CSSColors.Add('DarkSalmon',CSSDarkSalmon);
    2893   CSSColors.Add('DarkSeaGreen',CSSDarkSeaGreen);
    2894   CSSColors.Add('DarkSlateBlue',CSSDarkSlateBlue);
    2895   CSSColors.Add('DarkSlateGray',CSSDarkSlateGray);
    2896   CSSColors.Add('DarkTurquoise',CSSDarkTurquoise);
    2897   CSSColors.Add('DarkViolet',CSSDarkViolet);
    2898   CSSColors.Add('DeepPink',CSSDeepPink);
    2899   CSSColors.Add('DeepSkyBlue',CSSDeepSkyBlue);
    2900   CSSColors.Add('DimGray',CSSDimGray);
    2901   CSSColors.Add('DodgerBlue',CSSDodgerBlue);
    2902   CSSColors.Add('FireBrick',CSSFireBrick);
    2903   CSSColors.Add('FloralWhite',CSSFloralWhite);
    2904   CSSColors.Add('ForestGreen',CSSForestGreen);
    2905   CSSColors.Add('Fuchsia',CSSFuchsia);
    2906   CSSColors.Add('Gainsboro',CSSGainsboro);
    2907   CSSColors.Add('GhostWhite',CSSGhostWhite);
    2908   CSSColors.Add('Gold',CSSGold);
    2909   CSSColors.Add('Goldenrod',CSSGoldenrod);
    2910   CSSColors.Add('Gray',CSSGray);
    2911   CSSColors.Add('Green',CSSGreen);
    2912   CSSColors.Add('GreenYellow',CSSGreenYellow);
    2913   CSSColors.Add('Honeydew',CSSHoneydew);
    2914   CSSColors.Add('HotPink',CSSHotPink);
    2915   CSSColors.Add('IndianRed',CSSIndianRed);
    2916   CSSColors.Add('Indigo',CSSIndigo);
    2917   CSSColors.Add('Ivory',CSSIvory);
    2918   CSSColors.Add('Khaki',CSSKhaki);
    2919   CSSColors.Add('Lavender',CSSLavender);
    2920   CSSColors.Add('LavenderBlush',CSSLavenderBlush);
    2921   CSSColors.Add('LawnGreen',CSSLawnGreen);
    2922   CSSColors.Add('LemonChiffon',CSSLemonChiffon);
    2923   CSSColors.Add('LightBlue',CSSLightBlue);
    2924   CSSColors.Add('LightCoral',CSSLightCoral);
    2925   CSSColors.Add('LightCyan',CSSLightCyan);
    2926   CSSColors.Add('LightGoldenrodYellow',CSSLightGoldenrodYellow);
    2927   CSSColors.Add('LightGray',CSSLightGray);
    2928   CSSColors.Add('LightGreen',CSSLightGreen);
    2929   CSSColors.Add('LightPink',CSSLightPink);
    2930   CSSColors.Add('LightSalmon',CSSLightSalmon);
    2931   CSSColors.Add('LightSeaGreen',CSSLightSeaGreen);
    2932   CSSColors.Add('LightSkyBlue',CSSLightSkyBlue);
    2933   CSSColors.Add('LightSlateGray',CSSLightSlateGray);
    2934   CSSColors.Add('LightSteelBlue',CSSLightSteelBlue);
    2935   CSSColors.Add('LightYellow',CSSLightYellow);
    2936   CSSColors.Add('Lime',CSSLime);
    2937   CSSColors.Add('LimeGreen',CSSLimeGreen);
    2938   CSSColors.Add('Linen',CSSLinen);
    2939   CSSColors.Add('Magenta',CSSMagenta);
    2940   CSSColors.Add('Maroon',CSSMaroon);
    2941   CSSColors.Add('MediumAquamarine',CSSMediumAquamarine);
    2942   CSSColors.Add('MediumBlue',CSSMediumBlue);
    2943   CSSColors.Add('MediumOrchid',CSSMediumOrchid);
    2944   CSSColors.Add('MediumPurple',CSSMediumPurple);
    2945   CSSColors.Add('MediumSeaGreen',CSSMediumSeaGreen);
    2946   CSSColors.Add('MediumSlateBlue',CSSMediumSlateBlue);
    2947   CSSColors.Add('MediumSpringGreen',CSSMediumSpringGreen);
    2948   CSSColors.Add('MediumTurquoise',CSSMediumTurquoise);
    2949   CSSColors.Add('MediumVioletRed',CSSMediumVioletRed);
    2950   CSSColors.Add('MidnightBlue',CSSMidnightBlue);
    2951   CSSColors.Add('MintCream',CSSMintCream);
    2952   CSSColors.Add('MistyRose',CSSMistyRose);
    2953   CSSColors.Add('Moccasin',CSSMoccasin);
    2954   CSSColors.Add('NavajoWhite',CSSNavajoWhite);
    2955   CSSColors.Add('Navy',CSSNavy);
    2956   CSSColors.Add('OldLace',CSSOldLace);
    2957   CSSColors.Add('Olive',CSSOlive);
    2958   CSSColors.Add('OliveDrab',CSSOliveDrab);
    2959   CSSColors.Add('Orange',CSSOrange);
    2960   CSSColors.Add('OrangeRed',CSSOrangeRed);
    2961   CSSColors.Add('Orchid',CSSOrchid);
    2962   CSSColors.Add('PaleGoldenrod',CSSPaleGoldenrod);
    2963   CSSColors.Add('PaleGreen',CSSPaleGreen);
    2964   CSSColors.Add('PaleTurquoise',CSSPaleTurquoise);
    2965   CSSColors.Add('PaleVioletRed',CSSPaleVioletRed);
    2966   CSSColors.Add('PapayaWhip',CSSPapayaWhip);
    2967   CSSColors.Add('PeachPuff',CSSPeachPuff);
    2968   CSSColors.Add('Peru',CSSPeru);
    2969   CSSColors.Add('Pink',CSSPink);
    2970   CSSColors.Add('Plum',CSSPlum);
    2971   CSSColors.Add('PowderBlue',CSSPowderBlue);
    2972   CSSColors.Add('Purple',CSSPurple);
    2973   CSSColors.Add('Red',CSSRed);
    2974   CSSColors.Add('RosyBrown',CSSRosyBrown);
    2975   CSSColors.Add('RoyalBlue',CSSRoyalBlue);
    2976   CSSColors.Add('SaddleBrown',CSSSaddleBrown);
    2977   CSSColors.Add('Salmon',CSSSalmon);
    2978   CSSColors.Add('SandyBrown',CSSSandyBrown);
    2979   CSSColors.Add('SeaGreen',CSSSeaGreen);
    2980   CSSColors.Add('Seashell',CSSSeashell);
    2981   CSSColors.Add('Sienna',CSSSienna);
    2982   CSSColors.Add('Silver',CSSSilver);
    2983   CSSColors.Add('SkyBlue',CSSSkyBlue);
    2984   CSSColors.Add('SlateBlue',CSSSlateBlue);
    2985   CSSColors.Add('SlateGray',CSSSlateGray);
    2986   CSSColors.Add('Snow',CSSSnow);
    2987   CSSColors.Add('SpringGreen',CSSSpringGreen);
    2988   CSSColors.Add('SteelBlue',CSSSteelBlue);
    2989   CSSColors.Add('Tan',CSSTan);
    2990   CSSColors.Add('Teal',CSSTeal);
    2991   CSSColors.Add('Thistle',CSSThistle);
    2992   CSSColors.Add('Tomato',CSSTomato);
    2993   CSSColors.Add('Turquoise',CSSTurquoise);
    2994   CSSColors.Add('Violet',CSSViolet);
    2995   CSSColors.Add('Wheat',CSSWheat);
    2996   CSSColors.Add('White',CSSWhite);
    2997   CSSColors.Add('WhiteSmoke',CSSWhiteSmoke);
    2998   CSSColors.Add('Yellow',CSSYellow);
    2999   CSSColors.Add('YellowGreen',CSSYellowGreen);
    3000   CSSColors.Finished;
     4331  {$DEFINE INCLUDE_COLOR_LIST}
     4332  {$I csscolorconst.inc}
     4333  DefaultBGRAImageWriter[ifJpeg] := TFPWriterJPEG;
     4334  DefaultBGRAImageWriter[ifPng] := TFPWriterPNG;
     4335  DefaultBGRAImageWriter[ifBmp] := TFPWriterBMP;
     4336  DefaultBGRAImageWriter[ifPcx] := TFPWriterPCX;
     4337  DefaultBGRAImageWriter[ifTarga] := TFPWriterTarga;
     4338  DefaultBGRAImageWriter[ifXPixMap] := TFPWriterXPM;
     4339  DefaultBGRAImageWriter[ifTiff] := TFPWriterTiff;
     4340  //writing XWD not implemented
     4341
     4342  DefaultBGRAImageReader[ifTiff] := TFPReaderTiff;
     4343  DefaultBGRAImageReader[ifXwd] := TFPReaderXWD;
     4344  //the other readers are registered by their unit
    30014345
    30024346finalization
    30034347
    30044348  CSSColors.Free;
     4349  VGAColors.Free;
    30054350
    30064351end.
  • GraphicTest/Packages/bgrabitmap/bgrablend.pas

    r452 r472  
    2626
    2727{ Draw a series of pixels with alpha blending }
     28procedure PutPixels(pdest: PBGRAPixel; psource: PBGRAPixel; copycount: integer; mode: TDrawMode; AOpacity:byte);
    2829procedure DrawPixelsInline(dest: PBGRAPixel; c: TBGRAPixel; Count: integer); inline; overload;
    2930procedure DrawExpandedPixelsInline(dest: PBGRAPixel; ec: TExpandedPixel; Count: integer); inline; overload;
     
    238239end;
    239240
     241procedure PutPixels(pdest: PBGRAPixel; psource: PBGRAPixel; copycount: integer;
     242  mode: TDrawMode; AOpacity: byte);
     243var i: integer; tempPixel: TBGRAPixel;
     244begin
     245  case mode of
     246    dmSet:
     247    begin
     248      if AOpacity <> 255 then
     249          CopyPixelsWithOpacity(pdest, psource, AOpacity, copycount)
     250      else
     251      begin
     252        copycount *= sizeof(TBGRAPixel);
     253        move(psource^, pdest^, copycount);
     254      end;
     255    end;
     256    dmSetExceptTransparent:
     257    begin
     258        if AOpacity <> 255 then
     259        begin
     260          for i := copycount - 1 downto 0 do
     261          begin
     262            if psource^.alpha = 255 then
     263            begin
     264              tempPixel := psource^;
     265              tempPixel.alpha := ApplyOpacity(tempPixel.alpha,AOpacity);
     266              FastBlendPixelInline(pdest,tempPixel);
     267            end;
     268            Inc(pdest);
     269            Inc(psource);
     270          end;
     271        end else
     272          for i := copycount - 1 downto 0 do
     273          begin
     274            if psource^.alpha = 255 then
     275              pdest^ := psource^;
     276            Inc(pdest);
     277            Inc(psource);
     278          end;
     279    end;
     280    dmDrawWithTransparency:
     281    begin
     282        if AOpacity <> 255 then
     283        begin
     284          for i := copycount - 1 downto 0 do
     285          begin
     286            DrawPixelInlineWithAlphaCheck(pdest, psource^, AOpacity);
     287            Inc(pdest);
     288            Inc(psource);
     289          end;
     290        end
     291        else
     292          for i := copycount - 1 downto 0 do
     293          begin
     294            DrawPixelInlineWithAlphaCheck(pdest, psource^);
     295            Inc(pdest);
     296            Inc(psource);
     297          end;
     298    end;
     299    dmFastBlend:
     300    begin
     301        if AOpacity <> 255 then
     302        begin
     303          for i := copycount - 1 downto 0 do
     304          begin
     305            FastBlendPixelInline(pdest, psource^, AOpacity);
     306            Inc(pdest);
     307            Inc(psource);
     308          end;
     309        end else
     310          for i := copycount - 1 downto 0 do
     311          begin
     312            FastBlendPixelInline(pdest, psource^);
     313            Inc(pdest);
     314            Inc(psource);
     315          end;
     316    end;
     317    dmXor:
     318    begin
     319      if AOpacity <> 255 then
     320      begin
     321          for i := copycount - 1 downto 0 do
     322          begin
     323            FastBlendPixelInline(pdest, TBGRAPixel(PDWord(pdest)^ xor PDword(psource)^), AOpacity);
     324            Inc(pdest);
     325            Inc(psource);
     326          end;
     327      end else
     328          XorPixels(pdest, psource, copycount);
     329    end;
     330  end;
     331end;
     332
    240333procedure DrawPixelsInline(dest: PBGRAPixel; c: TBGRAPixel; Count: integer);
    241334var
     
    385478  a1f, a2f, a12, a12m: cardinal;
    386479begin
     480  {$HINTS OFF}
    387481  a12  := 65025 - (not dest^.alpha) * (not c.alpha);
     482  {$HINTS ON}
    388483  a12m := a12 shr 1;
    389484
     
    412507  a1f, a2f, a12, a12m: cardinal;
    413508begin
     509  {$HINTS OFF}
    414510  a12  := 65025 - (not dest^.alpha) * (not calpha);
     511  {$HINTS ON}
    415512  a12m := a12 shr 1;
    416513
     
    446543  end;
    447544
     545  {$HINTS OFF}
    448546  a12  := 65025 - (not dest^.alpha) * (not c.alpha);
     547  {$HINTS ON}
    449548  a12m := a12 shr 1;
    450549
  • GraphicTest/Packages/bgrabitmap/bgracanvas.pas

    r452 r472  
    66
    77uses
    8   Classes, SysUtils, Graphics, GraphType, Types, FPImage, FPCanvas, BGRABitmapTypes;
     8  Classes, SysUtils, FPCanvas, Graphics, GraphType, Types, FPImage, BGRABitmapTypes;
    99
    1010type
     
    229229procedure TBGRAFont.SetAntialiasing(const AValue: Boolean);
    230230begin
    231   if AValue and not Antialiasing then
    232     Quality := fqFineAntialiasing;
     231  if AValue = Antialiasing then exit;
     232  if AValue then
     233    Quality := fqFineAntialiasing
     234  else
     235    Quality := fqSystem;
    233236end;
    234237
     
    279282    if cf.Italic then Style += [fsItalic];
    280283    if cf.Underline then Style += [fsUnderline];
     284{$IF FPC_FULLVERSION>=20602} //changed in 2.6.2 and 2.7   
     285    if cf.StrikeThrough then Style += [fsStrikeOut];
     286{$ELSE}
    281287    if cf.StrikeTrough then Style += [fsStrikeOut];
     288{$ENDIF}
    282289    Name := cf.Name;
    283290    //Orientation := cf.Orientation;
     
    916923    dec(x2);
    917924    dec(y2);
     925
     926    if (Pen.Style = psSolid) and not Filled then
     927    begin
     928      ApplyPenStyle;
     929      FBitmap.RectangleAntialias(x1,y1,x2,y2,Pen.ActualColor,Pen.ActualWidth);
     930      exit;
     931    end;
     932
    918933    tex := Brush.BuildTexture(FBitmap);
    919934
  • GraphicTest/Packages/bgrabitmap/bgracanvas2d.pas

    r452 r472  
    33{ To do :
    44
     5  draw text with a different precision if the matrix is scaled
     6  drawImage(in image, in double sx, in double sy, in double sw, in double sh, in double dx, in double dy, in double dw, in double dh)
     7  -> using FillPoly with texture coordinates
    58  linear gradient any transformation
    69  clearPath clipping
    710  createRadialGradient
    8   text functions
    911  globalCompositeOperation
    10   drawImage(in image, in double sx, in double sy, in double sw, in double sh, in double dx, in double dy, in double dw, in double dh)
    1112  image data functions
    1213}
     
    1718
    1819uses
    19   Classes, SysUtils, Graphics, BGRABitmapTypes, BGRATransform, BGRAGradientScanner;
     20  Classes, SysUtils, Graphics, BGRABitmapTypes, BGRATransform, BGRAGradientScanner, BGRAPath;
    2021
    2122type
     
    4748    globalAlpha: byte;
    4849
     50    fontName: string;
     51    fontStyle: TFontStyles;
     52    fontEmHeight: single;
     53    textAlign: TAlignment;
     54    textBaseline: string;
     55
    4956    lineWidth: single;
    5057    lineCap: TPenEndCap;
     
    5562    shadowOffsetX,shadowOffsetY,shadowBlur: single;
    5663    shadowColor: TBGRAPixel;
     64    shadowFastest: boolean;
    5765
    5866    matrix: TAffineMatrix;
     
    6371  end;
    6472
     73  TCanvas2dTextSize = record
     74    width,height: single;
     75  end;
     76
    6577  { TBGRACanvas2D }
    6678
    67   TBGRACanvas2D = class
     79  TBGRACanvas2D = class(IBGRAPath)
    6880  private
    6981    FSurface: TBGRACustomBitmap;
     
    7486    FPathPoints: array of TPointF;
    7587    FPathPointCount: integer;
     88    FFontRenderer: TBGRACustomFontRenderer;
     89    FLastCoord, FStartCoord: TPointF;
     90    function GetCurrentPath: ArrayOfTPointF;
     91    function GetFontName: string;
     92    function GetFontRenderer: TBGRACustomFontRenderer;
     93    function GetFontEmHeight: single;
     94    function GetFontString: string;
     95    function GetFontStyle: TFontStyles;
    7696    function GetGlobalAlpha: single;
    7797    function GetHasShadow: boolean;
    7898    function GetHeight: Integer;
    7999    function GetLineCap: string;
     100    function GetLineCapLCL: TPenEndCap;
    80101    function GetlineJoin: string;
     102    function GetlineJoinLCL: TPenJoinStyle;
    81103    function GetLineWidth: single;
     104    function GetMatrix: TAffineMatrix;
    82105    function GetMiterLimit: single;
    83106    function GetPixelCenteredCoordinates: boolean;
    84107    function GetShadowBlur: single;
     108    function GetShadowFastest: boolean;
    85109    function GetShadowOffset: TPointF;
    86110    function GetShadowOffsetX: single;
    87111    function GetShadowOffsetY: single;
     112    function GetTextAlign: string;
     113    function GetTextAlignLCL: TAlignment;
     114    function GetTextBaseline: string;
    88115    function GetWidth: Integer;
     116    procedure SetFontName(AValue: string);
     117    procedure SetFontRenderer(AValue: TBGRACustomFontRenderer);
     118    procedure SetFontEmHeight(AValue: single);
     119    procedure SetFontString(AValue: string);
     120    procedure SetFontStyle(AValue: TFontStyles);
    89121    procedure SetGlobalAlpha(const AValue: single);
    90122    procedure SetLineCap(const AValue: string);
     123    procedure SetLineCapLCL(AValue: TPenEndCap);
    91124    procedure SetLineJoin(const AValue: string);
    92125    procedure FillPoly(const points: array of TPointF);
    93126    procedure FillStrokePoly(const points: array of TPointF; fillOver: boolean);
     127    procedure SetLineJoinLCL(AValue: TPenJoinStyle);
    94128    procedure SetLineWidth(const AValue: single);
     129    procedure SetMatrix(AValue: TAffineMatrix);
    95130    procedure SetMiterLimit(const AValue: single);
    96131    procedure SetPixelCenteredCoordinates(const AValue: boolean);
    97132    procedure SetShadowBlur(const AValue: single);
     133    procedure SetShadowFastest(AValue: boolean);
    98134    procedure SetShadowOffset(const AValue: TPointF);
    99135    procedure SetShadowOffsetX(const AValue: single);
    100136    procedure SetShadowOffsetY(const AValue: single);
     137    procedure SetTextAlign(AValue: string);
     138    procedure SetTextAlignLCL(AValue: TAlignment);
     139    procedure SetTextBaseine(AValue: string);
    101140    procedure StrokePoly(const points: array of TPointF);
    102141    procedure DrawShadow(const points, points2: array of TPointF);
     
    105144    function ApplyTransform(const points: array of TPointF): ArrayOfTPointF; overload;
    106145    function ApplyTransform(point: TPointF): TPointF; overload;
    107     function GetPenPos: TPointF;
     146    function GetPenPos(defaultX, defaultY: single): TPointF;
     147    function GetPenPos(defaultPt: TPointF): TPointF;
    108148    procedure AddPoint(point: TPointF);
    109149    procedure AddPoints(const points: array of TPointF);
    110150    procedure AddPointsRev(const points: array of TPointF);
    111151    function ApplyGlobalAlpha(color: TBGRAPixel): TBGRAPixel;
     152    function GetDrawMode: TDrawMode;
     153    procedure copyTo({%H-}dest: IBGRAPath); //IBGRAPath
    112154  public
     155    antialiasing, linearBlend: boolean;
    113156    constructor Create(ASurface: TBGRACustomBitmap);
    114157    destructor Destroy; override;
     
    118161    procedure save;
    119162    procedure restore;
    120     procedure scale(x,y: single);
    121     procedure rotate(angleRad: single);
     163    procedure scale(x,y: single); overload;
     164    procedure scale(factor: single); overload;
     165    procedure rotate(angleRadCW: single);
    122166    procedure translate(x,y: single);
    123     procedure transform(a,b,c,d,e,f: single);
     167    procedure transform(a,b,c,d,e,f: single); overload;
     168    procedure transform(AMatrix: TAffineMatrix); overload;
    124169    procedure setTransform(a,b,c,d,e,f: single);
    125170    procedure resetTransform;
     
    137182    procedure shadowColor(color: TColor); overload;
    138183    procedure shadowColor(color: string); overload;
     184    procedure shadowNone;
    139185    function getShadowColor: TBGRAPixel;
    140186    function createLinearGradient(x0,y0,x1,y1: single): IBGRACanvasGradient2D; overload;
     
    149195    procedure clearRect(x,y,w,h: single);
    150196
     197    procedure addPath(APath: IBGRAPath); overload;
     198    procedure addPath(ASvgPath: string); overload;
     199    procedure path(APath: IBGRAPath); overload;
     200    procedure path(ASvgPath: string); overload;
    151201    procedure beginPath;
    152202    procedure closePath;
     
    154204    procedure moveTo(x,y: single); overload;
    155205    procedure lineTo(x,y: single); overload;
    156     procedure moveTo(pt: TPointF); overload;
    157     procedure lineTo(pt: TPointF); overload;
     206    procedure moveTo(const pt: TPointF); overload;
     207    procedure lineTo(const pt: TPointF); overload;
    158208    procedure polylineTo(const pts: array of TPointF);
    159209    procedure quadraticCurveTo(cpx,cpy,x,y: single); overload;
    160     procedure quadraticCurveTo(cp,pt: TPointF); overload;
     210    procedure quadraticCurveTo(const cp,pt: TPointF); overload;
    161211    procedure bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y: single); overload;
    162     procedure bezierCurveTo(cp1,cp2,pt: TPointF); overload;
     212    procedure bezierCurveTo(const cp1,cp2,pt: TPointF); overload;
    163213    procedure rect(x,y,w,h: single);
    164     procedure roundRect(x,y,w,h,radius: single);
     214    procedure roundRect(x,y,w,h,radius: single); overload;
     215    procedure roundRect(x,y,w,h,rx,ry: single); overload;
    165216    procedure spline(const pts: array of TPointF; style: TSplineStyle= ssOutside);
    166217    procedure splineTo(const pts: array of TPointF; style: TSplineStyle= ssOutside);
    167     procedure arc(x, y, radius, startAngle, endAngle: single; anticlockwise: boolean); overload;
    168     procedure arc(x, y, radius, startAngle, endAngle: single); overload;
     218    procedure arc(x, y, radius, startAngleRadCW, endAngleRadCW: single; anticlockwise: boolean); overload;
     219    procedure arc(x, y, radius, startAngleRadCW, endAngleRadCW: single); overload;
     220    procedure arc(cx, cy, rx,ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single; anticlockwise: boolean); overload;
     221    procedure arc(cx, cy, rx,ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single); overload;
     222    procedure arc(const arcDef: TArcDef); overload;
    169223    procedure arcTo(x1, y1, x2, y2, radius: single); overload;
    170224    procedure arcTo(p1,p2: TPointF; radius: single); overload;
     225    procedure arcTo(rx, ry, xAngleRadCW: single; largeArc,anticlockwise: boolean; x, y: single);
     226    procedure circle(x,y,r: single);
     227    procedure ellipse(x,y,rx,ry: single);
     228    procedure text(AText: string; x,y: single);
     229    procedure fillText(AText: string; x,y: single);
     230    procedure strokeText(AText: string; x,y: single);
     231    function measureText(AText: string): TCanvas2dTextSize;
     232
    171233    procedure fill;
    172234    procedure stroke;
     
    183245
    184246    function getLineStyle: TBGRAPenStyle;
    185     procedure lineStyle(const AValue: array of single);
     247    procedure lineStyle(const AValue: array of single); overload;
     248    procedure lineStyle(AStyle: TPenStyle); overload;
    186249
    187250    property surface: TBGRACustomBitmap read FSurface;
     
    190253    property pixelCenteredCoordinates: boolean read GetPixelCenteredCoordinates write SetPixelCenteredCoordinates;
    191254    property globalAlpha: single read GetGlobalAlpha write SetGlobalAlpha;
     255    property matrix: TAffineMatrix read GetMatrix write SetMatrix;
    192256
    193257    property lineWidth: single read GetLineWidth write SetLineWidth;
    194258    property lineCap: string read GetLineCap write SetLineCap;
     259    property lineCapLCL: TPenEndCap read GetLineCapLCL write SetLineCapLCL;
    195260    property lineJoin: string read GetlineJoin write SetLineJoin;
     261    property lineJoinLCL: TPenJoinStyle read GetlineJoinLCL write SetLineJoinLCL;
    196262    property miterLimit: single read GetMiterLimit write SetMiterLimit;
    197263
     
    200266    property shadowOffset: TPointF read GetShadowOffset write SetShadowOffset;
    201267    property shadowBlur: single read GetShadowBlur write SetShadowBlur;
     268    property shadowFastest: boolean read GetShadowFastest write SetShadowFastest;
    202269    property hasShadow: boolean read GetHasShadow;
     270
     271    property fontName: string read GetFontName write SetFontName;
     272    property fontEmHeight: single read GetFontEmHeight write SetFontEmHeight;
     273    property fontStyle: TFontStyles read GetFontStyle write SetFontStyle;
     274    property font: string read GetFontString write SetFontString;
     275    property textAlignLCL: TAlignment read GetTextAlignLCL write SetTextAlignLCL;
     276    property textAlign: string read GetTextAlign write SetTextAlign;
     277    property textBaseline: string read GetTextBaseline write SetTextBaseine;
     278
     279    property currentPath: ArrayOfTPointF read GetCurrentPath;
     280    property fontRenderer: TBGRACustomFontRenderer read GetFontRenderer write SetFontRenderer;
     281
     282  protected
     283    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};
     284    function _AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
     285    function _Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    203286  end;
    204287
    205288implementation
    206289
    207 uses Math, BGRAPen, BGRAFillInfo, BGRAPolygon, BGRABlend, FPWriteJPEG, FPWriteBMP, base64;
     290uses Types, Math, BGRAPen, BGRAFillInfo, BGRAPolygon, BGRABlend, FPWriteJPEG, FPWriteBMP, base64;
    208291
    209292type
     
    446529  globalAlpha := 255;
    447530
     531  fontName := 'Arial';
     532  fontEmHeight := 10;
     533  fontStyle := [];
     534  textAlign:= taLeftJustify;
     535  textBaseline := 'alphabetic';
     536
    448537  lineWidth := 1;
    449538  lineCap := pecFlat;
     
    456545  shadowBlur := 0;
    457546  shadowColor := BGRAPixelTransparent;
     547  shadowFastest:= false;
    458548
    459549  matrix := AMatrix;
     
    473563  result.globalAlpha := globalAlpha;
    474564
     565  result.fontName:= fontName;
     566  result.fontEmHeight := fontEmHeight;
     567  result.fontStyle := fontStyle;
     568
    475569  result.lineWidth := lineWidth;
    476570  result.lineCap := lineCap;
     
    483577  result.shadowBlur := shadowBlur;
    484578  result.shadowColor := shadowColor;
     579  result.shadowFastest := shadowFastest;
    485580end;
    486581
     
    495590function TBGRACanvas2D.GetHeight: Integer;
    496591begin
    497   result := Surface.Height;
     592  if Assigned(surface) then
     593    result := Surface.Height
     594  else
     595    result := 0;
    498596end;
    499597
     
    507605end;
    508606
     607function TBGRACanvas2D.GetLineCapLCL: TPenEndCap;
     608begin
     609  result := currentState.lineCap;
     610end;
     611
    509612function TBGRACanvas2D.GetlineJoin: string;
    510613begin
     
    516619end;
    517620
     621function TBGRACanvas2D.GetlineJoinLCL: TPenJoinStyle;
     622begin
     623  result := currentState.lineJoin;
     624end;
     625
    518626function TBGRACanvas2D.getLineStyle: TBGRAPenStyle;
    519627begin
     
    526634end;
    527635
     636function TBGRACanvas2D.GetMatrix: TAffineMatrix;
     637begin
     638  result := currentState.matrix;
     639end;
     640
    528641function TBGRACanvas2D.GetMiterLimit: single;
    529642begin
     
    541654end;
    542655
     656function TBGRACanvas2D.GetShadowFastest: boolean;
     657begin
     658  result := currentState.shadowFastest;
     659end;
     660
    543661function TBGRACanvas2D.GetShadowOffset: TPointF;
    544662begin
     
    556674end;
    557675
     676function TBGRACanvas2D.GetTextAlign: string;
     677begin
     678  case currentState.textAlign of
     679    taRightJustify: result := 'right';
     680    taCenter: result := 'center';
     681  else
     682    result := 'left';
     683  end;
     684end;
     685
     686function TBGRACanvas2D.GetTextAlignLCL: TAlignment;
     687begin
     688  result := currentState.textAlign;
     689end;
     690
     691function TBGRACanvas2D.GetTextBaseline: string;
     692begin
     693  result := currentState.textBaseline;
     694end;
     695
    558696function TBGRACanvas2D.GetGlobalAlpha: single;
    559697begin
    560698  result := currentState.globalAlpha/255;
     699end;
     700
     701function TBGRACanvas2D.GetCurrentPath: ArrayOfTPointF;
     702var i: integer;
     703begin
     704  setlength(result, FPathPointCount);
     705  for i := 0 to high(result) do
     706    result[i] := FPathPoints[i];
     707end;
     708
     709function TBGRACanvas2D.GetFontName: string;
     710begin
     711  result := currentState.fontName;
     712end;
     713
     714function TBGRACanvas2D.GetFontRenderer: TBGRACustomFontRenderer;
     715var zoom1,zoom2,zoom: single;
     716begin
     717  if FFontRenderer = nil then
     718  begin
     719    if FSurface <> nil then
     720      result := FSurface.FontRenderer
     721    else
     722      result := nil;
     723  end else
     724    result := FFontRenderer;
     725  if Assigned(result) then
     726  begin
     727    result.FontName := currentState.fontName;
     728    result.FontStyle := currentState.fontStyle;
     729    if antialiasing then
     730      result.FontQuality:= fqFineAntialiasing
     731    else
     732      result.FontQuality := fqSystem;
     733    result.FontOrientation := 0;
     734    zoom1 := VectLen(currentState.matrix[1,1],currentState.matrix[2,1]);
     735    zoom2 := VectLen(currentState.matrix[1,2],currentState.matrix[2,2]);
     736    if zoom1>zoom2 then zoom := zoom1 else zoom := zoom2;
     737    result.FontEmHeight := round(currentState.fontEmHeight*zoom);
     738  end;
     739end;
     740
     741function TBGRACanvas2D.GetFontEmHeight: single;
     742begin
     743  result := currentState.fontEmHeight;
     744end;
     745
     746function TBGRACanvas2D.GetFontString: string;
     747var formats: TFormatSettings;
     748begin
     749  formats := DefaultFormatSettings;
     750  formats.DecimalSeparator := '.';
     751
     752  result := '';
     753  if fsItalic in currentState.fontStyle then
     754    result := result+'italic ';
     755  if fsBold in currentState.fontStyle then
     756    result += 'bold ';
     757  result += FloatToStrF(currentState.fontEmHeight,ffGeneral,6,0,formats)+'px ';
     758  result += currentState.fontName;
     759  result := trim(result);
     760end;
     761
     762function TBGRACanvas2D.GetFontStyle: TFontStyles;
     763begin
     764  result := currentState.fontStyle;
    561765end;
    562766
     
    570774function TBGRACanvas2D.GetWidth: Integer;
    571775begin
    572   result := Surface.Width;
     776  if Assigned(Surface) then
     777    result := Surface.Width
     778  else
     779    result := 0;
     780end;
     781
     782procedure TBGRACanvas2D.SetFontName(AValue: string);
     783begin
     784  currentState.fontName := AValue;
     785end;
     786
     787procedure TBGRACanvas2D.SetFontRenderer(AValue: TBGRACustomFontRenderer);
     788begin
     789  if AValue = FFontRenderer then exit;
     790  FreeAndNil(FFontRenderer);
     791  FFontRenderer := AValue;
     792end;
     793
     794procedure TBGRACanvas2D.SetFontEmHeight(AValue: single);
     795begin
     796  currentState.fontEmHeight := AValue;
     797end;
     798
     799procedure TBGRACanvas2D.SetFontString(AValue: string);
     800var idxSpace,errPos: integer;
     801  attrib,u: string;
     802  value: single;
     803begin
     804  currentState.fontStyle := [];
     805  currentState.fontEmHeight := 10;
     806  currentState.fontName := 'Arial';
     807  AValue := trim(AValue);
     808  while AValue <> '' do
     809  begin
     810    while (AValue <> '') and (AValue[1]in [#0..#32]) do delete(AValue,1,1);
     811    idxSpace := pos(' ',AValue);
     812    if idxSpace = 0 then
     813      attrib := AValue
     814    else
     815      attrib := copy(AValue,1,idxSpace-1);
     816    attrib := lowerCase(attrib);
     817    if attrib = '' then break;
     818    if (attrib = 'normal') or (attrib = 'small-caps') or (attrib = 'lighter') then
     819    begin
     820      //nothing
     821    end else
     822    if (attrib = 'italic') or (attrib = 'oblique') then
     823    begin
     824      currentState.fontStyle += [fsItalic];
     825    end else
     826    if (attrib = 'bold') or (attrib = 'bolder') then
     827    begin
     828      currentState.fontStyle += [fsBold];
     829    end else
     830    if (attrib[1] in ['.','0'..'9']) then
     831    begin
     832      u := '';
     833      while (length(attrib)>0) and (attrib[length(attrib)] in['a'..'z']) do
     834      begin
     835        u := attrib[length(attrib)]+u;
     836        delete(attrib,length(attrib),1);
     837      end;
     838      val(attrib,value,errPos);
     839      if errPos = 0 then
     840      begin
     841        if u = '' then //weight
     842        begin
     843          if value >= 600 then currentState.fontStyle += [fsBold];
     844        end else
     845        if u = 'px' then currentState.fontEmHeight := value else
     846        if u = 'pt' then currentState.fontEmHeight:= value/72*96 else
     847        if u = 'in' then currentState.fontEmHeight:= value*96 else
     848        if u = 'mm' then currentState.fontEmHeight:= value/25.4*96 else
     849        if u = 'cm' then currentState.fontEmHeight:= value/2.54*96;
     850      end;
     851    end else
     852      break;
     853    delete(AValue,1,length(attrib)+1);
     854  end;
     855  AValue := trim(AValue);
     856  if AValue <> '' then currentState.fontName := AValue;
     857end;
     858
     859procedure TBGRACanvas2D.SetFontStyle(AValue: TFontStyles);
     860begin
     861  currentState.fontStyle:= AValue;
    573862end;
    574863
     
    590879end;
    591880
     881procedure TBGRACanvas2D.SetLineCapLCL(AValue: TPenEndCap);
     882begin
     883  currentState.lineCap := AValue;
     884end;
     885
    592886procedure TBGRACanvas2D.SetLineJoin(const AValue: string);
    593887begin
     
    604898  tempScan: TBGRACustomScanner;
    605899begin
    606   if length(points) = 0 then exit;
     900  if (length(points) = 0) or (surface = nil) then exit;
    607901  If hasShadow then DrawShadow(points,[]);
    608902  if currentState.clipMask <> nil then
     
    612906    else
    613907      tempScan := TBGRASolidColorMaskScanner.Create(currentState.clipMask,Point(0,0),ApplyGlobalAlpha(currentState.fillColor));
    614     BGRAPolygon.FillPolyAntialiasWithTexture(surface, points, tempScan, true);
     908    if self.antialiasing then
     909      BGRAPolygon.FillPolyAntialiasWithTexture(surface, points, tempScan, true, linearBlend)
     910    else
     911      BGRAPolygon.FillPolyAliasedWithTexture(surface, points, tempScan, true, GetDrawMode);
    615912    tempScan.free;
    616913  end else
     
    621918      begin
    622919        tempScan := TBGRAOpacityScanner.Create(currentState.fillTextureProvider.texture, currentState.globalAlpha);
    623         BGRAPolygon.FillPolyAntialiasWithTexture(surface, points, tempScan, true);
     920        if self.antialiasing then
     921          BGRAPolygon.FillPolyAntialiasWithTexture(surface, points, tempScan, true, linearBlend)
     922        else
     923          BGRAPolygon.FillPolyAliasedWithTexture(surface, points, tempScan, true, GetDrawMode);
    624924        tempScan.Free;
    625925      end else
    626         BGRAPolygon.FillPolyAntialiasWithTexture(surface, points, currentState.fillTextureProvider.texture, true)
     926      begin
     927        if self.antialiasing then
     928          BGRAPolygon.FillPolyAntialiasWithTexture(surface, points, currentState.fillTextureProvider.texture, true, linearBlend)
     929        else
     930          BGRAPolygon.FillPolyAliasedWithTexture(surface, points, currentState.fillTextureProvider.texture, true, GetDrawMode);
     931      end
    627932    end
    628933    else
    629       BGRAPolygon.FillPolyAntialias(surface, points, ApplyGlobalAlpha(currentState.fillColor), false, true);
     934    begin
     935      if self.antialiasing then
     936        BGRAPolygon.FillPolyAntialias(surface, points, ApplyGlobalAlpha(currentState.fillColor), false, true, linearBlend)
     937      else
     938        BGRAPolygon.FillPolyAliased(surface, points, ApplyGlobalAlpha(currentState.fillColor), false, true, GetDrawMode)
     939    end
    630940  end;
    631941end;
     
    639949  texture: IBGRAScanner;
    640950begin
    641   if length(points) = 0 then exit;
     951  if (length(points) = 0) or (surface = nil) then exit;
    642952  tempScan := nil;
    643953  tempScan2 := nil;
     
    6931003
    6941004  if fillOver then multi.PolygonOrder := poFirstOnTop else multi.PolygonOrder:= poLastOnTop;
     1005  multi.Antialiasing := self.antialiasing;
    6951006  multi.Draw(surface);
    6961007  tempScan.free;
     
    6991010end;
    7001011
     1012procedure TBGRACanvas2D.SetLineJoinLCL(AValue: TPenJoinStyle);
     1013begin
     1014  currentState.lineJoin := AValue;
     1015end;
     1016
    7011017procedure TBGRACanvas2D.lineStyle(const AValue: array of single);
    7021018begin
     
    7041020end;
    7051021
     1022procedure TBGRACanvas2D.lineStyle(AStyle: TPenStyle);
     1023begin
     1024  case AStyle of
     1025    psSolid: lineStyle(SolidPenStyle);
     1026    psDash: lineStyle(DashPenStyle);
     1027    psDot: lineStyle(DotPenStyle);
     1028    psDashDot: lineStyle(DashDotPenStyle);
     1029    psDashDotDot: lineStyle(DashDotDotPenStyle);
     1030    psClear: lineStyle(ClearPenStyle);
     1031  end;
     1032end;
     1033
     1034function TBGRACanvas2D.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};
     1035begin
     1036  if GetInterface(iid, obj) then
     1037    Result := S_OK
     1038  else
     1039    Result := longint(E_NOINTERFACE);
     1040end;
     1041
     1042{ There is no automatic reference counting, but it is compulsory to define these functions }
     1043function TBGRACanvas2D._AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
     1044begin
     1045  result := 0;
     1046end;
     1047
     1048function TBGRACanvas2D._Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
     1049begin
     1050  result := 0;
     1051end;
     1052
    7061053procedure TBGRACanvas2D.SetLineWidth(const AValue: single);
    7071054begin
    7081055  currentState.lineWidth := AValue;
     1056end;
     1057
     1058procedure TBGRACanvas2D.SetMatrix(AValue: TAffineMatrix);
     1059begin
     1060  currentState.matrix := AValue;
    7091061end;
    7101062
     
    7281080end;
    7291081
     1082procedure TBGRACanvas2D.SetShadowFastest(AValue: boolean);
     1083begin
     1084  currentState.shadowFastest := AValue;
     1085end;
     1086
    7301087procedure TBGRACanvas2D.SetShadowOffset(const AValue: TPointF);
    7311088begin
     
    7421099begin
    7431100  currentState.shadowOffsetY := AValue;
     1101end;
     1102
     1103procedure TBGRACanvas2D.SetTextAlign(AValue: string);
     1104begin
     1105  AValue := trim(LowerCase(AValue));
     1106  if (AValue = 'left') or (AValue = 'start') then
     1107    textAlignLCL := taLeftJustify else
     1108  if (AValue = 'right') or (AValue = 'end') then
     1109    textAlignLCL := taRightJustify else
     1110  if AValue = 'center' then
     1111    textAlignLCL := taCenter;
     1112end;
     1113
     1114procedure TBGRACanvas2D.SetTextAlignLCL(AValue: TAlignment);
     1115begin
     1116  currentState.textAlign := AValue;
     1117end;
     1118
     1119procedure TBGRACanvas2D.SetTextBaseine(AValue: string);
     1120begin
     1121  currentState.textBaseline := trim(lowercase(AValue));
    7441122end;
    7451123
     
    7501128  contour: array of TPointF;
    7511129begin
    752   if (length(points)= 0) or (currentState.lineWidth = 0) then exit;
     1130  if (length(points)= 0) or (currentState.lineWidth = 0) or (surface = nil) then exit;
    7531131  contour := ComputeWidePolylinePoints(points,currentState.lineWidth,BGRAPixelTransparent,
    7541132      currentState.lineCap,currentState.lineJoin,currentState.lineStyle,[plAutoCycle],miterLimit);
     
    7611139    else
    7621140      tempScan := TBGRASolidColorMaskScanner.Create(currentState.clipMask,Point(0,0),ApplyGlobalAlpha(currentState.strokeColor));
    763     BGRAPolygon.FillPolyAntialiasWithTexture(Surface,contour,tempScan,True);
     1141    if self.antialiasing then
     1142      BGRAPolygon.FillPolyAntialiasWithTexture(Surface,contour,tempScan,True, linearBlend)
     1143    else
     1144      BGRAPolygon.FillPolyAliasedWithTexture(Surface,contour,tempScan,True,GetDrawMode);
    7641145    tempScan.free;
    7651146  end else
     
    7691150      texture := nil;
    7701151    if texture = nil then
    771       BGRAPolygon.FillPolyAntialias(Surface,contour,ApplyGlobalAlpha(currentState.strokeColor),false,True)
     1152    begin
     1153      if self.antialiasing then
     1154        BGRAPolygon.FillPolyAntialias(Surface,contour,ApplyGlobalAlpha(currentState.strokeColor),false,True, linearBlend)
     1155      else
     1156        BGRAPolygon.FillPolyAliased(Surface,contour,ApplyGlobalAlpha(currentState.strokeColor),false,True,GetDrawMode)
     1157    end
    7721158    else
    773       BGRAPolygon.FillPolyAntialiasWithTexture(Surface,contour,texture,True);
     1159    begin
     1160      if self.antialiasing then
     1161        BGRAPolygon.FillPolyAntialiasWithTexture(Surface,contour,texture,True, linearBlend)
     1162      else
     1163        BGRAPolygon.FillPolyAliasedWithTexture(Surface,contour,texture,True,GetDrawMode)
     1164    end;
    7741165  end;
    7751166end;
    7761167
    7771168procedure TBGRACanvas2D.DrawShadow(const points, points2: array of TPointF);
     1169const invSqrt2 = 1/sqrt(2);
    7781170var ofsPts,ofsPts2: array of TPointF;
    7791171    offset: TPointF;
    7801172    i: Integer;
    7811173    tempBmp,blurred: TBGRACustomBitmap;
    782 begin
    783   if not hasShadow then exit;
     1174    maxRect: TRect;
     1175    foundRect: TRect;
     1176    firstFound: boolean;
     1177
     1178    procedure AddPt(const coord: TPointF);
     1179    var pixRect: TRect;
     1180    begin
     1181      if isEmptyPointF(coord) then exit;
     1182      pixRect := Types.Rect(round(floor(coord.x)),round(floor(coord.y)),round(ceil(coord.x+0.999))+1,round(ceil(coord.y+0.999))+1);
     1183      if firstFound then
     1184      begin
     1185        foundRect := pixRect;
     1186        firstFound := false
     1187      end
     1188      else
     1189      begin
     1190        if pixRect.left < foundRect.left then foundRect.left := pixRect.Left;
     1191        if pixRect.top < foundRect.top then foundRect.top := pixRect.top;
     1192        if pixRect.right > foundRect.right then foundRect.right := pixRect.right;
     1193        if pixRect.bottom > foundRect.bottom then foundRect.bottom := pixRect.bottom;
     1194      end;
     1195    end;
     1196
     1197begin
     1198  if not hasShadow or (surface = nil) then exit;
    7841199  offset := PointF(shadowOffsetX,shadowOffsetY);
    7851200  setlength(ofsPts, length(points));
     
    7891204  for i := 0 to high(ofsPts2) do
    7901205    ofsPts2[i] := points2[i]+offset;
    791   tempBmp := surface.NewBitmap(width,height,BGRAPixelTransparent);
     1206
     1207  maxRect := Types.Rect(0,0,width,height);
     1208  if currentState.clipMask <> nil then
     1209    foundRect := maxRect
     1210  else
     1211  begin
     1212    firstFound := true;
     1213    for i := 0 to high(ofsPts) do
     1214      AddPt(ofsPts[i]);
     1215    for i := 0 to high(ofsPts2) do
     1216      AddPt(ofsPts2[i]);
     1217    if firstFound then exit;
     1218    InflateRect(foundRect, ceil(shadowBlur),ceil(shadowBlur));
     1219    if not IntersectRect(foundRect, foundRect,maxRect) then exit;
     1220    offset := PointF(-foundRect.Left,-foundRect.Top);
     1221    for i := 0 to high(ofsPts) do
     1222      ofsPts[i] += offset;
     1223    for i := 0 to high(ofsPts2) do
     1224      ofsPts2[i] += offset;
     1225  end;
     1226
     1227  tempBmp := surface.NewBitmap(foundRect.Right-foundRect.Left,foundRect.Bottom-foundRect.Top,BGRAPixelTransparent);
    7921228  tempBmp.FillMode := fmWinding;
    7931229  tempBmp.FillPolyAntialias(ofsPts, getShadowColor);
     
    7951231  if shadowBlur > 0 then
    7961232  begin
    797     if (shadowBlur < 5) and (abs(shadowBlur-round(shadowBlur)) > 1e-6) then
    798       blurred := tempBmp.FilterBlurRadial(round(shadowBlur*10),rbPrecise)
     1233    if shadowFastest then
     1234    begin
     1235      if shadowBlur*invSqrt2 >= 0.5 then
     1236      begin
     1237        blurred := tempBmp.FilterBlurRadial(round(shadowBlur*invSqrt2),rbBox);
     1238        tempBmp.Free;
     1239        tempBmp := blurred;
     1240      end;
     1241    end
    7991242    else
    800       blurred := tempBmp.FilterBlurRadial(round(shadowBlur),rbFast);
    801     tempBmp.Free;
    802     tempBmp := blurred;
     1243    begin
     1244      if (shadowBlur < 5) and (abs(shadowBlur-round(shadowBlur)) > 1e-6) then
     1245        blurred := tempBmp.FilterBlurRadial(round(shadowBlur*10),rbPrecise)
     1246      else
     1247        blurred := tempBmp.FilterBlurRadial(round(shadowBlur),rbFast);
     1248      tempBmp.Free;
     1249      tempBmp := blurred;
     1250    end;
    8031251  end;
    8041252  if currentState.clipMask <> nil then
    8051253    tempBmp.ApplyMask(currentState.clipMask);
    806   surface.PutImage(0,0,tempBmp,dmDrawWithTransparency,currentState.globalAlpha);
     1254  surface.PutImage(foundRect.Left,foundRect.Top,tempBmp,GetDrawMode,currentState.globalAlpha);
    8071255  tempBmp.Free;
    8081256end;
     
    8101258procedure TBGRACanvas2D.ClearPoly(const points: array of TPointF);
    8111259begin
    812   BGRAPolygon.FillPolyAntialias(surface, points, BGRA(0,0,0,255), true, true)
     1260  if surface = nil then exit;
     1261  if self.antialiasing then
     1262    BGRAPolygon.FillPolyAntialias(surface, points, BGRA(0,0,0,255), true, true, linearBlend)
     1263  else
     1264    BGRAPolygon.FillPolyAliased(surface, points, BGRA(0,0,0,255), true, true, dmSet);
    8131265end;
    8141266
     
    8441296end;
    8451297
    846 function TBGRACanvas2D.GetPenPos: TPointF;
    847 begin
    848   if FPathPointCount = 0 then
    849     result := PointF(0,0)
     1298function TBGRACanvas2D.GetPenPos(defaultX,defaultY: single): TPointF;
     1299begin
     1300  if isEmptyPointF(FLastCoord) then
     1301    result := PointF(defaultX,defaultY)
    8501302  else
    851     result := FPathPoints[FPathPointCount-1];
     1303    result := FLastCoord;
     1304end;
     1305
     1306function TBGRACanvas2D.GetPenPos(defaultPt: TPointF): TPointF;
     1307begin
     1308  result := GetPenPos(defaultPt.x,defaultPt.y);
    8521309end;
    8531310
     
    8891346end;
    8901347
     1348function TBGRACanvas2D.GetDrawMode: TDrawMode;
     1349begin
     1350  if linearBlend then result := dmLinearBlend else result := dmDrawWithTransparency;
     1351end;
     1352
     1353procedure TBGRACanvas2D.copyTo(dest: IBGRAPath);
     1354begin
     1355  //nothing
     1356end;
     1357
    8911358constructor TBGRACanvas2D.Create(ASurface: TBGRACustomBitmap);
    8921359begin
     
    8941361  StateStack := TList.Create;
    8951362  FPathPointCount := 0;
     1363  FLastCoord := EmptyPointF;
     1364  FStartCoord := EmptyPointF;
    8961365  currentState := TBGRACanvasState2D.Create(AffineMatrixIdentity,nil);
    8971366  pixelCenteredCoordinates := false;
     1367  antialiasing := true;
    8981368end;
    8991369
     
    9061376  StateStack.Free;
    9071377  currentState.Free;
     1378  FreeAndNil(FFontRenderer);
    9081379  inherited Destroy;
    9091380end;
     
    9171388  encode64: TBase64EncodingStream;
    9181389begin
     1390  if surface = nil then exit;
    9191391  stream := TMemoryStream.Create;
    9201392  if mimeType='image/jpeg' then
     
    9671439end;
    9681440
    969 procedure TBGRACanvas2D.rotate(angleRad: single);
    970 begin
    971   currentState.matrix *= AffineMatrixRotationRad(-angleRad);
     1441procedure TBGRACanvas2D.scale(factor: single);
     1442begin
     1443  currentState.matrix *= AffineMatrixScale(factor,factor);
     1444end;
     1445
     1446procedure TBGRACanvas2D.rotate(angleRadCW: single);
     1447begin
     1448  currentState.matrix *= AffineMatrixRotationRad(-angleRadCW);
    9721449end;
    9731450
     
    9801457begin
    9811458  currentState.matrix *= AffineMatrix(a,c,e,b,d,f);
     1459end;
     1460
     1461procedure TBGRACanvas2D.transform(AMatrix: TAffineMatrix);
     1462begin
     1463  currentState.matrix *= AMatrix;
    9821464end;
    9831465
     
    10631545begin
    10641546  shadowColor(StrToBGRA(color));
     1547end;
     1548
     1549procedure TBGRACanvas2D.shadowNone;
     1550begin
     1551  shadowColor(BGRAPixelTransparent);
    10651552end;
    10661553
     
    11451632end;
    11461633
     1634procedure TBGRACanvas2D.addPath(APath: IBGRAPath);
     1635begin
     1636  if (FPathPointCount <> 0) and not isEmptyPointF(FPathPoints[FPathPointCount-1]) then
     1637  begin
     1638    AddPoint(EmptyPointF);
     1639    FLastCoord := EmptyPointF;
     1640    FStartCoord := EmptyPointF;
     1641  end;
     1642  APath.copyTo(self);
     1643end;
     1644
     1645procedure TBGRACanvas2D.addPath(ASvgPath: string);
     1646var p: TBGRAPath;
     1647begin
     1648  p := TBGRAPath.Create(ASvgPath);
     1649  addPath(p);
     1650  p.Free;
     1651end;
     1652
     1653procedure TBGRACanvas2D.path(APath: IBGRAPath);
     1654begin
     1655  beginPath;
     1656  addPath(APath);
     1657end;
     1658
     1659procedure TBGRACanvas2D.path(ASvgPath: string);
     1660begin
     1661  beginPath;
     1662  addPath(ASvgPath);
     1663end;
     1664
    11471665procedure TBGRACanvas2D.beginPath;
    11481666begin
    11491667  FPathPointCount := 0;
     1668  FLastCoord := EmptyPointF;
     1669  FStartCoord := EmptyPointF;
    11501670end;
    11511671
     
    11581678    while (i > 0) and not isEmptyPointF(FPathPoints[i-1]) do dec(i);
    11591679    AddPoint(FPathPoints[i]);
     1680    FLastCoord := FStartCoord;
    11601681  end;
    11611682end;
     
    11751696      pts[j] := FPathPoints[i+j];
    11761697    if closed then
    1177       splinePts := surface.ComputeClosedSpline(pts,style)
     1698      splinePts := BGRAPath.ComputeClosedSpline(pts,style)
    11781699    else
    1179       splinePts := surface.ComputeOpenedSpline(pts,style);
     1700      splinePts := BGRAPath.ComputeOpenedSpline(pts,style);
    11801701    dec(FPathPointCount,nb);
    11811702    AddPoints(splinePts);
     
    11931714end;
    11941715
    1195 procedure TBGRACanvas2D.moveTo(pt: TPointF);
    1196 begin
    1197   if FPathPointCount <> 0 then
     1716procedure TBGRACanvas2D.moveTo(const pt: TPointF);
     1717begin
     1718  if (FPathPointCount <> 0) and not isEmptyPointF(FPathPoints[FPathPointCount-1]) then
    11981719    AddPoint(EmptyPointF);
    11991720  AddPoint(ApplyTransform(pt));
    1200 end;
    1201 
    1202 procedure TBGRACanvas2D.lineTo(pt: TPointF);
     1721  FStartCoord := pt;
     1722  FLastCoord := pt;
     1723end;
     1724
     1725procedure TBGRACanvas2D.lineTo(const pt: TPointF);
    12031726begin
    12041727  AddPoint(ApplyTransform(pt));
     1728  FLastCoord := pt;
    12051729end;
    12061730
    12071731procedure TBGRACanvas2D.polylineTo(const pts: array of TPointF);
    12081732begin
    1209   AddPoints(ApplyTransform(pts));
     1733  if length(pts)> 0 then
     1734  begin
     1735    AddPoints(ApplyTransform(pts));
     1736    FLastCoord := pts[high(pts)];
     1737  end;
    12101738end;
    12111739
     
    12151743  pts : array of TPointF;
    12161744begin
    1217   curve := BezierCurve(GetPenPos,ApplyTransform(PointF(cpx,cpy)),ApplyTransform(PointF(x,y)));
    1218   pts := Surface.ComputeBezierCurve(curve);
     1745  curve := BezierCurve(ApplyTransform(GetPenPos(cpx,cpy)),ApplyTransform(PointF(cpx,cpy)),ApplyTransform(PointF(x,y)));
     1746  pts := BGRAPath.ComputeBezierCurve(curve);
    12191747  AddPoints(pts);
    1220 end;
    1221 
    1222 procedure TBGRACanvas2D.quadraticCurveTo(cp, pt: TPointF);
     1748  FLastCoord := PointF(x,y);
     1749end;
     1750
     1751procedure TBGRACanvas2D.quadraticCurveTo(const cp, pt: TPointF);
    12231752begin
    12241753  quadraticCurveTo(cp.x,cp.y,pt.x,pt.y);
     
    12301759  pts : array of TPointF;
    12311760begin
    1232   curve := BezierCurve(GetPenPos,ApplyTransform(PointF(cp1x,cp1y)),
     1761  curve := BezierCurve(ApplyTransform(GetPenPos(cp1x,cp1y)),ApplyTransform(PointF(cp1x,cp1y)),
    12331762    ApplyTransform(PointF(cp2x,cp2y)),ApplyTransform(PointF(x,y)));
    1234   pts := Surface.ComputeBezierCurve(curve);
     1763  pts := BGRAPath.ComputeBezierCurve(curve);
    12351764  AddPoints(pts);
    1236 end;
    1237 
    1238 procedure TBGRACanvas2D.bezierCurveTo(cp1, cp2, pt: TPointF);
     1765  FLastCoord := PointF(x,y);
     1766end;
     1767
     1768procedure TBGRACanvas2D.bezierCurveTo(const cp1, cp2, pt: TPointF);
    12391769begin
    12401770  bezierCurveTo(cp1.x,cp1.y,cp2.x,cp2.y,pt.x,pt.y);
     
    12471777  LineTo(x+w,y+h);
    12481778  LineTo(x,y+h);
    1249   LineTo(x,y);
     1779  closePath;
    12501780end;
    12511781
     
    12651795  arcTo(PointF(x,y+h),PointF(x,y), radius);
    12661796  arcTo(PointF(x,y),PointF(x+w,y), radius);
     1797  closePath;
     1798end;
     1799
     1800procedure TBGRACanvas2D.roundRect(x, y, w, h, rx, ry: single);
     1801begin
     1802  if (w <= 0) or (h <= 0) then exit;
     1803  if rx < 0 then rx := 0;
     1804  if ry < 0 then ry := 0;
     1805  if (rx = 0) and (ry = 0) then
     1806  begin
     1807    rect(x,y,w,h);
     1808    exit;
     1809  end;
     1810  if rx*2 > w then rx := w/2;
     1811  if ry*2 > h then ry := h/2;
     1812  moveTo(x+rx,y);
     1813  lineTo(x+w-rx,y);
     1814  arcTo(rx,ry,0,false,false,x+w,y+ry);
     1815  lineTo(x+w,y+h-ry);
     1816  arcTo(rx,ry,0,false,false,x+w-rx,y+h);
     1817  lineTo(x+rx,y+h);
     1818  arcTo(rx,ry,0,false,false,x,y+h-ry);
     1819  lineTo(x,y+ry);
     1820  arcTo(rx,ry,0,false,false,x+rx,y);
     1821  closePath;
    12671822end;
    12681823
     
    12731828  transf := ApplyTransform(pts);
    12741829  if (pts[0] = pts[high(pts)]) and (length(pts) > 1) then
    1275     transf := surface.ComputeClosedSpline(slice(transf, length(transf)-1),style)
     1830    transf := BGRAPath.ComputeClosedSpline(slice(transf, length(transf)-1),style)
    12761831  else
    1277     transf := surface.ComputeOpenedSpline(transf,style);
     1832    transf := BGRAPath.ComputeOpenedSpline(transf,style);
    12781833  AddPoints(transf);
     1834  FLastCoord := pts[high(pts)];
    12791835end;
    12801836
     
    12841840  i: Integer;
    12851841begin
     1842  if length(pts) = 0 then exit;
    12861843  transf := ApplyTransform(pts);
    12871844  if FPathPointCount <> 0 then
     
    12901847    for i := high(transf) downto 1 do
    12911848      transf[i]:= transf[i-1];
    1292     transf[0] := GetPenPos;
    1293   end;
    1294   transf := surface.ComputeOpenedSpline(transf,style);
     1849    transf[0] := ApplyTransform(GetPenPos(pts[0].x,pts[0].y));
     1850  end;
     1851  transf := BGRAPath.ComputeOpenedSpline(transf,style);
    12951852  AddPoints(transf);
    1296 end;
    1297 
    1298 procedure TBGRACanvas2D.arc(x, y, radius, startAngle, endAngle: single;
     1853  FLastCoord := pts[high(pts)];
     1854end;
     1855
     1856procedure TBGRACanvas2D.arc(x, y, radius, startAngleRadCW, endAngleRadCW: single;
    12991857  anticlockwise: boolean);
    13001858var pts: array of TPointF;
     
    13051863  unitAffine: TAffineMatrix;
    13061864  v1orig,v2orig,v1ortho,v2ortho: TPointF;
     1865  startRadCCW,endRadCCW: single;
    13071866begin
    13081867  v1orig := PointF(currentState.matrix[1,1],currentState.matrix[2,1]);
     
    13171876  unitAffine := AffineMatrix(v1ortho.x, v2ortho.x, pt.x,
    13181877                             v1ortho.y, v2ortho.y, pt.y);
    1319   startAngle := -startAngle;
    1320   endAngle := -endAngle;
     1878  startRadCCW := -startAngleRadCW;
     1879  endRadCCW := -endAngleRadCW;
    13211880  if not anticlockwise then
    13221881  begin
    1323     temp := startAngle;
    1324     startAngle := endAngle;
    1325     endAngle := temp;
    1326     pts := surface.ComputeArcRad(0,0,rx,ry,startAngle,endAngle);
     1882    temp := startRadCCW;
     1883    startRadCCW := endRadCCW;
     1884    endRadCCW:= temp;
     1885    pts := BGRAPath.ComputeArcRad(0,0,rx,ry,startRadCCW,endRadCCW);
    13271886    pts := ApplyTransform(pts,unitAffine);
    13281887    AddPointsRev(pts);
    13291888  end else
    13301889  begin
    1331     pts := surface.ComputeArcRad(0,0,rx,ry,startAngle,endAngle);
     1890    pts := BGRAPath.ComputeArcRad(0,0,rx,ry,startRadCCW,endRadCCW);
    13321891    pts := ApplyTransform(pts,unitAffine);
    13331892    AddPoints(pts);
    13341893  end;
    1335 end;
    1336 
    1337 procedure TBGRACanvas2D.arc(x, y, radius, startAngle, endAngle: single);
    1338 begin
    1339   arc(x,y,radius,startAngle,endAngle,false);
     1894  FLastCoord := ArcEndPoint(ArcDef(x,y,radius,radius,0,startAngleRadCW,endAngleRadCW,anticlockwise));
     1895end;
     1896
     1897procedure TBGRACanvas2D.arc(x, y, radius, startAngleRadCW, endAngleRadCW: single);
     1898begin
     1899  arc(x,y,radius,startAngleRadCW,endAngleRadCW,false);
     1900end;
     1901
     1902procedure TBGRACanvas2D.arc(cx, cy, rx, ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single;
     1903  anticlockwise: boolean);
     1904begin
     1905  arc(ArcDef(cx,cy,rx,ry,xAngleRadCW,startAngleRadCW,endAngleRadCW,anticlockwise))
     1906end;
     1907
     1908procedure TBGRACanvas2D.arc(cx, cy, rx, ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single);
     1909begin
     1910  arc(ArcDef(cx,cy,rx,ry,xAngleRadCW,startAngleRadCW,endAngleRadCW,false))
     1911end;
     1912
     1913procedure TBGRACanvas2D.arc(const arcDef: TArcDef);
     1914var previousMatrix: TAffineMatrix;
     1915begin
     1916  if (arcDef.radius.x = 0) and (arcDef.radius.y = 0) then
     1917    lineTo(arcDef.center) else
     1918  begin
     1919    previousMatrix := currentState.matrix;
     1920    translate(arcDef.center.x,arcDef.center.y);
     1921    rotate(arcDef.xAngleRadCW);
     1922    scale(arcDef.radius.x,arcDef.radius.y);
     1923    arc(0,0,1,arcDef.startAngleRadCW,arcDef.endAngleRadCW,arcDef.anticlockwise);
     1924    currentState.matrix := previousMatrix;
     1925    FLastCoord := ArcEndPoint(arcDef);
     1926  end;
    13401927end;
    13411928
    13421929procedure TBGRACanvas2D.arcTo(x1, y1, x2, y2, radius: single);
    1343 var p0,p1,p2,p3,p4,an,bn,cn,c: TPointF;
    1344     dir, a2, b2, c2, cosx, sinx, d,
    1345     angle0, angle1: single;
    1346     anticlockwise: boolean;
    1347 begin
    1348   if FPathPointCount = 0 then
    1349     moveTo(x1,y1);
    1350   radius := abs(radius);
    1351   p0 := GetPenPos;
    1352   p1 := PointF(x1,y1);
    1353   p2 := PointF(x2,y2);
    1354 
    1355   if (p0 = p1) or (p1 = p2) or (radius = 0) then
    1356   begin
    1357     lineto(x1,y1);
    1358     exit;
    1359   end;
    1360 
    1361   dir := (x2-x1)*(p0.y-y1) + (y2-y1)*(x1-p0.x);
    1362   if dir = 0 then
    1363   begin
    1364     lineto(x1,y1);
    1365     exit;
    1366   end;
    1367 
    1368   a2 := (p0.x-x1)*(p0.x-x1) + (p0.y-y1)*(p0.y-y1);
    1369   b2 := (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);
    1370   c2 := (p0.x-x2)*(p0.x-x2) + (p0.y-y2)*(p0.y-y2);
    1371   cosx := (a2+b2-c2)/(2*sqrt(a2*b2));
    1372 
    1373   sinx := sqrt(1 - cosx*cosx);
    1374   if (sinx = 0) or (cosx = 1) then
    1375   begin
    1376     lineto(x1,y1);
    1377     exit;
    1378   end;
    1379   d := radius / ((1 - cosx) / sinx);
    1380 
    1381   an := (p1-p0)*(1/sqrt(a2));
    1382   bn := (p1-p2)*(1/sqrt(b2));
    1383   p3 := p1 - an*d;
    1384   p4 := p1 - bn*d;
    1385   anticlockwise := (dir < 0);
    1386 
    1387   cn := PointF(an.y,-an.x)*radius;
    1388   if not anticlockwise then cn := -cn;
    1389   c := p3 + cn;
    1390   angle0 := arctan2((p3.y-c.y), (p3.x-c.x));
    1391   angle1 := arctan2((p4.y-c.y), (p4.x-c.x));
    1392 
    1393   lineTo(p3.x,p3.y);
    1394   arc(c.x,c.y, radius, angle0, angle1, anticlockwise);
     1930var p0: TPointF;
     1931begin
     1932  p0 := GetPenPos(x1,y1);
     1933  arc(Html5ArcTo(p0,PointF(x1,y1),PointF(x2,y2),radius));
    13951934end;
    13961935
     
    13981937begin
    13991938  arcTo(p1.x,p1.y,p2.x,p2.y,radius);
     1939end;
     1940
     1941procedure TBGRACanvas2D.arcTo(rx, ry, xAngleRadCW: single; largeArc,
     1942  anticlockwise: boolean; x, y: single);
     1943begin
     1944  arc(SvgArcTo(GetPenPos(x,y), rx,ry, xAngleRadCW, largeArc, anticlockwise, PointF(x,y)));
     1945  FLastCoord := PointF(x,y);
     1946end;
     1947
     1948procedure TBGRACanvas2D.circle(x, y, r: single);
     1949begin
     1950  arc(x,y,r,0,0);
     1951end;
     1952
     1953procedure TBGRACanvas2D.ellipse(x, y, rx, ry: single);
     1954begin
     1955  arc(x,y,rx,ry,0,0,0);
     1956end;
     1957
     1958procedure TBGRACanvas2D.text(AText: string; x, y: single);
     1959var renderer : TBGRACustomFontRenderer;
     1960  previousMatrix: TAffineMatrix;
     1961begin
     1962  renderer := fontRenderer;
     1963  if renderer.FontEmHeight <= 0 then exit;
     1964  previousMatrix := currentState.matrix;
     1965
     1966  scale(currentState.fontEmHeight/renderer.FontEmHeight);
     1967  if (currentState.textBaseline <> 'top') and
     1968    (currentState.textBaseline <> 'hanging') then
     1969  with renderer.GetFontPixelMetric do
     1970  begin
     1971    if currentState.textBaseline = 'bottom' then
     1972       translate(0,-Lineheight)
     1973    else if currentState.textBaseline = 'middle' then
     1974       translate(0,-Lineheight/2)
     1975    else if currentState.textBaseline = 'alphabetic' then
     1976       translate(0,-baseline);
     1977  end;
     1978
     1979  if renderer <> nil then
     1980    renderer.CopyTextPathTo(self, x,y, AText, taLeftJustify);
     1981
     1982  currentState.matrix := previousMatrix;
     1983  FLastCoord := EmptyPointF;
     1984  FStartCoord := EmptyPointF;
     1985end;
     1986
     1987procedure TBGRACanvas2D.fillText(AText: string; x, y: single);
     1988begin
     1989  beginPath;
     1990  text(AText,x,y);
     1991  fill;
     1992  beginPath;
     1993end;
     1994
     1995procedure TBGRACanvas2D.strokeText(AText: string; x, y: single);
     1996begin
     1997  beginPath;
     1998  text(AText,x,y);
     1999  stroke;
     2000  beginPath;
     2001end;
     2002
     2003function TBGRACanvas2D.measureText(AText: string): TCanvas2dTextSize;
     2004var renderer: TBGRACustomFontRenderer;
     2005begin
     2006  renderer := fontRenderer;
     2007  if renderer <> nil then
     2008  begin
     2009    with renderer.TextSize(AText) do
     2010    begin
     2011      result.width := cx;
     2012      result.height:= cy;
     2013    end;
     2014  end
     2015  else
     2016  begin
     2017    result.width := 0;
     2018    result.height := 0;
     2019  end;
    14002020end;
    14012021
     
    14422062    currentState.clipMask := surface.NewBitmap(width,height,BGRAWhite);
    14432063  tempBmp := surface.NewBitmap(width,height,BGRABlack);
    1444   tempBmp.FillPolyAntialias(slice(FPathPoints,FPathPointCount),BGRAWhite);
     2064  if antialiasing then
     2065    tempBmp.FillPolyAntialias(slice(FPathPoints,FPathPointCount),BGRAWhite)
     2066  else
     2067    tempBmp.FillPoly(slice(FPathPoints,FPathPointCount),BGRAWhite,dmSet);
    14452068  currentState.clipMask.BlendImage(0,0,tempBmp,boDarken);
    14462069  tempBmp.Free;
     
    14512074  if FPathPointCount = 0 then exit;
    14522075  if currentState.clipMask = nil then exit;
    1453   currentState.clipMask.FillPolyAntialias(slice(FPathPoints,FPathPointCount),BGRAWhite);
     2076  if antialiasing then
     2077    currentState.clipMask.FillPolyAntialias(slice(FPathPoints,FPathPointCount),BGRAWhite)
     2078  else
     2079    currentState.clipMask.FillPoly(slice(FPathPoints,FPathPointCount),BGRAWhite,dmSet);
    14542080  if currentState.clipMask.Equals(BGRAWhite) then
    14552081    FreeAndNil(currentState.clipMask);
  • GraphicTest/Packages/bgrabitmap/bgracolorint.pas

    r452 r472  
    167167  end;
    168168{$else}
    169 begin
    170   result.r := int64(color1.r)*factor65536 shr 16;
    171   result.g := int64(color1.g)*factor65536 shr 16;
    172   result.b := int64(color1.b)*factor65536 shr 16;
    173   result.a := int64(color1.a)*factor65536 shr 16;
     169var prod: int64;
     170begin
     171  prod := int64(color1.r)*factor65536;
     172  if prod >= 0 then result.r := prod shr 16
     173  else result.r := -((-prod) shr 16);
     174  prod := int64(color1.g)*factor65536;
     175  if prod >= 0 then result.g := prod shr 16
     176  else result.g := -((-prod) shr 16);
     177  prod := int64(color1.b)*factor65536;
     178  if prod >= 0 then result.b := prod shr 16
     179  else result.b := -((-prod) shr 16);
     180  prod := int64(color1.a)*factor65536;
     181  if prod >= 0 then result.a := prod shr 16
     182  else result.a := -((-prod) shr 16);
    174183end;
    175184{$endif}
  • GraphicTest/Packages/bgrabitmap/bgracompressablebitmap.pas

    r452 r472  
    4444     procedure Decompress;
    4545     procedure FreeData;
     46     procedure Init;
    4647   public
    4748     CompressionLevel: Tcompressionlevel;
     
    6263     property Width : Integer read FWidth;
    6364     property Height: Integer read FHeight;
    64      property Caption : string read FCaption;
     65     property Caption : string read FCaption write FCaption;
    6566
    6667   end;
     
    7778constructor TBGRACompressableBitmap.Create;
    7879begin
    79   FUncompressedData := nil;
    80   FCompressedDataArray := nil;
    81   FWidth := 0;
    82   FHeight := 0;
    83   FCaption := '';
    84   FCompressionProgress := 0;
    85   CompressionLevel := clfastest;
     80  Init;
    8681end;
    8782
    8883constructor TBGRACompressableBitmap.Create(Source: TBGRABitmap);
    8984begin
    90   FUncompressedData := nil;
    91   FCompressedDataArray := nil;
    92   FWidth := 0;
    93   FHeight := 0;
    94   FCaption := '';
    95   FCompressionProgress := 0;
     85  Init;
    9686  Assign(Source);
    9787end;
     
    219209  setlength(FCaption,WinReadLongint(AStream));
    220210  AStream.Read(FCaption[1],length(FCaption));
    221   if (FWidth=0) or (FHeight = 0) then exit;
     211  if (FWidth=0) or (FHeight = 0) then
     212  begin
     213    FUncompressedData := TMemoryStream.Create;
     214    exit;
     215  end;
    222216
    223217  FBounds.Left := WinReadLongint(AStream);
     
    234228    FCompressedDataArray[i].CopyFrom(AStream,size);
    235229  end;
     230
     231  if FCompressedDataArray = nil then
     232    FUncompressedData := TMemoryStream.Create;
    236233end;
    237234
     
    270267end;
    271268
     269procedure TBGRACompressableBitmap.Init;
     270begin
     271  FUncompressedData := nil;
     272  FCompressedDataArray := nil;
     273  FWidth := 0;
     274  FHeight := 0;
     275  FCaption := '';
     276  FCompressionProgress := 0;
     277  CompressionLevel := clfastest;
     278end;
     279
    272280{ Copy a bitmap into this object. As it is copied, you need not
    273281  keep a copy of the source }
  • GraphicTest/Packages/bgrabitmap/bgracoordpool3d.pas

    r452 r472  
    66
    77uses
    8   Classes, SysUtils, BGRABitmapTypes, BGRASSE;
     8  Classes, SysUtils, BGRABitmapTypes, BGRASSE, BGRAMatrix3D;
    99
    1010type
     
    1515    {32} projectedCoord: TPointF;
    1616    {40} InvZ: single;
    17     {44} used: longbool;
     17    {44} used: wordbool; customNormalUsed: wordbool;
    1818    {48} viewNormal: TPoint3D_128;
    19   end; {64}
    20 
    21   { TBGRACoordPool3D }
    22 
    23   TBGRACoordPool3D = class
     19    {64} customNormal: TPoint3D_128;
     20  end; {80}
     21
     22  PBGRANormalData3D = ^TBGRANormalData3D;
     23  TBGRANormalData3D = packed record
     24    {0} customNormal: TPoint3D_128;
     25    {16} viewNormal: TPoint3D_128;
     26    {32} used: longbool;
     27    {36} filler1,filler2,filler3: longword;
     28  end; {48}
     29
     30  { TBGRAGenericPool }
     31
     32  TBGRAGenericPool = class
    2433  private
    2534    FFirstFree: integer;
    26     FNbCoord,FCapacity: integer;
     35    FNbElements,FCapacity: integer;
     36    FElementSize: PtrInt;
     37    FUsedCapacity : integer;
     38    function GetElement(AIndex: integer): Pointer;
     39    procedure SetCapacity(ACapacity: integer);
     40  protected
    2741    FPoolData: TMemoryBlockAlign128;
    28     FUsedCapacity : integer;
    29     function GetCoordData(AIndex: integer): PBGRACoordData3D;
    30     procedure SetCapacity(ACapacity: integer);
     42    function GetUsed({%H-}AElement: integer): boolean; virtual;
     43    procedure SetUsed({%H-}AElement: integer; {%H-}AUsed: boolean); virtual;
     44    procedure Remove(AIndex: integer); //does not work if GetUsed/SetUsed are not implemented
    3145  public
    32     constructor Create(ACapacity: integer);
     46    constructor Create(ACapacity: integer; AElementSize: integer);
    3347    destructor Destroy; override;
    34     procedure Remove(AIndex: integer);
    3548    function Add: integer;
    36     property CoordData[AIndex: integer]: PBGRACoordData3D read GetCoordData;
     49    property Element[AIndex: integer]: Pointer read GetElement;
    3750    property Capacity: integer read FCapacity;
    3851    property UsedCapacity: integer read FUsedCapacity;
    3952  end;
    4053
     54  { TBGRACoordPool3D }
     55
     56  TBGRACoordPool3D = class(TBGRAGenericPool)
     57  private
     58    function GetCoordData(AIndex: integer): PBGRACoordData3D;
     59  protected
     60    function GetUsed(AElement: integer): boolean; override;
     61    procedure SetUsed(AElement: integer; AUsed: boolean); override;
     62  public
     63    procedure Remove(AIndex: integer);
     64    constructor Create(ACapacity: integer);
     65    procedure ComputeWithMatrix(const AMatrix: TMatrix3D; const AProjection: TProjection3D);
     66    property CoordData[AIndex: integer]: PBGRACoordData3D read GetCoordData;
     67  end;
     68
     69  { TBGRANormalPool3D }
     70
     71  TBGRANormalPool3D = class(TBGRAGenericPool)
     72  private
     73    function GetNormalData(AIndex: integer): PBGRANormalData3D;
     74  protected
     75    function GetUsed(AElement: integer): boolean; override;
     76    procedure SetUsed(AElement: integer; AUsed: boolean); override;
     77  public
     78    procedure Remove(AIndex: integer);
     79    constructor Create(ACapacity: integer);
     80    procedure ComputeWithMatrix(const AMatrix: TMatrix3D);
     81    property NormalData[AIndex: integer]: PBGRANormalData3D read GetNormalData;
     82  end;
     83
    4184implementation
    4285
    43 { TBGRACoordPool3D }
    44 
    45 procedure TBGRACoordPool3D.SetCapacity(ACapacity: integer);
     86{ TBGRAGenericPool }
     87
     88function TBGRAGenericPool.GetElement(AIndex: integer): Pointer;
     89begin
     90  result := Pointer(PByte(FPoolData.Data)+AIndex*FElementSize);
     91end;
     92
     93procedure TBGRAGenericPool.SetCapacity(ACapacity: integer);
    4694var NewPoolData: TMemoryBlockAlign128;
    4795begin
     
    52100    else
    53101    begin
    54       NewPoolData := TMemoryBlockAlign128.Create(ACapacity*sizeof(TBGRACoordData3D));
     102      NewPoolData := TMemoryBlockAlign128.Create(ACapacity*FElementSize);
    55103      if FCapacity <> 0 then
    56104      begin
     
    58106        if FCapacity < ACapacity then
    59107        begin
    60           move(FPoolData.Data^, NewPoolData.Data^, FCapacity*sizeof(TBGRACoordData3D));
     108          move(FPoolData.Data^, NewPoolData.Data^, FCapacity*FElementSize);
    61109          //pad with zeros
    62           fillchar((pbyte(NewPoolData.Data)+FCapacity*sizeof(TBGRACoordData3D))^,(ACapacity-FCapacity)*sizeof(TBGRACoordData3D),0);
     110          fillchar((pbyte(NewPoolData.Data)+FCapacity*FElementSize)^,(ACapacity-FCapacity)*FElementSize,0);
    63111        end
    64112        else //previous block is greater or equal
    65           move(FPoolData.Data^, NewPoolData.Data^, ACapacity*sizeof(TBGRACoordData3D));
     113          move(FPoolData.Data^, NewPoolData.Data^, ACapacity*FElementSize);
    66114        FreeAndNil(FPoolData);
    67115      end else
    68116       //clear new block
    69         fillchar(pbyte(NewPoolData.Data)^,ACapacity*sizeof(TBGRACoordData3D),0);
     117        fillchar(pbyte(NewPoolData.Data)^,ACapacity*FElementSize,0);
    70118
    71119      FPoolData := NewPoolData;
     
    75123end;
    76124
    77 constructor TBGRACoordPool3D.Create(ACapacity: integer);
     125function TBGRAGenericPool.GetUsed(AElement: integer): boolean;
     126begin
     127  result := false;
     128end;
     129
     130procedure TBGRAGenericPool.SetUsed(AElement: integer; AUsed: boolean);
     131begin
     132  //nothing
     133end;
     134
     135constructor TBGRAGenericPool.Create(ACapacity: integer; AElementSize: integer);
    78136begin
    79137  FCapacity := 0;
    80138  FPoolData := nil;
    81   FNbCoord:= 0;
     139  FNbElements:= 0;
    82140  FFirstFree := 0;
    83141  FUsedCapacity := 0;
     142  FElementSize:= AElementSize;
    84143  SetCapacity(ACapacity);
    85144end;
    86145
    87 destructor TBGRACoordPool3D.Destroy;
    88 begin
    89   FPoolData.Free;
     146destructor TBGRAGenericPool.Destroy;
     147begin
     148  FreeAndNil(FPoolData);
     149  FCapacity := 0;
     150  FNbElements:= 0;
     151  FFirstFree := 0;
     152  FUsedCapacity := 0;
    90153  inherited Destroy;
    91154end;
    92155
    93 procedure TBGRACoordPool3D.Remove(AIndex: integer);
    94 begin
    95   if CoordData[AIndex]^.used then
    96   begin
    97     CoordData[AIndex]^.used := false;
     156procedure TBGRAGenericPool.Remove(AIndex: integer);
     157begin
     158  if (AIndex < 0) or (AIndex >= FUsedCapacity) then
     159    raise ERangeError.Create('Index out of bounds');
     160  if GetUsed(AIndex) then
     161  begin
     162    SetUsed(AIndex, false);
    98163    if AIndex < FFirstFree then FFirstFree := AIndex;
    99164    if AIndex = FUsedCapacity-1 then
    100165    begin
    101       while (FUsedCapacity > 0) and not CoordData[FUsedCapacity-1]^.used do
     166      while (FUsedCapacity > 0) and not GetUsed(FUsedCapacity-1) do
    102167        dec(FUsedCapacity);
    103168    end;
     
    105170end;
    106171
    107 function TBGRACoordPool3D.Add: integer;
     172function TBGRAGenericPool.Add: integer;
    108173begin
    109174  //check for free space
    110175  while FFirstFree < FCapacity do
    111176  begin
    112     if not CoordData[FFirstFree]^.used then
    113     begin
    114       CoordData[FFirstFree]^.used := false;
     177    if not GetUsed(FFirstFree) then
     178    begin
     179      SetUsed(FFirstFree,True);
    115180      result := FFirstFree;
    116181      inc(FFirstFree);
     
    124189  //no free space
    125190  SetCapacity(FCapacity*2+8);
    126   CoordData[FFirstFree]^.used := false;
     191  SetUsed(FFirstFree, true);
    127192  result := FFirstFree;
    128193  inc(FFirstFree);
     
    131196end;
    132197
     198{ TBGRACoordPool3D }
     199
     200constructor TBGRACoordPool3D.Create(ACapacity: integer);
     201begin
     202  inherited Create(ACapacity,SizeOf(TBGRACoordData3D));
     203end;
     204
     205procedure TBGRACoordPool3D.ComputeWithMatrix(const AMatrix: TMatrix3D;
     206  const AProjection: TProjection3D);
     207var
     208  P: PBGRACoordData3D;
     209  I: NativeInt;
     210begin
     211  if UsedCapacity = 0 then exit;
     212  P := PBGRACoordData3D(FPoolData.Data);
     213  {$IFDEF CPUI386}
     214  {$asmmode intel}
     215  if UseSSE then
     216  begin
     217    Matrix3D_SSE_Load(AMatrix);
     218    asm
     219      mov eax,[AProjection]
     220      movups xmm4,[eax]
     221      xorps xmm1,xmm1
     222    end;
     223    i := UsedCapacity;
     224    if UseSSE3 then
     225    begin
     226      while i > 0 do
     227      with P^ do
     228      begin
     229        if used then
     230        begin
     231          MatrixMultiplyVect3D_SSE3_Aligned(sceneCoord,viewCoord);
     232          if viewCoord.z > 0 then
     233          begin
     234            asm
     235              mov eax, P
     236              movaps xmm3, [eax+16] //viewCoord
     237              movaps xmm2,xmm3
     238              shufps xmm2,xmm3,2+8+32+128
     239              rcpps xmm2,xmm2  //xmm2 = InvZ
     240              movss [eax+40],xmm2 //-> InvZ
     241
     242              mulps xmm3,xmm4  //xmm3 *= Projection.Zoom
     243              mulps xmm3,xmm2  //xmm3 *= InvZ
     244
     245              movhlps xmm0,xmm4  //xmm0 = Projection.Center
     246              addps xmm3,xmm0  //xmm3 += Projection.Center
     247
     248              movlps [eax+32],xmm3 //->projectedCoord
     249              movaps [eax+48],xmm1 //->normal
     250            end;
     251          end else
     252          asm
     253            mov eax, P
     254            movlps [eax+32],xmm1  //0->projectedCoord
     255            movaps [eax+48],xmm1 //->normal
     256          end;
     257          if customNormalUsed then
     258            MatrixMultiplyVect3DWithoutTranslation_SSE3_Aligned(customNormal,viewNormal);
     259        end;
     260        dec(i);
     261        inc(p);
     262      end;
     263    end else
     264    begin
     265      while i > 0 do
     266      with P^ do
     267      begin
     268        if used then
     269        begin
     270          MatrixMultiplyVect3D_SSE_Aligned(sceneCoord,viewCoord);
     271          if viewCoord.z > 0 then
     272          begin
     273            asm
     274              mov eax, P
     275              movaps xmm3, [eax+16] //viewCoord
     276              movaps xmm2,xmm3
     277              shufps xmm2,xmm3,2+8+32+128
     278              rcpps xmm2,xmm2  //xmm2 = InvZ
     279              movss [eax+40],xmm2 //-> InvZ
     280
     281              mulps xmm3,xmm4  //xmm3 *= Projection.Zoom
     282              mulps xmm3,xmm2  //xmm3 *= InvZ
     283
     284              movhlps xmm0,xmm4  //xmm0 = Projection.Center
     285              addps xmm3,xmm0  //xmm3 += Projection.Center
     286
     287              movlps [eax+32],xmm3 //->projectedCoord
     288              movaps [eax+48],xmm1 //->normal
     289            end;
     290          end else
     291          asm
     292            mov eax, P
     293            movlps [eax+32],xmm1  //0 ->projectedCoord
     294            movaps [eax+48],xmm1 //->normal
     295          end;
     296          if customNormalUsed then
     297            MatrixMultiplyVect3DWithoutTranslation_SSE_Aligned(customNormal,viewNormal);
     298        end;
     299        dec(i);
     300        inc(p);
     301      end;
     302    end;
     303  end
     304  else
     305  {$ENDIF}
     306  begin
     307    i := UsedCapacity;
     308    while i > 0 do
     309    with P^ do
     310    begin
     311      if used then
     312      begin
     313        viewCoord := AMatrix*sceneCoord;
     314        if customNormalUsed then
     315          viewNormal := MultiplyVect3DWithoutTranslation(AMatrix,customNormal)
     316        else
     317          ClearPoint3D_128(viewNormal);
     318        if viewCoord.z > 0 then
     319        begin
     320          InvZ := 1/viewCoord.z;
     321          projectedCoord := PointF(viewCoord.x*InvZ*AProjection.Zoom.x + AProjection.Center.x,
     322                                   viewCoord.y*InvZ*AProjection.Zoom.Y + AProjection.Center.y);
     323        end else
     324          projectedCoord := PointF(0,0);
     325      end;
     326      dec(i);
     327      inc(p);
     328    end;
     329  end;
     330end;
     331
    133332function TBGRACoordPool3D.GetCoordData(AIndex: integer): PBGRACoordData3D;
    134333begin
     
    136335end;
    137336
     337function TBGRACoordPool3D.GetUsed(AElement: integer): boolean;
     338begin
     339  Result:= CoordData[AElement]^.used;
     340end;
     341
     342procedure TBGRACoordPool3D.SetUsed(AElement: integer; AUsed: boolean);
     343begin
     344  CoordData[AElement]^.used := AUsed;
     345end;
     346
     347procedure TBGRACoordPool3D.Remove(AIndex: integer);
     348begin
     349  inherited Remove(AIndex);
     350end;
     351
     352{ TBGRANormalPool3D }
     353
     354function TBGRANormalPool3D.GetNormalData(AIndex: integer): PBGRANormalData3D;
     355begin
     356  result := PBGRANormalData3D(FPoolData.Data)+AIndex;
     357end;
     358
     359function TBGRANormalPool3D.GetUsed(AElement: integer): boolean;
     360begin
     361  Result:= NormalData[AElement]^.used;
     362end;
     363
     364procedure TBGRANormalPool3D.SetUsed(AElement: integer; AUsed: boolean);
     365begin
     366  NormalData[AElement]^.used := AUsed;
     367end;
     368
     369procedure TBGRANormalPool3D.Remove(AIndex: integer);
     370begin
     371  inherited Remove(AIndex);
     372end;
     373
     374constructor TBGRANormalPool3D.Create(ACapacity: integer);
     375begin
     376  inherited Create(ACapacity,SizeOf(TBGRANormalData3D));
     377end;
     378
     379procedure TBGRANormalPool3D.ComputeWithMatrix(const AMatrix: TMatrix3D);
     380var
     381  P: PBGRANormalData3D;
     382  I: NativeInt;
     383begin
     384  if UsedCapacity = 0 then exit;
     385  P := PBGRANormalData3D(FPoolData.Data);
     386  {$IFDEF CPUI386}
     387  {$asmmode intel}
     388  if UseSSE then
     389  begin
     390    Matrix3D_SSE_Load(AMatrix);
     391    i := UsedCapacity;
     392    if UseSSE3 then
     393    begin
     394      while i > 0 do
     395      with P^ do
     396      begin
     397        if used then
     398          MatrixMultiplyVect3DWithoutTranslation_SSE3_Aligned(customNormal,viewNormal);
     399        dec(i);
     400        inc(p);
     401      end;
     402    end else
     403    begin
     404      while i > 0 do
     405      with P^ do
     406      begin
     407        if used then
     408          MatrixMultiplyVect3DWithoutTranslation_SSE_Aligned(customNormal,viewNormal);
     409        dec(i);
     410        inc(p);
     411      end;
     412    end;
     413  end
     414  else
     415  {$ENDIF}
     416  begin
     417    i := UsedCapacity;
     418    while i > 0 do
     419    with P^ do
     420    begin
     421      if used then
     422        viewNormal := MultiplyVect3DWithoutTranslation(AMatrix,customNormal);
     423      dec(i);
     424      inc(p);
     425    end;
     426  end;
     427end;
     428
    138429end.
    139430
  • GraphicTest/Packages/bgrabitmap/bgradefaultbitmap.pas

    r452 r472  
    3333
    3434uses
    35   Classes, SysUtils, Types, FPImage, Graphics, BGRABitmapTypes, GraphType, FPImgCanv, BGRACanvas, BGRACanvas2D, FPWritePng;
     35  Classes, SysUtils, Types, FPImage, Graphics, BGRABitmapTypes, GraphType, FPImgCanv,
     36  BGRACanvas, BGRACanvas2D, FPWritePng, BGRAArrow, BGRAPen;
    3637
    3738type
     
    4344      if the coordinates are visible and return true if it is the case, swap
    4445      coordinates if necessary and make them fit into the clipping rectangle }
    45     function CheckHorizLineBounds(var x, y, x2: integer): boolean; inline;
    46     function CheckVertLineBounds(var x, y, y2: integer; out delta: integer): boolean; inline;
     46    function CheckHorizLineBounds(var x, y, x2: int32or64): boolean; inline;
     47    function CheckVertLineBounds(var x, y, y2: int32or64; out delta: int32or64): boolean; inline;
    4748    function CheckRectBounds(var x,y,x2,y2: integer; minsize: integer): boolean; inline;
    4849    function CheckClippedRectBounds(var x,y,x2,y2: integer): boolean; inline;
    49     function CheckPutImageBounds(x, y, tx, ty: integer; out minxb, minyb, maxxb, maxyb, ignoreleft: integer): boolean; inline;
    5050    function CheckAntialiasRectBounds(var x,y,x2,y2: single; w: single): boolean;
    5151    function GetCanvasBGRA: TBGRACanvas;
     
    7575    FCanvasFP: TFPImageCanvas;
    7676    FCanvasDrawModeFP: TDrawMode;
    77     FCanvasPixelProcFP: procedure(x, y: integer; col: TBGRAPixel) of object;
     77    FCanvasPixelProcFP: procedure(x, y: int32or64; col: TBGRAPixel) of object;
    7878
    7979    //canvas-like with antialiasing and texturing
     
    8383    //drawing options
    8484    FEraseMode: boolean;      //when polygons are erased instead of drawn
    85     FFont: TFont;             //font parameters
    8685    FFontHeight: integer;
    87     FFontHeightSign: integer; //sign correction
     86    FFontRenderer: TBGRACustomFontRenderer;
    8887
    8988    { Pen style can be defined by PenStyle property of by CustomPenStyle property.
     
    9291    FCustomPenStyle:  TBGRAPenStyle;
    9392    FPenStyle: TPenStyle;
     93    FArrow: TBGRAArrow;
     94    FLineCap: TPenEndCap;
    9495
    9596    //Pixel data
     
    99100      AlwaysReplaceAlpha: boolean = False; RaiseErrorOnInvalidPixelFormat: boolean = True): boolean;
    100101    function GetDataPtr: PBGRAPixel; override;
    101     procedure ClearTransparentPixels;
     102    procedure ClearTransparentPixels; override;
    102103    function GetScanlineFast(y: integer): PBGRAPixel; inline;
    103104    function GetLineOrder: TRawImageLineOrder; override;
     
    144145    function GetAveragePixel: TBGRAPixel; override;
    145146    function CreateAdaptedPngWriter: TFPWriterPNG;
    146     function LoadAsBmp32(Str: TStream): boolean; override;
    147147
    148148    //drawing
     
    151151    procedure SetPenStyle(const AValue: TPenStyle); override;
    152152    function GetPenStyle: TPenStyle; override;
    153 
    154     procedure UpdateFont;
     153    function GetLineCap: TPenEndCap; override;
     154    procedure SetLineCap(AValue: TPenEndCap); override;
     155    function GetArrowEndSize: TPointF; override;
     156    function GetArrowStartSize: TPointF; override;
     157    procedure SetArrowEndSize(AValue: TPointF); override;
     158    procedure SetArrowStartSize(AValue: TPointF); override;
     159    function GetArrowEndOffset: single; override;
     160    function GetArrowStartOffset: single; override;
     161    procedure SetArrowEndOffset(AValue: single); override;
     162    procedure SetArrowStartOffset(AValue: single); override;
     163    function GetArrowEndRepeat: integer; override;
     164    function GetArrowStartRepeat: integer; override;
     165    procedure SetArrowEndRepeat(AValue: integer); override;
     166    procedure SetArrowStartRepeat(AValue: integer); override;
     167
    155168    function GetFontHeight: integer; override;
    156169    procedure SetFontHeight(AHeight: integer); override;
     
    158171    procedure SetFontFullHeight(AHeight: integer); override;
    159172    function GetFontPixelMetric: TFontPixelMetric; override;
     173    function GetFontRenderer: TBGRACustomFontRenderer; override;
     174    procedure SetFontRenderer(AValue: TBGRACustomFontRenderer); override;
    160175
    161176    function GetClipRect: TRect; override;
    162177    procedure SetClipRect(const AValue: TRect); override;
    163178
    164     function GetPixelCycleInline(ix,iy: integer; iFactX,iFactY: integer): TBGRAPixel; inline;
     179    function InternalGetPixelCycle256(ix,iy: int32or64; iFactX,iFactY: int32or64): TBGRAPixel;
     180    function InternalGetPixel256(ix,iy: int32or64; iFactX,iFactY: int32or64; smoothBorder: boolean): TBGRAPixel;
     181    function GetPolyLineOption: TBGRAPolyLineOptions;
     182    function GetArrow: TBGRAArrow;
     183    procedure SetArrowStart(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle = pjsMiter; ARelativePenWidth: single = 1; ATriangleBackOffset: single = 0); override;
     184    procedure SetArrowEnd(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle = pjsMiter; ARelativePenWidth: single = 1; ATriangleBackOffset: single = 0); override;
    165185
    166186  public
     
    170190    function GetUnique: TBGRACustomBitmap;
    171191
    172     {TFPCustomImage override}
    173     constructor Create(AWidth, AHeight: integer); override;
    174     procedure SetSize(AWidth, AHeight: integer); override;
    175 
    176     {Constructors}
    177     constructor Create; override;
    178     constructor Create(ABitmap: TBitmap); override;
    179     constructor Create(AWidth, AHeight: integer; Color: TColor); override;
    180     constructor Create(AWidth, AHeight: integer; Color: TBGRAPixel); override;
    181     constructor Create(AFilename: string); override;
    182     constructor Create(AStream: TStream); override;
    183     destructor Destroy; override;
    184 
    185     {Loading functions}
    186     function NewBitmap(AWidth, AHeight: integer): TBGRACustomBitmap; override;
    187     function NewBitmap(AWidth, AHeight: integer; Color: TBGRAPixel): TBGRACustomBitmap; override;
    188     function NewBitmap(Filename: string): TBGRACustomBitmap; override;
    189 
    190     procedure LoadFromFile(const filename: string); override;
     192    {------------------------- Constructors from TFPCustomImage----------------}
     193    constructor Create(AWidth, AHeight: integer); override; //Creates a new bitmap, initialize properties and bitmap data
     194    procedure SetSize(AWidth, AHeight: integer); override;  //Can only be called with an existing instance of TBGRABitmap.
     195                                                            //Sets the dimensions of an existing TBGRABitmap instance.
     196
     197    {------------------------- Constructors from TBGRACustomBitmap-------------}
     198    constructor Create; override;                    //Creates an image of width and height equal to zero.
     199    constructor Create(ABitmap: TBitmap); override;  //Creates an image of dimensions AWidth and AHeight and filled with transparent pixels.
     200    constructor Create(AWidth, AHeight: integer; Color: TColor); override;      //Creates an image of dimensions AWidth and AHeight and fills it with the opaque color Color.
     201    constructor Create(AWidth, AHeight: integer; Color: TBGRAPixel); override;  //Creates an image of dimensions AWidth and AHeight and fills it with Color.
     202
     203    constructor Create(AFilename: string); override; // Creates an image by loading its content from the file AFilename.
     204                                                     // The encoding of the string is the default one for the operating system.
     205                                                     // It is recommended to use the next constructor and UTF8 encoding.
     206
     207    constructor Create(AFilename: string; AIsUtf8: boolean); override; //Creates an image by loading its content from the file AFilename.
     208                                                                       //The boolean AIsUtf8Filename specifies if UTF8 encoding is assumed for the filename.
     209
     210    constructor Create(AStream: TStream); override;  // Creates an image by loading its content from the stream AStream.
     211    destructor Destroy; override;                    // Free the object and all its resources
     212
     213    {------------------------- Quasi-constructors -----------------------------}
     214    function NewBitmap(AWidth, AHeight: integer): TBGRACustomBitmap; override;  //Can only be called from an existing instance of TBGRABitmap.
     215                                                                                //Creates a new instance with dimensions AWidth and AHeight,
     216                                                                                //containing transparent pixels.
     217
     218    function NewBitmap(AWidth, AHeight: integer; Color: TBGRAPixel): TBGRACustomBitmap; override;  //Can only be called from an existing instance of TBGRABitmap.
     219                                                                                //Creates a new instance with dimensions AWidth and AHeight,
     220                                                                                //and fills it with Color.
     221
     222    function NewBitmap(Filename: string): TBGRACustomBitmap; override;          //Can only be called from an existing instance of TBGRABitmap.
     223                                                                                //Creates a new instance with by loading its content
     224                                                                                //from the file Filename. The encoding of the string
     225                                                                                //is the default one for the operating system.
     226
     227    function NewBitmap(Filename: string; AIsUtf8: boolean): TBGRACustomBitmap; override; //Can only be called from an existing instance of TBGRABitmap.
     228                                                                                //Creates a new instance with by loading its content
     229                                                                                //from the file Filename.
     230
    191231    procedure SaveToFile(const filename: string); override;
    192232    procedure SaveToStreamAsPng(Str: TStream); override;
    193     procedure Assign(ABitmap: TBitmap); override; overload;
     233    procedure Assign(ARaster: TRasterImage); override; overload;
    194234    procedure Assign(MemBitmap: TBGRACustomBitmap);override; overload;
    195235    procedure Serialize(AStream: TStream); override;
     
    198238
    199239    {Pixel functions}
    200     function PtInClipRect(x, y: integer): boolean; inline;
    201     procedure SetPixel(x, y: integer; c: TColor); override;
    202     procedure SetPixel(x, y: integer; c: TBGRAPixel); override;
    203     procedure XorPixel(x, y: integer; c: TBGRAPixel); override;
    204     procedure DrawPixel(x, y: integer; c: TBGRAPixel); override;
    205     procedure DrawPixel(x, y: integer; ec: TExpandedPixel); override;
    206     procedure FastBlendPixel(x, y: integer; c: TBGRAPixel); override;
    207     procedure ErasePixel(x, y: integer; alpha: byte); override;
    208     procedure AlphaPixel(x, y: integer; alpha: byte); override;
    209     function GetPixel(x, y: integer): TBGRAPixel; override;
    210     function GetPixel(x, y: single; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel; override;
     240    function PtInClipRect(x, y: int32or64): boolean; inline;
     241    procedure SetPixel(x, y: int32or64; c: TColor); override;
     242    procedure SetPixel(x, y: int32or64; c: TBGRAPixel); override;
     243    procedure XorPixel(x, y: int32or64; c: TBGRAPixel); override;
     244    procedure DrawPixel(x, y: int32or64; c: TBGRAPixel); override;
     245    procedure DrawPixel(x, y: int32or64; ec: TExpandedPixel); override;
     246    procedure FastBlendPixel(x, y: int32or64; c: TBGRAPixel); override;
     247    procedure ErasePixel(x, y: int32or64; alpha: byte); override;
     248    procedure AlphaPixel(x, y: int32or64; alpha: byte); override;
     249    function GetPixel(x, y: int32or64): TBGRAPixel; override;
     250    function GetPixel256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter = rfLinear; smoothBorder: boolean = true): TBGRAPixel; override;
     251    function GetPixel(x, y: single; AResampleFilter: TResampleFilter = rfLinear; smoothBorder: boolean = true): TBGRAPixel; override;
    211252    function GetPixelCycle(x, y: single; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel; override;
    212     function GetPixelCycle(x, y: single; AResampleFilter: TResampleFilter;
    213       repeatX: boolean; repeatY: boolean): TBGRAPixel; override; overload;
     253    function GetPixelCycle(x, y: single; AResampleFilter: TResampleFilter; repeatX: boolean; repeatY: boolean): TBGRAPixel; override;
     254    function GetPixelCycle256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel; override;
     255    function GetPixelCycle256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter; repeatX: boolean; repeatY: boolean): TBGRAPixel; override;
    214256
    215257    {Line primitives}
    216     procedure SetHorizLine(x, y, x2: integer; c: TBGRAPixel); override;
    217     procedure XorHorizLine(x, y, x2: integer; c: TBGRAPixel); override;
    218     procedure DrawHorizLine(x, y, x2: integer; c: TBGRAPixel); override;
    219     procedure DrawHorizLine(x, y, x2: integer; ec: TExpandedPixel); override;
    220     procedure DrawHorizLine(x, y, x2: integer; texture: IBGRAScanner); override;
    221     procedure FastBlendHorizLine(x, y, x2: integer; c: TBGRAPixel); override;
    222     procedure AlphaHorizLine(x, y, x2: integer; alpha: byte); override;
    223     procedure SetVertLine(x, y, y2: integer; c: TBGRAPixel); override;
    224     procedure XorVertLine(x, y, y2: integer; c: TBGRAPixel); override;
    225     procedure DrawVertLine(x, y, y2: integer; c: TBGRAPixel); override;
    226     procedure AlphaVertLine(x, y, y2: integer; alpha: byte); override;
    227     procedure FastBlendVertLine(x, y, y2: integer; c: TBGRAPixel); override;
    228     procedure DrawHorizLineDiff(x, y, x2: integer; c, compare: TBGRAPixel;
     258    procedure SetHorizLine(x, y, x2: int32or64; c: TBGRAPixel); override;
     259    procedure XorHorizLine(x, y, x2: int32or64; c: TBGRAPixel); override;
     260    procedure DrawHorizLine(x, y, x2: int32or64; c: TBGRAPixel); override;
     261    procedure DrawHorizLine(x, y, x2: int32or64; ec: TExpandedPixel); override;
     262    procedure HorizLine(x, y, x2: int32or64; texture: IBGRAScanner; ADrawMode : TDrawMode); override;
     263
     264    procedure FastBlendHorizLine(x, y, x2: int32or64; c: TBGRAPixel); override;
     265    procedure AlphaHorizLine(x, y, x2: int32or64; alpha: byte); override;
     266    procedure SetVertLine(x, y, y2: int32or64; c: TBGRAPixel); override;
     267    procedure XorVertLine(x, y, y2: int32or64; c: TBGRAPixel); override;
     268    procedure DrawVertLine(x, y, y2: int32or64; c: TBGRAPixel); override;
     269    procedure AlphaVertLine(x, y, y2: int32or64; alpha: byte); override;
     270    procedure FastBlendVertLine(x, y, y2: int32or64; c: TBGRAPixel); override;
     271    procedure DrawHorizLineDiff(x, y, x2: int32or64; c, compare: TBGRAPixel;
    229272      maxDiff: byte); override;
    230273
    231274    {Shapes}
    232     procedure DrawLine(x1, y1, x2, y2: integer; c: TBGRAPixel; DrawLastPixel: boolean); override;
     275    procedure DrawPath(APath: IBGRAPath; c: TBGRAPixel; w: single); override;
     276    procedure DrawPath(APath: IBGRAPath; texture: IBGRAScanner; w: single); override;
     277
     278    procedure DrawLine(x1, y1, x2, y2: integer; c: TBGRAPixel; DrawLastPixel: boolean; ADrawMode: TDrawMode = dmDrawWithTransparency); override;
    233279    procedure DrawLineAntialias(x1, y1, x2, y2: integer; c: TBGRAPixel; DrawLastPixel: boolean); override;
    234280    procedure DrawLineAntialias(x1, y1, x2, y2: integer; c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean); override;
     
    242288    procedure DrawPolyLineAntialias(const points: array of TPointF; texture: IBGRAScanner; w: single); override;
    243289    procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; Closed: boolean); override;
     290    procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; fillcolor: TBGRAPixel); override;
    244291    procedure DrawPolygonAntialias(const points: array of TPointF; c: TBGRAPixel; w: single); override;
    245292    procedure DrawPolygonAntialias(const points: array of TPointF; texture: IBGRAScanner; w: single); override;
     293    procedure DrawPolygonAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; fillcolor: TBGRAPixel); override;
    246294
    247295    procedure EraseLine(x1, y1, x2, y2: integer; alpha: byte; DrawLastPixel: boolean); override;
     
    250298    procedure EraseLineAntialias(x1, y1, x2, y2: single; alpha: byte; w: single; Closed: boolean); override;
    251299    procedure ErasePolyLineAntialias(const points: array of TPointF; alpha: byte; w: single); override;
     300
     301    procedure FillPath(APath: IBGRAPath; c: TBGRAPixel); override;
     302    procedure FillPath(APath: IBGRAPath; texture: IBGRAScanner); override;
    252303
    253304    procedure FillTriangleLinearColor(pt1,pt2,pt3: TPointF; c1,c2,c3: TBGRAPixel); override;
     
    263314    procedure FillQuadLinearMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); override;
    264315    procedure FillQuadPerspectiveMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); override;
     316    procedure FillQuadPerspectiveMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACleanBorders: TRect); override;
    265317    procedure FillQuadPerspectiveMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); override;
     318    procedure FillQuadPerspectiveMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACleanBorders: TRect); override;
    266319
    267320    procedure FillPolyLinearMapping(const points: array of TPointF; texture: IBGRAScanner; texCoords: array of TPointF; TextureInterpolation: Boolean); override;
     
    270323    procedure FillPolyPerspectiveMapping(const points: array of TPointF; const pointsZ: array of single; texture: IBGRAScanner; texCoords: array of TPointF; TextureInterpolation: Boolean; zbuffer: psingle = nil); override;
    271324    procedure FillPolyPerspectiveMappingLightness(const points: array of TPointF; const pointsZ: array of single; texture: IBGRAScanner; texCoords: array of TPointF; lightnesses: array of word; TextureInterpolation: Boolean; zbuffer: psingle = nil); override;
     325
    272326    procedure FillPoly(const points: array of TPointF; c: TBGRAPixel; drawmode: TDrawMode); override;
    273327    procedure FillPoly(const points: array of TPointF; texture: IBGRAScanner; drawmode: TDrawMode); override;
     
    276330    procedure ErasePoly(const points: array of TPointF; alpha: byte); override;
    277331    procedure ErasePolyAntialias(const points: array of TPointF; alpha: byte); override;
     332
     333    procedure FillShape(shape: TBGRACustomFillInfo; c: TBGRAPixel; drawmode: TDrawMode); override;
     334    procedure FillShape(shape: TBGRACustomFillInfo; texture: IBGRAScanner; drawmode: TDrawMode); override;
     335    procedure FillShapeAntialias(shape: TBGRACustomFillInfo; c: TBGRAPixel); override;
     336    procedure FillShapeAntialias(shape: TBGRACustomFillInfo; texture: IBGRAScanner); override;
     337    procedure EraseShape(shape: TBGRACustomFillInfo; alpha: byte); override;
     338    procedure EraseShapeAntialias(shape: TBGRACustomFillInfo; alpha: byte); override;
    278339
    279340    procedure EllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel; w: single); override;
     
    295356    procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; penTexture: IBGRAScanner; w: single; fillTexture: IBGRAScanner; options: TRoundRectangleOptions = []); override;
    296357
    297     procedure FillRect(x, y, x2, y2: integer; c: TBGRAPixel; mode: TDrawMode); override;
    298     procedure FillRect(x, y, x2, y2: integer; texture: IBGRAScanner; mode: TDrawMode); override;
     358    procedure FillRect(x, y, x2, y2: integer; c: TBGRAPixel; mode: TDrawMode); override; overload;
     359    procedure FillRect(x, y, x2, y2: integer; texture: IBGRAScanner; mode: TDrawMode); override; overload;
    299360    procedure FillRectAntialias(x, y, x2, y2: single; c: TBGRAPixel); override;
    300361    procedure EraseRectAntialias(x, y, x2, y2: single; alpha: byte); override;
     
    305366    procedure AlphaFillRect(x, y, x2, y2: integer; alpha: byte); override;
    306367    procedure RoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer;
    307       BorderColor, FillColor: TBGRAPixel); override;
    308 
    309     procedure TextOutAngle(x, y: single; orientation: integer; s: string; c: TBGRAPixel; align: TAlignment); override;
    310     procedure TextOutAngle(x, y: single; orientation: integer; s: string; texture: IBGRAScanner; align: TAlignment); override;
    311     procedure TextOut(x, y: single; s: string; texture: IBGRAScanner; align: TAlignment); override;
    312     procedure TextOut(x, y: single; s: string; c: TBGRAPixel; align: TAlignment); override;
    313     procedure TextRect(ARect: TRect; x, y: integer; s: string; style: TTextStyle; c: TBGRAPixel); override;
    314     procedure TextRect(ARect: TRect; x, y: integer; s: string; style: TTextStyle; texture: IBGRAScanner); override;
    315     function TextSize(s: string): TSize; override;
     368      BorderColor, FillColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); override;
     369    procedure RoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer;
     370      BorderColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); override;
     371
     372    { Draws the UTF8 encoded string, with color c.
     373      If align is taLeftJustify, (x,y) is the top-left corner.
     374      If align is taCenter, (x,y) is at the top and middle of the text.
     375      If align is taRightJustify, (x,y) is the top-right corner.
     376      The value of FontOrientation is taken into account, so that the text may be rotated. }
     377    procedure TextOut(x, y: single; sUTF8: string; c: TBGRAPixel; align: TAlignment); override; overload;
     378
     379    { Same as above functions, except that the text is filled using texture.
     380      The value of FontOrientation is taken into account, so that the text may be rotated. }
     381    procedure TextOut(x, y: single; sUTF8: string; texture: IBGRAScanner; align: TAlignment); override; overload;
     382
     383    { Same as above, except that the orientation is specified, overriding the value of the property FontOrientation. }
     384    procedure TextOutAngle(x, y: single; orientationTenthDegCCW: integer; sUTF8: string; c: TBGRAPixel; align: TAlignment); override; overload;
     385    procedure TextOutAngle(x, y: single; orientationTenthDegCCW: integer; sUTF8: string; texture: IBGRAScanner; align: TAlignment); override; overload;
     386
     387    { Draw the UTF8 encoded string at the coordinate (x,y), clipped inside the rectangle ARect.
     388      Additional style information is provided by the style parameter.
     389      The color c or texture is used to fill the text. No rotation is applied. }
     390    procedure TextRect(ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; c: TBGRAPixel); override; overload;
     391    procedure TextRect(ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; texture: IBGRAScanner); override; overload;
     392
     393    { Returns the total size of the string provided using the current font.
     394      Orientation is not taken into account, so that the width is along the text.  }
     395    function TextSize(sUTF8: string): TSize; override;
    316396
    317397    {Spline}
     
    344424    procedure DrawPixels(c: TBGRAPixel; start, Count: integer); override;
    345425    procedure AlphaFill(alpha: byte; start, Count: integer); override;
    346     procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; color: TBGRAPixel); override;
    347     procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner); override;
     426    procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; color: TBGRAPixel; ADrawMode: TDrawMode); override;
     427    procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner; ADrawMode: TDrawMode); override;
    348428    procedure FillClearTypeMask(x,y: integer; xThird: integer; AMask: TBGRACustomBitmap; color: TBGRAPixel; ARGBOrder: boolean = true); override;
    349429    procedure FillClearTypeMask(x,y: integer; xThird: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner; ARGBOrder: boolean = true); override;
     
    380460
    381461    {BGRA bitmap functions}
     462    procedure CrossFade(ARect: TRect; Source1, Source2: IBGRAScanner; AFadePosition: byte; mode: TDrawMode = dmDrawWithTransparency); override;
     463    procedure CrossFade(ARect: TRect; Source1, Source2: IBGRAScanner; AFadeMask: IBGRAScanner; mode: TDrawMode = dmDrawWithTransparency); override;
    382464    procedure PutImage(x, y: integer; Source: TBGRACustomBitmap; mode: TDrawMode; AOpacity: byte = 255); override;
    383     procedure PutImageAngle(x,y: single; Source: TBGRACustomBitmap; angle: single; imageCenterX: single = 0; imageCenterY: single = 0; AOpacity: Byte=255; ARestoreOffsetAfterRotation: boolean = false); override;
    384     procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AOpacity: Byte=255); override;
     465    procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AOutputBounds: TRect; AResampleFilter: TResampleFilter; AMode: TDrawMode; AOpacity: Byte=255); override;
     466    procedure StretchPutImage(ARect: TRect; Source: TBGRACustomBitmap; mode: TDrawMode; AOpacity: byte = 255); override;
     467
    385468    procedure BlendImage(x, y: integer; Source: TBGRACustomBitmap; operation: TBlendOperation); override;
    386469    procedure BlendImageOver(x, y: integer; Source: TBGRACustomBitmap; operation: TBlendOperation; AOpacity: byte = 255;
     
    394477    function Equals(comp: TBGRAPixel): boolean; override;
    395478    function GetImageBounds(Channel: TChannel = cAlpha; ANothingValue: Byte = 0): TRect; override;
    396     function GetImageBounds(Channels: TChannels): TRect; override;
     479    function GetImageBounds(Channels: TChannels; ANothingValue: Byte = 0): TRect; override;
    397480    function GetDifferenceBounds(ABitmap: TBGRACustomBitmap): TRect; override;
    398481    function MakeBitmapCopy(BackgroundColor: TColor): TBitmap; override;
     
    400483    function Resample(newWidth, newHeight: integer;
    401484      mode: TResampleMode = rmFineResample): TBGRACustomBitmap; override;
    402     procedure VerticalFlip; override;
    403     procedure HorizontalFlip; override;
     485    procedure VerticalFlip(ARect: TRect); override;
     486    procedure HorizontalFlip(ARect: TRect); override;
    404487    function RotateCW: TBGRACustomBitmap; override;
    405488    function RotateCCW: TBGRACustomBitmap; override;
    406489    procedure Negative; override;
     490    procedure NegativeRect(ABounds: TRect); override;
    407491    procedure LinearNegative; override;
     492    procedure LinearNegativeRect(ABounds: TRect); override;
     493    procedure InplaceGrayscale; override;
     494    procedure InplaceGrayscale(ABounds: TRect); override;
    408495    procedure SwapRedBlue; override;
    409496    procedure GrayscaleToAlpha; override;
    410497    procedure AlphaToGrayscale; override;
    411     procedure ApplyMask(mask: TBGRACustomBitmap); override;
     498    procedure ApplyMask(mask: TBGRACustomBitmap; ARect: TRect; AMaskRectTopLeft: TPoint); override;
    412499    procedure ApplyGlobalOpacity(alpha: byte); override;
    413500    procedure ConvertToLinearRGB; override;
    414501    procedure ConvertFromLinearRGB; override;
     502    procedure DrawCheckers(ARect: TRect; AColorEven,AColorOdd: TBGRAPixel);
    415503
    416504    {Filters}
     
    418506    function FilterMedian(Option: TMedianOption): TBGRACustomBitmap; override;
    419507    function FilterSmooth: TBGRACustomBitmap; override;
    420     function FilterSharpen: TBGRACustomBitmap; override;
     508    function FilterSharpen(Amount: single = 1): TBGRACustomBitmap; override;
     509    function FilterSharpen(ABounds: TRect; Amount: single = 1): TBGRACustomBitmap; override;
    421510    function FilterContour: TBGRACustomBitmap; override;
     511    function FilterPixelate(pixelSize: integer; useResample: boolean; filter: TResampleFilter = rfLinear): TBGRACustomBitmap; override;
    422512    function FilterBlurRadial(radius: integer;
    423513      blurType: TRadialBlurType): TBGRACustomBitmap; override;
    424     function FilterPixelate(pixelSize: integer; useResample: boolean; filter: TResampleFilter = rfLinear): TBGRACustomBitmap; override;
     514    function FilterBlurRadial(ABounds: TRect; radius: integer;
     515      blurType: TRadialBlurType): TBGRACustomBitmap; override;
    425516    function FilterBlurMotion(distance: integer; angle: single;
    426517      oriented: boolean): TBGRACustomBitmap; override;
     518    function FilterBlurMotion(ABounds: TRect; distance: integer; angle: single;
     519      oriented: boolean): TBGRACustomBitmap; override;
    427520    function FilterCustomBlur(mask: TBGRACustomBitmap): TBGRACustomBitmap; override;
     521    function FilterCustomBlur(ABounds: TRect; mask: TBGRACustomBitmap): TBGRACustomBitmap; override;
    428522    function FilterEmboss(angle: single): TBGRACustomBitmap; override;
     523    function FilterEmboss(angle: single; ABounds: TRect): TBGRACustomBitmap; override;
    429524    function FilterEmbossHighlight(FillSelection: boolean): TBGRACustomBitmap; override;
    430525    function FilterEmbossHighlight(FillSelection: boolean; BorderColor: TBGRAPixel): TBGRACustomBitmap; override;
    431526    function FilterEmbossHighlight(FillSelection: boolean; BorderColor: TBGRAPixel; var Offset: TPoint): TBGRACustomBitmap; override;
    432527    function FilterGrayscale: TBGRACustomBitmap; override;
     528    function FilterGrayscale(ABounds: TRect): TBGRACustomBitmap; override;
    433529    function FilterNormalize(eachChannel: boolean = True): TBGRACustomBitmap; override;
    434     function FilterRotate(origin: TPointF; angle: single): TBGRACustomBitmap; override;
     530    function FilterNormalize(ABounds: TRect; eachChannel: boolean = True): TBGRACustomBitmap; override;
     531    function FilterRotate(origin: TPointF; angle: single; correctBlur: boolean = false): TBGRACustomBitmap; override;
    435532    function FilterSphere: TBGRACustomBitmap; override;
    436533    function FilterTwirl(ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap; override;
     534    function FilterTwirl(ABounds: TRect; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap; override;
    437535    function FilterCylinder: TBGRACustomBitmap; override;
    438536    function FilterPlane: TBGRACustomBitmap; override;
     
    465563
    466564uses Math, LCLIntf, LCLType,
    467   BGRABlend, BGRAFilters, BGRAPen, BGRAText, BGRATextFX, BGRAGradientScanner,
     565  BGRABlend, BGRAFilters, BGRAText, BGRATextFX, BGRAGradientScanner,
    468566  BGRAResample, BGRATransform, BGRAPolygon, BGRAPolygonAliased,
    469567  BGRAPath, FPReadPcx, FPWritePcx, FPReadXPM, FPWriteXPM;
     
    486584procedure TBitmapTracker.Changed(Sender: TObject);
    487585begin
    488   FUser.FBitmapModified := True;
     586  if FUser <> nil then
     587    FUser.FBitmapModified := True;
    489588  inherited Changed(Sender);
    490589end;
     
    570669end;
    571670
    572 { Update font properties to internal TFont object }
    573 procedure TBGRADefaultBitmap.UpdateFont;
    574 begin
    575   if FFont.Name <> FontName then
    576     FFont.Name := FontName;
    577   if FFont.Style <> FontStyle then
    578     FFont.Style := FontStyle;
    579   if FFont.Height <> FFontHeight * FFontHeightSign then
    580     FFont.Height := FFontHeight * FFontHeightSign;
    581   if FFont.Orientation <> FontOrientation then
    582     FFont.Orientation := FontOrientation;
    583   if FontQuality = fqSystemClearType then
    584     FFont.Quality := fqCleartype
    585   else
    586     FFont.Quality := FontDefaultQuality;
     671function TBGRADefaultBitmap.GetLineCap: TPenEndCap;
     672begin
     673  result := FLineCap;
     674end;
     675
     676procedure TBGRADefaultBitmap.SetLineCap(AValue: TPenEndCap);
     677begin
     678  if AValue <> FLineCap then
     679  begin
     680    FLineCap:= AValue;
     681    if Assigned(FArrow) then FArrow.LineCap := AValue;
     682  end;
     683end;
     684
     685function TBGRADefaultBitmap.GetArrowEndSize: TPointF;
     686begin
     687  result := GetArrow.EndSize;
     688end;
     689
     690function TBGRADefaultBitmap.GetArrowStartSize: TPointF;
     691begin
     692  result := GetArrow.StartSize;
     693end;
     694
     695procedure TBGRADefaultBitmap.SetArrowEndSize(AValue: TPointF);
     696begin
     697  GetArrow.EndSize := AValue;
     698end;
     699
     700procedure TBGRADefaultBitmap.SetArrowStartSize(AValue: TPointF);
     701begin
     702  GetArrow.StartSize := AValue;
     703end;
     704
     705function TBGRADefaultBitmap.GetArrowEndOffset: single;
     706begin
     707  result := GetArrow.EndOffsetX;
     708end;
     709
     710function TBGRADefaultBitmap.GetArrowStartOffset: single;
     711begin
     712  result := GetArrow.StartOffsetX;
     713end;
     714
     715procedure TBGRADefaultBitmap.SetArrowEndOffset(AValue: single);
     716begin
     717  GetArrow.EndOffsetX := AValue;
     718end;
     719
     720procedure TBGRADefaultBitmap.SetArrowStartOffset(AValue: single);
     721begin
     722  GetArrow.StartOffsetX := AValue;
     723end;
     724
     725function TBGRADefaultBitmap.GetArrowEndRepeat: integer;
     726begin
     727  result := GetArrow.EndRepeatCount;
     728end;
     729
     730function TBGRADefaultBitmap.GetArrowStartRepeat: integer;
     731begin
     732  result := GetArrow.StartRepeatCount;
     733end;
     734
     735procedure TBGRADefaultBitmap.SetArrowEndRepeat(AValue: integer);
     736begin
     737  GetArrow.EndRepeatCount := AValue;
     738end;
     739
     740procedure TBGRADefaultBitmap.SetArrowStartRepeat(AValue: integer);
     741begin
     742  GetArrow.StartRepeatCount := AValue;
    587743end;
    588744
     
    609765
    610766function TBGRADefaultBitmap.GetFontPixelMetric: TFontPixelMetric;
    611 var fxFont: TFont;
    612 begin
    613   UpdateFont;
    614   if FontQuality = fqSystem then
    615     result := BGRAText.GetFontPixelMetric(FFont)
    616   else
    617   begin
    618     FxFont := TFont.Create;
    619     FxFont.Assign(FFont);
    620     FxFont.Height := fxFont.Height*FontAntialiasingLevel;
    621     Result:= BGRAText.GetFontPixelMetric(FxFont);
    622     if Result.Baseline <> -1 then Result.Baseline:= round((Result.Baseline-1)/FontAntialiasingLevel);
    623     if Result.CapLine <> -1 then Result.CapLine:= round(Result.CapLine/FontAntialiasingLevel);
    624     if Result.DescentLine <> -1 then Result.DescentLine:= round((Result.DescentLine-1)/FontAntialiasingLevel);
    625     if Result.Lineheight <> -1 then Result.Lineheight:= round(Result.Lineheight/FontAntialiasingLevel);
    626     if Result.xLine <> -1 then Result.xLine:= round(Result.xLine/FontAntialiasingLevel);
    627   end;
     767begin
     768  result := FontRenderer.GetFontPixelMetric;
     769end;
     770
     771function TBGRADefaultBitmap.GetFontRenderer: TBGRACustomFontRenderer;
     772begin
     773  if FFontRenderer = nil then FFontRenderer := TLCLFontRenderer.Create;
     774  result := FFontRenderer;
     775  result.FontName := FontName;
     776  result.FontStyle := FontStyle;
     777  result.FontQuality := FontQuality;
     778  result.FontOrientation := FontOrientation;
     779  result.FontEmHeight := FFontHeight;
     780end;
     781
     782procedure TBGRADefaultBitmap.SetFontRenderer(AValue: TBGRACustomFontRenderer);
     783begin
     784  if AValue = FFontRenderer then exit;
     785  FFontRenderer.Free;
     786  FFontRenderer := AValue
    628787end;
    629788
     
    689848end;
    690849
    691 { Creates a new bitmap. Internally, it uses the same type so that if you
     850{ Creates a new bitmap with dimensions AWidth and AHeight and filled with
     851  transparent pixels. Internally, it uses the same type so that if you
    692852  use an optimized version, you get a new bitmap with the same optimizations }
    693853function TBGRADefaultBitmap.NewBitmap(AWidth, AHeight: integer): TBGRACustomBitmap;
     
    701861end;
    702862
     863{ Can only be called from an existing instance of TBGRABitmap.
     864  Creates a new instance with dimensions AWidth and AHeight,
     865  and fills it with Color. }
    703866function TBGRADefaultBitmap.NewBitmap(AWidth, AHeight: integer;
    704867  Color: TBGRAPixel): TBGRACustomBitmap;
     
    712875end;
    713876
    714 { Creates a new bitmap and loads it contents from a file }
     877{ Creates a new bitmap and loads it contents from a file.
     878  The encoding of the string is the default one for the operating system.
     879  It is recommended to use the next function and UTF8 encoding }
    715880function TBGRADefaultBitmap.NewBitmap(Filename: string): TBGRACustomBitmap;
    716881var
     
    719884  BGRAClass := TBGRABitmapAny(self.ClassType);
    720885  Result    := BGRAClass.Create(Filename);
     886end;
     887
     888{ Creates a new bitmap and loads it contents from a file.
     889  It is recommended to use UTF8 encoding }
     890function TBGRADefaultBitmap.NewBitmap(Filename: string; AIsUtf8: boolean): TBGRACustomBitmap;
     891var
     892  BGRAClass: TBGRABitmapAny;
     893begin
     894  BGRAClass := TBGRABitmapAny(self.ClassType);
     895  Result    := BGRAClass.Create(Filename,AIsUtf8);
    721896end;
    722897
     
    754929{---------------------- Constructors ---------------------------------}
    755930
     931{ Creates an image of width and height equal to zero. }
    756932constructor TBGRADefaultBitmap.Create;
    757933begin
     
    760936end;
    761937
     938{ Creates an image of dimensions AWidth and AHeight and filled with transparent pixels. }
    762939constructor TBGRADefaultBitmap.Create(ABitmap: TBitmap);
    763940begin
     
    767944end;
    768945
     946{ Creates an image of dimensions AWidth and AHeight and fills it with the opaque color Color. }
    769947constructor TBGRADefaultBitmap.Create(AWidth, AHeight: integer; Color: TColor);
    770948begin
     
    774952end;
    775953
     954{ Creates an image of dimensions AWidth and AHeight and fills it with Color. }
    776955constructor TBGRADefaultBitmap.Create(AWidth, AHeight: integer; Color: TBGRAPixel);
    777956begin
     
    781960end;
    782961
     962{ Creates an image by loading its content from the file AFilename.
     963  The encoding of the string is the default one for the operating system.
     964  It is recommended to use the next constructor and UTF8 encoding. }
     965constructor TBGRADefaultBitmap.Create(AFilename: string);
     966begin
     967  Init;
     968  inherited Create(0, 0);
     969  LoadFromFile(Afilename);
     970end;
     971
     972{ Free the object and all its resources }
    783973destructor TBGRADefaultBitmap.Destroy;
    784974begin
    785975  FreeData;
    786   FFont.Free;
     976  FFontRenderer.Free;
    787977  FBitmap.Free;
    788978  FCanvasFP.Free;
    789979  FCanvasBGRA.Free;
    790980  FCanvas2D.Free;
     981  FArrow.Free;
    791982  inherited Destroy;
    792983end;
     
    794985{------------------------- Loading functions ----------------------------------}
    795986
    796 constructor TBGRADefaultBitmap.Create(AFilename: string);
     987{ Creates an image by loading its content from the file AFilename.
     988  The boolean AIsUtf8Filename specifies if UTF8 encoding is assumed for the filename. }
     989constructor TBGRADefaultBitmap.Create(AFilename: string; AIsUtf8: boolean);
    797990begin
    798991  Init;
    799   inherited Create(0, 0); 
    800   LoadFromFile(Afilename);
    801 end;
    802 
     992  inherited Create(0, 0);
     993  if AIsUtf8 then
     994    LoadFromFileUTF8(Afilename)
     995  else
     996    LoadFromFile(Afilename);
     997end;
     998
     999{ Creates an image by loading its content from the stream AStream. }
    8031000constructor TBGRADefaultBitmap.Create(AStream: TStream);
    8041001begin
     
    8081005end;
    8091006
    810 procedure TBGRADefaultBitmap.Assign(ABitmap: TBitmap);
     1007procedure TBGRADefaultBitmap.Assign(ARaster: TRasterImage);
    8111008var TempBmp: TBitmap;
    8121009    ConvertOk: boolean;
    8131010begin
    8141011  DiscardBitmapChange;
    815   SetSize(ABitmap.Width, ABitmap.Height);
    816   if not LoadFromRawImage(ABitmap.RawImage,0,False,False) then
     1012  SetSize(ARaster.Width, ARaster.Height);
     1013  if not LoadFromRawImage(ARaster.RawImage,0,False,False) then
     1014  if ARaster is TBitmap then
    8171015  begin //try to convert
    8181016    TempBmp := TBitmap.Create;
    819     TempBmp.Width := ABitmap.Width;
    820     TempBmp.Height := ABitmap.Height;
    821     TempBmp.Canvas.Draw(0,0,ABitmap);
     1017    TempBmp.Width := ARaster.Width;
     1018    TempBmp.Height := ARaster.Height;
     1019    TempBmp.Canvas.Draw(0,0,ARaster);
    8221020    ConvertOk := LoadFromRawImage(TempBmp.RawImage,0,False,False);
    8231021    TempBmp.Free;
    8241022    if not ConvertOk then
    8251023      raise Exception.Create('Unable to convert image to 24 bit');
    826   end;
     1024  end else
     1025    raise Exception.Create('Unable to convert image to 24 bit');
    8271026  If Empty then AlphaFill(255); // if bitmap seems to be empty, assume
    8281027                                // it is an opaque bitmap without alpha channel
     
    8371036
    8381037procedure TBGRADefaultBitmap.Serialize(AStream: TStream);
    839 var lWidth,lHeight: integer;
     1038var lWidth,lHeight,y: integer;
    8401039begin
    8411040  lWidth := NtoLE(Width);
     
    8431042  AStream.Write(lWidth,sizeof(lWidth));
    8441043  AStream.Write(lHeight,sizeof(lHeight));
    845   AStream.Write(Data^, NbPixels*sizeof(TBGRAPixel));
     1044  for y := 0 to Height-1 do
     1045    AStream.Write(ScanLine[y]^, Width*sizeof(TBGRAPixel));
    8461046end;
    8471047
    8481048{$hints off}
    8491049procedure TBGRADefaultBitmap.Deserialize(AStream: TStream);
    850 var lWidth,lHeight: integer;
     1050var lWidth,lHeight,y: integer;
    8511051begin
    8521052  AStream.Read(lWidth,sizeof(lWidth));
     
    8551055  lHeight := LEtoN(lHeight);
    8561056  SetSize(lWidth,lHeight);
    857   AStream.Read(Data^, NbPixels*sizeof(TBGRAPixel));
     1057  for y := 0 to Height-1 do
     1058    AStream.Read(ScanLine[y]^, Width*sizeof(TBGRAPixel));
    8581059end;
    8591060{$hints on}
     
    8651066  AStream.Write(zero,sizeof(zero));
    8661067  AStream.Write(zero,sizeof(zero));
    867 end;
    868 
    869 procedure TBGRADefaultBitmap.LoadFromFile(const filename: string);
    870 var
    871   OldDrawMode: TDrawMode;
    872 begin
    873   OldDrawMode := CanvasDrawModeFP;
    874   CanvasDrawModeFP := dmSet;
    875   ClipRect := rect(0,0,Width,Height);
    876   try
    877     inherited LoadFromfile(filename);
    878   finally
    879     CanvasDrawModeFP := OldDrawMode;
    880     ClearTransparentPixels;
    881   end;
    8821068end;
    8831069
     
    9181104
    9191105{ Check if a point is in the clipping rectangle }
    920 function TBGRADefaultBitmap.PtInClipRect(x, y: integer): boolean;
     1106function TBGRADefaultBitmap.PtInClipRect(x, y: int32or64): boolean;
    9211107begin
    9221108  result := (x >= FClipRect.Left) and (y >= FClipRect.Top) and (x < FClipRect.Right) and (y < FClipRect.Bottom);
     
    9431129end;
    9441130
    945 function TBGRADefaultBitmap.GetPixelCycleInline(ix, iy: integer; iFactX,
    946   iFactY: integer): TBGRAPixel;
    947 var
    948   ixMod1,ixMod2: integer;
    949   w1,w2,w3,w4,alphaW: cardinal;
    950   bSum, gSum, rSum: cardinal;
    951   aSum: cardinal;
     1131function TBGRADefaultBitmap.InternalGetPixelCycle256(ix, iy: int32or64; iFactX,
     1132  iFactY: int32or64): TBGRAPixel;
     1133var
     1134  ixMod1,ixMod2: int32or64;
     1135  w1,w2,w3,w4,alphaW: UInt32or64;
     1136  bSum, gSum, rSum: UInt32or64;
     1137  aSum: UInt32or64;
    9521138
    9531139  c:    TBGRAPixel;
     
    9641150  aSum   := 0;
    9651151
    966   scan := GetScanlineFast(PositiveMod(iy,Height));
    967 
    968   ixMod1 := PositiveMod(ix,Width); //apply cycle
    969   c      := (scan + ixMod1)^;
     1152  scan := GetScanlineFast(iy);
     1153
     1154  ixMod1 := ix;
     1155  c      := (scan + ix)^;
    9701156  alphaW := c.alpha * w1;
    9711157  aSum   += alphaW;
     
    9751161  bSum   += c.blue * alphaW;
    9761162
    977   Inc(ix);
    978   ixMod2 := PositiveMod(ix,Width); //apply cycle
     1163  ixMod2 := ix+1;
     1164  if ixMod2=Width then ixMod2 := 0;
    9791165  c      := (scan + ixMod2)^;
    9801166  alphaW := c.alpha * w2;
     
    9861172
    9871173  Inc(iy);
    988   scan := GetScanlineFast(PositiveMod(iy,Height));
     1174  if iy = Height then iy := 0;
     1175  scan := GetScanlineFast(iy);
    9891176
    9901177  c      := (scan + ixMod2)^;
     
    10141201  end;
    10151202end;
     1203
     1204function TBGRADefaultBitmap.InternalGetPixel256(ix, iy: int32or64; iFactX,
     1205  iFactY: int32or64; smoothBorder: boolean): TBGRAPixel;
     1206var
     1207  w1,w2,w3,w4,alphaW: cardinal;
     1208  rSum, gSum, bSum: cardinal; //rgbDiv = aSum
     1209  aSum, aDiv: cardinal;
     1210  c:    TBGRAPixel;
     1211  scan: PBGRAPixel;
     1212begin
     1213  rSum   := 0;
     1214  gSum   := 0;
     1215  bSum   := 0;
     1216  aSum   := 0;
     1217  aDiv   := 0;
     1218
     1219  w4 := (iFactX*iFactY+127) shr 8;
     1220  w3 := iFactY-w4;
     1221  {$PUSH}{$HINTS OFF}
     1222  w1 := (256-iFactX)-w3;
     1223  {$POP}
     1224  w2 := iFactX-w4;
     1225
     1226  { For each pixel around the coordinate, compute
     1227    the weight for it and multiply values by it before
     1228    adding to the sum }
     1229  if (iy >= 0) and (iy < Height) then
     1230  begin
     1231    scan := GetScanlineFast(iy);
     1232
     1233    if (ix >= 0) and (ix < Width) then
     1234    begin
     1235      c      := (scan + ix)^;
     1236      alphaW := c.alpha * w1;
     1237      aDiv   += w1;
     1238      aSum   += alphaW;
     1239      rSum   += c.red * alphaW;
     1240      gSum   += c.green * alphaW;
     1241      bSum   += c.blue * alphaW;
     1242    end;
     1243
     1244    Inc(ix);
     1245    if (ix >= 0) and (ix < Width) then
     1246    begin
     1247      c      := (scan + ix)^;
     1248      alphaW := c.alpha * w2;
     1249      aDiv   += w2;
     1250      aSum   += alphaW;
     1251      rSum   += c.red * alphaW;
     1252      gSum   += c.green * alphaW;
     1253      bSum   += c.blue * alphaW;
     1254    end;
     1255  end
     1256  else
     1257  begin
     1258    Inc(ix);
     1259  end;
     1260
     1261  Inc(iy);
     1262  if (iy >= 0) and (iy < Height) then
     1263  begin
     1264    scan := GetScanlineFast(iy);
     1265
     1266    if (ix >= 0) and (ix < Width) then
     1267    begin
     1268      c      := (scan + ix)^;
     1269      alphaW := c.alpha * w4;
     1270      aDiv   += w4;
     1271      aSum   += alphaW;
     1272      rSum   += c.red * alphaW;
     1273      gSum   += c.green * alphaW;
     1274      bSum   += c.blue * alphaW;
     1275    end;
     1276
     1277    Dec(ix);
     1278    if (ix >= 0) and (ix < Width) then
     1279    begin
     1280      c      := (scan + ix)^;
     1281      alphaW := c.alpha * w3;
     1282      aDiv   += w3;
     1283      aSum   += alphaW;
     1284      rSum   += c.red * alphaW;
     1285      gSum   += c.green * alphaW;
     1286      bSum   += c.blue * alphaW;
     1287    end;
     1288  end;
     1289
     1290  if aSum < 128 then //if there is no alpha
     1291    Result := BGRAPixelTransparent
     1292  else
     1293  begin
     1294    Result.red   := (rSum + aSum shr 1) div aSum;
     1295    Result.green := (gSum + aSum shr 1) div aSum;
     1296    Result.blue  := (bSum + aSum shr 1) div aSum;
     1297    if smoothBorder or (aDiv = 256) then
     1298      Result.alpha := (aSum + 128) shr 8
     1299    else
     1300      Result.alpha := (aSum + aDiv shr 1) div aDiv;
     1301  end;
     1302end;
     1303
     1304function TBGRADefaultBitmap.GetPolyLineOption: TBGRAPolyLineOptions;
     1305begin
     1306  result := [];
     1307  if Assigned(FArrow) and FArrow.IsStartDefined then result += [plNoStartCap];
     1308  if Assigned(FArrow) and FArrow.IsEndDefined then result += [plNoEndCap];
     1309end;
     1310
     1311function TBGRADefaultBitmap.GetArrow: TBGRAArrow;
     1312begin
     1313  if FArrow = nil then
     1314  begin
     1315    FArrow := TBGRAArrow.Create;
     1316    FArrow.LineCap := LineCap;
     1317  end;
     1318  result := FArrow;
     1319end;
     1320
    10161321{-------------------------- Pixel functions -----------------------------------}
    10171322
    1018 procedure TBGRADefaultBitmap.SetPixel(x, y: integer; c: TBGRAPixel);
     1323procedure TBGRADefaultBitmap.SetPixel(x, y: int32or64; c: TBGRAPixel);
    10191324begin
    10201325  if not PtInClipRect(x,y) then exit;
     
    10241329end;
    10251330
    1026 procedure TBGRADefaultBitmap.XorPixel(x, y: integer; c: TBGRAPixel);
     1331procedure TBGRADefaultBitmap.XorPixel(x, y: int32or64; c: TBGRAPixel);
    10271332var
    10281333  p : PDWord;
     
    10351340end;
    10361341
    1037 procedure TBGRADefaultBitmap.SetPixel(x, y: integer; c: TColor);
     1342procedure TBGRADefaultBitmap.SetPixel(x, y: int32or64; c: TColor);
    10381343var
    10391344  p: PByte;
     
    10521357end;
    10531358
    1054 procedure TBGRADefaultBitmap.DrawPixel(x, y: integer; c: TBGRAPixel);
     1359procedure TBGRADefaultBitmap.DrawPixel(x, y: int32or64; c: TBGRAPixel);
    10551360begin
    10561361  if not PtInClipRect(x,y) then exit;
     
    10601365end;
    10611366
    1062 procedure TBGRADefaultBitmap.DrawPixel(x, y: integer; ec: TExpandedPixel);
     1367procedure TBGRADefaultBitmap.DrawPixel(x, y: int32or64; ec: TExpandedPixel);
    10631368begin
    10641369  if not PtInClipRect(x,y) then exit;
     
    10681373end;
    10691374
    1070 procedure TBGRADefaultBitmap.FastBlendPixel(x, y: integer; c: TBGRAPixel);
     1375procedure TBGRADefaultBitmap.FastBlendPixel(x, y: int32or64; c: TBGRAPixel);
    10711376begin
    10721377  if not PtInClipRect(x,y) then exit;
     
    10761381end;
    10771382
    1078 procedure TBGRADefaultBitmap.ErasePixel(x, y: integer; alpha: byte);
     1383procedure TBGRADefaultBitmap.ErasePixel(x, y: int32or64; alpha: byte);
    10791384begin
    10801385  if not PtInClipRect(x,y) then exit;
     
    10841389end;
    10851390
    1086 procedure TBGRADefaultBitmap.AlphaPixel(x, y: integer; alpha: byte);
     1391procedure TBGRADefaultBitmap.AlphaPixel(x, y: int32or64; alpha: byte);
    10871392begin
    10881393  if not PtInClipRect(x,y) then exit;
     
    10951400end;
    10961401
    1097 function TBGRADefaultBitmap.GetPixel(x, y: integer): TBGRAPixel;
     1402function TBGRADefaultBitmap.GetPixel(x, y: int32or64): TBGRAPixel;
    10981403begin
    10991404  if (x < 0) or (x >= Width) or (y < 0) or (y >= Height) then //it is possible to read pixels outside of the cliprect
     
    11061411end;
    11071412
     1413function TBGRADefaultBitmap.GetPixel256(x, y, fracX256, fracY256: int32or64;
     1414  AResampleFilter: TResampleFilter; smoothBorder: boolean = true): TBGRAPixel;
     1415begin
     1416  if (fracX256 = 0) and (fracY256 = 0) then
     1417    result := GetPixel(x,y)
     1418  else if AResampleFilter = rfBox then
     1419  begin
     1420    if fracX256 >= 128 then inc(x);
     1421    if fracY256 >= 128 then inc(y);
     1422    result := GetPixel(x,y);
     1423  end else
     1424  begin
     1425    LoadFromBitmapIfNeeded;
     1426    result := InternalGetPixel256(x,y,FineInterpolation256(fracX256,AResampleFilter),FineInterpolation256(fracY256,AResampleFilter),smoothBorder);
     1427  end;
     1428end;
     1429
    11081430{$hints off}
    11091431{ This function compute an interpolated pixel at floating point coordinates }
    1110 function TBGRADefaultBitmap.GetPixel(x, y: single; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel;
    1111 var
    1112   ix, iy: integer;
    1113   w1,w2,w3,w4,alphaW: cardinal;
    1114   rSum, gSum, bSum: cardinal; //rgbDiv = aSum
    1115   aSum: cardinal;
    1116   c:    TBGRAPixel;
    1117   scan: PBGRAPixel;
    1118   factX,factY: single;
    1119   iFactX,iFactY: integer;
    1120 begin
    1121   ix := floor(x);
    1122   iy := floor(y);
    1123   factX := x-ix; //distance from integer coordinate
    1124   factY := y-iy;
     1432function TBGRADefaultBitmap.GetPixel(x, y: single; AResampleFilter: TResampleFilter = rfLinear; smoothBorder: boolean = true): TBGRAPixel;
     1433var
     1434  ix, iy: Int32or64;
     1435  iFactX,iFactY: Int32or64;
     1436begin
     1437  ix := round(x*256);
     1438  if (ix<= -256) or (ix>=Width shl 8) then
     1439  begin
     1440    result := BGRAPixelTransparent;
     1441    exit;
     1442  end;
     1443  iy := round(y*256);
     1444  if (iy<= -256) or (iy>=Height shl 8) then
     1445  begin
     1446    result := BGRAPixelTransparent;
     1447    exit;
     1448  end;
     1449
     1450  iFactX := ix and 255; //distance from integer coordinate
     1451  iFactY := iy and 255;
     1452  if ix<0 then ix := -1 else ix := ix shr 8;
     1453  if iy<0 then iy := -1 else iy := iy shr 8;
    11251454
    11261455  //if the coordinate is integer, then call standard GetPixel function
    1127   if (factX = 0) and (factY = 0) then
    1128   begin
    1129     Result := GetPixel(ix, iy);
     1456  if (iFactX = 0) and (iFactY = 0) then
     1457  begin
     1458    Result := (GetScanlineFast(iy)+ix)^;
    11301459    exit;
    11311460  end;
     1461
    11321462  LoadFromBitmapIfNeeded;
    1133 
    1134   rSum   := 0;
    1135   gSum   := 0;
    1136   bSum   := 0;
    1137   aSum   := 0;
    1138 
    1139   //apply interpolation filter
    1140   factX := FineInterpolation( factX, AResampleFilter );
    1141   factY := FineInterpolation( factY, AResampleFilter );
    1142 
    1143   iFactX := round(factX*256); //integer values for fractionnal part
    1144   iFactY := round(factY*256);
    1145 
    1146   w4 := (iFactX*iFactY+127) shr 8;
    1147   w3 := iFactY-w4;
    1148   w1 := (256-iFactX)-w3;
    1149   w2 := iFactX-w4;
    1150 
    1151   { For each pixel around the coordinate, compute
    1152     the weight for it and multiply values by it before
    1153     adding to the sum }
    1154   if (iy >= 0) and (iy < Height) then
    1155   begin
    1156     scan := GetScanlineFast(iy);
    1157 
    1158     if (ix >= 0) and (ix < Width) then
    1159     begin
    1160       c      := (scan + ix)^;
    1161       alphaW := c.alpha * w1;
    1162       aSum   += alphaW;
    1163       rSum   += c.red * alphaW;
    1164       gSum   += c.green * alphaW;
    1165       bSum   += c.blue * alphaW;
    1166     end;
    1167 
    1168     Inc(ix);
    1169     if (ix >= 0) and (ix < Width) then
    1170     begin
    1171       c      := (scan + ix)^;
    1172       alphaW := c.alpha * w2;
    1173       aSum   += alphaW;
    1174       rSum   += c.red * alphaW;
    1175       gSum   += c.green * alphaW;
    1176       bSum   += c.blue * alphaW;
    1177     end;
    1178   end
    1179   else
    1180   begin
    1181     Inc(ix);
    1182   end;
    1183 
    1184   Inc(iy);
    1185   if (iy >= 0) and (iy < Height) then
    1186   begin
    1187     scan := GetScanlineFast(iy);
    1188 
    1189     if (ix >= 0) and (ix < Width) then
    1190     begin
    1191       c      := (scan + ix)^;
    1192       alphaW := c.alpha * w4;
    1193       aSum   += alphaW;
    1194       rSum   += c.red * alphaW;
    1195       gSum   += c.green * alphaW;
    1196       bSum   += c.blue * alphaW;
    1197     end;
    1198 
    1199     Dec(ix);
    1200     if (ix >= 0) and (ix < Width) then
    1201     begin
    1202       c      := (scan + ix)^;
    1203       alphaW := c.alpha * w3;
    1204       aSum   += alphaW;
    1205       rSum   += c.red * alphaW;
    1206       gSum   += c.green * alphaW;
    1207       bSum   += c.blue * alphaW;
    1208     end;
    1209   end;
    1210 
    1211   if aSum < 128 then //if there is no alpha
    1212     Result := BGRAPixelTransparent
    1213   else
    1214   begin
    1215     Result.red   := (rSum + aSum shr 1) div aSum;
    1216     Result.green := (gSum + aSum shr 1) div aSum;
    1217     Result.blue  := (bSum + aSum shr 1) div aSum;
    1218     Result.alpha := (aSum + 128) shr 8;
    1219   end;
     1463  result := InternalGetPixel256(ix,iy,FineInterpolation256(iFactX,AResampleFilter),FineInterpolation256(iFactY,AResampleFilter),smoothBorder);
    12201464end;
    12211465
     
    12231467function TBGRADefaultBitmap.GetPixelCycle(x, y: single; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel;
    12241468var
    1225   ix, iy: integer;
    1226   iFactX,iFactY: integer;
    1227 begin
     1469  ix, iy: Int32or64;
     1470  iFactX,iFactY: Int32or64;
     1471begin
     1472  if FData = nil then
     1473  begin
     1474    result := BGRAPixelTransparent;
     1475    exit;
     1476  end;
    12281477  LoadFromBitmapIfNeeded;
    1229   iFactX := round(x*256);
    1230   iFactY := round(y*256);
    1231   ix := (iFactX shr 8)+ScanOffset.X;
    1232   iy := (iFactY shr 8)+ScanOffset.Y;
    1233   iFactX := iFactX and 255;
    1234   iFactY := iFactY and 255;
    1235 
     1478  ix := round(x*256);
     1479  iy := round(y*256);
     1480  iFactX := ix and 255;
     1481  iFactY := iy and 255;
     1482  ix := PositiveMod(ix, FWidth shl 8) shr 8;
     1483  iy := PositiveMod(iy, FHeight shl 8) shr 8;
    12361484  if (iFactX = 0) and (iFactY = 0) then
    12371485  begin
    1238     result := (ScanLine[PositiveMod(iy, FHeight)]+PositiveMod(ix, FWidth))^;
     1486    result := (GetScanlineFast(iy)+ix)^;
    12391487    exit;
    12401488  end;
    1241 
    12421489  if ScanInterpolationFilter <> rfLinear then
    12431490  begin
     
    12451492    iFactY := FineInterpolation256( iFactY, ScanInterpolationFilter );
    12461493  end;
    1247 
    1248   result := GetPixelCycleInline(ix,iy, iFactX,iFactY);
     1494  result := InternalGetPixelCycle256(ix,iy, iFactX,iFactY);
    12491495end;
    12501496
     
    12531499  ): TBGRAPixel;
    12541500var
    1255   alpha: byte;
    1256 begin
    1257   alpha := 255;
    1258   if not repeatX then
    1259   begin
    1260     if (x < -0.5) or (x > Width-0.5) then
    1261     begin
    1262       result := BGRAPixelTransparent;
    1263       exit;
     1501  ix, iy: Int32or64;
     1502  iFactX,iFactY: Int32or64;
     1503begin
     1504  if FData = nil then
     1505  begin
     1506    result := BGRAPixelTransparent;
     1507    exit;
     1508  end;
     1509  ix := round(x*256);
     1510  iy := round(y*256);
     1511  iFactX := ix and 255;
     1512  iFactY := iy and 255;
     1513  if ix < 0 then ix := -((iFactX-ix) shr 8)
     1514  else ix := ix shr 8;
     1515  if iy < 0 then iy := -((iFactY-iy) shr 8)
     1516  else iy := iy shr 8;
     1517  result := GetPixelCycle256(ix,iy,iFactX,iFactY,AResampleFilter,repeatX,repeatY);
     1518end;
     1519
     1520function TBGRADefaultBitmap.GetPixelCycle256(x, y, fracX256,
     1521  fracY256: int32or64; AResampleFilter: TResampleFilter): TBGRAPixel;
     1522begin
     1523  if (fracX256 = 0) and (fracY256 = 0) then
     1524    result := GetPixelCycle(x,y)
     1525  else if AResampleFilter = rfBox then
     1526  begin
     1527    if fracX256 >= 128 then inc(x);
     1528    if fracY256 >= 128 then inc(y);
     1529    result := GetPixelCycle(x,y);
     1530  end else
     1531  begin
     1532    LoadFromBitmapIfNeeded;
     1533    result := InternalGetPixelCycle256(PositiveMod(x,FWidth),PositiveMod(y,FHeight),FineInterpolation256(fracX256,AResampleFilter),FineInterpolation256(fracY256,AResampleFilter));
     1534  end;
     1535end;
     1536
     1537function TBGRADefaultBitmap.GetPixelCycle256(x, y, fracX256,
     1538  fracY256: int32or64; AResampleFilter: TResampleFilter; repeatX: boolean;
     1539  repeatY: boolean): TBGRAPixel;
     1540begin
     1541  if not repeatX and not repeatY then
     1542    result := GetPixel256(x,y,fracX256,fracY256,AResampleFilter)
     1543  else if repeatX and repeatY then
     1544    result := GetPixelCycle256(x,y,fracX256,fracY256,AResampleFilter)
     1545  else
     1546  begin
     1547    if not repeatX then
     1548    begin
     1549      if x < 0 then
     1550      begin
     1551        if x < -1 then
     1552        begin
     1553          result := BGRAPixelTransparent;
     1554          exit;
     1555        end;
     1556        result := GetPixelCycle256(0,y,0,fracY256,AResampleFilter);
     1557        result.alpha:= result.alpha*fracX256 shr 8;
     1558        if result.alpha = 0 then
     1559          result := BGRAPixelTransparent;
     1560        exit;
     1561      end;
     1562      if x >= FWidth-1 then
     1563      begin
     1564        if x >= FWidth then
     1565        begin
     1566          result := BGRAPixelTransparent;
     1567          exit;
     1568        end;
     1569        result := GetPixelCycle256(FWidth-1,y,0,fracY256,AResampleFilter);
     1570        result.alpha:= result.alpha*(256-fracX256) shr 8;
     1571        if result.alpha = 0 then
     1572          result := BGRAPixelTransparent;
     1573        exit;
     1574      end;
     1575    end else
     1576    begin
     1577      if y < 0 then
     1578      begin
     1579        if y < -1 then
     1580        begin
     1581          result := BGRAPixelTransparent;
     1582          exit;
     1583        end;
     1584        result := GetPixelCycle256(x,0,fracX256,0,AResampleFilter);
     1585        result.alpha:= result.alpha*fracY256 shr 8;
     1586        if result.alpha = 0 then
     1587          result := BGRAPixelTransparent;
     1588        exit;
     1589      end;
     1590      if y >= FHeight-1 then
     1591      begin
     1592        if y >= FHeight then
     1593        begin
     1594          result := BGRAPixelTransparent;
     1595          exit;
     1596        end;
     1597        result := GetPixelCycle256(x,FHeight-1,fracX256,0,AResampleFilter);
     1598        result.alpha:= result.alpha*(256-fracY256) shr 8;
     1599        if result.alpha = 0 then
     1600          result := BGRAPixelTransparent;
     1601        exit;
     1602      end;
    12641603    end;
    1265     if x < 0 then
    1266     begin
    1267       alpha := round((0.5+x)*510);
    1268       x := 0;
    1269     end
    1270     else
    1271     if x > Width-1 then
    1272     begin
    1273       alpha := round((Width-0.5-x)*510);
    1274       x := Width-1;
    1275     end;
    1276   end;
    1277   if not repeatY then
    1278   begin
    1279     if (y < -0.5) or (y > Height-0.5) then
    1280     begin
    1281       result := BGRAPixelTransparent;
    1282       exit;
    1283     end;
    1284     if y < 0 then
    1285     begin
    1286       alpha := round((0.5+y)*2*alpha);
    1287       y := 0;
    1288     end
    1289     else
    1290     if y > Height-1 then
    1291     begin
    1292       alpha := round((Height-0.5-y)*2*alpha);
    1293       y := Height-1;
    1294     end;
    1295   end;
    1296   result := GetPixelCycle(x,y,AResampleFilter);
    1297   if alpha<>255 then
    1298     result.alpha := ApplyOpacity(result.alpha,alpha);
     1604    result := GetPixelCycle256(x,y,fracX256,fracY256,AResampleFilter);
     1605  end;
    12991606end;
    13001607
     
    15401847    (ARawImage.Description.BlueShift = 8) and
    15411848    (ARawImage.Description.ByteOrder = riboMSBFirst)) then
    1542     mustSwapRedBlue:= true
     1849  begin
     1850    mustSwapRedBlue:= true;
     1851    mustReverse32 := false;
     1852  end
    15431853  else
    15441854  begin
     
    16551965end;
    16561966
     1967procedure TBGRADefaultBitmap.CrossFade(ARect: TRect; Source1, Source2: IBGRAScanner; AFadePosition: byte; mode: TDrawMode = dmDrawWithTransparency);
     1968var constScanner: TBGRAConstantScanner;
     1969begin
     1970  if AFadePosition = 0 then
     1971    FillRect(ARect, Source1, mode) else
     1972  if AFadePosition = 255 then
     1973    FillRect(ARect, Source2, mode) else
     1974  begin
     1975    constScanner := TBGRAConstantScanner.Create(BGRA(AFadePosition,AFadePosition,AFadePosition,255));
     1976    CrossFade(ARect, Source1,Source2, constScanner, mode);
     1977    constScanner.Free;
     1978  end;
     1979end;
     1980
     1981procedure TBGRADefaultBitmap.CrossFade(ARect: TRect; Source1, Source2: IBGRAScanner; AFadeMask: IBGRAScanner; mode: TDrawMode = dmDrawWithTransparency);
     1982var xb,yb: NativeInt;
     1983  pdest: PBGRAPixel;
     1984  c: TBGRAPixel;
     1985  fadePos: byte;
     1986begin
     1987  if not IntersectRect(ARect,ARect,ClipRect) then exit;
     1988  for yb := ARect.top to ARect.Bottom-1 do
     1989  begin
     1990    pdest := GetScanlineFast(yb)+ARect.Left;
     1991    Source1.ScanMoveTo(ARect.left, yb);
     1992    Source2.ScanMoveTo(ARect.left, yb);
     1993    AFadeMask.ScanMoveTo(ARect.left, yb);
     1994    for xb := ARect.left to ARect.Right-1 do
     1995    begin
     1996      fadePos := AFadeMask.ScanNextPixel.green;
     1997      c := MergeBGRAWithGammaCorrection(Source1.ScanNextPixel,not fadePos,Source2.ScanNextPixel,fadePos);
     1998      case mode of
     1999      dmSet: pdest^ := c;
     2000      dmDrawWithTransparency: DrawPixelInlineWithAlphaCheck(pdest, c);
     2001      dmLinearBlend: FastBlendPixelInline(pdest,c);
     2002      dmSetExceptTransparent: if c.alpha = 255 then pdest^ := c;
     2003      end;
     2004      inc(pdest);
     2005    end;
     2006  end;
     2007  InvalidateBitmap;
     2008end;
     2009
    16572010procedure TBGRADefaultBitmap.DiscardBitmapChange; inline;
    16582011begin
     
    16772030  FillMode := fmWinding;
    16782031
    1679   FFont     := TFont.Create;
    16802032  FontName  := 'Arial';
    16812033  FontStyle := [];
    16822034  FontAntialias := False;
    16832035  FFontHeight := 20;
    1684   FFontHeightSign := GetFontHeightSign(FFont);
    16852036
    16862037  PenStyle := psSolid;
     
    17562107{---------------------------- Line primitives ---------------------------------}
    17572108
    1758 function TBGRADefaultBitmap.CheckHorizLineBounds(var x,y,x2: integer): boolean; inline;
    1759 var
    1760   temp: integer;
     2109function TBGRADefaultBitmap.CheckHorizLineBounds(var x,y,x2: int32or64): boolean; inline;
     2110var
     2111  temp: int32or64;
    17612112begin
    17622113  if (x2 < x) then
     
    17782129end;
    17792130
    1780 procedure TBGRADefaultBitmap.SetHorizLine(x, y, x2: integer; c: TBGRAPixel);
     2131procedure TBGRADefaultBitmap.SetHorizLine(x, y, x2: int32or64; c: TBGRAPixel);
    17812132begin
    17822133  if not CheckHorizLineBounds(x,y,x2) then exit;
     
    17852136end;
    17862137
    1787 procedure TBGRADefaultBitmap.XorHorizLine(x, y, x2: integer; c: TBGRAPixel);
     2138procedure TBGRADefaultBitmap.XorHorizLine(x, y, x2: int32or64; c: TBGRAPixel);
    17882139begin
    17892140  if not CheckHorizLineBounds(x,y,x2) then exit;
     
    17922143end;
    17932144
    1794 procedure TBGRADefaultBitmap.DrawHorizLine(x, y, x2: integer; c: TBGRAPixel);
     2145procedure TBGRADefaultBitmap.DrawHorizLine(x, y, x2: int32or64; c: TBGRAPixel);
    17952146begin
    17962147  if not CheckHorizLineBounds(x,y,x2) then exit;
     
    17992150end;
    18002151
    1801 procedure TBGRADefaultBitmap.DrawHorizLine(x, y, x2: integer; ec: TExpandedPixel
     2152procedure TBGRADefaultBitmap.DrawHorizLine(x, y, x2: int32or64; ec: TExpandedPixel
    18022153  );
    18032154begin
     
    18072158end;
    18082159
    1809 procedure TBGRADefaultBitmap.DrawHorizLine(x, y, x2: integer;
    1810   texture: IBGRAScanner);
     2160procedure TBGRADefaultBitmap.HorizLine(x, y, x2: int32or64;
     2161  texture: IBGRAScanner; ADrawMode : TDrawMode);
    18112162begin
    18122163  if not CheckHorizLineBounds(x,y,x2) then exit;
    18132164  texture.ScanMoveTo(x,y);
    1814   ScannerPutPixels(texture,scanline[y] + x, x2 - x + 1,dmDrawWithTransparency);
     2165  ScannerPutPixels(texture,scanline[y] + x, x2 - x + 1,ADrawMode);
    18152166  InvalidateBitmap;
    18162167end;
    18172168
    1818 procedure TBGRADefaultBitmap.FastBlendHorizLine(x, y, x2: integer; c: TBGRAPixel);
     2169procedure TBGRADefaultBitmap.FastBlendHorizLine(x, y, x2: int32or64; c: TBGRAPixel);
    18192170begin
    18202171  if not CheckHorizLineBounds(x,y,x2) then exit;
     
    18232174end;
    18242175
    1825 procedure TBGRADefaultBitmap.AlphaHorizLine(x, y, x2: integer; alpha: byte);
     2176procedure TBGRADefaultBitmap.AlphaHorizLine(x, y, x2: int32or64; alpha: byte);
    18262177begin
    18272178  if alpha = 0 then
     
    18352186end;
    18362187
    1837 function TBGRADefaultBitmap.CheckVertLineBounds(var x,y,y2: integer; out delta: integer): boolean; inline;
    1838 var
    1839   temp: integer;
     2188function TBGRADefaultBitmap.CheckVertLineBounds(var x,y,y2: int32or64; out delta: int32or64): boolean; inline;
     2189var
     2190  temp: int32or64;
    18402191begin
    18412192  if FLineOrder = riloBottomToTop then
     
    18652216end;
    18662217
    1867 procedure TBGRADefaultBitmap.SetVertLine(x, y, y2: integer; c: TBGRAPixel);
    1868 var
    1869   n, delta: integer;
     2218procedure TBGRADefaultBitmap.SetVertLine(x, y, y2: int32or64; c: TBGRAPixel);
     2219var
     2220  n, delta: int32or64;
    18702221  p: PBGRAPixel;
    18712222begin
     
    18802231end;
    18812232
    1882 procedure TBGRADefaultBitmap.XorVertLine(x, y, y2: integer; c: TBGRAPixel);
    1883 var
    1884   n, delta: integer;
     2233procedure TBGRADefaultBitmap.XorVertLine(x, y, y2: int32or64; c: TBGRAPixel);
     2234var
     2235  n, delta: int32or64;
    18852236  p: PBGRAPixel;
    18862237begin
     
    18952246end;
    18962247
    1897 procedure TBGRADefaultBitmap.DrawVertLine(x, y, y2: integer; c: TBGRAPixel);
    1898 var
    1899   n, delta: integer;
     2248procedure TBGRADefaultBitmap.DrawVertLine(x, y, y2: int32or64; c: TBGRAPixel);
     2249var
     2250  n, delta: int32or64;
    19002251  p: PBGRAPixel;
    19012252begin
     
    19152266end;
    19162267
    1917 procedure TBGRADefaultBitmap.AlphaVertLine(x, y, y2: integer; alpha: byte);
    1918 var
    1919   n, delta: integer;
     2268procedure TBGRADefaultBitmap.AlphaVertLine(x, y, y2: int32or64; alpha: byte);
     2269var
     2270  n, delta: int32or64;
    19202271  p: PBGRAPixel;
    19212272begin
     
    19352286end;
    19362287
    1937 procedure TBGRADefaultBitmap.FastBlendVertLine(x, y, y2: integer; c: TBGRAPixel);
    1938 var
    1939   n, delta: integer;
     2288procedure TBGRADefaultBitmap.FastBlendVertLine(x, y, y2: int32or64; c: TBGRAPixel);
     2289var
     2290  n, delta: int32or64;
    19402291  p: PBGRAPixel;
    19412292begin
     
    19502301end;
    19512302
    1952 procedure TBGRADefaultBitmap.DrawHorizLineDiff(x, y, x2: integer;
     2303procedure TBGRADefaultBitmap.DrawHorizLineDiff(x, y, x2: int32or64;
    19532304  c, compare: TBGRAPixel; maxDiff: byte);
    19542305begin
     
    19582309end;
    19592310
     2311procedure TBGRADefaultBitmap.SetArrowStart(AStyle: TBGRAArrowStyle;
     2312  ATipStyle: TPenJoinStyle; ARelativePenWidth: single; ATriangleBackOffset: single);
     2313begin
     2314  GetArrow.SetStart(AStyle,ATipStyle,ARelativePenWidth,ATriangleBackOffset);
     2315end;
     2316
     2317procedure TBGRADefaultBitmap.SetArrowEnd(AStyle: TBGRAArrowStyle;
     2318  ATipStyle: TPenJoinStyle; ARelativePenWidth: single; ATriangleBackOffset: single);
     2319begin
     2320  GetArrow.SetEnd(AStyle,ATipStyle,ARelativePenWidth,ATriangleBackOffset);
     2321end;
     2322
     2323procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; c: TBGRAPixel; w: single);
     2324var tempCanvas: TBGRACanvas2D;
     2325begin
     2326  tempCanvas:= TBGRACanvas2D.Create(self);
     2327  tempCanvas.strokeStyle(c);
     2328  tempCanvas.lineWidth := w;
     2329  tempCanvas.lineStyle(CustomPenStyle);
     2330  tempCanvas.lineCapLCL := LineCap;
     2331  tempCanvas.lineJoinLCL := JoinStyle;
     2332  tempCanvas.path(APath);
     2333  tempCanvas.stroke;
     2334  tempCanvas.Free;
     2335end;
     2336
     2337procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; texture: IBGRAScanner; w: single);
     2338var tempCanvas: TBGRACanvas2D;
     2339begin
     2340  tempCanvas:= TBGRACanvas2D.Create(self);
     2341  tempCanvas.strokeStyle(texture);
     2342  tempCanvas.lineWidth := w;
     2343  tempCanvas.lineStyle(CustomPenStyle);
     2344  tempCanvas.lineCapLCL := LineCap;
     2345  tempCanvas.lineJoinLCL := JoinStyle;
     2346  tempCanvas.path(APath);
     2347  tempCanvas.stroke;
     2348  tempCanvas.Free;
     2349end;
     2350
    19602351{---------------------------- Lines ---------------------------------}
    19612352{ Call appropriate functions }
    19622353
    19632354procedure TBGRADefaultBitmap.DrawLine(x1, y1, x2, y2: integer;
    1964   c: TBGRAPixel; DrawLastPixel: boolean);
    1965 begin
    1966   BGRADrawLineAliased(self,x1,y1,x2,y2,c,DrawLastPixel);
     2355  c: TBGRAPixel; DrawLastPixel: boolean; ADrawMode: TDrawMode);
     2356begin
     2357  BGRADrawLineAliased(self,x1,y1,x2,y2,c,DrawLastPixel,ADrawMode);
    19672358end;
    19682359
     
    19702361  c: TBGRAPixel; DrawLastPixel: boolean);
    19712362begin
    1972   BGRADrawLineAntialias(self,x1,y1,x2,y2,c,DrawLastPixel);
     2363  BGRADrawLineAntialias(self,x1,y1,x2,y2,c,DrawLastPixel,LinearAntialiasing);
    19732364end;
    19742365
     
    19782369begin
    19792370  DashPos := 0;
    1980   BGRADrawLineAntialias(self,x1,y1,x2,y2,c1,c2,dashLen,DrawLastPixel,DashPos);
     2371  BGRADrawLineAntialias(self,x1,y1,x2,y2,c1,c2,dashLen,DrawLastPixel,DashPos,LinearAntialiasing);
    19812372end;
    19822373
     
    19842375  c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean; var DashPos: integer);
    19852376begin
    1986   BGRADrawLineAntialias(self,x1,y1,x2,y2,c1,c2,dashLen,DrawLastPixel,DashPos);
     2377  BGRADrawLineAntialias(self,x1,y1,x2,y2,c1,c2,dashLen,DrawLastPixel,DashPos,LinearAntialiasing);
    19872378end;
    19882379
     
    19902381  c: TBGRAPixel; w: single);
    19912382begin
    1992   BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,c,LineCap,JoinStyle,FCustomPenStyle,[],nil,JoinMiterLimit);
     2383  if Assigned(FArrow) then
     2384    BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,c,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,nil,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX)
     2385  else
     2386    BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,c,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,nil,JoinMiterLimit);
    19932387end;
    19942388
     
    19962390  texture: IBGRAScanner; w: single);
    19972391begin
    1998   BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,BGRAPixelTransparent,LineCap,JoinStyle,FCustomPenStyle,[],texture,JoinMiterLimit);
     2392  if Assigned(FArrow) then
     2393    BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,BGRAPixelTransparent,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,texture,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX)
     2394  else
     2395    BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,BGRAPixelTransparent,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,texture,JoinMiterLimit);
    19992396end;
    20002397
    20012398procedure TBGRADefaultBitmap.DrawLineAntialias(x1, y1, x2, y2: single;
    2002   c: TBGRAPixel; w: single; closed: boolean);
     2399  c: TBGRAPixel; w: single; Closed: boolean);
    20032400var
    20042401  options: TBGRAPolyLineOptions;
    20052402begin
    20062403  if not closed then options := [plRoundCapOpen] else options := [];
    2007   BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,c,pecRound,pjsRound,FCustomPenStyle,options,nil,JoinMiterLimit);
     2404  options += GetPolyLineOption;
     2405  if Assigned(FArrow) then
     2406    BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,c,pecRound,pjsRound,FCustomPenStyle,options,nil,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX)
     2407  else
     2408    BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,c,pecRound,pjsRound,FCustomPenStyle,options,nil,JoinMiterLimit)
    20082409end;
    20092410
     
    20232424    c := BGRAPixelTransparent;
    20242425  end;
    2025   BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,c,pecRound,pjsRound,FCustomPenStyle,options,texture,JoinMiterLimit);
     2426  options += GetPolyLineOption;
     2427  if Assigned(FArrow) then
     2428    BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,c,pecRound,pjsRound,FCustomPenStyle,options,texture,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX)
     2429  else
     2430    BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,c,pecRound,pjsRound,FCustomPenStyle,options,texture,JoinMiterLimit);
    20262431end;
    20272432
     
    20292434  c: TBGRAPixel; w: single);
    20302435begin
    2031   BGRAPolyLine(self,points,w,c,LineCap,JoinStyle,FCustomPenStyle,[],nil,JoinMiterLimit);
     2436  if Assigned(FArrow) then
     2437    BGRAPen.BGRAPolyLine(self,points,w,c,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,nil,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX)
     2438  else
     2439    BGRAPen.BGRAPolyLine(self,points,w,c,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,nil,JoinMiterLimit)
    20322440end;
    20332441
     
    20352443  const points: array of TPointF; texture: IBGRAScanner; w: single);
    20362444begin
    2037   BGRAPolyLine(self,points,w,BGRAPixelTransparent,LineCap,JoinStyle,FCustomPenStyle,[],texture,JoinMiterLimit);
     2445  if Assigned(FArrow) then
     2446    BGRAPen.BGRAPolyLine(self,points,w,BGRAPixelTransparent,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,texture,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX)
     2447  else
     2448    BGRAPen.BGRAPolyLine(self,points,w,BGRAPixelTransparent,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,texture,JoinMiterLimit);
    20382449end;
    20392450
     
    20442455begin
    20452456  if not closed then options := [plRoundCapOpen] else options := [];
    2046   BGRAPolyLine(self,points,w,c,pecRound,JoinStyle,FCustomPenStyle,options,nil,JoinMiterLimit);
     2457  options += GetPolyLineOption;
     2458  if Assigned(FArrow) then
     2459    BGRAPen.BGRAPolyLine(self,points,w,c,pecRound,JoinStyle,FCustomPenStyle,options,nil,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX)
     2460  else
     2461    BGRAPen.BGRAPolyLine(self,points,w,c,pecRound,JoinStyle,FCustomPenStyle,options,nil,JoinMiterLimit);
     2462end;
     2463
     2464procedure TBGRADefaultBitmap.DrawPolyLineAntialias(
     2465  const points: array of TPointF; c: TBGRAPixel; w: single;
     2466  fillcolor: TBGRAPixel);
     2467var multi: TBGRAMultishapeFiller;
     2468begin
     2469  multi := TBGRAMultishapeFiller.Create;
     2470  multi.PolygonOrder := poLastOnTop;
     2471  multi.AddPolygon(points,fillcolor);
     2472  multi.AddPolygon(ComputeWidePolyline(points,w),c);
     2473  if LinearAntialiasing then
     2474    multi.Draw(self,dmLinearBlend)
     2475  else
     2476    multi.Draw(self,dmDrawWithTransparency);
     2477  multi.Free;
    20472478end;
    20482479
     
    20502481  c: TBGRAPixel; w: single);
    20512482begin
    2052    BGRAPolyLine(self,points,w,c,LineCap,JoinStyle,FCustomPenStyle,[plCycle],nil,JoinMiterLimit);
     2483  BGRAPolyLine(self,points,w,c,LineCap,JoinStyle,FCustomPenStyle,[plCycle],nil,JoinMiterLimit);
    20532484end;
    20542485
     
    20572488begin
    20582489  BGRAPolyLine(self,points,w,BGRAPixelTransparent,LineCap,JoinStyle,FCustomPenStyle,[plCycle],texture,JoinMiterLimit);
     2490end;
     2491
     2492procedure TBGRADefaultBitmap.DrawPolygonAntialias(
     2493  const points: array of TPointF; c: TBGRAPixel; w: single;
     2494  fillcolor: TBGRAPixel);
     2495var multi: TBGRAMultishapeFiller;
     2496begin
     2497  multi := TBGRAMultishapeFiller.Create;
     2498  multi.PolygonOrder := poLastOnTop;
     2499  multi.AddPolygon(points,fillcolor);
     2500  multi.AddPolygon(ComputeWidePolygon(points,w),c);
     2501  if LinearAntialiasing then
     2502    multi.Draw(self,dmLinearBlend)
     2503  else
     2504    multi.Draw(self,dmDrawWithTransparency);
     2505  multi.Free;
    20592506end;
    20602507
     
    20852532  DrawPolyLineAntialias(points, BGRA(0,0,0,alpha),w);
    20862533  FEraseMode := False;
     2534end;
     2535
     2536procedure TBGRADefaultBitmap.FillPath(APath: IBGRAPath; c: TBGRAPixel);
     2537var tempCanvas: TBGRACanvas2D;
     2538begin
     2539  tempCanvas:= TBGRACanvas2D.Create(self);
     2540  tempCanvas.fillStyle(c);
     2541  tempCanvas.path(APath);
     2542  tempCanvas.fill;
     2543  tempCanvas.Free;
     2544end;
     2545
     2546procedure TBGRADefaultBitmap.FillPath(APath: IBGRAPath; texture: IBGRAScanner);
     2547var tempCanvas: TBGRACanvas2D;
     2548begin
     2549  tempCanvas:= TBGRACanvas2D.Create(self);
     2550  tempCanvas.fillStyle(texture);
     2551  tempCanvas.path(APath);
     2552  tempCanvas.fill;
     2553  tempCanvas.Free;
    20872554end;
    20882555
     
    22152682end;
    22162683
     2684procedure TBGRADefaultBitmap.FillQuadPerspectiveMapping(pt1, pt2, pt3,
     2685  pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF;
     2686  ACleanBorders: TRect);
     2687var
     2688  persp: TBGRAPerspectiveScannerTransform;
     2689  clean: TBGRAExtendedBorderScanner;
     2690begin
     2691  clean := TBGRAExtendedBorderScanner.Create(texture,ACleanBorders);
     2692  persp := TBGRAPerspectiveScannerTransform.Create(clean,[tex1,tex2,tex3,tex4],[pt1,pt2,pt3,pt4]);
     2693  FillPoly([pt1,pt2,pt3,pt4],persp,dmDrawWithTransparency);
     2694  persp.Free;
     2695  clean.Free;
     2696end;
     2697
    22172698procedure TBGRADefaultBitmap.FillQuadPerspectiveMappingAntialias(pt1, pt2, pt3,
    22182699  pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF);
     
    22232704  FillPolyAntialias([pt1,pt2,pt3,pt4],persp);
    22242705  persp.Free;
     2706end;
     2707
     2708procedure TBGRADefaultBitmap.FillQuadPerspectiveMappingAntialias(pt1, pt2, pt3,
     2709  pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF;
     2710  ACleanBorders: TRect);
     2711var
     2712  persp: TBGRAPerspectiveScannerTransform;
     2713  clean: TBGRAExtendedBorderScanner;
     2714begin
     2715  clean := TBGRAExtendedBorderScanner.Create(texture,ACleanBorders);
     2716  persp := TBGRAPerspectiveScannerTransform.Create(clean,[tex1,tex2,tex3,tex4],[pt1,pt2,pt3,pt4]);
     2717  FillPolyAntialias([pt1,pt2,pt3,pt4],persp);
     2718  persp.Free;
     2719  clean.Free;
    22252720end;
    22262721
     
    22842779procedure TBGRADefaultBitmap.FillPolyAntialias(const points: array of TPointF; c: TBGRAPixel);
    22852780begin
    2286   BGRAPolygon.FillPolyAntialias(self, points, c, FEraseMode, FillMode = fmWinding);
     2781  BGRAPolygon.FillPolyAntialias(self, points, c, FEraseMode, FillMode = fmWinding, LinearAntialiasing);
    22872782end;
    22882783
     
    22902785  texture: IBGRAScanner);
    22912786begin
    2292   BGRAPolygon.FillPolyAntialiasWithTexture(self, points, texture, FillMode = fmWinding);
     2787  BGRAPolygon.FillPolyAntialiasWithTexture(self, points, texture, FillMode = fmWinding, LinearAntialiasing);
    22932788end;
    22942789
     
    23062801end;
    23072802
     2803procedure TBGRADefaultBitmap.FillShape(shape: TBGRACustomFillInfo; c: TBGRAPixel;
     2804  drawmode: TDrawMode);
     2805begin
     2806  BGRAPolygon.FillShapeAliased(self, shape, c, FEraseMode, nil, FillMode = fmWinding, drawmode);
     2807end;
     2808
     2809procedure TBGRADefaultBitmap.FillShape(shape: TBGRACustomFillInfo;
     2810  texture: IBGRAScanner; drawmode: TDrawMode);
     2811begin
     2812  BGRAPolygon.FillShapeAliased(self, shape, BGRAPixelTransparent, false, texture, FillMode = fmWinding, drawmode);
     2813end;
     2814
     2815procedure TBGRADefaultBitmap.FillShapeAntialias(shape: TBGRACustomFillInfo;
     2816  c: TBGRAPixel);
     2817begin
     2818  BGRAPolygon.FillShapeAntialias(self, shape, c, FEraseMode, nil, FillMode = fmWinding, LinearAntialiasing);
     2819end;
     2820
     2821procedure TBGRADefaultBitmap.FillShapeAntialias(shape: TBGRACustomFillInfo;
     2822  texture: IBGRAScanner);
     2823begin
     2824  BGRAPolygon.FillShapeAntialiasWithTexture(self, shape, texture, FillMode = fmWinding, LinearAntialiasing);
     2825end;
     2826
     2827procedure TBGRADefaultBitmap.EraseShape(shape: TBGRACustomFillInfo; alpha: byte);
     2828begin
     2829  BGRAPolygon.FillShapeAliased(self, shape, BGRA(0, 0, 0, alpha), True, nil, FillMode = fmWinding, dmDrawWithTransparency);
     2830end;
     2831
     2832procedure TBGRADefaultBitmap.EraseShapeAntialias(shape: TBGRACustomFillInfo;
     2833  alpha: byte);
     2834begin
     2835  FEraseMode := True;
     2836  FillShapeAntialias(shape, BGRA(0, 0, 0, alpha));
     2837  FEraseMode := False;
     2838end;
     2839
    23082840procedure TBGRADefaultBitmap.EllipseAntialias(x, y, rx, ry: single;
    23092841  c: TBGRAPixel; w: single);
     
    23112843  if IsClearPenStyle(FCustomPenStyle) or (c.alpha = 0) then exit;
    23122844  if IsSolidPenStyle(FCustomPenStyle) then
    2313     BGRAPolygon.BorderEllipseAntialias(self, x, y, rx, ry, w, c, FEraseMode)
     2845    BGRAPolygon.BorderEllipseAntialias(self, x, y, rx, ry, w, c, FEraseMode, LinearAntialiasing)
    23142846  else
    23152847    DrawPolygonAntialias(ComputeEllipseContour(x,y,rx,ry),c,w);
     
    23212853  if IsClearPenStyle(FCustomPenStyle) then exit;
    23222854  if IsSolidPenStyle(FCustomPenStyle) then
    2323     BGRAPolygon.BorderEllipseAntialiasWithTexture(self, x, y, rx, ry, w, texture)
     2855    BGRAPolygon.BorderEllipseAntialiasWithTexture(self, x, y, rx, ry, w, texture, LinearAntialiasing)
    23242856  else
    23252857    DrawPolygonAntialias(ComputeEllipseContour(x,y,rx,ry),texture,w);
     
    23622894procedure TBGRADefaultBitmap.FillEllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel);
    23632895begin
    2364   BGRAPolygon.FillEllipseAntialias(self, x, y, rx, ry, c, FEraseMode);
     2896  BGRAPolygon.FillEllipseAntialias(self, x, y, rx, ry, c, FEraseMode, LinearAntialiasing);
    23652897end;
    23662898
     
    23682900  texture: IBGRAScanner);
    23692901begin
    2370   BGRAPolygon.FillEllipseAntialiasWithTexture(self, x, y, rx, ry, texture);
     2902  BGRAPolygon.FillEllipseAntialiasWithTexture(self, x, y, rx, ry, texture, LinearAntialiasing);
    23712903end;
    23722904
     
    24863018  if IsClearPenStyle(FCustomPenStyle) or (c.alpha = 0) then exit;
    24873019  if IsSolidPenStyle(FCustomPenStyle) then
    2488     BGRAPolygon.BorderRoundRectangleAntialias(self,x,y,x2,y2,rx,ry,w,options,c,False)
     3020    BGRAPolygon.BorderRoundRectangleAntialias(self,x,y,x2,y2,rx,ry,w,options,c,False, LinearAntialiasing)
    24893021  else
    24903022    DrawPolygonAntialias(BGRAPath.ComputeRoundRect(x,y,x2,y2,rx,ry,options),c,w);
     
    25443076  if IsClearPenStyle(FCustomPenStyle) then exit;
    25453077  if IsSolidPenStyle(FCustomPenStyle) then
    2546     BGRAPolygon.BorderRoundRectangleAntialiasWithTexture(self,x,y,x2,y2,rx,ry,w,options,texture)
     3078    BGRAPolygon.BorderRoundRectangleAntialiasWithTexture(self,x,y,x2,y2,rx,ry,w,options,texture, LinearAntialiasing)
    25473079  else
    25483080    DrawPolygonAntialias(BGRAPath.ComputeRoundRect(x,y,x2,y2,rx,ry,options),texture,w);
     
    26863218  end else
    26873219  begin
    2688     if (mode <> dmSet) and (c.alpha = 0) then exit;
     3220    if (mode <> dmSet) and (mode <> dmXor) and (c.alpha = 0) then exit;
    26893221
    26903222    p := Scanline[y] + x;
     
    27143246        end;
    27153247      dmXor:
     3248        if DWord(c) = 0 then exit
     3249        else
    27163250        for yb := y2 - y downto 0 do
    27173251        begin
     
    28253359  c: TBGRAPixel; options: TRoundRectangleOptions);
    28263360begin
    2827   BGRAPolygon.FillRoundRectangleAntialias(self,x,y,x2,y2,rx,ry,options,c,False);
     3361  BGRAPolygon.FillRoundRectangleAntialias(self,x,y,x2,y2,rx,ry,options,c,False, LinearAntialiasing);
    28283362end;
    28293363
     
    28313365  ry: single; texture: IBGRAScanner; options: TRoundRectangleOptions);
    28323366begin
    2833   BGRAPolygon.FillRoundRectangleAntialiasWithTexture(self,x,y,x2,y2,rx,ry,options,texture);
     3367  BGRAPolygon.FillRoundRectangleAntialiasWithTexture(self,x,y,x2,y2,rx,ry,options,texture, LinearAntialiasing);
    28343368end;
    28353369
     
    28373371  ry: single; alpha: byte; options: TRoundRectangleOptions);
    28383372begin
    2839   BGRAPolygon.FillRoundRectangleAntialias(self,x,y,x2,y2,rx,ry,options,BGRA(0,0,0,alpha),True);
     3373  BGRAPolygon.FillRoundRectangleAntialias(self,x,y,x2,y2,rx,ry,options,BGRA(0,0,0,alpha),True, LinearAntialiasing);
    28403374end;
    28413375
    28423376procedure TBGRADefaultBitmap.RoundRect(X1, Y1, X2, Y2: integer;
    2843   DX, DY: integer; BorderColor, FillColor: TBGRAPixel);
    2844 begin
    2845   BGRARoundRectAliased(self,X1,Y1,X2,Y2,DX,DY,BorderColor,FillColor);
     3377  DX, DY: integer; BorderColor, FillColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency);
     3378begin
     3379  BGRARoundRectAliased(self,X1,Y1,X2,Y2,DX,DY,BorderColor,FillColor,nil,ADrawMode);
     3380end;
     3381
     3382procedure TBGRADefaultBitmap.RoundRect(X1, Y1, X2, Y2: integer; DX,
     3383  DY: integer; BorderColor: TBGRAPixel; ADrawMode: TDrawMode);
     3384begin
     3385  BGRARoundRectAliased(self,X1,Y1,X2,Y2,DX,DY,BorderColor,BGRAPixelTransparent,nil,ADrawMode,true);
    28463386end;
    28473387
    28483388{------------------------- Text functions ---------------------------------------}
    28493389
    2850 procedure TBGRADefaultBitmap.TextOutAngle(x, y: single; orientation: integer;
    2851   s: string; c: TBGRAPixel; align: TAlignment);
    2852 begin
    2853   UpdateFont;
    2854   BGRAText.BGRATextOutAngle(self,FFont,FontQuality,x,y,orientation,s,c,nil,align);
    2855 end;
    2856 
    2857 procedure TBGRADefaultBitmap.TextOutAngle(x, y: single; orientation: integer;
    2858   s: string; texture: IBGRAScanner; align: TAlignment);
    2859 begin
    2860   UpdateFont;
    2861   BGRAText.BGRATextOutAngle(self,FFont,FontQuality,x,y,orientation,s,BGRAPixelTransparent,texture,align);
    2862 end;
    2863 
    2864 procedure TBGRADefaultBitmap.TextOut(x, y: single; s: string;
     3390procedure TBGRADefaultBitmap.TextOutAngle(x, y: single; orientationTenthDegCCW: integer;
     3391  sUTF8: string; c: TBGRAPixel; align: TAlignment);
     3392begin
     3393  FontRenderer.TextOutAngle(self,x,y,orientationTenthDegCCW,CleanTextOutString(sUTF8),c,align);
     3394end;
     3395
     3396procedure TBGRADefaultBitmap.TextOutAngle(x, y: single; orientationTenthDegCCW: integer;
     3397  sUTF8: string; texture: IBGRAScanner; align: TAlignment);
     3398begin
     3399  FontRenderer.TextOutAngle(self,x,y,orientationTenthDegCCW,CleanTextOutString(sUTF8),texture,align);
     3400end;
     3401
     3402procedure TBGRADefaultBitmap.TextOut(x, y: single; sUTF8: string;
    28653403  texture: IBGRAScanner; align: TAlignment);
    28663404begin
    2867   UpdateFont;
    2868 
    2869   if (FontQuality in[fqFineAntialiasing,fqFineClearTypeBGR,fqFineClearTypeRGB]) and (FFont.Orientation mod 3600 = 0) then
    2870     BGRATextFX.BGRATextOutImproveReadability(self,FFont,x,y,s,BGRAPixelTransparent,texture,align,
    2871      FontQuality in[fqFineClearTypeRGB,fqFineClearTypeBGR], FontQuality = fqFineClearTypeRGB) else
    2872 
    2873     BGRAText.BGRATextOut(self,FFont,FontQuality,x,y,s,BGRAPixelTransparent,texture,align);
    2874 end;
    2875 
    2876 procedure TBGRADefaultBitmap.TextOut(x, y: single; s: string;
     3405  FontRenderer.TextOut(self,x,y,CleanTextOutString(sUTF8),texture,align);
     3406end;
     3407
     3408procedure TBGRADefaultBitmap.TextOut(x, y: single; sUTF8: string;
    28773409  c: TBGRAPixel; align: TAlignment);
    28783410begin
    2879   UpdateFont;
    2880 
    2881   if (FontQuality in[fqFineAntialiasing,fqFineClearTypeBGR,fqFineClearTypeRGB]) and (FFont.Orientation mod 3600 = 0) then
    2882     BGRATextFX.BGRATextOutImproveReadability(self,FFont,x,y,s,c,nil,align,
    2883     FontQuality in[fqFineClearTypeRGB,fqFineClearTypeBGR], FontQuality = fqFineClearTypeRGB) else
    2884 
    2885     BGRAText.BGRATextOut(self,FFont,FontQuality,x,y,s,c,nil,align);
     3411  FontRenderer.TextOut(self,x,y,CleanTextOutString(sUTF8),c,align);
    28863412end;
    28873413
    28883414procedure TBGRADefaultBitmap.TextRect(ARect: TRect; x, y: integer;
    2889   s: string; style: TTextStyle; c: TBGRAPixel);
    2890 begin
    2891   UpdateFont;
    2892   BGRAText.BGRATextRect(self,FFont,FontQuality,ARect,x,y,s,style,c,nil);
    2893 end;
    2894 
    2895 procedure TBGRADefaultBitmap.TextRect(ARect: TRect; x, y: integer; s: string;
     3415  sUTF8: string; style: TTextStyle; c: TBGRAPixel);
     3416begin
     3417  FontRenderer.TextRect(self,ARect,x,y,sUTF8,style,c);
     3418end;
     3419
     3420procedure TBGRADefaultBitmap.TextRect(ARect: TRect; x, y: integer; sUTF8: string;
    28963421  style: TTextStyle; texture: IBGRAScanner);
    28973422begin
    2898   UpdateFont;
    2899   BGRAText.BGRATextRect(self,FFont,FontQuality,ARect,x,y,s,style,BGRAPixelTransparent,texture);
    2900 end;
    2901 
    2902 function TBGRADefaultBitmap.TextSize(s: string): TSize;
    2903 begin
    2904   UpdateFont;
    2905   result := BGRAText.BGRATextSize(FFont,FontQuality,s,FontAntialiasingLevel);
    2906   if (result.cy >= 24) and FontAntialias then
    2907     result := BGRAText.BGRATextSize(FFont,FontQuality,s,4);
     3423  FontRenderer.TextRect(self,ARect,x,y,sUTF8,style,texture);
     3424end;
     3425
     3426{ Returns the total size of the string provided using the current font.
     3427  Orientation is not taken into account, so that the width is along the text.  }
     3428function TBGRADefaultBitmap.TextSize(sUTF8: string): TSize;
     3429begin
     3430  result := FontRenderer.TextSize(sUTF8);
    29083431end;
    29093432
     
    29473470  w: single): ArrayOfTPointF;
    29483471begin
    2949   Result:= BGRAPen.ComputeWidePolylinePoints(points,w,BGRAWhite,LineCap,JoinStyle,FCustomPenStyle,[],JoinMiterLimit);
     3472  if Assigned(FArrow) then
     3473    Result:= BGRAPen.ComputeWidePolylinePoints(points,w,BGRAWhite,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX)
     3474  else
     3475    Result:= BGRAPen.ComputeWidePolylinePoints(points,w,BGRAWhite,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,JoinMiterLimit)
    29503476end;
    29513477
     
    29563482begin
    29573483  if not closed then options := [plRoundCapOpen] else options := [];
    2958   Result:= BGRAPen.ComputeWidePolylinePoints(points,w,BGRAWhite,pecRound,pjsRound,FCustomPenStyle,options,JoinMiterLimit);
     3484  options += GetPolyLineOption;
     3485  if Assigned(FArrow) then
     3486    Result:= BGRAPen.ComputeWidePolylinePoints(points,w,BGRAWhite,pecRound,pjsRound,FCustomPenStyle,options,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX)
     3487  else
     3488    Result:= BGRAPen.ComputeWidePolylinePoints(points,w,BGRAWhite,pecRound,pjsRound,FCustomPenStyle,options,JoinMiterLimit);
    29593489end;
    29603490
     
    29623492  w: single): ArrayOfTPointF;
    29633493begin
    2964   Result:= BGRAPen.ComputeWidePolylinePoints(points,w,BGRAWhite,LineCap,JoinStyle,FCustomPenStyle,[plCycle],JoinMiterLimit);
     3494  Result:= BGRAPen.ComputeWidePolylinePoints(points,w,BGRAWhite,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption+[plCycle],JoinMiterLimit);
    29653495end;
    29663496
     
    29843514  endRad: single; quality: single): ArrayOfTPointF;
    29853515begin
    2986   result := BGRAPath.ComputeArc65536(x,y,rx,ry,round(startRad*32768/Pi),round(endRad*32768/Pi),quality);
     3516  result := BGRAPath.ComputeArcRad(x,y,rx,ry,startRad,endRad,quality);
    29873517end;
    29883518
     
    30573587
    30583588procedure TBGRADefaultBitmap.FillMask(x, y: integer; AMask: TBGRACustomBitmap;
    3059   color: TBGRAPixel);
     3589  color: TBGRAPixel; ADrawMode: TDrawMode);
    30603590var
    30613591  scan: TBGRACustomScanner;
     
    30633593  if (AMask = nil) or (color.alpha = 0) then exit;
    30643594  scan := TBGRASolidColorMaskScanner.Create(AMask,Point(-X,-Y),color);
    3065   self.FillRect(X,Y,X+AMask.Width,Y+AMask.Height,scan,dmDrawWithTransparency);
     3595  self.FillRect(X,Y,X+AMask.Width,Y+AMask.Height,scan,ADrawMode);
    30663596  scan.Free;
    30673597end;
    30683598
    30693599procedure TBGRADefaultBitmap.FillMask(x, y: integer; AMask: TBGRACustomBitmap;
    3070   texture: IBGRAScanner);
     3600  texture: IBGRAScanner; ADrawMode: TDrawMode);
    30713601var
    30723602  scan: TBGRACustomScanner;
     
    30743604  if AMask = nil then exit;
    30753605  scan := TBGRATextureMaskScanner.Create(AMask,Point(-X,-Y),texture);
    3076   self.FillRect(X,Y,X+AMask.Width,Y+AMask.Height,scan,dmDrawWithTransparency);
     3606  self.FillRect(X,Y,X+AMask.Width,Y+AMask.Height,scan,ADrawMode);
    30773607  scan.Free;
    30783608end;
     
    33513881function TBGRADefaultBitmap.ScanAt(X, Y: Single): TBGRAPixel;
    33523882var
    3353   ix, iy: integer;
    3354   iFactX,iFactY: integer;
     3883  ix, iy: Int32or64;
     3884  iFactX,iFactY: Int32or64;
    33553885begin
    33563886  if FData = nil then
     
    33593889    exit;
    33603890  end;
    3361   iFactX := round(x*256);
    3362   iFactY := round(y*256);
    3363   ix := (iFactX shr 8)+ScanOffset.X;
    3364   iy := (iFactY shr 8)+ScanOffset.Y;
    3365   iFactX := iFactX and 255;
    3366   iFactY := iFactY and 255;
    3367 
     3891  LoadFromBitmapIfNeeded;
     3892  ix := round(x*256);
     3893  iy := round(y*256);
     3894  iFactX := ix and 255;
     3895  iFactY := iy and 255;
     3896  ix := PositiveMod(ix+(ScanOffset.X shl 8), FWidth shl 8) shr 8;
     3897  iy := PositiveMod(iy+(ScanOffset.Y shl 8), FHeight shl 8) shr 8;
    33683898  if (iFactX = 0) and (iFactY = 0) then
    33693899  begin
    3370     result := (GetScanlineFast(PositiveMod(iy, FHeight))+PositiveMod(ix, FWidth))^;
     3900    result := (GetScanlineFast(iy)+ix)^;
    33713901    exit;
    33723902  end;
    3373 
    33743903  if ScanInterpolationFilter <> rfLinear then
    33753904  begin
     
    33773906    iFactY := FineInterpolation256( iFactY, ScanInterpolationFilter );
    33783907  end;
    3379 
    3380   result := GetPixelCycleInline(ix,iy, iFactX,iFactY);
     3908  result := InternalGetPixelCycle256(ix,iy, iFactX,iFactY);
    33813909end;
    33823910
     
    34974025end;
    34984026
    3499 function TBGRADefaultBitmap.CheckPutImageBounds(x,y,tx,ty: integer; out minxb,minyb,maxxb,maxyb,ignoreleft: integer): boolean inline;
    3500 var x2,y2: integer;
    3501 begin
    3502   if (x >= FClipRect.Right) or (y >= FClipRect.Bottom) or (x <= FClipRect.Left-tx) or
    3503     (y <= FClipRect.Top-ty) or (Height = 0) or (ty = 0) or (tx = 0) then
    3504   begin
    3505     result := false;
    3506     exit;
    3507   end;
    3508 
    3509   x2 := x + tx - 1;
    3510   y2 := y + ty - 1;
    3511 
    3512   if y < FClipRect.Top then
    3513     minyb := FClipRect.Top
    3514   else
    3515     minyb := y;
    3516   if y2 >= FClipRect.Bottom then
    3517     maxyb := FClipRect.Bottom - 1
    3518   else
    3519     maxyb := y2;
    3520 
    3521   if x < FClipRect.Left then
    3522   begin
    3523     ignoreleft := FClipRect.Left-x;
    3524     minxb      := FClipRect.Left;
    3525   end
    3526   else
    3527   begin
    3528     ignoreleft := 0;
    3529     minxb      := x;
    3530   end;
    3531   if x2 >= FClipRect.Right then
    3532     maxxb := FClipRect.Right - 1
    3533   else
    3534     maxxb := x2;
    3535 
    3536   result := true;
    3537 end;
    3538 
    35394027function TBGRADefaultBitmap.CheckAntialiasRectBounds(var x, y, x2, y2: single;
    35404028  w: single): boolean;
     
    35844072  sourcewidth := Source.Width;
    35854073
    3586   if not CheckPutImageBounds(x,y,sourcewidth,source.height,minxb,minyb,maxxb,maxyb,ignoreleft) then exit;
     4074  if not CheckPutImageBounds(x,y,sourcewidth,source.height,minxb,minyb,maxxb,maxyb,ignoreleft,FClipRect) then exit;
    35874075
    35884076  copycount := maxxb - minxb + 1;
     
    37494237  sourcewidth := Source.Width;
    37504238
    3751   if not CheckPutImageBounds(x,y,sourcewidth,source.height,minxb,minyb,maxxb,maxyb,ignoreleft) then exit;
     4239  if not CheckPutImageBounds(x,y,sourcewidth,source.height,minxb,minyb,maxxb,maxyb,ignoreleft,FClipRect) then exit;
    37524240
    37534241  copycount := maxxb - minxb + 1;
     
    37834271  sourcewidth := Source.Width;
    37844272
    3785   if not CheckPutImageBounds(x,y,sourcewidth,source.height,minxb,minyb,maxxb,maxyb,ignoreleft) then exit;
     4273  if not CheckPutImageBounds(x,y,sourcewidth,source.height,minxb,minyb,maxxb,maxyb,ignoreleft,FClipRect) then exit;
    37864274
    37874275  copycount := maxxb - minxb + 1;
     
    38084296end;
    38094297
    3810 { Draw an image wih an angle. Use an affine transformation to do this. }
    3811 procedure TBGRADefaultBitmap.PutImageAngle(x, y: single;
    3812   Source: TBGRACustomBitmap; angle: single; imageCenterX: single;
    3813   imageCenterY: single; AOpacity: Byte; ARestoreOffsetAfterRotation: boolean);
    3814 var
    3815   cosa,sina: single;
    3816 
    3817   { Compute rotated coordinates }
    3818   function Coord(relX,relY: single): TPointF;
    3819   begin
    3820     relX -= imageCenterX;
    3821     relY -= imageCenterY;
    3822     result.x := relX*cosa-relY*sina+x;
    3823     result.y := relY*cosa+relX*sina+y;
    3824     if ARestoreOffsetAfterRotation then
    3825     begin
    3826       result.x += imageCenterX;
    3827       result.y += imageCenterY;
    3828     end;
    3829   end;
    3830 
    3831 begin
    3832   cosa := cos(-angle*Pi/180);
    3833   sina := -sin(-angle*Pi/180);
    3834   PutImageAffine(Coord(0,0),Coord(source.Width,0),Coord(0,source.Height),source,AOpacity);
    3835 end;
    3836 
    38374298{ Draw an image with an affine transformation (rotation, scale, translate).
    3838   Parameters are the bitmap origin, the end of the horizontal axis and the end of the vertical axis. }
    3839 procedure TBGRADefaultBitmap.PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AOpacity: Byte);
     4299  Parameters are the bitmap origin, the end of the horizontal axis and the end of the vertical axis.
     4300  The output bounds correspond to the pixels that will be affected in the destination. }
     4301procedure TBGRADefaultBitmap.PutImageAffine(Origin, HAxis, VAxis: TPointF;
     4302  Source: TBGRACustomBitmap; AOutputBounds: TRect; AResampleFilter: TResampleFilter; AMode: TDrawMode; AOpacity: Byte);
    38404303var affine: TBGRAAffineBitmapTransform;
    3841     minx,miny,maxx,maxy: integer;
    3842     pt4: TPointF;
    3843 
    3844   //include specified point in the bounds
    3845   procedure Include(pt: TPointF);
    3846   begin
    3847     if floor(pt.X) < minx then minx := floor(pt.X);
    3848     if floor(pt.Y) < miny then miny := floor(pt.Y);
    3849     if ceil(pt.X) > maxx then maxx := ceil(pt.X);
    3850     if ceil(pt.Y) > maxy then maxy := ceil(pt.Y);
    3851   end;
    3852 
    3853 begin
     4304    SourceBounds: TRect;
     4305begin
     4306  if (Source = nil) or (AOpacity = 0) then exit;
     4307  IntersectRect(AOutputBounds,AOutputBounds,ClipRect);
     4308  if IsRectEmpty(AOutputBounds) then exit;
     4309
    38544310  if (abs(Origin.x-round(Origin.x))<1e-6) and (abs(Origin.y-round(Origin.Y))<1e-6) and
    38554311     (abs(HAxis.x-(Origin.x+Source.Width))<1e-6) and (abs(HAxis.y-origin.y)<1e-6) and
    38564312     (abs(VAxis.x-Origin.x)<1e-6) and (abs(VAxis.y-(Origin.y+Source.Height))<1e-6) then
    38574313  begin
    3858     PutImage(round(origin.x),round(origin.y),Source,dmDrawWithTransparency,AOpacity);
     4314    SourceBounds := AOutputBounds;
     4315    OffsetRect(SourceBounds, -round(origin.x),-round(origin.y));
     4316    IntersectRect(SourceBounds,SourceBounds,rect(0,0,Source.Width,Source.Height));
     4317    PutImagePart(round(origin.x)+SourceBounds.Left,round(origin.y)+SourceBounds.Top,Source,SourceBounds,AMode,AOpacity);
    38594318    exit;
    38604319  end;
    38614320
    38624321  { Create affine transformation }
    3863   affine := TBGRAAffineBitmapTransform.Create(Source);
     4322  affine := TBGRAAffineBitmapTransform.Create(Source, false, AResampleFilter);
    38644323  affine.GlobalOpacity := AOpacity;
    38654324  affine.Fit(Origin,HAxis,VAxis);
    3866 
    3867   { Compute bounds }
    3868   pt4.x := VAxis.x+HAxis.x-Origin.x;
    3869   pt4.y := VAxis.y+HAxis.y-Origin.y;
    3870   minx := floor(Origin.X);
    3871   miny := floor(Origin.Y);
    3872   maxx := ceil(Origin.X);
    3873   maxy := ceil(Origin.Y);
    3874   Include(HAxis);
    3875   Include(VAxis);
    3876   Include(pt4);
    3877 
    3878   { Use the affine transformation as a scanner }
    3879   FillRect(minx,miny,maxx+1,maxy+1,affine,dmDrawWithTransparency);
     4325  FillRect(AOutputBounds,affine,AMode);
    38804326  affine.Free;
     4327end;
     4328
     4329procedure TBGRADefaultBitmap.StretchPutImage(ARect: TRect;
     4330  Source: TBGRACustomBitmap; mode: TDrawMode; AOpacity: byte);
     4331begin
     4332  If (Source = nil) or (AOpacity = 0) then exit;
     4333  if (ARect.Right-ARect.Left = Source.Width) and (ARect.Bottom-ARect.Top = Source.Height) then
     4334     PutImage(ARect.Left,ARect.Top,Source,mode,AOpacity)
     4335  else
     4336     BGRAResample.StretchPutImage(Source, ARect.Right-ARect.Left, ARect.Bottom-ARect.Top, self, ARect.left,ARect.Top, mode, AOpacity);
    38814337end;
    38824338
     
    39914447end;
    39924448
     4449function TBGRADefaultBitmap.FilterTwirl(ABounds: TRect; ACenter: TPoint;
     4450  ARadius: Single; ATurn: Single; AExponent: Single): TBGRACustomBitmap;
     4451begin
     4452  result := BGRAFilters.FilterTwirl(self, ABounds, ACenter, ARadius, ATurn, AExponent);
     4453end;
     4454
    39934455function TBGRADefaultBitmap.FilterCylinder: TBGRACustomBitmap;
    39944456begin
     
    40014463end;
    40024464
    4003 function TBGRADefaultBitmap.FilterSharpen: TBGRACustomBitmap;
    4004 begin
    4005   Result := BGRAFilters.FilterSharpen(self);
     4465function TBGRADefaultBitmap.FilterSharpen(Amount: single = 1): TBGRACustomBitmap;
     4466begin
     4467  Result := BGRAFilters.FilterSharpen(self,round(Amount*256));
     4468end;
     4469
     4470function TBGRADefaultBitmap.FilterSharpen(ABounds: TRect; Amount: single
     4471  ): TBGRACustomBitmap;
     4472begin
     4473  Result := BGRAFilters.FilterSharpen(self,ABounds,round(Amount*256));
    40064474end;
    40074475
     
    40174485end;
    40184486
     4487function TBGRADefaultBitmap.FilterBlurRadial(ABounds: TRect; radius: integer;
     4488  blurType: TRadialBlurType): TBGRACustomBitmap;
     4489var task: TFilterTask;
     4490begin
     4491  task := BGRAFilters.CreateRadialBlurTask(self, ABounds, radius, blurType);
     4492  try
     4493    result := task.Execute;
     4494  finally
     4495    task.Free;
     4496  end;
     4497end;
     4498
    40194499function TBGRADefaultBitmap.FilterPixelate(pixelSize: integer;
    40204500  useResample: boolean; filter: TResampleFilter): TBGRACustomBitmap;
     
    40294509end;
    40304510
     4511function TBGRADefaultBitmap.FilterBlurMotion(ABounds: TRect; distance: integer;
     4512  angle: single; oriented: boolean): TBGRACustomBitmap;
     4513var task: TFilterTask;
     4514begin
     4515  task := BGRAFilters.CreateMotionBlurTask(self,ABounds,distance,angle,oriented);
     4516  try
     4517    Result := task.Execute;
     4518  finally
     4519    task.Free;
     4520  end;
     4521end;
     4522
    40314523function TBGRADefaultBitmap.FilterCustomBlur(mask: TBGRACustomBitmap):
    40324524TBGRACustomBitmap;
     
    40354527end;
    40364528
     4529function TBGRADefaultBitmap.FilterCustomBlur(ABounds: TRect;
     4530  mask: TBGRACustomBitmap): TBGRACustomBitmap;
     4531var task: TFilterTask;
     4532begin
     4533  task := BGRAFilters.CreateBlurTask(self, ABounds, mask);
     4534  try
     4535    result := task.Execute;
     4536  finally
     4537    task.Free;
     4538  end;
     4539end;
     4540
    40374541function TBGRADefaultBitmap.FilterEmboss(angle: single): TBGRACustomBitmap;
    40384542begin
    40394543  Result := BGRAFilters.FilterEmboss(self, angle);
     4544end;
     4545
     4546function TBGRADefaultBitmap.FilterEmboss(angle: single; ABounds: TRect): TBGRACustomBitmap;
     4547begin
     4548  Result := BGRAFilters.FilterEmboss(self, angle, ABounds);
    40404549end;
    40414550
     
    40634572end;
    40644573
     4574function TBGRADefaultBitmap.FilterGrayscale(ABounds: TRect): TBGRACustomBitmap;
     4575begin
     4576  Result := BGRAFilters.FilterGrayscale(self, ABounds);
     4577end;
     4578
    40654579function TBGRADefaultBitmap.FilterNormalize(eachChannel: boolean = True):
    40664580TBGRACustomBitmap;
     
    40694583end;
    40704584
     4585function TBGRADefaultBitmap.FilterNormalize(ABounds: TRect; eachChannel: boolean): TBGRACustomBitmap;
     4586begin
     4587  Result := BGRAFilters.FilterNormalize(self, ABounds, eachChannel);
     4588end;
     4589
    40714590function TBGRADefaultBitmap.FilterRotate(origin: TPointF;
    4072   angle: single): TBGRACustomBitmap;
    4073 begin
    4074   Result := BGRAFilters.FilterRotate(self, origin, angle);
     4591  angle: single; correctBlur: boolean): TBGRACustomBitmap;
     4592begin
     4593  Result := BGRAFilters.FilterRotate(self, origin, angle, correctBlur);
    40754594end;
    40764595
     
    41404659end;
    41414660
    4142 {$hints off}
    4143 function TBGRADefaultBitmap.LoadAsBmp32(Str: TStream): boolean;
    4144 var OldPos: int64;
    4145     fileHeader: TBitmapFileHeader;
    4146     infoHeader: TBitmapInfoHeader;
    4147     dataSize: integer;
    4148 begin
    4149   OldPos := Str.Position;
    4150   result := false;
    4151   try
    4152     if Str.Read(fileHeader,sizeof(fileHeader)) <> sizeof(fileHeader) then
    4153       raise exception.Create('Inuable to read file header');
    4154     if fileHeader.bfType = $4D42 then
    4155     begin
    4156       if Str.Read(infoHeader,sizeof(infoHeader)) <> sizeof(infoHeader) then
    4157         raise exception.Create('Inuable to read info header');
    4158 
    4159       if (infoHeader.biPlanes = 1) and (infoHeader.biBitCount = 32) and (infoHeader.biCompression = 0) then
    4160       begin
    4161         SetSize(infoHeader.biWidth,infoHeader.biHeight);
    4162         Str.Position := OldPos+fileHeader.bfOffBits;
    4163         dataSize := NbPixels*sizeof(TBGRAPixel);
    4164         if Str.Read(Data^, dataSize) <> dataSize then
    4165         Begin
    4166           SetSize(0,0);
    4167           raise exception.Create('Unable to read data');
    4168         end;
    4169         result := true;
    4170       end;
    4171     end;
    4172 
    4173   except
    4174     on ex:exception do
    4175     begin
    4176 
    4177     end;
    4178   end;
    4179   Str.Position := OldPos;
    4180 
    4181 end;
    4182 {$hints on}
    4183 
    41844661procedure TBGRADefaultBitmap.SetCanvasOpacity(AValue: byte);
    41854662begin
     
    42254702
    42264703  It is an involution, i.e it does nothing when applied twice }
    4227 procedure TBGRADefaultBitmap.VerticalFlip;
    4228 var
    4229   yb:     integer;
     4704procedure TBGRADefaultBitmap.VerticalFlip(ARect: TRect);
     4705var
     4706  yb,h2:     integer;
    42304707  line:   PBGRAPixel;
    4231   linesize: integer;
     4708  linesize, delta: integer;
    42324709  PStart: PBGRAPixel;
    42334710  PEnd:   PBGRAPixel;
     
    42364713    exit;
    42374714
     4715  if (ARect.Right <= ARect.Left) or (ARect.Bottom <= ARect.Top) then exit;
     4716  if not IntersectRect(ARect, ARect, rect(0,0,Width,Height)) then exit;
    42384717  LoadFromBitmapIfNeeded;
    4239   linesize := Width * sizeof(TBGRAPixel);
     4718  linesize := (ARect.Right-ARect.Left) * sizeof(TBGRAPixel);
    42404719  line     := nil;
    42414720  getmem(line, linesize);
    4242   PStart := Data;
    4243   PEnd   := Data + (Height - 1) * Width;
    4244   for yb := 0 to (Height div 2) - 1 do
     4721  PStart := GetScanlineFast(ARect.Top)+ARect.Left;
     4722  PEnd   := GetScanlineFast(ARect.Bottom-1)+ARect.Left;
     4723  h2 := (ARect.Bottom-ARect.Top) div 2;
     4724  if LineOrder = riloTopToBottom then delta := +Width else delta := -Width;
     4725  for yb := h2-1 downto 0 do
    42454726  begin
    42464727    move(PStart^, line^, linesize);
    42474728    move(PEnd^, PStart^, linesize);
    42484729    move(line^, PEnd^, linesize);
    4249     Inc(PStart, Width);
    4250     Dec(PEnd, Width);
     4730    Inc(PStart, delta);
     4731    Dec(PEnd, delta);
    42514732  end;
    42524733  freemem(line);
     
    42574738
    42584739  It is an involution, i.e it does nothing when applied twice}
    4259 procedure TBGRADefaultBitmap.HorizontalFlip;
    4260 var
    4261   yb, xb: integer;
     4740procedure TBGRADefaultBitmap.HorizontalFlip(ARect: TRect);
     4741var
     4742  yb, xb, w: integer;
    42624743  PStart: PBGRAPixel;
    42634744  PEnd:   PBGRAPixel;
     
    42674748    exit;
    42684749
     4750  if (ARect.Right <= ARect.Left) or (ARect.Bottom <= ARect.Top) then exit;
     4751  if not IntersectRect(ARect, ARect, rect(0,0,Width,Height)) then exit;
     4752  w := ARect.Right-ARect.Left;
    42694753  LoadFromBitmapIfNeeded;
    4270   for yb := 0 to Height - 1 do
    4271   begin
    4272     PStart := Scanline[yb];
    4273     PEnd   := PStart + Width;
    4274     for xb := 0 to (Width div 2) - 1 do
     4754  for yb := ARect.Top to ARect.Bottom-1 do
     4755  begin
     4756    PStart := GetScanlineFast(yb)+ARect.Left;
     4757    PEnd   := PStart + w;
     4758    for xb := 0 to (w div 2) - 1 do
    42754759    begin
    42764760      Dec(PEnd);
     
    43394823  complentary colors (black becomes white etc.).
    43404824
    4341   It is an involution, i.e it does nothing when applied twice }
     4825  It is NOT EXACTLY an involution, when applied twice, some color information is lost }
    43424826procedure TBGRADefaultBitmap.Negative;
    43434827var
     
    43604844end;
    43614845
     4846procedure TBGRADefaultBitmap.NegativeRect(ABounds: TRect);
     4847var p: PBGRAPixel;
     4848  xb,yb,xcount: integer;
     4849begin
     4850  if not IntersectRect(ABounds,ABounds,ClipRect) then exit;
     4851  xcount := ABounds.Right-ABounds.Left;
     4852  for yb := ABounds.Top to ABounds.Bottom-1 do
     4853  begin
     4854    p := ScanLine[yb]+ABounds.Left;
     4855    for xb := xcount-1 downto 0 do
     4856    begin
     4857      if p^.alpha <> 0 then
     4858      begin
     4859        p^.red   := GammaCompressionTab[not GammaExpansionTab[p^.red]];
     4860        p^.green := GammaCompressionTab[not GammaExpansionTab[p^.green]];
     4861        p^.blue  := GammaCompressionTab[not GammaExpansionTab[p^.blue]];
     4862      end;
     4863      Inc(p);
     4864    end;
     4865  end;
     4866end;
     4867
    43624868{ Compute negative without gamma correction.
    43634869
     
    43814887  end;
    43824888  InvalidateBitmap;
     4889end;
     4890
     4891procedure TBGRADefaultBitmap.LinearNegativeRect(ABounds: TRect);
     4892var p: PBGRAPixel;
     4893  xb,yb,xcount: integer;
     4894begin
     4895  if not IntersectRect(ABounds,ABounds,ClipRect) then exit;
     4896  xcount := ABounds.Right-ABounds.Left;
     4897  for yb := ABounds.Top to ABounds.Bottom-1 do
     4898  begin
     4899    p := ScanLine[yb]+ABounds.Left;
     4900    for xb := xcount-1 downto 0 do
     4901    begin
     4902      if p^.alpha <> 0 then
     4903      begin
     4904        p^.red   := not p^.red;
     4905        p^.green := not p^.green;
     4906        p^.blue  := not p^.blue;
     4907      end;
     4908      Inc(p);
     4909    end;
     4910  end;
     4911end;
     4912
     4913procedure TBGRADefaultBitmap.InplaceGrayscale;
     4914begin
     4915  InplaceGrayscale(rect(0,0,Width,Height));
     4916end;
     4917
     4918procedure TBGRADefaultBitmap.InplaceGrayscale(ABounds: TRect);
     4919var
     4920  task: TFilterTask;
     4921begin
     4922  task := CreateGrayscaleTask(self, ABounds);
     4923  task.Destination := self;
     4924  task.Execute;
     4925  task.Free;
    43834926end;
    43844927
     
    44524995
    44534996  See : http://wiki.lazarus.freepascal.org/BGRABitmap_tutorial_5 }
    4454 procedure TBGRADefaultBitmap.ApplyMask(mask: TBGRACustomBitmap);
     4997procedure TBGRADefaultBitmap.ApplyMask(mask: TBGRACustomBitmap; ARect: TRect; AMaskRectTopLeft: TPoint);
    44554998var
    44564999  p, pmask: PBGRAPixel;
    44575000  yb, xb:   integer;
    4458 begin
    4459   if (Mask.Width <> Width) or (Mask.Height <> Height) then
    4460     exit;
     5001  MaskOffsetX,MaskOffsetY,w: integer;
     5002  opacity: NativeUint;
     5003begin
     5004  if (ARect.Right <= ARect.Left) or (ARect.Bottom <= ARect.Top) then exit;
     5005  IntersectRect(ARect, ARect, rect(0,0,Width,Height));
     5006  MaskOffsetX := AMaskRectTopLeft.x - ARect.Left;
     5007  MaskOffsetY := AMaskRectTopLeft.y - ARect.Top;
     5008  OffsetRect(ARect, MaskOffsetX, MaskOffsetY);
     5009  IntersectRect(ARect, ARect, rect(0,0,mask.Width,mask.Height));
     5010  OffsetRect(ARect, -MaskOffsetX, -MaskOffsetY);
    44615011
    44625012  LoadFromBitmapIfNeeded;
    4463   for yb := 0 to Height - 1 do
    4464   begin
    4465     p     := Scanline[yb];
    4466     pmask := Mask.Scanline[yb];
    4467     for xb := Width - 1 downto 0 do
    4468     begin
    4469       p^.alpha := ApplyOpacity(p^.alpha, pmask^.red);
     5013  w := ARect.Right-ARect.Left-1;
     5014  for yb := ARect.Top to ARect.Bottom - 1 do
     5015  begin
     5016    p     := Scanline[yb]+ARect.Left;
     5017    pmask := Mask.Scanline[yb+MaskOffsetY]+ARect.Left+MaskOffsetX;
     5018    for xb := w downto 0 do
     5019    begin
     5020      opacity := ApplyOpacity(p^.alpha, pmask^.red);
     5021      if opacity = 0 then p^ := BGRAPixelTransparent
     5022      else p^.alpha := opacity;
    44705023      Inc(p);
    44715024      Inc(pmask);
     
    45225075end;
    45235076
     5077procedure TBGRADefaultBitmap.DrawCheckers(ARect: TRect; AColorEven,
     5078  AColorOdd: TBGRAPixel);
     5079const tx = 8; ty = 8; //must be a power of 2
     5080      xMask = tx*2-1;
     5081var xcount,patY,w,n,patY1,patY2m1,patX,patX1: NativeInt;
     5082    pdest: PBGRAPixel;
     5083    delta: PtrInt;
     5084    actualRect: TRect;
     5085begin
     5086  actualRect := ARect;
     5087  IntersectRect(actualRect, ARect, self.ClipRect);
     5088  w := actualRect.Right-actualRect.Left;
     5089  if (w <= 0) or (actualRect.Bottom <= actualRect.Top) then exit;
     5090  delta := self.Width;
     5091  if self.LineOrder = riloBottomToTop then delta := -delta;
     5092  delta := (delta-w)*SizeOf(TBGRAPixel);
     5093  pdest := self.ScanLine[actualRect.Top]+actualRect.left;
     5094  patY1 := actualRect.Top - ARect.Top;
     5095  patY2m1 := actualRect.Bottom - ARect.Top-1;
     5096  patX1 := (actualRect.Left - ARect.Left) and xMask;
     5097  for patY := patY1 to patY2m1 do
     5098  begin
     5099    xcount := w;
     5100    if patY and ty = 0 then
     5101       patX := patX1
     5102    else
     5103       patX := (patX1+tx) and xMask;
     5104    while xcount > 0 do
     5105    begin
     5106      if patX and tx = 0 then
     5107      begin
     5108        n := 8-patX;
     5109        if n > xcount then n := xcount;
     5110        FillDWord(pdest^,n,DWord(AColorEven));
     5111        dec(xcount,n);
     5112        inc(pdest,n);
     5113        patX := tx;
     5114      end else
     5115      begin
     5116        n := 16-patX;
     5117        if n > xcount then n := xcount;
     5118        FillDWord(pdest^,n,DWord(AColorOdd));
     5119        dec(xcount,n);
     5120        inc(pdest,n);
     5121        patX := 0;
     5122      end;
     5123    end;
     5124    inc(pbyte(pdest),delta);
     5125  end;
     5126  self.InvalidateBitmap;
     5127end;
     5128
    45245129{ Get bounds of non zero values of specified channel }
    45255130function TBGRADefaultBitmap.GetImageBounds(Channel: TChannel = cAlpha; ANothingValue: Byte = 0): TRect;
     5131begin
     5132  result := GetImageBounds([Channel], ANothingValue);
     5133end;
     5134
     5135function TBGRADefaultBitmap.GetImageBounds(Channels: TChannels; ANothingValue: Byte = 0): TRect;
    45265136var
    45275137  minx, miny, maxx, maxy: integer;
    4528   xb, yb: integer;
    4529   p:      pbyte;
    4530   offset: integer;
     5138  xb, xb2, yb: integer;
     5139  p:      PDWord;
     5140  colorMask, colorZeros: DWord;
    45315141begin
    45325142  maxx := -1;
     
    45345144  minx := self.Width;
    45355145  miny := self.Height;
    4536   case Channel of
    4537     cBlue: offset  := 0;
    4538     cGreen: offset := 1;
    4539     cRed: offset   := 2;
    4540     else
    4541       offset := 3;
    4542   end;
     5146  colorMask := 0;
     5147  colorZeros := 0;
     5148  if cBlue in Channels then
     5149  begin
     5150    colorMask := colorMask or $ff;
     5151    colorZeros:= colorZeros or ANothingValue;
     5152  end;
     5153  if cGreen in Channels then
     5154  begin
     5155    colorMask := colorMask or $ff00;
     5156    colorZeros:= colorZeros or (ANothingValue shl 8);
     5157  end;
     5158  if cRed in Channels then
     5159  begin
     5160    colorMask := colorMask or $ff0000;
     5161    colorZeros:= colorZeros or (ANothingValue shl 16);
     5162  end;
     5163  if cAlpha in Channels then
     5164  begin
     5165    colorMask := colorMask or $ff000000;
     5166    colorZeros:= colorZeros or (ANothingValue shl 24);
     5167  end;
     5168  colorMask := NtoLE(colorMask);
     5169  colorZeros := NtoLE(colorZeros);
    45435170  for yb := 0 to self.Height - 1 do
    45445171  begin
    4545     p := PByte(self.ScanLine[yb]) + offset;
     5172    p := PDWord(self.ScanLine[yb]);
    45465173    for xb := 0 to self.Width - 1 do
    45475174    begin
    4548       if p^ <> ANothingValue then
     5175      if (p^ and colorMask) <> colorZeros then
    45495176      begin
    45505177        if xb < minx then
     
    45565183        if yb > maxy then
    45575184          maxy := yb;
     5185
     5186        inc(p, self.width-1-xb);
     5187        for xb2 := self.Width-1 downto xb+1 do
     5188        begin
     5189          if (p^ and colorMask) <> colorZeros then
     5190          begin
     5191            if xb2 > maxx then
     5192              maxx := xb2;
     5193            break;
     5194          end;
     5195          dec(p);
     5196        end;
     5197        break;
    45585198      end;
    4559       Inc(p, sizeof(TBGRAPixel));
     5199      Inc(p);
    45605200    end;
    45615201  end;
     
    45745214    Result.bottom := maxy + 1;
    45755215  end;
    4576 end;
    4577 
    4578 function TBGRADefaultBitmap.GetImageBounds(Channels: TChannels): TRect;
    4579 var c: TChannel;
    4580 begin
    4581   result := rect(0,0,0,0);
    4582   for c := low(TChannel) to high(TChannel) do
    4583     if c in Channels then
    4584       UnionRect(result,result,GetImageBounds(c));
    45855216end;
    45865217
  • GraphicTest/Packages/bgrabitmap/bgradnetdeserial.pas

    r452 r472  
    155155    procedure LoadFromStream(Stream: TStream);
    156156    procedure LoadFromFile(filename: string);
     157    procedure LoadFromFileUTF8(filenameUTF8: string);
    157158    function ToString: string; override;
    158159    constructor Create;
     
    182183
    183184implementation
     185
     186uses lazutf8classes;
    184187
    185188const
     
    859862begin
    860863  stream := TFileStream.Create(filename, fmOpenRead);
     864  try
     865    LoadFromStream(stream);
     866  finally
     867    stream.Free;
     868  end;
     869end;
     870
     871procedure TDotNetDeserialization.LoadFromFileUTF8(filenameUTF8: string);
     872var
     873  stream: TFileStreamUTF8;
     874begin
     875  stream := TFileStreamUTF8.Create(filenameUTF8, fmOpenRead);
    861876  try
    862877    LoadFromStream(stream);
  • GraphicTest/Packages/bgrabitmap/bgrafillinfo.pas

    r452 r472  
    1717
    1818type
    19 
    20   { TIntersectionInfo }
    21 
    22   TIntersectionInfo = class
    23     interX: single;
    24     winding: integer;
    25     numSegment: integer;
    26     procedure SetValues(AInterX: Single; AWinding, ANumSegment: integer);
    27   end;
    28   ArrayOfTIntersectionInfo = array of TIntersectionInfo;
    29 
    3019  { TFillShapeInfo }
    3120
    32   TFillShapeInfo = class
     21  TFillShapeInfo = class(TBGRACustomFillInfo)
    3322    protected
    3423      //compute intersections. the array must be big enough
     
    4332    public
    4433      //returns true if the same segment number can be curved
    45       function SegmentsCurved: boolean; virtual;
     34      function SegmentsCurved: boolean; override;
    4635
    4736      //returns integer bounds
    48       function GetBounds: TRect; virtual;
     37      function GetBounds: TRect; override;
    4938
    5039      //compute min-max to be drawn on destination bitmap according to cliprect. Returns false if
    5140      //there is nothing to draw
    52       function ComputeMinMax(out minx,miny,maxx,maxy: integer; bmpDest: TBGRACustomBitmap): boolean;
     41      function ComputeMinMax(out minx,miny,maxx,maxy: integer; bmpDest: TBGRACustomBitmap): boolean; override;
    5342
    5443      //check if the point is inside the filling zone
    55       function IsPointInside(x,y: single; windingMode: boolean): boolean;
     44      function IsPointInside(x,y: single; windingMode: boolean): boolean; override;
    5645
    5746      //create an array that will contain computed intersections.
    5847      //you may augment, in this case, use CreateIntersectionInfo for new items
    59       function CreateIntersectionArray: ArrayOfTIntersectionInfo;
    60       function CreateIntersectionInfo: TIntersectionInfo; virtual; //creates a single info
    61       procedure FreeIntersectionArray(var inter: ArrayOfTIntersectionInfo);
     48      function CreateIntersectionArray: ArrayOfTIntersectionInfo; override;
     49      function CreateIntersectionInfo: TIntersectionInfo; override; //creates a single info
     50      procedure FreeIntersectionArray(var inter: ArrayOfTIntersectionInfo); override;
    6251
    6352      //fill a previously created array of intersections with actual intersections at the current y coordinate.
    6453      //nbInter gets the number of computed intersections
    65       procedure ComputeAndSort(cury: single; var inter: ArrayOfTIntersectionInfo; out nbInter: integer; windingMode: boolean);
     54      procedure ComputeAndSort(cury: single; var inter: ArrayOfTIntersectionInfo; out nbInter: integer; windingMode: boolean); override;
    6655
    6756  end;
     
    157146  end;
    158147
    159   { TFillPolyInfo }
    160 
    161   TFillPolyInfo = class(TFillShapeInfo)
     148  { TCustomFillPolyInfo }
     149
     150  TCustomFillPolyInfo = class(TFillShapeInfo)
    162151  private
    163152    function GetNbPoints: integer;
     
    167156    FEmptyPt:     array of boolean;
    168157    FNext, FPrev: array of integer;
    169 
     158    function NbMaxIntersection: integer; override;
     159    procedure SetIntersectionValues(AInter: TIntersectionInfo; AInterX: Single; AWinding, ANumSegment: integer; {%H-}dy: single; {%H-}AData: pointer); virtual;
     160  public
     161    constructor Create(const points: array of TPointF);
     162    function CreateSegmentData(numPt,nextPt: integer; x,y: single): pointer; virtual;
     163    procedure FreeSegmentData(data: pointer); virtual;
     164    function GetBounds: TRect; override;
     165    property NbPoints: integer read GetNbPoints;
     166  end;
     167
     168  { TFillPolyInfo }
     169
     170  TFillPolyInfo = class(TCustomFillPolyInfo)
     171  protected
    170172    FSlices:   array of TPolySlice;
    171173    FCurSlice: integer;
    172 
     174    FMaxIntersection: integer;
    173175    function NbMaxIntersection: integer; override;
    174176    procedure ComputeIntersection(cury: single;
     
    177179    constructor Create(const points: array of TPointF);
    178180    destructor Destroy; override;
    179     function CreateSegmentData(numPt,nextPt: integer; x,y: single): pointer; virtual;
    180     procedure FreeSegmentData(data: pointer); virtual;
    181     function GetBounds: TRect; override;
    182     property NbPoints: integer read GetNbPoints;
     181  end;
     182
     183  POnePassRecord = ^TOnePassRecord;
     184  TOnePassRecord = record
     185                id: integer;
     186                data: pointer;
     187                slope: single;
     188                winding: integer;
     189                includeStartingPoint: boolean;
     190                originalY1: single;
     191                x1,y1,y2: single;
     192                next: POnePassRecord;
     193                nextWaiting: POnePassRecord;
     194                nextDrawing: POnePassRecord;
     195            end;
     196
     197  { TOnePassFillPolyInfo }
     198
     199  TOnePassFillPolyInfo = class(TCustomFillPolyInfo)
     200  private
     201    procedure InsertionSortByY;
     202    function PartitionByY(left, right: integer): integer;
     203    procedure QuickSortByY(left, right: integer);
     204    procedure SortByY;
     205  protected
     206    FOnePass: array of TOnePassRecord;
     207    FSortedByY: array of POnePassRecord;
     208    FFirstWaiting, FFirstDrawing: POnePassRecord;
     209    FShouldInitializeDrawing: boolean;
     210    procedure ComputeIntersection(cury: single;
     211      var inter: ArrayOfTIntersectionInfo; var nbInter: integer); override;
     212  public
     213    constructor Create(const points: array of TPointF);
     214    function CreateIntersectionArray: ArrayOfTIntersectionInfo; override;
     215    destructor Destroy; override;
     216  end;
     217
     218  { TSimpleFillPolyInfo }
     219
     220  TSimpleFillPolyInfo = class(TCustomFillPolyInfo)
     221  protected
     222    FSimple: array of record
     223                  winding: integer;
     224                  includeStartingPoint: boolean;
     225                  data: pointer;
     226    end;
     227    procedure ComputeIntersection(cury: single; var inter: ArrayOfTIntersectionInfo;
     228      var nbInter: integer); override;
     229  public
     230    constructor Create(const points: array of TPointF);
     231    destructor Destroy; override;
    183232  end;
    184233
    185234procedure AddDensity(dest: PDensity; start,count: integer; value : word); inline;
    186 function DivByAntialiasPrecision(value: cardinal): cardinal; inline;
    187 function DivByAntialiasPrecision256(value: cardinal): cardinal; inline;
    188 function DivByAntialiasPrecision65536(value: cardinal): cardinal; inline;
     235function DivByAntialiasPrecision(value: UInt32or64): UInt32or64; inline;
     236function DivByAntialiasPrecision256(value: UInt32or64): UInt32or64; inline;
     237function DivByAntialiasPrecision65536(value: UInt32or64): UInt32or64; inline;
    189238procedure ComputeAliasedRowBounds(x1,x2: single; minx,maxx: integer; out ix1,ix2: integer);
    190239
     
    215264function IsPointInPolygon(const points: ArrayOfTPointF; point: TPointF
    216265  ; windingMode: boolean): boolean;
    217 var info: TFillShapeInfo;
    218 begin
    219   info := TFillPolyInfo.Create(points);
     266var info: TBGRACustomFillInfo;
     267begin
     268  info := TSimpleFillPolyInfo.Create(points);
    220269  result := info.IsPointInside(point.x+0.5,point.y+0.5,windingMode);
    221270  info.free;
     
    223272
    224273function IsPointInEllipse(x, y, rx, ry: single; point: TPointF): boolean;
    225 var info: TFillShapeInfo;
     274var info: TBGRACustomFillInfo;
    226275begin
    227276  info := TFillEllipseInfo.Create(x,y,rx,ry);
     
    232281function IsPointInRoundRectangle(x1, y1, x2, y2, rx, ry: single; point: TPointF
    233282  ): boolean;
    234 var info: TFillShapeInfo;
     283var info: TBGRACustomFillInfo;
    235284begin
    236285  info := TFillRoundRectangleInfo.Create(x1, y1, x2, y2, rx, ry,[]);
     
    274323end;
    275324
    276 function DivByAntialiasPrecision(value: cardinal): cardinal;
     325function DivByAntialiasPrecision(value: UInt32or64): UInt32or64;
    277326begin             //
    278327  result := value shr AntialiasPrecisionShift;// div AntialiasPrecision;
    279328end;
    280329
    281 function DivByAntialiasPrecision256(value: cardinal): cardinal;
     330function DivByAntialiasPrecision256(value: UInt32or64): UInt32or64;
    282331begin             //
    283332  result := value shr (AntialiasPrecisionShift+8);// div (256*AntialiasPrecision);
    284333end;
    285334
    286 function DivByAntialiasPrecision65536(value: cardinal): cardinal;
     335function DivByAntialiasPrecision65536(value: UInt32or64): UInt32or64;
    287336begin             //
    288337  result := value shr (AntialiasPrecisionShift+16);//div (65536*AntialiasPrecision);
    289 end;
    290 
    291 { TIntersectionInfo }
    292 
    293 procedure TIntersectionInfo.SetValues(AInterX: Single; AWinding, ANumSegment: integer);
    294 begin
    295   interX := AInterX;
    296   winding := AWinding;
    297   numSegment := ANumSegment;
    298338end;
    299339
     
    571611end;
    572612
    573 { TFillPolyInfo }
    574 
    575 constructor TFillPolyInfo.Create(const points: array of TPointF);
    576 function AddSeg(numSlice: integer): integer;
    577 begin
    578   result := FSlices[numSlice].nbSegments;
    579   if length(FSlices[numSlice].segments)=FSlices[numSlice].nbSegments then
    580     setlength(FSlices[numSlice].segments,FSlices[numSlice].nbSegments*2+2);
    581   inc(FSlices[numSlice].nbSegments);
    582 end;
    583 
     613{ TCustomFillPolyInfo }
     614
     615constructor TCustomFillPolyInfo.Create(const points: array of TPointF);
    584616var
    585   i, j, k: integer;
     617  i, j: integer;
    586618  First, cur, nbP: integer;
    587   yList: array of single;
    588   nbYList: integer;
    589   ya,yb,temp: single;
    590   sliceStart,sliceEnd,idxSeg: integer;
    591   idSeg: integer;
    592 
    593619begin
    594620  setlength(FPoints, length(points));
    595621  nbP := 0;
     622  first := -1;
    596623  for i := 0 to high(points) do
    597   if (i=0) or (points[i]<>points[i-1]) then
    598   begin
     624  if isEmptyPointF(points[i]) then
     625  begin
     626    if first<>-1 then
     627    begin
     628      if nbP = first+1 then //is there only one point?
     629      begin
     630        dec(nbP);
     631        first := -1; //remove subpolygon
     632      end else
     633      if (FPoints[nbP-1] = FPoints[first]) then
     634        dec(nbP); //remove just last looping point
     635    end;
     636    if first<>-1 then
     637    begin
     638      FPoints[nbP] := points[i];
     639      inc(nbP);
     640      first := -1;
     641    end;
     642  end else
     643  if (first=-1) or (points[i]<>points[i-1]) then
     644  begin
     645    if first = -1 then first := nbP;
    599646    FPoints[nbP] := points[i];
    600647    inc(nbP);
    601648  end;
    602   if (nbP>0) and (FPoints[nbP-1] = FPoints[0]) then dec(NbP);
    603649  setlength(FPoints, nbP);
    604650
     
    656702    else
    657703      FSlopes[i]    := EmptySingle;
     704end;
     705
     706{$hints off}
     707function TCustomFillPolyInfo.CreateSegmentData(numPt,nextPt: integer; x, y: single
     708  ): pointer;
     709begin
     710  result := nil;
     711end;
     712{$hints on}
     713
     714procedure TCustomFillPolyInfo.FreeSegmentData(data: pointer);
     715begin
     716  freemem(data);
     717end;
     718
     719function TCustomFillPolyInfo.GetBounds: TRect;
     720var
     721  minx, miny, maxx, maxy, i: integer;
     722begin
     723  if length(FPoints) = 0 then
     724  begin
     725    result := rect(0,0,0,0);
     726    exit;
     727  end;
     728  miny := floor(FPoints[0].y);
     729  maxy := ceil(FPoints[0].y);
     730  minx := floor(FPoints[0].x);
     731  maxx := ceil(FPoints[0].x);
     732  for i := 1 to high(FPoints) do
     733    if not FEmptyPt[i] then
     734    begin
     735      if floor(FPoints[i].y) < miny then
     736        miny := floor(FPoints[i].y)
     737      else
     738      if ceil(FPoints[i].y) > maxy then
     739        maxy := ceil(FPoints[i].y);
     740
     741      if floor(FPoints[i].x) < minx then
     742        minx := floor(FPoints[i].x)
     743      else
     744      if ceil(FPoints[i].x) > maxx then
     745        maxx := ceil(FPoints[i].x);
     746    end;
     747  Result := rect(minx, miny, maxx + 1, maxy + 1);
     748end;
     749
     750function TCustomFillPolyInfo.GetNbPoints: integer;
     751begin
     752  result := length(FPoints);
     753end;
     754
     755function TCustomFillPolyInfo.NbMaxIntersection: integer;
     756begin
     757  Result := length(FPoints);
     758end;
     759
     760procedure TCustomFillPolyInfo.SetIntersectionValues(AInter: TIntersectionInfo;
     761  AInterX: Single; AWinding, ANumSegment: integer; dy: single; AData: pointer);
     762begin
     763  AInter.SetValues( AInterX, AWinding, ANumSegment );
     764end;
     765
     766{ TFillPolyInfo }
     767
     768function TFillPolyInfo.NbMaxIntersection: integer;
     769begin
     770  Result:= FMaxIntersection;
     771end;
     772
     773procedure TFillPolyInfo.ComputeIntersection(cury: single;
     774  var inter: ArrayOfTIntersectionInfo; var nbInter: integer);
     775var
     776  j: integer;
     777begin
     778  if length(FSlices)=0 then exit;
     779
     780  while (cury < FSlices[FCurSlice].y1) and (FCurSlice > 0) do dec(FCurSlice);
     781  while (cury > FSlices[FCurSlice].y2) and (FCurSlice < high(FSlices)) do inc(FCurSlice);
     782  with FSlices[FCurSlice] do
     783  if (cury >= y1) and (cury <= y2) then
     784  begin
     785    for j := 0 to nbSegments-1 do
     786    begin
     787      SetIntersectionValues(inter[nbinter], (cury - segments[j].y1) * segments[j].slope + segments[j].x1,
     788                                segments[j].winding, segments[j].id, cury - segments[j].y1, segments[j].data );
     789      Inc(nbinter);
     790    end;
     791  end;
     792end;
     793
     794constructor TFillPolyInfo.Create(const points: array of TPointF);
     795  function AddSeg(numSlice: integer): integer;
     796  begin
     797    result := FSlices[numSlice].nbSegments;
     798    if length(FSlices[numSlice].segments)=FSlices[numSlice].nbSegments then
     799      setlength(FSlices[numSlice].segments,FSlices[numSlice].nbSegments*2+2);
     800    inc(FSlices[numSlice].nbSegments);
     801  end;
     802
     803var
     804  yList: array of single;
     805  nbYList: integer;
     806  ya,yb,temp: single;
     807  sliceStart,sliceEnd,idxSeg: integer;
     808  i,j,k,idSeg: integer;
     809
     810begin
     811  inherited Create(points);
    658812
    659813  //slice
     
    716870
    717871  FCurSlice := 0;
     872  FMaxIntersection:= 0;
     873  for i := 0 to high(FSlices) do
     874    if FSlices[i].nbSegments > FMaxIntersection then
     875      FMaxIntersection:= FSlices[i].nbSegments;
    718876end;
    719877
     
    728886end;
    729887
    730 {$hints off}
    731 function TFillPolyInfo.CreateSegmentData(numPt,nextPt: integer; x, y: single
    732   ): pointer;
    733 begin
    734   result := nil;
    735 end;
    736 {$hints on}
    737 
    738 procedure TFillPolyInfo.FreeSegmentData(data: pointer);
    739 begin
    740   freemem(data);
    741 end;
    742 
    743 function TFillPolyInfo.GetBounds: TRect;
     888{ TOnePassFillPolyInfo }
     889
     890function TOnePassFillPolyInfo.PartitionByY(left,right: integer): integer;
     891
     892  procedure Swap(idx1,idx2: integer); inline;
     893  var temp: POnePassRecord;
     894  begin
     895    temp := FSortedByY[idx1];
     896    FSortedByY[idx1] := FSortedByY[idx2];
     897    FSortedByY[idx2] := temp;
     898  end;
     899
     900var pivotIndex: integer;
     901    pivotValue: single;
     902    storeIndex: integer;
     903    i: integer;
     904
     905begin
     906  pivotIndex := left + random(right-left+1);
     907  pivotValue := FSortedByY[pivotIndex]^.y1;
     908  swap(pivotIndex,right);
     909  storeIndex := left;
     910  for i := left to right-1 do
     911    if FSortedByY[i]^.y1 <= pivotValue then
     912    begin
     913      swap(i,storeIndex);
     914      inc(storeIndex);
     915    end;
     916  swap(storeIndex,right);
     917  result := storeIndex;
     918end;
     919
     920procedure TOnePassFillPolyInfo.QuickSortByY(left,right: integer);
     921var pivotNewIndex: integer;
     922begin
     923  if right > left+9 then
     924  begin
     925    pivotNewIndex := PartitionByY(left,right);
     926    QuickSortByY(left,pivotNewIndex-1);
     927    QuickSortByY(pivotNewIndex+1,right);
     928  end;
     929end;
     930
     931procedure TOnePassFillPolyInfo.InsertionSortByY;
     932var i,j: integer;
     933    tempValue: single;
     934    tempPtr: POnePassRecord;
     935begin
     936  for i := 1 to high(FSortedByY) do
     937  begin
     938    tempPtr := FSortedByY[i];
     939    TempValue := tempPtr^.y1;
     940    j := i;
     941    while (j>0) and (FSortedByY[j-1]^.y1 > TempValue) do
     942    begin
     943      FSortedByY[j] := FSortedByY[j-1];
     944      dec(j);
     945    end;
     946    FSortedByY[j] := tempPtr;
     947  end;
     948end;
     949
     950procedure TOnePassFillPolyInfo.SortByY;
     951var i,nbSorted: integer;
     952begin
     953  setlength(FSortedByY, length(FPoints));
     954  nbSorted := 0;
     955  for i := 0 to high(FSortedByY) do
     956    if not FEmptyPt[i] then
     957    begin
     958      FSortedByY[nbSorted] := @FOnePass[i];
     959      inc(nbSorted);
     960    end;
     961  setlength(FSortedByY,nbSorted);
     962  if length(FSortedByY) < 10 then InsertionSortByY else
     963  begin
     964    QuickSortByY(0,high(FSortedByY));
     965    InsertionSortByY;
     966  end;
     967end;
     968
     969procedure TOnePassFillPolyInfo.ComputeIntersection(cury: single;
     970  var inter: ArrayOfTIntersectionInfo; var nbInter: integer);
    744971var
    745   minx, miny, maxx, maxy, i: integer;
    746 begin
    747   if length(FPoints) = 0 then
    748   begin
    749     result := rect(0,0,0,0);
    750     exit;
    751   end;
    752   miny := floor(FPoints[0].y);
    753   maxy := ceil(FPoints[0].y);
    754   minx := floor(FPoints[0].x);
    755   maxx := ceil(FPoints[0].x);
    756   for i := 1 to high(FPoints) do
    757     if not FEmptyPt[i] then
    758     begin
    759       if floor(FPoints[i].y) < miny then
    760         miny := floor(FPoints[i].y)
     972  p,pprev,pnext: POnePassRecord;
     973begin
     974  FShouldInitializeDrawing := true;
     975
     976  p := FFirstWaiting;
     977  while p <> nil do
     978  begin
     979    if (cury >= p^.y1) then
     980    begin
     981      if cury <= p^.y2+1 then
     982      begin
     983        p^.nextDrawing := FFirstDrawing;
     984        FFirstDrawing := p;
     985      end;
     986    end
     987      else break;
     988    p := p^.nextWaiting;
     989  end;
     990  FFirstWaiting:= p;
     991
     992  p := FFirstDrawing;
     993  pprev := nil;
     994  while p <> nil do
     995  begin
     996    pnext := p^.nextDrawing;
     997{    if p^.slope = EmptySingle then
     998      raise exception.Create('Unexpected');}
     999    if ((cury > p^.y1) and (cury <= p^.y2)) or
     1000       (p^.includeStartingPoint and (cury = p^.y1)) then
     1001    begin
     1002{      if nbinter = length(inter) then
     1003        raise exception.Create('too much'); }
     1004      if inter[nbinter] = nil then inter[nbinter] := CreateIntersectionInfo;
     1005      SetIntersectionValues(inter[nbinter], (cury - p^.y1)*p^.slope + p^.x1, p^.winding, p^.id, cury - p^.originalY1, p^.data);
     1006      inc(nbinter);
     1007    end else
     1008    if (cury > p^.y2+1) then
     1009    begin
     1010      if pprev <> nil then
     1011        pprev^.nextDrawing := pnext
    7611012      else
    762       if ceil(FPoints[i].y) > maxy then
    763         maxy := ceil(FPoints[i].y);
    764 
    765       if floor(FPoints[i].x) < minx then
    766         minx := floor(FPoints[i].x)
    767       else
    768       if ceil(FPoints[i].x) > maxx then
    769         maxx := ceil(FPoints[i].x);
    770     end;
    771   Result := rect(minx, miny, maxx + 1, maxy + 1);
    772 end;
    773 
    774 function TFillPolyInfo.GetNbPoints: integer;
    775 begin
    776   result := length(FPoints);
    777 end;
    778 
    779 function TFillPolyInfo.NbMaxIntersection: integer;
    780 begin
    781   Result := length(FPoints);
    782 end;
    783 
    784 procedure TFillPolyInfo.ComputeIntersection(cury: single;
    785       var inter: ArrayOfTIntersectionInfo; var nbInter: integer);
    786 var
    787   j: integer;
    788 begin
    789   if length(FSlices)=0 then exit;
    790 
    791   while (cury < FSlices[FCurSlice].y1) and (FCurSlice > 0) do dec(FCurSlice);
    792   while (cury > FSlices[FCurSlice].y2) and (FCurSlice < high(FSlices)) do inc(FCurSlice);
    793   with FSlices[FCurSlice] do
    794   if (cury >= y1) and (cury <= y2) then
    795   begin
    796     for j := 0 to nbSegments-1 do
    797     begin
    798       inter[nbinter].SetValues( (cury - segments[j].y1) * segments[j].slope + segments[j].x1,
    799                                 segments[j].winding, segments[j].id );
    800       Inc(nbinter);
    801     end;
    802   end;
     1013        FFirstDrawing:= pnext;
     1014      p := pnext;
     1015      continue;
     1016    end;
     1017    pprev := p;
     1018    p := pnext;
     1019  end;
     1020end;
     1021
     1022constructor TOnePassFillPolyInfo.Create(const points: array of TPointF);
     1023var i,j: integer;
     1024  p: POnePassRecord;
     1025  temp: single;
     1026begin
     1027  inherited create(points);
     1028
     1029  FShouldInitializeDrawing := true;
     1030  setlength(FOnePass, length(FPoints));
     1031  for i := 0 to high(FPoints) do
     1032  if not FEmptyPt[i] then
     1033  begin
     1034    p := @FOnePass[i];
     1035    j := FNext[i];
     1036    p^.next := @FOnePass[FNext[i]];
     1037    p^.id := i;
     1038    p^.slope := FSlopes[i];
     1039    if p^.slope <> EmptySingle then
     1040      p^.data := CreateSegmentData(i, j, FPoints[i].x, FPoints[i].y);
     1041    p^.y1 := FPoints[i].y;
     1042    p^.y2 := FPoints[j].y;
     1043    p^.originalY1 := p^.y1;
     1044    p^.winding:= ComputeWinding(p^.y1,p^.y2);
     1045    if p^.y1 < p^.y2 then
     1046      p^.x1 := FPoints[i].x
     1047    else
     1048    if p^.y1 > p^.y2 then
     1049    begin
     1050      temp := p^.y1;
     1051      p^.y1 := p^.y2;
     1052      p^.y2 := temp;
     1053      p^.x1 := FPoints[j].x;
     1054    end;
     1055  end;
     1056
     1057  SortByY;
     1058end;
     1059
     1060function TOnePassFillPolyInfo.CreateIntersectionArray: ArrayOfTIntersectionInfo;
     1061var i: integer;
     1062  p,pprev: POnePassRecord;
     1063begin
     1064  if FShouldInitializeDrawing then
     1065  begin
     1066    FShouldInitializeDrawing := false;
     1067    FFirstWaiting:= nil;
     1068    pprev := nil;
     1069    for i := 0 to high(FSortedByY) do
     1070    begin
     1071      p := FSortedByY[i];
     1072      if p^.winding > 0 then
     1073        p^.includeStartingPoint := p^.next^.winding <= 0
     1074      else if p^.winding < 0 then
     1075        p^.includeStartingPoint := p^.next^.winding >= 0;
     1076      if p^.slope <> EmptySingle then
     1077      begin
     1078        if pprev <> nil then
     1079          pprev^.nextWaiting:= p
     1080        else
     1081          FFirstWaiting := p;
     1082        pprev := p;
     1083      end;
     1084    end;
     1085  end;
     1086
     1087  setlength(result, NbMaxIntersection);
     1088end;
     1089
     1090destructor TOnePassFillPolyInfo.Destroy;
     1091var i: integer;
     1092begin
     1093  for i := 0 to high(FOnePass) do
     1094    if FOnePass[i].data<>nil then FreeSegmentData(FOnePass[i].data);
     1095  FOnePass := nil;
     1096  inherited Destroy;
     1097end;
     1098
     1099{ TSimpleFillPolyInfo }
     1100
     1101procedure TSimpleFillPolyInfo.ComputeIntersection(cury: single;
     1102  var inter: ArrayOfTIntersectionInfo; var nbInter: integer);
     1103var i,j: integer;
     1104begin
     1105  for i := 0 to high(FPoints) do
     1106    if FSlopes[i] <> EmptySingle then
     1107    begin
     1108      j := FNext[i];
     1109      if ((cury > FPoints[i].y) and (cury <= FPoints[j].y)) or
     1110        ((cury < FPoints[i].y) and (cury >= FPoints[j].y)) or
     1111       (FSimple[i].includeStartingPoint and (cury = FPoints[i].y)) then
     1112     begin
     1113       SetIntersectionValues(inter[nbinter], (cury - FPoints[i].y)*FSlopes[i] + FPoints[i].x, FSimple[i].winding, i, cury - FPoints[i].y, FSimple[i].data);
     1114       inc(nbinter);
     1115     end;
     1116    end;
     1117end;
     1118
     1119constructor TSimpleFillPolyInfo.Create(const points: array of TPointF);
     1120var i,j: integer;
     1121begin
     1122  inherited Create(points);
     1123
     1124  setlength(FSimple, length(FPoints));
     1125  for i := 0 to high(FPoints) do
     1126  begin
     1127    j := FNext[i];
     1128    if j <> -1 then
     1129      FSimple[i].winding:= ComputeWinding(FPoints[i].y,FPoints[j].y)
     1130    else
     1131      FSimple[i].winding:= 0;
     1132    if FSlopes[i] <> EmptySingle then
     1133      FSimple[i].data := CreateSegmentData(i, j, FPoints[i].x, FPoints[i].y);
     1134  end;
     1135end;
     1136
     1137destructor TSimpleFillPolyInfo.Destroy;
     1138var i: integer;
     1139begin
     1140  for i := 0 to high(FSimple) do
     1141    if FSimple[i].data <> nil then
     1142      FreeSegmentData(FSimple[i].data);
     1143  FSimple := nil;
     1144  inherited Destroy;
    8031145end;
    8041146
     
    11001442end;
    11011443
     1444initialization
     1445
     1446  Randomize;
     1447
    11021448end.
    11031449
  • GraphicTest/Packages/bgrabitmap/bgrafilters.pas

    r452 r472  
    1111uses
    1212  Classes, BGRABitmapTypes;
     13
     14type
     15  TCheckShouldStopFunc = function(ACurrentY: integer) : boolean of object;
     16
     17  { TFilterTask }
     18
     19  TFilterTask = class
     20  private
     21    FCheckShouldStop: TCheckShouldStopFunc;
     22    procedure SetDestination(AValue: TBGRACustomBitmap);
     23  protected
     24    FDestination: TBGRACustomBitmap;
     25    FSource: TBGRACustomBitmap;
     26    FCurrentY: integer;
     27    function GetShouldStop(ACurrentY: integer): boolean;
     28    procedure DoExecute; virtual; abstract;
     29  public
     30    function Execute: TBGRACustomBitmap;
     31    property Destination: TBGRACustomBitmap read FDestination write SetDestination;
     32    property CheckShouldStop: TCheckShouldStopFunc read FCheckShouldStop write FCheckShouldStop;
     33    property CurrentY: integer read FCurrentY;
     34  end;
    1335
    1436{ The median filter consist in calculating the median value of pixels. Here
     
    2547
    2648{ Sharpen filter add more contrast between pixels }
    27 function FilterSharpen(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     49function FilterSharpen(bmp: TBGRACustomBitmap; AAmount: integer = 256): TBGRACustomBitmap;
     50function FilterSharpen(bmp: TBGRACustomBitmap; ABounds: TRect; AAmount: integer = 256): TBGRACustomBitmap;
    2851
    2952{ A radial blur applies a blur with a circular influence, i.e, each pixel
     
    3255function FilterBlurRadial(bmp: TBGRACustomBitmap; radius: integer;
    3356  blurType: TRadialBlurType): TBGRACustomBitmap;
     57function CreateRadialBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ARadius: integer;
     58  ABlurType: TRadialBlurType): TFilterTask;
    3459
    3560{ The precise blur allow to specify the blur radius with subpixel accuracy }
    36 function FilterBlurRadialPrecise(bmp: TBGRACustomBitmap;
    37   radius: single): TBGRACustomBitmap;
     61function FilterBlurRadialPrecise(bmp: TBGRACustomBitmap; radius: single): TBGRACustomBitmap;
     62function CreateRadialPreciseBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ARadius: single): TFilterTask;
    3863
    3964{ Motion blur merge pixels in a direction. The oriented parameter specifies
     
    4166function FilterBlurMotion(bmp: TBGRACustomBitmap; distance: single;
    4267  angle: single; oriented: boolean): TBGRACustomBitmap;
    43 
    44 function FilterPixelate(bmp: TBGRACustomBitmap; pixelSize: integer; useResample: boolean; filter: TResampleFilter = rfLinear): TBGRACustomBitmap;
     68function CreateMotionBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ADistance,AAngle: single; AOriented: boolean): TFilterTask;
    4569
    4670{ General purpose blur filter, with a blur mask as parameter to describe
    4771  how pixels influence each other }
    48 function FilterBlur(bmp: TBGRACustomBitmap;
    49   blurMask: TBGRACustomBitmap): TBGRACustomBitmap;
     72function FilterBlur(bmp: TBGRACustomBitmap; blurMask: TBGRACustomBitmap): TBGRACustomBitmap;
     73function CreateBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean = false): TFilterTask;
     74
     75function FilterPixelate(bmp: TBGRACustomBitmap; pixelSize: integer; useResample: boolean; filter: TResampleFilter = rfLinear): TBGRACustomBitmap;
    5076
    5177{ Emboss filter compute a color difference in the angle direction }
    5278function FilterEmboss(bmp: TBGRACustomBitmap; angle: single): TBGRACustomBitmap;
     79function FilterEmboss(bmp: TBGRACustomBitmap; angle: single; ABounds: TRect): TBGRACustomBitmap;
    5380
    5481{ Emboss highlight computes a sort of emboss with 45 degrees angle and
     
    6390function FilterNormalize(bmp: TBGRACustomBitmap;
    6491  eachChannel: boolean = True): TBGRACustomBitmap;
     92function FilterNormalize(bmp: TBGRACustomBitmap; ABounds: TRect;
     93  eachChannel: boolean = True): TBGRACustomBitmap;
    6594
    6695{ Rotate filter rotate the image and clip it in the bounding rectangle }
    6796function FilterRotate(bmp: TBGRACustomBitmap; origin: TPointF;
    68   angle: single): TBGRACustomBitmap;
     97  angle: single; correctBlur: boolean = false): TBGRACustomBitmap;
    6998
    7099{ Grayscale converts colored pixel into grayscale with same luminosity }
    71100function FilterGrayscale(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     101function FilterGrayscale(bmp: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap;
     102function CreateGrayscaleTask(bmp: TBGRACustomBitmap; ABounds: TRect): TFilterTask;
    72103
    73104{ Compute a contour, as if the image was drawn with a 2 pixels-wide black pencil }
     
    79110{ Twirl distortion, i.e. a progressive rotation }
    80111function FilterTwirl(bmp: TBGRACustomBitmap; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap;
     112function FilterTwirl(bmp: TBGRACustomBitmap; ABounds: TRect; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap;
    81113
    82114{ Distort the image as if it were on a vertical cylinder }
     
    88120implementation
    89121
    90 uses Math, GraphType, Dialogs, BGRATransform;
     122uses Math, GraphType, Dialogs, BGRATransform, Types, SysUtils;
     123
     124type
     125  { TGrayscaleTask }
     126
     127  TGrayscaleTask = class(TFilterTask)
     128  private
     129    FBounds: TRect;
     130  public
     131    constructor Create(bmp: TBGRACustomBitmap; ABounds: TRect);
     132  protected
     133    procedure DoExecute; override;
     134  end;
     135
     136  { TBoxBlurTask }
     137
     138  TBoxBlurTask = class(TFilterTask)
     139  private
     140    FBounds: TRect;
     141    FRadius: integer;
     142  public
     143    constructor Create(bmp: TBGRACustomBitmap; ABounds: TRect; radius: integer);
     144  protected
     145    procedure DoExecute; override;
     146  end;
     147
     148  { TRadialBlurTask }
     149
     150  TRadialBlurTask = class(TFilterTask)
     151  private
     152    FBounds: TRect;
     153    FRadius: integer;
     154    FBlurType: TRadialBlurType;
     155  public
     156    constructor Create(bmp: TBGRACustomBitmap; ABounds: TRect; radius: integer;
     157                       blurType: TRadialBlurType);
     158  protected
     159    procedure DoExecute; override;
     160  end;
     161
     162  { TCustomBlurTask }
     163
     164  TCustomBlurTask = class(TFilterTask)
     165  private
     166    FBounds: TRect;
     167    FMask: TBGRACustomBitmap;
     168    FMaskOwned: boolean;
     169  public
     170    constructor Create(bmp: TBGRACustomBitmap; ABounds: TRect; AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean = false);
     171    destructor Destroy; override;
     172  protected
     173    procedure DoExecute; override;
     174  end;
     175
     176  { TRadialPreciseBlurTask }
     177
     178  TRadialPreciseBlurTask = class(TFilterTask)
     179  private
     180    FBounds: TRect;
     181    FRadius: Single;
     182  public
     183    constructor Create(bmp: TBGRACustomBitmap; ABounds: TRect; radius: single);
     184  protected
     185    procedure DoExecute; override;
     186  end;
     187
     188  { TMotionBlurTask }
     189
     190  TMotionBlurTask = class(TFilterTask)
     191  private
     192    FBounds: TRect;
     193    FDistance,FAngle: single;
     194    FOriented: boolean;
     195  public
     196    constructor Create(ABmp: TBGRACustomBitmap; ABounds: TRect; ADistance, AAngle: single; AOriented: boolean);
     197  protected
     198    procedure DoExecute; override;
     199  end;
     200
     201procedure FilterBlurRadial(bmp: TBGRACustomBitmap; ABounds: TRect; radius: integer;
     202  blurType: TRadialBlurType; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
     203procedure FilterBlurRadialPrecise(bmp: TBGRACustomBitmap; ABounds: TRect;
     204  radius: single; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
     205procedure FilterBlurMotion(bmp: TBGRACustomBitmap; ABounds: TRect; distance: single;
     206  angle: single; oriented: boolean; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
     207procedure FilterBlur(bmp: TBGRACustomBitmap; ABounds: TRect;
     208   blurMask: TBGRACustomBitmap; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
    91209
    92210function FilterSmartZoom3(bmp: TBGRACustomBitmap;
     
    98216
    99217var
    100   xb, yb: integer;
     218  xb, yb: Int32or64;
    101219  diag1, diag2, h1, h2, v1, v2: TSmartDiff;
    102220  c,c1,c2:      TBGRAPixel;
     
    105223  function ColorDiff(c1, c2: TBGRAPixel): single;
    106224  var
    107     max1, max2: integer;
     225    max1, max2: Int32or64;
    108226  begin
    109227    if (c1.alpha = 0) and (c2.alpha = 0) then
     
    156274  end;
    157275
    158   function smartDiff(x1, y1, x2, y2: integer): TSmartDiff;
     276  function smartDiff(x1, y1, x2, y2: Int32or64): TSmartDiff;
    159277  var
    160278    c1, c2, c1m, c2m: TBGRAPixel;
     
    209327        begin
    210328          c1 := bmp.GetPixel(xb, yb);
    211           c2 := bmp.GetPixel(integer(xb + 1), integer(yb + 1));
     329          c2 := bmp.GetPixel(xb + 1, yb + 1);
    212330          c := MergeBGRA(c1, c2);
    213331          //restore
    214332          Result.SetPixel(xb * 3 + 2, yb * 3 + 2, bmp.GetPixel(xb, yb));
    215           Result.SetPixel(xb * 3 + 3, yb * 3 + 3, bmp.GetPixel(integer(xb + 1), integer(yb + 1)));
     333          Result.SetPixel(xb * 3 + 3, yb * 3 + 3, bmp.GetPixel(xb + 1, yb + 1));
    216334
    217335          if (diag1.sd < h1.sd) and (diag1.sd < v2.sd) then
     
    250368  of the square. Finally the difference is added to the new pixel, exagerating
    251369  its difference with its neighbours. }
    252 function FilterSharpen(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
    253 const
    254   nbpix = 8;
     370function FilterSharpen(bmp: TBGRACustomBitmap; ABounds: TRect; AAmount: integer = 256): TBGRACustomBitmap;
    255371var
    256   yb, xb:   integer;
    257   dx, dy, n, j: integer;
    258   a_pixels: array[0..nbpix - 1] of TBGRAPixel;
    259   sumR, sumG, sumB, sumA, RGBdiv, nbA: cardinal;
    260   tempPixel, refPixel: TBGRAPixel;
    261   pdest:    PBGRAPixel;
     372  yb, xcount: Int32or64;
     373  dx, dy: Int32or64;
     374  a_pixels: array[-2..1,-2..1] of PBGRAPixel;
     375  sumR, sumG, sumB, sumA, {RGBdiv, }nbA: UInt32or64;
     376  refPixel: TBGRAPixel;
     377  pdest,ptempPixel:    PBGRAPixel;
    262378  bounds:   TRect;
    263 begin
     379  Amount256: boolean;
     380  lastXincluded: boolean;
     381  alpha,rgbDivShr1: uint32or64;
     382begin
     383  if IsRectEmpty(ABounds) then exit;
     384  Amount256 := AAmount = 256;
    264385  Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    265386
    266387  //determine where pixels are in the bitmap
    267388  bounds := bmp.GetImageBounds;
    268   if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then
    269     exit;
     389  if not IntersectRect(bounds, bounds,ABounds) then exit;
    270390  bounds.Left   := max(0, bounds.Left - 1);
    271391  bounds.Top    := max(0, bounds.Top - 1);
    272392  bounds.Right  := min(bmp.Width, bounds.Right + 1);
    273393  bounds.Bottom := min(bmp.Height, bounds.Bottom + 1);
     394  lastXincluded:= bounds.Right < bmp.Width;
    274395
    275396  //loop through the destination bitmap
     
    277398  begin
    278399    pdest := Result.scanline[yb] + bounds.Left;
    279     for xb := bounds.Left to bounds.Right - 1 do
    280     begin
     400    fillchar({%H-}a_pixels,sizeof(a_pixels),0);
     401    for dy := -1 to 1 do
     402      if (yb+dy >= bounds.Top) and (yb+dy < bounds.Bottom) then
     403        a_pixels[dy,1] := bmp.ScanLine[yb+dy]+bounds.Left else
     404          a_pixels[dy,1] := nil;
     405    xcount := bounds.right-bounds.left;
     406    while xcount > 0 do
     407    begin
     408      dec(xcount);
     409
    281410      //for each pixel, read eight surrounding pixels in the source bitmap
    282       n := 0;
    283411      for dy := -1 to 1 do
    284         for dx := -1 to 1 do
    285           if (dx <> 0) or (dy <> 0) then
    286           begin
    287             a_pixels[n] := bmp.GetPixel(integer(xb + dx), integer(yb + dy));
    288             Inc(n);
    289           end;
     412        for dx := -1 to 0 do
     413          a_pixels[dy,dx] := a_pixels[dy,dx+1];
     414      if (xcount > 0) or lastXincluded then
     415      begin
     416        for dy := -1 to 1 do
     417          if a_pixels[dy,0] <> nil then a_pixels[dy,1] := a_pixels[dy,0]+1;
     418      end;
    290419
    291420      //compute sum
     
    294423      sumB   := 0;
    295424      sumA   := 0;
    296       RGBdiv := 0;
     425      //RGBdiv := 0;
    297426      nbA    := 0;
    298427
    299428       {$hints off}
    300       for j := 0 to n - 1 do
    301       begin
    302         tempPixel := a_pixels[j];
    303         sumR      += tempPixel.red * tempPixel.alpha;
    304         sumG      += tempPixel.green * tempPixel.alpha;
    305         sumB      += tempPixel.blue * tempPixel.alpha;
    306         RGBdiv    += tempPixel.alpha;
    307         sumA      += tempPixel.alpha;
    308         Inc(nbA);
    309       end;
     429      for dy := -1 to 1 do
     430        for dx := -1 to 1 do
     431        if (dx<>0) or (dy<>0) then
     432        begin
     433          ptempPixel := a_pixels[dy,dx];
     434          if ptempPixel <> nil then
     435          begin
     436            alpha := ptempPixel^.alpha;
     437            sumR      += ptempPixel^.red * alpha;
     438            sumG      += ptempPixel^.green * alpha;
     439            sumB      += ptempPixel^.blue * alpha;
     440            //RGBdiv    += alpha;
     441            sumA      += alpha;
     442            Inc(nbA);
     443          end;
     444        end;
    310445       {$hints on}
    311446
    312447      //we finally have an average pixel
    313       if (RGBdiv = 0) then
     448      if ({RGBdiv}sumA = 0) then
    314449        refPixel := BGRAPixelTransparent
    315450      else
    316451      begin
    317         refPixel.red   := (sumR + RGBdiv shr 1) div RGBdiv;
    318         refPixel.green := (sumG + RGBdiv shr 1) div RGBdiv;
    319         refPixel.blue  := (sumB + RGBdiv shr 1) div RGBdiv;
     452        rgbDivShr1:= {RGBDiv}sumA shr 1;
     453        refPixel.red   := (sumR + rgbDivShr1) div {RGBdiv}sumA;
     454        refPixel.green := (sumG + rgbDivShr1) div {RGBdiv}sumA;
     455        refPixel.blue  := (sumB + rgbDivShr1) div {RGBdiv}sumA;
    320456        refPixel.alpha := (sumA + nbA shr 1) div nbA;
    321457      end;
    322458
    323459      //read the pixel at the center of the square
    324       tempPixel := bmp.GetPixel(xb, yb);
     460      ptempPixel := a_pixels[0,0];
    325461      if refPixel <> BGRAPixelTransparent then
    326462      begin
    327463        //compute sharpened pixel by adding the difference
    328         tempPixel.red   := max(0, min(255, tempPixel.red +
    329           integer(tempPixel.red - refPixel.red)));
    330         tempPixel.green := max(0, min(255, tempPixel.green +
    331           integer(tempPixel.green - refPixel.green)));
    332         tempPixel.blue  := max(0, min(255, tempPixel.blue +
    333           integer(tempPixel.blue - refPixel.blue)));
    334         tempPixel.alpha := max(0, min(255, tempPixel.alpha +
    335           integer(tempPixel.alpha - refPixel.alpha)));
    336       end;
    337       pdest^ := tempPixel;
     464        if not Amount256 then
     465          pdest^ := BGRA( max(0, min($FFFF, Int32or64(ptempPixel^.red shl 8) +
     466            AAmount*(ptempPixel^.red - refPixel.red))) shr 8,
     467              max(0, min($FFFF, Int32or64(ptempPixel^.green shl 8) +
     468            AAmount*(ptempPixel^.green - refPixel.green))) shr 8,
     469             max(0, min($FFFF, Int32or64(ptempPixel^.blue shl 8) +
     470            AAmount*(ptempPixel^.blue - refPixel.blue))) shr 8,
     471             max(0, min($FFFF, Int32or64(ptempPixel^.alpha shl 8) +
     472            AAmount*(ptempPixel^.alpha - refPixel.alpha))) shr 8 )
     473        else
     474          pdest^ := BGRA( max(0, min(255, (ptempPixel^.red shl 1) - refPixel.red)),
     475             max(0, min(255, (ptempPixel^.green shl 1) - refPixel.green)),
     476             max(0, min(255, (ptempPixel^.blue shl 1) - refPixel.blue)),
     477             max(0, min(255, (ptempPixel^.alpha shl 1) - refPixel.alpha)));
     478      end else
     479        pdest^ := ptempPixel^;
    338480      Inc(pdest);
    339481    end;
    340482  end;
    341483  Result.InvalidateBitmap;
     484end;
     485
     486function FilterSharpen(bmp: TBGRACustomBitmap; AAmount: integer
     487  ): TBGRACustomBitmap;
     488begin
     489  result := FilterSharpen(bmp,rect(0,0,bmp.Width,bmp.Height),AAmount);
    342490end;
    343491
    344492{ Precise blur builds a blur mask with a gradient fill and use
    345493  general purpose blur }
    346 function FilterBlurRadialPrecise(bmp: TBGRACustomBitmap;
    347   radius: single): TBGRACustomBitmap;
     494procedure FilterBlurRadialPrecise(bmp: TBGRACustomBitmap;
     495  ABounds: TRect; radius: single; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    348496var
    349497  blurShape: TBGRACustomBitmap;
     
    352500  if radius = 0 then
    353501  begin
    354     result := bmp.Duplicate;
     502    ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);
    355503    exit;
    356504  end;
     
    360508    BGRABlack, gtRadial, pointF(intRadius, intRadius), pointF(
    361509    intRadius - radius - 1, intRadius), dmSet);
    362   Result := FilterBlur(bmp, blurShape);
     510  FilterBlur(bmp, ABounds, blurShape, ADestination, ACheckShouldStop);
    363511  blurShape.Free;
     512end;
     513
     514function FilterBlurRadialPrecise(bmp: TBGRACustomBitmap; radius: single
     515  ): TBGRACustomBitmap;
     516begin
     517  result := bmp.NewBitmap(bmp.Width,bmp.Height);
     518  FilterBlurRadialPrecise(bmp, rect(0,0,bmp.Width,bmp.Height), radius, result, nil);
     519end;
     520
     521function CreateRadialPreciseBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect;
     522  ARadius: single): TFilterTask;
     523begin
     524  result := TRadialPreciseBlurTask.Create(ABmp,ABounds,ARadius);
    364525end;
    365526
     
    369530  the vertical sums are kept except for the last column of
    370531  the square }
    371 function FilterBlurFast(bmp: TBGRACustomBitmap;
    372   radius: integer): TBGRACustomBitmap;
    373 
     532procedure FilterBlurFast(bmp: TBGRACustomBitmap; ABounds: TRect;
     533  radius: integer; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
     534 {$IFDEF CPU64}{$DEFINE FASTBLUR_DOUBLE}{$ENDIF}
    374535  type
    375536    TRowSum = record
    376       sumR,sumG,sumB,rgbDiv,sumA,aDiv: cardinal;
    377     end;
    378 
    379   function ComputeAverage(sum: TRowSum): TBGRAPixel;
    380   begin
     537      sumR,sumG,sumB,rgbDiv,sumA,aDiv: uint32or64;
     538    end;
     539    TExtendedRowValue = {$IFDEF FASTBLUR_DOUBLE}double{$ELSE}uint64{$ENDIF};
     540    TExtendedRowSum = record
     541      sumR,sumG,sumB,rgbDiv,sumA,aDiv: TExtendedRowValue;
     542    end;
     543
     544  function ComputeExtendedAverage(sum: TExtendedRowSum): TBGRAPixel;
     545  {$IFDEF FASTBLUR_DOUBLE}
     546  var v: uint32or64;
     547  {$ENDIF}
     548  begin
     549    {$IFDEF FASTBLUR_DOUBLE}
     550    v := round(sum.sumA/sum.aDiv);
     551    if v > 255 then result.alpha := 255 else result.alpha := v;
     552    v := round(sum.sumR/sum.rgbDiv);
     553    if v > 255 then result.red := 255 else result.red := v;
     554    v := round(sum.sumG/sum.rgbDiv);
     555    if v > 255 then result.green := 255 else result.green := v;
     556    v := round(sum.sumB/sum.rgbDiv);
     557    if v > 255 then result.blue := 255 else result.blue := v;
     558    {$ELSE}
    381559    result.alpha:= (sum.sumA+sum.aDiv shr 1) div sum.aDiv;
    382560    result.red := (sum.sumR+sum.rgbDiv shr 1) div sum.rgbDiv;
    383561    result.green := (sum.sumG+sum.rgbDiv shr 1) div sum.rgbDiv;
    384562    result.blue := (sum.sumB+sum.rgbDiv shr 1) div sum.rgbDiv;
     563    {$ENDIF}
     564  end;
     565
     566  function ComputeClampedAverage(sum: TRowSum): TBGRAPixel;
     567  var v: UInt32or64;
     568  begin
     569    v := (sum.sumA+sum.aDiv shr 1) div sum.aDiv;
     570    if v > 255 then result.alpha := 255 else result.alpha := v;
     571    v := (sum.sumR+sum.rgbDiv shr 1) div sum.rgbDiv;
     572    if v > 255 then result.red := 255 else result.red := v;
     573    v := (sum.sumG+sum.rgbDiv shr 1) div sum.rgbDiv;
     574    if v > 255 then result.green := 255 else result.green := v;
     575    v := (sum.sumB+sum.rgbDiv shr 1) div sum.rgbDiv;
     576    if v > 255 then result.blue := 255 else result.blue := v;
     577  end;
     578
     579  function ComputeAverage(sum: TRowSum): TBGRAPixel;
     580  begin
     581    result.alpha:= (sum.sumA+sum.aDiv shr 1) div sum.aDiv;
     582    result.red := (sum.sumR+sum.rgbDiv shr 1) div sum.rgbDiv;
     583    result.green := (sum.sumG+sum.rgbDiv shr 1) div sum.rgbDiv;
     584    result.blue := (sum.sumB+sum.rgbDiv shr 1) div sum.rgbDiv;
    385585  end;
    386586
     
    389589{ Normal radial blur compute a blur mask with a GradientFill and
    390590  then posterize to optimize general purpose blur }
    391 function FilterBlurRadialNormal(bmp: TBGRACustomBitmap;
    392   radius: integer): TBGRACustomBitmap;
     591procedure FilterBlurRadialNormal(bmp: TBGRACustomBitmap;
     592  ABounds: TRect; radius: integer; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    393593var
    394594  blurShape: TBGRACustomBitmap;
    395   n: Integer;
     595  n: Int32or64;
    396596  p: PBGRAPixel;
    397597begin
     598  if radius = 0 then
     599  begin
     600    ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);
     601    exit;
     602  end;
    398603  blurShape := bmp.NewBitmap(2 * radius + 1, 2 * radius + 1);
    399604  blurShape.GradientFill(0, 0, blurShape.Width, blurShape.Height, BGRAWhite,
     
    407612    inc(p);
    408613  end;
    409   Result := FilterBlur(bmp, blurShape);
     614  FilterBlur(bmp, ABounds, blurShape, ADestination, ACheckShouldStop);
    410615  blurShape.Free;
    411616end;
    412617
    413618{ Blur disk creates a disk mask with a FillEllipse }
    414 function FilterBlurDisk(bmp: TBGRACustomBitmap; radius: integer): TBGRACustomBitmap;
     619procedure FilterBlurDisk(bmp: TBGRACustomBitmap; ABounds: TRect; radius: integer; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    415620var
    416621  blurShape: TBGRACustomBitmap;
    417622begin
     623  if radius = 0 then
     624  begin
     625    ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);
     626    exit;
     627  end;
    418628  blurShape := bmp.NewBitmap(2 * radius + 1, 2 * radius + 1);
    419629  blurShape.Fill(BGRABlack);
    420630  blurShape.FillEllipseAntialias(radius, radius, radius + 0.5, radius + 0.5, BGRAWhite);
    421   Result := FilterBlur(bmp, blurShape);
     631  FilterBlur(bmp, ABounds, blurShape, ADestination, ACheckShouldStop);
    422632  blurShape.Free;
    423633end;
    424634
    425635{ Corona blur use a circle as mask }
    426 function FilterBlurCorona(bmp: TBGRACustomBitmap; radius: integer): TBGRACustomBitmap;
     636procedure FilterBlurCorona(bmp: TBGRACustomBitmap; ABounds: TRect; radius: integer; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    427637var
    428638  blurShape: TBGRACustomBitmap;
    429639begin
     640  if radius = 0 then
     641  begin
     642    ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);
     643    exit;
     644  end;
    430645  blurShape := bmp.NewBitmap(2 * radius + 1, 2 * radius + 1);
    431646  blurShape.Fill(BGRABlack);
    432647  blurShape.EllipseAntialias(radius, radius, radius, radius, BGRAWhite, 1);
    433   Result := FilterBlur(bmp, blurShape);
     648  FilterBlur(bmp, ABounds, blurShape, ADestination, ACheckShouldStop);
    434649  blurShape.Free;
     650end;
     651
     652function FilterBlurBox(bmp: TBGRACustomBitmap; radius: integer; ADestination: TBGRACustomBitmap): TBGRACustomBitmap;
     653var task: TBoxBlurTask;
     654begin
     655  task := TBoxBlurTask.Create(bmp, rect(0,0,bmp.Width,bmp.Height), radius);
     656  task.Destination := ADestination;
     657  result := task.Execute;
     658  task.Free;
     659end;
     660
     661procedure FilterBlurRadial(bmp: TBGRACustomBitmap; ABounds: TRect; radius: integer;
     662  blurType: TRadialBlurType; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
     663begin
     664  if radius = 0 then
     665  begin
     666    ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);
     667    exit;
     668  end;
     669  case blurType of
     670    rbCorona:  FilterBlurCorona(bmp, ABounds, radius, ADestination, ACheckShouldStop);
     671    rbDisk:    FilterBlurDisk(bmp, ABounds, radius, ADestination, ACheckShouldStop);
     672    rbNormal:  FilterBlurRadialNormal(bmp, ABounds, radius, ADestination, ACheckShouldStop);
     673    rbFast:    FilterBlurFast(bmp, ABounds, radius, ADestination, ACheckShouldStop);
     674    rbPrecise: FilterBlurRadialPrecise(bmp, ABounds, radius / 10, ADestination, ACheckShouldStop);
     675    rbBox:     FilterBlurBox(bmp, radius, ADestination);
     676  end;
    435677end;
    436678
     
    438680  blurType: TRadialBlurType): TBGRACustomBitmap;
    439681begin
    440   if radius = 0 then
    441   begin
    442     result := bmp.Duplicate;
    443     exit;
    444   end;
    445   case blurType of
    446     rbCorona: Result  := FilterBlurCorona(bmp, radius);
    447     rbDisk: Result    := FilterBlurDisk(bmp, radius);
    448     rbNormal: Result  := FilterBlurRadialNormal(bmp, radius);
    449     rbFast: Result  := FilterBlurFast(bmp, radius);
    450     rbPrecise: Result := FilterBlurRadialPrecise(bmp, radius / 10);
    451     else
    452       Result := nil;
    453   end;
     682  if blurType = rbBox then
     683  begin
     684    result := FilterBlurBox(bmp,radius,nil);
     685  end else
     686  begin
     687    result := bmp.NewBitmap(bmp.width,bmp.Height);
     688    FilterBlurRadial(bmp, rect(0,0,bmp.Width,bmp.height), radius, blurType,result,nil);
     689  end;
     690end;
     691
     692function CreateRadialBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ARadius: integer;
     693  ABlurType: TRadialBlurType): TFilterTask;
     694begin
     695  if ABlurType = rbBox then
     696    result := TBoxBlurTask.Create(ABmp,ABounds,ARadius)
     697  else
     698    result := TRadialBlurTask.Create(ABmp,ABounds,ARadius,ABlurType);
    454699end;
    455700
    456701{ This filter draws an antialiased line to make the mask, and
    457702  if the motion blur is oriented, does a GradientFill to orient it }
    458 function FilterBlurMotion(bmp: TBGRACustomBitmap; distance: single;
    459   angle: single; oriented: boolean): TBGRACustomBitmap;
     703procedure FilterBlurMotion(bmp: TBGRACustomBitmap; ABounds: TRect; distance: single;
     704  angle: single; oriented: boolean; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    460705var
    461706  blurShape: TBGRACustomBitmap;
     
    463708  dx, dy, d: single;
    464709begin
    465   if distance = 0 then
    466   begin
    467     result := bmp.Duplicate;
     710  if distance < 1e-6 then
     711  begin
     712    ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);
    468713    exit;
    469714  end;
     
    482727      pointF(intRadius + dx * (d + 0.5), intRadius + dy * (d + 0.5)),
    483728      dmFastBlend, False);
    484   Result := FilterBlur(bmp, blurShape);
     729  FilterBlur(bmp, ABounds, blurShape, ADestination, ACheckShouldStop);
    485730  blurShape.Free;
     731end;
     732
     733function FilterBlurMotion(bmp: TBGRACustomBitmap; distance: single;
     734  angle: single; oriented: boolean): TBGRACustomBitmap;
     735begin
     736  result := bmp.NewBitmap(bmp.Width,bmp.Height);
     737  FilterBlurMotion(bmp,rect(0,0,bmp.Width,bmp.Height),distance,angle,oriented,result,nil);
     738end;
     739
     740function CreateMotionBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect;
     741  ADistance, AAngle: single; AOriented: boolean): TFilterTask;
     742begin
     743  result := TMotionBlurTask.Create(ABmp,ABounds,ADistance,AAngle,AOriented);
    486744end;
    487745
    488746{ General purpose blur : compute pixel sum according to the mask and then
    489747  compute only difference while scanning from the left to the right }
    490 function FilterBlurSmallMask(bmp: TBGRACustomBitmap;
    491   blurMask: TBGRACustomBitmap): TBGRACustomBitmap; forward;
    492 function FilterBlurSmallMaskWithShift(bmp: TBGRACustomBitmap;
    493   blurMask: TBGRACustomBitmap; maskShift: integer): TBGRACustomBitmap; forward;
    494 function FilterBlurBigMask(bmp: TBGRACustomBitmap;
    495   blurMask: TBGRACustomBitmap): TBGRACustomBitmap; forward;
     748procedure FilterBlurSmallMask(bmp: TBGRACustomBitmap;
     749  blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
     750procedure FilterBlurSmallMaskWithShift(bmp: TBGRACustomBitmap;
     751  blurMask: TBGRACustomBitmap; maskShift: integer; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
     752procedure FilterBlurBigMask(bmp: TBGRACustomBitmap;
     753  blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
     754procedure FilterBlurMask64(bmp: TBGRACustomBitmap;
     755  blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
    496756
    497757//make sure value is in the range 0..255
    498 function clampByte(value: integer): byte; inline;
     758function clampByte(value: Int32or64): byte; inline;
    499759begin
    500760  if value < 0 then result := 0 else
     
    505765function FilterPixelate(bmp: TBGRACustomBitmap; pixelSize: integer;
    506766  useResample: boolean; filter: TResampleFilter): TBGRACustomBitmap;
    507 var yb,xb, xs,ys, tx,ty: integer;
     767var yb,xb, xs,ys, tx,ty: Int32or64;
    508768    psrc,pdest: PBGRAPixel;
    509769    temp,stretched: TBGRACustomBitmap;
     
    531791      psrc := bmp.scanline[ys]+xs;
    532792      inc(ys,pixelSize);
    533       for xb := 0 to temp.width-1 do
     793      for xb := temp.width-1 downto 0 do
    534794      begin
    535795        pdest^ := psrc^;
     
    560820end;
    561821
    562 function FilterBlur(bmp: TBGRACustomBitmap;
    563   blurMask: TBGRACustomBitmap): TBGRACustomBitmap;
     822function FilterBlur(bmp: TBGRACustomBitmap; blurMask: TBGRACustomBitmap): TBGRACustomBitmap;
     823begin
     824  result := bmp.NewBitmap(bmp.Width,bmp.Height);
     825  FilterBlur(bmp,rect(0,0,bmp.Width,bmp.Height),blurMask,result,nil);
     826end;
     827
     828function CreateBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect;
     829  AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean): TFilterTask;
     830begin
     831  result := TCustomBlurTask.Create(ABmp,ABounds,AMask,AMaskIsThreadSafe);
     832end;
     833
     834procedure FilterBlur(bmp: TBGRACustomBitmap;
     835  ABounds: TRect; blurMask: TBGRACustomBitmap; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
     836{$IFDEF CPU64}
     837begin
     838    FilterBlurMask64(bmp,blurMask,ABounds,ADestination,ACheckShouldStop);
     839end;
     840{$ELSE}
    564841var
    565842  maskSum: int64;
    566   i: Integer;
     843  i: Int32or64;
    567844  p: PBGRAPixel;
    568845  maskShift: integer;
     
    583860  //check if sum can be stored in a 32-bit signed integer
    584861  if maskShift = 0 then
    585     result := FilterBlurSmallMask(bmp,blurMask) else
     862    FilterBlurSmallMask(bmp,blurMask,ABounds,ADestination,ACheckShouldStop) else
     863  {$IFDEF CPU32}
    586864  if maskShift < 8 then
    587     result := FilterBlurSmallMaskWithShift(bmp,blurMask,maskShift) else
    588     result := FilterBlurBigMask(bmp,blurMask);
    589 end;
     865    FilterBlurSmallMaskWithShift(bmp,blurMask,maskShift,ABounds,ADestination,ACheckShouldStop) else
     866  {$ENDIF}
     867    FilterBlurBigMask(bmp,blurMask,ABounds,ADestination,ACheckShouldStop);
     868end;
     869{$ENDIF}
    590870
    591871//32-bit blur with shift
    592 function FilterBlurSmallMaskWithShift(bmp: TBGRACustomBitmap;
    593   blurMask: TBGRACustomBitmap; maskShift: integer): TBGRACustomBitmap;
     872procedure FilterBlurSmallMaskWithShift(bmp: TBGRACustomBitmap;
     873  blurMask: TBGRACustomBitmap; maskShift: integer; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    594874
    595875  var
     
    613893
    614894//32-bit blur
    615 function FilterBlurSmallMask(bmp: TBGRACustomBitmap;
    616   blurMask: TBGRACustomBitmap): TBGRACustomBitmap;
     895procedure FilterBlurSmallMask(bmp: TBGRACustomBitmap;
     896  blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    617897
    618898  var
     
    634914  {$I blurnormal.inc}
    635915
     916//64-bit blur
     917procedure FilterBlurMask64(bmp: TBGRACustomBitmap;
     918  blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
     919
     920  var
     921    sumR, sumG, sumB, sumA, Adiv : int64;
     922
     923  function ComputeAverage: TBGRAPixel; inline;
     924  begin
     925    result.alpha := (sumA + Adiv shr 1) div Adiv;
     926    if result.alpha = 0 then
     927      result := BGRAPixelTransparent
     928    else
     929    begin
     930      result.red   := clampByte((sumR + sumA shr 1) div sumA);
     931      result.green := clampByte((sumG + sumA shr 1) div sumA);
     932      result.blue  := clampByte((sumB + sumA shr 1) div sumA);
     933    end;
     934  end;
     935
     936  {$I blurnormal.inc}
     937
    636938//floating point blur
    637 function FilterBlurBigMask(bmp: TBGRACustomBitmap;
    638   blurMask: TBGRACustomBitmap): TBGRACustomBitmap;
     939procedure FilterBlurBigMask(bmp: TBGRACustomBitmap;
     940  blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    639941
    640942  var
     
    655957
    656958  {$I blurnormal.inc}
     959function FilterEmboss(bmp: TBGRACustomBitmap; angle: single): TBGRACustomBitmap;
     960begin
     961  result := FilterEmboss(bmp, angle, rect(0,0,bmp.Width,bmp.Height));
     962end;
    657963
    658964{ Emboss filter computes the difference between each pixel and the surrounding pixels
    659965  in the specified direction. }
    660 function FilterEmboss(bmp: TBGRACustomBitmap; angle: single): TBGRACustomBitmap;
     966function FilterEmboss(bmp: TBGRACustomBitmap; angle: single; ABounds: TRect): TBGRACustomBitmap;
    661967var
    662   yb, xb: integer;
     968  yb, xb: Int32or64;
    663969  dx, dy: single;
    664   idx1, idy1, idx2, idy2, idx3, idy3, idx4, idy4: integer;
     970  idx1, idy1, idx2, idy2, idx3, idy3, idx4, idy4: Int32or64;
    665971  w:      array[1..4] of single;
    666   iw:     cardinal;
     972  iw:     uint32or64;
    667973  c:      array[0..4] of TBGRAPixel;
    668974
    669   i:     integer;
    670   sumR, sumG, sumB, sumA, RGBdiv, Adiv: cardinal;
     975  i:     Int32or64;
     976  sumR, sumG, sumB, sumA, RGBdiv, Adiv: UInt32or64;
    671977  tempPixel, refPixel: TBGRAPixel;
    672978  pdest: PBGRAPixel;
    673979
    674980  bounds: TRect;
    675 begin
     981  onHorizBorder: boolean;
     982  psrc: array[-1..1] of PBGRAPixel;
     983begin
     984  if IsRectEmpty(ABounds) then exit;
    676985  //compute pixel position and weight
    677986  dx   := cos(angle * Pi / 180);
     
    6961005
    6971006  bounds := bmp.GetImageBounds;
    698   if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then
    699     exit;
     1007  if not IntersectRect(bounds, bounds, ABounds) then exit;
    7001008  bounds.Left   := max(0, bounds.Left - 1);
    7011009  bounds.Top    := max(0, bounds.Top - 1);
     
    7071015  begin
    7081016    pdest := Result.scanline[yb] + bounds.Left;
     1017    onHorizBorder:= (yb=0) or (yb=bmp.Height-1);
     1018    psrc[0] := bmp.ScanLine[yb]+bounds.Left;
     1019    if (yb>0) then psrc[-1] := bmp.ScanLine[yb-1]+bounds.Left else psrc[-1] := nil;
     1020    if (yb<bmp.Height-1) then psrc[1] := bmp.ScanLine[yb+1]+bounds.Left else psrc[1] := nil;
    7091021    for xb := bounds.Left to bounds.Right - 1 do
    7101022    begin
    711       c[0] := bmp.getPixel(xb, yb);
    712       c[1] := bmp.getPixel(integer(xb + idx1), integer(yb + idy1));
    713       c[2] := bmp.getPixel(integer(xb + idx2), integer(yb + idy2));
    714       c[3] := bmp.getPixel(integer(xb + idx3), integer(yb + idy3));
    715       c[4] := bmp.getPixel(integer(xb + idx4), integer(yb + idy4));
     1023      c[0] := psrc[0]^;
     1024      if onHorizBorder or (xb=0) or (xb=bmp.Width-1) then
     1025      begin
     1026        c[1] := bmp.getPixel(xb + idx1, yb + idy1);
     1027        c[2] := bmp.getPixel(xb + idx2, yb + idy2);
     1028        c[3] := bmp.getPixel(xb + idx3, yb + idy3);
     1029        c[4] := bmp.getPixel(xb + idx4, yb + idy4);
     1030      end else
     1031      begin
     1032        c[1] := (psrc[idy1]+idx1)^;
     1033        c[2] := (psrc[idy2]+idx2)^;
     1034        c[3] := (psrc[idy3]+idx3)^;
     1035        c[4] := (psrc[idy4]+idx4)^;
     1036      end;
    7161037
    7171038      sumR   := 0;
     
    7621083      pdest^ := tempPixel;
    7631084      Inc(pdest);
     1085      inc(psrc[0]);
     1086      if psrc[-1] <> nil then inc(psrc[-1]);
     1087      if psrc[1] <> nil then inc(psrc[1]);
    7641088    end;
    7651089  end;
     
    7711095  FillSelection: boolean; DefineBorderColor: TBGRAPixel): TBGRACustomBitmap;
    7721096var
    773   yb, xb: integer;
    774   c0,c1,c2,c3,c4,c5,c6: integer;
    775 
    776   bmpWidth, bmpHeight: integer;
     1097  yb, xb: Int32or64;
     1098  c0,c1,c2,c3,c4,c5,c6: Int32or64;
     1099
     1100  bmpWidth, bmpHeight: Int32or64;
    7771101  slope, h: byte;
    778   sum:      integer;
     1102  sum:      Int32or64;
    7791103  tempPixel, highlight: TBGRAPixel;
    7801104  pdest, psrcUp, psrc, psrcDown: PBGRAPixel;
     
    7821106  bounds: TRect;
    7831107  borderColorOverride: boolean;
    784   borderColorLevel: integer;
    785 
    786   currentBorderColor: integer;
     1108  borderColorLevel: Int32or64;
     1109
     1110  currentBorderColor: Int32or64;
    7871111begin
    7881112  borderColorOverride := DefineBorderColor.alpha <> 0;
     
    9051229  FillSelection: boolean; DefineBorderColor: TBGRAPixel; var Offset: TPoint): TBGRACustomBitmap;
    9061230var
    907   yb, xb: integer;
    908   c0,c1,c2,c3,c4,c5,c6: integer;
    909 
    910   bmpWidth, bmpHeight: integer;
     1231  yb, xb: int32or64;
     1232  c0,c1,c2,c3,c4,c5,c6: int32or64;
     1233
     1234  bmpWidth, bmpHeight: int32or64;
    9111235  slope, h: byte;
    912   sum:      integer;
     1236  sum:      int32or64;
    9131237  tempPixel, highlight: TBGRAPixel;
    9141238  pdest, psrcUp, psrc, psrcDown: PBGRAPixel;
     
    9161240  bounds: TRect;
    9171241  borderColorOverride: boolean;
    918   borderColorLevel: integer;
    919 
    920   currentBorderColor: integer;
     1242  borderColorLevel: int32or64;
     1243
     1244  currentBorderColor: int32or64;
    9211245begin
    9221246  borderColorOverride := DefineBorderColor.alpha <> 0;
     
    10421366end;
    10431367
     1368function FilterNormalize(bmp: TBGRACustomBitmap; eachChannel: boolean
     1369  ): TBGRACustomBitmap;
     1370begin
     1371  result := FilterNormalize(bmp, rect(0,0,bmp.Width,bmp.Height), eachChannel);
     1372end;
     1373
    10441374{ Normalize compute min-max of specified channel and apply an affine transformation
    10451375  to make it use the full range of values }
    1046 function FilterNormalize(bmp: TBGRACustomBitmap;
     1376function FilterNormalize(bmp: TBGRACustomBitmap; ABounds: TRect;
    10471377  eachChannel: boolean = True): TBGRACustomBitmap;
    10481378var
    10491379  psrc, pdest: PBGRAPixel;
    10501380  c: TExpandedPixel;
    1051   n: integer;
     1381  xcount,xb,yb: int32or64;
    10521382  minValRed, maxValRed, minValGreen, maxValGreen, minValBlue, maxValBlue,
    10531383  minAlpha, maxAlpha, addValRed, addValGreen, addValBlue, addAlpha: word;
    1054   factorValRed, factorValGreen, factorValBlue, factorAlpha: integer;
    1055 begin
     1384  factorValRed, factorValGreen, factorValBlue, factorAlpha: int32or64;
     1385begin
     1386  if not IntersectRect(ABounds,ABounds,rect(0,0,bmp.Width,bmp.Height)) then exit;
    10561387  Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    10571388  bmp.LoadFromBitmapIfNeeded;
    1058   psrc      := bmp.Data;
    10591389  maxValRed := 0;
    10601390  minValRed := 65535;
     
    10651395  maxAlpha  := 0;
    10661396  minAlpha  := 65535;
    1067   for n := bmp.Width * bmp.Height - 1 downto 0 do
    1068   begin
    1069     c := GammaExpansion(psrc^);
    1070     Inc(psrc);
    1071     if c.red > maxValRed then
    1072       maxValRed := c.red;
    1073     if c.green > maxValGreen then
    1074       maxValGreen := c.green;
    1075     if c.blue > maxValBlue then
    1076       maxValBlue := c.blue;
    1077     if c.red < minValRed then
    1078       minValRed := c.red;
    1079     if c.green < minValGreen then
    1080       minValGreen := c.green;
    1081     if c.blue < minValBlue then
    1082       minValBlue := c.blue;
    1083 
    1084     if c.alpha > maxAlpha then
    1085       maxAlpha := c.alpha;
    1086     if c.alpha < minAlpha then
    1087       minAlpha := c.alpha;
     1397  xcount := ABounds.Right-ABounds.Left;
     1398  for yb := ABounds.Top to ABounds.Bottom-1 do
     1399  begin
     1400    psrc := bmp.ScanLine[yb]+ABounds.Left;
     1401    for xb := xcount-1 downto 0 do
     1402    begin
     1403      c := GammaExpansion(psrc^);
     1404      Inc(psrc);
     1405      if c.red > maxValRed then
     1406        maxValRed := c.red;
     1407      if c.green > maxValGreen then
     1408        maxValGreen := c.green;
     1409      if c.blue > maxValBlue then
     1410        maxValBlue := c.blue;
     1411      if c.red < minValRed then
     1412        minValRed := c.red;
     1413      if c.green < minValGreen then
     1414        minValGreen := c.green;
     1415      if c.blue < minValBlue then
     1416        minValBlue := c.blue;
     1417
     1418      if c.alpha > maxAlpha then
     1419        maxAlpha := c.alpha;
     1420      if c.alpha < minAlpha then
     1421        minAlpha := c.alpha;
     1422    end;
    10881423  end;
    10891424  if not eachChannel then
     
    11491484  end;
    11501485
    1151   psrc  := bmp.Data;
    1152   pdest := Result.Data;
    1153   for n := bmp.Width * bmp.Height - 1 downto 0 do
    1154   begin
    1155     c := GammaExpansion(psrc^);
    1156     Inc(psrc);
    1157     c.red   := ((c.red - minValRed) * factorValRed + 2047) shr 12 + addValRed;
    1158     c.green := ((c.green - minValGreen) * factorValGreen + 2047) shr 12 + addValGreen;
    1159     c.blue  := ((c.blue - minValBlue) * factorValBlue + 2047) shr 12 + addValBlue;
    1160     c.alpha := ((c.alpha - minAlpha) * factorAlpha + 2047) shr 12 + addAlpha;
    1161     pdest^  := GammaCompression(c);
    1162     Inc(pdest);
     1486  for yb := ABounds.Top to ABounds.Bottom-1 do
     1487  begin
     1488    psrc := bmp.ScanLine[yb]+ABounds.Left;
     1489    pdest := Result.ScanLine[yb]+ABounds.Left;
     1490    for xb := xcount-1 downto 0 do
     1491    begin
     1492      c := GammaExpansion(psrc^);
     1493      Inc(psrc);
     1494      c.red   := ((c.red - minValRed) * factorValRed + 2047) shr 12 + addValRed;
     1495      c.green := ((c.green - minValGreen) * factorValGreen + 2047) shr 12 + addValGreen;
     1496      c.blue  := ((c.blue - minValBlue) * factorValBlue + 2047) shr 12 + addValBlue;
     1497      c.alpha := ((c.alpha - minAlpha) * factorAlpha + 2047) shr 12 + addAlpha;
     1498      pdest^  := GammaCompression(c);
     1499      Inc(pdest);
     1500    end;
    11631501  end;
    11641502  Result.InvalidateBitmap;
     
    11681506  calculates the position in the source bitmap with an affine transformation }
    11691507function FilterRotate(bmp: TBGRACustomBitmap; origin: TPointF;
    1170   angle: single): TBGRACustomBitmap;
     1508  angle: single; correctBlur: boolean): TBGRACustomBitmap;
    11711509var
    11721510  bounds:     TRect;
     
    11751513  savexysrc, pt: TPointF;
    11761514  dx, dy:     single;
    1177   xb, yb:     integer;
     1515  xb, yb:     int32or64;
    11781516  minx, miny, maxx, maxy: single;
     1517  rf : TResampleFilter;
    11791518
    11801519  function RotatePos(x, y: single): TPointF;
     
    11881527
    11891528begin
    1190   Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    11911529  bounds := bmp.GetImageBounds;
    11921530  if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then
     1531  begin
     1532    Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    11931533    exit;
     1534  end;
     1535
     1536  Result := bmp.NewBitmap(bmp.Width, bmp.Height);
     1537  if correctBlur then rf := rfHalfCosine else rf := rfLinear;
    11941538
    11951539  //compute new bounding rectangle
     
    12511595    for xb := bounds.left to bounds.right - 1 do
    12521596    begin
    1253       pdest^ := bmp.GetPixel(xsrc, ysrc);
     1597      pdest^ := bmp.GetPixel(xsrc, ysrc, rf);
    12541598      Inc(pdest);
    12551599      xsrc += dx;
     
    12631607
    12641608{ Filter grayscale applies BGRAToGrayscale function to all pixels }
    1265 function FilterGrayscale(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     1609procedure FilterGrayscale(bmp: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    12661610var
    1267   bounds:      TRect;
    12681611  pdest, psrc: PBGRAPixel;
    1269   xb, yb:      integer;
    1270 
    1271 begin
    1272   Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    1273   bounds := bmp.GetImageBounds;
    1274   if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then
    1275     exit;
    1276 
    1277   for yb := bounds.Top to bounds.bottom - 1 do
    1278   begin
    1279     pdest := Result.scanline[yb] + bounds.left;
    1280     psrc  := bmp.scanline[yb] + bounds.left;
    1281     for xb := bounds.left to bounds.right - 1 do
     1612  xb, yb:      int32or64;
     1613
     1614begin
     1615  if IsRectEmpty(ABounds) then exit;
     1616
     1617  for yb := ABounds.Top to ABounds.bottom - 1 do
     1618  begin
     1619    if Assigned(ACheckShouldStop) and ACheckShouldStop(yb) then break;
     1620    pdest := ADestination.scanline[yb] + ABounds.left;
     1621    psrc  := bmp.scanline[yb] + ABounds.left;
     1622    for xb := ABounds.left to ABounds.right - 1 do
    12821623    begin
    12831624      pdest^ := BGRAToGrayscale(psrc^);
     
    12861627    end;
    12871628  end;
    1288   Result.InvalidateBitmap;
     1629  ADestination.InvalidateBitmap;
     1630end;
     1631
     1632function FilterGrayscale(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     1633begin
     1634  result := FilterGrayscale(bmp, rect(0,0,bmp.width,bmp.Height));
     1635end;
     1636
     1637function FilterGrayscale(bmp: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap;
     1638begin
     1639  result := bmp.NewBitmap(bmp.Width,bmp.Height);
     1640  FilterGrayscale(bmp,ABounds,result,nil);
     1641end;
     1642
     1643function CreateGrayscaleTask(bmp: TBGRACustomBitmap; ABounds: TRect
     1644  ): TFilterTask;
     1645begin
     1646  result := TGrayscaleTask.Create(bmp,ABounds);
    12891647end;
    12901648
     
    12941652function FilterContour(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
    12951653var
    1296   yb, xb: integer;
     1654  yb, xb: int32or64;
    12971655  c:      array[0..8] of TBGRAPixel;
    12981656
    1299   i, bmpWidth, bmpHeight: integer;
     1657  i, bmpWidth, bmpHeight: int32or64;
    13001658  slope: byte;
    1301   sum:   integer;
     1659  sum:   int32or64;
    13021660  tempPixel: TBGRAPixel;
    13031661  pdest, psrcUp, psrc, psrcDown: PBGRAPixel;
     
    14121770var
    14131771  cx, cy, x, y, len, fact: single;
    1414   xb, yb: integer;
     1772  xb, yb: int32or64;
    14151773  mask:   TBGRACustomBitmap;
    14161774begin
     
    14431801
    14441802{ Applies twirl scanner. See TBGRATwirlScanner }
    1445 function FilterTwirl(bmp: TBGRACustomBitmap; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap;
     1803function FilterTwirl(bmp: TBGRACustomBitmap; ABounds: TRect; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap;
    14461804var twirl: TBGRATwirlScanner;
    14471805begin
    14481806  twirl := TBGRATwirlScanner.Create(bmp,ACenter,ARadius,ATurn,AExponent);
    14491807  Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    1450   result.Fill(twirl);
     1808  result.FillRect(ABounds, twirl, dmSet);
    14511809  twirl.free;
     1810end;
     1811
     1812function FilterTwirl(bmp: TBGRACustomBitmap; ACenter: TPoint;
     1813  ARadius: Single; ATurn: Single; AExponent: Single): TBGRACustomBitmap;
     1814begin
     1815  result := FilterTwirl(bmp,rect(0,0,bmp.Width,bmp.Height),ACenter,ARadius,ATurn,AExponent);
    14521816end;
    14531817
     
    14581822var
    14591823  cx, cy, x, y, len, fact: single;
    1460   xb, yb: integer;
     1824  xb, yb: int32or64;
    14611825begin
    14621826  Result := bmp.NewBitmap(bmp.Width, bmp.Height);
     
    14851849var
    14861850  cy, x1, x2, y1, y2, z1, z2, h: single;
    1487   yb: integer;
     1851  yb: int32or64;
    14881852  resampledBmp: TBGRACustomBitmap;
    1489   resampledBmpWidth: integer;
     1853  resampledBmpWidth: int32or64;
    14901854  resampledFactor,newResampleFactor: single;
    14911855  sub,resampledSub: TBGRACustomBitmap;
    14921856  partRect: TRect;
    1493   resampleSizeY : integer;
     1857  resampleSizeY : int32or64;
    14941858begin
    14951859  resampledBmp := bmp.Resample(bmp.Width*2,bmp.Height*2,rmSimpleStretch);
     
    15541918  begin
    15551919    if (p1.red + p1.green + p1.blue = p2.red + p2.green + p2.blue) then
    1556       Result := (integer(p1.red) shl 8) + (integer(p1.green) shl 16) +
    1557         integer(p1.blue) < (integer(p2.red) shl 8) + (integer(p2.green) shl 16) +
    1558         integer(p2.blue)
     1920      Result := (int32or64(p1.red) shl 8) + (int32or64(p1.green) shl 16) +
     1921        int32or64(p1.blue) < (int32or64(p2.red) shl 8) + (int32or64(p2.green) shl 16) +
     1922        int32or64(p2.blue)
    15591923    else
    15601924      Result := (p1.red + p1.green + p1.blue) < (p2.red + p2.green + p2.blue);
     
    15641928  nbpix = 9;
    15651929var
    1566   yb, xb:    integer;
    1567   dx, dy, n, i, j, k: integer;
     1930  yb, xb:    int32or64;
     1931  dx, dy, n, i, j, k: int32or64;
    15681932  a_pixels:  array[0..nbpix - 1] of TBGRAPixel;
    15691933  tempPixel, refPixel: TBGRAPixel;
    15701934  tempValue: byte;
    1571   sumR, sumG, sumB, sumA, BGRAdiv, nbA: cardinal;
     1935  sumR, sumG, sumB, sumA, BGRAdiv, nbA: uint32or64;
    15721936  tempAlpha: word;
    15731937  bounds:    TRect;
     
    15931957        for dx := -1 to 1 do
    15941958        begin
    1595           a_pixels[n] := bmp.GetPixel(integer(xb + dx), integer(yb + dy));
     1959          a_pixels[n] := bmp.GetPixel(xb + dx, yb + dy);
    15961960          if a_pixels[n].alpha = 0 then
    15971961            a_pixels[n] := BGRAPixelTransparent;
     
    16952059end;
    16962060
     2061constructor TBoxBlurTask.Create(bmp: TBGRACustomBitmap; ABounds: TRect;
     2062  radius: integer);
     2063begin
     2064  FSource := bmp;
     2065  FBounds := ABounds;
     2066  FRadius := radius;
     2067end;
     2068
     2069procedure TBoxBlurTask.DoExecute;
     2070type
     2071  TVertical = record red,green,blue,alpha,count: NativeUint; end;
     2072  PVertical = ^TVertical;
     2073var
     2074  verticals: PVertical;
     2075  left,right,width,height: NativeInt;
     2076  delta: PtrInt;
     2077
     2078  procedure PrepareVerticals;
     2079  var
     2080    xb,yb: NativeInt;
     2081    psrc,p: PBGRAPixel;
     2082    pvert : PVertical;
     2083  begin
     2084    fillchar(verticals^, width*sizeof(TVertical), 0);
     2085    psrc := FSource.ScanLine[FBounds.Top];
     2086    pvert := verticals;
     2087    for xb := left to right-1 do
     2088    begin
     2089      p := psrc+xb;
     2090      for yb := 0 to FRadius-1 do
     2091      begin
     2092        if yb = height then break;
     2093        if p^.alpha <> 0 then
     2094        begin
     2095          pvert^.red += p^.red * p^.alpha;
     2096          pvert^.green += p^.green * p^.alpha;
     2097          pvert^.blue += p^.blue * p^.alpha;
     2098          pvert^.alpha += p^.alpha;
     2099        end;
     2100        inc(pvert^.count);
     2101        PByte(p) += delta;
     2102      end;
     2103      inc(pvert);
     2104    end;
     2105  end;
     2106
     2107  procedure NextVerticals(y: integer);
     2108  var
     2109    psrc1,psrc2: PBGRAPixel;
     2110    pvert : PVertical;
     2111    xb: NativeInt;
     2112  begin
     2113    pvert := verticals;
     2114    if y-FRadius-1 >= 0 then
     2115      psrc1 := FSource.ScanLine[y-FRadius-1]
     2116    else
     2117      psrc1 := nil;
     2118    if y+FRadius < FSource.Height then
     2119      psrc2 := FSource.ScanLine[y+FRadius]
     2120    else
     2121      psrc2 := nil;
     2122    for xb := width-1 downto 0 do
     2123    begin
     2124      if psrc1 <> nil then
     2125      begin
     2126        if psrc1^.alpha <> 0 then
     2127        begin
     2128          {$HINTS OFF}
     2129          pvert^.red -= psrc1^.red * psrc1^.alpha;
     2130          pvert^.green -= psrc1^.green * psrc1^.alpha;
     2131          pvert^.blue -= psrc1^.blue * psrc1^.alpha;
     2132          pvert^.alpha -= psrc1^.alpha;
     2133          {$HINTS ON}
     2134        end;
     2135        dec(pvert^.count);
     2136        inc(psrc1);
     2137      end;
     2138      if psrc2 <> nil then
     2139      begin
     2140        if psrc2^.alpha <> 0 then
     2141        begin
     2142          pvert^.red += psrc2^.red * psrc2^.alpha;
     2143          pvert^.green += psrc2^.green * psrc2^.alpha;
     2144          pvert^.blue += psrc2^.blue * psrc2^.alpha;
     2145          pvert^.alpha += psrc2^.alpha;
     2146        end;
     2147        inc(pvert^.count);
     2148        inc(psrc2);
     2149      end;
     2150      inc(pvert);
     2151    end;
     2152  end;
     2153
     2154  procedure MainLoop;
     2155  var
     2156    xb,yb,xdest: NativeInt;
     2157    pdest: PBGRAPixel;
     2158    pvert : PVertical;
     2159    sumRed,sumGreen,sumBlue,sumAlpha,sumCount: NativeUInt;
     2160  begin
     2161    for yb := FBounds.Top to FBounds.Bottom-1 do
     2162    begin
     2163      NextVerticals(yb);
     2164      if GetShouldStop(yb) then exit;
     2165      pdest := Destination.ScanLine[yb]+left;
     2166      sumRed := 0;
     2167      sumGreen := 0;
     2168      sumBlue := 0;
     2169      sumAlpha := 0;
     2170      sumCount := 0;
     2171      pvert := verticals;
     2172      for xb := 0 to FRadius-1 do
     2173      begin
     2174        if xb = width then break;
     2175        sumRed += pvert^.red;
     2176        sumGreen += pvert^.green;
     2177        sumBlue += pvert^.blue;
     2178        sumAlpha += pvert^.alpha;
     2179        sumCount += pvert^.count;
     2180        inc(pvert);
     2181      end;
     2182      for xdest := 0 to width-1 do
     2183      begin
     2184        if xdest-FRadius-1 >= 0 then
     2185        begin
     2186          pvert := verticals+(xdest-FRadius-1);
     2187          sumRed -= pvert^.red;
     2188          sumGreen -= pvert^.green;
     2189          sumBlue -= pvert^.blue;
     2190          sumAlpha -= pvert^.alpha;
     2191          sumCount -= pvert^.count;
     2192        end;
     2193        if xdest+FRadius < width then
     2194        begin
     2195          pvert := verticals+(xdest+FRadius);
     2196          sumRed += pvert^.red;
     2197          sumGreen += pvert^.green;
     2198          sumBlue += pvert^.blue;
     2199          sumAlpha += pvert^.alpha;
     2200          sumCount += pvert^.count;
     2201        end;
     2202        if (sumCount > 0) and (sumAlpha >= (sumCount+1) shr 1) then
     2203        begin
     2204          pdest^.red := (sumRed+(sumAlpha shr 1)) div sumAlpha;
     2205          pdest^.green := (sumGreen+(sumAlpha shr 1)) div sumAlpha;
     2206          pdest^.blue := (sumBlue+(sumAlpha shr 1)) div sumAlpha;
     2207          pdest^.alpha := (sumAlpha+(sumCount shr 1)) div sumCount;
     2208        end else
     2209          pdest^ := BGRAPixelTransparent;
     2210        inc(pdest);
     2211      end;
     2212    end;
     2213  end;
     2214
     2215begin
     2216  if (FBounds.Right <= FBounds.Left) or (FBounds.Bottom <= FBounds.Top) or (FRadius <= 0) then exit;
     2217  left := FBounds.left;
     2218  right := FBounds.right;
     2219  width := right-left;
     2220  height := FBounds.bottom-FBounds.top;
     2221  delta := FSource.Width*SizeOf(TBGRAPixel);
     2222  if FSource.LineOrder = riloBottomToTop then delta := -delta;
     2223
     2224  getmem(verticals, width*sizeof(TVertical));
     2225  try
     2226    PrepareVerticals;
     2227    MainLoop;
     2228  finally
     2229    freemem(verticals);
     2230  end;
     2231end;
     2232
     2233constructor TGrayscaleTask.Create(bmp: TBGRACustomBitmap; ABounds: TRect);
     2234begin
     2235  FSource := bmp;
     2236  FBounds := ABounds;
     2237end;
     2238
     2239procedure TGrayscaleTask.DoExecute;
     2240begin
     2241  FilterGrayscale(FSource,FBounds,Destination,@GetShouldStop);
     2242end;
     2243
     2244{ TCustomBlurTask }
     2245
     2246constructor TCustomBlurTask.Create(bmp: TBGRACustomBitmap; ABounds: TRect;
     2247  AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean);
     2248begin
     2249  FSource := bmp;
     2250  FBounds := ABounds;
     2251  if AMaskIsThreadSafe then
     2252  begin
     2253    FMask := AMask;
     2254    FMaskOwned := false;
     2255  end else
     2256  begin
     2257    FMask := AMask.Duplicate;
     2258    FMaskOwned := true;
     2259  end;
     2260end;
     2261
     2262destructor TCustomBlurTask.Destroy;
     2263begin
     2264  If FMaskOwned then FreeAndNil(FMask);
     2265  inherited Destroy;
     2266end;
     2267
     2268procedure TCustomBlurTask.DoExecute;
     2269begin
     2270  FilterBlur(FSource,FBounds,FMask,Destination,@GetShouldStop);
     2271end;
     2272
     2273constructor TMotionBlurTask.Create(ABmp: TBGRACustomBitmap; ABounds: TRect;
     2274  ADistance, AAngle: single; AOriented: boolean);
     2275begin
     2276  FSource := ABmp;
     2277  FBounds := ABounds;
     2278  FDistance := ADistance;
     2279  FAngle := AAngle;
     2280  FOriented:= AOriented;
     2281end;
     2282
     2283procedure TMotionBlurTask.DoExecute;
     2284begin
     2285  FilterBlurMotion(FSource,FBounds,FDistance,FAngle,FOriented,Destination,@GetShouldStop);
     2286end;
     2287
     2288constructor TRadialPreciseBlurTask.Create(bmp: TBGRACustomBitmap;
     2289  ABounds: TRect; radius: single);
     2290begin
     2291  FSource := bmp;
     2292  FBounds := ABounds;
     2293  FRadius := radius;
     2294end;
     2295
     2296procedure TRadialPreciseBlurTask.DoExecute;
     2297begin
     2298  FilterBlurRadialPrecise(FSource,FBounds,FRadius,Destination,@GetShouldStop);
     2299end;
     2300
     2301{ TRadialBlurTask }
     2302
     2303constructor TRadialBlurTask.Create(bmp: TBGRACustomBitmap; ABounds: TRect;
     2304  radius: integer; blurType: TRadialBlurType);
     2305begin
     2306  FSource := bmp;
     2307  FBounds := ABounds;
     2308  FRadius := radius;
     2309  FBlurType:= blurType;
     2310end;
     2311
     2312procedure TRadialBlurTask.DoExecute;
     2313begin
     2314  FilterBlurRadial(FSource,FBounds,FRadius,FBlurType,Destination,@GetShouldStop);
     2315end;
     2316
     2317{ TFilterTask }
     2318
     2319function TFilterTask.GetShouldStop(ACurrentY: integer): boolean;
     2320begin
     2321  FCurrentY:= ACurrentY;
     2322  if Assigned(FCheckShouldStop) then
     2323    result := FCheckShouldStop(ACurrentY)
     2324  else
     2325    result := false;
     2326end;
     2327
     2328function TFilterTask.Execute: TBGRACustomBitmap;
     2329var DestinationOwned: boolean;
     2330begin
     2331  FCurrentY := 0;
     2332  if Destination = nil then
     2333  begin
     2334    FDestination := FSource.NewBitmap(FSource.Width,FSource.Height);
     2335    DestinationOwned:= true;
     2336  end else
     2337    DestinationOwned:= false;
     2338  try
     2339    DoExecute;
     2340    result := Destination;
     2341    FDestination := nil;
     2342  except
     2343    on ex: exception do
     2344    begin
     2345      if DestinationOwned then FreeAndNil(FDestination);
     2346      raise ex;
     2347    end;
     2348  end;
     2349end;
     2350
     2351procedure TFilterTask.SetDestination(AValue: TBGRACustomBitmap);
     2352begin
     2353  if FDestination <> nil then
     2354    raise exception.Create('Destination is already defined');
     2355  FDestination := AValue;
     2356end;
     2357
    16972358end.
    16982359
  • GraphicTest/Packages/bgrabitmap/bgrafreetype.pas

    r452 r472  
    33{$mode objfpc}{$H+}
    44
     5{
     6  Font rendering units : BGRAText, BGRATextFX, BGRAVectorize, BGRAFreeType
     7
     8  This units provide a font renderer with FreeType fonts, using the integrated FreeType font engine in Lazarus.
     9  The simplest way to render effects is to use TBGRAFreeTypeFontRenderer class.
     10  To do this, create an instance of this class and assign it to a TBGRABitmap.FontRenderer property. Now functions
     11  to draw text like TBGRABitmap.TextOut will use the chosen renderer.
     12
     13  >> Note that you need to defined the default FreeType font collection
     14  >> using LazFreeTypeFontCollection unit.
     15
     16  To set the effects, keep a variable containing
     17  the TBGRAFreeTypeFontRenderer class and modify ShadowVisible and other effects parameters. The FontHinted property
     18  allows you to choose if the font is snapped to pixels to make it more readable.
     19
     20  TBGRAFreeTypeDrawer class is the class that provides basic FreeType drawing
     21  by deriving the TFreeTypeDrawer type. You can use it directly, but it is not
     22  recommended, because there are less text layout parameters. However, it is
     23  necessary if you want to create TBGRATextEffect objects using FreeType fonts.
     24}
     25
    526interface
    627
    728uses
    8   Classes, SysUtils, Graphics, BGRABitmapTypes, EasyLazFreeType, FPimage;
     29  Types, Classes, SysUtils, Graphics, BGRABitmapTypes, EasyLazFreeType, FPimage, BGRAText, BGRATextFX, BGRAPhongTypes, LCLVersion;
    930
    1031type
     32  TBGRAFreeTypeDrawer = class;
     33
     34  //this is the class to assign to FontRenderer property of TBGRABitmap
     35  { TBGRAFreeTypeFontRenderer }
     36
     37  TBGRAFreeTypeFontRenderer = class(TBGRACustomFontRenderer)
     38  private
     39    FDrawer: TBGRAFreeTypeDrawer;
     40    FFont: TFreeTypeFont;
     41    function GetCollection: TCustomFreeTypeFontCollection;
     42    function GetDrawer(ASurface: TBGRACustomBitmap): TBGRAFreeTypeDrawer;
     43    function GetShaderLightPosition: TPoint;
     44    procedure SetShaderLightPosition(AValue: TPoint);
     45  protected
     46    FShaderOwner: boolean;
     47    FShader: TCustomPhongShading;
     48    procedure UpdateFont;
     49    procedure Init;
     50  public
     51    FontHinted: boolean;
     52
     53    ShaderActive: boolean;
     54
     55    ShadowVisible: boolean;
     56    ShadowColor: TBGRAPixel;
     57    ShadowRadius: integer;
     58    ShadowOffset: TPoint;
     59
     60    OutlineColor: TBGRAPixel;
     61    OutlineVisible,OuterOutlineOnly: boolean;
     62    OutlineTexture: IBGRAScanner;
     63
     64    constructor Create;
     65    constructor Create(AShader: TCustomPhongShading; AShaderOwner: boolean);
     66    function GetFontPixelMetric: TFontPixelMetric; override;
     67    procedure TextOutAngle({%H-}ADest: TBGRACustomBitmap; {%H-}x, {%H-}y: single; {%H-}orientation: integer; {%H-}s: string; {%H-}c: TBGRAPixel; {%H-}align: TAlignment); override;
     68    procedure TextOutAngle({%H-}ADest: TBGRACustomBitmap; {%H-}x, {%H-}y: single; {%H-}orientation: integer; {%H-}s: string; {%H-}texture: IBGRAScanner; {%H-}align: TAlignment); override;
     69    procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; s: string; texture: IBGRAScanner; align: TAlignment); override;
     70    procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; s: string; c: TBGRAPixel; align: TAlignment); override;
     71    procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; s: string; style: TTextStyle; c: TBGRAPixel); override;
     72    procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; s: string; style: TTextStyle; texture: IBGRAScanner); override;
     73    function TextSize(s: string): TSize; override;
     74    destructor Destroy; override;
     75    property Collection: TCustomFreeTypeFontCollection read GetCollection;
     76    property ShaderLightPosition: TPoint read GetShaderLightPosition write SetShaderLightPosition;
     77  end;
    1178
    1279  { TBGRAFreeTypeDrawer }
     
    1683    FMask: TBGRACustomBitmap;
    1784    FColor: TBGRAPixel;
     85    FInCreateTextEffect: boolean;
    1886    procedure RenderDirectly(x, y, tx: integer; data: pointer);
    1987    procedure RenderDirectlyClearType(x, y, tx: integer; data: pointer);
     88    function ShadowActuallyVisible :boolean;
     89    function OutlineActuallyVisible: boolean;
     90    function ShaderActuallyActive : boolean;
    2091  public
    2192    Destination: TBGRACustomBitmap;
    2293    ClearTypeRGBOrder: boolean;
     94    Texture: IBGRAScanner;
     95
     96    Shader: TCustomPhongShading;
     97    ShaderActive: boolean;
     98
     99    ShadowVisible: boolean;
     100    ShadowColor: TBGRAPixel;
     101    ShadowRadius: integer;
     102    ShadowOffset: TPoint;
     103
     104    OutlineColor: TBGRAPixel;
     105    OutlineVisible,OuterOutlineOnly: boolean;
     106    OutlineTexture: IBGRAScanner;
     107
    23108    constructor Create(ADestination: TBGRACustomBitmap);
    24109    procedure DrawText(AText: string; AFont: TFreeTypeRenderableFont; x,y: single; AColor: TFPColor); override; overload;
    25110    procedure DrawText(AText: string; AFont: TFreeTypeRenderableFont; x,y: single; AColor: TBGRAPixel); overload;
    26111    procedure DrawText(AText: string; AFont: TFreeTypeRenderableFont; x,y: single; AColor: TBGRAPixel; AAlign: TFreeTypeAlignments); overload;
     112    function CreateTextEffect(AText: string; AFont: TFreeTypeRenderableFont): TBGRATextEffect;
    27113    destructor Destroy; override;
    28114  end;
    29115
     116
    30117implementation
    31118
    32 uses LCLType, BGRABlend, BGRAText;
     119uses LCLType, BGRABlend, Math;
     120
     121{ TBGRAFreeTypeFontRenderer }
     122
     123function TBGRAFreeTypeFontRenderer.GetCollection: TCustomFreeTypeFontCollection;
     124begin
     125  result := EasyLazFreeType.FontCollection;
     126end;
     127
     128function TBGRAFreeTypeFontRenderer.GetDrawer(ASurface: TBGRACustomBitmap): TBGRAFreeTypeDrawer;
     129begin
     130  result := FDrawer;
     131  result.ShadowColor := ShadowColor;
     132  result.ShadowOffset := ShadowOffset;
     133  result.ShadowRadius := ShadowRadius;
     134  result.ShadowVisible := ShadowVisible;
     135  result.ClearTypeRGBOrder := FontQuality <> fqFineClearTypeBGR;
     136  result.Destination := ASurface;
     137  result.OutlineColor := OutlineColor;
     138  result.OutlineVisible := OutlineVisible;
     139  result.OuterOutlineOnly := OuterOutlineOnly;
     140  result.OutlineTexture := OutlineTexture;
     141  if ShaderActive then result.Shader := FShader
     142   else result.Shader := nil;
     143end;
     144
     145function TBGRAFreeTypeFontRenderer.GetShaderLightPosition: TPoint;
     146begin
     147  if FShader = nil then
     148    result := point(0,0)
     149  else
     150    result := FShader.LightPosition;
     151end;
     152
     153procedure TBGRAFreeTypeFontRenderer.SetShaderLightPosition(AValue: TPoint);
     154begin
     155  if FShader <> nil then
     156    FShader.LightPosition := AValue;
     157end;
     158
     159procedure TBGRAFreeTypeFontRenderer.UpdateFont;
     160var fts: TFreeTypeStyles;
     161begin
     162  fts := [];
     163  if fsBold in FontStyle then fts += [ftsBold];
     164  if fsItalic in FontStyle then fts += [ftsItalic];
     165  try
     166    {$IF (lcl_fullversion>=1010000)}
     167    FFont.SetNameAndStyle(FontName,fts);
     168    {$ELSE}
     169    FFont.Name := FontName;
     170    FFont.Style := fts;
     171    {$ENDIF}
     172  except
     173    on ex: exception do
     174    begin
     175    end;
     176  end;
     177  if FontEmHeight >= 0 then
     178    FFont.SizeInPixels := FontEmHeight
     179  else
     180    FFont.LineFullHeight := -FontEmHeight;
     181  case FontQuality of
     182    fqSystem:
     183    begin
     184      FFont.Quality := grqMonochrome;
     185      FFont.ClearType := false;
     186    end;
     187    fqSystemClearType:
     188    begin
     189      FFont.Quality:= grqLowQuality;
     190      FFont.ClearType:= true;
     191    end;
     192    fqFineAntialiasing:
     193    begin
     194      FFont.Quality:= grqHighQuality;
     195      FFont.ClearType:= false;
     196    end;
     197    fqFineClearTypeRGB,fqFineClearTypeBGR:
     198    begin
     199      FFont.Quality:= grqHighQuality;
     200      FFont.ClearType:= true;
     201    end;
     202  end;
     203  FFont.Hinted := FontHinted;
     204  {$IF (lcl_fullversion>=1010000)}
     205  FFont.StrikeOutDecoration := fsStrikeOut in FontStyle;
     206  FFont.UnderlineDecoration := fsUnderline in FontStyle;
     207  {$ENDIF}
     208end;
     209
     210procedure TBGRAFreeTypeFontRenderer.Init;
     211begin
     212  ShaderActive := true;
     213
     214  FDrawer := TBGRAFreeTypeDrawer.Create(nil);
     215  FFont := TFreeTypeFont.Create;
     216  FontHinted:= True;
     217
     218  ShadowColor := BGRABlack;
     219  ShadowVisible := false;
     220  ShadowOffset := Point(5,5);
     221  ShadowRadius := 5;
     222end;
     223
     224constructor TBGRAFreeTypeFontRenderer.Create;
     225begin
     226  Init;
     227end;
     228
     229constructor TBGRAFreeTypeFontRenderer.Create(AShader: TCustomPhongShading;
     230  AShaderOwner: boolean);
     231begin
     232  Init;
     233  FShader := AShader;
     234  FShaderOwner := AShaderOwner;
     235end;
     236
     237function TBGRAFreeTypeFontRenderer.GetFontPixelMetric: TFontPixelMetric;
     238begin
     239  UpdateFont;
     240  result.Baseline := round(FFont.Ascent);
     241  result.CapLine:= round(FFont.Ascent*0.2);
     242  result.DescentLine:= round(FFont.Ascent+FFont.Descent);
     243  result.Lineheight := round(FFont.LineFullHeight);
     244  result.xLine := round(FFont.Ascent*0.45);
     245  result.Defined := True;
     246end;
     247
     248procedure TBGRAFreeTypeFontRenderer.TextOutAngle(ADest: TBGRACustomBitmap; x,
     249  y: single; orientation: integer; s: string; c: TBGRAPixel; align: TAlignment);
     250begin
     251
     252end;
     253
     254procedure TBGRAFreeTypeFontRenderer.TextOutAngle(ADest: TBGRACustomBitmap; x,
     255  y: single; orientation: integer; s: string; texture: IBGRAScanner;
     256  align: TAlignment);
     257begin
     258
     259end;
     260
     261procedure TBGRAFreeTypeFontRenderer.TextOut(ADest: TBGRACustomBitmap; x,
     262  y: single; s: string; texture: IBGRAScanner; align: TAlignment);
     263begin
     264  FDrawer.Texture := texture;
     265  TextOut(ADest,x,y,s,BGRAWhite,align);
     266  FDrawer.Texture := nil;
     267end;
     268
     269procedure TBGRAFreeTypeFontRenderer.TextOut(ADest: TBGRACustomBitmap; x,
     270  y: single; s: string; c: TBGRAPixel; align: TAlignment);
     271var
     272  ftaAlign: TFreeTypeAlignments;
     273begin
     274  UpdateFont;
     275  ftaAlign:= [ftaTop];
     276  case align of
     277  taLeftJustify: ftaAlign += [ftaLeft];
     278  taCenter: ftaAlign += [ftaCenter];
     279  taRightJustify: ftaAlign += [ftaRight];
     280  end;
     281  GetDrawer(ADest).DrawText(s,FFont,x,y,BGRAToFPColor(c),ftaAlign);
     282end;
     283
     284procedure TBGRAFreeTypeFontRenderer.TextRect(ADest: TBGRACustomBitmap;
     285  ARect: TRect; x, y: integer; s: string; style: TTextStyle; c: TBGRAPixel);
     286var align: TFreeTypeAlignments;
     287    intersectedClip,previousClip: TRect;
     288begin
     289  previousClip := ADest.ClipRect;
     290  if style.Clipping then
     291  begin
     292    intersectedClip := rect(0,0,0,0);
     293    if not IntersectRect(intersectedClip, previousClip, ARect) then exit;
     294    ADest.ClipRect := intersectedClip;
     295  end;
     296  UpdateFont;
     297  align := [];
     298  case style.Alignment of
     299  taCenter: begin ARect.Left := x; align += [ftaCenter]; end;
     300  taRightJustify: begin ARect.Left := x; align += [ftaRight]; end;
     301  else
     302    align += [ftaLeft];
     303  end;
     304  case style.Layout of
     305  {$IF (lcl_fullversion>=1010000)}
     306  tlCenter: begin ARect.Top := y; align += [ftaVerticalCenter]; end;
     307  {$ENDIF}
     308  tlBottom: begin ARect.top := y; align += [ftaBottom]; end;
     309  else align += [ftaTop];
     310  end;
     311  try
     312    {$IF (lcl_fullversion>=1010000)}
     313    if style.Wordbreak then
     314      GetDrawer(ADest).DrawTextRect(s, FFont, ARect.Left,ARect.Top,ARect.Right,ARect.Bottom,BGRAToFPColor(c),align)
     315    else
     316    {$ENDIF}
     317    begin
     318      case style.Layout of
     319      tlCenter: y := (ARect.Top+ARect.Bottom) div 2;
     320      tlBottom: y := ARect.Bottom;
     321      else
     322        y := ARect.Top;
     323      end;
     324      case style.Alignment of
     325      taLeftJustify: GetDrawer(ADest).DrawText(s,FFont,ARect.Left,y,BGRAToFPColor(c),align);
     326      taCenter: GetDrawer(ADest).DrawText(s,FFont,(ARect.Left+ARect.Right-1) div 2,y,BGRAToFPColor(c),align);
     327      taRightJustify: GetDrawer(ADest).DrawText(s,FFont,ARect.Right,y,BGRAToFPColor(c),align);
     328      end;
     329    end;
     330  finally
     331    if style.Clipping then
     332      ADest.ClipRect := previousClip;
     333  end;
     334end;
     335
     336procedure TBGRAFreeTypeFontRenderer.TextRect(ADest: TBGRACustomBitmap;
     337  ARect: TRect; x, y: integer; s: string; style: TTextStyle;
     338  texture: IBGRAScanner);
     339begin
     340  FDrawer.Texture := texture;
     341  TextRect(ADest,ARect,x,y,s,style,BGRAWhite);
     342  FDrawer.Texture := nil;
     343end;
     344
     345function TBGRAFreeTypeFontRenderer.TextSize(s: string): TSize;
     346begin
     347  result.cx := round(FFont.TextWidth(s));
     348  result.cy := round(FFont.LineFullHeight);
     349end;
     350
     351destructor TBGRAFreeTypeFontRenderer.Destroy;
     352begin
     353  FDrawer.Free;
     354  FFont.Free;
     355  if FShaderOwner then FShader.Free;
     356  inherited Destroy;
     357end;
    33358
    34359{ TBGRAFreeTypeDrawer }
     
    45370    if (y < 0) or (y >= Destination.height) or (x < 0) or (x > Destination.width-tx) then exit;
    46371
    47     c := FColor;
    48372    psrc := pbyte(data);
    49373    pdest := Destination.ScanLine[y]+x;
    50     while tx > 0 do
    51     begin
    52       DrawPixelInlineWithAlphaCheck(pdest,c,psrc^);
    53       inc(psrc);
    54       inc(pdest);
    55       dec(tx);
     374    if Texture = nil then
     375    begin
     376      c := FColor;
     377      while tx > 0 do
     378      begin
     379        DrawPixelInlineWithAlphaCheck(pdest,c,psrc^);
     380        inc(psrc);
     381        inc(pdest);
     382        dec(tx);
     383      end;
     384    end else
     385    begin
     386      Texture.ScanMoveTo(x,y);
     387      while tx > 0 do
     388      begin
     389        DrawPixelInlineWithAlphaCheck(pdest,Texture.ScanNextPixel,psrc^);
     390        inc(psrc);
     391        inc(pdest);
     392        dec(tx);
     393      end;
    56394    end;
    57395  end;
     
    95433      pdest^.blue := ((psrc+1)^ + (psrc+2)^ + (psrc+2)^) div 3;
    96434    end;
    97     BGRAFillClearTypeRGBMask(Destination,x div 3,y,FMask,FColor,nil,ClearTypeRGBOrder);
    98   end;
     435    BGRAFillClearTypeRGBMask(Destination,x div 3,y,FMask,FColor,Texture,ClearTypeRGBOrder);
     436  end;
     437end;
     438
     439function TBGRAFreeTypeDrawer.ShadowActuallyVisible: boolean;
     440begin
     441  result := ShadowVisible and (ShadowColor.alpha <> 0);
     442end;
     443
     444function TBGRAFreeTypeDrawer.OutlineActuallyVisible: boolean;
     445begin
     446  result := ((OutlineTexture <> nil) or (OutlineColor.alpha <> 0)) and OutlineVisible;
     447end;
     448
     449function TBGRAFreeTypeDrawer.ShaderActuallyActive: boolean;
     450begin
     451  result := (Shader <> nil) and ShaderActive;
    99452end;
    100453
     
    103456  Destination := ADestination;
    104457  ClearTypeRGBOrder:= true;
     458  ShaderActive := true;
    105459end;
    106460
    107461procedure TBGRAFreeTypeDrawer.DrawText(AText: string;
    108462  AFont: TFreeTypeRenderableFont; x, y: single; AColor: TFPColor);
    109 begin
    110   FColor := FPColorToBGRA(AColor);
    111   if AFont.ClearType then
    112     AFont.RenderText(AText, x, y, Destination.ClipRect, @RenderDirectlyClearType)
    113   else
    114     AFont.RenderText(AText, x, y, Destination.ClipRect, @RenderDirectly);
     463var fx: TBGRATextEffect;
     464  procedure DoOutline;
     465  begin
     466    if OutlineActuallyVisible then
     467    begin
     468      if OutlineTexture <> nil then
     469        fx.DrawOutline(Destination,round(x),round(y), OutlineTexture)
     470      else
     471        fx.DrawOutline(Destination,round(x),round(y), OutlineColor);
     472    end;
     473  end;
     474begin
     475  if not FInCreateTextEffect and (ShadowActuallyVisible or OutlineActuallyVisible or ShaderActuallyActive) then
     476  begin
     477    fx := CreateTextEffect(AText, AFont);
     478    y -= AFont.Ascent;
     479    if ShadowActuallyVisible then fx.DrawShadow(Destination, round(x+ShadowOffset.X),round(y+ShadowOffset.Y), ShadowRadius, ShadowColor);
     480    if OuterOutlineOnly then DoOutline;
     481
     482    if texture <> nil then
     483    begin
     484      if ShaderActuallyActive then
     485        fx.DrawShaded(Destination,floor(x),floor(y), Shader, round(fx.TextSize.cy*0.05), texture)
     486      else
     487        fx.Draw(Destination,round(x),round(y), texture);
     488    end else
     489    begin
     490      if ShaderActuallyActive then
     491        fx.DrawShaded(Destination,floor(x),floor(y), Shader, round(fx.TextSize.cy*0.05), FPColorToBGRA(AColor))
     492      else
     493        fx.Draw(Destination,round(x),round(y), FPColorToBGRA(AColor));
     494    end;
     495    if not OuterOutlineOnly then DoOutline;
     496    fx.Free;
     497  end else
     498  begin
     499    FColor := FPColorToBGRA(AColor);
     500    if AFont.ClearType then
     501      AFont.RenderText(AText, x, y, Destination.ClipRect, @RenderDirectlyClearType)
     502    else
     503      AFont.RenderText(AText, x, y, Destination.ClipRect, @RenderDirectly);
     504  end;
    115505end;
    116506
     
    128518end;
    129519
     520function TBGRAFreeTypeDrawer.CreateTextEffect(AText: string;
     521  AFont: TFreeTypeRenderableFont): TBGRATextEffect;
     522var
     523  mask: TBGRACustomBitmap;
     524  tx,ty,marginHoriz,marginVert: integer;
     525  tempDest: TBGRACustomBitmap;
     526  tempTex: IBGRAScanner;
     527  tempClearType: boolean;
     528begin
     529  FInCreateTextEffect:= True;
     530  try
     531    tx := ceil(AFont.TextWidth(AText));
     532    ty := ceil(AFont.TextHeight(AText));
     533    marginHoriz := ty div 2;
     534    marginVert := 1;
     535    mask := BGRABitmapFactory.Create(tx+2*marginHoriz,ty+2*marginVert,BGRABlack);
     536    tempDest := Destination;
     537    tempTex := Texture;
     538    tempClearType:= AFont.ClearType;
     539    Destination := mask;
     540    Texture := nil;
     541    AFont.ClearType := false;
     542    DrawText(AText,AFont,marginHoriz,marginVert,BGRAWhite,[ftaTop,ftaLeft]);
     543    Destination := tempDest;
     544    Texture := tempTex;
     545    AFont.ClearType := tempClearType;
     546    mask.ConvertToLinearRGB;
     547    result := TBGRATextEffect.Create(mask, true,tx,ty,point(-marginHoriz,-marginVert));
     548  finally
     549    FInCreateTextEffect:= false;
     550  end;
     551end;
     552
    130553destructor TBGRAFreeTypeDrawer.Destroy;
    131554begin
  • GraphicTest/Packages/bgrabitmap/bgragradients.pas

    r452 r472  
    22
    33{$mode objfpc}{$H+}
     4
     5{$i bgrasse.inc}
    46
    57interface
     
    115117                   Color : TBGRAPixel);
    116118
    117     {$ifdef CPUI386}
     119    {$ifdef BGRASSE_AVAILABLE}
    118120    procedure DrawMapSSE(dest: TBGRACustomBitmap; map: TBGRACustomBitmap; mapAltitude: integer; ofsX,ofsY: integer;
    119121                   ColorMap : TBGRACustomBitmap);
     
    174176implementation
    175177
    176 uses Types, SysUtils, BGRATextFX;
     178uses GraphType, Types, SysUtils, BGRATextFX; {GraphType unit used by phongdraw.inc}
    177179
    178180function TextShadow(AWidth, AHeight: Integer; AText: String;
     
    375377                             Color : TBGRAPixel);
    376378begin
    377   {$ifdef CPUI386}
     379  {$ifdef BGRASSE_AVAILABLE}
    378380    if UseSSE then
    379381      DrawColorSSE(dest,map,mapAltitude,ofsX,ofsY,Color)
     
    386388            mapAltitude: integer; ofsX, ofsY: integer; ColorMap: TBGRACustomBitmap);
    387389begin
    388   {$ifdef CPUI386}
     390  {$ifdef BGRASSE_AVAILABLE}
    389391    if UseSSE then
    390392      DrawMapSSE(dest,map,mapAltitude,ofsX,ofsY,ColorMap)
     
    397399  mapAltitude: integer; ofsX, ofsY: integer; ColorScan: IBGRAScanner);
    398400begin
    399   {$ifdef CPUI386}
     401  {$ifdef BGRASSE_AVAILABLE}
    400402    if UseSSE then
    401403      DrawScannerSSE(dest,map,mapAltitude,ofsX,ofsY,ColorScan)
     
    575577  {$I phongdraw.inc }
    576578
    577 {$ifdef CPUI386}
     579{$ifdef BGRASSE_AVAILABLE}
    578580procedure TPhongShading.DrawMapSSE(dest: TBGRACustomBitmap; map: TBGRACustomBitmap;
    579581  mapAltitude: integer; ofsX, ofsY: integer; ColorMap: TBGRACustomBitmap);
     
    10871089end;
    10881090
     1091initialization
     1092
     1093  Randomize;
     1094
    10891095end.
    10901096
  • GraphicTest/Packages/bgrabitmap/bgragradientscanner.pas

    r452 r472  
    108108  end;
    109109
     110  { TBGRAConstantScanner }
     111
     112  TBGRAConstantScanner = class(TBGRAGradientScanner)
     113    constructor Create(c: TBGRAPixel);
     114  end;
     115
     116  { TBGRARandomScanner }
     117
     118  TBGRARandomScanner = class(TBGRACustomScanner)
     119  private
     120    FOpacity: byte;
     121    FGrayscale: boolean;
     122  public
     123    constructor Create(AGrayscale: Boolean; AOpacity: byte);
     124    function ScanAtInteger({%H-}X, {%H-}Y: integer): TBGRAPixel; override;
     125    function ScanNextPixel: TBGRAPixel; override;
     126    function ScanAt({%H-}X, {%H-}Y: Single): TBGRAPixel; override;
     127  end;
     128
    110129  { TBGRAGradientTriangleScanner }
    111130
     
    184203
    185204uses BGRABlend;
     205
     206{ TBGRAConstantScanner }
     207
     208constructor TBGRAConstantScanner.Create(c: TBGRAPixel);
     209begin
     210  inherited Create(c,c,gtLinear,PointF(0,0),PointF(0,0),false);
     211end;
     212
     213{ TBGRARandomScanner }
     214
     215constructor TBGRARandomScanner.Create(AGrayscale: Boolean; AOpacity: byte);
     216begin
     217  FGrayscale:= AGrayscale;
     218  FOpacity:= AOpacity;
     219end;
     220
     221function TBGRARandomScanner.ScanAtInteger(X, Y: integer): TBGRAPixel;
     222begin
     223  Result:=ScanNextPixel;
     224end;
     225
     226function TBGRARandomScanner.ScanNextPixel: TBGRAPixel;
     227begin
     228  if FGrayscale then
     229  begin
     230    result.red := random(256);
     231    result.green := result.red;
     232    result.blue := result.red;
     233    result.alpha:= FOpacity;
     234  end else
     235    Result:= BGRA(random(256),random(256),random(256),FOpacity);
     236end;
     237
     238function TBGRARandomScanner.ScanAt(X, Y: Single): TBGRAPixel;
     239begin
     240  Result:=ScanNextPixel;
     241end;
    186242
    187243{ TBGRAHueGradient }
     
    758814  InitScanInline(X,Y);
    759815  if FVertical then
    760     FHorizColor := ScanAt(X,Y);
     816    FHorizColor := ScanNextInline;
    761817end;
    762818
     
    12471303end;
    12481304
     1305initialization
     1306
     1307  Randomize;
     1308
    12491309end.
    12501310
  • GraphicTest/Packages/bgrabitmap/bgragtkbitmap.pas

    r452 r472  
    6565  gdk, gtkdef, gtkProc, gdkpixbuf, glib,
    6666  {$ENDIF}
    67   FPImage;
     67  FPImage, Dialogs;
    6868
    6969{$IFDEF LCLgtk2}
     
    141141  end;
    142142
    143   //SwapRedBlue;
     143  SwapRedBlue;
    144144 
    145145  P := Rect.TopLeft;
    146   DpToLP(ACanvas.Handle, P, 1);
     146  LPToDP(ACanvas.Handle, P, 1);
    147147  gdk_pixbuf_render_to_drawable(FPixBuf,
    148148    TGtkDeviceContext(ACanvas.Handle).Drawable,
    149149    TGtkDeviceContext(ACanvas.Handle).GC,
    150     0,0,
    151     TGtkDeviceContext(ACanvas.Handle).Offset.X + P.X,
    152     TGtkDeviceContext(ACanvas.Handle).Offset.Y + P.Y,
     150    0,0, P.X,P.Y,
    153151    Width,Height,
    154152    GDK_RGB_DITHER_NORMAL,0,0);   
    155153
    156   //SwapRedBlue;
     154  SwapRedBlue;
    157155end;
    158156
     
    251249
    252250  dest := ACanvas.Handle;
    253   pos := TGtkDeviceContext(dest).Offset;
    254   pos.X += rect.Left;
    255   pos.Y += rect.Top;
     251  pos := rect.TopLeft;
     252  LPtoDP(dest, pos, 1);
    256253  If ALineOrder = riloBottomToTop then VerticalFlip;
    257   //SwapRedBlue;
     254  SwapRedBlue;
    258255  gdk_draw_rgb_32_image(TGtkDeviceContext(dest).Drawable,
    259     TGtkDeviceContext(Dest).GC, pos.X,pos.Y,
     256    TGtkDeviceContext(Dest).GC, pos.x,pos.y,
    260257    AWidth,AHeight, GDK_RGB_DITHER_NORMAL,
    261258    AData, AWidth*sizeof(TBGRAPixel));
    262   //SwapRedBlue;
     259  SwapRedBlue;
    263260  If ALineOrder = riloBottomToTop then VerticalFlip;
    264261end;
     
    296293
    297294  P := Point(x,y);
    298   DpToLP(CanvasSource.Handle, P, 1);
     295  LPToDP(CanvasSource.Handle, P, 1);
    299296  gdk_pixbuf_get_from_drawable(FPixBuf,
    300297    TGtkDeviceContext(CanvasSource.Handle).Drawable,
    301     nil,
    302     TGtkDeviceContext(CanvasSource.Handle).Offset.X+P.X,
    303     TGtkDeviceContext(CanvasSource.Handle).Offset.Y+P.Y,0,0,Width,Height);
     298    nil, P.X,P.Y,0,0,Width,Height);
    304299  SwapRedBlue;
    305300  InvalidateBitmap;
  • GraphicTest/Packages/bgrabitmap/bgralayers.pas

    r452 r472  
    1414  TBGRALayeredBitmap = class;
    1515  TBGRALayeredBitmapClass = class of TBGRALayeredBitmap;
     16
     17  TBGRALayeredBitmapSaveToStreamProc = procedure(AStream: TStream; ALayers: TBGRACustomLayeredBitmap);
     18  TBGRALayeredBitmapLoadFromStreamProc = function(AStream: TStream): TBGRALayeredBitmap;
    1619
    1720  { TBGRACustomLayeredBitmap }
     
    3639    function GetLayerName(layer: integer): string; virtual;
    3740    function GetLayerOffset(layer: integer): TPoint; virtual;
    38     function GetLayerBitmapDirectly(layer: integer): TBGRABitmap; virtual;
    3941    function GetLayerFrozenRange(layer: integer): integer;
    4042    function GetLayerFrozen(layer: integer): boolean; virtual;
     43    function GetLayerUniqueId(layer: integer): integer; virtual;
    4144    procedure SetLayerFrozen(layer: integer; AValue: boolean); virtual;
    4245    function RangeIntersect(first1,last1,first2,last2: integer): boolean;
     
    5053
    5154  public
    52     procedure SaveToFile(const filename: string); override;
     55    procedure SaveToFile(const filenameUTF8: string); override;
    5356    procedure SaveToStream(Stream: TStream); override;
    5457    constructor Create; override;
    5558    destructor Destroy; override;
    5659    function ToString: ansistring; override;
     60    function GetLayerBitmapDirectly(layer: integer): TBGRABitmap; virtual;
    5761    function GetLayerBitmapCopy(layer: integer): TBGRABitmap; virtual; abstract;
    5862    function ComputeFlatImage: TBGRABitmap; overload;
     
    8084    property LayerOffset[layer: integer]: TPoint read GetLayerOffset;
    8185    property LayerFrozen[layer: integer]: boolean read GetLayerFrozen;
     86    property LayerUniqueId[layer: integer]: integer read GetLayerUniqueId;
    8287    property LinearBlend: boolean read GetLinearBlend write SetLinearBlend; //use linear blending unless specified
    8388    property DefaultBlendingOperation: TBlendOperation read GetDefaultBlendingOperation;
     
    103108    FLayers: array of TBGRALayerInfo;
    104109    FWidth,FHeight: integer;
    105     function GetLayerUniqueId(layer: integer): integer;
    106     procedure SetLayerUniqueId(layer: integer; AValue: integer);
    107110
    108111  protected
     
    122125    procedure SetLayerName(layer: integer; AValue: string);
    123126    procedure SetLayerFrozen(layer: integer; AValue: boolean); override;
    124     function GetLayerBitmapDirectly(layer: integer): TBGRABitmap; override;
     127    function GetLayerUniqueId(layer: integer): integer; override;
     128    procedure SetLayerUniqueId(layer: integer; AValue: integer);
    125129
    126130  public
    127     procedure LoadFromFile(const filename: string); override;
     131    procedure LoadFromFile(const filenameUTF8: string); override;
    128132    procedure LoadFromStream(stream: TStream); override;
    129133    procedure SetSize(AWidth, AHeight: integer); virtual;
     
    155159    function AddOwnedLayer(ABitmap: TBGRABitmap; BlendOp: TBlendOperation; Opacity: byte = 255): integer; overload;
    156160    destructor Destroy; override;
    157     constructor Create; override;
    158     constructor Create(AWidth, AHeight: integer);
     161    constructor Create; override; overload;
     162    constructor Create(AWidth, AHeight: integer); virtual; overload;
     163    function GetLayerBitmapDirectly(layer: integer): TBGRABitmap; override;
    159164    function GetLayerBitmapCopy(layer: integer): TBGRABitmap; override;
    160165    function GetLayerIndexFromId(AIdentifier: integer): integer;
    161166    function Duplicate(ASharedLayerIds: boolean = false): TBGRALayeredBitmap;
     167    function ProduceLayerUniqueId: integer;
    162168
    163169    procedure RotateCW;
     
    180186  end;
    181187
    182 procedure RegisterLayeredBitmapWriter(AExtension: string; AWriter: TBGRALayeredBitmapClass);
    183 procedure RegisterLayeredBitmapReader(AExtension: string; AReader: TBGRACustomLayeredBitmapClass);
     188procedure RegisterLayeredBitmapWriter(AExtensionUTF8: string; AWriter: TBGRALayeredBitmapClass);
     189procedure RegisterLayeredBitmapReader(AExtensionUTF8: string; AReader: TBGRACustomLayeredBitmapClass);
     190
     191var
     192  LayeredBitmapSaveToStreamProc : TBGRALayeredBitmapSaveToStreamProc;
     193  LayeredBitmapLoadFromStreamProc : TBGRALayeredBitmapLoadFromStreamProc;
     194
     195type
     196  TOnLayeredBitmapLoadStartProc = procedure(AFilenameUTF8: string) of object;
     197  TOnLayeredBitmapLoadProgressProc = procedure(APercentage: integer) of object;
     198  TOnLayeredBitmapLoadedProc = procedure() of object;
     199
     200procedure OnLayeredBitmapLoadFromStreamStart;
     201procedure OnLayeredBitmapLoadStart(AFilenameUTF8: string);
     202procedure OnLayeredBitmapLoadProgress(APercentage: integer);
     203procedure OnLayeredBitmapLoaded();
     204procedure RegisterLoadingHandler(AStart: TOnLayeredBitmapLoadStartProc; AProgress: TOnLayeredBitmapLoadProgressProc;
     205     ADone: TOnLayeredBitmapLoadedProc);
     206procedure UnregisterLoadingHandler(AStart: TOnLayeredBitmapLoadStartProc; AProgress: TOnLayeredBitmapLoadProgressProc;
     207     ADone: TOnLayeredBitmapLoadedProc);
    184208
    185209implementation
     210
     211uses LCLProc;
     212
     213var
     214  OnLayeredBitmapLoadStartProc: TOnLayeredBitmapLoadStartProc;
     215  OnLayeredBitmapLoadProgressProc: TOnLayeredBitmapLoadProgressProc;
     216  OnLayeredBitmapLoadedProc: TOnLayeredBitmapLoadedProc;
    186217
    187218var
     
    380411end;
    381412
    382 procedure TBGRALayeredBitmap.LoadFromFile(const filename: string);
     413procedure TBGRALayeredBitmap.LoadFromFile(const filenameUTF8: string);
    383414var bmp: TBGRABitmap;
    384415    index: integer;
     
    387418    i: integer;
    388419begin
    389   ext := lowercase(ExtractFileExt(filename));
     420  ext := UTF8LowerCase(ExtractFileExt(filenameUTF8));
    390421  for i := 0 to high(LayeredBitmapReaders) do
    391422    if '.'+LayeredBitmapReaders[i].extension = ext then
     
    393424      temp := LayeredBitmapReaders[i].theClass.Create;
    394425      try
    395         temp.LoadFromFile(filename);
     426        temp.LoadFromFile(filenameUTF8);
    396427        Assign(temp);
    397428      finally
     
    401432    end;
    402433
    403   bmp := TBGRABitmap.Create(filename);
     434  bmp := TBGRABitmap.Create(filenameUTF8, True);
    404435  Clear;
    405436  SetSize(bmp.Width,bmp.Height);
     
    411442var bmp: TBGRABitmap;
    412443   index: integer;
    413 begin
     444   temp: TBGRALayeredBitmap;
     445begin
     446  if Assigned(LayeredBitmapLoadFromStreamProc) then
     447  begin
     448    temp := LayeredBitmapLoadFromStreamProc(Stream);
     449    if temp <> nil then
     450    begin
     451      Assign(temp);
     452      temp.Free;
     453      exit;
     454    end;
     455  end;
    414456  bmp := TBGRABitmap.Create(stream);
    415457  Clear;
     
    538580  FLayers[FNbLayers].Visible := true;
    539581  FLayers[FNbLayers].Frozen := false;
    540   FLayers[FNbLayers].UniqueId := InterLockedIncrement(NextLayerUniqueId);
     582  FLayers[FNbLayers].UniqueId := ProduceLayerUniqueId;
    541583  if Shared then
    542584  begin
     
    694736  result := TBGRALayeredBitmap.Create;
    695737  result.Assign(self, ASharedLayerIds);
     738end;
     739
     740function TBGRALayeredBitmap.ProduceLayerUniqueId: integer;
     741begin
     742  result := InterLockedIncrement(NextLayerUniqueId);
    696743end;
    697744
     
    841888    end;
    842889  result := false;
     890end;
     891
     892function TBGRACustomLayeredBitmap.GetLayerUniqueId(layer: integer): integer;
     893begin
     894  result := layer;
    843895end;
    844896
     
    903955end;
    904956
    905 procedure TBGRACustomLayeredBitmap.SaveToFile(const filename: string);
     957procedure TBGRACustomLayeredBitmap.SaveToFile(const filenameUTF8: string);
    906958var bmp: TBGRABitmap;
    907959    ext: string;
     
    909961    i: integer;
    910962begin
    911   ext := lowercase(ExtractFileExt(filename));
     963  ext := UTF8LowerCase(ExtractFileExt(filenameUTF8));
    912964  for i := 0 to high(LayeredBitmapWriters) do
    913965    if '.'+LayeredBitmapWriters[i].extension = ext then
     
    916968      try
    917969        temp.Assign(self);
    918         temp.SaveToFile(filename);
     970        temp.SaveToFile(filenameUTF8);
    919971      finally
    920972        temp.Free;
     
    925977  bmp := ComputeFlatImage;
    926978  try
    927     bmp.SaveToFile(filename);
     979    bmp.SaveToFileUTF8(filenameUTF8);
    928980  finally
    929981    bmp.Free;
     
    933985procedure TBGRACustomLayeredBitmap.SaveToStream(Stream: TStream);
    934986begin
    935   raise exception.Create('Not implemented');
     987  if Assigned(LayeredBitmapSaveToStreamProc) then
     988    LayeredBitmapSaveToStreamProc(Stream, self)
     989  else
     990    raise exception.Create('Call BGRAStreamLayers.RegisterStreamLayers first');
    936991end;
    937992
     
    11881243  linear := false; //to avoid hint
    11891244  for j := firstlayer to lastLayer do
    1190   if (BlendOperation[j] in [boTransparent,boLinearBlend]) or (start= 0) then
     1245  if (BlendOperation[j] in [boTransparent,boLinearBlend]) or (start = 0) or ((firstlayer= 0) and (j=0)) then
    11911246  begin
    11921247    nextLinear := (BlendOperation[j] = boLinearBlend) or self.LinearBlend;
     
    12391294end;
    12401295
    1241 procedure RegisterLayeredBitmapReader(AExtension: string; AReader: TBGRACustomLayeredBitmapClass);
     1296procedure RegisterLayeredBitmapReader(AExtensionUTF8: string; AReader: TBGRACustomLayeredBitmapClass);
    12421297begin
    12431298  setlength(LayeredBitmapReaders,length(LayeredBitmapReaders)+1);
    12441299  with LayeredBitmapReaders[high(LayeredBitmapReaders)] do
    12451300  begin
    1246     extension:= AExtension;
     1301    extension:= UTF8LowerCase(AExtensionUTF8);
    12471302    theClass := AReader;
    12481303  end;
    12491304end;
    12501305
    1251 procedure RegisterLayeredBitmapWriter(AExtension: string; AWriter: TBGRALayeredBitmapClass);
    1252 begin
     1306procedure OnLayeredBitmapLoadFromStreamStart;
     1307begin
     1308  OnLayeredBitmapLoadStart('<Stream>');
     1309end;
     1310
     1311procedure OnLayeredBitmapLoadStart(AFilenameUTF8: string);
     1312begin
     1313  if Assigned(OnLayeredBitmapLoadStartProc) then
     1314    OnLayeredBitmapLoadStartProc(AFilenameUTF8);
     1315end;
     1316
     1317procedure OnLayeredBitmapLoadProgress(APercentage: integer);
     1318begin
     1319  if Assigned(OnLayeredBitmapLoadProgressProc) then
     1320    OnLayeredBitmapLoadProgressProc(APercentage);
     1321end;
     1322
     1323procedure OnLayeredBitmapLoaded;
     1324begin
     1325  if Assigned(OnLayeredBitmapLoadedProc) then
     1326    OnLayeredBitmapLoadedProc();
     1327end;
     1328
     1329procedure RegisterLoadingHandler(AStart: TOnLayeredBitmapLoadStartProc;
     1330  AProgress: TOnLayeredBitmapLoadProgressProc; ADone: TOnLayeredBitmapLoadedProc
     1331  );
     1332begin
     1333  OnLayeredBitmapLoadProgressProc:= AProgress;
     1334  OnLayeredBitmapLoadStartProc := AStart;
     1335  OnLayeredBitmapLoadedProc:= ADone;
     1336end;
     1337
     1338procedure UnregisterLoadingHandler(AStart: TOnLayeredBitmapLoadStartProc;
     1339  AProgress: TOnLayeredBitmapLoadProgressProc; ADone: TOnLayeredBitmapLoadedProc);
     1340begin
     1341  if OnLayeredBitmapLoadProgressProc = AProgress then OnLayeredBitmapLoadProgressProc := nil;
     1342  if OnLayeredBitmapLoadStartProc = AStart then OnLayeredBitmapLoadStartProc := nil;
     1343  if OnLayeredBitmapLoadedProc = ADone then OnLayeredBitmapLoadedProc := nil;
     1344end;
     1345
     1346procedure RegisterLayeredBitmapWriter(AExtensionUTF8: string; AWriter: TBGRALayeredBitmapClass);
     1347begin
     1348  while (length(AExtensionUTF8)>0) and (AExtensionUTF8[1]='.') do delete(AExtensionUTF8,1,1);
    12531349  setlength(LayeredBitmapWriters,length(LayeredBitmapWriters)+1);
    12541350  with LayeredBitmapWriters[high(LayeredBitmapWriters)] do
    12551351  begin
    1256     extension:= AExtension;
     1352    extension:= UTF8LowerCase(AExtensionUTF8);
    12571353    theClass := AWriter;
    12581354  end;
  • GraphicTest/Packages/bgrabitmap/bgramatrix3d.pas

    r452 r472  
    33{$mode objfpc}{$H+}
    44
    5 {$ifdef CPUI386}
     5{$i bgrasse.inc}
     6{$ifdef BGRASSE_AVAILABLE}
    67  {$asmmode intel}
    78{$endif}
     
    1415type
    1516  TMatrix3D = packed array[1..3,1..4] of single;
     17  TProjection3D = packed record
     18    Zoom, Center: TPointF;
     19  end;
    1620
    1721operator*(const A: TMatrix3D; const M: TPoint3D): TPoint3D;
    18 operator*(const A: TMatrix3D; var M: TPoint3D_128): TPoint3D_128;
     22operator*(constref A: TMatrix3D; var M: TPoint3D_128): TPoint3D_128;
     23function MultiplyVect3DWithoutTranslation(constref A: TMatrix3D; constref M: TPoint3D_128): TPoint3D_128;
    1924operator*(A,B: TMatrix3D): TMatrix3D;
    2025
     
    3035function MatrixRotateZ(angle: single): TMatrix3D;
    3136
    32 {$IFDEF CPUI386}
     37{$IFDEF BGRASSE_AVAILABLE}
    3338procedure Matrix3D_SSE_Load(const A: TMatrix3D);
    3439procedure MatrixMultiplyVect3D_SSE_Aligned(var M: TPoint3D_128; out N: TPoint3D_128);
    3540procedure MatrixMultiplyVect3D_SSE3_Aligned(var M: TPoint3D_128; out N: TPoint3D_128);
     41procedure MatrixMultiplyVect3DWithoutTranslation_SSE_Aligned(var M: TPoint3D_128; out N: TPoint3D_128);
     42procedure MatrixMultiplyVect3DWithoutTranslation_SSE3_Aligned(var M: TPoint3D_128; out N: TPoint3D_128);
    3643{$ENDIF}
    3744
     
    5259end;
    5360
    54 {$IFDEF CPUI386}
     61{$IFDEF BGRASSE_AVAILABLE}
    5562var SingleConst1 : single = 1;
    5663
    57 procedure Matrix3D_SSE_Load(const A: TMatrix3D);
    58 begin
    59   asm
    60     mov eax, A
    61     movups xmm5, [eax]
    62     movups xmm6, [eax+16]
    63     movups xmm7, [eax+32]
    64   end;
    65 end;
     64  procedure Matrix3D_SSE_Load(const A: TMatrix3D);
     65  begin
     66    {$IFDEF cpux86_64}
     67    asm
     68      mov rax, A
     69      movups xmm5, [rax]
     70      movups xmm6, [rax+16]
     71      movups xmm7, [rax+32]
     72    end;
     73    {$ELSE}
     74    asm
     75      mov eax, A
     76      movups xmm5, [eax]
     77      movups xmm6, [eax+16]
     78      movups xmm7, [eax+32]
     79    end;
     80   {$ENDIF}
     81  end;
    6682
    6783procedure MatrixMultiplyVect3D_SSE_Aligned(var M: TPoint3D_128; out N: TPoint3D_128);
     
    7086  oldMt := M.t;
    7187  M.t := SingleConst1;
     88  {$IFDEF cpux86_64}
     89  asm
     90    mov rax, M
     91    movaps xmm0, [rax]
     92
     93    mov rax, N
     94
     95    movaps xmm2,xmm0
     96    mulps xmm2,xmm5
     97    //mix1
     98    movaps xmm3, xmm2
     99    shufps xmm3, xmm3, $4e
     100    addps xmm2, xmm3
     101    //mix2
     102    movaps xmm3, xmm2
     103    shufps xmm3, xmm3, $11
     104    addps xmm2, xmm3
     105
     106    movss [rax], xmm2
     107
     108    movaps xmm2,xmm0
     109    mulps xmm2,xmm6
     110    //mix1
     111    movaps xmm3, xmm2
     112    shufps xmm3, xmm3, $4e
     113    addps xmm2, xmm3
     114    //mix2
     115    movaps xmm3, xmm2
     116    shufps xmm3, xmm3, $11
     117    addps xmm2, xmm3
     118
     119    movss [rax+4], xmm2
     120
     121    mulps xmm0,xmm7
     122    //mix1
     123    movaps xmm3, xmm0
     124    shufps xmm3, xmm3, $4e
     125    addps xmm0, xmm3
     126    //mix2
     127    movaps xmm3, xmm0
     128    shufps xmm3, xmm3, $11
     129    addps xmm0, xmm3
     130
     131    movss [rax+8], xmm0
     132  end;
     133  {$ELSE}
     134    asm
     135    mov eax, M
     136    movaps xmm0, [eax]
     137
     138    mov eax, N
     139
     140    movaps xmm2,xmm0
     141    mulps xmm2,xmm5
     142    //mix1
     143    movaps xmm3, xmm2
     144    shufps xmm3, xmm3, $4e
     145    addps xmm2, xmm3
     146    //mix2
     147    movaps xmm3, xmm2
     148    shufps xmm3, xmm3, $11
     149    addps xmm2, xmm3
     150
     151    movss [eax], xmm2
     152
     153    movaps xmm2,xmm0
     154    mulps xmm2,xmm6
     155    //mix1
     156    movaps xmm3, xmm2
     157    shufps xmm3, xmm3, $4e
     158    addps xmm2, xmm3
     159    //mix2
     160    movaps xmm3, xmm2
     161    shufps xmm3, xmm3, $11
     162    addps xmm2, xmm3
     163
     164    movss [eax+4], xmm2
     165
     166    mulps xmm0,xmm7
     167    //mix1
     168    movaps xmm3, xmm0
     169    shufps xmm3, xmm3, $4e
     170    addps xmm0, xmm3
     171    //mix2
     172    movaps xmm3, xmm0
     173    shufps xmm3, xmm3, $11
     174    addps xmm0, xmm3
     175
     176    movss [eax+8], xmm0
     177  end;
     178  {$ENDIF}
     179  M.t := oldMt;
     180  N.t := 0;
     181end;
     182
     183procedure MatrixMultiplyVect3D_SSE3_Aligned(var M: TPoint3D_128; out N: TPoint3D_128);
     184var oldMt: single;
     185begin
     186  oldMt := M.t;
     187  M.t := SingleConst1;
     188  {$IFDEF cpux86_64}
     189  asm
     190    mov rax, M
     191    movaps xmm0, [rax]
     192
     193    mov rax, N
     194
     195    movaps xmm2,xmm0
     196    mulps xmm2,xmm5
     197    haddps xmm2,xmm2
     198    haddps xmm2,xmm2
     199    movss [rax], xmm2
     200
     201    movaps xmm2,xmm0
     202    mulps xmm2,xmm6
     203    haddps xmm2,xmm2
     204    haddps xmm2,xmm2
     205    movss [rax+4], xmm2
     206
     207    mulps xmm0,xmm7
     208    haddps xmm0,xmm0
     209    haddps xmm0,xmm0
     210    movss [rax+8], xmm0
     211  end;
     212  {$ELSE}
    72213  asm
    73214    mov eax, M
     
    78219    movaps xmm2,xmm0
    79220    mulps xmm2,xmm5
    80     //mix1
    81     movaps xmm3, xmm2
    82     shufps xmm3, xmm3, $4e
    83     addps xmm2, xmm3
    84     //mix2
    85     movaps xmm3, xmm2
    86     shufps xmm3, xmm3, $11
    87     addps xmm2, xmm3
    88 
     221    haddps xmm2,xmm2
     222    haddps xmm2,xmm2
    89223    movss [eax], xmm2
    90224
    91225    movaps xmm2,xmm0
    92226    mulps xmm2,xmm6
    93     //mix1
    94     movaps xmm3, xmm2
    95     shufps xmm3, xmm3, $4e
    96     addps xmm2, xmm3
    97     //mix2
    98     movaps xmm3, xmm2
    99     shufps xmm3, xmm3, $11
    100     addps xmm2, xmm3
    101 
     227    haddps xmm2,xmm2
     228    haddps xmm2,xmm2
    102229    movss [eax+4], xmm2
    103230
    104231    mulps xmm0,xmm7
    105     //mix1
    106     movaps xmm3, xmm0
    107     shufps xmm3, xmm3, $4e
    108     addps xmm0, xmm3
    109     //mix2
    110     movaps xmm3, xmm0
    111     shufps xmm3, xmm3, $11
    112     addps xmm0, xmm3
    113 
     232    haddps xmm0,xmm0
     233    haddps xmm0,xmm0
    114234    movss [eax+8], xmm0
    115235  end;
     236  {$ENDIF}
    116237  M.t := oldMt;
    117   N.t := 0;
    118 end;
    119 
    120 procedure MatrixMultiplyVect3D_SSE3_Aligned(var M: TPoint3D_128; out N: TPoint3D_128);
    121 var oldMt: single;
    122 begin
    123   oldMt := M.t;
    124   M.t := SingleConst1;
     238end;
     239
     240procedure MatrixMultiplyVect3DWithoutTranslation_SSE_Aligned(
     241  var M: TPoint3D_128; out N: TPoint3D_128);
     242begin
     243  {$IFDEF cpux86_64}
     244  asm
     245    mov rax, M
     246    movaps xmm0, [rax]
     247
     248    mov rax, N
     249
     250    movaps xmm2,xmm0
     251    mulps xmm2,xmm5
     252    //mix1
     253    movaps xmm3, xmm2
     254    shufps xmm3, xmm3, $4e
     255    addps xmm2, xmm3
     256    //mix2
     257    movaps xmm3, xmm2
     258    shufps xmm3, xmm3, $11
     259    addps xmm2, xmm3
     260
     261    movss [rax], xmm2
     262
     263    movaps xmm2,xmm0
     264    mulps xmm2,xmm6
     265    //mix1
     266    movaps xmm3, xmm2
     267    shufps xmm3, xmm3, $4e
     268    addps xmm2, xmm3
     269    //mix2
     270    movaps xmm3, xmm2
     271    shufps xmm3, xmm3, $11
     272    addps xmm2, xmm3
     273
     274    movss [rax+4], xmm2
     275
     276    mulps xmm0,xmm7
     277    //mix1
     278    movaps xmm3, xmm0
     279    shufps xmm3, xmm3, $4e
     280    addps xmm0, xmm3
     281    //mix2
     282    movaps xmm3, xmm0
     283    shufps xmm3, xmm3, $11
     284    addps xmm0, xmm3
     285
     286    movss [rax+8], xmm0
     287  end;
     288  {$ELSE}
     289    asm
     290    mov eax, M
     291    movaps xmm0, [eax]
     292
     293    mov eax, N
     294
     295    movaps xmm2,xmm0
     296    mulps xmm2,xmm5
     297    //mix1
     298    movaps xmm3, xmm2
     299    shufps xmm3, xmm3, $4e
     300    addps xmm2, xmm3
     301    //mix2
     302    movaps xmm3, xmm2
     303    shufps xmm3, xmm3, $11
     304    addps xmm2, xmm3
     305
     306    movss [eax], xmm2
     307
     308    movaps xmm2,xmm0
     309    mulps xmm2,xmm6
     310    //mix1
     311    movaps xmm3, xmm2
     312    shufps xmm3, xmm3, $4e
     313    addps xmm2, xmm3
     314    //mix2
     315    movaps xmm3, xmm2
     316    shufps xmm3, xmm3, $11
     317    addps xmm2, xmm3
     318
     319    movss [eax+4], xmm2
     320
     321    mulps xmm0,xmm7
     322    //mix1
     323    movaps xmm3, xmm0
     324    shufps xmm3, xmm3, $4e
     325    addps xmm0, xmm3
     326    //mix2
     327    movaps xmm3, xmm0
     328    shufps xmm3, xmm3, $11
     329    addps xmm0, xmm3
     330
     331    movss [eax+8], xmm0
     332  end;
     333  {$ENDIF}
     334end;
     335
     336procedure MatrixMultiplyVect3DWithoutTranslation_SSE3_Aligned(
     337  var M: TPoint3D_128; out N: TPoint3D_128);
     338begin
     339  {$IFDEF cpux86_64}
     340  asm
     341    mov rax, M
     342    movaps xmm0, [rax]
     343
     344    mov rax, N
     345
     346    movaps xmm2,xmm0
     347    mulps xmm2,xmm5
     348    haddps xmm2,xmm2
     349    haddps xmm2,xmm2
     350    movss [rax], xmm2
     351
     352    movaps xmm2,xmm0
     353    mulps xmm2,xmm6
     354    haddps xmm2,xmm2
     355    haddps xmm2,xmm2
     356    movss [rax+4], xmm2
     357
     358    mulps xmm0,xmm7
     359    haddps xmm0,xmm0
     360    haddps xmm0,xmm0
     361    movss [rax+8], xmm0
     362  end;
     363  {$ELSE}
    125364  asm
    126365    mov eax, M
     
    146385    movss [eax+8], xmm0
    147386  end;
    148   M.t := oldMt;
    149 end;
     387  {$ENDIF}
     388end;
     389
    150390{$ENDIF}
    151391
    152 operator*(const A: TMatrix3D; var M: TPoint3D_128): TPoint3D_128;
     392operator*(constref A: TMatrix3D; var M: TPoint3D_128): TPoint3D_128;
    153393{$IFDEF CPUI386}var oldMt: single; {$ENDIF}
    154394begin
     
    248488end;
    249489
     490function MultiplyVect3DWithoutTranslation(constref A: TMatrix3D; constref M: TPoint3D_128): TPoint3D_128;
     491begin
     492  {$IFDEF CPUI386}
     493  if UseSSE then
     494  begin
     495    if UseSSE3 then
     496    asm
     497      mov eax, A
     498      movups xmm5, [eax]
     499      movups xmm6, [eax+16]
     500      movups xmm7, [eax+32]
     501
     502      mov eax, M
     503      movups xmm0, [eax]
     504
     505      mov eax, result
     506
     507      movaps xmm4,xmm0
     508      mulps xmm4,xmm5
     509      haddps xmm4,xmm4
     510      haddps xmm4,xmm4
     511      movss [eax], xmm4
     512
     513      movaps xmm4,xmm0
     514      mulps xmm4,xmm6
     515      haddps xmm4,xmm4
     516      haddps xmm4,xmm4
     517      movss [eax+4], xmm4
     518
     519      mulps xmm0,xmm7
     520      haddps xmm0,xmm0
     521      haddps xmm0,xmm0
     522      movss [eax+8], xmm0
     523    end else
     524    asm
     525      mov eax, A
     526      movups xmm5, [eax]
     527      movups xmm6, [eax+16]
     528      movups xmm7, [eax+32]
     529
     530      mov eax, M
     531      movups xmm0, [eax]
     532
     533      mov eax, result
     534
     535      movaps xmm4,xmm0
     536      mulps xmm4,xmm5
     537      //mix1
     538      movaps xmm3, xmm4
     539      shufps xmm3, xmm3, $4e
     540      addps xmm4, xmm3
     541      //mix2
     542      movaps xmm3, xmm4
     543      shufps xmm3, xmm3, $11
     544      addps xmm4, xmm3
     545
     546      movss [eax], xmm4
     547
     548      movaps xmm4,xmm0
     549      mulps xmm4,xmm6
     550      //mix1
     551      movaps xmm3, xmm4
     552      shufps xmm3, xmm3, $4e
     553      addps xmm4, xmm3
     554      //mix2
     555      movaps xmm3, xmm4
     556      shufps xmm3, xmm3, $11
     557      addps xmm4, xmm3
     558
     559      movss [eax+4], xmm4
     560
     561      mulps xmm0,xmm7
     562      //mix1
     563      movaps xmm3, xmm0
     564      shufps xmm3, xmm3, $4e
     565      addps xmm0, xmm3
     566      //mix2
     567      movaps xmm3, xmm0
     568      shufps xmm3, xmm3, $11
     569      addps xmm0, xmm3
     570
     571      movss [eax+8], xmm0
     572    end;
     573  end else
     574  {$ENDIF}
     575  begin
     576    result.x := M.x * A[1,1] + M.y * A[1,2] + M.z * A[1,3];
     577    result.y := M.x * A[2,1] + M.y * A[2,2] + M.z * A[2,3];
     578    result.z := M.x * A[3,1] + M.y * A[3,2] + M.z * A[3,3];
     579    result.t := 0;
     580  end;
     581end;
     582
    250583operator*(A,B: TMatrix3D): TMatrix3D;
    251584begin
  • GraphicTest/Packages/bgrabitmap/bgraopenraster.pas

    r452 r472  
    3838    function GetMemoryStreamAsString(AFilename: string): string;
    3939    procedure UnzipFromStream(AStream: TStream);
    40     procedure UnzipFromFile(AFilename: string);
    41     procedure ZipToFile(AFilename: string);
     40    procedure UnzipFromFile(AFilenameUTF8: string);
     41    procedure ZipToFile(AFilenameUTF8: string);
     42    procedure ZipToStream(AStream: TStream);
    4243    procedure CopyThumbnailToMemoryStream(AMaxWidth, AMaxHeight: integer);
    4344    procedure AnalyzeZip;
     45    procedure PrepareZipToSave;
    4446    function GetMimeType: string; override;
    4547
    4648  public
    47     constructor Create; override;
     49    constructor Create; override; overload;
     50    constructor Create(AWidth, AHeight: integer); override; overload;
    4851    procedure Clear; override;
     52    function CheckMimeType(AStream: TStream): boolean;
    4953    procedure LoadFromStream(AStream: TStream); override;
    50     procedure LoadFromFile(const filename: string); override;
    51     procedure SaveToFile(const filename: string); override;
     54    procedure LoadFromFile(const filenameUTF8: string); override;
     55    procedure SaveToFile(const filenameUTF8: string); override;
     56    procedure SaveToStream(AStream: TStream); override;
    5257    property MimeType : string read GetMimeType write SetMimeType;
    5358    property StackXML : TXMLDocument read FStackXML;
     
    5762
    5863  TFPReaderOpenRaster = class(TFPCustomImageReader)
     64    private
     65      FWidth,FHeight,FNbLayers: integer;
    5966    protected
    6067      function InternalCheck(Stream: TStream): boolean; override;
    6168      procedure InternalRead(Stream: TStream; Img: TFPCustomImage); override;
     69    public
     70      property Width: integer read FWidth;
     71      property Height: integer read FHeight;
     72      property NbLayers: integer read FNbLayers;
     73  end;
     74
     75  { TFPWriterOpenRaster }
     76
     77  TFPWriterOpenRaster = class(TFPCustomImageWriter)
     78    protected
     79      procedure InternalWrite (Str:TStream; Img:TFPCustomImage); override;
    6280  end;
    6381
     
    6684implementation
    6785
    68 uses Graphics, XMLRead, XMLWrite, FPReadPNG, dialogs, BGRABitmapTypes, zstream;
     86uses Graphics, XMLRead, XMLWrite, FPReadPNG, dialogs, BGRABitmapTypes, zstream, lazutf8classes,
     87  UnzipperExt;
    6988
    7089function IsZipStream(stream: TStream): boolean;
     
    89108end;
    90109
     110{ TFPWriterOpenRaster }
     111
     112procedure TFPWriterOpenRaster.InternalWrite(Str: TStream; Img: TFPCustomImage);
     113var doc: TBGRAOpenRasterDocument;
     114  tempBmp: TBGRABitmap;
     115  x,y: integer;
     116
     117begin
     118  doc := TBGRAOpenRasterDocument.Create;
     119  if Img is TBGRABitmap then doc.AddLayer(Img as TBGRABitmap) else
     120  begin
     121    tempBmp := TBGRABitmap.Create(img.Width,img.Height);
     122    for y := 0 to Img.Height-1 do
     123      for x := 0 to img.Width-1 do
     124        tempBmp.SetPixel(x,y, FPColorToBGRA(img.Colors[x,y]));
     125    doc.AddOwnedLayer(tempBmp);
     126  end;
     127  doc.SaveToStream(Str);
     128  doc.Free;
     129end;
     130
    91131{ TFPReaderOpenRaster }
    92132
    93133function TFPReaderOpenRaster.InternalCheck(Stream: TStream): boolean;
    94 begin
    95  result := IsZipStream(Stream);
     134var {%h-}magic: packed array[0..3] of byte;
     135  OldPos,BytesRead: Int64;
     136  doc : TBGRAOpenRasterDocument;
     137begin
     138  Result:=false;
     139  if Stream=nil then exit;
     140  oldPos := stream.Position;
     141  BytesRead := Stream.Read({%h-}magic,sizeof(magic));
     142  stream.Position:= OldPos;
     143  if BytesRead<>sizeof(magic) then exit;
     144  if (magic[0] = $50) and (magic[1] = $4b) and (magic[2] = $03) and (magic[3] = $04) then
     145  begin
     146    doc := TBGRAOpenRasterDocument.Create;
     147    result := doc.CheckMimeType(Stream);
     148    doc.Free;
     149  end;
    96150end;
    97151
     
    102156  x,y: integer;
    103157begin
     158  FWidth := 0;
     159  FHeight:= 0;
     160  FNbLayers:= 0;
    104161  layeredImage := TBGRAOpenRasterDocument.Create;
    105162  try
     
    107164    flat := layeredImage.ComputeFlatImage;
    108165    try
    109       Img.SetSize(flat.Width,flat.Height);
    110       for y := 0 to flat.Height-1 do
    111         for x := 0 to flat.Width-1 do
    112           Img.Colors[x,y] := BGRAToFPColor(flat.GetPixel(x,y));
     166      FWidth:= layeredImage.Width;
     167      FHeight:= layeredImage.Height;
     168      FNbLayers:= layeredImage.NbLayers;
     169      if Img is TBGRACustomBitmap then
     170        TBGRACustomBitmap(img).Assign(flat)
     171      else
     172      begin
     173        Img.SetSize(flat.Width,flat.Height);
     174        for y := 0 to flat.Height-1 do
     175          for x := 0 to flat.Width-1 do
     176            Img.Colors[x,y] := BGRAToFPColor(flat.GetPixel(x,y));
     177      end;
    113178    finally
    114179      flat.free;
     
    173238  for i := stackNode.ChildNodes.Length-1 downto 0 do
    174239  begin
     240    OnLayeredBitmapLoadProgress((stackNode.ChildNodes.Length-i)*100 div stackNode.ChildNodes.Length);
    175241    layerNode:= stackNode.ChildNodes[i];
    176242    if (layerNode.NodeName = 'layer') and Assigned(layerNode.Attributes) then
     
    291357end;
    292358
    293 procedure TBGRAOpenRasterDocument.LoadFromFile(const filename: string);
    294 begin
    295   UnzipFromFile(filename);
    296   AnalyzeZip;
    297 end;
    298 
    299 procedure TBGRAOpenRasterDocument.SaveToFile(const filename: string);
     359procedure TBGRAOpenRasterDocument.PrepareZipToSave;
    300360var i: integer;
    301361    imageNode,stackNode,layerNode: TDOMElement;
     
    375435  WriteXMLFile(StackXML, StackStream);
    376436  SetMemoryStream('stack.xml',StackStream);
    377 
    378   ZipToFile(filename);
     437end;
     438
     439procedure TBGRAOpenRasterDocument.LoadFromFile(const filenameUTF8: string);
     440var AStream: TFileStreamUTF8;
     441begin
     442  AStream := TFileStreamUTF8.Create(filenameUTF8,fmOpenRead or fmShareDenyWrite);
     443  try
     444    LoadFromStream(AStream);
     445  finally
     446    AStream.Free;
     447  end;
     448end;
     449
     450procedure TBGRAOpenRasterDocument.SaveToFile(const filenameUTF8: string);
     451begin
     452  PrepareZipToSave;
     453  ZipToFile(filenameUTF8);
     454end;
     455
     456procedure TBGRAOpenRasterDocument.SaveToStream(AStream: TStream);
     457begin
     458  PrepareZipToSave;
     459  ZipToStream(AStream);
    379460end;
    380461
     
    390471begin
    391472  inherited Create;
     473  RegisterOpenRasterFormat;
     474end;
     475
     476constructor TBGRAOpenRasterDocument.Create(AWidth, AHeight: integer);
     477begin
     478  inherited Create(AWidth, AHeight);
    392479  RegisterOpenRasterFormat;
    393480end;
     
    514601  finally
    515602    FZipInputStream := nil;
    516   end;
    517   unzip.Free;
    518 end;
    519 
    520 procedure TBGRAOpenRasterDocument.UnzipFromFile(AFilename: string);
     603    unzip.Free;
     604  end;
     605end;
     606
     607procedure TBGRAOpenRasterDocument.UnzipFromFile(AFilenameUTF8: string);
    521608var unzip: TUnZipper;
    522609begin
     
    524611  unzip := TUnZipper.Create;
    525612  try
    526     unzip.FileName := AFilename;
     613    unzip.FileName := Utf8ToAnsi(AFilenameUTF8);
    527614    unzip.OnCreateStream := @ZipOnCreateStream;
    528615    unzip.OnDoneStream := @ZipOnDoneStream;
    529616    unzip.UnZipAllFiles;
    530617  finally
    531   end;
    532   unzip.Free;
    533 end;
    534 
    535 procedure TBGRAOpenRasterDocument.ZipToFile(AFilename: string);
     618    unzip.Free;
     619  end;
     620end;
     621
     622procedure TBGRAOpenRasterDocument.ZipToFile(AFilenameUTF8: string);
     623var
     624  stream: TFileStreamUTF8;
     625begin
     626  stream := TFileStreamUTF8.Create(AFilenameUTF8, fmCreate);
     627  try
     628    ZipToStream(stream);
     629  finally
     630    stream.Free;
     631  end;
     632end;
     633
     634procedure TBGRAOpenRasterDocument.ZipToStream(AStream: TStream);
    536635var zip: TZipper;
    537636  i: integer;
     
    539638  zip := TZipper.Create;
    540639  try
    541     zip.FileName := AFilename;
    542640    for i := 0 to high(FFiles) do
    543641    begin
     
    545643      zip.Entries.AddFileEntry(FFiles[i].Stream,FFiles[i].Filename).CompressionLevel := clnone;
    546644    end;
    547     zip.ZipAllFiles;
     645    zip.SaveToStream(AStream);
    548646  finally
    549647    zip.Free;
     
    557655  if (Width = 0) or (Height = 0) then exit;
    558656  thumbnail := ComputeFlatImage;
     657  CopyBitmapToMemoryStream(thumbnail,'mergedimage.png');
    559658  if (thumbnail.Width > AMaxWidth) or
    560659   (thumbnail.Height > AMaxHeight) then
     
    586685end;
    587686
     687function TBGRAOpenRasterDocument.CheckMimeType(AStream: TStream): boolean;
     688var unzip: TUnzipperStreamUtf8;
     689  mimeTypeFound: string;
     690  oldPos: int64;
     691begin
     692  result := false;
     693  unzip := TUnzipperStreamUtf8.Create;
     694  oldPos := AStream.Position;
     695  try
     696    unzip.InputStream := AStream;
     697    mimeTypeFound := unzip.UnzipFileToString('mimetype');
     698    if mimeTypeFound = OpenRasterMimeType then result := true;
     699  except
     700  end;
     701  unzip.Free;
     702  astream.Position:= OldPos;
     703end;
     704
    588705procedure TBGRAOpenRasterDocument.LoadFromStream(AStream: TStream);
    589706begin
    590   UnzipFromStream(AStream);
    591   AnalyzeZip;
     707  OnLayeredBitmapLoadFromStreamStart;
     708  try
     709    UnzipFromStream(AStream);
     710    AnalyzeZip;
     711  finally
     712    OnLayeredBitmapLoaded;
     713  end;
    592714end;
    593715
     
    673795  RegisterLayeredBitmapWriter('ora', TBGRAOpenRasterDocument);
    674796  //TPicture.RegisterFileFormat('ora', 'OpenRaster', TBGRAOpenRasterDocument);
     797  DefaultBGRAImageReader[ifOpenRaster] := TFPReaderOpenRaster;
     798  DefaultBGRAImageWriter[ifOpenRaster] := TFPWriterOpenRaster;
    675799  AlreadyRegistered:= True;
    676800end;
  • GraphicTest/Packages/bgrabitmap/bgrapaintnet.pas

    r452 r472  
    2727  TPaintDotNetFile = class(TBGRACustomLayeredBitmap)
    2828  public
    29     procedure LoadFromFile(const filename: string); override;
     29    procedure LoadFromFile(const filenameUTF8: string); override;
    3030    procedure LoadFromStream(stream: TStream); override;
    3131    procedure Clear; override;
     
    3434    constructor Create; override;
    3535  protected
     36    procedure InternalLoadFromStream(stream: TStream);
    3637    function GetWidth: integer; override;
    3738    function GetHeight: integer; override;
     
    4243    function GetLayerName(layer: integer): string; override;
    4344  private
    44     XmlHeader: string;
    45     ThumbNail: TBGRABitmap;
    4645    Content:   TDotNetDeserialization;
    4746    Document:  TSerializedClass;
     
    6059
    6160  TFPReaderPaintDotNet = class(TFPCustomImageReader)
     61    private
     62      FWidth,FHeight,FNbLayers: integer;
    6263    protected
    6364      function InternalCheck(Stream: TStream): boolean; override;
    6465      procedure InternalRead(Stream: TStream; Img: TFPCustomImage); override;
     66    public
     67      property Width: integer read FWidth;
     68      property Height: integer read FHeight;
     69      property NbLayers: integer read FNbLayers;
    6570  end;
    6671
    6772function IsPaintDotNetFile(filename: string): boolean;
     73function IsPaintDotNetFileUTF8(filenameUTF8: string): boolean;
    6874function IsPaintDotNetStream(stream: TStream): boolean;
    6975function LoadPaintDotNetFile(filename: string): TBGRABitmap;
     76function LoadPaintDotNetFileUTF8(filenameUTF8: string): TBGRABitmap;
    7077
    7178procedure RegisterPaintNetFormat;
     
    7380implementation
    7481
    75 uses zstream, Math, graphtype, Graphics;
     82uses zstream, Math, graphtype, Graphics, lazutf8classes, FileUtil;
    7683
    7784{$hints off}
     
    99106  begin
    100107    stream := TFileStream.Create(filename, fmOpenRead);
     108    Result := IsPaintDotNetStream(stream);
     109    stream.Free;
     110  end;
     111end;
     112
     113function IsPaintDotNetFileUTF8(filenameUTF8: string): boolean;
     114var
     115  stream: TFileStreamUTF8;
     116begin
     117  Result := False;
     118  if FileExistsUTF8(filenameUTF8) then
     119  begin
     120    stream := TFileStreamUTF8.Create(filenameUTF8, fmOpenRead);
    101121    Result := IsPaintDotNetStream(stream);
    102122    stream.Free;
     
    127147
    128148function LoadPaintDotNetFile(filename: string): TBGRABitmap;
     149begin
     150  result := LoadPaintDotNetFileUTF8(SysToUTF8(filename));
     151end;
     152
     153function LoadPaintDotNetFileUTF8(filenameUTF8: string): TBGRABitmap;
    129154var
    130155  pdn: TPaintDotNetFile;
     
    133158  Result := nil;
    134159  try
    135     pdn.LoadFromFile(filename);
     160    pdn.LoadFromFile(filenameUTF8);
    136161    Result := pdn.ComputeFlatImage;
    137162    pdn.Free;
     
    181206  x,y: integer;
    182207begin
     208  FWidth := 0;
     209  FHeight:= 0;
     210  FNbLayers:= 0;
    183211  pdn    := TPaintDotNetFile.Create;
    184212  try
     
    186214    flat := pdn.ComputeFlatImage;
    187215    try
    188       Img.SetSize(pdn.Width,pdn.Height);
    189       for y := 0 to pdn.Height-1 do
    190         for x := 0 to pdn.Width-1 do
    191           Img.Colors[x,y] := BGRAToFPColor(flat.GetPixel(x,y));
     216      FWidth:= pdn.Width;
     217      FHeight:= pdn.Height;
     218      FNbLayers:= pdn.NbLayers;
     219
     220      if Img is TBGRACustomBitmap then
     221        TBGRACustomBitmap(Img).Assign(flat) else
     222      begin
     223        Img.SetSize(pdn.Width,pdn.Height);
     224        for y := 0 to pdn.Height-1 do
     225          for x := 0 to pdn.Width-1 do
     226            Img.Colors[x,y] := BGRAToFPColor(flat.GetPixel(x,y));
     227      end;
    192228    finally
    193229      flat.free;
     
    205241{ TPaintDotNetFile }
    206242
    207 procedure TPaintDotNetFile.LoadFromFile(const filename: string);
    208 var
    209   stream: TFileStream;
    210 begin
    211   stream := TFileStream.Create(filename, fmOpenRead);
     243procedure TPaintDotNetFile.LoadFromFile(const filenameUTF8: string);
     244var
     245  stream: TFileStreamUTF8;
     246begin
     247  stream := TFileStreamUTF8.Create(filenameUTF8, fmOpenRead);
     248  OnLayeredBitmapLoadStart(filenameUTF8);
    212249  try
    213     LoadFromStream(stream);
     250    InternalLoadFromStream(stream);
    214251  finally
     252    OnLayeredBitmapLoaded;
    215253    stream.Free;
    216254  end;
     
    218256
    219257procedure TPaintDotNetFile.LoadFromStream(stream: TStream);
     258begin
     259  OnLayeredBitmapLoadFromStreamStart;
     260  try
     261    InternalLoadFromStream(stream);
     262  finally
     263    OnLayeredBitmapLoaded;
     264  end;
     265end;
     266
     267procedure TPaintDotNetFile.InternalLoadFromStream(stream: TStream);
    220268var
    221269  header: packed array[0..3] of char;
     
    233281  stream.Read(XmlHeaderSize, 3);
    234282  XmlheaderSize := LEtoN(XmlheaderSize);
    235   setlength(XmlHeader, XmlHeaderSize);
    236   if stream.Read(XmlHeader[1], XmlHeaderSize) <> XmlHeaderSize then
     283  if Stream.Position + XmlHeaderSize > stream.Size then
    237284    raise Exception.Create('Xml header size error');
    238   XmlHeader := Utf8ToAnsi(XmlHeader);
     285  Stream.Position:= Stream.Position + XmlHeaderSize;
    239286     {$hints off}
    240287  stream.Read(CompressionFormat, sizeof(CompressionFormat));
     
    255302  for i := 0 to NbLayers - 1 do
    256303  begin
     304    OnLayeredBitmapLoadProgress((i+1)*100 div NbLayers);
    257305    LayerData[i] := TMemoryStream.Create;
    258306    LoadLayer(LayerData[i], Stream, LayerDataSize(i));
     
    266314begin
    267315  Result := 'Paint.Net document' + LineEnding + LineEnding;
    268   if length(XmlHeader) > 255 then
    269     Result += copy(XmlHeader, 1, 255) + '...'
    270   else
    271     Result += XmlHeader;
    272   Result += LineEnding + LineEnding + Content.ToString;
     316  Result += Content.ToString;
    273317  for i := 0 to NbLayers - 1 do
    274318  begin
     
    297341  inherited Create;
    298342  Content   := nil;
    299   ThumbNail := nil;
    300343  Document  := nil;
    301344  Layers    := nil;
     
    308351  i: integer;
    309352begin
    310   XmlHeader := '';
    311353  FreeAndNil(content);
    312   FreeAndNil(thumbNail);
    313354  document := nil;
    314355  Layers   := nil;
     
    610651  RegisterLayeredBitmapReader('pdn', TPaintDotNetFile);
    611652  //TPicture.RegisterFileFormat('pdn', 'Paint.NET image', TPaintDotNetFile);
     653  DefaultBGRAImageReader[ifPaintDotNet] := TFPReaderPaintDotNet;
    612654  AlreadyRegistered := true;
    613655end;
  • GraphicTest/Packages/bgrabitmap/bgrapath.pas

    r452 r472  
    55interface
    66
     7{ There are different conventions for angles.
     8
     9  First is about the unit. It can be one of the following:
     10  - degrees (0..360)
     11  - radian (0..2*Pi)
     12  - tenth of degrees (0..3600)
     13  - from 0 to 65536
     14
     15  Second is about the origin. It can be one of the following:
     16  - right-most position (this is the default origin for radian and 65536)
     17  - top-most position (this is the default origin for degrees)
     18
     19  Third is about the sign. It can be one of the following:
     20  - positive is clockwise (this is the default for degrees)
     21  - positive is counterclockwise (this is the default for radian and 65536)
     22
     23  TBGRAPath and TBGRACanvas2D follow HTML5 convention which is:
     24    (radian, right-most, clockwise) that can be shortened to (radian, clockwise)
     25    because right-most is the default for radian. This is abbreviated as "radCW".
     26
     27  When radian are CCW, it is also specified in order to make it clear, even
     28  if it is the default convention in mathematics.
     29
     30  In order to make things easier, there are some functions that accept angles
     31  in degrees. The convention used here is the usual degree convention:
     32    (degrees, top-most, clockwise) that can be shortened to (degree)
     33    because top-most and clockwise is the default for degrees.
     34
     35  }
     36
    737uses
    8   Classes, BGRABitmapTypes;
     38  Classes, BGRABitmapTypes, BGRATransform;
     39
     40type
     41  TBGRAPathElementType = (peNone, peMoveTo, peLineTo, peCloseSubPath, peQuadraticBezierTo, peCubicBezierTo, peArc);
     42  PBGRAPathElementType = ^TBGRAPathElementType;
     43
     44  { TBGRAPath }
     45
     46  TBGRAPath = class(IBGRAPath)
     47  private
     48    function GetSvgString: string;
     49    procedure SetSvgString(const AValue: string);
     50  protected
     51    FData: pbyte;
     52    FDataSize: integer;
     53    FDataPos: integer;
     54    FLastElementType: TBGRAPathElementType;
     55    FLastCoord,
     56    FStartCoord: TPointF;
     57    FExpectedControlPoint: TPointF;
     58    FMatrix: TAffineMatrix; //this matrix must have a base of vectors
     59                            //orthogonal, of same length and with positive
     60                            //orientation in order to preserve arcs
     61    FScale,FAngleRadCW: single;
     62    procedure NeedSpace(count: integer);
     63    procedure StoreCoord(const pt: TPointF);
     64    function ReadCoord: TPointF;
     65    procedure StoreElementType(value: TBGRAPathElementType);
     66    function ReadElementType: TBGRAPathElementType;
     67    function ReadArcDef: TArcDef;
     68    procedure RewindFloat;
     69    procedure Init;
     70  public
     71    constructor Create; overload;
     72    constructor Create(ASvgString: string); overload;
     73    destructor Destroy; override;
     74    procedure beginPath;
     75    procedure closePath;
     76    procedure translate(x,y: single);
     77    procedure resetTransform;
     78    procedure rotate(angleRadCW: single); overload;
     79    procedure rotateDeg(angleDeg: single); overload;
     80    procedure rotate(angleRadCW: single; center: TPointF); overload;
     81    procedure rotateDeg(angleDeg: single; center: TPointF); overload;
     82    procedure scale(factor: single);
     83    procedure moveTo(x,y: single); overload;
     84    procedure lineTo(x,y: single); overload;
     85    procedure moveTo(const pt: TPointF); overload;
     86    procedure lineTo(const pt: TPointF); overload;
     87    procedure polylineTo(const pts: array of TPointF);
     88    procedure quadraticCurveTo(cpx,cpy,x,y: single); overload;
     89    procedure quadraticCurveTo(const cp,pt: TPointF); overload;
     90    procedure quadraticCurve(const curve: TQuadraticBezierCurve); overload;
     91    procedure smoothQuadraticCurveTo(x,y: single); overload;
     92    procedure smoothQuadraticCurveTo(const pt: TPointF); overload;
     93    procedure bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y: single); overload;
     94    procedure bezierCurveTo(const cp1,cp2,pt: TPointF); overload;
     95    procedure bezierCurve(const curve: TCubicBezierCurve); overload;
     96    procedure smoothBezierCurveTo(cp2x,cp2y,x,y: single); overload;
     97    procedure smoothBezierCurveTo(const cp2,pt: TPointF); overload;
     98    procedure rect(x,y,w,h: single);
     99    procedure roundRect(x,y,w,h,radius: single);
     100    procedure arc(cx, cy, radius, startAngleRadCW, endAngleRadCW: single; anticlockwise: boolean); overload;
     101    procedure arc(cx, cy, radius, startAngleRadCW, endAngleRadCW: single); overload;
     102    procedure arcDeg(cx, cy, radius, startAngleDeg, endAngleDeg: single; anticlockwise: boolean); overload;
     103    procedure arcDeg(cx, cy, radius, startAngleDeg, endAngleDeg: single); overload;
     104    procedure arcTo(x1, y1, x2, y2, radius: single); overload;
     105    procedure arcTo(const p1,p2: TPointF; radius: single); overload;
     106    procedure arc(const arcDef: TArcDef); overload;
     107    procedure arc(cx, cy, rx,ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single); overload;
     108    procedure arc(cx, cy, rx,ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single; anticlockwise: boolean); overload;
     109    procedure arcTo(rx,ry, xAngleRadCW: single; largeArc, anticlockwise: boolean; x,y:single);
     110    procedure copyTo(dest: IBGRAPath);
     111    procedure addPath(const AValue: string); overload;
     112    procedure addPath(source: IBGRAPath); overload;
     113    property SvgString: string read GetSvgString write SetSvgString;
     114  protected
     115    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};
     116    function _AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
     117    function _Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
     118  end;
    9119
    10120{----------------------- Spline ------------------}
     
    21131function ComputeEllipse(x,y,rx,ry: single; quality: single = 1): ArrayOfTPointF;
    22132function ComputeArc65536(x, y, rx, ry: single; start65536,end65536: word; quality: single = 1): ArrayOfTPointF;
     133function ComputeArcRad(x, y, rx, ry: single; startRadCCW,endRadCCW: single; quality: single = 1): ArrayOfTPointF;
     134function ComputeArc(const arc: TArcDef; quality: single = 1): ArrayOfTPointF;
    23135function ComputeRoundRect(x1,y1,x2,y2,rx,ry: single; quality: single = 1): ArrayOfTPointF; overload;
    24136function ComputeRoundRect(x1,y1,x2,y2,rx,ry: single; options: TRoundRectangleOptions; quality: single = 1): ArrayOfTPointF; overload;
    25137
     138function Html5ArcTo(const p0, p1, p2: TPointF; radius: single): TArcDef;
     139function SvgArcTo(const p0: TPointF; rx, ry, xAngleRadCW: single; largeArc,
     140  anticlockwise: boolean; const p1: TPointF): TArcDef;
     141function ArcStartPoint(const arc: TArcDef): TPointF;
     142function ArcEndPoint(const arc: TArcDef): TPointF;
     143function IsLargeArc(const arc: TArcDef): boolean;
     144
    26145implementation
    27146
    28 uses Math, BGRAResample;
     147uses Math, BGRAResample, SysUtils;
    29148
    30149function SplineVertexToSide(y0, y1, y2, y3: single; t: single): single;
     
    330449end;
    331450
    332 {$PUSH}{$R-}
    333451function ComputeArc65536(x, y, rx, ry: single; start65536,end65536: word; quality: single): ArrayOfTPointF;
    334452var i,nb: integer;
     
    362480  for i := 0 to nb-1 do
    363481  begin
     482    {$PUSH}{$R-}
    364483    pos := start65536+int64(i)*arclen div (int64(nb)-1);
     484    {$POP}
    365485    result[i] := PointF(x+rx*(Cos65536(pos)-32768)/32768,
    366486                        y-ry*(Sin65536(pos)-32768)/32768);
    367487  end;
    368488end;
    369 {$R+}
    370489
    371490function ComputeEllipse(x, y, rx, ry: single; quality: single): ArrayOfTPointF;
    372491begin
    373492  result := ComputeArc65536(x,y,rx,ry,0,0,quality);
     493end;
     494
     495function ComputeArcRad(x, y, rx, ry: single; startRadCCW, endRadCCW: single;
     496  quality: single): ArrayOfTPointF;
     497begin
     498  result := ComputeArc65536(x,y,rx,ry,round(startRadCCW*32768/Pi) and $ffff,round(endRadCCW*32768/Pi) and $ffff,quality);
     499  result[0] := PointF(x+cos(startRadCCW)*rx,y-sin(startRadCCW)*ry);
     500  result[high(result)] := PointF(x+cos(endRadCCW)*rx,y-sin(endRadCCW)*ry);
     501end;
     502
     503function ComputeArc(const arc: TArcDef; quality: single): ArrayOfTPointF;
     504var startAngle,endAngle: single;
     505    i,n: integer;
     506    temp: TPointF;
     507    m: TAffineMatrix;
     508begin
     509  startAngle := -arc.startAngleRadCW;
     510  endAngle:= -arc.endAngleRadCW;
     511  if not arc.anticlockwise then
     512  begin
     513    result := ComputeArcRad(arc.center.x,arc.center.y,arc.radius.x,arc.radius.y,endAngle,startAngle,quality);
     514    n := length(result);
     515    if n>1 then
     516      for i := 0 to (n-2) div 2 do
     517      begin
     518        temp := result[i];
     519        result[i] := result[n-1-i];
     520        result[n-1-i] := temp;
     521      end;
     522  end else
     523    result := ComputeArcRad(arc.center.x,arc.center.y,arc.radius.x,arc.radius.y,startAngle,endAngle,quality);
     524  if arc.xAngleRadCW <> 0 then
     525  begin
     526    m := AffineMatrixTranslation(arc.center.x,arc.center.y)*AffineMatrixRotationRad(-arc.xAngleRadCW)*AffineMatrixTranslation(-arc.center.x,-arc.center.y);
     527    for i := 0 to high(result) do
     528      result[i] := m*result[i];
     529  end;
    374530end;
    375531
     
    436592end;
    437593
     594function Html5ArcTo(const p0, p1, p2: TPointF; radius: single
     595  ): TArcDef;
     596var p3,p4,an,bn,cn,c: TPointF;
     597    dir, a2, b2, c2, cosx, sinx, d: single;
     598    anticlockwise: boolean;
     599begin
     600  result.center := p1;
     601  result.radius := PointF(0,0);
     602  result.xAngleRadCW:= 0;
     603  result.startAngleRadCW := 0;
     604  result.endAngleRadCW:= 0;
     605  result.anticlockwise:= false;
     606
     607  radius := abs(radius);
     608  if (p0 = p1) or (p1 = p2) or (radius = 0) then exit;
     609
     610  dir := (p2.x-p1.x)*(p0.y-p1.y) + (p2.y-p1.y)*(p1.x-p0.x);
     611  if dir = 0 then exit;
     612
     613  a2 := (p0.x-p1.x)*(p0.x-p1.x) + (p0.y-p1.y)*(p0.y-p1.y);
     614  b2 := (p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y);
     615  c2 := (p0.x-p2.x)*(p0.x-p2.x) + (p0.y-p2.y)*(p0.y-p2.y);
     616  cosx := (a2+b2-c2)/(2*sqrt(a2*b2));
     617
     618  sinx := sqrt(1 - cosx*cosx);
     619  if (sinx = 0) or (cosx = 1) then exit;
     620  d := radius / ((1 - cosx) / sinx);
     621
     622  an := (p1-p0)*(1/sqrt(a2));
     623  bn := (p1-p2)*(1/sqrt(b2));
     624  p3 := p1 - an*d;
     625  p4 := p1 - bn*d;
     626  anticlockwise := (dir < 0);
     627
     628  cn := PointF(an.y,-an.x)*radius;
     629  if not anticlockwise then cn := -cn;
     630  c := p3 + cn;
     631
     632  result.center := c;
     633  result.radius:= PointF(radius,radius);
     634  result.startAngleRadCW := arctan2((p3.y-c.y), (p3.x-c.x));
     635  result.endAngleRadCW := arctan2((p4.y-c.y), (p4.x-c.x));
     636  result.anticlockwise:= anticlockwise;
     637end;
     638
     639function SvgArcTo(const p0: TPointF; rx, ry, xAngleRadCW: single; largeArc,
     640  anticlockwise: boolean; const p1: TPointF): TArcDef;
     641var
     642    p0p,cp: TPointF;
     643    cross1,cross2,lambda: single;
     644begin
     645  if (rx=0) or (ry=0) or (p0 = p1) then
     646  begin
     647    result.radius := PointF(0,0);
     648    result.xAngleRadCW:= 0;
     649    result.anticlockwise := false;
     650    result.endAngleRadCW := 0;
     651    result.startAngleRadCW:= 0;
     652    result.center := p1;
     653    exit;
     654  end;
     655  result.xAngleRadCW := xAngleRadCW;
     656  result.anticlockwise := anticlockwise;
     657  p0p := AffineMatrixRotationRad(xAngleRadCW)*( (p0-p1)*0.5 );
     658
     659  //ensure radius is big enough
     660  lambda := sqr(p0p.x/rx) + sqr(p0p.y/ry);
     661  if lambda > 1 then
     662  begin
     663    lambda := sqrt(lambda);
     664    rx *= lambda;
     665    ry *= lambda;
     666  end;
     667  result.radius := PointF(rx,ry);
     668
     669  //compute center
     670  cross2 := sqr(rx*p0p.y) + sqr(ry*p0p.x);
     671  cross1 := sqr(rx*ry);
     672  if cross1 <= cross2 then
     673    cp := PointF(0,0)
     674  else
     675    cp := sqrt((cross1-cross2)/cross2)*
     676       PointF(rx*p0p.y/ry, -ry*p0p.x/rx);
     677  if largeArc <> anticlockwise then cp := -cp;
     678
     679  result.center := AffineMatrixRotationRad(-xAngleRadCW)*cp +
     680                  (p0+p1)*0.5;
     681  result.startAngleRadCW := arctan2((p0p.y-cp.y)/ry,(p0p.x-cp.x)/rx);
     682  result.endAngleRadCW := arctan2((-p0p.y-cp.y)/ry,(-p0p.x-cp.x)/rx);
     683end;
     684
     685function ArcStartPoint(const arc: TArcDef): TPointF;
     686begin
     687  result := AffineMatrixRotationRad(-arc.xAngleRadCW)*PointF(cos(arc.startAngleRadCW)*arc.radius.x,
     688                                                       sin(arc.startAngleRadCW)*arc.radius.y) + arc.center;
     689end;
     690
     691function ArcEndPoint(const arc: TArcDef): TPointF;
     692begin
     693  result := AffineMatrixRotationRad(-arc.xAngleRadCW)*PointF(cos(arc.endAngleRadCW)*arc.radius.x,
     694                                                       sin(arc.endAngleRadCW)*arc.radius.y) + arc.center;
     695end;
     696
     697function IsLargeArc(const arc: TArcDef): boolean;
     698var diff,a1,a2: single;
     699begin
     700  a1 := arc.startAngleRadCW - floor(arc.startAngleRadCW/(2*Pi))*(2*Pi);
     701  a2 := arc.endAngleRadCW - floor(arc.endAngleRadCW/(2*Pi))*(2*Pi);
     702  if not arc.anticlockwise then
     703    diff := a2 - a1
     704  else
     705    diff := a1 - a2;
     706  result := (diff < 0) or (diff >= Pi);
     707end;
     708
     709{ TBGRAPath }
     710
     711function TBGRAPath.GetSvgString: string;
     712const RadToDeg = 180/Pi;
     713var savedPos: integer;
     714    a: TArcDef;
     715    formats: TFormatSettings;
     716    lastPos,p1: TPointF;
     717    implicitCommand: char;
     718
     719  function FloatToString(value: single): string;
     720  begin
     721    result := FloatToStrF(value,ffGeneral,7,0,formats)+' ';
     722  end;
     723
     724  function CoordToString(const pt: TPointF): string;
     725  begin
     726    lastPos := pt;
     727    result := FloatToString(pt.x)+FloatToString(pt.y);
     728  end;
     729
     730  function BoolToString(value: boolean): string;
     731  begin
     732    if value then
     733      result := '1 ' else result := '0 ';
     734  end;
     735
     736  procedure addCommand(command: char; parameters: string);
     737  begin
     738    if result <> '' then result += ' '; //optional whitespace
     739    if command <> implicitCommand then result += command;
     740    result += trim(parameters);
     741    if command = 'M' then implicitCommand:= 'L'
     742    else if command = 'm' then implicitCommand:= 'l'
     743    else if command in['z','Z'] then implicitCommand:= #0
     744    else implicitCommand := command;
     745  end;
     746
     747var param: string;
     748
     749begin
     750  formats := DefaultFormatSettings;
     751  formats.DecimalSeparator := '.';
     752
     753  result := '';
     754  savedPos:= FDataPos;
     755  FDataPos := 0;
     756  lastPos := EmptyPointF;
     757  implicitCommand := #0;
     758  while FDataPos < savedPos do
     759  begin
     760    case ReadElementType of
     761    peMoveTo: addCommand('M',CoordToString(ReadCoord));
     762    peLineTo: addCommand('L',CoordToString(ReadCoord));
     763    peCloseSubPath: addCommand('z','');
     764    peQuadraticBezierTo:
     765      begin
     766        param := CoordToString(ReadCoord);
     767        param += CoordToString(ReadCoord);
     768        addCommand('Q',param);
     769      end;
     770    peCubicBezierTo:
     771      begin
     772        param := CoordToString(ReadCoord);
     773        param += CoordToString(ReadCoord);
     774        param += CoordToString(ReadCoord);
     775        addCommand('C',param);
     776      end;
     777    peArc:
     778      begin
     779        a := ReadArcDef;
     780        p1 := ArcStartPoint(a);
     781        if isEmptyPointF(lastPos) or (p1 <> lastPos) then
     782          addCommand('L',CoordToString(p1));
     783        param := CoordToString(a.radius);
     784        param += FloatToString(a.xAngleRadCW*RadToDeg);
     785        param += BoolToString(IsLargeArc(a));
     786        param += BoolToString(not a.anticlockwise);
     787        param += CoordToString(ArcEndPoint(a));
     788        addCommand('A',param);
     789      end;
     790    end;
     791  end;
     792  FDataPos := savedPos;
     793end;
     794
     795procedure TBGRAPath.SetSvgString(const AValue: string);
     796begin
     797  resetTransform;
     798  beginPath;
     799  addPath(AValue);
     800end;
     801
     802procedure TBGRAPath.addPath(const AValue: string);
     803var p: integer;
     804    numberError: boolean;
     805
     806  function parseFloat: single;
     807  var numberStart: integer;
     808      errPos: integer;
     809  begin
     810    while (p <= length(AValue)) and (AValue[p] in[#0..#32,',']) do inc(p);
     811    numberStart:= p;
     812    if (p <= length(AValue)) and (AValue[p] in['+','-']) then inc(p);
     813    while (p <= length(AValue)) and (AValue[p] in['0'..'9','.']) do inc(p);
     814    if (p <= length(AValue)) and (AValue[p] in['e','E']) then inc(p);
     815    if (p <= length(AValue)) and (AValue[p] in['+','-']) then inc(p);
     816    while (p <= length(AValue)) and (AValue[p] in['0'..'9','.']) do inc(p);
     817    val(copy(AValue,numberStart,p-numberStart),result,errPos);
     818    if errPos <> 0 then numberError := true;
     819  end;
     820
     821  function parseCoord(relative: boolean): TPointF;
     822  begin
     823    result := PointF(parseFloat,parseFloat);
     824    if relative and not isEmptyPointF(FLastCoord) then result += FLastCoord;
     825  end;
     826
     827var
     828  command,implicitCommand: char;
     829  relative: boolean;
     830  c1,c2,p1: TPointF;
     831  a: TArcDef;
     832  largeArc: boolean;
     833begin
     834  FLastCoord := EmptyPointF;
     835  FStartCoord := EmptyPointF;
     836  p := 1;
     837  implicitCommand:= #0;
     838  while p <= length(AValue) do
     839  begin
     840    command := AValue[p];
     841    if (command in['0'..'9','.','+','-']) and (implicitCommand <> #0) then
     842      command := implicitCommand
     843    else
     844    begin
     845      inc(p);
     846    end;
     847    relative := (command = lowerCase(command));
     848    numberError := false;
     849    if upcase(command) in ['L','H','V','C','S','Q','T','A'] then
     850      implicitCommand:= command; //by default the command repeats
     851    case upcase(command) of
     852    'Z': begin
     853           closePath;
     854           implicitCommand:= #0;
     855         end;
     856    'M': begin
     857           p1 := parseCoord(relative);
     858           if not numberError then moveTo(p1);
     859           if relative then implicitCommand:= 'l' else
     860             implicitCommand:= 'L';
     861      end;
     862    'L': begin
     863           p1 := parseCoord(relative);
     864           if not numberError then lineTo(p1);
     865      end;
     866    'H': begin
     867        if not isEmptyPointF(FLastCoord) then p1 := FLastCoord
     868        else p1 := PointF(0,0);
     869        if relative then p1.x += parseFloat
     870        else p1.x := parseFloat;
     871        if not numberError then lineTo(p1);
     872      end;
     873    'V': begin
     874        if not isEmptyPointF(FLastCoord) then p1 := FLastCoord
     875        else p1 := PointF(0,0);
     876        if relative then p1.y += parseFloat
     877        else p1.y := parseFloat;
     878        if not numberError then lineTo(p1);
     879      end;
     880    'C': begin
     881        c1 := parseCoord(relative);
     882        c2 := parseCoord(relative);
     883        p1 := parseCoord(relative);
     884        if not numberError then bezierCurveTo(c1,c2,p1);
     885      end;
     886    'S': begin
     887        c2 := parseCoord(relative);
     888        p1 := parseCoord(relative);
     889        if not numberError then smoothBezierCurveTo(c2,p1);
     890      end;
     891    'Q': begin
     892        c1 := parseCoord(relative);
     893        p1 := parseCoord(relative);
     894        if not numberError then quadraticCurveTo(c1,p1);
     895      end;
     896    'T': begin
     897        p1 := parseCoord(relative);
     898        if not numberError then smoothQuadraticCurveTo(p1);
     899      end;
     900    'A': begin
     901        a.radius := parseCoord(false);
     902        a.xAngleRadCW := parseFloat*Pi/180;
     903        largeArc := parseFloat<>0;
     904        a.anticlockwise:= parseFloat=0;
     905        p1 := parseCoord(relative);
     906        arcTo(a.radius.x,a.radius.y,a.xAngleRadCW,largeArc,a.anticlockwise,p1.x,p1.y);
     907      end;
     908    end;
     909  end;
     910end;
     911
     912procedure TBGRAPath.addPath(source: IBGRAPath);
     913begin
     914  source.copyTo(self);
     915end;
     916
     917procedure TBGRAPath.NeedSpace(count: integer);
     918begin
     919  if FDataPos + count > FDataSize then
     920  begin
     921    FDataSize := FDataSize*2+8;
     922    ReAllocMem(FData, FDataSize);
     923  end;
     924end;
     925
     926procedure TBGRAPath.StoreCoord(const pt: TPointF);
     927begin
     928  NeedSpace(sizeof(single)*2);
     929  with FMatrix*pt do
     930  begin
     931    PSingle(FData+FDataPos)^ := x;
     932    PSingle(FData+FDataPos+sizeof(single))^ := y;
     933  end;
     934  Inc(FDataPos, sizeof(single)*2);
     935  FLastCoord := pt;
     936end;
     937
     938function TBGRAPath.ReadCoord: TPointF;
     939begin
     940  result := PPointF(FData+FDataPos)^;
     941  inc(FDataPos,sizeof(TPointF));
     942end;
     943
     944procedure TBGRAPath.StoreElementType(value: TBGRAPathElementType);
     945begin
     946  NeedSpace(sizeof(TBGRAPathElementType));
     947  PBGRAPathElementType(FData+FDataPos)^ := value;
     948  Inc(FDataPos, sizeof(TBGRAPathElementType));
     949  FLastElementType:= value;
     950end;
     951
     952function TBGRAPath.ReadElementType: TBGRAPathElementType;
     953begin
     954  result := PBGRAPathElementType(FData+FDataPos)^;
     955  inc(FDataPos,sizeof(TBGRAPathElementType));
     956end;
     957
     958function TBGRAPath.ReadArcDef: TArcDef;
     959begin
     960  result := PArcDef(FData+FDataPos)^;
     961  inc(FDataPos,sizeof(TArcDef));
     962end;
     963
     964procedure TBGRAPath.RewindFloat;
     965begin
     966  if FDataPos >= sizeof(single) then dec(FDataPos, sizeof(Single));
     967end;
     968
     969procedure TBGRAPath.Init;
     970begin
     971  FData := nil;
     972  FDataSize := 0;
     973  FDataPos := 0;
     974  FLastElementType := peNone;
     975  FLastCoord := EmptyPointF;
     976  FStartCoord := EmptyPointF;
     977  FExpectedControlPoint := EmptyPointF;
     978  resetTransform;
     979end;
     980
     981constructor TBGRAPath.Create;
     982begin
     983  Init;
     984end;
     985
     986constructor TBGRAPath.Create(ASvgString: string);
     987begin
     988  Init;
     989  SvgString:= ASvgString;
     990end;
     991
     992destructor TBGRAPath.Destroy;
     993begin
     994  if Assigned(FData) then
     995  begin
     996    FreeMem(FData);
     997    FData := nil;
     998  end;
     999  inherited Destroy;
     1000end;
     1001
     1002procedure TBGRAPath.beginPath;
     1003begin
     1004  FDataPos := 0;
     1005end;
     1006
     1007procedure TBGRAPath.closePath;
     1008begin
     1009  if (FLastElementType <> peNone) and (FLastElementType <> peCloseSubPath) then
     1010  begin
     1011    StoreElementType(peCloseSubPath);
     1012    FLastCoord := FStartCoord;
     1013  end;
     1014end;
     1015
     1016procedure TBGRAPath.translate(x, y: single);
     1017begin
     1018  FMatrix *= AffineMatrixTranslation(x,y);
     1019end;
     1020
     1021procedure TBGRAPath.resetTransform;
     1022begin
     1023  FMatrix := AffineMatrixIdentity;
     1024  FAngleRadCW := 0;
     1025  FScale:= 1;
     1026end;
     1027
     1028procedure TBGRAPath.rotate(angleRadCW: single);
     1029begin
     1030  FMatrix *= AffineMatrixRotationRad(-angleRadCW);
     1031  FAngleRadCW += angleRadCW;
     1032end;
     1033
     1034procedure TBGRAPath.rotateDeg(angleDeg: single);
     1035const degToRad = Pi/180;
     1036begin
     1037  rotate(angleDeg*degToRad);
     1038end;
     1039
     1040procedure TBGRAPath.rotate(angleRadCW: single; center: TPointF);
     1041begin
     1042  translate(center.x,center.y);
     1043  rotate(angleRadCW);
     1044  translate(-center.x,-center.y);
     1045end;
     1046
     1047procedure TBGRAPath.rotateDeg(angleDeg: single; center: TPointF);
     1048begin
     1049  translate(center.x,center.y);
     1050  rotateDeg(angleDeg);
     1051  translate(-center.x,-center.y);
     1052end;
     1053
     1054procedure TBGRAPath.scale(factor: single);
     1055begin
     1056  FMatrix *= AffineMatrixScale(factor,factor);
     1057  FScale *= factor;
     1058end;
     1059
     1060procedure TBGRAPath.moveTo(x, y: single);
     1061begin
     1062  moveTo(PointF(x,y));
     1063end;
     1064
     1065procedure TBGRAPath.lineTo(x, y: single);
     1066begin
     1067  lineTo(PointF(x,y));
     1068end;
     1069
     1070procedure TBGRAPath.moveTo(const pt: TPointF);
     1071begin
     1072  if FLastElementType <> peMoveTo then
     1073  begin
     1074    StoreElementType(peMoveTo);
     1075    StoreCoord(pt);
     1076  end else
     1077  begin
     1078    RewindFloat;
     1079    RewindFloat;
     1080    StoreCoord(pt);
     1081  end;
     1082  FLastCoord := pt;
     1083  FStartCoord := FLastCoord;
     1084end;
     1085
     1086procedure TBGRAPath.lineTo(const pt: TPointF);
     1087begin
     1088  if not isEmptyPointF(FLastCoord) then
     1089  begin
     1090    StoreElementType(peLineTo);
     1091    StoreCoord(pt);
     1092    FLastCoord := pt;
     1093  end else
     1094    moveTo(pt);
     1095end;
     1096
     1097procedure TBGRAPath.polylineTo(const pts: array of TPointF);
     1098var i: integer;
     1099begin
     1100  NeedSpace((sizeof(TBGRAPathElementType)+2*sizeof(single))*length(pts));
     1101  for i := 0 to high(pts) do with pts[i] do lineTo(x,y);
     1102end;
     1103
     1104procedure TBGRAPath.quadraticCurveTo(cpx, cpy, x, y: single);
     1105begin
     1106  quadraticCurveTo(PointF(cpx,cpy),PointF(x,y));
     1107end;
     1108
     1109procedure TBGRAPath.quadraticCurveTo(const cp, pt: TPointF);
     1110begin
     1111  if not isEmptyPointF(FLastCoord) then
     1112  begin
     1113    StoreElementType(peQuadraticBezierTo);
     1114    StoreCoord(cp);
     1115    StoreCoord(pt);
     1116    FLastCoord := pt;
     1117  end else
     1118    lineTo(pt);
     1119  FExpectedControlPoint := pt+(pt-cp);
     1120end;
     1121
     1122procedure TBGRAPath.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y: single);
     1123begin
     1124  bezierCurveTo(PointF(cp1x,cp1y),PointF(cp2x,cp2y),PointF(x,y));
     1125end;
     1126
     1127procedure TBGRAPath.bezierCurveTo(const cp1, cp2, pt: TPointF);
     1128begin
     1129  if isEmptyPointF(FLastCoord) then moveTo(cp1);
     1130  StoreElementType(peCubicBezierTo);
     1131  StoreCoord(cp1);
     1132  StoreCoord(cp2);
     1133  StoreCoord(pt);
     1134  FLastCoord := pt;
     1135  FExpectedControlPoint := pt + (pt-cp2);
     1136end;
     1137
     1138procedure TBGRAPath.bezierCurve(const curve: TCubicBezierCurve);
     1139begin
     1140  moveTo(curve.p1);
     1141  bezierCurveTo(curve.c1,curve.c2,curve.p2);
     1142end;
     1143
     1144procedure TBGRAPath.smoothBezierCurveTo(cp2x, cp2y, x, y: single);
     1145begin
     1146  smoothBezierCurveTo(PointF(cp2x,cp2y),PointF(x,y));
     1147end;
     1148
     1149procedure TBGRAPath.smoothBezierCurveTo(const cp2, pt: TPointF);
     1150begin
     1151  if (FLastElementType = peCubicBezierTo) and not isEmptyPointF(FExpectedControlPoint) then
     1152    bezierCurveTo(FExpectedControlPoint,cp2,pt)
     1153  else if not isEmptyPointF(FLastCoord) then
     1154    bezierCurveTo(FLastCoord,cp2,pt)
     1155  else
     1156    bezierCurveTo(cp2,cp2,pt);
     1157end;
     1158
     1159procedure TBGRAPath.quadraticCurve(const curve: TQuadraticBezierCurve);
     1160begin
     1161  moveTo(curve.p1);
     1162  quadraticCurveTo(curve.c,curve.p2);
     1163end;
     1164
     1165procedure TBGRAPath.smoothQuadraticCurveTo(x, y: single);
     1166begin
     1167  smoothQuadraticCurveTo(PointF(x,y));
     1168end;
     1169
     1170procedure TBGRAPath.smoothQuadraticCurveTo(const pt: TPointF);
     1171begin
     1172  if (FLastElementType = peQuadraticBezierTo) and not isEmptyPointF(FExpectedControlPoint) then
     1173    quadraticCurveTo(FExpectedControlPoint,pt)
     1174  else if not isEmptyPointF(FLastCoord) then
     1175    quadraticCurveTo(FLastCoord,pt)
     1176  else
     1177    quadraticCurveTo(pt,pt);
     1178end;
     1179
     1180procedure TBGRAPath.rect(x, y, w, h: single);
     1181begin
     1182  moveTo(x,y);
     1183  lineTo(x+w,y);
     1184  lineTo(x+w,y+h);
     1185  lineTo(x,y+h);
     1186  closePath;
     1187end;
     1188
     1189procedure TBGRAPath.roundRect(x, y, w, h, radius: single);
     1190begin
     1191  if radius <= 0 then
     1192  begin
     1193    rect(x,y,w,h);
     1194    exit;
     1195  end;
     1196  if (w <= 0) or (h <= 0) then exit;
     1197  if radius*2 > w then radius := w/2;
     1198  if radius*2 > h then radius := h/2;
     1199  moveTo(x+radius,y);
     1200  arcTo(PointF(x+w,y),PointF(x+w,y+h), radius);
     1201  arcTo(PointF(x+w,y+h),PointF(x,y+h), radius);
     1202  arcTo(PointF(x,y+h),PointF(x,y), radius);
     1203  arcTo(PointF(x,y),PointF(x+w,y), radius);
     1204  closePath;
     1205end;
     1206
     1207procedure TBGRAPath.arc(cx, cy, radius, startAngleRadCW, endAngleRadCW: single;
     1208  anticlockwise: boolean);
     1209begin
     1210  arc(cx,cy,radius,radius,0,startAngleRadCW,endAngleRadCW,anticlockwise);
     1211end;
     1212
     1213procedure TBGRAPath.arc(cx, cy, radius, startAngleRadCW, endAngleRadCW: single);
     1214begin
     1215  arc(cx,cy,radius,startAngleRadCW,endAngleRadCW,false);
     1216end;
     1217
     1218procedure TBGRAPath.arcDeg(cx, cy, radius, startAngleDeg, endAngleDeg: single;
     1219  anticlockwise: boolean);
     1220const degToRad = Pi/180;
     1221begin
     1222  arc(cx,cy,radius,(startAngleDeg-90)*degToRad,(endAngleDeg-90)*degToRad,anticlockwise);
     1223end;
     1224
     1225procedure TBGRAPath.arcDeg(cx, cy, radius, startAngleDeg, endAngleDeg: single);
     1226const degToRad = Pi/180;
     1227begin
     1228  arc(cx,cy,radius,(startAngleDeg-90)*degToRad,(endAngleDeg-90)*degToRad);
     1229end;
     1230
     1231procedure TBGRAPath.arcTo(x1, y1, x2, y2, radius: single);
     1232begin
     1233  arcTo(PointF(x1,y1), PointF(x2,y2), radius);
     1234end;
     1235
     1236procedure TBGRAPath.arcTo(const p1, p2: TPointF; radius: single);
     1237var p0 : TPointF;
     1238begin
     1239  if isEmptyPointF(FLastCoord) then
     1240    p0 := p1 else p0 := FLastCoord;
     1241  arc(Html5ArcTo(p0,p1,p2,radius));
     1242end;
     1243
     1244procedure TBGRAPath.arc(const arcDef: TArcDef);
     1245var transformedArc: TArcDef;
     1246begin
     1247  if (arcDef.radius.x = 0) and (arcDef.radius.y = 0) then
     1248    lineTo(arcDef.center)
     1249  else
     1250  begin
     1251    if isEmptyPointF(FLastCoord) then
     1252      moveTo(ArcStartPoint(arcDef));
     1253    StoreElementType(peArc);
     1254    NeedSpace(sizeof(TArcDef));
     1255    transformedArc.anticlockwise := arcDef.anticlockwise;
     1256    transformedArc.startAngleRadCW := arcDef.startAngleRadCW;
     1257    transformedArc.endAngleRadCW := arcDef.endAngleRadCW;
     1258    transformedArc.center := FMatrix*arcDef.center;
     1259    transformedArc.radius := arcDef.radius*FScale;
     1260    transformedArc.xAngleRadCW := arcDef.xAngleRadCW+FAngleRadCW;
     1261    PArcDef(FData+FDataPos)^ := transformedArc;
     1262    inc(FDataPos, sizeof(TArcDef));
     1263    FLastCoord := ArcEndPoint(arcDef);
     1264  end;
     1265end;
     1266
     1267procedure TBGRAPath.arc(cx, cy, rx, ry, xAngleRadCW, startAngleRadCW,
     1268  endAngleRadCW: single);
     1269begin
     1270  arc(ArcDef(cx,cy,rx,ry,xAngleRadCW,startAngleRadCW,endAngleRadCW,false));
     1271end;
     1272
     1273procedure TBGRAPath.arc(cx, cy, rx, ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single;
     1274  anticlockwise: boolean);
     1275begin
     1276  arc(ArcDef(cx,cy,rx,ry,xAngleRadCW,startAngleRadCW,endAngleRadCW,anticlockwise));
     1277end;
     1278
     1279procedure TBGRAPath.arcTo(rx, ry, xAngleRadCW: single; largeArc,
     1280  anticlockwise: boolean; x, y: single);
     1281begin
     1282  if isEmptyPointF(FLastCoord) then
     1283    moveTo(x,y)
     1284  else
     1285    arc(SvgArcTo(FLastCoord, rx,ry, xAngleRadCW, largeArc, anticlockwise, PointF(x,y)));
     1286end;
     1287
     1288procedure TBGRAPath.copyTo(dest: IBGRAPath);
     1289var savedPos: integer;
     1290    cp1,cp2,p1: TPointF;
     1291begin
     1292  savedPos:= FDataPos;
     1293  FDataPos := 0;
     1294  while FDataPos < savedPos do
     1295  begin
     1296    case ReadElementType of
     1297    peMoveTo: dest.moveTo(ReadCoord);
     1298    peLineTo: dest.lineTo(ReadCoord);
     1299    peCloseSubPath: dest.closePath;
     1300    peQuadraticBezierTo:
     1301      begin
     1302        cp1 := ReadCoord;
     1303        p1 := ReadCoord;
     1304        dest.quadraticCurveTo(cp1,p1);
     1305      end;
     1306    peCubicBezierTo:
     1307      begin
     1308        cp1 := ReadCoord;
     1309        cp2 := ReadCoord;
     1310        p1 := ReadCoord;
     1311        dest.bezierCurveTo(cp1,cp2,p1);
     1312      end;
     1313    peArc: dest.arc(ReadArcDef);
     1314    end;
     1315  end;
     1316  FDataPos := savedPos;
     1317end;
     1318
     1319function TBGRAPath.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};
     1320begin
     1321  if GetInterface(iid, obj) then
     1322    Result := S_OK
     1323  else
     1324    Result := longint(E_NOINTERFACE);
     1325end;
     1326
     1327{ There is no automatic reference counting, but it is compulsory to define these functions }
     1328function TBGRAPath._AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
     1329begin
     1330  result := 0;
     1331end;
     1332
     1333function TBGRAPath._Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
     1334begin
     1335  result := 0;
     1336end;
    4381337
    4391338end.
  • GraphicTest/Packages/bgrabitmap/bgrapen.pas

    r452 r472  
    2020  TBGRAPolyLineOption = (plRoundCapOpen, //specifies that the line ending is opened
    2121                         plCycle,        //specifies that it is a polygon
    22                          plAutoCycle);   //specifies that a cycle must be used if the last point is the first point
     22                         plAutoCycle,    //specifies that a cycle must be used if the last point is the first point
     23                         plNoStartCap,
     24                         plNoEndCap);
    2325  TBGRAPolyLineOptions = set of TBGRAPolyLineOption;
     26  TComputeArrowHeadProc = function(const APosition: TPointF; const ADirection: TPointF; const AWidth: single; const ACurrentPos: single): ArrayOfTPointF of object;
    2427
    2528{ Draw a polyline with specified parameters. If a scanner is specified, it is used as a texture.
     
    2730procedure BGRAPolyLine(bmp: TBGRACustomBitmap; const linepts: array of TPointF;
    2831     width: single; pencolor: TBGRAPixel; linecap: TPenEndCap; joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle;
    29      options: TBGRAPolyLineOptions; scan: IBGRAScanner = nil; miterLimit: single = 2);
     32     options: TBGRAPolyLineOptions; scan: IBGRAScanner = nil; miterLimit: single = 2; arrowStart: TComputeArrowHeadProc = nil; arrowStartPos: single = 0; arrowEnd: TComputeArrowHeadProc = nil; arrowEndPos: single = 0);
    3033
    3134{ Compute the path for a polyline }
    3235function ComputeWidePolylinePoints(const linepts: array of TPointF; width: single;
    3336          pencolor: TBGRAPixel; linecap: TPenEndCap; joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle;
    34           options: TBGRAPolyLineOptions; miterLimit: single = 2): ArrayOfTPointF;
     37          options: TBGRAPolyLineOptions; miterLimit: single = 2; arrowStart: TComputeArrowHeadProc = nil; wantedStartArrowPos: single = 0; arrowEnd: TComputeArrowHeadProc = nil; WantedEndArrowPos: single = 0): ArrayOfTPointF;
    3538
    3639{ Compute the path for a poly-polyline }
    3740function ComputeWidePolyPolylinePoints(const linepts: array of TPointF; width: single;
    3841          pencolor: TBGRAPixel; linecap: TPenEndCap; joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle;
    39           options: TBGRAPolyLineOptions; miterLimit: single = 2): ArrayOfTPointF;
     42          options: TBGRAPolyLineOptions; miterLimit: single = 2; arrowStart: TComputeArrowHeadProc = nil; arrowStartPos: single = 0; arrowEnd: TComputeArrowHeadProc = nil; arrowEndPos: single = 0): ArrayOfTPointF;
    4043
    4144{--------------------- Pixel line procedures --------------------------}
     
    4447
    4548//aliased version
    46 procedure BGRADrawLineAliased(dest: TBGRACustomBitmap; x1, y1, x2, y2: integer; c: TBGRAPixel; DrawLastPixel: boolean);
     49procedure BGRADrawLineAliased(dest: TBGRACustomBitmap; x1, y1, x2, y2: integer; c: TBGRAPixel; DrawLastPixel: boolean; ADrawMode: TDrawMode = dmDrawWithTransparency);
    4750procedure BGRAEraseLineAliased(dest: TBGRACustomBitmap; x1, y1, x2, y2: integer; alpha: byte; DrawLastPixel: boolean);
    4851
    4952//antialiased version
    50 procedure BGRADrawLineAntialias(dest: TBGRACustomBitmap; x1, y1, x2, y2: integer;
    51   c: TBGRAPixel; DrawLastPixel: boolean);
     53procedure BGRADrawLineAntialias({%H-}dest: TBGRACustomBitmap; x1, y1, x2, y2: integer;
     54  c: TBGRAPixel; DrawLastPixel: boolean; LinearBlend : boolean = false);
    5255procedure BGRAEraseLineAntialias(dest: TBGRACustomBitmap; x1, y1, x2, y2: integer;
    5356  calpha: byte; DrawLastPixel: boolean);
     
    5558//antialiased version with bicolor dashes (to draw a frame)
    5659procedure BGRADrawLineAntialias(dest: TBGRACustomBitmap; x1, y1, x2, y2: integer;
    57   c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean; var DashPos: integer);
     60  c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean; var DashPos: integer; LinearBlend : boolean = false);
    5861
    5962//length added to ensure accepable alpha join (using TBGRAMultishapeFiller is still better)
     
    7477
    7578procedure BGRADrawLineAliased(dest: TBGRACustomBitmap; x1, y1, x2, y2: integer;
    76   c: TBGRAPixel; DrawLastPixel: boolean);
     79  c: TBGRAPixel; DrawLastPixel: boolean; ADrawMode: TDrawMode);
     80var
     81  Y, X: integer;
     82  DX, DY, SX, SY, E: integer;
     83  PixelProc: procedure (x, y: int32or64; c: TBGRAPixel) of object;
     84begin
     85  if (Y1 = Y2) then
     86  begin
     87    if (X1 = X2) then
     88    begin
     89      if DrawLastPixel then
     90        dest.DrawPixel(X1, Y1, c, ADrawMode);
     91    end else
     92    begin
     93      if not DrawLastPixel then
     94      begin
     95        if X2 > X1 then dec(X2) else inc(X2);
     96      end;
     97      dest.HorizLine(X1,Y1,X2,c, ADrawMode);
     98    end;
     99    Exit;
     100  end else
     101  if (X1 = X2) then
     102  begin
     103    if not DrawLastPixel then
     104    begin
     105      if Y2 > Y1 then dec(Y2) else inc(Y2);
     106    end;
     107    dest.VertLine(X1,Y1,Y2,c, ADrawMode);
     108  end;
     109
     110  DX := X2 - X1;
     111  DY := Y2 - Y1;
     112
     113  if (ADrawMode = dmSetExceptTransparent) and (c.alpha <> 255) then exit else
     114  if c.alpha = 0 then
     115  begin
     116    if ADrawMode in[dmDrawWithTransparency,dmLinearBlend] then exit;
     117    if (ADrawMode = dmXor) and (DWord(c)=0) then exit;
     118  end;
     119  case ADrawMode of
     120  dmDrawWithTransparency: PixelProc := @dest.DrawPixel;
     121  dmXor: PixelProc := @dest.XorPixel;
     122  dmLinearBlend: PixelProc := @dest.FastBlendPixel;
     123  else
     124    PixelProc := @dest.SetPixel;
     125  end;
     126
     127  if DX < 0 then
     128  begin
     129    SX := -1;
     130    DX := -DX;
     131  end
     132  else
     133    SX := 1;
     134
     135  if DY < 0 then
     136  begin
     137    SY := -1;
     138    DY := -DY;
     139  end
     140  else
     141    SY := 1;
     142
     143  DX := DX shl 1;
     144  DY := DY shl 1;
     145
     146  X := X1;
     147  Y := Y1;
     148  if DX > DY then
     149  begin
     150    E := DY - DX shr 1;
     151
     152    while X <> X2 do
     153    begin
     154      PixelProc(X, Y, c);
     155      if E >= 0 then
     156      begin
     157        Inc(Y, SY);
     158        Dec(E, DX);
     159      end;
     160      Inc(X, SX);
     161      Inc(E, DY);
     162    end;
     163  end
     164  else
     165  begin
     166    E := DX - DY shr 1;
     167
     168    while Y <> Y2 do
     169    begin
     170      PixelProc(X, Y, c);
     171      if E >= 0 then
     172      begin
     173        Inc(X, SX);
     174        Dec(E, DY);
     175      end;
     176      Inc(Y, SY);
     177      Inc(E, DX);
     178    end;
     179  end;
     180
     181  if DrawLastPixel then
     182    PixelProc(X2, Y2, c);
     183end;
     184
     185procedure BGRAEraseLineAliased(dest: TBGRACustomBitmap; x1, y1, x2,
     186  y2: integer; alpha: byte; DrawLastPixel: boolean);
    77187var
    78188  Y, X: integer;
     
    83193  begin
    84194    if DrawLastPixel then
    85       dest.DrawPixel(X1, Y1, c);
     195      dest.ErasePixel(X1, Y1, alpha);
    86196    Exit;
    87197  end;
     
    117227    while X <> X2 do
    118228    begin
    119       dest.DrawPixel(X, Y, c);
     229      dest.ErasePixel(X, Y, alpha);
    120230      if E >= 0 then
    121231      begin
     
    133243    while Y <> Y2 do
    134244    begin
    135       dest.DrawPixel(X, Y, c);
     245      dest.ErasePixel(X, Y, alpha);
    136246      if E >= 0 then
    137247      begin
     
    145255
    146256  if DrawLastPixel then
    147     dest.DrawPixel(X2, Y2, c);
     257    dest.ErasePixel(X2, Y2, alpha);
    148258end;
    149259
    150 procedure BGRAEraseLineAliased(dest: TBGRACustomBitmap; x1, y1, x2,
    151   y2: integer; alpha: byte; DrawLastPixel: boolean);
    152 var
    153   Y, X: integer;
     260procedure BGRADrawLineAntialias(dest: TBGRACustomBitmap; x1, y1, x2, y2: integer;
     261  c: TBGRAPixel; DrawLastPixel: boolean; LinearBlend : boolean);
     262var
     263  Y, X:  integer;
    154264  DX, DY, SX, SY, E: integer;
     265  alpha: NativeUInt;
     266  pixelproc: procedure(x,y: int32or64; c: TBGRAPixel) of object;
    155267begin
     268  if LinearBlend then
     269    pixelproc := @dest.FastBlendPixel
     270  else
     271    pixelproc := @dest.DrawPixel;
    156272
    157273  if (Y1 = Y2) and (X1 = X2) then
    158274  begin
    159275    if DrawLastPixel then
    160       dest.ErasePixel(X1, Y1, alpha);
     276      pixelproc(X1, Y1, c);
    161277    Exit;
    162278  end;
     
    186302  X := X1;
    187303  Y := Y1;
     304
    188305  if DX > DY then
    189306  begin
    190     E := DY - DX shr 1;
     307    E := 0;
    191308
    192309    while X <> X2 do
    193310    begin
    194       dest.ErasePixel(X, Y, alpha);
    195       if E >= 0 then
     311      alpha := c.alpha * E div DX;
     312      pixelproc(X, Y, BGRA(c.red, c.green, c.blue, c.alpha - alpha));
     313      pixelproc(X, Y + SY, BGRA(c.red, c.green, c.blue, alpha));
     314      Inc(E, DY);
     315      if E >= DX then
    196316      begin
    197317        Inc(Y, SY);
     
    199319      end;
    200320      Inc(X, SX);
    201       Inc(E, DY);
    202321    end;
    203322  end
    204323  else
    205324  begin
    206     E := DX - DY shr 1;
     325    E := 0;
    207326
    208327    while Y <> Y2 do
    209328    begin
    210       dest.ErasePixel(X, Y, alpha);
    211       if E >= 0 then
     329      alpha := c.alpha * E div DY;
     330      pixelproc(X, Y, BGRA(c.red, c.green, c.blue, c.alpha - alpha));
     331      pixelproc(X + SX, Y, BGRA(c.red, c.green, c.blue, alpha));
     332      Inc(E, DX);
     333      if E >= DY then
    212334      begin
    213335        Inc(X, SX);
     
    215337      end;
    216338      Inc(Y, SY);
    217       Inc(E, DX);
    218     end;
    219   end;
    220 
     339    end;
     340  end;
    221341  if DrawLastPixel then
    222     dest.ErasePixel(X2, Y2, alpha);
     342    pixelproc(X2, Y2, c);
    223343end;
    224344
    225 procedure BGRADrawLineAntialias(dest: TBGRACustomBitmap; x1, y1, x2, y2: integer;
    226   c: TBGRAPixel; DrawLastPixel: boolean);
     345procedure BGRAEraseLineAntialias(dest: TBGRACustomBitmap; x1, y1, x2,
     346  y2: integer; calpha: byte; DrawLastPixel: boolean);
    227347var
    228348  Y, X:  integer;
    229349  DX, DY, SX, SY, E: integer;
    230   alpha: single;
     350  alpha: NativeUInt;
    231351begin
    232352
     
    234354  begin
    235355    if DrawLastPixel then
    236       dest.DrawPixel(X1, Y1, c);
     356      dest.ErasePixel(X1, Y1, calpha);
    237357    Exit;
    238358  end;
     
    269389    while X <> X2 do
    270390    begin
    271       alpha := 1 - E / DX;
    272       dest.DrawPixel(X, Y, BGRA(c.red, c.green, c.blue, round(c.alpha * sqrt(alpha))));
    273       dest.DrawPixel(X, Y + SY, BGRA(c.red, c.green, c.blue,
    274         round(c.alpha * sqrt(1 - alpha))));
     391      alpha := calpha * E div DX;
     392      dest.ErasePixel(X, Y, calpha - alpha);
     393      dest.ErasePixel(X, Y + SY, alpha);
    275394      Inc(E, DY);
    276395      if E >= DX then
     
    288407    while Y <> Y2 do
    289408    begin
    290       alpha := 1 - E / DY;
    291       dest.DrawPixel(X, Y, BGRA(c.red, c.green, c.blue, round(c.alpha * sqrt(alpha))));
    292       dest.DrawPixel(X + SX, Y, BGRA(c.red, c.green, c.blue,
    293         round(c.alpha * sqrt(1 - alpha))));
    294       Inc(E, DX);
    295       if E >= DY then
    296       begin
    297         Inc(X, SX);
    298         Dec(E, DY);
    299       end;
    300       Inc(Y, SY);
    301     end;
    302   end;
    303   if DrawLastPixel then
    304     dest.DrawPixel(X2, Y2, c);
    305 end;
    306 
    307 procedure BGRAEraseLineAntialias(dest: TBGRACustomBitmap; x1, y1, x2,
    308   y2: integer; calpha: byte; DrawLastPixel: boolean);
    309 var
    310   Y, X:  integer;
    311   DX, DY, SX, SY, E: integer;
    312   alpha: single;
    313 begin
    314 
    315   if (Y1 = Y2) and (X1 = X2) then
    316   begin
    317     if DrawLastPixel then
    318       dest.ErasePixel(X1, Y1, calpha);
    319     Exit;
    320   end;
    321 
    322   DX := X2 - X1;
    323   DY := Y2 - Y1;
    324 
    325   if DX < 0 then
    326   begin
    327     SX := -1;
    328     DX := -DX;
    329   end
    330   else
    331     SX := 1;
    332 
    333   if DY < 0 then
    334   begin
    335     SY := -1;
    336     DY := -DY;
    337   end
    338   else
    339     SY := 1;
    340 
    341   DX := DX shl 1;
    342   DY := DY shl 1;
    343 
    344   X := X1;
    345   Y := Y1;
    346 
    347   if DX > DY then
    348   begin
    349     E := 0;
    350 
    351     while X <> X2 do
    352     begin
    353       alpha := 1 - E / DX;
    354       dest.ErasePixel(X, Y, round(calpha * sqrt(alpha)));
    355       dest.ErasePixel(X, Y + SY, round(calpha * sqrt(1 - alpha)));
    356       Inc(E, DY);
    357       if E >= DX then
    358       begin
    359         Inc(Y, SY);
    360         Dec(E, DX);
    361       end;
    362       Inc(X, SX);
    363     end;
    364   end
    365   else
    366   begin
    367     E := 0;
    368 
    369     while Y <> Y2 do
    370     begin
    371       alpha := 1 - E / DY;
    372       dest.ErasePixel(X, Y, round(calpha * sqrt(alpha)));
    373       dest.ErasePixel(X + SX, Y, round(calpha * sqrt(1 - alpha)));
     409      alpha := calpha * E div DY;
     410      dest.ErasePixel(X, Y, calpha - alpha);
     411      dest.ErasePixel(X + SX, Y, alpha);
    374412      Inc(E, DX);
    375413      if E >= DY then
     
    386424
    387425procedure BGRADrawLineAntialias(dest: TBGRACustomBitmap; x1, y1, x2, y2: integer;
    388   c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean; var DashPos: integer);
     426  c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean; var DashPos: integer; LinearBlend : boolean);
    389427var
    390428  Y, X:  integer;
    391429  DX, DY, SX, SY, E: integer;
    392   alpha: single;
     430  alpha: NativeUInt;
    393431  c:     TBGRAPixel;
    394432begin
     
    396434  if DashLen <= 0 then
    397435  begin
    398     BGRADrawLineAntialias(dest,x1,y1,x2,y2,MergeBGRA(c1,c2),DrawLastPixel);
     436    BGRADrawLineAntialias(dest,x1,y1,x2,y2,MergeBGRA(c1,c2),DrawLastPixel,LinearBlend);
    399437    exit;
    400438  end;
     
    441479    while X <> X2 do
    442480    begin
    443       alpha := 1 - E / DX;
    444       dest.DrawPixel(X, Y, BGRA(c.red, c.green, c.blue, round(c.alpha * sqrt(alpha))));
    445       dest.DrawPixel(X, Y + SY, BGRA(c.red, c.green, c.blue,
    446         round(c.alpha * sqrt(1 - alpha))));
     481      alpha := c.alpha * E div DX;
     482      dest.DrawPixel(X, Y, BGRA(c.red, c.green, c.blue, c.alpha - alpha));
     483      dest.DrawPixel(X, Y + SY, BGRA(c.red, c.green, c.blue, alpha));
    447484      Inc(E, DY);
    448485      if E >= DX then
     
    470507    while Y <> Y2 do
    471508    begin
    472       alpha := 1 - E / DY;
    473       dest.DrawPixel(X, Y, BGRA(c.red, c.green, c.blue, round(c.alpha * sqrt(alpha))));
    474       dest.DrawPixel(X + SX, Y, BGRA(c.red, c.green, c.blue,
    475         round(c.alpha * sqrt(1 - alpha))));
     509      alpha := c.alpha * E div DY;
     510      dest.DrawPixel(X, Y, BGRA(c.red, c.green, c.blue, c.alpha - alpha));
     511      dest.DrawPixel(X + SX, Y, BGRA(c.red, c.green, c.blue, alpha));
    476512      Inc(E, DX);
    477513      if E >= DY then
     
    592628  procedure AddPt(pt: TPointF);
    593629  begin
    594     if nbStyled = length(styledPts) then
    595       setlength(styledPts,nbStyled*2+4);
    596     styledPts[nbStyled] := pt;
    597     inc(nbStyled);
     630    if (nbStyled = 0) or (pt <> styledPts[nbStyled-1]) then
     631    begin
     632      if nbStyled = length(styledPts) then
     633        setlength(styledPts,nbStyled*2+4);
     634      styledPts[nbStyled] := pt;
     635      inc(nbStyled);
     636    end;
    598637  end;
    599638
     
    708747procedure BGRAPolyLine(bmp: TBGRACustomBitmap; const linepts: array of TPointF; width: single;
    709748          pencolor: TBGRAPixel; linecap: TPenEndCap; joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle;
    710           options: TBGRAPolyLineOptions; scan: IBGRAScanner; miterLimit: single);
     749          options: TBGRAPolyLineOptions; scan: IBGRAScanner; miterLimit: single; arrowStart: TComputeArrowHeadProc; arrowStartPos: single; arrowEnd: TComputeArrowHeadProc; arrowEndPos: single);
    711750var
    712751  widePolylinePoints: ArrayOfTPointF;
    713752begin
    714   widePolylinePoints := ComputeWidePolylinePoints(linepts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit);
     753  widePolylinePoints := ComputeWidePolylinePoints(linepts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit,arrowStart,arrowStartPos,arrowEnd,arrowEndPos);
    715754  if scan <> nil then
    716755    bmp.FillPolyAntialias(widePolylinePoints,scan)
     
    721760function ComputeWidePolylinePoints(const linepts: array of TPointF; width: single;
    722761          pencolor: TBGRAPixel; linecap: TPenEndCap; joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle;
    723           options: TBGRAPolyLineOptions; miterLimit: single): ArrayOfTPointF;
    724 var
     762          options: TBGRAPolyLineOptions; miterLimit: single; arrowStart: TComputeArrowHeadProc; wantedStartArrowPos: single; arrowEnd: TComputeArrowHeadProc; wantedEndArrowPos: single): ArrayOfTPointF;
     763var
     764  startArrowPos, startArrowDir, endArrowPos, endArrowDir: TPointF;
     765  startArrowLinePos, endArrowLinePos: single;
    725766  borders : array of record
    726767              leftSide,rightSide: TLineDef;
     
    883924              pts[lastPointIndex] - borders[lastPointIndex-1].leftDir);
    884925
    885     if (lastPointIndex = high(pts)) and (linecap = pecRound) then
     926    if (lastPointIndex = high(pts)) and (linecap = pecRound) and not (plNoEndCap in options) then
    886927    begin
    887928      if not (plRoundCapOpen in options) then
     
    937978      end;
    938979      FlushLine(-1);
     980    end;
     981  end;
     982
     983  procedure FinalizeArray;
     984  var arrowStartData, arrowEndData: ArrayOfTPointF;
     985    finalNb,i,delta: integer;
     986    hasStart,hasEnd: boolean;
     987  begin
     988    if assigned(arrowStart) and not isEmptyPointF(startArrowPos) then
     989      arrowStartData := arrowStart(startArrowPos, startArrowDir, width, startArrowLinePos)
     990    else
     991      arrowStartData := nil;
     992    if assigned(arrowEnd) and not isEmptyPointF(endArrowPos) then
     993      arrowEndData := arrowEnd(endArrowPos, endArrowDir, width, endArrowLinePos)
     994    else
     995      arrowEndData := nil;
     996    hasStart := length(arrowStartData)>0;
     997    hasEnd := length(arrowEndData)>0;
     998    finalNb := NbPolyAcc;
     999    if hasStart then
     1000    begin
     1001      delta := length(arrowStartData)+1;
     1002      finalNb += delta;
     1003    end else delta := 0;
     1004    if hasEnd then finalNb += length(arrowEndData)+1;
     1005    SetLength(Result, finalNb);
     1006    if hasStart then
     1007    begin
     1008      for i := NbPolyAcc-1 downto 0 do
     1009        result[i+delta] := result[i];
     1010      result[delta-1] := EmptyPointF;
     1011      for i := 0 to high(arrowStartData) do
     1012        result[i] := arrowStartData[i];
     1013    end;
     1014    if hasEnd then
     1015    begin
     1016      delta += NbPolyAcc+1;
     1017      result[delta-1] := EmptyPointF;
     1018      for i := 0 to high(arrowEndData) do
     1019        result[i+delta] := arrowEndData[i];
    9391020    end;
    9401021  end;
     
    9501031  ShouldFlushLine, HasLittleBorder, NormalRestart: Boolean;
    9511032  pt1,pt2,pt3,pt4: TPointF;
     1033  linePos: single;
     1034  startArrowDone,endArrowDone: boolean;
    9521035
    9531036begin
    9541037  Result := nil;
    9551038
    956   if length(linepts)=0 then exit;
     1039  if (length(linepts)=0) or (width = 0) then exit;
    9571040  if IsClearPenStyle(penstyle) then exit;
    9581041  for i := 0 to high(linepts) do
     
    9651048  if (plAutoCycle in options) and (length(linepts) >= 2) and (linepts[0]=linepts[high(linepts)]) then
    9661049    options := options + [plCycle];
     1050  if plNoEndCap in options then options := options - [plRoundCapOpen];
    9671051
    9681052  hw := width / 2;
     
    10061090    exit;
    10071091  end;
     1092
     1093  startArrowDir := EmptyPointF;
     1094  startArrowPos := EmptyPointF;
     1095  endArrowDir := EmptyPointF;
     1096  endArrowPos := EmptyPointF;
     1097  startArrowDone := @arrowStart = nil;
     1098  endArrowDone := @arrowEnd = nil;
    10081099
    10091100  //init computed points arrays
     
    10141105  NbPolyAcc := 0;
    10151106
     1107  if not endArrowDone then
     1108  begin
     1109    wantedEndArrowPos:= -wantedEndArrowPos*width;
     1110    linePos := 0;
     1111    for i := high(pts) downto 1 do
     1112    begin
     1113      dir := pts[i-1]-pts[i];
     1114      len := sqrt(dir*dir);
     1115      dir *= 1/len;
     1116      if not endArrowDone and (linePos+len >= wantedEndArrowPos) then
     1117      begin
     1118        endArrowPos := pts[i];
     1119        endArrowDir := -dir;
     1120        endArrowLinePos := -linePos/width;
     1121        endArrowDone := true;
     1122        break;
     1123      end;
     1124      linePos += len;
     1125    end;
     1126  end;
     1127
     1128  wantedStartArrowPos:= -wantedStartArrowPos*width;
     1129  linePos := 0;
    10161130  //compute borders
    10171131  setlength(borders, length(pts)-1);
     
    10211135    len := sqrt(dir*dir);
    10221136    dir *= 1/len;
    1023 
    1024     if (linecap = pecSquare) and ((i=0) or (i=high(pts)-1)) then //for square cap, just start and end further
     1137    if not startArrowDone and (linePos+len >= wantedStartArrowPos) then
     1138    begin
     1139      startArrowPos := pts[i];
     1140      startArrowDir := -dir;
     1141      startArrowLinePos := -linePos/width;
     1142      startArrowDone := true;
     1143    end;
     1144    if (linecap = pecSquare) and ((not (plNoStartCap in options) and (i=0)) or
     1145      (not (plNoEndCap in options) and (i=high(pts)-1))) then //for square cap, just start and end further
    10251146    begin
    10261147      if i=0 then
     
    10351156      dir *= 1/len;
    10361157    end else
    1037     if (linecap = pecRound) and (i=0) and not (plCycle in options) then
     1158    if not (plNoStartCap in options) and (linecap = pecRound) and (i=0) and not (plCycle in options) then
    10381159      AddRoundCap(pts[0], -dir ,true);
    10391160
     
    10441165    borders[i].rightSide.origin := pts[i] - borders[i].leftDir;
    10451166    borders[i].rightSide.dir := dir;
     1167    linePos += len;
    10461168  end;
    10471169
     
    12831405    FlushLine(high(pts));
    12841406
    1285   SetLength(Result, NbPolyAcc);
     1407  FinalizeArray;
    12861408end;
    12871409
     
    12891411  width: single; pencolor: TBGRAPixel; linecap: TPenEndCap;
    12901412  joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle;
    1291   options: TBGRAPolyLineOptions; miterLimit: single): ArrayOfTPointF;
     1413  options: TBGRAPolyLineOptions; miterLimit: single; arrowStart: TComputeArrowHeadProc; arrowStartPos: single; arrowEnd: TComputeArrowHeadProc; arrowEndPos: single): ArrayOfTPointF;
    12921414
    12931415var
     
    13061428      for j := startIndex to endIndexP1-1 do
    13071429        subPts[j-startIndex] := linepts[j];
    1308       tempWidePolyline := ComputeWidePolylinePoints(subPts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit);
     1430      tempWidePolyline := ComputeWidePolylinePoints(subPts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit,arrowStart,arrowStartPos,arrowEnd,arrowEndPos);
    13091431      if length(results) = nbresults then
    13101432        setlength(results,(nbresults+1)*2);
  • GraphicTest/Packages/bgrabitmap/bgrapolygon.pas

    r452 r472  
    55{ This unit contains polygon drawing functions and spline functions.
    66
    7   Shapes are drawn using a TFillShapeInfo object, which calculates the
     7  Shapes are drawn using a TBGRACustomFillInfo object, which calculates the
    88  intersection of an horizontal line and the polygon.
    99
    1010  Various shapes are handled :
    11   - TFillPolyInfo : polygon
     11  - TFillPolyInfo : polygon scanned in any order
     12  - TOnePassFillPolyInfo : polygon scanned from top to bottom
    1213  - TFillEllipseInfo : ellipse
    1314  - TFillBorderEllipseInfo : ellipse border
     
    3435  Classes, SysUtils, Graphics, BGRABitmapTypes, BGRAFillInfo;
    3536
    36 procedure FillShapeAntialias(bmp: TBGRACustomBitmap; shapeInfo: TFillShapeInfo;
    37   c: TBGRAPixel; EraseMode: boolean; scan: IBGRAScanner; NonZeroWinding: boolean);
    38 procedure FillShapeAntialiasWithTexture(bmp: TBGRACustomBitmap; shapeInfo: TFillShapeInfo;
    39   scan: IBGRAScanner; NonZeroWinding: boolean);
    40 procedure FillShapeAliased(bmp: TBGRACustomBitmap; shapeInfo: TFillShapeInfo;
     37procedure FillShapeAntialias(bmp: TBGRACustomBitmap; shapeInfo: TBGRACustomFillInfo;
     38  c: TBGRAPixel; EraseMode: boolean; scan: IBGRAScanner; NonZeroWinding: boolean; LinearBlend: boolean = false);
     39procedure FillShapeAntialiasWithTexture(bmp: TBGRACustomBitmap; shapeInfo: TBGRACustomFillInfo;
     40  scan: IBGRAScanner; NonZeroWinding: boolean; LinearBlend: boolean = false);
     41procedure FillShapeAliased(bmp: TBGRACustomBitmap; shapeInfo: TBGRACustomFillInfo;
    4142  c: TBGRAPixel; EraseMode: boolean; scan: IBGRAScanner; NonZeroWinding: boolean; drawmode: TDrawMode; AliasingIncludeBottomRight: Boolean= false);
    4243
     
    4950    nbShapes: integer;
    5051    shapes: array of record
    51         info: TFillShapeInfo;
     52        info: TBGRACustomFillInfo;
    5253        internalInfo: boolean;
    5354        texture: IBGRAScanner;
     
    5657        bounds: TRect;
    5758      end;
    58     procedure AddShape(AInfo: TFillShapeInfo; AInternalInfo: boolean; ATexture: IBGRAScanner; AInternalTexture: TObject; AColor: TBGRAPixel);
     59    procedure AddShape(AInfo: TBGRACustomFillInfo; AInternalInfo: boolean; ATexture: IBGRAScanner; AInternalTexture: TObject; AColor: TBGRAPixel);
    5960    function CheckRectangleBorderBounds(var x1, y1, x2, y2: single; w: single): boolean;
    6061  public
     
    6566    constructor Create;
    6667    destructor Destroy; override;
    67     procedure AddShape(AShape: TFillShapeInfo; AColor: TBGRAPixel);
    68     procedure AddShape(AShape: TFillShapeInfo; ATexture: IBGRAScanner);
     68    procedure AddShape(AShape: TBGRACustomFillInfo; AColor: TBGRAPixel);
     69    procedure AddShape(AShape: TBGRACustomFillInfo; ATexture: IBGRAScanner);
    6970    procedure AddPolygon(const points: array of TPointF; AColor: TBGRAPixel);
    7071    procedure AddPolygon(const points: array of TPointF; ATexture: IBGRAScanner);
     
    8687    procedure AddRectangleBorder(x1, y1, x2, y2, w: single; AColor: TBGRAPixel);
    8788    procedure AddRectangleBorder(x1, y1, x2, y2, w: single; ATexture: IBGRAScanner);
    88     procedure Draw(dest: TBGRACustomBitmap);
     89    procedure Draw(dest: TBGRACustomBitmap; ADrawMode: TDrawMode = dmDrawWithTransparency);
    8990  end;
    9091
     
    9495  scan: IBGRAScanner; NonZeroWinding: boolean; drawmode: TDrawMode);
    9596procedure FillPolyAntialias(bmp: TBGRACustomBitmap; points: array of TPointF;
    96   c: TBGRAPixel; EraseMode: boolean; NonZeroWinding: boolean);
     97  c: TBGRAPixel; EraseMode: boolean; NonZeroWinding: boolean; LinearBlend: boolean = false);
    9798procedure FillPolyAntialiasWithTexture(bmp: TBGRACustomBitmap; points: array of TPointF;
    98   scan: IBGRAScanner; NonZeroWinding: boolean);
     99  scan: IBGRAScanner; NonZeroWinding: boolean; LinearBlend: boolean = false);
    99100
    100101procedure FillEllipseAntialias(bmp: TBGRACustomBitmap; x, y, rx, ry: single;
    101   c: TBGRAPixel; EraseMode: boolean);
     102  c: TBGRAPixel; EraseMode: boolean; LinearBlend: boolean = false);
    102103procedure FillEllipseAntialiasWithTexture(bmp: TBGRACustomBitmap; x, y, rx, ry: single;
    103   scan: IBGRAScanner);
     104  scan: IBGRAScanner; LinearBlend: boolean = false);
    104105
    105106procedure BorderEllipseAntialias(bmp: TBGRACustomBitmap; x, y, rx, ry, w: single;
    106   c: TBGRAPixel; EraseMode: boolean);
     107  c: TBGRAPixel; EraseMode: boolean; LinearBlend: boolean = false);
    107108procedure BorderEllipseAntialiasWithTexture(bmp: TBGRACustomBitmap; x, y, rx, ry, w: single;
    108   scan: IBGRAScanner);
     109  scan: IBGRAScanner; LinearBlend: boolean = false);
    109110
    110111procedure FillRoundRectangleAntialias(bmp: TBGRACustomBitmap; x1, y1, x2, y2, rx, ry: single;
    111   options: TRoundRectangleOptions; c: TBGRAPixel; EraseMode: boolean);
     112  options: TRoundRectangleOptions; c: TBGRAPixel; EraseMode: boolean; LinearBlend: boolean = false);
    112113procedure FillRoundRectangleAntialiasWithTexture(bmp: TBGRACustomBitmap; x1, y1, x2, y2, rx, ry: single;
    113   options: TRoundRectangleOptions; scan: IBGRAScanner);
     114  options: TRoundRectangleOptions; scan: IBGRAScanner; LinearBlend: boolean = false);
    114115
    115116procedure BorderRoundRectangleAntialias(bmp: TBGRACustomBitmap; x1, y1, x2, y2, rx, ry, w: single;
    116   options: TRoundRectangleOptions; c: TBGRAPixel; EraseMode: boolean);
     117  options: TRoundRectangleOptions; c: TBGRAPixel; EraseMode: boolean; LinearBlend: boolean = false);
    117118procedure BorderRoundRectangleAntialiasWithTexture(bmp: TBGRACustomBitmap; x1, y1, x2, y2, rx, ry, w: single;
    118   options: TRoundRectangleOptions; scan: IBGRAScanner);
     119  options: TRoundRectangleOptions; scan: IBGRAScanner; LinearBlend: boolean = false);
    119120
    120121procedure BorderAndFillRoundRectangleAntialias(bmp: TBGRACustomBitmap; x1, y1, x2, y2, rx, ry, w: single;
     
    125126uses Math, BGRABlend, BGRAGradientScanner, BGRATransform;
    126127
    127 procedure FillShapeAntialias(bmp: TBGRACustomBitmap; shapeInfo: TFillShapeInfo;
    128   c: TBGRAPixel; EraseMode: boolean; scan: IBGRAScanner; NonZeroWinding: boolean);
     128procedure FillShapeAntialias(bmp: TBGRACustomBitmap; shapeInfo: TBGRACustomFillInfo;
     129  c: TBGRAPixel; EraseMode: boolean; scan: IBGRAScanner; NonZeroWinding: boolean; LinearBlend: boolean);
    129130var
    130131  inter:   array of TIntersectionInfo;
     
    141142  density: PDensity;
    142143
    143   xb, yb, yc, i, j: integer;
     144  xb, yb, yc, i: integer;
     145  tempDensity: UInt32or64;
    144146
    145147  x1, x2, x1b,x2b: single;
     
    170172      curdens: single;
    171173      pdens: pdensity;
     174      newvalue: Int32or64;
    172175  begin
    173176    if (x1 <> x2) and (x1 < maxx + 1) and (x2 >= minx) then
     
    188191
    189192      if ix1 = ix2 then
    190         (density + (ix1 - minx))^ -= round((x2 - x1)*(density1+density2)/2)
     193      begin
     194        newValue := (density + (ix1 - minx))^ - round((x2 - x1)*(density1+density2)/2);
     195        if newValue < 0 then newValue := 0;
     196        if newValue > 256 then newValue := 256;
     197        (density + (ix1 - minx))^ := newValue
     198      end
    191199      else
    192200      begin
    193         (density + (ix1 - minx))^ := max(0, (density + (ix1 - minx))^ - round((1 - (x1 - ix1))*(density1+densityAt(ix1+1))/2) );
     201        newValue := (density + (ix1 - minx))^ - round((1 - (x1 - ix1))*(density1+densityAt(ix1+1))/2) ;
     202        if newValue < 0 then newValue := 0;
     203        if newValue > 256 then newValue := 256;
     204        (density + (ix1 - minx))^ := newValue;
    194205        if (ix2 <= maxx) then
    195           (density + (ix2 - minx))^ := max(0, (density + (ix2 - minx))^ - round((x2 - ix2)*(density2+densityAt(ix2))/2) );
     206        begin
     207          newValue := (density + (ix2 - minx))^ - round((x2 - ix2)*(density2+densityAt(ix2))/2);
     208          if newValue < 0 then newValue := 0;
     209          if newValue > 256 then newValue := 256;
     210          (density + (ix2 - minx))^ := newValue;
     211        end;
    196212      end;
    197213      if ix2 > ix1 + 1 then
     
    201217        for n := ix2-1-(ix1+1) downto 0 do
    202218        begin
    203           pdens^ -= round(curdens);
     219          newValue := pdens^ - round(curdens);
     220          if newValue < 0 then newValue := 0;
     221          if newValue > 256 then newValue := 256;
     222          pdens^ := newValue;
    204223          curdens += slope;
    205224          inc(pdens);
     
    308327    end;
    309328
    310     if optimised then
    311       {$i renderdensity256.inc}
    312     else
    313       {$define PARAM_ANTIALIASINGFACTOR}
    314       {$i renderdensity256.inc}
     329    if LinearBlend then
     330    begin
     331      if optimised then
     332        {$define PARAM_LINEARANTIALIASING}
     333        {$i renderdensity256.inc}
     334      else
     335        {$define PARAM_LINEARANTIALIASING}
     336        {$define PARAM_ANTIALIASINGFACTOR}
     337        {$i renderdensity256.inc}
     338    end else
     339    begin
     340      if optimised then
     341        {$i renderdensity256.inc}
     342      else
     343        {$define PARAM_ANTIALIASINGFACTOR}
     344        {$i renderdensity256.inc}
     345    end;
    315346  end;
    316347
     
    336367end;
    337368
    338 procedure FillShapeAliased(bmp: TBGRACustomBitmap; shapeInfo: TFillShapeInfo;
     369procedure FillShapeAliased(bmp: TBGRACustomBitmap; shapeInfo: TBGRACustomFillInfo;
    339370  c: TBGRAPixel; EraseMode: boolean; scan: IBGRAScanner; NonZeroWinding: boolean; drawmode: TDrawMode; AliasingIncludeBottomRight: Boolean= false);
    340371var
     
    370401    for i := 0 to nbinter div 2 - 1 do
    371402    begin
    372       x1 := inter[i + i].interX-AliasingOfs.X;
    373       x2 := inter[i + i+ 1].interX-AliasingOfs.X;
     403      x1 := inter[i + i].interX+AliasingOfs.X;
     404      x2 := inter[i + i+ 1].interX+AliasingOfs.X;
    374405
    375406      if x1 <> x2 then
     
    413444
    414445procedure FillShapeAntialiasWithTexture(bmp: TBGRACustomBitmap;
    415   shapeInfo: TFillShapeInfo; scan: IBGRAScanner; NonZeroWinding: boolean);
    416 begin
    417   FillShapeAntialias(bmp,shapeInfo,BGRAPixelTransparent,False,scan,NonZeroWinding);
     446  shapeInfo: TBGRACustomFillInfo; scan: IBGRAScanner; NonZeroWinding: boolean; LinearBlend: boolean);
     447begin
     448  FillShapeAntialias(bmp,shapeInfo,BGRAPixelTransparent,False,scan,NonZeroWinding,LinearBlend);
    418449end;
    419450
     
    421452  c: TBGRAPixel; EraseMode: boolean; NonZeroWinding: boolean; drawmode: TDrawMode);
    422453var
    423   info: TFillPolyInfo;
     454  info: TCustomFillPolyInfo;
    424455begin
    425456  if length(points) < 3 then
    426457    exit;
    427458
    428   info := TFillPolyInfo.Create(points);
     459  info := TOnePassFillPolyInfo.Create(points);
    429460  FillShapeAliased(bmp, info, c, EraseMode, nil, NonZeroWinding, drawmode);
    430461  info.Free;
     
    434465  points: array of TPointF; scan: IBGRAScanner; NonZeroWinding: boolean; drawmode: TDrawMode);
    435466var
    436   info: TFillPolyInfo;
     467  info: TCustomFillPolyInfo;
    437468begin
    438469  if length(points) < 3 then
    439470    exit;
    440471
    441   info := TFillPolyInfo.Create(points);
     472  info := TOnePassFillPolyInfo.Create(points);
    442473  FillShapeAliased(bmp, info, BGRAPixelTransparent,False,scan, NonZeroWinding, drawmode);
    443474  info.Free;
     
    445476
    446477procedure FillPolyAntialias(bmp: TBGRACustomBitmap; points: array of TPointF;
    447   c: TBGRAPixel; EraseMode: boolean; NonZeroWinding: boolean);
    448 var
    449   info: TFillPolyInfo;
     478  c: TBGRAPixel; EraseMode: boolean; NonZeroWinding: boolean; LinearBlend: boolean);
     479var
     480  info: TCustomFillPolyInfo;
    450481begin
    451482  if length(points) < 3 then
    452483    exit;
    453484
    454   info := TFillPolyInfo.Create(points);
    455   FillShapeAntialias(bmp, info, c, EraseMode, nil, NonZeroWinding);
     485  info := TOnePassFillPolyInfo.Create(points);
     486  FillShapeAntialias(bmp, info, c, EraseMode, nil, NonZeroWinding, LinearBlend);
    456487  info.Free;
    457488end;
    458489
    459490procedure FillPolyAntialiasWithTexture(bmp: TBGRACustomBitmap;
    460   points: array of TPointF; scan: IBGRAScanner; NonZeroWinding: boolean
    461   );
    462 var
    463   info: TFillPolyInfo;
     491  points: array of TPointF; scan: IBGRAScanner; NonZeroWinding: boolean; LinearBlend: boolean);
     492var
     493  info: TCustomFillPolyInfo;
    464494begin
    465495  if length(points) < 3 then
    466496    exit;
    467497
    468   info := TFillPolyInfo.Create(points);
    469   FillShapeAntialiasWithTexture(bmp, info, scan, NonZeroWinding);
     498  info := TOnePassFillPolyInfo.Create(points);
     499  FillShapeAntialiasWithTexture(bmp, info, scan, NonZeroWinding, LinearBlend);
    470500  info.Free;
    471501end;
    472502
    473503procedure FillEllipseAntialias(bmp: TBGRACustomBitmap; x, y, rx, ry: single;
    474   c: TBGRAPixel; EraseMode: boolean);
     504  c: TBGRAPixel; EraseMode: boolean; LinearBlend: boolean);
    475505var
    476506  info: TFillEllipseInfo;
     
    480510
    481511  info := TFillEllipseInfo.Create(x, y, rx, ry);
    482   FillShapeAntialias(bmp, info, c, EraseMode, nil, False);
     512  FillShapeAntialias(bmp, info, c, EraseMode, nil, False, LinearBlend);
    483513  info.Free;
    484514end;
    485515
    486516procedure FillEllipseAntialiasWithTexture(bmp: TBGRACustomBitmap; x, y, rx,
    487   ry: single; scan: IBGRAScanner);
     517  ry: single; scan: IBGRAScanner; LinearBlend: boolean);
    488518var
    489519  info: TFillEllipseInfo;
     
    493523
    494524  info := TFillEllipseInfo.Create(x, y, rx, ry);
    495   FillShapeAntialiasWithTexture(bmp, info, scan, False);
     525  FillShapeAntialiasWithTexture(bmp, info, scan, False, LinearBlend);
    496526  info.Free;
    497527end;
    498528
    499529procedure BorderEllipseAntialias(bmp: TBGRACustomBitmap; x, y, rx, ry, w: single;
    500   c: TBGRAPixel; EraseMode: boolean);
     530  c: TBGRAPixel; EraseMode: boolean; LinearBlend: boolean);
    501531var
    502532  info: TFillBorderEllipseInfo;
     
    505535    exit;
    506536  info := TFillBorderEllipseInfo.Create(x, y, rx, ry, w);
    507   FillShapeAntialias(bmp, info, c, EraseMode, nil, False);
     537  FillShapeAntialias(bmp, info, c, EraseMode, nil, False, LinearBlend);
    508538  info.Free;
    509539end;
    510540
    511541procedure BorderEllipseAntialiasWithTexture(bmp: TBGRACustomBitmap; x, y, rx,
    512   ry, w: single; scan: IBGRAScanner);
     542  ry, w: single; scan: IBGRAScanner; LinearBlend: boolean);
    513543var
    514544  info: TFillBorderEllipseInfo;
     
    517547    exit;
    518548  info := TFillBorderEllipseInfo.Create(x, y, rx, ry, w);
    519   FillShapeAntialiasWithTexture(bmp, info, scan, False);
     549  FillShapeAntialiasWithTexture(bmp, info, scan, False, LinearBlend);
    520550  info.Free;
    521551end;
     
    523553{ TBGRAMultishapeFiller }
    524554
    525 procedure TBGRAMultishapeFiller.AddShape(AInfo: TFillShapeInfo; AInternalInfo: boolean; ATexture: IBGRAScanner; AInternalTexture: TObject; AColor: TBGRAPixel);
     555procedure TBGRAMultishapeFiller.AddShape(AInfo: TBGRACustomFillInfo; AInternalInfo: boolean; ATexture: IBGRAScanner; AInternalTexture: TObject; AColor: TBGRAPixel);
    526556begin
    527557  if length(shapes) = nbShapes then
     
    580610end;
    581611
    582 procedure TBGRAMultishapeFiller.AddShape(AShape: TFillShapeInfo; AColor: TBGRAPixel);
     612procedure TBGRAMultishapeFiller.AddShape(AShape: TBGRACustomFillInfo; AColor: TBGRAPixel);
    583613begin
    584614  AddShape(AShape,False,nil,nil,AColor);
    585615end;
    586616
    587 procedure TBGRAMultishapeFiller.AddShape(AShape: TFillShapeInfo;
     617procedure TBGRAMultishapeFiller.AddShape(AShape: TBGRACustomFillInfo;
    588618  ATexture: IBGRAScanner);
    589619begin
     
    595625begin
    596626  if length(points) <= 2 then exit;
    597   AddShape(TFillPolyInfo.Create(points),True,nil,nil,AColor);
     627  AddShape(TOnePassFillPolyInfo.Create(points),True,nil,nil,AColor);
    598628end;
    599629
     
    602632begin
    603633  if length(points) <= 2 then exit;
    604   AddShape(TFillPolyInfo.Create(points),True,ATexture,nil,BGRAPixelTransparent);
     634  AddShape(TOnePassFillPolyInfo.Create(points),True,ATexture,nil,BGRAPixelTransparent);
    605635end;
    606636
     
    611641begin
    612642  grad := TBGRAGradientTriangleScanner.Create(pt1,pt2,pt3, c1,c2,c3);
    613   AddShape(TFillPolyInfo.Create([pt1,pt2,pt3]),True,grad,grad,BGRAPixelTransparent);
     643  AddShape(TOnePassFillPolyInfo.Create([pt1,pt2,pt3]),True,grad,grad,BGRAPixelTransparent);
    614644end;
    615645
     
    620650begin
    621651  mapping := TBGRATriangleLinearMapping.Create(texture, pt1,pt2,pt3, tex1, tex2, tex3);
    622   AddShape(TFillPolyInfo.Create([pt1,pt2,pt3]),True,mapping,mapping,BGRAPixelTransparent);
     652  AddShape(TOnePassFillPolyInfo.Create([pt1,pt2,pt3]),True,mapping,mapping,BGRAPixelTransparent);
    623653end;
    624654
     
    657687begin
    658688  persp := TBGRAPerspectiveScannerTransform.Create(texture,[tex1,tex2,tex3,tex4],[pt1,pt2,pt3,pt4]);
    659   AddShape(TFillPolyInfo.Create([pt1,pt2,pt3,pt4]),True,persp,persp,BGRAPixelTransparent);
     689  AddShape(TOnePassFillPolyInfo.Create([pt1,pt2,pt3,pt4]),True,persp,persp,BGRAPixelTransparent);
    660690end;
    661691
     
    745775end;
    746776
    747 procedure TBGRAMultishapeFiller.Draw(dest: TBGRACustomBitmap);
     777procedure TBGRAMultishapeFiller.Draw(dest: TBGRACustomBitmap; ADrawMode: TDrawMode = dmDrawWithTransparency);
    748778var
    749779  shapeRow: array of record
     
    767797      procedure AddSegment(xa,xb: single);
    768798      var nb: PInteger;
    769           prevNb,k: integer;
    770799      begin
    771800        nb := @shapeRow[dest].nbinter;
    772801        if length(shapeRow[dest].inter) < nb^+2 then
     802          setlength(shapeRow[dest].inter, nb^*2+2);
     803        with shapeRow[dest] do
    773804        begin
    774           prevNb := length(shapeRow[dest].inter);
    775           setlength(shapeRow[dest].inter, nb^*2+2);
    776           for k := prevNb to high(shapeRow[dest].inter) do
    777             shapeRow[dest].inter[k] := shapes[dest].info.CreateIntersectionInfo;
     805          if inter[nb^] = nil then inter[nb^] := shapes[dest].info.CreateIntersectionInfo;
     806          inter[nb^].interX := xa;
     807          if inter[nb^+1] = nil then inter[nb^+1] := shapes[dest].info.CreateIntersectionInfo;
     808          inter[nb^+1].interX := xb;
    778809        end;
    779         shapeRow[dest].inter[nb^].interX := xa;
    780         shapeRow[dest].inter[nb^+1].interX := xb;
    781810        inc(nb^,2);
    782811      end;
     
    813842var
    814843    AliasingOfs: TPointF;
     844    useAA: boolean;
    815845
    816846  procedure AddOneLineDensity(cury: single);
     
    847877      begin
    848878        //fill density
    849         if not Antialiasing then
     879        if not useAA then
    850880        begin
    851881          for i := 0 to nbinter div 2 - 1 do
     
    895925begin
    896926  if nbShapes = 0 then exit;
     927  useAA := Antialiasing and (ADrawMode in [dmDrawWithTransparency,dmLinearBlend]);
    897928  if nbShapes = 1 then
    898929  begin
    899     if Antialiasing then
    900       FillShapeAntialias(dest,shapes[0].info,GammaCompression(shapes[0].color),False,shapes[0].texture,FillMode = fmWinding) else
    901       FillShapeAliased(dest,shapes[0].info,GammaCompression(shapes[0].color),False,shapes[0].texture,FillMode = fmWinding, dmDrawWithTransparency,
     930    if useAA then
     931      FillShapeAntialias(dest,shapes[0].info,GammaCompression(shapes[0].color),False,shapes[0].texture,FillMode = fmWinding, ADrawMode=dmLinearBlend) else
     932      FillShapeAliased(dest,shapes[0].info,GammaCompression(shapes[0].color),False,shapes[0].texture,FillMode = fmWinding, ADrawMode,
    902933        AliasingIncludeBottomRight);
    903934    exit;
     
    963994    end;
    964995
    965     If Antialiasing then
     996    If useAA then
    966997    begin
    967998      //precision scan
     
    9821013      FillChar(sums[rowminx-minx],(rowmaxx-rowminx+1)*sizeof(sums[0]),0);
    9831014
    984       if Antialiasing then
     1015      if useAA then
    9851016        {$define PARAM_ANTIALIASINGFACTOR}
    9861017        {$i multishapeline.inc}
     
    9911022      xb := rowminx;
    9921023      nextSum := @sums[xb-minx];
    993       while xb <= rowmaxx do
    994       begin
    995         curSum := nextSum;
    996         inc(nextSum);
    997         with curSum^ do
    998         begin
    999           if sumA <> 0 then
     1024      case ADrawMode of
     1025        dmDrawWithTransparency:
     1026          while xb <= rowmaxx do
    10001027          begin
    1001             ec.red := (sumR+sumA shr 1) div sumA;
    1002             ec.green := (sumG+sumA shr 1) div sumA;
    1003             ec.blue := (sumB+sumA shr 1) div sumA;
    1004             if sumA > 255 then sumA := 255;
    1005             ec.alpha := sumA shl 8 + sumA;
    1006             count := 1;
    1007             while (xb < rowmaxx) and (nextSum^.sumA = sumA) and (nextSum^.sumB = sumB)
    1008               and (nextSum^.sumG = sumG) and (nextSum^.sumR = sumR) do
     1028            curSum := nextSum;
     1029            inc(nextSum);
     1030            with curSum^ do
    10091031            begin
    1010               inc(xb);
    1011               inc(nextSum);
    1012               inc(count);
     1032              if sumA <> 0 then
     1033              begin
     1034                ec.red := (sumR+sumA shr 1) div sumA;
     1035                ec.green := (sumG+sumA shr 1) div sumA;
     1036                ec.blue := (sumB+sumA shr 1) div sumA;
     1037                if sumA > 255 then sumA := 255;
     1038                ec.alpha := sumA shl 8 + sumA;
     1039                count := 1;
     1040                while (xb < rowmaxx) and (nextSum^.sumA = sumA) and (nextSum^.sumB = sumB)
     1041                  and (nextSum^.sumG = sumG) and (nextSum^.sumR = sumR) do
     1042                begin
     1043                  inc(xb);
     1044                  inc(nextSum);
     1045                  inc(count);
     1046                end;
     1047                if count = 1 then
     1048                  DrawExpandedPixelInlineNoAlphaCheck(pdest,ec,sumA) else
     1049                   DrawExpandedPixelsInline(pdest, ec, count );
     1050                inc(pdest,count-1);
     1051              end;
    10131052            end;
    1014             if count = 1 then
    1015               DrawExpandedPixelInlineWithAlphaCheck(pdest,ec) else
    1016                DrawExpandedPixelsInline(pdest, ec, count );
    1017             inc(pdest,count-1);
     1053            inc(xb);
     1054            inc(pdest);
    10181055          end;
    1019         end;
    1020         inc(xb);
    1021         inc(pdest);
     1056
     1057        dmLinearBlend:
     1058          while xb <= rowmaxx do
     1059          begin
     1060            curSum := nextSum;
     1061            inc(nextSum);
     1062            with curSum^ do
     1063            begin
     1064              if sumA <> 0 then
     1065              begin
     1066                ec.red := (sumR+sumA shr 1) div sumA;
     1067                ec.green := (sumG+sumA shr 1) div sumA;
     1068                ec.blue := (sumB+sumA shr 1) div sumA;
     1069                if sumA > 255 then sumA := 255;
     1070                ec.alpha := sumA shl 8 + sumA;
     1071                count := 1;
     1072                while (xb < rowmaxx) and (nextSum^.sumA = sumA) and (nextSum^.sumB = sumB)
     1073                  and (nextSum^.sumG = sumG) and (nextSum^.sumR = sumR) do
     1074                begin
     1075                  inc(xb);
     1076                  inc(nextSum);
     1077                  inc(count);
     1078                end;
     1079                if count = 1 then
     1080                  DrawPixelInlineNoAlphaCheck(pdest,GammaCompression(ec)) else
     1081                   DrawPixelsInline(pdest, GammaCompression(ec), count );
     1082                inc(pdest,count-1);
     1083              end;
     1084            end;
     1085            inc(xb);
     1086            inc(pdest);
     1087          end;
     1088
     1089        dmXor:
     1090          while xb <= rowmaxx do
     1091          begin
     1092            curSum := nextSum;
     1093            inc(nextSum);
     1094            with curSum^ do
     1095            begin
     1096              if sumA <> 0 then
     1097              begin
     1098                ec.red := (sumR+sumA shr 1) div sumA;
     1099                ec.green := (sumG+sumA shr 1) div sumA;
     1100                ec.blue := (sumB+sumA shr 1) div sumA;
     1101                if sumA > 255 then sumA := 255;
     1102                ec.alpha := sumA shl 8 + sumA;
     1103                count := 1;
     1104                while (xb < rowmaxx) and (nextSum^.sumA = sumA) and (nextSum^.sumB = sumB)
     1105                  and (nextSum^.sumG = sumG) and (nextSum^.sumR = sumR) do
     1106                begin
     1107                  inc(xb);
     1108                  inc(nextSum);
     1109                  inc(count);
     1110                end;
     1111                XorInline(pdest,GammaCompression(ec),count);
     1112                inc(pdest,count-1);
     1113              end;
     1114            end;
     1115            inc(xb);
     1116            inc(pdest);
     1117          end;
     1118
     1119        dmSet:
     1120          while xb <= rowmaxx do
     1121          begin
     1122            curSum := nextSum;
     1123            inc(nextSum);
     1124            with curSum^ do
     1125            begin
     1126              if sumA <> 0 then
     1127              begin
     1128                ec.red := (sumR+sumA shr 1) div sumA;
     1129                ec.green := (sumG+sumA shr 1) div sumA;
     1130                ec.blue := (sumB+sumA shr 1) div sumA;
     1131                if sumA > 255 then sumA := 255;
     1132                ec.alpha := sumA shl 8 + sumA;
     1133                count := 1;
     1134                while (xb < rowmaxx) and (nextSum^.sumA = sumA) and (nextSum^.sumB = sumB)
     1135                  and (nextSum^.sumG = sumG) and (nextSum^.sumR = sumR) do
     1136                begin
     1137                  inc(xb);
     1138                  inc(nextSum);
     1139                  inc(count);
     1140                end;
     1141                FillInline(pdest,GammaCompression(ec),count);
     1142                inc(pdest,count-1);
     1143              end;
     1144            end;
     1145            inc(xb);
     1146            inc(pdest);
     1147          end;
     1148
     1149        dmSetExceptTransparent:
     1150          while xb <= rowmaxx do
     1151          begin
     1152            curSum := nextSum;
     1153            inc(nextSum);
     1154            with curSum^ do
     1155            begin
     1156              if sumA >= 255 then
     1157              begin
     1158                ec.red := (sumR+sumA shr 1) div sumA;
     1159                ec.green := (sumG+sumA shr 1) div sumA;
     1160                ec.blue := (sumB+sumA shr 1) div sumA;
     1161                if sumA > 255 then sumA := 255;
     1162                ec.alpha := sumA shl 8 + sumA;
     1163                count := 1;
     1164                while (xb < rowmaxx) and (nextSum^.sumA = sumA) and (nextSum^.sumB = sumB)
     1165                  and (nextSum^.sumG = sumG) and (nextSum^.sumR = sumR) do
     1166                begin
     1167                  inc(xb);
     1168                  inc(nextSum);
     1169                  inc(count);
     1170                end;
     1171                FillInline(pdest,GammaCompression(ec),count);
     1172                inc(pdest,count-1);
     1173              end;
     1174            end;
     1175            inc(xb);
     1176            inc(pdest);
     1177          end;
     1178
    10221179      end;
    10231180    end;
     
    10351192
    10361193procedure FillRoundRectangleAntialias(bmp: TBGRACustomBitmap; x1, y1, x2, y2,
    1037   rx, ry: single; options: TRoundRectangleOptions; c: TBGRAPixel; EraseMode: boolean);
     1194  rx, ry: single; options: TRoundRectangleOptions; c: TBGRAPixel; EraseMode: boolean; LinearBlend: boolean);
    10381195var
    10391196  info: TFillRoundRectangleInfo;
     
    10411198  if (x1 = x2) or (y1 = y2) then exit;
    10421199  info := TFillRoundRectangleInfo.Create(x1, y1, x2, y2, rx, ry, options);
    1043   FillShapeAntialias(bmp, info, c, EraseMode,nil, False);
     1200  FillShapeAntialias(bmp, info, c, EraseMode,nil, False, LinearBlend);
    10441201  info.Free;
    10451202end;
     
    10471204procedure FillRoundRectangleAntialiasWithTexture(bmp: TBGRACustomBitmap; x1,
    10481205  y1, x2, y2, rx, ry: single; options: TRoundRectangleOptions;
    1049   scan: IBGRAScanner);
     1206  scan: IBGRAScanner; LinearBlend: boolean);
    10501207var
    10511208  info: TFillRoundRectangleInfo;
     
    10531210  if (x1 = x2) or (y1 = y2) then exit;
    10541211  info := TFillRoundRectangleInfo.Create(x1, y1, x2, y2, rx, ry, options);
    1055   FillShapeAntialiasWithTexture(bmp, info, scan, False);
     1212  FillShapeAntialiasWithTexture(bmp, info, scan, False, LinearBlend);
    10561213  info.Free;
    10571214end;
     
    10591216procedure BorderRoundRectangleAntialias(bmp: TBGRACustomBitmap; x1, y1, x2,
    10601217  y2, rx, ry, w: single; options: TRoundRectangleOptions; c: TBGRAPixel;
    1061   EraseMode: boolean);
     1218  EraseMode: boolean; LinearBlend: boolean);
    10621219var
    10631220  info: TFillBorderRoundRectInfo;
     
    10651222  if (rx = 0) or (ry = 0) or (w=0) then exit;
    10661223  info := TFillBorderRoundRectInfo.Create(x1, y1, x2,y2, rx, ry, w, options);
    1067   FillShapeAntialias(bmp, info, c, EraseMode, nil, False);
     1224  FillShapeAntialias(bmp, info, c, EraseMode, nil, False, LinearBlend);
    10681225  info.Free;
    10691226end;
     
    10711228procedure BorderRoundRectangleAntialiasWithTexture(bmp: TBGRACustomBitmap; x1,
    10721229  y1, x2, y2, rx, ry, w: single; options: TRoundRectangleOptions;
    1073   scan: IBGRAScanner);
     1230  scan: IBGRAScanner; LinearBlend: boolean);
    10741231var
    10751232  info: TFillBorderRoundRectInfo;
     
    10771234  if (rx = 0) or (ry = 0) or (w=0) then exit;
    10781235  info := TFillBorderRoundRectInfo.Create(x1, y1, x2,y2, rx, ry, w, options);
    1079   FillShapeAntialiasWithTexture(bmp, info, scan, False);
     1236  FillShapeAntialiasWithTexture(bmp, info, scan, False, LinearBlend);
    10801237  info.Free;
    10811238end;
     
    11061263  end else
    11071264  begin
    1108     FillShapeAntialias(bmp, info.innerBorder, fillcolor, EraseMode, nil, False);
    1109     FillShapeAntialias(bmp, info, bordercolor, EraseMode, nil, False);
     1265    FillShapeAntialias(bmp, info.innerBorder, fillcolor, EraseMode, nil, False, False);
     1266    FillShapeAntialias(bmp, info, bordercolor, EraseMode, nil, False, False);
    11101267  end;
    11111268  info.Free;
    11121269end;
    11131270
    1114 initialization
    1115 
    1116   Randomize;
    1117 
    11181271end.
  • GraphicTest/Packages/bgrabitmap/bgrapolygonaliased.pas

    r452 r472  
    22
    33{$mode objfpc}{$H+}
     4
     5{$i bgrasse.inc}
    46
    57interface
     
    1214
    1315uses
    14   Classes, SysUtils, BGRABitmapTypes, BGRAFillInfo, BGRAPolygon, BGRASSE;
     16  Classes, SysUtils, BGRABitmapTypes, BGRAFillInfo, BGRASSE;
    1517
    1618type
     
    2931  { TPolygonLinearColorGradientInfo }
    3032
    31   TPolygonLinearColorGradientInfo = class(TFillPolyInfo)
     33  TPolygonLinearColorGradientInfo = class(TOnePassFillPolyInfo)
    3234  protected
    3335    FColors: array of TColorF;
     36    procedure SetIntersectionValues(AInter: TIntersectionInfo; AInterX: Single; AWinding,
     37      ANumSegment: integer; dy: single; AData: pointer); override;
    3438  public
    3539    constructor Create(const points: array of TPointF; const Colors: array of TBGRAPixel);
    3640    function CreateSegmentData(numPt,nextPt: integer; x,y: single): pointer; override;
    3741    function CreateIntersectionInfo: TIntersectionInfo; override;
    38     procedure ComputeIntersection(cury: single;
    39       var inter: ArrayOfTIntersectionInfo; var nbInter: integer); override;
    4042  end;
    4143
     
    6163  { TPolygonPerspectiveColorGradientInfo }
    6264
    63   TPolygonPerspectiveColorGradientInfo = class(TFillPolyInfo)
     65  TPolygonPerspectiveColorGradientInfo = class(TOnePassFillPolyInfo)
    6466  protected
    6567    FColors: array of TColorF;
    6668    FPointsZ: array of single;
     69    procedure SetIntersectionValues(AInter: TIntersectionInfo; AInterX: Single; AWinding,
     70      ANumSegment: integer; dy: single; AData: pointer); override;
    6771  public
    6872    constructor Create(const points: array of TPointF; const pointsZ: array of single; const Colors: array of TBGRAPixel);
    6973    function CreateSegmentData(numPt,nextPt: integer; x,y: single): pointer; override;
    7074    function CreateIntersectionInfo: TIntersectionInfo; override;
    71     procedure ComputeIntersection(cury: single;
    72       var inter: ArrayOfTIntersectionInfo; var nbInter: integer); override;
    7375  end;
    7476
     
    9698  { TPolygonLinearTextureMappingInfo }
    9799
    98   TPolygonLinearTextureMappingInfo = class(TFillPolyInfo)
     100  TPolygonLinearTextureMappingInfo = class(TOnePassFillPolyInfo)
    99101  protected
    100102    FTexCoords: array of TPointF;
    101103    FLightnesses: array of Word;
     104    procedure SetIntersectionValues(AInter: TIntersectionInfo; AInterX: Single; AWinding,
     105      ANumSegment: integer; dy: single; AData: pointer); override;
    102106  public
    103107    constructor Create(const points: array of TPointF; const texCoords: array of TPointF);
     
    105109    function CreateSegmentData(numPt,nextPt: integer; x,y: single): pointer; override;
    106110    function CreateIntersectionInfo: TIntersectionInfo; override;
    107     procedure ComputeIntersection(cury: single;
    108       var inter: ArrayOfTIntersectionInfo; var nbInter: integer); override;
    109111  end;
    110112
     
    140142  { TPolygonPerspectiveTextureMappingInfo }
    141143
    142   TPolygonPerspectiveTextureMappingInfo = class(TFillPolyInfo)
     144  TPolygonPerspectiveTextureMappingInfo = class(TOnePassFillPolyInfo)
    143145  protected
    144146    FTexCoords: array of TPointF;
    145147    FPointsZ: array of single;
    146148    FLightnesses: array of Word;
     149    procedure SetIntersectionValues(AInter: TIntersectionInfo; AInterX: Single; AWinding,
     150      ANumSegment: integer; dy: single; AData: pointer); override;
    147151  public
    148152    constructor Create(const points: array of TPointF; const pointsZ: array of single; const texCoords: array of TPointF);
     
    150154    function CreateSegmentData(numPt,nextPt: integer; x,y: single): pointer; override;
    151155    function CreateIntersectionInfo: TIntersectionInfo; override;
    152     procedure ComputeIntersection(cury: single;
    153       var inter: ArrayOfTIntersectionInfo; var nbInter: integer); override;
    154156  end;
    155157
    156158  { TPolygonPerspectiveMappingShaderInfo }
    157159
    158   TPolygonPerspectiveMappingShaderInfo = class(TFillPolyInfo)
     160  TPolygonPerspectiveMappingShaderInfo = class(TOnePassFillPolyInfo)
    159161  protected
    160162    FTexCoords: array of TPointF;
    161163    FPositions3D, FNormals3D: array of TPoint3D_128;
     164    procedure SetIntersectionValues(AInter: TIntersectionInfo; AInterX: Single; AWinding,
     165      ANumSegment: integer; dy: single; AData: pointer); override;
    162166  public
    163167    constructor Create(const points: array of TPointF; const points3D: array of TPoint3D; const normals: array of TPoint3D; const texCoords: array of TPointF);
     
    165169    function CreateSegmentData(numPt,nextPt: integer; x,y: single): pointer; override;
    166170    function CreateIntersectionInfo: TIntersectionInfo; override;
    167     procedure ComputeIntersection(cury: single;
    168       var inter: ArrayOfTIntersectionInfo; var nbInter: integer); override;
    169171  end;
    170172
     
    192194{ Aliased round rectangle }
    193195procedure BGRARoundRectAliased(dest: TBGRACustomBitmap; X1, Y1, X2, Y2: integer;
    194   DX, DY: integer; BorderColor, FillColor: TBGRAPixel; FillTexture: IBGRAScanner = nil);
     196  DX, DY: integer; BorderColor, FillColor: TBGRAPixel; FillTexture: IBGRAScanner = nil; ADrawMode: TDrawMode = dmDrawWithTransparency;
     197  skipFill: boolean = false);
    195198
    196199implementation
     
    199202
    200203{ TPolygonPerspectiveColorGradientInfo }
     204
     205procedure TPolygonPerspectiveColorGradientInfo.SetIntersectionValues(
     206  AInter: TIntersectionInfo; AInterX: Single; AWinding, ANumSegment: integer;
     207  dy: single; AData: pointer);
     208var
     209  info: PPerspectiveColorInfo;
     210begin
     211  AInter.SetValues(AInterX,AWinding,ANumSegment);
     212  info := PPerspectiveColorInfo(AData);
     213  TPerspectiveColorGradientIntersectionInfo(AInter).coordInvZ := dy*info^.InvZSlope + info^.InvZ;
     214  TPerspectiveColorGradientIntersectionInfo(AInter).ColorDivZ := info^.ColorDivZ + info^.ColorSlopesDivZ*dy;
     215end;
    201216
    202217constructor TPolygonPerspectiveColorGradientInfo.Create(
     
    266281end;
    267282
    268 procedure TPolygonPerspectiveColorGradientInfo.ComputeIntersection(
    269   cury: single; var inter: ArrayOfTIntersectionInfo; var nbInter: integer);
    270 var
    271   j: integer;
    272   dy: single;
    273   info: PPerspectiveColorInfo;
    274 begin
    275   if length(FSlices)=0 then exit;
    276 
    277   while (cury < FSlices[FCurSlice].y1) and (FCurSlice > 0) do dec(FCurSlice);
    278   while (cury > FSlices[FCurSlice].y2) and (FCurSlice < high(FSlices)) do inc(FCurSlice);
    279   with FSlices[FCurSlice] do
    280   if (cury >= y1) and (cury <= y2) then
    281   begin
    282     for j := 0 to nbSegments-1 do
    283     begin
    284       dy := cury - segments[j].y1;
    285       inter[nbinter].interX := dy * segments[j].slope + segments[j].x1;
    286       inter[nbinter].winding := segments[j].winding;
    287       info := PPerspectiveColorInfo(segments[j].data);
    288       TPerspectiveColorGradientIntersectionInfo(inter[nbinter]).coordInvZ := dy*info^.InvZSlope + info^.InvZ;
    289       TPerspectiveColorGradientIntersectionInfo(inter[nbinter]).ColorDivZ := info^.ColorDivZ + info^.ColorSlopesDivZ*dy;
    290       Inc(nbinter);
    291     end;
    292   end;
    293 end;
    294 
    295283{ TPolygonLinearColorGradientInfo }
     284
     285procedure TPolygonLinearColorGradientInfo.SetIntersectionValues(
     286  AInter: TIntersectionInfo; AInterX: Single; AWinding, ANumSegment: integer;
     287  dy: single; AData: pointer);
     288var
     289  info: PLinearColorInfo;
     290begin
     291  AInter.SetValues(AInterX,AWinding,ANumSegment);
     292  info := PLinearColorInfo(AData);
     293  TLinearColorGradientIntersectionInfo(AInter).color := info^.Color + info^.ColorSlopes*dy;
     294end;
    296295
    297296constructor TPolygonLinearColorGradientInfo.Create(
     
    343342begin
    344343  Result:= TLinearColorGradientIntersectionInfo.Create;
    345 end;
    346 
    347 procedure TPolygonLinearColorGradientInfo.ComputeIntersection(cury: single;
    348       var inter: ArrayOfTIntersectionInfo; var nbInter: integer);
    349 var
    350   j: integer;
    351   dy: single;
    352   info: PLinearColorInfo;
    353 begin
    354   if length(FSlices)=0 then exit;
    355 
    356   while (cury < FSlices[FCurSlice].y1) and (FCurSlice > 0) do dec(FCurSlice);
    357   while (cury > FSlices[FCurSlice].y2) and (FCurSlice < high(FSlices)) do inc(FCurSlice);
    358   with FSlices[FCurSlice] do
    359   if (cury >= y1) and (cury <= y2) then
    360   begin
    361     for j := 0 to nbSegments-1 do
    362     begin
    363       dy := cury - segments[j].y1;
    364       inter[nbinter].interX := dy * segments[j].slope + segments[j].x1;
    365       inter[nbinter].winding := segments[j].winding;
    366       info := PLinearColorInfo(segments[j].data);
    367       TLinearColorGradientIntersectionInfo(inter[nbinter]).color := info^.Color + info^.ColorSlopes*dy;
    368       Inc(nbinter);
    369     end;
    370   end;
    371344end;
    372345
     
    389362        r,g,b,a: integer;
    390363       end;
    391     {$IFDEF CPUI386} c: TBGRAPixel; {$ENDIF}
     364    {$IFDEF BGRASSE_AVAILABLE} c: TBGRAPixel; {$ENDIF}
    392365  begin
    393366    t := ((ix1+0.5)-x1)/(x2-x1);
     
    396369    pdest := bmp.ScanLine[yb]+ix1;
    397370
    398     {$IFDEF CPUI386} {$asmmode intel}
     371    {$IFDEF BGRASSE_AVAILABLE} {$asmmode intel}
    399372    If UseSSE then
    400373    begin
     
    499472{ TPolygonLinearTextureMappingInfo }
    500473
     474procedure TPolygonLinearTextureMappingInfo.SetIntersectionValues(
     475  AInter: TIntersectionInfo; AInterX: Single; AWinding, ANumSegment: integer;
     476  dy: single; AData: pointer);
     477var
     478  info: PLinearTextureInfo;
     479begin
     480  AInter.SetValues(AInterX,AWinding,ANumSegment);
     481  info := PLinearTextureInfo(AData);
     482  TLinearTextureMappingIntersectionInfo(AInter).texCoord := info^.TexCoord + info^.TexCoordSlopes*dy;
     483  if FLightnesses<>nil then
     484    TLinearTextureMappingIntersectionInfo(AInter).lightness := round(info^.lightness + info^.lightnessSlope*dy)
     485  else
     486    TLinearTextureMappingIntersectionInfo(AInter).lightness := 32768;
     487end;
     488
    501489constructor TPolygonLinearTextureMappingInfo.Create(const points: array of TPointF;
    502490  const texCoords: array of TPointF);
     
    585573begin
    586574  result := TLinearTextureMappingIntersectionInfo.Create;
    587 end;
    588 
    589 procedure TPolygonLinearTextureMappingInfo.ComputeIntersection(cury: single;
    590       var inter: ArrayOfTIntersectionInfo; var nbInter: integer);
    591 var
    592   j: integer;
    593   dy: single;
    594   info: PLinearTextureInfo;
    595 begin
    596   if length(FSlices)=0 then exit;
    597 
    598   while (cury < FSlices[FCurSlice].y1) and (FCurSlice > 0) do dec(FCurSlice);
    599   while (cury > FSlices[FCurSlice].y2) and (FCurSlice < high(FSlices)) do inc(FCurSlice);
    600   with FSlices[FCurSlice] do
    601   if (cury >= y1) and (cury <= y2) then
    602   begin
    603     for j := 0 to nbSegments-1 do
    604     begin
    605       dy := cury - segments[j].y1;
    606       inter[nbinter].interX := dy * segments[j].slope + segments[j].x1;
    607       inter[nbinter].winding := segments[j].winding;
    608       info := PLinearTextureInfo(segments[j].data);
    609       TLinearTextureMappingIntersectionInfo(inter[nbinter]).texCoord := info^.TexCoord + info^.TexCoordSlopes*dy;
    610       if FLightnesses<>nil then
    611         TLinearTextureMappingIntersectionInfo(inter[nbinter]).lightness := round(info^.lightness + info^.lightnessSlope*dy)
    612       else
    613         TLinearTextureMappingIntersectionInfo(inter[nbinter]).lightness := 32768;
    614       Inc(nbinter);
    615     end;
    616   end;
    617575end;
    618576
     
    637595    z,invZ,InvZStep: single;
    638596    r,g,b,a: integer;
    639     {$IFDEF CPUI386}minVal,maxVal: single;
     597    {$IFDEF BGRASSE_AVAILABLE}minVal,maxVal: single;
    640598    cInt: packed record
    641599      r,g,b,a: integer;
     
    657615    {$DEFINE PARAM_USEZBUFFER}
    658616      zbufferpos := zbuffer + yb*bmp.Width + ix1;
    659       {$IFDEF CPUI386}
     617      {$IFDEF BGRASSE_AVAILABLE}
    660618      If UseSSE then
    661619      begin
     
    679637    end else
    680638    begin
    681       {$IFDEF CPUI386}
     639      {$IFDEF BGRASSE_AVAILABLE}
    682640      If UseSSE then
    683641      begin
     
    846804{From LazRGBGraphics}
    847805procedure BGRARoundRectAliased(dest: TBGRACustomBitmap; X1, Y1, X2, Y2: integer;
    848   DX, DY: integer; BorderColor, FillColor: TBGRAPixel; FillTexture: IBGRAScanner = nil);
     806  DX, DY: integer; BorderColor, FillColor: TBGRAPixel; FillTexture: IBGRAScanner = nil; ADrawMode: TDrawMode = dmDrawWithTransparency;
     807  skipFill: boolean = false);
    849808var
    850809  CX, CY, CX1, CY1, A, B, NX, NY: single;
     
    858817  LX, LY: integer;
    859818  RowStart,RowEnd: integer;
    860   eBorderColor,eFillColor: TExpandedPixel;
     819  PixelProc: procedure (x, y: int32or64; c: TBGRAPixel) of object;
     820  skipBorder: boolean;
    861821
    862822  procedure AddEdge(X, Y: integer);
     
    891851  Dec(y2);
    892852
    893   eBorderColor := GammaExpansion(BorderColor);
    894   eFillColor := GammaExpansion(FillColor);
    895 
    896853  if (X1 = X2) and (Y1 = Y2) then
    897854  begin
    898     dest.DrawPixel(X1, Y1, eBorderColor);
     855    dest.DrawPixel(X1, Y1, BorderColor, ADrawMode);
    899856    Exit;
    900857  end;
     
    902859  if (X2 - X1 = 1) or (Y2 - Y1 = 1) then
    903860  begin
    904     dest.FillRect(X1, Y1, X2 + 1, Y2 + 1, BorderColor, dmDrawWithTransparency);
     861    dest.FillRect(X1, Y1, X2 + 1, Y2 + 1, BorderColor, ADrawMode);
    905862    Exit;
    906863  end;
     
    908865  if (LX > X2 - X1) or (LY > Y2 - Y1) then
    909866  begin
    910     dest.Rectangle(X1, Y1, X2 + 1, Y2 + 1, BorderColor, dmDrawWithTransparency);
    911     if FillTexture <> nil then
    912       dest.FillRect(X1 + 1, Y1 + 1, X2, Y2, FillTexture, dmDrawWithTransparency) else
    913       dest.FillRect(X1 + 1, Y1 + 1, X2, Y2, FillColor, dmDrawWithTransparency);
     867    dest.Rectangle(X1, Y1, X2 + 1, Y2 + 1, BorderColor, ADrawMode);
     868    if not skipFill then
     869      if FillTexture <> nil then
     870        dest.FillRect(X1 + 1, Y1 + 1, X2, Y2, FillTexture, ADrawMode) else
     871        dest.FillRect(X1 + 1, Y1 + 1, X2, Y2, FillColor, ADrawMode);
    914872    Exit;
    915873  end;
     
    977935  end;
    978936
     937  case ADrawMode of
     938  dmSetExceptTransparent: begin PixelProc := @dest.SetPixel; skipBorder:= BorderColor.alpha <> 255; end;  dmDrawWithTransparency: begin PixelProc := @dest.DrawPixel; skipBorder:= BorderColor.alpha = 0; end;
     939  dmXor: begin PixelProc := @dest.XorPixel; skipBorder:= DWord(BorderColor) = 0; end;
     940  dmLinearBlend: begin PixelProc := @dest.FastBlendPixel; skipBorder:= BorderColor.alpha = 0; end;
     941  else
     942  begin PixelProc := @dest.SetPixel; skipBorder := false; end;
     943  end;
     944
    979945  J := 0;
    980946  while J < Length(EdgeList) do
     
    982948    if (J = 0) and (Frac(CY) > 0) then
    983949    begin
     950      if not skipBorder then
    984951      for I := EdgeList[J].X to EdgeList[J].Y do
    985952      begin
    986         dest.DrawPixel(Floor(CX) + I, Floor(CY) + J, eBorderColor);
    987         dest.DrawPixel(Ceil(CX) - Succ(I), Floor(CY) + J, eBorderColor);
     953        PixelProc(Floor(CX) + I, Floor(CY) + J, BorderColor);
     954        PixelProc(Ceil(CX) - Succ(I), Floor(CY) + J, BorderColor);
    988955      end;
    989956
    990       if FillTexture <> nil then
    991         dest.DrawHorizLine(Ceil(CX) - EdgeList[J].X, Floor(CY) + J, Floor(CX) +
    992           Pred(EdgeList[J].X), FillTexture) else
    993         dest.DrawHorizLine(Ceil(CX) - EdgeList[J].X, Floor(CY) + J, Floor(CX) +
    994           Pred(EdgeList[J].X), eFillColor);
     957      if not SkipFill then
     958        if FillTexture <> nil then
     959          dest.HorizLine(Ceil(CX) - EdgeList[J].X, Floor(CY) + J, Floor(CX) +
     960            Pred(EdgeList[J].X), FillTexture, ADrawMode) else
     961          dest.HorizLine(Ceil(CX) - EdgeList[J].X, Floor(CY) + J, Floor(CX) +
     962            Pred(EdgeList[J].X), FillColor, ADrawMode);
    995963    end
    996964    else
     
    1002970        S := -Succ(EdgeList[J].Y);
    1003971
     972      if not skipBorder then
    1004973      for I := S to EdgeList[J].Y do
    1005974      begin
    1006         dest.DrawPixel(Floor(CX) + I, Floor(CY) + J, eBorderColor);
    1007         dest.DrawPixel(Floor(CX) + I, Ceil(CY) - Succ(J), eBorderColor);
     975        PixelProc(Floor(CX) + I, Floor(CY) + J, BorderColor);
     976        PixelProc(Floor(CX) + I, Ceil(CY) - Succ(J), BorderColor);
    1008977      end;
    1009978    end
    1010979    else
    1011980    begin
     981      if not skipBorder then
    1012982      for I := EdgeList[J].X to EdgeList[J].Y do
    1013983      begin
    1014         dest.DrawPixel(Floor(CX) + I, Floor(CY) + J, eBorderColor);
    1015         dest.DrawPixel(Floor(CX) + I, Ceil(CY) - Succ(J), eBorderColor);
     984        PixelProc(Floor(CX) + I, Floor(CY) + J, BorderColor);
     985        PixelProc(Floor(CX) + I, Ceil(CY) - Succ(J), BorderColor);
    1016986        if Floor(CX) + I <> Ceil(CX) - Succ(I) then
    1017987        begin
    1018           dest.DrawPixel(Ceil(CX) - Succ(I), Floor(CY) + J, eBorderColor);
    1019           dest.DrawPixel(Ceil(CX) - Succ(I), Ceil(CY) - Succ(J), eBorderColor);
     988          PixelProc(Ceil(CX) - Succ(I), Floor(CY) + J, BorderColor);
     989          PixelProc(Ceil(CX) - Succ(I), Ceil(CY) - Succ(J), BorderColor);
    1020990        end;
    1021991      end;
    1022992
    1023       RowStart := Ceil(CX) - EdgeList[J].X;
    1024       RowEnd := Floor(CX) + Pred(EdgeList[J].X);
    1025       if RowEnd >= RowStart then
    1026       begin
    1027         if FillTexture <> nil then
     993      if not SkipFill then
     994      begin
     995        RowStart := Ceil(CX) - EdgeList[J].X;
     996        RowEnd := Floor(CX) + Pred(EdgeList[J].X);
     997        if RowEnd >= RowStart then
    1028998        begin
    1029           dest.DrawHorizLine(RowStart, Floor(CY) + J,
    1030             RowEnd, FillTexture);
    1031           dest.DrawHorizLine(RowStart, Ceil(CY) - Succ(J),
    1032             RowEnd, FillTexture);
    1033         end else
    1034         begin
    1035           dest.DrawHorizLine(RowStart, Floor(CY) + J,
    1036             RowEnd, eFillColor);
    1037           dest.DrawHorizLine(RowStart, Ceil(CY) - Succ(J),
    1038             RowEnd, eFillColor);
     999          if FillTexture <> nil then
     1000          begin
     1001            dest.HorizLine(RowStart, Floor(CY) + J,
     1002              RowEnd, FillTexture, ADrawMode);
     1003            dest.HorizLine(RowStart, Ceil(CY) - Succ(J),
     1004              RowEnd, FillTexture, ADrawMode);
     1005          end else
     1006          begin
     1007            dest.HorizLine(RowStart, Floor(CY) + J,
     1008              RowEnd, FillColor, ADrawMode);
     1009            dest.HorizLine(RowStart, Ceil(CY) - Succ(J),
     1010              RowEnd, FillColor, ADrawMode);
     1011          end;
    10391012        end;
    10401013      end;
     1014
    10411015    end;
    10421016    Inc(J);
  • GraphicTest/Packages/bgrabitmap/bgraresample.pas

    r452 r472  
    88  without interpolation filters.
    99
    10   SimpleStretch does a fast stretch by splitting the image into zones defined
    11   by integers. This can be quite ugly.
     10  SimpleStretch does a boxed resample with limited antialiasing.
    1211
    1312  FineResample uses floating point coordinates to get an antialiased resample.
     
    2019
    2120uses
    22   SysUtils, BGRABitmapTypes;
     21  Types, SysUtils, BGRABitmapTypes;
    2322
    2423{------------------------------- Simple stretch ------------------------------------}
     
    2625function SimpleStretch(bmp: TBGRACustomBitmap;
    2726  NewWidth, NewHeight: integer): TBGRACustomBitmap;
     27procedure StretchPutImage(bmp: TBGRACustomBitmap;
     28  NewWidth, NewHeight: integer; dest: TBGRACustomBitmap; OffsetX,OffsetY: Integer; ADrawMode: TDrawMode; AOpacity: byte);
     29procedure DownSamplePutImage(source: TBGRACustomBitmap; factorX,factorY: integer; dest: TBGRACustomBitmap; OffsetX,OffsetY: Integer; ADrawMode: TDrawMode);
     30function DownSample(source: TBGRACustomBitmap; factorX,factorY: integer): TBGRACustomBitmap;
    2831
    2932{---------------------------- Interpolation filters --------------------------------}
     
    6669  end;
    6770
     71  { TLanczosKernel }
     72
     73  TLanczosKernel = class(TWideKernelFilter)
     74  private
     75    FNumberOfLobes: integer;
     76    FFactor: ValReal;
     77    procedure SetNumberOfLobes(AValue: integer);
     78  public
     79    constructor Create(ANumberOfLobes: integer);
     80    function Interpolation(t: single): single; override;
     81    function ShouldCheckRange: boolean; override;
     82    function KernelWidth: single; override;
     83
     84    property NumberOfLobes : integer read FNumberOfLobes write SetNumberOfLobes;
     85  end;
     86
    6887function CreateInterpolator(style: TSplineStyle): TWideKernelFilter;
    6988
     
    7897implementation
    7998
    80 uses GraphType, Math;
    81 
    82 {-------------------------------- Simple stretch ------------------------------------}
    83 
    84 function FastSimpleStretchLarger(bmp: TBGRACustomBitmap;
    85   xFactor, yFactor: integer): TBGRACustomBitmap;
     99uses GraphType, Math, BGRABlend;
     100
     101function SimpleStretch(bmp: TBGRACustomBitmap;
     102  newWidth, newHeight: integer): TBGRACustomBitmap;
     103begin
     104  if (NewWidth = bmp.Width) and (NewHeight = bmp.Height) then
     105  begin
     106    Result := bmp.Duplicate;
     107    exit;
     108  end;
     109  Result := bmp.NewBitmap(NewWidth, NewHeight);
     110  StretchPutImage(bmp, newWidth,newHeight, result, 0,0, dmSet, 255);
     111end;
     112
     113procedure StretchPutImage(bmp: TBGRACustomBitmap; NewWidth, NewHeight: integer;
     114  dest: TBGRACustomBitmap; OffsetX, OffsetY: Integer; ADrawMode: TDrawMode; AOpacity: byte);
     115type
     116  TTransitionState = (tsNone, tsPlain, tsLeft, tsMiddle, tsRight);
    86117var
    87   y_src, yb, y_dest: integer;
    88 
    89   x_src, xb: integer;
    90   srcColor:  TBGRAPixel;
    91 
    92   PSrc:  PBGRAPixel;
    93   PDest: array of PBGRAPixel;
    94   temp:  PBGRAPixel;
    95 
    96 begin
    97   if (xFactor < 1) or (yFactor < 1) then
    98     raise ERangeError.Create('FastSimpleStretchLarger: New dimensions must be greater or equal (*'+IntToStr(xFactor)+'x*'+IntToStr(yFactor)+')');
    99 
    100   Result := bmp.NewBitmap(bmp.Width * xFactor, bmp.Height * yFactor);
    101   if (Result.Width = 0) or (Result.Height = 0) then
     118  x_src,y_src, y_src2, prev_y_src, prev_y_src2: NativeInt;
     119  inc_x_src, mod_x_src, acc_x_src, inc_y_src, mod_y_src, acc_y_src,
     120  acc_x_src2, acc_y_src2: NativeInt;
     121  x_dest, y_dest: NativeInt;
     122
     123  PDest, PSrc1, PSrc2: PBGRAPixel;
     124  vertColors: packed array[1..2] of TBGRAPixel;
     125  DeltaSrcX: NativeInt;
     126  targetRect: TRect;
     127  tempData: PBGRAPixel;
     128  prevHorizTransition,horizTransition,prevVertTransition,vertTransition: TTransitionState;
     129  horizSlightlyDifferent,vertSlightlyDifferent: boolean;
     130
     131  procedure LinearMix(PSrc: PBGRAPixel; DeltaSrc: integer; AccSrcQuarter: boolean;
     132        PDest: PBGRAPixel; slightlyDifferent: boolean; var transition: TTransitionState);
     133  var
     134    asum: NativeInt;
     135    a1,a2: NativeInt;
     136    newTransition: TTransitionState;
     137  begin
     138    if DeltaSrc=0 then
     139    begin
     140      PDest^ := PSrc^;
     141      transition:= tsPlain;
     142    end
     143    else
     144    begin
     145      if slightlyDifferent then
     146      begin
     147        if AccSrcQuarter then newTransition:= tsRight else
     148          newTransition:= tsLeft;
     149      end else
     150        newTransition:= tsMiddle;
     151
     152      if (newTransition = tsMiddle) or ((newTransition = tsRight) and (transition = tsLeft)) or
     153        ((newTransition = tsLeft) and (transition = tsRight)) then
     154      begin
     155        transition:= tsMiddle;
     156        asum := psrc^.alpha + (psrc+DeltaSrc)^.alpha;
     157        if asum = 0 then
     158          pdest^ := BGRAPixelTransparent
     159        else if asum = 510 then
     160        begin
     161          pdest^.alpha := 255;
     162          pdest^.red := (psrc^.red + (psrc+DeltaSrc)^.red + 1) shr 1;
     163          pdest^.green := (psrc^.green + (psrc+DeltaSrc)^.green + 1) shr 1;
     164          pdest^.blue := (psrc^.blue + (psrc+DeltaSrc)^.blue + 1) shr 1;
     165        end else
     166        begin
     167          pdest^.alpha := asum shr 1;
     168          a1 := psrc^.alpha;
     169          a2 := (psrc+DeltaSrc)^.alpha;
     170          pdest^.red := (psrc^.red*a1 + (psrc+DeltaSrc)^.red*a2 + (asum shr 1)) div asum;
     171          pdest^.green := (psrc^.green*a1 + (psrc+DeltaSrc)^.green*a2 + (asum shr 1)) div asum;
     172          pdest^.blue := (psrc^.blue*a1 + (psrc+DeltaSrc)^.blue*a2 + (asum shr 1)) div asum;
     173        end;
     174      end else
     175      if newTransition = tsRight then
     176      begin
     177        transition := tsRight;
     178        asum := psrc^.alpha + (psrc+DeltaSrc)^.alpha*3;
     179        if asum = 0 then
     180          pdest^ := BGRAPixelTransparent
     181        else if asum = 1020 then
     182        begin
     183          pdest^.alpha := 255;
     184          pdest^.red := (psrc^.red + (psrc+DeltaSrc)^.red*3 + 2) shr 2;
     185          pdest^.green := (psrc^.green + (psrc+DeltaSrc)^.green*3 + 2) shr 2;
     186          pdest^.blue := (psrc^.blue + (psrc+DeltaSrc)^.blue*3 + 2) shr 2;
     187        end else
     188        begin
     189          pdest^.alpha := asum shr 2;
     190          a1 := psrc^.alpha;
     191          a2 := (psrc+DeltaSrc)^.alpha;
     192          pdest^.red := (psrc^.red*a1 + (psrc+DeltaSrc)^.red*a2*3 + (asum shr 1)) div asum;
     193          pdest^.green := (psrc^.green*a1 + (psrc+DeltaSrc)^.green*a2*3 + (asum shr 1)) div asum;
     194          pdest^.blue := (psrc^.blue*a1 + (psrc+DeltaSrc)^.blue*a2*3 + (asum shr 1)) div asum;
     195        end;
     196      end else
     197      begin
     198        transition:= tsLeft;
     199        asum := psrc^.alpha*3 + (psrc+DeltaSrc)^.alpha;
     200        if asum = 0 then
     201          pdest^ := BGRAPixelTransparent
     202        else if asum = 1020 then
     203        begin
     204          pdest^.alpha := 255;
     205          pdest^.red := (psrc^.red*3 + (psrc+DeltaSrc)^.red + 2) shr 2;
     206          pdest^.green := (psrc^.green*3 + (psrc+DeltaSrc)^.green + 2) shr 2;
     207          pdest^.blue := (psrc^.blue*3 + (psrc+DeltaSrc)^.blue + 2) shr 2;
     208        end else
     209        begin
     210          pdest^.alpha := asum shr 2;
     211          a1 := psrc^.alpha;
     212          a2 := (psrc+DeltaSrc)^.alpha;
     213          pdest^.red := (psrc^.red*a1*3 + (psrc+DeltaSrc)^.red*a2 + (asum shr 1)) div asum;
     214          pdest^.green := (psrc^.green*a1*3 + (psrc+DeltaSrc)^.green*a2 + (asum shr 1)) div asum;
     215          pdest^.blue := (psrc^.blue*a1*3 + (psrc+DeltaSrc)^.blue*a2 + (asum shr 1)) div asum;
     216        end;
     217      end;
     218    end;
     219  end;
     220
     221begin
     222  if (newWidth <= 0) or (newHeight <= 0) or (bmp.Width <= 0)
     223    or (bmp.Height <= 0) then
    102224    exit;
    103225
     226  targetRect := rect(0,0,NewWidth,NewHeight);
     227  if OffsetX < dest.ClipRect.Left then targetRect.Left:= dest.ClipRect.Left-OffsetX;
     228  if OffsetY < dest.ClipRect.Top then targetRect.Top:= dest.ClipRect.Top-OffsetY;
     229  if OffsetX+NewWidth > dest.ClipRect.Right then targetRect.Right := dest.ClipRect.Right-OffsetX;
     230  if OffsetY+NewHeight > dest.ClipRect.Bottom then targetRect.Bottom := dest.ClipRect.Bottom-OffsetY;
     231  if (targetRect.Right <= targetRect.Left) or (targetRect.Bottom <= targetRect.Top) then exit;
     232
    104233  bmp.LoadFromBitmapIfNeeded;
    105234
    106   SetLength(PDest, yFactor);
    107   y_dest := 0;
    108   for y_src := 0 to bmp.Height - 1 do
    109   begin
    110     PSrc := bmp.Scanline[y_src];
    111     for yb := 0 to yFactor - 1 do
    112       PDest[yb] := Result.scanLine[y_dest + yb];
    113 
    114     for x_src := 0 to bmp.Width - 1 do
    115     begin
    116       srcColor := PSrc^;
    117       Inc(PSrc);
    118 
    119       for yb := 0 to yFactor - 1 do
    120       begin
    121         temp := PDest[yb];
    122         for xb := 0 to xFactor - 1 do
    123         begin
    124           temp^ := srcColor;
    125           Inc(temp);
     235  if (ADrawMode <> dmSet) or (AOpacity <> 255) then
     236     getmem(tempData, (targetRect.Right-targetRect.Left)*sizeof(TBGRAPixel) )
     237  else
     238      tempData := nil;
     239
     240  inc_x_src := bmp.Width div newwidth;
     241  mod_x_src := bmp.Width mod newwidth;
     242  inc_y_src := bmp.Height div newheight;
     243  mod_y_src := bmp.Height mod newheight;
     244
     245  prev_y_src := -1;
     246  prev_y_src2 := -1;
     247
     248  acc_y_src := targetRect.Top*mod_y_src;
     249  y_src     := targetRect.Top*inc_y_src + (acc_y_src div NewHeight);
     250  acc_y_src := acc_y_src mod NewHeight;
     251
     252  y_src     := y_src+ (bmp.Height div 4) div newheight;
     253  acc_y_src := acc_y_src+ (bmp.Height div 4) mod newheight;
     254
     255  y_src2     := y_src+ (bmp.Height div 2) div newheight;
     256  acc_y_src2 := acc_y_src+ (bmp.Height div 2) mod newheight;
     257  if acc_y_src2 > NewHeight then
     258  begin
     259    dec(acc_y_src2, NewHeight);
     260    inc(y_src2);
     261  end;
     262  horizSlightlyDifferent := (NewWidth > bmp.Width*2 div 3) and (NewWidth < bmp.Width*4 div 3);
     263  prevVertTransition:= tsNone;
     264  vertSlightlyDifferent := (NewHeight > bmp.Height*2 div 3) and (NewHeight < bmp.Height*4 div 3);
     265  for y_dest := targetRect.Top to targetRect.Bottom - 1 do
     266  begin
     267    if (y_src = prev_y_src) and (y_src2 = prev_y_src2) and not vertSlightlyDifferent then
     268    begin
     269      if tempData = nil then
     270        move((dest.ScanLine[y_dest-1+OffsetY]+OffsetX+targetRect.Left)^,(dest.ScanLine[y_dest+OffsetY]+OffsetX+targetRect.Left)^,(targetRect.Right-targetRect.Left)*sizeof(TBGRAPixel))
     271      else
     272        PutPixels(dest.ScanLine[y_dest+OffsetY]+OffsetX+targetRect.Left,tempData,targetRect.right-targetRect.left,ADrawMode,AOpacity);
     273    end else
     274    begin
     275      if tempData = nil then
     276         PDest := dest.ScanLine[y_dest+OffsetY]+OffsetX+targetRect.Left
     277      else
     278        PDest := tempData;
     279      PSrc1 := bmp.Scanline[y_src];
     280
     281      acc_x_src := targetRect.Left*mod_x_src;
     282      x_src     := targetRect.Left*inc_x_src + (acc_x_src div NewWidth);
     283      acc_x_src := acc_x_src mod NewWidth;
     284
     285      x_src     := x_src+ (bmp.Width div 4) div NewWidth;
     286      acc_x_src := acc_x_src+ (bmp.Width div 4) mod NewWidth;
     287
     288      DeltaSrcX := (bmp.Width div 2) div NewWidth;
     289      acc_x_src2 := acc_x_src+ (bmp.Width div 2) mod NewWidth;
     290      if acc_x_src2 > NewWidth then
     291      begin
     292        dec(acc_x_src2, NewWidth);
     293        inc(DeltaSrcX);
     294      end;
     295      inc(Psrc1, x_src);
     296      prevHorizTransition := tsNone;
     297
     298      if y_src2=y_src then
     299      begin
     300        horizTransition:= prevHorizTransition;
     301        for x_dest := targetRect.left to targetRect.right - 1 do
     302        begin
     303          LinearMix(psrc1, DeltaSrcX, acc_x_src2 >= NewWidth shr 2, PDest, horizSlightlyDifferent, horizTransition);
     304
     305          Inc(PSrc1, inc_x_src);
     306          Inc(acc_x_src, mod_x_src);
     307          if acc_x_src >= newWidth then
     308          begin
     309            Dec(acc_x_src, newWidth);
     310            Inc(PSrc1);
     311            dec(DeltaSrcX);
     312          end;
     313          Inc(acc_x_src2, mod_x_src);
     314          if acc_x_src2 >= newWidth then
     315          begin
     316            Dec(acc_x_src2, newWidth);
     317            Inc(DeltaSrcX);
     318          end;
     319          inc(PDest);
    126320        end;
    127         PDest[yb] := temp;
    128       end;
    129     end;
    130     Inc(y_dest, yFactor);
    131   end;
    132 
    133   Result.InvalidateBitmap;
    134 end;
    135 
    136 function SimpleStretchLarger(bmp: TBGRACustomBitmap;
    137   newWidth, newHeight: integer): TBGRACustomBitmap;
    138 var
    139   x_src, y_src: integer;
    140   inc_x_dest, mod_x_dest, acc_x_dest, inc_y_dest, mod_y_dest, acc_y_dest: integer;
    141   x_dest, y_dest, prev_x_dest, prev_y_dest: integer;
    142 
    143   xb, yb:      integer;
    144   srcColor:    TBGRAPixel;
    145   PDest, PSrc: PBGRAPixel;
    146   delta, lineDelta: integer;
    147 
    148 begin
    149   if (newWidth < bmp.Width) or (newHeight < bmp.Height) then
    150     raise ERangeError.Create('SimpleStretchLarger: New dimensions must be greater or equal ('+IntToStr(bmp.Width)+'x'+IntToStr(bmp.Height)+'->'+IntToStr(newWidth)+'x'+IntToStr(newHeight)+')');
    151 
    152   if ((newWidth div bmp.Width) * bmp.Width = newWidth) and
    153     ((newHeight div bmp.Height) * bmp.Height = newHeight) then
    154   begin
    155     Result := FastSimpleStretchLarger(bmp, newWidth div bmp.Width,
    156       newHeight div bmp.Height);
    157     exit;
    158   end;
    159 
    160   Result := bmp.NewBitmap(NewWidth, NewHeight);
    161   if (newWidth = 0) or (newHeight = 0) then
    162     exit;
    163 
    164   bmp.LoadFromBitmapIfNeeded;
    165 
    166   inc_x_dest := newwidth div bmp.Width;
    167   mod_x_dest := newwidth mod bmp.Width;
    168   inc_y_dest := newheight div bmp.Height;
    169   mod_y_dest := newheight mod bmp.Height;
    170 
    171   y_dest     := 0;
    172   acc_y_dest := bmp.Height div 2;
    173   if Result.LineOrder = riloTopToBottom then
    174     lineDelta := newWidth
    175   else
    176     lineDelta := -newWidth;
    177   for y_src := 0 to bmp.Height - 1 do
    178   begin
    179     prev_y_dest := y_dest;
    180     Inc(y_dest, inc_y_dest);
    181     Inc(acc_y_dest, mod_y_dest);
    182     if acc_y_dest >= bmp.Height then
    183     begin
    184       Dec(acc_y_dest, bmp.Height);
    185       Inc(y_dest);
    186     end;
    187 
    188     PSrc := bmp.Scanline[y_src];
    189 
    190     x_dest     := 0;
    191     acc_x_dest := bmp.Width div 2;
    192     for x_src := 0 to bmp.Width - 1 do
    193     begin
    194       prev_x_dest := x_dest;
    195       Inc(x_dest, inc_x_dest);
    196       Inc(acc_x_dest, mod_x_dest);
    197       if acc_x_dest >= bmp.Width then
    198       begin
    199         Dec(acc_x_dest, bmp.Width);
    200         Inc(x_dest);
    201       end;
    202 
    203       srcColor := PSrc^;
    204       Inc(PSrc);
    205 
    206       PDest := Result.scanline[prev_y_dest] + prev_x_dest;
    207       delta := lineDelta - (x_dest - prev_x_dest);
    208       for yb := prev_y_dest to y_dest - 1 do
    209       begin
    210         for xb := prev_x_dest to x_dest - 1 do
    211         begin
    212           PDest^ := srcColor;
    213           Inc(PDest);
     321        prevVertTransition:= tsPlain;
     322      end else
     323      begin
     324        PSrc2 := bmp.Scanline[y_src2]+x_src;
     325        for x_dest := targetRect.left to targetRect.right - 1 do
     326        begin
     327          horizTransition:= prevHorizTransition;
     328          LinearMix(psrc1, DeltaSrcX, acc_x_src2 >= NewWidth shr 2, @vertColors[1], horizSlightlyDifferent, horizTransition);
     329          horizTransition:= prevHorizTransition;
     330          LinearMix(psrc2, DeltaSrcX, acc_x_src2 >= NewWidth shr 2, @vertColors[2], horizSlightlyDifferent, horizTransition);
     331          prevHorizTransition:= horizTransition;
     332          vertTransition:= prevVertTransition;
     333          LinearMix(@vertColors[1],1,acc_y_src2 >= NewHeight shr 2,PDest,vertSlightlyDifferent,vertTransition);
     334
     335          Inc(PSrc1, inc_x_src);
     336          Inc(PSrc2, inc_x_src);
     337          Inc(acc_x_src, mod_x_src);
     338          if acc_x_src >= newWidth then
     339          begin
     340            Dec(acc_x_src, newWidth);
     341            Inc(PSrc1);
     342            Inc(PSrc2);
     343            dec(DeltaSrcX);
     344          end;
     345          Inc(acc_x_src2, mod_x_src);
     346          if acc_x_src2 >= newWidth then
     347          begin
     348            Dec(acc_x_src2, newWidth);
     349            Inc(DeltaSrcX);
     350          end;
     351          inc(PDest);
    214352        end;
    215         Inc(PDest, delta);
    216       end;
    217     end;
    218   end;
    219   Result.InvalidateBitmap;
    220 end;
    221 
    222 function SimpleStretchSmallerFactor2(source: TBGRACustomBitmap): TBGRACustomBitmap;
    223 var xb,yb: integer;
     353        prevVertTransition:= vertTransition;
     354      end;
     355
     356      if tempData <> nil then
     357         PutPixels(dest.ScanLine[y_dest+OffsetY]+OffsetX+targetRect.Left,tempData,targetRect.right-targetRect.left,ADrawMode,AOpacity);
     358    end;
     359
     360    prev_y_src := y_src;
     361    prev_y_src2 := y_src2;
     362
     363    Inc(y_src, inc_y_src);
     364    Inc(acc_y_src, mod_y_src);
     365    if acc_y_src >= newheight then
     366    begin
     367      Dec(acc_y_src, newheight);
     368      Inc(y_src);
     369    end;
     370
     371    Inc(y_src2, inc_y_src);
     372    Inc(acc_y_src2, mod_y_src);
     373    if acc_y_src2 >= newheight then
     374    begin
     375      Dec(acc_y_src2, newheight);
     376      Inc(y_src2);
     377    end;
     378  end;
     379  dest.InvalidateBitmap;
     380  if Assigned(tempData) then FreeMem(tempData);
     381end;
     382
     383procedure DownSamplePutImage2(source: TBGRACustomBitmap;
     384  dest: TBGRACustomBitmap; OffsetX, OffsetY: Integer; ADrawMode: TDrawMode);
     385const factorX = 2; factorY = 2; nbi= factorX*factorY;
     386var xb,yb,ys: NativeInt;
    224387    pdest: PBGRAPixel;
    225388    psrc1,psrc2: PBGRAPixel;
    226     asum: integer;
    227     a1,a2,a3,a4: integer;
    228     newWidth,newHeight: integer;
    229 begin
    230   newWidth := source.Width div 2;
    231   newHeight := source.Height div 2;
    232   result := source.NewBitmap(newWidth,newHeight);
     389    asum,maxsum: NativeUInt;
     390    newWidth,newHeight: NativeInt;
     391    r,g,b: NativeUInt;
     392begin
     393  if (source.Width mod factorX <> 0) or (source.Height mod factorY <> 0) then
     394     raise exception.Create('Source size must be a multiple of factorX and factorY');
     395  newWidth := source.Width div factorX;
     396  newHeight := source.Height div factorY;
     397  ys := 0;
     398  maxsum := 255*NativeInt(factorX)*NativeInt(factorY);
    233399  for yb := 0 to newHeight-1 do
    234400  begin
    235     pdest := result.ScanLine[yb];
    236     psrc1 := source.Scanline[yb shl 1];
    237     psrc2 := source.Scanline[yb shl 1+1];
     401    pdest := dest.ScanLine[yb+OffsetY]+OffsetX;
     402    psrc1 := source.Scanline[ys]; inc(ys);
     403    psrc2 := source.Scanline[ys]; inc(ys);
    238404    for xb := newWidth-1 downto 0 do
    239405    begin
    240       asum := psrc1^.alpha + (psrc1+1)^.alpha + psrc2^.alpha + (psrc2+1)^.alpha;
    241       if asum = 0 then
    242         pdest^ := BGRAPixelTransparent
    243       else if asum = 1020 then
     406      asum := 0;
     407      asum := psrc1^.alpha + psrc2^.alpha + (psrc1+1)^.alpha + (psrc2+1)^.alpha;
     408      if asum = maxsum then
    244409      begin
    245410        pdest^.alpha := 255;
    246         pdest^.red := (psrc1^.red + (psrc1+1)^.red + psrc2^.red + (psrc2+1)^.red + 2) shr 2;
    247         pdest^.green := (psrc1^.green + (psrc1+1)^.green + psrc2^.green + (psrc2+1)^.green+ 2) shr 2;
    248         pdest^.blue := (psrc1^.blue + (psrc1+1)^.blue + psrc2^.blue + (psrc2+1)^.blue+ 2) shr 2;
     411        r := psrc1^.red + psrc2^.red + (psrc1+1)^.red + (psrc2+1)^.red;
     412        g := psrc1^.green + psrc2^.green + (psrc1+1)^.green + (psrc2+1)^.green;
     413        b := psrc1^.blue + psrc2^.blue + (psrc1+1)^.blue + (psrc2+1)^.blue;
     414        inc(psrc1,factorX); inc(psrc2,factorX);
     415        pdest^.red := (r + (nbi shr 1)) shr 2;
     416        pdest^.green := (g + (nbi shr 1)) shr 2;
     417        pdest^.blue := (b + (nbi shr 1)) shr 2;
    249418      end else
    250       begin
    251         pdest^.alpha := asum shr 2;
    252         a1 := psrc1^.alpha;
    253         a2 := (psrc1+1)^.alpha;
    254         a3 := psrc2^.alpha;
    255         a4 := (psrc2+1)^.alpha;
    256         pdest^.red := (psrc1^.red*a1 + (psrc1+1)^.red*a2 + psrc2^.red*a3 + (psrc2+1)^.red*a4 + (asum shr 1)) div asum;
    257         pdest^.green := (psrc1^.green*a1 + (psrc1+1)^.green*a2 + psrc2^.green*a3 + (psrc2+1)^.green*a4+ (asum shr 1)) div asum;
    258         pdest^.blue := (psrc1^.blue*a1 + (psrc1+1)^.blue*a2 + psrc2^.blue*a3 + (psrc2+1)^.blue*a4+ (asum shr 1)) div asum;
    259       end;
    260       inc(psrc1,2);
    261       inc(psrc2,2);
     419      if ADrawMode <> dmSetExceptTransparent then
     420      begin
     421        if asum = 0 then
     422        begin
     423          if ADrawMode = dmSet then
     424            pdest^ := BGRAPixelTransparent;
     425          inc(psrc1,factorX); inc(psrc2,factorX);
     426        end
     427        else
     428        begin
     429          r := psrc1^.red*psrc1^.alpha + psrc2^.red*psrc2^.alpha + (psrc1+1)^.red*(psrc1+1)^.alpha + (psrc2+1)^.red*(psrc2+1)^.alpha;
     430          g := psrc1^.green*psrc1^.alpha + psrc2^.green*psrc2^.alpha + (psrc1+1)^.green*(psrc1+1)^.alpha + (psrc2+1)^.green*(psrc2+1)^.alpha;
     431          b := psrc1^.blue*psrc1^.alpha + psrc2^.blue*psrc2^.alpha + (psrc1+1)^.blue*(psrc1+1)^.alpha + (psrc2+1)^.blue*(psrc2+1)^.alpha;
     432          inc(psrc1,factorX); inc(psrc2,factorX);
     433          if ADrawMode = dmSet then
     434          begin
     435            pdest^.alpha := (asum + (nbi shr 1)) shr 2;
     436            pdest^.red := (r + (asum shr 1)) div asum;
     437            pdest^.green := (g + (asum shr 1)) div asum;
     438            pdest^.blue := (b + (asum shr 1)) div asum;
     439          end
     440          else
     441          begin
     442            if ADrawMode = dmDrawWithTransparency then
     443              DrawPixelInlineWithAlphaCheck(pdest,BGRA((r + (asum shr 1)) div asum,
     444                 (g + (asum shr 1)) div asum,
     445                 (b + (asum shr 1)) div asum,
     446                 (asum + (nbi shr 1)) shr 2)) else
     447             if ADrawMode = dmFastBlend then
     448               FastBlendPixelInline(pdest,BGRA((r + (asum shr 1)) div asum,
     449                  (g + (asum shr 1)) div asum,
     450                  (b + (asum shr 1)) div asum,
     451                  (asum + (nbi shr 1)) shr 2));
     452          end;
     453        end;
     454      end;
    262455      inc(pdest);
    263456    end;
     
    265458end;
    266459
    267 function SimpleStretchSmallerFactor4(source: TBGRACustomBitmap): TBGRACustomBitmap;
    268 var xb,yb: integer;
     460procedure DownSamplePutImage3(source: TBGRACustomBitmap;
     461  dest: TBGRACustomBitmap; OffsetX, OffsetY: Integer; ADrawMode: TDrawMode);
     462const factorX = 3; factorY = 3; nbi= factorX*factorY;
     463var xb,yb,ys: NativeInt;
    269464    pdest: PBGRAPixel;
    270     psrc1,psrc2,psrc3,psrc4: PBGRAPixel;
    271     asum: integer;
    272     a1,a2,a3,a4,
    273     a5,a6,a7,a8,
    274     a9,a10,a11,a12,
    275     a13,a14,a15,a16: integer;
    276     newWidth,newHeight: integer;
    277 begin
    278   newWidth := source.Width div 4;
    279   newHeight := source.Height div 4;
    280   result := source.NewBitmap(newWidth,newHeight);
     465    psrc1,psrc2,psrc3: PBGRAPixel;
     466    asum,maxsum: NativeUInt;
     467    newWidth,newHeight: NativeInt;
     468    r,g,b: NativeUInt;
     469begin
     470  if (source.Width mod factorX <> 0) or (source.Height mod factorY <> 0) then
     471     raise exception.Create('Source size must be a multiple of factorX and factorY');
     472  newWidth := source.Width div factorX;
     473  newHeight := source.Height div factorY;
     474  ys := 0;
     475  maxsum := 255*NativeInt(factorX)*NativeInt(factorY);
    281476  for yb := 0 to newHeight-1 do
    282477  begin
    283     pdest := result.ScanLine[yb];
    284     psrc1 := source.Scanline[yb shl 2];
    285     psrc2 := source.Scanline[yb shl 2+1];
    286     psrc3 := source.Scanline[yb shl 2+2];
    287     psrc4 := source.Scanline[yb shl 2+3];
     478    pdest := dest.ScanLine[yb+OffsetY]+OffsetX;
     479    psrc1 := source.Scanline[ys]; inc(ys);
     480    psrc2 := source.Scanline[ys]; inc(ys);
     481    psrc3 := source.Scanline[ys]; inc(ys);
    288482    for xb := newWidth-1 downto 0 do
    289483    begin
    290       asum := psrc1^.alpha + (psrc1+1)^.alpha + (psrc1+2)^.alpha + (psrc1+3)^.alpha +
    291               psrc2^.alpha + (psrc2+1)^.alpha + (psrc2+2)^.alpha + (psrc2+3)^.alpha +
    292               psrc3^.alpha + (psrc3+1)^.alpha + (psrc3+2)^.alpha + (psrc3+3)^.alpha +
    293               psrc4^.alpha + (psrc4+1)^.alpha + (psrc4+2)^.alpha + (psrc4+3)^.alpha;
    294       if asum = 0 then
    295         pdest^ := BGRAPixelTransparent
    296       else if asum = 4080 then
     484      asum := 0;
     485      asum := psrc1^.alpha + psrc2^.alpha + psrc3^.alpha
     486           + (psrc1+1)^.alpha + (psrc2+1)^.alpha + (psrc3+1)^.alpha
     487           + (psrc1+2)^.alpha + (psrc2+2)^.alpha + (psrc3+2)^.alpha;
     488      if asum = maxsum then
    297489      begin
    298490        pdest^.alpha := 255;
    299         pdest^.red := (psrc1^.red + (psrc1+1)^.red + (psrc1+2)^.red + (psrc1+3)^.red +
    300               psrc2^.red + (psrc2+1)^.red + (psrc2+2)^.red + (psrc2+3)^.red +
    301               psrc3^.red + (psrc3+1)^.red + (psrc3+2)^.red + (psrc3+3)^.red +
    302               psrc4^.red + (psrc4+1)^.red + (psrc4+2)^.red + (psrc4+3)^.red + 8) shr 4;
    303         pdest^.green := (psrc1^.green + (psrc1+1)^.green + (psrc1+2)^.green + (psrc1+3)^.green +
    304               psrc2^.green + (psrc2+1)^.green + (psrc2+2)^.green + (psrc2+3)^.green +
    305               psrc3^.green + (psrc3+1)^.green + (psrc3+2)^.green + (psrc3+3)^.green +
    306               psrc4^.green + (psrc4+1)^.green + (psrc4+2)^.green + (psrc4+3)^.green + 8) shr 4;
    307         pdest^.blue := (psrc1^.blue + (psrc1+1)^.blue + (psrc1+2)^.blue + (psrc1+3)^.blue +
    308               psrc2^.blue + (psrc2+1)^.blue + (psrc2+2)^.blue + (psrc2+3)^.blue +
    309               psrc3^.blue + (psrc3+1)^.blue + (psrc3+2)^.blue + (psrc3+3)^.blue +
    310               psrc4^.blue + (psrc4+1)^.blue + (psrc4+2)^.blue + (psrc4+3)^.blue + 8) shr 4;
     491        r := psrc1^.red + psrc2^.red + psrc3^.red
     492           + (psrc1+1)^.red + (psrc2+1)^.red + (psrc3+1)^.red
     493           + (psrc1+2)^.red + (psrc2+2)^.red + (psrc3+2)^.red;
     494        g := psrc1^.green + psrc2^.green + psrc3^.green
     495           + (psrc1+1)^.green + (psrc2+1)^.green + (psrc3+1)^.green
     496           + (psrc1+2)^.green + (psrc2+2)^.green + (psrc3+2)^.green;
     497        b := psrc1^.blue + psrc2^.blue + psrc3^.blue
     498           + (psrc1+1)^.blue + (psrc2+1)^.blue + (psrc3+1)^.blue
     499           + (psrc1+2)^.blue + (psrc2+2)^.blue + (psrc3+2)^.blue;
     500        inc(psrc1,factorX); inc(psrc2,factorX); inc(psrc3,factorX);
     501        pdest^.red := (r + (nbi shr 1)) div 9;
     502        pdest^.green := (g + (nbi shr 1)) div 9;
     503        pdest^.blue := (b + (nbi shr 1)) div 9;
    311504      end else
    312       begin
    313         pdest^.alpha := asum shr 4;
    314         a1 := psrc1^.alpha;
    315         a2 := (psrc1+1)^.alpha;
    316         a3 := (psrc1+2)^.alpha;
    317         a4 := (psrc1+3)^.alpha;
    318         a5 := psrc2^.alpha;
    319         a6 := (psrc2+1)^.alpha;
    320         a7 := (psrc2+2)^.alpha;
    321         a8 := (psrc2+3)^.alpha;
    322         a9 := psrc3^.alpha;
    323         a10 := (psrc3+1)^.alpha;
    324         a11 := (psrc3+2)^.alpha;
    325         a12 := (psrc3+3)^.alpha;
    326         a13 := psrc4^.alpha;
    327         a14 := (psrc4+1)^.alpha;
    328         a15 := (psrc4+2)^.alpha;
    329         a16 := (psrc4+3)^.alpha;
    330         pdest^.red := (psrc1^.red*a1 + (psrc1+1)^.red*a2 + (psrc1+2)^.red*a3 + (psrc1+3)^.red*a4 +
    331               psrc2^.red*a5 + (psrc2+1)^.red*a6 + (psrc2+2)^.red*a7 + (psrc2+3)^.red*a8 +
    332               psrc3^.red*a9 + (psrc3+1)^.red*a10 + (psrc3+2)^.red*a11 + (psrc3+3)^.red*a12 +
    333               psrc4^.red*a13 + (psrc4+1)^.red*a14 + (psrc4+2)^.red*a15 + (psrc4+3)^.red*a16 + (asum shr 1)) div asum;
    334         pdest^.green := (psrc1^.green*a1 + (psrc1+1)^.green*a2 + (psrc1+2)^.green*a3 + (psrc1+3)^.green*a4 +
    335               psrc2^.green*a5 + (psrc2+1)^.green*a6 + (psrc2+2)^.green*a7 + (psrc2+3)^.green*a8 +
    336               psrc3^.green*a9 + (psrc3+1)^.green*a10 + (psrc3+2)^.green*a11 + (psrc3+3)^.green*a12 +
    337               psrc4^.green*a13 + (psrc4+1)^.green*a14 + (psrc4+2)^.green*a15 + (psrc4+3)^.green*a16 + (asum shr 1)) div asum;
    338         pdest^.blue := (psrc1^.blue*a1 + (psrc1+1)^.blue*a2 + (psrc1+2)^.blue*a3 + (psrc1+3)^.blue*a4 +
    339               psrc2^.blue*a5 + (psrc2+1)^.blue*a6 + (psrc2+2)^.blue*a7 + (psrc2+3)^.blue*a8 +
    340               psrc3^.blue*a9 + (psrc3+1)^.blue*a10 + (psrc3+2)^.blue*a11 + (psrc3+3)^.blue*a12 +
    341               psrc4^.blue*a13 + (psrc4+1)^.blue*a14 + (psrc4+2)^.blue*a15 + (psrc4+3)^.blue*a16 + (asum shr 1)) div asum;
    342       end;
    343       inc(psrc1,4);
    344       inc(psrc2,4);
    345       inc(psrc3,4);
    346       inc(psrc4,4);
     505      if ADrawMode <> dmSetExceptTransparent then
     506      begin
     507        if asum = 0 then
     508        begin
     509          if ADrawMode = dmSet then
     510            pdest^ := BGRAPixelTransparent;
     511          inc(psrc1,factorX); inc(psrc2,factorX); inc(psrc3,factorX);
     512        end
     513        else
     514        begin
     515          r := psrc1^.red*psrc1^.alpha + psrc2^.red*psrc2^.alpha + psrc3^.red*psrc3^.alpha
     516            + (psrc1+1)^.red*(psrc1+1)^.alpha + (psrc2+1)^.red*(psrc2+1)^.alpha + (psrc3+1)^.red*(psrc3+1)^.alpha
     517            + (psrc1+2)^.red*(psrc1+2)^.alpha + (psrc2+2)^.red*(psrc2+2)^.alpha + (psrc3+2)^.red*(psrc3+2)^.alpha;
     518          g := psrc1^.green*psrc1^.alpha + psrc2^.green*psrc2^.alpha + psrc3^.green*psrc3^.alpha
     519            + (psrc1+1)^.green*(psrc1+1)^.alpha + (psrc2+1)^.green*(psrc2+1)^.alpha + (psrc3+1)^.green*(psrc3+1)^.alpha
     520            + (psrc1+2)^.green*(psrc1+2)^.alpha + (psrc2+2)^.green*(psrc2+2)^.alpha + (psrc3+2)^.green*(psrc3+2)^.alpha;
     521          b := psrc1^.blue*psrc1^.alpha + psrc2^.blue*psrc2^.alpha + psrc3^.blue*psrc3^.alpha
     522            + (psrc1+1)^.blue*(psrc1+1)^.alpha + (psrc2+1)^.blue*(psrc2+1)^.alpha + (psrc3+1)^.blue*(psrc3+1)^.alpha
     523            + (psrc1+2)^.blue*(psrc1+2)^.alpha + (psrc2+2)^.blue*(psrc2+2)^.alpha + (psrc3+2)^.blue*(psrc3+2)^.alpha;
     524          inc(psrc1,factorX); inc(psrc2,factorX); inc(psrc3,factorX);
     525          if ADrawMode = dmSet then
     526          begin
     527            pdest^.alpha := (asum + (nbi shr 1)) div 9;
     528            pdest^.red := (r + (asum shr 1)) div asum;
     529            pdest^.green := (g + (asum shr 1)) div asum;
     530            pdest^.blue := (b + (asum shr 1)) div asum;
     531          end
     532          else
     533          begin
     534            if ADrawMode = dmDrawWithTransparency then
     535              DrawPixelInlineWithAlphaCheck(pdest,BGRA((r + (asum shr 1)) div asum,
     536                 (g + (asum shr 1)) div asum,
     537                 (b + (asum shr 1)) div asum,
     538                 (asum + (nbi shr 1)) div 9)) else
     539             if ADrawMode = dmFastBlend then
     540               FastBlendPixelInline(pdest,BGRA((r + (asum shr 1)) div asum,
     541                  (g + (asum shr 1)) div asum,
     542                  (b + (asum shr 1)) div asum,
     543                  (asum + (nbi shr 1)) div 9));
     544          end;
     545        end;
     546      end;
    347547      inc(pdest);
    348548    end;
     
    350550end;
    351551
    352 function SimpleStretchSmallerFactor(source: TBGRACustomBitmap; fx,fy: integer): TBGRACustomBitmap;
    353 var xb,yb,ys,iy,ix: integer;
    354     pdest: PBGRAPixel;
     552procedure DownSamplePutImage(source: TBGRACustomBitmap; factorX, factorY: integer;
     553  dest: TBGRACustomBitmap; OffsetX, OffsetY: Integer; ADrawMode: TDrawMode);
     554var xb,yb,ys,iy,ix: NativeInt;
     555    pdest,psrci: PBGRAPixel;
    355556    psrc: array of PBGRAPixel;
    356     psrci: PBGRAPixel;
    357     asum,maxsum: integer;
    358     newWidth,newHeight: integer;
    359     r,g,b,nbi: integer;
    360 begin
    361   newWidth := source.Width div fx;
    362   newHeight := source.Height div fy;
    363   result := source.NewBitmap(newWidth,newHeight);
     557    asum,maxsum: NativeUInt;
     558    newWidth,newHeight: NativeInt;
     559    r,g,b,nbi: NativeUInt;
     560begin
     561  if ADrawMode = dmXor then raise exception.Create('dmXor drawmode not supported');
     562  if (factorX = 2) and (factorY = 2) then
     563  begin
     564     DownSamplePutImage2(source,dest,OffsetX,OffsetY,ADrawMode);
     565     exit;
     566  end;
     567  if (factorX = 3) and (factorY = 3) then
     568  begin
     569     DownSamplePutImage3(source,dest,OffsetX,OffsetY,ADrawMode);
     570     exit;
     571  end;
     572  if (source.Width mod factorX <> 0) or (source.Height mod factorY <> 0) then
     573     raise exception.Create('Source size must be a multiple of factorX and factorY');
     574  newWidth := source.Width div factorX;
     575  newHeight := source.Height div factorY;
    364576  ys := 0;
    365   maxsum := 255*fx*fy;
    366   nbi := fx*fy;
    367   setlength(psrc, fy);
     577  maxsum := 255*NativeInt(factorX)*NativeInt(factorY);
     578  nbi := factorX*factorY;
     579  setlength(psrc, factorY);
    368580  for yb := 0 to newHeight-1 do
    369581  begin
    370     pdest := result.ScanLine[yb];
    371     for iy := fy-1 downto 0 do
     582    pdest := dest.ScanLine[yb+OffsetY]+OffsetX;
     583    for iy := factorY-1 downto 0 do
    372584    begin
    373585      psrc[iy] := source.Scanline[ys];
     
    377589    begin
    378590      asum := 0;
    379       for iy := fy-1 downto 0 do
     591      for iy := factorY-1 downto 0 do
    380592      begin
    381593        psrci := psrc[iy];
    382         for ix := fx-1 downto 0 do
     594        for ix := factorX-1 downto 0 do
    383595          asum += (psrci+ix)^.alpha;
    384596      end;
    385       if asum = 0 then
    386         pdest^ := BGRAPixelTransparent
    387       else if asum = maxsum then
     597      if asum = maxsum then
    388598      begin
    389599        pdest^.alpha := 255;
     
    391601        g := 0;
    392602        b := 0;
    393         for iy := fy-1 downto 0 do
    394         begin
    395           psrci := psrc[iy];
    396           for ix := fx-1 downto 0 do
     603        for iy := factorY-1 downto 0 do
     604          for ix := factorX-1 downto 0 do
    397605          begin
    398             with (psrci+ix)^ do
     606            with psrc[iy]^ do
    399607            begin
    400608              r += red;
     
    402610              b += blue;
    403611            end;
     612            inc(psrc[iy]);
    404613          end;
    405         end;
    406614        pdest^.red := (r + (nbi shr 1)) div nbi;
    407615        pdest^.green := (g + (nbi shr 1)) div nbi;
    408616        pdest^.blue := (b + (nbi shr 1)) div nbi;
    409617      end else
    410       begin
    411         pdest^.alpha := (asum + (nbi shr 1)) div nbi;
    412         r := 0;
    413         g := 0;
    414         b := 0;
    415         for iy := fy-1 downto 0 do
    416         begin
    417           psrci := psrc[iy];
    418           for ix := fx-1 downto 0 do
     618      if ADrawMode <> dmSetExceptTransparent then
     619      begin
     620        if asum = 0 then
     621        begin
     622          if ADrawMode = dmSet then
     623            pdest^ := BGRAPixelTransparent;
     624          for iy := factorY-1 downto 0 do
     625            inc(psrc[iy],factorX);
     626        end
     627        else
     628        begin
     629          r := 0;
     630          g := 0;
     631          b := 0;
     632          for iy := factorY-1 downto 0 do
     633            for ix := factorX-1 downto 0 do
     634            begin
     635              with psrc[iy]^ do
     636              begin
     637                r += red*alpha;
     638                g += green*alpha;
     639                b += blue*alpha;
     640              end;
     641              inc(psrc[iy]);
     642            end;
     643          if ADrawMode = dmSet then
    419644          begin
    420             with (psrci+ix)^ do
    421             begin
    422               r += integer(red)*integer(alpha);
    423               g += integer(green)*integer(alpha);
    424               b += integer(blue)*integer(alpha);
    425             end;
     645            pdest^.alpha := (asum + (nbi shr 1)) div nbi;
     646            pdest^.red := (r + (asum shr 1)) div asum;
     647            pdest^.green := (g + (asum shr 1)) div asum;
     648            pdest^.blue := (b + (asum shr 1)) div asum;
     649          end
     650          else
     651          begin
     652            if ADrawMode = dmDrawWithTransparency then
     653              DrawPixelInlineWithAlphaCheck(pdest,BGRA((r + (asum shr 1)) div asum,
     654                 (g + (asum shr 1)) div asum,
     655                 (b + (asum shr 1)) div asum,
     656                 (asum + (nbi shr 1)) div nbi)) else
     657             if ADrawMode = dmFastBlend then
     658               FastBlendPixelInline(pdest,BGRA((r + (asum shr 1)) div asum,
     659                  (g + (asum shr 1)) div asum,
     660                  (b + (asum shr 1)) div asum,
     661                  (asum + (nbi shr 1)) div nbi));
    426662          end;
    427663        end;
    428         pdest^.red := (r + (asum shr 1)) div asum;
    429         pdest^.green := (g + (asum shr 1)) div asum;
    430         pdest^.blue := (b + (asum shr 1)) div asum;
    431       end;
    432       for iy := fy-1 downto 0 do
    433         inc(psrc[iy],fx);
     664      end;
    434665      inc(pdest);
    435666    end;
     
    437668end;
    438669
    439 function SimpleStretchSmaller(bmp: TBGRACustomBitmap;
    440   newWidth, newHeight: integer): TBGRACustomBitmap;
    441 var
    442   x_dest, y_dest: integer;
    443   inc_x_src, mod_x_src, acc_x_src, inc_y_src, mod_y_src, acc_y_src: integer;
    444   x_src, y_src, prev_x_src, prev_y_src: integer;
    445   x_src2, y_src2: integer;
    446 
    447   xb, yb: integer;
    448   v1, v2, v3, v4, v4shr1: int64;
    449   nb,a:     integer;
    450   pdest, psrc, psrcscan: PBGRAPixel;
    451   lineDelta, delta: integer;
    452 
    453 begin
    454   if (newWidth > bmp.Width) or (newHeight > bmp.Height) then
    455     raise ERangeError.Create('SimpleStretchSmaller: New dimensions must be smaller or equal ('+IntToStr(bmp.Width)+'x'+IntToStr(bmp.Height)+'->'+IntToStr(newWidth)+'x'+IntToStr(newHeight)+')');
    456 
    457   if (newWidth = 0) or (newHeight = 0) or (bmp.Width = 0) or (bmp.Height = 0) then
    458   begin
    459     Result := bmp.NewBitmap(NewWidth, NewHeight);
    460     exit;
    461   end;
    462 
    463   if (newWidth*2 = bmp.Width) and (newHeight*2 = bmp.Height) then
    464   begin
    465     result := SimpleStretchSmallerFactor2(bmp);
    466     exit
    467   end
    468   else
    469   if (newWidth*4 = bmp.Width) and (newHeight*4 = bmp.Height) then
    470   begin
    471     result := SimpleStretchSmallerFactor4(bmp);
    472     exit;
    473   end
    474   else
    475   if (newWidth < bmp.Width) and (newHeight < bmp.Height) and
    476      (bmp.Width mod newWidth = 0) and (bmp.Height mod newHeight = 0) then
    477   begin
    478     result := SimpleStretchSmallerFactor(bmp, bmp.Width div newWidth, bmp.Height div newHeight);
    479     exit;
    480   end;
    481 
    482   Result := bmp.NewBitmap(NewWidth, NewHeight);
    483 
    484   bmp.LoadFromBitmapIfNeeded;
    485 
    486   inc_x_src := bmp.Width div newWidth;
    487   mod_x_src := bmp.Width mod newWidth;
    488   inc_y_src := bmp.Height div newHeight;
    489   mod_y_src := bmp.Height mod newHeight;
    490 
    491   if bmp.lineOrder = riloTopToBottom then
    492     lineDelta := bmp.Width
    493   else
    494     lineDelta := -bmp.Width;
    495 
    496   y_src     := 0;
    497   acc_y_src := 0;
    498   for y_dest := 0 to newHeight - 1 do
    499   begin
    500     PDest := Result.ScanLine[y_dest];
    501 
    502     prev_y_src := y_src;
    503     Inc(y_src, inc_y_src);
    504     Inc(acc_y_src, mod_y_src);
    505     if acc_y_src >= newHeight then
    506     begin
    507       Dec(acc_y_src, newHeight);
    508       Inc(y_src);
    509     end;
    510     if y_src > prev_y_src then
    511       y_src2 := y_src - 1
    512     else
    513       y_src2 := y_src;
    514     psrcscan := bmp.Scanline[prev_y_src];
    515 
    516     x_src     := 0;
    517     acc_x_src := 0;
    518     for x_dest := 0 to newWidth - 1 do
    519     begin
    520       prev_x_src := x_src;
    521       Inc(x_src, inc_x_src);
    522       Inc(acc_x_src, mod_x_src);
    523       if acc_x_src >= newWidth then
    524       begin
    525         Dec(acc_x_src, newWidth);
    526         Inc(x_src);
    527       end;
    528       if x_src > prev_x_src then
    529         x_src2 := x_src - 1
    530       else
    531         x_src2 := x_src;
    532 
    533       v1    := 0;
    534       v2    := 0;
    535       v3    := 0;
    536       v4    := 0;
    537       nb    := 0;
    538       delta := lineDelta - (x_src2 - prev_x_src + 1);
    539 
    540       PSrc  := psrcscan + prev_x_src;
    541       for yb := prev_y_src to y_src2 do
    542       begin
    543         for xb := prev_x_src to x_src2 do
    544         begin
    545           with PSrc^ do
    546           begin
    547             a := alpha;
    548                     {$HINTS OFF}
    549             v1 += integer(red) * a;
    550             v2 += integer(green) * a;
    551             v3 += integer(blue) * a;
    552                     {$HINTS ON}
    553           end;
    554           v4 += a;
    555           Inc(PSrc);
    556           Inc(nb);
    557         end;
    558         Inc(PSrc, delta);
    559       end;
    560 
    561       if (v4 <> 0) and (nb <> 0) then
    562       begin
    563         v4shr1  := v4 shr 1;
    564         with PDest^ do
    565         begin
    566           red   := (v1 + v4shr1) div v4;
    567           green := (v2 + v4shr1) div v4;
    568           blue  := (v3 + v4shr1) div v4;
    569           alpha := (v4 + (nb shr 1)) div nb;
    570         end;
    571       end
    572       else
    573        PDest^ := BGRAPixelTransparent;
    574 
    575       Inc(PDest);
    576     end;
    577   end;
    578   Result.InvalidateBitmap;
    579 end;
    580 
    581 function SimpleStretch(bmp: TBGRACustomBitmap;
    582   NewWidth, NewHeight: integer): TBGRACustomBitmap;
    583 var
    584   temp, newtemp: TBGRACustomBitmap;
    585 begin
    586   if (NewWidth = bmp.Width) and (NewHeight = bmp.Height) then
    587     Result := bmp.Duplicate
    588   else
    589   if (NewWidth >= bmp.Width) and (NewHeight >= bmp.Height) then
    590     Result := SimpleStretchLarger(bmp, NewWidth, NewHeight)
    591   else
    592   if (NewWidth <= bmp.Width) and (NewHeight <= bmp.Height) then
    593     Result := SimpleStretchSmaller(bmp, NewWidth, NewHeight)
    594   else
    595   begin
    596     temp := bmp;
    597 
    598     if NewWidth < bmp.Width then
    599     begin
    600       newtemp := SimpleStretchSmaller(temp, NewWidth, temp.Height);
    601       if (temp <> bmp) then
    602         temp.Free;
    603       temp := newtemp;
    604     end;
    605 
    606     if NewHeight < bmp.Height then
    607     begin
    608       newtemp := SimpleStretchSmaller(temp, temp.Width, NewHeight);
    609       if (temp <> bmp) then
    610         temp.Free;
    611       temp := newtemp;
    612     end;
    613 
    614     if NewWidth > bmp.Width then
    615     begin
    616       newtemp := SimpleStretchLarger(temp, NewWidth, temp.Height);
    617       if (temp <> bmp) then
    618         temp.Free;
    619       temp := newtemp;
    620     end;
    621 
    622     if NewHeight > bmp.Height then
    623     begin
    624       newtemp := SimpleStretchLarger(temp, temp.Width, NewHeight);
    625       if (temp <> bmp) then
    626         temp.Free;
    627       temp := newtemp;
    628     end;
    629 
    630     if temp <> bmp then
    631       Result := temp
    632     else
    633       Result := bmp.Duplicate;
    634   end;
     670function DownSample(source: TBGRACustomBitmap; factorX, factorY: integer): TBGRACustomBitmap;
     671begin
     672  if (source.Width mod factorX <> 0) or (source.Height mod factorY <> 0) then
     673     raise exception.Create('Source size must be a multiple of factorX and factorY');
     674  result := source.NewBitmap(source.Width div factorX, source.Height div factorY);
     675  DownSamplePutImage(source,factorX,factorY,result,0,0,dmSet);
    635676end;
    636677
     
    639680function FineInterpolation(t: single; ResampleFilter: TResampleFilter): single;
    640681begin
    641   if ResampleFilter = rfLinear then
    642     result := t else
     682  if ResampleFilter <= rfLinear then
     683  begin
     684    if ResampleFilter = rfBox then
     685    begin
     686       result := round(t);
     687    end else
     688      result := t;
     689  end else
    643690  begin
    644691    if t <= 0.5 then
     
    651698function FineInterpolation256(t256: integer; ResampleFilter: TResampleFilter): integer;
    652699begin
    653   if ResampleFilter = rfLinear then
    654     result := t256 else
     700  if ResampleFilter <= rfLinear then
     701  begin
     702    if ResampleFilter = rfBox then
     703    begin
     704      if t256 < 128 then
     705        result := 0
     706      else
     707        result := 256;
     708    end
     709    else
     710      result := t256;
     711  end else
    655712  begin
    656713    if t256 <= 128 then
     
    751808begin
    752809  Result := 2;
     810end;
     811
     812{ TLanczosKernel }
     813{ by stab }
     814procedure TLanczosKernel.SetNumberOfLobes(AValue: integer);
     815begin
     816  if AValue < 1 then AValue := 1;
     817  if FNumberOfLobes=AValue then Exit;
     818  FNumberOfLobes:=AValue;
     819  if AValue = 1 then FFactor := 1.5 else FFactor := AValue;
     820end;
     821
     822constructor TLanczosKernel.Create(ANumberOfLobes: integer);
     823begin
     824  NumberOfLobes:= ANumberOfLobes;
     825end;
     826
     827function TLanczosKernel.Interpolation(t: single): single;
     828var Pi_t: ValReal;
     829begin
     830  if t = 0 then
     831    Result := 1
     832  else if t < FNumberOfLobes then
     833  begin
     834    Pi_t := pi * t;
     835    Result := FFactor * sin(Pi_t) * sin(Pi_t / FNumberOfLobes) /
     836      (Pi_t * Pi_t)
     837  end
     838  else
     839    Result := 0;
     840end;
     841
     842function TLanczosKernel.ShouldCheckRange: boolean;
     843begin
     844  Result := True;
     845end;
     846
     847function TLanczosKernel.KernelWidth: single;
     848begin
     849  Result := FNumberOfLobes;
    753850end;
    754851
     
    10901187  tempFilter1,tempFilter2: TWideKernelFilter;
    10911188begin
     1189  if (NewWidth = bmp.Width) and (NewHeight = bmp.Height) then
     1190  begin
     1191    Result := bmp.Duplicate;
     1192    exit;
     1193  end;
    10921194  case ResampleFilter of
    10931195    rfBicubic: //blur
     
    11081210    begin
    11091211      tempFilter1 := TSplineKernel.Create;
     1212      result := WideKernelResample(bmp,NewWidth,NewHeight,tempFilter1,tempFilter1);
     1213      tempFilter1.Free;
     1214      exit;
     1215    end;
     1216    rfLanczos2,rfLanczos3,rfLanczos4:
     1217    begin
     1218      tempFilter1 := TLanczosKernel.Create(ord(ResampleFilter)-ord(rfLanczos2)+2);
    11101219      result := WideKernelResample(bmp,NewWidth,NewHeight,tempFilter1,tempFilter1);
    11111220      tempFilter1.Free;
     
    11231232  end;
    11241233
    1125   if (NewWidth = bmp.Width) and (NewHeight = bmp.Height) then
    1126     Result := bmp.Duplicate
    1127   else
    11281234  if (NewWidth >= bmp.Width) and (NewHeight >= bmp.Height) then
    11291235    Result := FineResampleLarger(bmp, NewWidth, NewHeight, ResampleFilter)
  • GraphicTest/Packages/bgrabitmap/bgrascene3d.pas

    r452 r472  
    99
    1010type
     11  TProjection3D = BGRAMatrix3D.TProjection3D;
     12  TBox3D = record
     13    min,max: TPoint3D;
     14  end;
     15
     16  TLightingNormal3D = (lnNone, lnFace, lnVertex, lnFaceVertexMix);
     17  TLightingInterpolation3D = (liLowQuality, liSpecularHighQuality, liAlwaysHighQuality);
     18  TAntialiasingMode3D = (am3dNone, am3dMultishape, am3dResample);
     19  TPerspectiveMode3D = (pmLinearMapping, pmPerspectiveMapping, pmZBuffer);
     20
     21  TRenderingOptions = record
     22    LightingInterpolation: TLightingInterpolation3D;
     23    AntialiasingMode: TAntialiasingMode3D;
     24    AntialiasingResampleLevel: integer;
     25    PerspectiveMode: TPerspectiveMode3D;
     26    TextureInterpolation: boolean;
     27    MinZ: single;
     28  end;
     29
    1130  PSceneLightingContext = ^TSceneLightingContext;
    1231  TSceneLightingContext = packed record
     
    2342    SaturationHigh: integer;
    2443    SaturationHighF: single;
    25   end;
    26 
    27   TProjection3D = packed record
    28     Zoom, Center: TPointF;
    29   end;
    30 
    31   TBox3D = record
    32     min,max: TPoint3D;
    33   end;
    34 
    35   TLightingNormal3D = (lnNone, lnFace, lnVertex, lnFaceVertexMix);
    36   TLightingInterpolation3D = (liLowQuality, liSpecularHighQuality, liAlwaysHighQuality);
    37   TAntialiasingMode3D = (am3dNone, am3dMultishape, am3dResample);
    38   TPerspectiveMode3D = (pmLinearMapping, pmPerspectiveMapping, pmZBuffer);
    39 
    40 type
    41   TRenderingOptions = record
    42     LightingInterpolation: TLightingInterpolation3D;
    43     AntialiasingMode: TAntialiasingMode3D;
    44     AntialiasingResampleLevel: integer;
    45     PerspectiveMode: TPerspectiveMode3D;
    46     TextureInterpolation: boolean;
    47     MinZ: single;
    4844  end;
    4945
     
    8177    function GetLightCount: integer;
    8278    function GetMaterial(AIndex: integer): IBGRAMaterial3D;
     79    function GetNormalCount: integer;
    8380    function GetObject(AIndex: integer): IBGRAObject3D;
    8481    function GetVertexCount: integer;
     
    111108    function ApplyNoLighting(Context: PSceneLightingContext; Color: TBGRAPixel): TBGRAPixel; virtual;
    112109    procedure UseMaterial(AMaterialName: string; AFace: IBGRAFace3D); virtual;
     110    function FetchTexture({%H-}AName: string; out texSize: TPointF): IBGRAScanner; virtual;
    113111
    114112  public
     
    116114    DefaultMaterial : IBGRAMaterial3D;
    117115    RenderingOptions: TRenderingOptions;
     116    UnknownColor: TBGRAPixel;
    118117
    119118    constructor Create;
    120119    constructor Create(ASurface: TBGRACustomBitmap);
    121120    destructor Destroy; override;
    122     procedure Clear;
     121    procedure Clear; virtual;
    123122    function LoadObjectFromFile(AFilename: string; SwapFacesOrientation: boolean = true): IBGRAObject3D;
     123    function LoadObjectFromFileUTF8(AFilename: string; SwapFacesOrientation: boolean = true): IBGRAObject3D;
     124    function LoadObjectFromStream(AStream: TStream; SwapFacesOrientation: boolean = true): IBGRAObject3D;
     125    procedure LoadMaterialsFromFile(AFilename: string);
     126    procedure LoadMaterialsFromFileUTF8(AFilename: string);
     127    procedure LoadMaterialsFromStream(AStream: TStream);
    124128    procedure LookAt(AWhere: TPoint3D; ATopDir: TPoint3D);
    125129    procedure LookLeft(angleDeg: single);
     
    145149    function CreateMaterial: IBGRAMaterial3D;
    146150    function CreateMaterial(ASpecularIndex: integer): IBGRAMaterial3D;
     151    function GetMaterialByName(AName: string): IBGRAMaterial3D;
    147152    procedure UpdateMaterials; virtual;
    148153    procedure UpdateMaterial(AMaterialName: string); virtual;
     154    procedure ForEachVertex(ACallback: TVertex3DCallback);
     155    procedure ForEachFace(ACallback: TFace3DCallback);
    149156    property ViewCenter: TPointF read GetViewCenter write SetViewCenter;
    150157    property AutoViewCenter: boolean read FAutoViewCenter write SetAutoViewCenter;
     
    154161    property Object3DCount: integer read FObjectCount;
    155162    property VertexCount: integer read GetVertexCount;
     163    property NormalCount: integer read GetNormalCount;
    156164    property FaceCount: integer read GetFaceCount;
    157165    property Zoom: TPointF read GetZoom write SetZoom;
     
    169177implementation
    170178
    171 uses BGRAPolygon, BGRAPolygonAliased, BGRACoordPool3D;
     179uses BGRAPolygon, BGRAPolygonAliased, BGRACoordPool3D, BGRAResample,
     180  lazutf8classes;
    172181
    173182{$i lightingclasses3d.inc}
     183{$i vertex3d.inc}
     184{$i face3d.inc}
    174185
    175186type
     
    200211    function AddFace(const AVertices: array of IBGRAVertex3D; AColors: array of TBGRAPixel): IBGRAFace3D;
    201212    function AddFaceReversed(const AVertices: array of IBGRAVertex3D): IBGRAFace3D;
    202     procedure ComputeWithMatrix(const AMatrix: TMatrix3D; const AProjection: TProjection3D);
     213    procedure ComputeWithMatrix(constref AMatrix: TMatrix3D; constref AProjection: TProjection3D);
    203214    function GetColor: TBGRAPixel;
    204215    function GetLight: Single;
     
    210221    function GetFaceCount: integer;
    211222    function GetTotalVertexCount: integer;
     223    function GetTotalNormalCount: integer;
    212224    function GetMaterial: IBGRAMaterial3D;
    213225    procedure SetLightingNormal(const AValue: TLightingNormal3D);
     
    222234    function GetRefCount: integer;
    223235    procedure SetBiface(AValue : boolean);
    224   end;
    225 
    226 {$i shape3D.inc}
    227 
    228 type
    229   { TBGRAPart3D }
    230 
    231   TBGRAPart3D = class(TInterfacedObject,IBGRAPart3D)
    232   private
    233     FVertices: array of IBGRAVertex3D;
    234     FVertexCount: integer;
    235     FMatrix: TMatrix3D;
    236     FParts: array of IBGRAPart3D;
    237     FPartCount: integer;
    238     FContainer: IBGRAPart3D;
    239     FCoordPool: TBGRACoordPool3D;
    240   public
    241     constructor Create(AContainer: IBGRAPart3D);
    242     destructor Destroy; override;
    243     procedure Clear(ARecursive: boolean);
    244     function Add(x,y,z: single): IBGRAVertex3D;
    245     function Add(pt: TPoint3D): IBGRAVertex3D;
    246     function Add(pt: TPoint3D_128): IBGRAVertex3D;
    247     function Add(const coords: array of single): arrayOfIBGRAVertex3D;
    248     function Add(const pts: array of TPoint3D): arrayOfIBGRAVertex3D;
    249     function Add(const pts: array of TPoint3D_128): arrayOfIBGRAVertex3D;
    250     procedure Add(const pts: array of IBGRAVertex3D);
    251     procedure Add(AVertex: IBGRAVertex3D);
    252     procedure RemoveVertex(Index: integer);
    253     function GetBoundingBox: TBox3D;
    254     function GetRadius: single;
    255     function GetMatrix: TMatrix3D;
    256     function GetPart(AIndex: Integer): IBGRAPart3D;
    257     function GetPartCount: integer;
    258     function GetVertex(AIndex: Integer): IBGRAVertex3D;
    259     function GetVertexCount: integer;
    260     function GetTotalVertexCount: integer;
    261     function GetContainer: IBGRAPart3D;
    262     procedure SetVertex(AIndex: Integer; const AValue: IBGRAVertex3D);
    263     procedure ResetTransform;
    264     procedure Translate(x,y,z: single; Before: boolean = true);
    265     procedure Translate(ofs: TPoint3D; Before: boolean = true);
    266     procedure Scale(size: single; Before: boolean = true);
    267     procedure Scale(x,y,z: single; Before: boolean = true);
    268     procedure Scale(size: TPoint3D; Before: boolean = true);
    269     procedure RotateXDeg(angle: single; Before: boolean = true);
    270     procedure RotateYDeg(angle: single; Before: boolean = true);
    271     procedure RotateZDeg(angle: single; Before: boolean = true);
    272     procedure RotateXRad(angle: single; Before: boolean = true);
    273     procedure RotateYRad(angle: single; Before: boolean = true);
    274     procedure RotateZRad(angle: single; Before: boolean = true);
    275     procedure SetMatrix(const AValue: TMatrix3D);
    276     procedure ComputeWithMatrix(const AMatrix: TMatrix3D; const AProjection: TProjection3D);
    277     function ComputeCoordinate(var ASceneCoord: TPoint3D_128; const AProjection: TProjection3D): TPointF;
    278     procedure NormalizeViewNormal;
    279     function CreatePart: IBGRAPart3D;
    280     procedure LookAt(ALookWhere,ATopDir: TPoint3D);
    281     procedure RemoveUnusedVertices;
    282     function IndexOf(AVertex: IBGRAVertex3D): integer;
    283   end;
    284 
    285   { TBGRAFace3D }
    286 
    287   PBGRAFaceVertexDescription = ^TBGRAFaceVertexDescription;
    288   TBGRAFaceVertexDescription = record
    289        Vertex: IBGRAVertex3D;
    290        Color: TBGRAPixel;
    291        TexCoord: TPointF;
    292        ColorOverride: boolean;
    293        TexCoordOverride: boolean;
    294      end;
    295 
    296   TBGRAFace3D = class(TInterfacedObject,IBGRAFace3D)
    297   private
    298     FVertices: packed array of TBGRAFaceVertexDescription;
    299     FVertexCount: integer;
    300     FTexture: IBGRAScanner;
    301     FMaterial: IBGRAMaterial3D;
    302     FMaterialName: string;
    303     FParentTexture: boolean;
    304     FViewNormal: TPoint3D_128;
    305     FViewCenter: TPoint3D_128;
    306     FObject3D : IBGRAObject3D;
    307     FBiface: boolean;
    308     FLightThroughFactor: single;
    309     FLightThroughFactorOverride: boolean;
    310     function GetVertexDescription(AIndex : integer): PBGRAFaceVertexDescription;
    311   public
    312     function GetObject3D: IBGRAObject3D;
    313     constructor Create(AObject3D: IBGRAObject3D; AVertices: array of IBGRAVertex3D);
    314     destructor Destroy; override;
    315     procedure AddVertex(AVertex: IBGRAVertex3D);
    316     function GetParentTexture: boolean;
    317     function GetTexture: IBGRAScanner;
    318     function GetVertex(AIndex: Integer): IBGRAVertex3D;
    319     function GetVertexColor(AIndex: Integer): TBGRAPixel;
    320     function GetVertexColorOverride(AIndex: Integer): boolean;
    321     function GetVertexCount: integer;
    322     function GetMaterial: IBGRAMaterial3D;
    323     function GetMaterialName: string;
    324     function GetTexCoord(AIndex: Integer): TPointF;
    325     function GetTexCoordOverride(AIndex: Integer): boolean;
    326     function GetViewNormal: TPoint3D;
    327     function GetViewNormal_128: TPoint3D_128;
    328     function GetViewCenter: TPoint3D;
    329     function GetViewCenter_128: TPoint3D_128;
    330     function GetViewCenterZ: single;
    331     function GetBiface: boolean;
    332     function GetLightThroughFactor: single;
    333     function GetLightThroughFactorOverride: boolean;
    334     procedure SetParentTexture(const AValue: boolean);
    335     procedure SetTexture(const AValue: IBGRAScanner);
    336     procedure SetColor(AColor: TBGRAPixel);
    337     procedure SetVertexColor(AIndex: Integer; const AValue: TBGRAPixel);
    338     procedure SetVertexColorOverride(AIndex: Integer; const AValue: boolean);
    339     procedure SetTexCoord(AIndex: Integer; const AValue: TPointF);
    340     procedure SetTexCoordOverride(AIndex: Integer; const AValue: boolean);
    341     procedure SetBiface(const AValue: boolean);
    342     procedure SetLightThroughFactor(const AValue: single);
    343     procedure SetLightThroughFactorOverride(const AValue: boolean);
    344     procedure SetVertex(AIndex: Integer; const AValue: IBGRAVertex3D);
    345     procedure ComputeViewNormalAndCenter;
    346     procedure SetMaterial(const AValue: IBGRAMaterial3D);
    347     procedure SetMaterialName(const AValue: string);
    348     function GetAsObject: TObject;
    349     property Texture: IBGRAScanner read GetTexture write SetTexture;
    350     property ParentTexture: boolean read GetParentTexture write SetParentTexture;
    351     property VertexCount: integer read GetVertexCount;
    352     property Vertex[AIndex: Integer]: IBGRAVertex3D read GetVertex write SetVertex;
    353     property VertexColor[AIndex: Integer]: TBGRAPixel read GetVertexColor write SetVertexColor;
    354     property VertexColorOverride[AIndex: Integer]: boolean read GetVertexColorOverride write SetVertexColorOverride;
    355     property TexCoord[AIndex: Integer]: TPointF read GetTexCoord write SetTexCoord;
    356     property TexCoordOverride[AIndex: Integer]: boolean read GetTexCoordOverride write SetTexCoordOverride;
    357     property ViewNormal: TPoint3D read GetViewNormal;
    358     property ViewNormal_128: TPoint3D_128 read GetViewNormal_128;
    359     property ViewCenter: TPoint3D read GetViewCenter;
    360     property ViewCenter_128: TPoint3D_128 read GetViewCenter_128;
    361     property ViewCenterZ: single read GetViewCenterZ;
    362     property Object3D: IBGRAObject3D read GetObject3D;
    363     property Biface: boolean read GetBiface write SetBiface;
    364     property LightThroughFactor: single read GetLightThroughFactor write SetLightThroughFactor;
    365     property LightThroughFactorOverride: boolean read GetLightThroughFactorOverride write SetLightThroughFactorOverride;
    366     property Material: IBGRAMaterial3D read GetMaterial write SetMaterial;
    367     property VertexDescription[AIndex : integer]: PBGRAFaceVertexDescription read GetVertexDescription;
    368   end;
    369 
    370   { TBGRAVertex3D }
    371 
    372   TBGRAVertex3D = class(TInterfacedObject,IBGRAVertex3D)
    373   private
    374     FColor: TBGRAPixel;
    375     FParentColor: boolean;
    376     FLight: Single;
    377     FTexCoord: TPointF;
    378     FCoordPool: TBGRACoordPool3D;
    379     FCoordPoolIndex: integer;
    380     function GetCoordData: PBGRACoordData3D;
    381     procedure Init(ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D_128);
    382   public
    383     constructor Create(ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D); overload;
    384     constructor Create(ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D_128); overload;
    385     destructor Destroy; override;
    386     function GetColor: TBGRAPixel;
    387     function GetLight: Single;
    388     function GetViewNormal: TPoint3D;
    389     function GetViewNormal_128: TPoint3D_128;
    390     function GetSceneCoord: TPoint3D;
    391     function GetSceneCoord_128: TPoint3D_128;
    392     function GetTexCoord: TPointF;
    393     function GetViewCoord: TPoint3D;
    394     function GetViewCoord_128: TPoint3D_128;
    395     function GetUsage: integer;
    396     procedure SetColor(const AValue: TBGRAPixel);
    397     procedure SetLight(const AValue: Single);
    398     procedure SetViewNormal(const AValue: TPoint3D);
    399     procedure SetViewNormal_128(const AValue: TPoint3D_128);
    400     procedure NormalizeViewNormal;
    401     procedure AddViewNormal(const AValue: TPoint3D_128);
    402     procedure SetSceneCoord(const AValue: TPoint3D);
    403     procedure SetSceneCoord_128(const AValue: TPoint3D_128);
    404     procedure SetTexCoord(const AValue: TPointF);
    405     procedure SetViewCoord(const AValue: TPoint3D);
    406     procedure SetViewCoord_128(const AValue: TPoint3D_128);
    407     function GetViewCoordZ: single;
    408     function GetParentColor: Boolean;
    409     procedure SetParentColor(const AValue: Boolean);
    410     function GetProjectedCoord: TPointF;
    411     procedure SetProjectedCoord(const AValue: TPointF);
    412     procedure ComputeCoordinateAndClearNormal(const AMatrix: TMatrix3D; const AProjection: TProjection3D);
    413     property SceneCoord: TPoint3D read GetSceneCoord write SetSceneCoord;
    414     property SceneCoord_128: TPoint3D_128 read GetSceneCoord_128 write SetSceneCoord_128;
    415     property ViewCoord: TPoint3D read GetViewCoord write SetViewCoord;
    416     property ViewCoord_128: TPoint3D_128 read GetViewCoord_128 write SetViewCoord_128;
    417     property ViewCoordZ: single read GetViewCoordZ;
    418     property ProjectedCoord: TPointF read GetProjectedCoord write SetProjectedCoord;
    419     property TexCoord: TPointF read GetTexCoord write SetTexCoord;
    420     property Color: TBGRAPixel read GetColor write SetColor;
    421     property ParentColor: Boolean read GetParentColor write SetParentColor;
    422     property Light: Single read GetLight write SetLight;
    423     property ViewNormal: TPoint3D read GetViewNormal write SetViewNormal;
    424     property ViewNormal_128: TPoint3D_128 read GetViewNormal_128 write SetViewNormal_128;
    425     property Usage: integer read GetUsage;
    426     property CoordData: PBGRACoordData3D read GetCoordData;
    427     function GetAsObject: TObject;
    428   end;
    429 
    430 { TBGRAVertex3D }
    431 
    432 procedure TBGRAVertex3D.Init(ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D_128);
    433 begin
    434   FCoordPool := ACoordPool;
    435   FCoordPoolIndex := FCoordPool.Add;
    436   FColor := BGRAWhite;
    437   FParentColor := True;
    438   FLight := 1;
    439   SceneCoord_128 := ASceneCoord;
    440 end;
    441 
    442 function TBGRAVertex3D.GetCoordData: PBGRACoordData3D;
    443 begin
    444   result := FCoordPool.CoordData[FCoordPoolIndex];
    445 end;
    446 
    447 constructor TBGRAVertex3D.Create(ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D);
    448 begin
    449   Init(ACoordPool, Point3D_128(ASceneCoord));
    450 end;
    451 
    452 constructor TBGRAVertex3D.Create(ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D_128);
    453 begin
    454   Init(ACoordPool, ASceneCoord);
    455 end;
    456 
    457 destructor TBGRAVertex3D.Destroy;
    458 begin
    459   FCoordPool.Remove(FCoordPoolIndex);
    460   inherited Destroy;
    461 end;
    462 
    463 function TBGRAVertex3D.GetColor: TBGRAPixel;
    464 begin
    465   result := FColor;
    466 end;
    467 
    468 function TBGRAVertex3D.GetLight: Single;
    469 begin
    470   result := FLight;
    471 end;
    472 
    473 function TBGRAVertex3D.GetViewNormal: TPoint3D;
    474 begin
    475   result := Point3D(FCoordPool.CoordData[FCoordPoolIndex]^.viewNormal);
    476 end;
    477 
    478 function TBGRAVertex3D.GetViewNormal_128: TPoint3D_128;
    479 begin
    480   result := FCoordPool.CoordData[FCoordPoolIndex]^.viewNormal;
    481 end;
    482 
    483 function TBGRAVertex3D.GetSceneCoord: TPoint3D;
    484 begin
    485   result := Point3D(FCoordPool.CoordData[FCoordPoolIndex]^.sceneCoord);
    486 end;
    487 
    488 function TBGRAVertex3D.GetSceneCoord_128: TPoint3D_128;
    489 begin
    490   result := FCoordPool.CoordData[FCoordPoolIndex]^.sceneCoord;
    491 end;
    492 
    493 function TBGRAVertex3D.GetTexCoord: TPointF;
    494 begin
    495   result := FTexCoord;
    496 end;
    497 
    498 function TBGRAVertex3D.GetViewCoord: TPoint3D;
    499 begin
    500   result := Point3D(FCoordPool.CoordData[FCoordPoolIndex]^.viewCoord);
    501 end;
    502 
    503 function TBGRAVertex3D.GetViewCoord_128: TPoint3D_128;
    504 begin
    505   result := FCoordPool.CoordData[FCoordPoolIndex]^.viewCoord;
    506 end;
    507 
    508 function TBGRAVertex3D.GetUsage: integer;
    509 begin
    510   result := frefcount;
    511 end;
    512 
    513 procedure TBGRAVertex3D.SetColor(const AValue: TBGRAPixel);
    514 begin
    515   FColor := AValue;
    516   FParentColor := false;
    517 end;
    518 
    519 procedure TBGRAVertex3D.SetLight(const AValue: Single);
    520 begin
    521   FLight := AValue;
    522 end;
    523 
    524 procedure TBGRAVertex3D.SetViewNormal(const AValue: TPoint3D);
    525 begin
    526   FCoordPool.CoordData[FCoordPoolIndex]^.viewNormal := Point3D_128(AValue);
    527 end;
    528 
    529 procedure TBGRAVertex3D.SetViewNormal_128(const AValue: TPoint3D_128);
    530 begin
    531   FCoordPool.CoordData[FCoordPoolIndex]^.viewNormal := AValue;
    532 end;
    533 
    534 procedure TBGRAVertex3D.SetSceneCoord(const AValue: TPoint3D);
    535 begin
    536   FCoordPool.CoordData[FCoordPoolIndex]^.sceneCoord := Point3D_128(AValue);
    537 end;
    538 
    539 procedure TBGRAVertex3D.SetSceneCoord_128(const AValue: TPoint3D_128);
    540 begin
    541   FCoordPool.CoordData[FCoordPoolIndex]^.sceneCoord := AValue;
    542 end;
    543 
    544 procedure TBGRAVertex3D.SetTexCoord(const AValue: TPointF);
    545 begin
    546   FTexCoord := AValue;
    547 end;
    548 
    549 procedure TBGRAVertex3D.SetViewCoord(const AValue: TPoint3D);
    550 begin
    551   FCoordPool.CoordData[FCoordPoolIndex]^.viewCoord := Point3D_128(AValue);
    552 end;
    553 
    554 procedure TBGRAVertex3D.SetViewCoord_128(const AValue: TPoint3D_128);
    555 begin
    556   FCoordPool.CoordData[FCoordPoolIndex]^.viewCoord := AValue;
    557 end;
    558 
    559 function TBGRAVertex3D.GetViewCoordZ: single;
    560 begin
    561   result := FCoordPool.CoordData[FCoordPoolIndex]^.viewCoord.Z;
    562 end;
    563 
    564 function TBGRAVertex3D.GetParentColor: Boolean;
    565 begin
    566   result := FParentColor;
    567 end;
    568 
    569 procedure TBGRAVertex3D.SetParentColor(const AValue: Boolean);
    570 begin
    571   FParentColor := AValue;
    572 end;
    573 
    574 function TBGRAVertex3D.GetProjectedCoord: TPointF;
    575 begin
    576   result := FCoordPool.CoordData[FCoordPoolIndex]^.projectedCoord;
    577 end;
    578 
    579 procedure TBGRAVertex3D.SetProjectedCoord(const AValue: TPointF);
    580 begin
    581   FCoordPool.CoordData[FCoordPoolIndex]^.projectedCoord := AValue;
    582 end;
    583 
    584 procedure TBGRAVertex3D.ComputeCoordinateAndClearNormal(const AMatrix: TMatrix3D; const AProjection : TProjection3D);
    585 var P: PBGRACoordData3D;
    586 begin
    587   P := FCoordPool.CoordData[FCoordPoolIndex];
    588   with p^ do
    589   begin
    590     viewCoord := AMatrix*sceneCoord;
    591     ClearPoint3D_128(viewNormal);
    592     if viewCoord.z > 0 then
    593     begin
    594       InvZ := 1/viewCoord.z;
    595       projectedCoord := PointF(viewCoord.x*InvZ*AProjection.Zoom.x + AProjection.Center.x,
    596                                viewCoord.y*InvZ*AProjection.Zoom.Y + AProjection.Center.y);
    597     end else
    598       projectedCoord := PointF(0,0);
    599   end;
    600 end;
    601 
    602 function TBGRAVertex3D.GetAsObject: TObject;
    603 begin
    604   result := self;
    605 end;
    606 
    607 procedure TBGRAVertex3D.NormalizeViewNormal;
    608 begin
    609   Normalize3D_128(FCoordPool.CoordData[FCoordPoolIndex]^.viewNormal);
    610 end;
    611 
    612 procedure TBGRAVertex3D.AddViewNormal(const AValue: TPoint3D_128);
    613 begin
    614   Add3D_Aligned(FCoordPool.CoordData[FCoordPoolIndex]^.viewNormal, AValue);
    615 end;
    616 
    617 { TBGRAFace3D }
    618 
    619 function TBGRAFace3D.GetVertexDescription(AIndex : integer
    620   ): PBGRAFaceVertexDescription;
    621 begin
    622   result := @FVertices[AIndex];
    623 end;
    624 
    625 function TBGRAFace3D.GetObject3D: IBGRAObject3D;
    626 begin
    627   result := FObject3D;
    628 end;
    629 
    630 constructor TBGRAFace3D.Create(AObject3D: IBGRAObject3D;
    631   AVertices: array of IBGRAVertex3D);
    632 var
    633   i: Integer;
    634 begin
    635   SetLength(FVertices, length(AVertices));
    636   for i:= 0 to high(AVertices) do
    637     AddVertex(AVertices[i]);
    638   FObject3D := AObject3D;
    639   FBiface := false;
    640   FParentTexture := True;
    641   FLightThroughFactor:= 0;
    642   FLightThroughFactorOverride:= false;
    643 end;
    644 
    645 destructor TBGRAFace3D.Destroy;
    646 begin
    647   fillchar(FTexture,sizeof(FTexture),0);
    648   inherited Destroy;
    649 end;
    650 
    651 procedure TBGRAFace3D.AddVertex(AVertex: IBGRAVertex3D);
    652 begin
    653   if FVertexCount = length(FVertices) then
    654     setlength(FVertices, FVertexCount*2+3);
    655   with FVertices[FVertexCount] do
    656   begin
    657     Color := BGRAWhite;
    658     ColorOverride := false;
    659     TexCoord := PointF(0,0);
    660     TexCoordOverride := false;
    661     Vertex := AVertex;
    662   end;
    663   inc(FVertexCount);
    664 end;
    665 
    666 function TBGRAFace3D.GetParentTexture: boolean;
    667 begin
    668   result := FParentTexture;
    669 end;
    670 
    671 function TBGRAFace3D.GetTexture: IBGRAScanner;
    672 begin
    673   result := FTexture;
    674 end;
    675 
    676 function TBGRAFace3D.GetVertex(AIndex: Integer): IBGRAVertex3D;
    677 begin
    678   if (AIndex < 0) or (AIndex >= FVertexCount) then
    679     raise Exception.Create('Index out of bounds');
    680   result := FVertices[AIndex].Vertex;
    681 end;
    682 
    683 procedure TBGRAFace3D.SetVertex(AIndex: Integer; const AValue: IBGRAVertex3D);
    684 begin
    685   if (AIndex < 0) or (AIndex >= FVertexCount) then
    686     raise Exception.Create('Index out of bounds');
    687   FVertices[AIndex].Vertex := AValue;
    688 end;
    689 
    690 function TBGRAFace3D.GetVertexColor(AIndex: Integer): TBGRAPixel;
    691 begin
    692   if (AIndex < 0) or (AIndex >= FVertexCount) then
    693     raise Exception.Create('Index out of bounds');
    694   result := FVertices[AIndex].Color;
    695 end;
    696 
    697 function TBGRAFace3D.GetVertexColorOverride(AIndex: Integer): boolean;
    698 begin
    699   if (AIndex < 0) or (AIndex >= FVertexCount) then
    700     raise Exception.Create('Index out of bounds');
    701   result := FVertices[AIndex].ColorOverride;
    702 end;
    703 
    704 function TBGRAFace3D.GetVertexCount: integer;
    705 begin
    706   result := FVertexCount;
    707 end;
    708 
    709 function TBGRAFace3D.GetMaterial: IBGRAMaterial3D;
    710 begin
    711   result := FMaterial;
    712 end;
    713 
    714 function TBGRAFace3D.GetMaterialName: string;
    715 begin
    716   result := FMaterialName;
    717 end;
    718 
    719 procedure TBGRAFace3D.SetParentTexture(const AValue: boolean);
    720 begin
    721   FParentTexture := AValue;
    722 end;
    723 
    724 procedure TBGRAFace3D.SetTexture(const AValue: IBGRAScanner);
    725 begin
    726   FTexture := AValue;
    727   FParentTexture := false;
    728 end;
    729 
    730 procedure TBGRAFace3D.SetColor(AColor: TBGRAPixel);
    731 var i: integer;
    732 begin
    733   for i := 0 to GetVertexCount-1 do
    734     SetVertexColor(i,AColor);
    735 end;
    736 
    737 procedure TBGRAFace3D.SetVertexColor(AIndex: Integer; const AValue: TBGRAPixel
    738   );
    739 begin
    740   if (AIndex < 0) or (AIndex >= FVertexCount) then
    741     raise Exception.Create('Index out of bounds');
    742   with FVertices[AIndex] do
    743   begin
    744     Color := AValue;
    745     ColorOverride := true;
    746   end;
    747 end;
    748 
    749 procedure TBGRAFace3D.SetVertexColorOverride(AIndex: Integer;
    750   const AValue: boolean);
    751 begin
    752   if (AIndex < 0) or (AIndex >= FVertexCount) then
    753     raise Exception.Create('Index out of bounds');
    754   FVertices[AIndex].ColorOverride := AValue;
    755 end;
    756 
    757 function TBGRAFace3D.GetTexCoord(AIndex: Integer): TPointF;
    758 begin
    759   if (AIndex < 0) or (AIndex >= FVertexCount) then
    760     raise Exception.Create('Index out of bounds');
    761   result := FVertices[AIndex].TexCoord;
    762 end;
    763 
    764 function TBGRAFace3D.GetTexCoordOverride(AIndex: Integer): boolean;
    765 begin
    766   if (AIndex < 0) or (AIndex >= FVertexCount) then
    767     raise Exception.Create('Index out of bounds');
    768   result := FVertices[AIndex].TexCoordOverride;
    769 end;
    770 
    771 procedure TBGRAFace3D.SetTexCoord(AIndex: Integer; const AValue: TPointF);
    772 begin
    773   if (AIndex < 0) or (AIndex >= FVertexCount) then
    774     raise Exception.Create('Index out of bounds');
    775   FVertices[AIndex].TexCoord := AValue;
    776   FVertices[AIndex].TexCoordOverride := true;
    777 end;
    778 
    779 procedure TBGRAFace3D.SetTexCoordOverride(AIndex: Integer; const AValue: boolean
    780   );
    781 begin
    782   if (AIndex < 0) or (AIndex >= FVertexCount) then
    783     raise Exception.Create('Index out of bounds');
    784   FVertices[AIndex].TexCoordOverride := AValue;
    785 end;
    786 
    787 function TBGRAFace3D.GetViewNormal: TPoint3D;
    788 begin
    789   result := Point3D(FViewNormal);
    790 end;
    791 
    792 function TBGRAFace3D.GetViewNormal_128: TPoint3D_128;
    793 begin
    794   result := FViewNormal;
    795 end;
    796 
    797 function TBGRAFace3D.GetViewCenter: TPoint3D;
    798 begin
    799   result := Point3D(FViewCenter);
    800 end;
    801 
    802 function TBGRAFace3D.GetViewCenter_128: TPoint3D_128;
    803 begin
    804   result := FViewCenter;
    805 end;
    806 
    807 function TBGRAFace3D.GetViewCenterZ: single;
    808 begin
    809   result := FViewCenter.Z;
    810 end;
    811 
    812 function TBGRAFace3D.GetBiface: boolean;
    813 begin
    814   result := FBiface;
    815 end;
    816 
    817 procedure TBGRAFace3D.SetBiface(const AValue: boolean);
    818 begin
    819   FBiface := AValue;
    820 end;
    821 
    822 function TBGRAFace3D.GetLightThroughFactor: single;
    823 begin
    824   result := FLightThroughFactor;
    825 end;
    826 
    827 function TBGRAFace3D.GetLightThroughFactorOverride: boolean;
    828 begin
    829   result := FLightThroughFactorOverride;
    830 end;
    831 
    832 procedure TBGRAFace3D.SetLightThroughFactor(const AValue: single);
    833 begin
    834   if AValue < 0 then
    835     FLightThroughFactor := 0
    836   else
    837     FLightThroughFactor:= AValue;
    838   FLightThroughFactorOverride := true;
    839 end;
    840 
    841 procedure TBGRAFace3D.SetLightThroughFactorOverride(const AValue: boolean);
    842 begin
    843   FLightThroughFactorOverride := AValue;
    844 end;
    845 
    846 procedure TBGRAFace3D.ComputeViewNormalAndCenter;
    847 var v1,v2: TPoint3D_128;
    848   i: Integer;
    849   p0,p1,p2: IBGRAVertex3D;
    850 begin
    851   if FVertexCount < 3 then
    852     ClearPoint3D_128(FViewNormal)
    853   else
    854   begin
    855     p0 := FVertices[0].Vertex;
    856     p1 := FVertices[1].Vertex;
    857     p2 := FVertices[2].Vertex;
    858     v1 := p1.ViewCoord_128 - p0.ViewCoord_128;
    859     v2 := p2.ViewCoord_128 - p1.ViewCoord_128;
    860     VectProduct3D_128(v2,v1,FViewNormal);
    861     Normalize3D_128(FViewNormal);
    862     for i := 0 to FVertexCount-1 do
    863       FVertices[i].Vertex.AddViewNormal(FViewNormal);
    864   end;
    865   ClearPoint3D_128(FViewCenter);
    866   if FVertexCount > 0 then
    867   begin
    868     for i := 0 to FVertexCount-1 do
    869       FViewCenter += FVertices[i].Vertex.ViewCoord_128;
    870     FViewCenter *= 1/FVertexCount;
    871   end;
    872 end;
    873 
    874 procedure TBGRAFace3D.SetMaterial(const AValue: IBGRAMaterial3D);
    875 begin
    876   FMaterial := AValue;
    877 end;
    878 
    879 procedure TBGRAFace3D.SetMaterialName(const AValue: string);
    880 begin
    881   if AValue <> FMaterialName then
    882   begin
    883     FMaterialName := AValue;
    884     FObject3D.Scene.UseMaterial(FMaterialName, self);
    885   end;
    886 end;
    887 
    888 function TBGRAFace3D.GetAsObject: TObject;
    889 begin
    890   result := self;
    891 end;
    892 
    893 { TBGRAPart3D }
    894 
    895 procedure TBGRAPart3D.LookAt(ALookWhere,ATopDir: TPoint3D);
    896 var ZDir, XDir, YDir: TPoint3D_128;
    897     ViewPoint: TPoint3D_128;
    898     CurPart: IBGRAPart3D;
    899     ComposedMatrix: TMatrix3D;
    900 begin
    901   YDir := -Point3D_128(ATopDir);
    902   if IsPoint3D_128_Zero(YDir) then exit;
    903   Normalize3D_128(YDir);
    904 
    905   ComposedMatrix := FMatrix;
    906   CurPart := self.FContainer;
    907   while CurPart <> nil do
    908   begin
    909     ComposedMatrix := CurPart.Matrix*ComposedMatrix;
    910     CurPart := CurPart.Container;
    911   end;
    912   ViewPoint := ComposedMatrix*Point3D_128_Zero;
    913 
    914   ZDir := Point3D_128(ALookWhere)-ViewPoint;
    915   if IsPoint3D_128_Zero(ZDir) then exit;
    916   Normalize3D_128(ZDir);
    917 
    918   VectProduct3D_128(YDir,ZDir,XDir);
    919   VectProduct3D_128(ZDir,XDir,YDir); //correct Y dir
    920 
    921   FMatrix := Matrix3D(XDir,YDir,ZDir,ViewPoint);
    922   ComposedMatrix := MatrixIdentity3D;
    923   CurPart := self.FContainer;
    924   while CurPart <> nil do
    925   begin
    926     ComposedMatrix := CurPart.Matrix*ComposedMatrix;
    927     CurPart := CurPart.Container;
    928   end;
    929   FMatrix := MatrixInverse3D(ComposedMatrix)*FMatrix;
    930 end;
    931 
    932 procedure TBGRAPart3D.RemoveUnusedVertices;
    933 var
    934   i: Integer;
    935 begin
    936   for i := FVertexCount-1 downto 0 do
    937     if FVertices[i].Usage <= 2 then RemoveVertex(i);
    938   for i := 0 to FPartCount-1 do
    939     FParts[i].RemoveUnusedVertices;
    940 end;
    941 
    942 function TBGRAPart3D.IndexOf(AVertex: IBGRAVertex3D): integer;
    943 var i: integer;
    944 begin
    945   for i := 0 to FVertexCount-1 do
    946     if FVertices[i] = AVertex then
    947     begin
    948       result := i;
    949       exit;
    950     end;
    951   result := -1;
    952 end;
    953 
    954 procedure TBGRAPart3D.Add(AVertex: IBGRAVertex3D);
    955 begin
    956   if FVertexCount = length(FVertices) then
    957     setlength(FVertices, FVertexCount*2+3);
    958   FVertices[FVertexCount] := AVertex;
    959   inc(FVertexCount);
    960 end;
    961 
    962 procedure TBGRAPart3D.RemoveVertex(Index: integer);
    963 var i: integer;
    964 begin
    965   if (Index >= 0) and (Index < FVertexCount) then
    966   begin
    967     for i := Index to FVertexCount-2 do
    968       FVertices[i] := FVertices[i+1];
    969     FVertices[FVertexCount-1] := nil;
    970     dec(FVertexCount);
    971   end;
    972 end;
    973 
    974 function TBGRAPart3D.GetRadius: single;
    975 var i: integer;
    976     pt: TPoint3D_128;
    977     d: single;
    978 begin
    979   result := 0;
    980   for i := 0 to GetVertexCount-1 do
    981   begin
    982     pt := GetVertex(i).SceneCoord_128;
    983     d:= sqrt(DotProduct3D_128(pt,pt));
    984     if d > result then result := d;
    985   end;
    986 end;
    987 
    988 constructor TBGRAPart3D.Create(AContainer: IBGRAPart3D);
    989 begin
    990   FContainer := AContainer;
    991   FMatrix := MatrixIdentity3D;
    992   FCoordPool := TBGRACoordPool3D.Create(4);
    993 end;
    994 
    995 destructor TBGRAPart3D.Destroy;
    996 begin
    997   FCoordPool.Free;
    998   inherited Destroy;
    999 end;
    1000 
    1001 procedure TBGRAPart3D.Clear(ARecursive: boolean);
    1002 var i: integer;
    1003 begin
    1004   FVertices := nil;
    1005   FVertexCount := 0;
    1006   if ARecursive then
    1007   begin
    1008     for i := 0 to FPartCount-1 do
    1009       FParts[i].Clear(ARecursive);
    1010     FParts := nil;
    1011     FPartCount := 0;
    1012   end;
    1013 end;
    1014 
    1015 function TBGRAPart3D.Add(x, y, z: single): IBGRAVertex3D;
    1016 begin
    1017   result := TBGRAVertex3D.Create(FCoordPool,Point3D(x,y,z));
    1018   Add(result);
    1019 end;
    1020 
    1021 function TBGRAPart3D.Add(pt: TPoint3D): IBGRAVertex3D;
    1022 begin
    1023   result := TBGRAVertex3D.Create(FCoordPool,pt);
    1024   Add(result);
    1025 end;
    1026 
    1027 function TBGRAPart3D.Add(pt: TPoint3D_128): IBGRAVertex3D;
    1028 begin
    1029   result := TBGRAVertex3D.Create(FCoordPool,pt);
    1030   Add(result);
    1031 end;
    1032 
    1033 function TBGRAPart3D.Add(const coords: array of single
    1034   ): arrayOfIBGRAVertex3D;
    1035 var pts: array of TPoint3D;
    1036     CoordsIdx: integer;
    1037     i: Integer;
    1038 begin
    1039   if length(coords) mod 3 <> 0 then
    1040     raise exception.Create('Array size must be a multiple of 3');
    1041   setlength(pts, length(coords) div 3);
    1042   coordsIdx := 0;
    1043   for i := 0 to high(pts) do
    1044   begin
    1045     pts[i] := Point3D(coords[CoordsIdx],coords[CoordsIdx+1],coords[CoordsIdx+2]);
    1046     inc(coordsIdx,3);
    1047   end;
    1048   result := Add(pts);
    1049 end;
    1050 
    1051 function TBGRAPart3D.Add(const pts: array of TPoint3D): arrayOfIBGRAVertex3D;
    1052 var
    1053   i: Integer;
    1054 begin
    1055   setlength(result, length(pts));
    1056   for i := 0 to high(pts) do
    1057     result[i] := TBGRAVertex3D.Create(FCoordPool,pts[i]);
    1058   Add(result);
    1059 end;
    1060 
    1061 function TBGRAPart3D.Add(const pts: array of TPoint3D_128
    1062   ): arrayOfIBGRAVertex3D;
    1063 var
    1064   i: Integer;
    1065 begin
    1066   setlength(result, length(pts));
    1067   for i := 0 to high(pts) do
    1068     result[i] := TBGRAVertex3D.Create(FCoordPool,pts[i]);
    1069   Add(result);
    1070 end;
    1071 
    1072 procedure TBGRAPart3D.Add(const pts: array of IBGRAVertex3D);
    1073 var
    1074   i: Integer;
    1075 begin
    1076   if FVertexCount + length(pts) > length(FVertices) then
    1077     setlength(FVertices, (FVertexCount*2 + length(pts))+1);
    1078   for i := 0 to high(pts) do
    1079   begin
    1080     FVertices[FVertexCount] := pts[i];
    1081     inc(FVertexCount);
    1082   end;
    1083 end;
    1084 
    1085 function TBGRAPart3D.GetBoundingBox: TBox3D;
    1086 var i: integer;
    1087     pt: TPoint3D_128;
    1088 begin
    1089   if GetVertexCount > 0 then
    1090   begin
    1091     result.min := GetVertex(0).SceneCoord;
    1092     result.max := result.min;
    1093   end else
    1094   begin
    1095     result.min := Point3D(0,0,0);
    1096     result.max := Point3D(0,0,0);
    1097     exit;
    1098   end;
    1099   for i := 1 to GetVertexCount-1 do
    1100   begin
    1101     pt := GetVertex(i).SceneCoord_128;
    1102     if pt.x < result.min.x then result.min.x := pt.x else
    1103     if pt.x > result.max.x then result.max.x := pt.x;
    1104     if pt.y < result.min.y then result.min.y := pt.y else
    1105     if pt.y > result.max.y then result.max.y := pt.y;
    1106     if pt.z < result.min.z then result.min.z := pt.z else
    1107     if pt.z > result.max.z then result.max.z := pt.z;
    1108   end;
    1109 end;
    1110 
    1111 function TBGRAPart3D.GetMatrix: TMatrix3D;
    1112 begin
    1113   result := FMatrix;
    1114 end;
    1115 
    1116 function TBGRAPart3D.GetPart(AIndex: Integer): IBGRAPart3D;
    1117 begin
    1118   if (AIndex < 0) or (AIndex >= FPartCount) then
    1119     raise exception.Create('Index of out bounds');
    1120   result := FParts[AIndex];
    1121 end;
    1122 
    1123 function TBGRAPart3D.GetPartCount: integer;
    1124 begin
    1125   result := FPartCount;
    1126 end;
    1127 
    1128 function TBGRAPart3D.GetVertex(AIndex: Integer): IBGRAVertex3D;
    1129 begin
    1130   if (AIndex < 0) or (AIndex >= FVertexCount) then
    1131     raise exception.Create('Index of out bounds');
    1132   result := FVertices[AIndex];
    1133 end;
    1134 
    1135 function TBGRAPart3D.GetVertexCount: integer;
    1136 begin
    1137   result := FVertexCount;
    1138 end;
    1139 
    1140 function TBGRAPart3D.GetTotalVertexCount: integer;
    1141 var i: integer;
    1142 begin
    1143   result := GetVertexCount;
    1144   for i := 0 to GetPartCount-1 do
    1145     result += GetPart(i).GetTotalVertexCount;
    1146 end;
    1147 
    1148 procedure TBGRAPart3D.ResetTransform;
    1149 begin
    1150   FMatrix := MatrixIdentity3D;
    1151 end;
    1152 
    1153 procedure TBGRAPart3D.Scale(size: single; Before: boolean = true);
    1154 begin
    1155   Scale(size,size,size,Before);
    1156 end;
    1157 
    1158 procedure TBGRAPart3D.Scale(x, y, z: single; Before: boolean = true);
    1159 begin
    1160   Scale(Point3D(x,y,z),Before);
    1161 end;
    1162 
    1163 procedure TBGRAPart3D.Scale(size: TPoint3D; Before: boolean = true);
    1164 begin
    1165   if Before then
    1166     FMatrix *= MatrixScale3D(size)
    1167   else
    1168     FMatrix := MatrixScale3D(size)*FMatrix;
    1169 end;
    1170 
    1171 procedure TBGRAPart3D.RotateXDeg(angle: single; Before: boolean = true);
    1172 begin
    1173   RotateXRad(-angle*Pi/180, Before);
    1174 end;
    1175 
    1176 procedure TBGRAPart3D.RotateYDeg(angle: single; Before: boolean = true);
    1177 begin
    1178   RotateYRad(-angle*Pi/180, Before);
    1179 end;
    1180 
    1181 procedure TBGRAPart3D.RotateZDeg(angle: single; Before: boolean = true);
    1182 begin
    1183   RotateZRad(-angle*Pi/180, Before);
    1184 end;
    1185 
    1186 procedure TBGRAPart3D.RotateXRad(angle: single; Before: boolean = true);
    1187 begin
    1188   if Before then
    1189     FMatrix *= MatrixRotateX(angle)
    1190   else
    1191     FMatrix := MatrixRotateX(angle) * FMatrix;
    1192 end;
    1193 
    1194 procedure TBGRAPart3D.RotateYRad(angle: single; Before: boolean = true);
    1195 begin
    1196   if Before then
    1197     FMatrix *= MatrixRotateY(angle)
    1198   else
    1199     FMatrix := MatrixRotateY(angle) * FMatrix;
    1200 end;
    1201 
    1202 procedure TBGRAPart3D.RotateZRad(angle: single; Before: boolean = true);
    1203 begin
    1204   if Before then
    1205     FMatrix *= MatrixRotateZ(angle)
    1206   else
    1207     FMatrix := MatrixRotateZ(angle) * FMatrix;
    1208 end;
    1209 
    1210 procedure TBGRAPart3D.SetMatrix(const AValue: TMatrix3D);
    1211 begin
    1212   FMatrix := AValue;
    1213 end;
    1214 
    1215 procedure TBGRAPart3D.ComputeWithMatrix(const AMatrix: TMatrix3D; const AProjection: TProjection3D);
    1216 var
    1217   i: Integer;
    1218   Composed: TMatrix3D;
    1219   P: PBGRACoordData3D;
    1220 begin
    1221   Composed := AMatrix* self.FMatrix;
    1222   {$IFDEF CPUI386}
    1223   if UseSSE then
    1224   begin
    1225     Matrix3D_SSE_Load(Composed);
    1226     asm
    1227       mov eax,[AProjection]
    1228       movups xmm4,[eax]
    1229       xorps xmm1,xmm1
    1230     end;
    1231     P := FCoordPool.CoordData[0];
    1232     i := FCoordPool.UsedCapacity;
    1233     if UseSSE3 then
    1234     begin
    1235       while i > 0 do
    1236       with P^ do
    1237       begin
    1238         MatrixMultiplyVect3D_SSE3_Aligned(sceneCoord,viewCoord);
    1239         if viewCoord.z > 0 then
    1240         begin
    1241           asm
    1242             mov eax, P
    1243             movaps xmm3, [eax+16] //viewCoord
    1244             movaps xmm2,xmm3
    1245             shufps xmm2,xmm3,2+8+32+128
    1246             rcpps xmm2,xmm2  //xmm2 = InvZ
    1247             movss [eax+40],xmm2 //-> InvZ
    1248 
    1249             mulps xmm3,xmm4  //xmm3 *= Projection.Zoom
    1250             mulps xmm3,xmm2  //xmm3 *= InvZ
    1251 
    1252             movhlps xmm0,xmm4  //xmm2 = Projection.Center
    1253             addps xmm3,xmm0  //xmm3 += Projection.Center
    1254 
    1255             movlps [eax+32],xmm3 //->projectedCoord
    1256             movaps [eax+48],xmm1 //->normal
    1257           end;
    1258         end else
    1259         asm
    1260           mov eax, P
    1261           movlps [eax+32],xmm1  //0 ->projectedCoord
    1262           movaps [eax+48],xmm1 //->normal
    1263         end;
    1264         dec(i);
    1265         inc(p);
    1266       end;
    1267     end else
    1268     begin
    1269       while i > 0 do
    1270       with P^ do
    1271       begin
    1272         MatrixMultiplyVect3D_SSE_Aligned(sceneCoord,viewCoord);
    1273         if viewCoord.z > 0 then
    1274         begin
    1275           asm
    1276             mov eax, P
    1277             movaps xmm3, [eax+16] //viewCoord
    1278             movaps xmm2,xmm3
    1279             shufps xmm2,xmm3,2+8+32+128
    1280             rcpps xmm2,xmm2  //xmm2 = InvZ
    1281             movss [eax+40],xmm2 //-> InvZ
    1282 
    1283             mulps xmm3,xmm4  //xmm3 *= Projection.Zoom
    1284             mulps xmm3,xmm2  //xmm3 *= InvZ
    1285 
    1286             movhlps xmm0,xmm4  //xmm2 = Projection.Center
    1287             addps xmm3,xmm0  //xmm3 += Projection.Center
    1288 
    1289             movlps [eax+32],xmm3 //->projectedCoord
    1290             movaps [eax+48],xmm1 //->normal
    1291           end;
    1292         end else
    1293         asm
    1294           mov eax, P
    1295           movlps [eax+32],xmm1  //0 ->projectedCoord
    1296           movaps [eax+48],xmm1 //->normal
    1297         end;
    1298         dec(i);
    1299         inc(p);
    1300       end;
    1301     end;
    1302   end
    1303   else
    1304   {$ENDIF}
    1305   begin
    1306     P := FCoordPool.CoordData[0];
    1307     i := FCoordPool.UsedCapacity;
    1308     while i > 0 do
    1309     with P^ do
    1310     begin
    1311       viewCoord := Composed*sceneCoord;
    1312       ClearPoint3D_128(viewNormal);
    1313       if viewCoord.z > 0 then
    1314       begin
    1315         InvZ := 1/viewCoord.z;
    1316         projectedCoord := PointF(viewCoord.x*InvZ*AProjection.Zoom.x + AProjection.Center.x,
    1317                                  viewCoord.y*InvZ*AProjection.Zoom.Y + AProjection.Center.y);
    1318       end else
    1319         projectedCoord := PointF(0,0);
    1320       dec(i);
    1321       inc(p);
    1322     end;
    1323   end;
    1324   for i := 0 to FPartCount-1 do
    1325     FParts[i].ComputeWithMatrix(Composed,AProjection);
    1326 end;
    1327 
    1328 function TBGRAPart3D.ComputeCoordinate(var ASceneCoord: TPoint3D_128; const AProjection: TProjection3D): TPointF;
    1329 var part: IBGRAPart3D;
    1330   newViewCoord: TPoint3D_128;
    1331   InvZ: single;
    1332 begin
    1333   newViewCoord := FMatrix * ASceneCoord;
    1334   part := FContainer;
    1335   while part <> nil do
    1336   begin
    1337     newViewCoord := part.Matrix * newViewCoord;
    1338     part := part.Container;
    1339   end;
    1340   if NewViewCoord.z > 0 then
    1341   begin
    1342     InvZ := 1/NewViewCoord.z;
    1343     result := PointF(NewViewCoord.x*InvZ*AProjection.Zoom.x + AProjection.Center.x,
    1344                      NewViewCoord.y*InvZ*AProjection.Zoom.Y + AProjection.Center.y);
    1345   end else
    1346     result := PointF(0,0);
    1347 end;
    1348 
    1349 procedure TBGRAPart3D.NormalizeViewNormal;
    1350 var
    1351   i: Integer;
    1352 begin
    1353   for i := 0 to FVertexCount-1 do
    1354     FVertices[i].NormalizeViewNormal;
    1355   for i := 0 to FPartCount-1 do
    1356     FParts[i].NormalizeViewNormal;
    1357 end;
    1358 
    1359 procedure TBGRAPart3D.Translate(x, y, z: single; Before: boolean = true);
    1360 begin
    1361   Translate(Point3D(x,y,z),Before);
    1362 end;
    1363 
    1364 procedure TBGRAPart3D.Translate(ofs: TPoint3D; Before: boolean = true);
    1365 begin
    1366   if Before then
    1367     FMatrix *= MatrixTranslation3D(ofs)
    1368   else
    1369     FMatrix := MatrixTranslation3D(ofs)*FMatrix;
    1370 end;
    1371 
    1372 function TBGRAPart3D.CreatePart: IBGRAPart3D;
    1373 begin
    1374   if FPartCount = length(FParts) then
    1375     setlength(FParts, FPartCount*2+1);
    1376   result := TBGRAPart3D.Create(self);
    1377   FParts[FPartCount] := result;
    1378   inc(FPartCount);
    1379 end;
    1380 
    1381 function TBGRAPart3D.GetContainer: IBGRAPart3D;
    1382 begin
    1383   result := FContainer;
    1384 end;
    1385 
    1386 procedure TBGRAPart3D.SetVertex(AIndex: Integer; const AValue: IBGRAVertex3D);
    1387 begin
    1388   if (AIndex < 0) or (AIndex >= FVertexCount) then
    1389     raise exception.Create('Index of out bounds');
    1390   FVertices[AIndex] := AValue;
    1391 end;
    1392 
    1393 { TBGRAObject3D }
    1394 
    1395 procedure TBGRAObject3D.AddFace(AFace: IBGRAFace3D);
    1396 begin
    1397   if FFaceCount = length(FFaces) then
    1398      setlength(FFaces,FFaceCount*2+3);
    1399   FFaces[FFaceCount] := AFace;
    1400   inc(FFaceCount);
    1401 end;
    1402 
    1403 constructor TBGRAObject3D.Create(AScene: TBGRAScene3D);
    1404 begin
    1405   FColor := BGRAWhite;
    1406   FLight := 1;
    1407   FTexture := nil;
    1408   FMainPart := TBGRAPart3D.Create(nil);
    1409   FLightingNormal:= AScene.DefaultLightingNormal;
    1410   FParentLighting:= True;
    1411   FScene := AScene;
    1412 end;
    1413 
    1414 destructor TBGRAObject3D.Destroy;
    1415 begin
    1416   fillchar(FTexture,sizeof(FTexture),0);
    1417   inherited Destroy;
    1418 end;
    1419 
    1420 procedure TBGRAObject3D.Clear;
    1421 begin
    1422   FFaces := nil;
    1423   FFaceCount := 0;
    1424   FMainPart.Clear(True);
    1425 end;
    1426 
    1427 function TBGRAObject3D.GetColor: TBGRAPixel;
    1428 begin
    1429   result := FColor;
    1430 end;
    1431 
    1432 function TBGRAObject3D.GetLight: Single;
    1433 begin
    1434   result := FLight;
    1435 end;
    1436 
    1437 function TBGRAObject3D.GetTexture: IBGRAScanner;
    1438 begin
    1439   result := FTexture;
    1440 end;
    1441 
    1442 function TBGRAObject3D.GetMainPart: IBGRAPart3D;
    1443 begin
    1444   result := FMainPart;
    1445 end;
    1446 
    1447 procedure TBGRAObject3D.SetColor(const AValue: TBGRAPixel);
    1448 begin
    1449   FColor := AValue;
    1450   FTexture := nil;
    1451 end;
    1452 
    1453 procedure TBGRAObject3D.SetLight(const AValue: Single);
    1454 begin
    1455   FLight := AValue;
    1456 end;
    1457 
    1458 procedure TBGRAObject3D.SetTexture(const AValue: IBGRAScanner);
    1459 begin
    1460   FTexture := AValue;
    1461 end;
    1462 
    1463 procedure TBGRAObject3D.SetMaterial(const AValue: IBGRAMaterial3D);
    1464 begin
    1465   FMaterial := AValue;
    1466 end;
    1467 
    1468 procedure TBGRAObject3D.RemoveUnusedVertices;
    1469 begin
    1470   GetMainPart.RemoveUnusedVertices;
    1471 end;
    1472 
    1473 procedure TBGRAObject3D.SeparatePart(APart: IBGRAPart3D);
    1474 var
    1475   vertexInfo: array of record
    1476        orig,dup: IBGRAVertex3D;
    1477      end;
    1478 
    1479   i,j: integer;
    1480   inPart,outPart: boolean;
    1481   idxV: integer;
    1482 begin
    1483   setlength(vertexInfo, APart.VertexCount);
    1484   for i := 0 to high(vertexInfo) do
    1485     with vertexInfo[i] do
    1486     begin
    1487       orig := APart.Vertex[i];
    1488       dup := APart.Add(orig.SceneCoord_128);
    1489     end;
    1490 
    1491   for i := 0 to GetFaceCount-1 do
    1492     with GetFace(i) do
    1493     begin
    1494       inPart := false;
    1495       outPart := false;
    1496       for j := 0 to VertexCount-1 do
    1497         if (APart.IndexOf(Vertex[j]) <> -1) then
    1498           inPart := true
    1499         else
    1500           outPart := true;
    1501 
    1502       if inPart and not outPart then
    1503       begin
    1504         for j := 0 to VertexCount-1 do
    1505         begin
    1506           idxV := APart.IndexOf(Vertex[j]);
    1507           if idxV <> -1 then
    1508             Vertex[j] := vertexInfo[idxV].dup;
    1509         end;
    1510       end;
    1511     end;
    1512 
    1513   for i := APart.VertexCount-1 downto 0 do
    1514     APart.RemoveVertex(i);
    1515 end;
    1516 
    1517 function TBGRAObject3D.GetScene: TBGRAScene3D;
    1518 begin
    1519   result := FScene;
    1520 end;
    1521 
    1522 function TBGRAObject3D.GetRefCount: integer;
    1523 begin
    1524   result := RefCount;
    1525 end;
    1526 
    1527 procedure TBGRAObject3D.SetBiface(AValue: boolean);
    1528 var i: integer;
    1529 begin
    1530   for i := 0 to GetFaceCount-1 do
    1531     GetFace(i).Biface := AValue;
    1532 end;
    1533 
    1534 function TBGRAObject3D.GetLightingNormal: TLightingNormal3D;
    1535 begin
    1536   result := FLightingNormal;
    1537 end;
    1538 
    1539 function TBGRAObject3D.GetParentLighting: boolean;
    1540 begin
    1541   result := FParentLighting;
    1542 end;
    1543 
    1544 procedure TBGRAObject3D.SetLightingNormal(const AValue: TLightingNormal3D);
    1545 begin
    1546   FLightingNormal := AValue;
    1547   FParentLighting:= False;
    1548 end;
    1549 
    1550 procedure TBGRAObject3D.SetParentLighting(const AValue: boolean);
    1551 begin
    1552   FParentLighting:= AValue;
    1553 end;
    1554 
    1555 procedure TBGRAObject3D.ComputeWithMatrix(const AMatrix: TMatrix3D; const AProjection: TProjection3D);
    1556 var
    1557   i: Integer;
    1558 begin
    1559   FMainPart.ComputeWithMatrix(AMatrix,AProjection);
    1560   for i := 0 to FFaceCount-1 do
    1561     FFaces[i].ComputeViewNormalAndCenter;
    1562   FMainPart.NormalizeViewNormal;
    1563 end;
    1564 
    1565 function TBGRAObject3D.AddFaceReversed(const AVertices: array of IBGRAVertex3D
    1566   ): IBGRAFace3D;
    1567 var
    1568   tempVertices: array of IBGRAVertex3D;
    1569   i: Integer;
    1570 begin
    1571   setlength(tempVertices,length(AVertices));
    1572   for i := 0 to high(tempVertices) do
    1573     tempVertices[i] := AVertices[high(AVertices)-i];
    1574   result := AddFace(tempVertices);
    1575 end;
    1576 
    1577 function TBGRAObject3D.AddFace(const AVertices: array of IBGRAVertex3D): IBGRAFace3D;
    1578 begin
    1579   result := TBGRAFace3D.Create(self,AVertices);
    1580   AddFace(result);
    1581 end;
    1582 
    1583 function TBGRAObject3D.AddFace(const AVertices: array of IBGRAVertex3D;
    1584   ABiface: boolean): IBGRAFace3D;
    1585 begin
    1586   result := TBGRAFace3D.Create(self,AVertices);
    1587   result.Biface := ABiface;
    1588   AddFace(result);
    1589 end;
    1590 
    1591 function TBGRAObject3D.AddFace(const AVertices: array of IBGRAVertex3D; ATexture: IBGRAScanner): IBGRAFace3D;
    1592 var Face: IBGRAFace3D;
    1593 begin
    1594   Face := TBGRAFace3D.Create(self,AVertices);
    1595   Face.Texture := ATexture;
    1596   AddFace(Face);
    1597   result := face;
    1598 end;
    1599 
    1600 function TBGRAObject3D.AddFace(const AVertices: array of IBGRAVertex3D;
    1601   AColor: TBGRAPixel): IBGRAFace3D;
    1602 var Face: IBGRAFace3D;
    1603 begin
    1604   Face := TBGRAFace3D.Create(self,AVertices);
    1605   Face.SetColor(AColor);
    1606   Face.Texture := nil;
    1607   AddFace(Face);
    1608   result := face;
    1609 end;
    1610 
    1611 function TBGRAObject3D.AddFace(const AVertices: array of IBGRAVertex3D;
    1612   AColors: array of TBGRAPixel): IBGRAFace3D;
    1613 var
    1614   i: Integer;
    1615 begin
    1616   if length(AColors) <> length(AVertices) then
    1617     raise Exception.Create('Dimension mismatch');
    1618   result := TBGRAFace3D.Create(self,AVertices);
    1619   for i := 0 to high(AColors) do
    1620     result.VertexColor[i] := AColors[i];
    1621   AddFace(result);
    1622 end;
    1623 
    1624 function TBGRAObject3D.GetFace(AIndex: integer): IBGRAFace3D;
    1625 begin
    1626   if (AIndex < 0) or (AIndex >= FFaceCount) then
    1627     raise Exception.Create('Index out of bounds');
    1628   result := FFaces[AIndex];
    1629 end;
    1630 
    1631 function TBGRAObject3D.GetFaceCount: integer;
    1632 begin
    1633   result := FFaceCount;
    1634 end;
    1635 
    1636 function TBGRAObject3D.GetTotalVertexCount: integer;
    1637 begin
    1638   result := GetMainPart.TotalVertexCount;
    1639 end;
    1640 
    1641 function TBGRAObject3D.GetMaterial: IBGRAMaterial3D;
    1642 begin
    1643   result := FMaterial;
    1644 end;
     236    procedure ForEachVertex(ACallback: TVertex3DCallback);
     237    procedure ForEachFace(ACallback: TFace3DCallback);
     238  end;
     239
     240{$i part3d.inc}
     241{$i object3d.inc}
     242{$i shapes3d.inc}
    1645243
    1646244{ TBGRAScene3D }
     
    1746344end;
    1747345
     346function TBGRAScene3D.GetNormalCount: integer;
     347var i: integer;
     348begin
     349  result := 0;
     350  for i := 0 to Object3DCount-1 do
     351    result += Object3D[i].TotalNormalCount;
     352end;
     353
    1748354function TBGRAScene3D.GetAmbiantLightness: single;
    1749355begin
     
    1822428procedure TBGRAScene3D.Init;
    1823429begin
     430  UnknownColor := BGRA(0,128,255);
    1824431  FAutoZoom := True;
    1825432  FAutoViewCenter := True;
     
    1866473var i: integer;
    1867474begin
     475  for i := 0 to FLights.Count-1 do
     476    TBGRALight3D(FLights[i])._Release;
     477  FLights.Clear;
     478
    1868479  for i := 0 to FObjectCount-1 do
    1869480    FObjects[i].Clear;
    1870481  FObjects := nil;
    1871482  FObjectCount := 0;
    1872   for i := 0 to FLights.Count-1 do
    1873     IBGRALight3D(TBGRALight3D(FLights[i]))._Release;
    1874   FLights.Clear;
    1875 end;
    1876 
    1877 {$hints off}
     483
     484  FMaterials := nil;
     485  FMaterialCount := 0;
     486  DefaultMaterial := CreateMaterial;
     487end;
     488
    1878489procedure TBGRAScene3D.UseMaterial(AMaterialName: string; AFace: IBGRAFace3D);
    1879 var color: TBGRAPixel;
    1880 begin
    1881   color := BGRA(0,128,255);
    1882   AFace.SetColor(color);
    1883 end;
    1884 {$hints on}
     490
     491  function ParseColor(text: string): TBGRAPixel;
     492  var
     493    color,tempColor: TBGRAPixel;
     494  begin
     495    color := UnknownColor;
     496
     497    if copy(text,1,2) = 'dk' then
     498    begin
     499      tempcolor := ParseColor(copy(text,3,length(text)-2));
     500      tempcolor := MergeBGRA(tempcolor,3,BGRABlack,1);
     501      color := StrToBGRA('dark'+copy(text,3,length(text)-2),tempcolor);
     502    end;
     503    if copy(text,1,2) = 'lt' then
     504    begin
     505      tempcolor := ParseColor(copy(text,3,length(text)-2));
     506      tempcolor := MergeBGRA(tempcolor,3,BGRAWhite,1);
     507      color := StrToBGRA('light'+copy(text,3,length(text)-2),tempcolor);
     508    end;
     509    Color := StrToBGRA(StringReplace(text,'deep','dark',[]),Color);
     510    Color := StrToBGRA(StringReplace(text,'dark','deep',[]),Color);
     511    Color := StrToBGRA(text,Color);
     512    result := color;
     513  end;
     514
     515var
     516  mat: IBGRAMaterial3D;
     517  c: TBGRAPixel;
     518begin
     519  mat := GetMaterialByName(AMaterialName);
     520  if mat = nil then
     521  begin
     522    mat := CreateMaterial;
     523    mat.Name := AMaterialName;
     524    c := ParseColor(AMaterialName);
     525    mat.AmbiantColor := c;
     526    mat.DiffuseColor := c;
     527  end;
     528  AFace.Material := mat;
     529end;
     530
     531function TBGRAScene3D.FetchTexture(AName: string; out texSize: TPointF): IBGRAScanner;
     532begin
     533  result := nil;
     534  texSize := PointF(1,1);
     535end;
    1885536
    1886537function TBGRAScene3D.LoadObjectFromFile(AFilename: string; SwapFacesOrientation: boolean): IBGRAObject3D;
    1887 var t: textfile;
    1888     s: string;
     538var source: TFileStream;
     539begin
     540  source := TFileStream.Create(AFilename,fmOpenRead,fmShareDenyWrite);
     541  try
     542    result := LoadObjectFromStream(source,SwapFacesOrientation);
     543  finally
     544    source.free;
     545  end;
     546end;
     547
     548function TBGRAScene3D.LoadObjectFromFileUTF8(AFilename: string;
     549  SwapFacesOrientation: boolean): IBGRAObject3D;
     550var source: TFileStreamUTF8;
     551begin
     552  source := TFileStreamUTF8.Create(AFilename,fmOpenRead,fmShareDenyWrite);
     553  try
     554    result := LoadObjectFromStream(source,SwapFacesOrientation);
     555  finally
     556    source.free;
     557  end;
     558end;
     559
     560function TBGRAScene3D.LoadObjectFromStream(AStream: TStream;
     561  SwapFacesOrientation: boolean): IBGRAObject3D;
     562var s: string;
     563  secondValue,thirdValue: string;
    1889564
    1890565  function GetNextToken: string;
    1891   var idxStart,idxEnd: integer;
     566  var idxStart,idxEnd,idxSlash: integer;
    1892567  begin
    1893568    idxStart := 1;
    1894     while (idxStart <= length(s)) and (s[idxStart]=' ') do inc(idxStart);
     569    while (idxStart <= length(s)) and (s[idxStart]in[' ',#9]) do inc(idxStart);
    1895570    if idxStart > length(s) then
    1896571    begin
     
    1899574    end;
    1900575    idxEnd := idxStart;
    1901     while (idxEnd < length(s)) and (s[idxEnd+1]<> ' ') do inc(idxEnd);
     576    while (idxEnd < length(s)) and not (s[idxEnd+1]in[' ',#9]) do inc(idxEnd);
    1902577    result := copy(s,idxStart, idxEnd-idxStart+1);
    1903578    delete(s,1,idxEnd);
    1904     if pos('/',result) <> 0 then result := copy(result,1,pos('/',result)-1);
     579    idxSlash := pos('/',result);
     580    if idxSlash <> 0 then
     581    begin
     582      secondValue:= copy(result,idxSlash+1,length(result)-idxSlash);
     583      result := copy(result,1,idxSlash-1);
     584      idxSlash:= pos('/',secondValue);
     585      if idxSlash <> 0 then
     586      begin
     587        thirdValue:= copy(secondValue,idxSlash+1,length(secondValue)-idxSlash);
     588        secondValue:= copy(secondValue,1,idxSlash-1);
     589      end else
     590        thirdValue:= '';
     591    end else
     592    begin
     593      secondValue:= '';
     594      thirdValue:= '';
     595    end;
     596  end;
     597
     598type
     599  TFaceVertexExtra = record
     600    normal: IBGRANormal3D;
     601    texCoord: TPointF;
    1905602  end;
    1906603
     
    1908605    x,y,z : single;
    1909606    code : integer;
    1910     vertices: array of IBGRAVertex3D;
    1911     NbVertices,v,i: integer;
     607    faceVertices: array of IBGRAVertex3D;
     608    faceExtra: array of TFaceVertexExtra;
     609    NbFaceVertices,v,v2,v3,i: integer;
    1912610    tempV: IBGRAVertex3D;
     611    tempN: TFaceVertexExtra;
    1913612    materialname: string;
    1914613    face: IBGRAFace3D;
    1915 
    1916 begin
     614    lines: TStringList;
     615    lineIndex: integer;
     616    texCoords: array of TPointF;
     617    nbTexCoords: integer;
     618
     619begin
     620  lines := TStringList.Create;
     621  lines.LoadFromStream(AStream);
    1917622  result := CreateObject;
    1918   assignfile(t,AFilename);
    1919   reset(t);
    1920   vertices := nil;
    1921   NbVertices:= 0;
     623  faceVertices := nil;
     624  faceExtra := nil;
     625  NbFaceVertices:= 0;
    1922626  materialname := 'default';
    1923   while not eof(t) do
    1924   begin
    1925     readln(t,s);
     627  lineIndex := 0;
     628  texCoords := nil;
     629  nbTexCoords:= 0;
     630  while lineIndex < lines.Count do
     631  begin
     632    s := lines[lineIndex];
     633    if pos('#',s) <> 0 then
     634      s := copy(s,1,pos('#',s)-1);
     635    inc(lineIndex);
    1926636    lineType := GetNextToken;
    1927637    if lineType = 'v' then
     
    1932642      result.MainPart.Add(x,y,z);
    1933643    end else
     644    if lineType = 'vt' then
     645    begin
     646      val(GetNextToken,x,code);
     647      val(GetNextToken,y,code);
     648      if nbTexCoords >= length(texCoords) then
     649        setlength(texCoords, length(texCoords)*2+1);
     650      texCoords[nbTexCoords] := PointF(x,y);
     651      inc(nbTexCoords);
     652    end else
     653    if lineType = 'vn' then
     654    begin
     655      val(GetNextToken,x,code);
     656      val(GetNextToken,y,code);
     657      val(GetNextToken,z,code);
     658      result.MainPart.AddNormal(x,y,z);
     659      result.LightingNormal := lnVertex;
     660    end else
    1934661    if lineType = 'usemtl' then
    1935662      materialname := trim(s)
     
    1937664    if lineType = 'f' then
    1938665    begin
    1939       NbVertices:= 0;
     666      NbFaceVertices:= 0;
    1940667      repeat
    1941668        val(GetNextToken,v,code);
     669        if (code = 0) and (v < 0) then v := result.MainPart.VertexCount+1+v;
    1942670        if (code = 0) and (v >= 1) and (v <= result.MainPart.VertexCount) then
    1943671        begin
    1944           if length(vertices) = nbvertices then
    1945             setlength(vertices, length(vertices)*2+1);
    1946           vertices[NbVertices] := result.MainPart.Vertex[v-1];
    1947           inc(NbVertices);
     672          if length(faceVertices) = NbFaceVertices then
     673          begin
     674            setlength(faceVertices, length(faceVertices)*2+1);
     675            setlength(faceExtra, length(faceExtra)*2+1);
     676          end;
     677          faceVertices[NbFaceVertices] := result.MainPart.Vertex[v-1];
     678          val(secondValue,v2,code);
     679          if (code = 0) and (v2 < 0) then v2 := nbTexCoords+1+v2;
     680          if (code = 0) and (v2 >= 1) and (v2-1 < nbTexCoords) then
     681            faceExtra[NbFaceVertices].texCoord := texCoords[v2-1]
     682          else if nbTexCoords > v-1 then
     683            faceExtra[NbFaceVertices].texCoord := texCoords[v-1]
     684          else
     685            faceExtra[NbFaceVertices].texCoord := PointF(0,0);
     686          val(thirdValue,v3,code);
     687          if (code = 0) and (v3 < 0) then v3 := result.MainPart.NormalCount+1+v3;
     688          if code = 0 then
     689            faceExtra[NbFaceVertices].normal := result.MainPart.Normal[v3-1]
     690          else if result.MainPart.NormalCount > v-1 then
     691            faceExtra[NbFaceVertices].normal := result.MainPart.Normal[v-1]
     692          else
     693            faceExtra[NbFaceVertices].normal := nil;
     694          inc(NbFaceVertices);
    1948695        end else break;
    1949696      until false;
    1950       if NbVertices > 2 then
     697      if NbFaceVertices > 2 then
    1951698      begin
    1952699        if SwapFacesOrientation then
    1953           for i := 0 to NbVertices div 2-1 do
     700          for i := 0 to NbFaceVertices div 2-1 do
    1954701          begin
    1955             tempV := vertices[i];
    1956             vertices[i] := vertices[NbVertices-1-i];
    1957             vertices[NbVertices-1-i] := tempV;
     702            tempV := faceVertices[i];
     703            faceVertices[i] := faceVertices[NbFaceVertices-1-i];
     704            faceVertices[NbFaceVertices-1-i] := tempV;
     705            tempN := faceExtra[i];
     706            faceExtra[i] := faceExtra[NbFaceVertices-1-i];
     707            faceExtra[NbFaceVertices-1-i] := tempN;
    1958708          end;
    1959         face := result.AddFace(slice(vertices,NbVertices));
     709        face := result.AddFace(slice(faceVertices,NbFaceVertices));
     710        for i := 0 to NbFaceVertices-1 do
     711        begin
     712          face.SetNormal(i, faceExtra[i].normal);
     713          face.SetTexCoord(i, faceExtra[i].texCoord);
     714        end;
    1960715        face.MaterialName := materialname;
    1961716      end;
    1962717    end;
    1963718  end;
    1964   closefile(t);
     719  lines.Free;
     720end;
     721
     722procedure TBGRAScene3D.LoadMaterialsFromFile(AFilename: string);
     723var source: TFileStream;
     724begin
     725  source := TFileStream.Create(AFilename,fmOpenRead,fmShareDenyWrite);
     726  try
     727    LoadMaterialsFromStream(source);
     728  finally
     729    source.free;
     730  end;
     731end;
     732
     733procedure TBGRAScene3D.LoadMaterialsFromFileUTF8(AFilename: string);
     734var source: TFileStreamUTF8;
     735begin
     736  source := TFileStreamUTF8.Create(AFilename,fmOpenRead,fmShareDenyWrite);
     737  try
     738    LoadMaterialsFromStream(source);
     739  finally
     740    source.free;
     741  end;
     742end;
     743
     744procedure TBGRAScene3D.LoadMaterialsFromStream(AStream: TStream);
     745var
     746  s: String;
     747
     748  function GetNextToken: string;
     749  var idxStart,idxEnd: integer;
     750  begin
     751    idxStart := 1;
     752    while (idxStart <= length(s)) and (s[idxStart]in[#9,' ']) do inc(idxStart);
     753    if idxStart > length(s) then
     754    begin
     755      result := '';
     756      exit;
     757    end;
     758    idxEnd := idxStart;
     759    while (idxEnd < length(s)) and not (s[idxEnd+1]in[#9,' ']) do inc(idxEnd);
     760    result := copy(s,idxStart, idxEnd-idxStart+1);
     761    delete(s,1,idxEnd);
     762  end;
     763
     764  function GetSingle: single;
     765  var code: integer;
     766  begin
     767    val(GetNextToken,result,code);
     768  end;
     769
     770  function GetColorF: TColorF;
     771  var r,g,b: single;
     772    code: integer;
     773  begin
     774    val(GetNextToken,r,code);
     775    val(GetNextToken,g,code);
     776    val(GetNextToken,b,code);
     777    result := ColorF(r,g,b,1);
     778  end;
     779
     780var
     781  lines: TStringList;
     782  lineIndex: integer;
     783  lineType: String;
     784  currentMaterial: IBGRAMaterial3D;
     785  materialName: string;
     786  texZoom: TPointF;
     787  v: single;
     788
     789begin
     790  lines := TStringList.Create;
     791  lines.LoadFromStream(AStream);
     792  lineIndex := 0;
     793  while lineIndex < lines.Count do
     794  begin
     795    s := lines[lineIndex];
     796    if pos('#',s) <> 0 then
     797      s := copy(s,1,pos('#',s)-1);
     798    inc(lineIndex);
     799    lineType := GetNextToken;
     800    if lineType = 'newmtl' then
     801    begin
     802      materialName := trim(s);
     803      currentMaterial := GetMaterialByName(materialName);
     804      if currentMaterial = nil then
     805      begin
     806        currentMaterial := CreateMaterial;
     807        currentMaterial.Name := materialName;
     808      end;
     809    end else
     810    if currentMaterial <> nil then
     811    begin
     812      if lineType = 'Ka' then currentMaterial.AmbiantColorF := GetColorF else
     813      if lineType = 'Kd' then currentMaterial.DiffuseColorF := GetColorF else
     814      if lineType = 'Ks' then currentMaterial.SpecularColorF := GetColorF else
     815      if (lineType = 'map_Ka') or (lineType = 'map_Kd') then
     816      begin
     817        currentMaterial.Texture := FetchTexture(trim(s),texZoom);
     818        texZoom.y := -texZoom.y;
     819        currentMaterial.TextureZoom := texZoom;
     820      end else
     821      if lineType = 'Ns' then currentMaterial.SpecularIndex := round(GetSingle) else
     822      if lineType = 'd' then
     823      begin
     824        v := GetSingle;
     825        if v > 1 then
     826          currentMaterial.SimpleAlpha := 255
     827        else if v < 0 then
     828          currentMaterial.SimpleAlpha := 0
     829        else
     830          currentMaterial.SimpleAlpha := round(v*255);
     831      end;
     832    end;
     833  end;
     834  lines.Free;
    1965835end;
    1966836
     
    23581228            LColors[idxL] := BGRA(128,128,128)
    23591229          else
     1230          begin
    23601231            if ColorOverride then
    23611232              LColors[idxL] := Color
     
    23671238                LColors[idxL] := tempV.Color;
    23681239            end;
     1240          end;
    23691241
    23701242          if TexCoordOverride then
     
    23721244          else
    23731245            LTexCoord[idxL] := tempV.TexCoord;
     1246          with LMaterial.GetTextureZoom do
     1247          begin
     1248            LTexCoord[idxL].x *= x;
     1249            LTexCoord[idxL].y *= y;
     1250          end;
    23741251
    23751252          with tempV.CoordData^ do
     
    23801257            LZ[idxL] := viewCoord.Z;
    23811258          end;
     1259          if Normal <> nil then
     1260            LNormal3D[idxL] := Normal.ViewNormal_128;
    23821261        end;
    23831262      end;
     
    23891268       VCount := VertexCount;
    23901269       if VCount < 3 then exit;
    2391 
    2392        if ParentTexture then
    2393          LTexture := Object3D.Texture
    2394        else
    2395          LTexture := Texture;
    23961270
    23971271       if Material <> nil then
     
    24031277       else
    24041278         exit;
     1279
     1280       if ParentTexture then
     1281       begin
     1282         if LMaterial.GetTexture <> nil then
     1283           LTexture := LMaterial.GetTexture
     1284         else
     1285           LTexture := Object3D.Texture
     1286       end
     1287       else
     1288         LTexture := Texture;
    24051289
    24061290       LLightNormal := Object3D.LightingNormal;
     
    26351519  procedure DrawWithResample;
    26361520  var
    2637     tempSurface,resampledTempSurface: TBGRACustomBitmap;
     1521    tempSurface: TBGRACustomBitmap;
    26381522  begin
    26391523    tempSurface := ASurface.NewBitmap(ASurface.Width*RenderingOptions.AntialiasingResampleLevel,ASurface.Height*RenderingOptions.AntialiasingResampleLevel);
    26401524    InternalRender(tempSurface, am3dNone, RenderingOptions.AntialiasingResampleLevel);
    2641     resampledTempSurface := tempSurface.Resample(ASurface.Width,ASurface.Height,rmSimpleStretch);
     1525    BGRAResample.DownSamplePutImage(tempSurface,RenderingOptions.AntialiasingResampleLevel,RenderingOptions.AntialiasingResampleLevel,
     1526                 ASurface, 0,0, dmDrawWithTransparency);
    26421527    tempSurface.Free;
    2643     ASurface.PutImage(0,0,resampledTempSurface,dmDrawWithTransparency);
    2644     resampledTempSurface.Free;
    26451528  end;
    26461529
     
    27391622  Color: TBGRAPixel): TBGRAPixel;
    27401623var i: Integer;
    2741 begin
     1624  m: TBGRAMaterial3D;
     1625begin
     1626  m := TBGRAMaterial3D(Context^.material);
     1627  if not m.GetAutoSimpleColor then Color := ColorIntToBGRA(BGRAToColorIntMultiply(Color, m.GetSimpleColorInt));
     1628
    27421629  Context^.lightness := FAmbiantLightness;
    27431630
     
    27671654  Color: TBGRAPixel): TBGRAPixel;
    27681655var i: Integer;
    2769 begin
    2770   Context^.diffuseColor := FAmbiantLightColor;
     1656  m: TBGRAMaterial3D;
     1657begin
     1658  m := TBGRAMaterial3D(Context^.material);
     1659
     1660  if m.GetAutoAmbiantColor then
     1661    Context^.diffuseColor := FAmbiantLightColor
     1662  else
     1663    Context^.diffuseColor := FAmbiantLightColor*m.GetAmbiantColorInt;
    27711664
    27721665  i := FLights.Count-1;
     
    27841677  Color: TBGRAPixel): TBGRAPixel;
    27851678var i: Integer;
    2786 begin
    2787   Context^.diffuseColor := FAmbiantLightColor;
     1679  m: TBGRAMaterial3D;
     1680begin
     1681  m := TBGRAMaterial3D(Context^.material);
     1682
     1683  if m.GetAutoAmbiantColor then
     1684    Context^.diffuseColor := FAmbiantLightColor
     1685  else
     1686    Context^.diffuseColor := FAmbiantLightColor*m.GetAmbiantColorInt;
    27881687  Context^.specularColor := ColorInt65536(0,0,0,0);
    27891688
     
    28021701end;
    28031702
    2804 {$hints off}
    28051703function TBGRAScene3D.ApplyNoLighting(Context: PSceneLightingContext;
    28061704  Color: TBGRAPixel): TBGRAPixel;
    2807 begin
    2808   result := Color;
     1705var
     1706  m: TBGRAMaterial3D;
     1707begin
     1708  m := TBGRAMaterial3D(Context^.material);
     1709
     1710  if not m.GetAutoAmbiantColor then
     1711    result := ColorIntToBGRA(BGRAToColorIntMultiply(Color, m.GetAmbiantColorInt))
     1712  else
     1713    result := Color;
    28091714end;
    28101715
    28111716function TBGRAScene3D.ApplyLightingWithAmbiantLightnessOnly(
    28121717  Context: PSceneLightingContext; Color: TBGRAPixel): TBGRAPixel;
    2813 begin
     1718var
     1719  m: TBGRAMaterial3D;
     1720begin
     1721  m := TBGRAMaterial3D(Context^.material);
     1722
     1723  if not m.GetAutoAmbiantColor then
     1724    Color := ColorIntToBGRA(BGRAToColorIntMultiply(Color, m.GetAmbiantColorInt));
     1725
    28141726  if FAmbiantLightness <= 0 then
    28151727    result := BGRA(0,0,0,color.alpha)
     
    28171729    result := ApplyIntensityFast(Color, FAmbiantLightness);
    28181730end;
    2819 
    2820 {$hints on}
    28211731
    28221732function TBGRAScene3D.CreateObject: IBGRAObject3D;
     
    29631873end;
    29641874
     1875function TBGRAScene3D.GetMaterialByName(AName: string): IBGRAMaterial3D;
     1876var i: integer;
     1877begin
     1878  for i := 0 to MaterialCount-1 do
     1879    if AName = Material[i].Name then
     1880    begin
     1881      result := Material[i];
     1882      exit;
     1883    end;
     1884  result := nil;
     1885end;
     1886
    29651887procedure TBGRAScene3D.UpdateMaterials;
    29661888var i,j: integer;
     
    29971919end;
    29981920
     1921procedure TBGRAScene3D.ForEachVertex(ACallback: TVertex3DCallback);
     1922var i: integer;
     1923begin
     1924  for i := 0 to Object3DCount-1 do
     1925    Object3D[i].ForEachVertex(ACallback);
     1926end;
     1927
     1928procedure TBGRAScene3D.ForEachFace(ACallback: TFace3DCallback);
     1929var i: integer;
     1930begin
     1931  for i := 0 to Object3DCount-1 do
     1932    Object3D[i].ForEachFace(ACallback);
     1933end;
     1934
     1935initialization
     1936
     1937  Randomize;
     1938
    29991939end.
    30001940
  • GraphicTest/Packages/bgrabitmap/bgrascene3dinterface.inc

    r452 r472  
    44  { IBGRALight3D }
    55
    6   IBGRALight3D = interface
     6  IBGRALight3D = interface ['{85C683B6-07AC-4B8D-9324-06BC22882433}']
    77    procedure ComputeDiffuseLightness(Context: PSceneLightingContext);
    88    procedure ComputeDiffuseColor(Context: PSceneLightingContext);
     
    3030  end;
    3131
    32   IBGRAPointLight3D = interface(IBGRALight3D)
     32  IBGRAPointLight3D = interface(IBGRALight3D) ['{C939900D-DDD6-49F0-B1E9-E29F94FDB4C8}']
    3333    function GetVertex: IBGRAVertex3D;
    3434    procedure SetVertex(const AValue: IBGRAVertex3D);
     
    3636  end;
    3737
    38   IBGRADirectionalLight3D = interface(IBGRALight3D)
     38  IBGRADirectionalLight3D = interface(IBGRALight3D) ['{8D575CEE-8DD2-46FB-9BCC-17DE3DAAF53D}']
    3939    function GetDirection: TPoint3D;
    4040    procedure SetDirection(const AValue: TPoint3D);
     
    4545
    4646  IBGRAMaterial3D = interface
     47    function GetAmbiantAlpha: byte;
     48    function GetAutoAmbiantColor: boolean;
    4749    function GetAutoDiffuseColor: boolean;
     50    function GetAutoSimpleColor: boolean;
    4851    function GetAutoSpecularColor: boolean;
     52    function GetAmbiantColor: TBGRAPixel;
     53    function GetAmbiantColorF: TColorF;
     54    function GetAmbiantColorInt: TColorInt65536;
     55    function GetDiffuseAlpha: byte;
    4956    function GetDiffuseColor: TBGRAPixel;
    5057    function GetDiffuseColorF: TColorF;
    5158    function GetDiffuseColorInt: TColorInt65536;
    5259    function GetLightThroughFactor: single;
     60    function GetName: string;
    5361    function GetSaturationHigh: single;
    5462    function GetSaturationLow: single;
     63    function GetSimpleAlpha: byte;
     64    function GetSimpleColor: TBGRAPixel;
     65    function GetSimpleColorF: TColorF;
     66    function GetSimpleColorInt: TColorInt65536;
    5567    function GetSpecularColor: TBGRAPixel;
    5668    function GetSpecularColorF: TColorF;
     
    5870    function GetSpecularIndex: integer;
    5971    function GetSpecularOn: boolean;
     72    function GetTexture: IBGRAScanner;
     73    function GetTextureZoom: TPointF;
     74    procedure SetAmbiantAlpha(AValue: byte);
    6075    procedure SetAutoDiffuseColor(const AValue: boolean);
    6176    procedure SetAutoSpecularColor(const AValue: boolean);
     77    procedure SetAmbiantColor(const AValue: TBGRAPixel);
     78    procedure SetAmbiantColorF(const AValue: TColorF);
     79    procedure SetAmbiantColorInt(const AValue: TColorInt65536);
     80    procedure SetDiffuseAlpha(AValue: byte);
    6281    procedure SetDiffuseColor(const AValue: TBGRAPixel);
    6382    procedure SetDiffuseColorF(const AValue: TColorF);
    6483    procedure SetDiffuseColorInt(const AValue: TColorInt65536);
    6584    procedure SetLightThroughFactor(const AValue: single);
     85    procedure SetName(const AValue: string);
    6686    procedure SetSaturationHigh(const AValue: single);
    6787    procedure SetSaturationLow(const AValue: single);
     88    procedure SetSimpleAlpha(AValue: byte);
     89    procedure SetSimpleColor(AValue: TBGRAPixel);
     90    procedure SetSimpleColorF(AValue: TColorF);
     91    procedure SetSimpleColorInt(AValue: TColorInt65536);
    6892    procedure SetSpecularColor(const AValue: TBGRAPixel);
    6993    procedure SetSpecularColorF(const AValue: TColorF);
     
    7195    procedure SetSpecularIndex(const AValue: integer);
    7296    function GetAsObject: TObject;
     97    procedure SetTexture(AValue: IBGRAScanner);
     98    procedure SetTextureZoom(AValue: TPointF);
     99
     100    property AutoSimpleColor: boolean read GetAutoSimpleColor;
     101    property SimpleColor: TBGRAPixel read GetSimpleColor write SetSimpleColor;
     102    property SimpleColorF: TColorF read GetSimpleColorF write SetSimpleColorF;
     103    property SimpleColorInt: TColorInt65536 read GetSimpleColorInt write SetSimpleColorInt;
     104    property SimpleAlpha: byte read GetSimpleAlpha write SetSimpleAlpha;
     105
     106    property AmbiantColor: TBGRAPixel read GetAmbiantColor write SetAmbiantColor;
     107    property AmbiantColorF: TColorF read GetAmbiantColorF write SetAmbiantColorF;
     108    property AmbiantColorInt: TColorInt65536 read GetAmbiantColorInt write SetAmbiantColorInt;
     109    property AutoAmbiantColor: boolean read GetAutoAmbiantColor;
     110    property AmbiantAlpha: byte read GetAmbiantAlpha write SetAmbiantAlpha;
     111    property Texture: IBGRAScanner read GetTexture write SetTexture;
     112    property TextureZoom: TPointF read GetTextureZoom write SetTextureZoom;
    73113
    74114    property DiffuseColor: TBGRAPixel read GetDiffuseColor write SetDiffuseColor;
     
    76116    property DiffuseColorInt: TColorInt65536 read GetDiffuseColorInt write SetDiffuseColorInt;
    77117    property AutoDiffuseColor: boolean read GetAutoDiffuseColor write SetAutoDiffuseColor;
     118    property DiffuseAlpha: byte read GetDiffuseAlpha write SetDiffuseAlpha;
    78119    property SaturationLow: single read GetSaturationLow write SetSaturationLow;
    79120    property SaturationHigh: single read GetSaturationHigh write SetSaturationHigh;
     
    87128
    88129    property LightThroughFactor: single read GetLightThroughFactor write SetLightThroughFactor;
     130    property Name: string read GetName write SetName;
     131  end;
     132
     133  { IBGRANormal3D }
     134
     135  IBGRANormal3D = interface
     136    function GetCustomNormal: TPoint3D;
     137    function GetCustomNormal_128: TPoint3D_128;
     138    function GetViewNormal: TPoint3D;
     139    function GetViewNormal_128: TPoint3D_128;
     140    procedure SetCustomNormal(AValue: TPoint3D);
     141    procedure SetCustomNormal_128(AValue: TPoint3D_128);
     142    procedure SetViewNormal(AValue: TPoint3D);
     143    procedure SetViewNormal_128(AValue: TPoint3D_128);
     144    property ViewNormal: TPoint3D read GetViewNormal write SetViewNormal;
     145    property ViewNormal_128: TPoint3D_128 read GetViewNormal_128 write SetViewNormal_128;
     146    property CustomNormal: TPoint3D read GetCustomNormal write SetCustomNormal;
     147    property CustomNormal_128: TPoint3D_128 read GetCustomNormal_128 write SetCustomNormal_128;
    89148  end;
    90149
     
    93152  IBGRAVertex3D = interface
    94153    function GetColor: TBGRAPixel;
     154    function GetCustomFlags: DWord;
     155    function GetCustomNormal: TPoint3D;
     156    function GetCustomNormal_128: TPoint3D_128;
    95157    function GetLight: Single;
    96158    function GetProjectedCoord: TPointF;
     
    107169    function GetViewCoordZ: single;
    108170    procedure SetColor(const AValue: TBGRAPixel);
     171    procedure SetCustomFlags(AValue: DWord);
     172    procedure SetCustomNormal(AValue: TPoint3D);
     173    procedure SetCustomNormal_128(AValue: TPoint3D_128);
    109174    procedure SetLight(const AValue: Single);
    110175    procedure SetProjectedCoord(const AValue: TPointF);
     
    131196    property ViewNormal: TPoint3D read GetViewNormal write SetViewNormal;
    132197    property ViewNormal_128: TPoint3D_128 read GetViewNormal_128 write SetViewNormal_128;
     198    property CustomNormal: TPoint3D read GetCustomNormal write SetCustomNormal;
     199    property CustomNormal_128: TPoint3D_128 read GetCustomNormal_128 write SetCustomNormal_128;
    133200    property Usage: integer read GetUsage;
     201    property CustomFlags: DWord read GetCustomFlags write SetCustomFlags;
    134202    function GetAsObject: TObject;
    135203  end;
    136204
    137205  arrayOfIBGRAVertex3D = array of IBGRAVertex3D;
     206  TVertex3DCallback = procedure(AVertex: IBGRAVertex3D) of object;
    138207
    139208  { IBGRAPart3D }
     
    143212    function Add(x,y,z: single): IBGRAVertex3D;
    144213    function Add(pt: TPoint3D): IBGRAVertex3D;
     214    function Add(pt: TPoint3D; normal: TPoint3D): IBGRAVertex3D;
    145215    function Add(pt: TPoint3D_128): IBGRAVertex3D;
     216    function Add(pt: TPoint3D_128; normal: TPoint3D_128): IBGRAVertex3D;
     217    function AddNormal(x,y,z: single): IBGRANormal3D;
     218    function AddNormal(pt: TPoint3D): IBGRANormal3D;
     219    function AddNormal(pt: TPoint3D_128): IBGRANormal3D;
    146220    function Add(const coords: array of single): arrayOfIBGRAVertex3D;
    147221    function Add(const pts: array of TPoint3D): arrayOfIBGRAVertex3D;
     
    149223    procedure Add(const pts: array of IBGRAVertex3D);
    150224    procedure Add(AVertex: IBGRAVertex3D);
     225    function GetTotalNormalCount: integer;
    151226    function IndexOf(AVertex: IBGRAVertex3D): integer;
    152227    procedure RemoveVertex(Index: integer);
     228    procedure RemoveNormal(Index: integer);
    153229    function GetBoundingBox: TBox3D;
    154230    function GetMatrix: TMatrix3D;
     
    158234    function GetVertex(AIndex: Integer): IBGRAVertex3D;
    159235    function GetVertexCount: integer;
     236    function GetNormal(AIndex: Integer): IBGRANormal3D;
     237    function GetNormalCount: integer;
    160238    function GetTotalVertexCount: integer;
    161239    function GetContainer: IBGRAPart3D;
     
    165243    procedure Scale(size: TPoint3D; Before: boolean = true);
    166244    procedure SetMatrix(const AValue: TMatrix3D);
    167     procedure SetVertex(AIndex: Integer; const AValue: IBGRAVertex3D);
     245    procedure SetNormal(AIndex: Integer; AValue: IBGRANormal3D);
     246    procedure SetVertex(AIndex: Integer; AValue: IBGRAVertex3D);
    168247    procedure Translate(x,y,z: single; Before: boolean = true);
    169248    procedure Translate(ofs: TPoint3D; Before: boolean = true);
     
    180259    procedure RemoveUnusedVertices;
    181260    function CreatePart: IBGRAPart3D;
     261    procedure ForEachVertex(ACallback: TVertex3DCallback);
    182262    property VertexCount: integer read GetVertexCount;
     263    property NormalCount: integer read GetNormalCount;
    183264    property Vertex[AIndex: Integer]: IBGRAVertex3D read GetVertex write SetVertex;
     265    property Normal[AIndex: Integer]: IBGRANormal3D read GetNormal write SetNormal;
    184266    property Matrix: TMatrix3D read GetMatrix write SetMatrix;
    185267    property PartCount: integer read GetPartCount;
     
    188270    property BoundingBox: TBox3D read GetBoundingBox;
    189271    property TotalVertexCount: integer read GetTotalVertexCount;
     272    property TotalNormalCount: integer read GetTotalNormalCount;
    190273    property Container: IBGRAPart3D read GetContainer;
    191274  end;
     
    196279
    197280  IBGRAFace3D = interface
    198     procedure AddVertex(AVertex: IBGRAVertex3D);
     281    function AddVertex(AVertex: IBGRAVertex3D): integer;
    199282    function GetBiface: boolean;
     283    function GetCustomFlags: DWord;
    200284    function GetLightThroughFactorOverride: boolean;
    201285    function GetMaterial: IBGRAMaterial3D;
     
    207291    function GetTexture: IBGRAScanner;
    208292    function GetVertex(AIndex: Integer): IBGRAVertex3D;
     293    function GetNormal(AIndex: Integer): IBGRANormal3D;
    209294    function GetVertexColor(AIndex: Integer): TBGRAPixel;
    210295    function GetVertexColorOverride(AIndex: Integer): boolean;
     
    216301    function GetViewNormal_128: TPoint3D_128;
    217302    function GetLightThroughFactor: single;
     303    procedure SetCustomFlags(AValue: DWord);
    218304    procedure SetLightThroughFactor(const AValue: single);
    219305    procedure SetBiface(const AValue: boolean);
     
    225311    procedure SetTexCoordOverride(AIndex: Integer; const AValue: boolean);
    226312    procedure SetTexture(const AValue: IBGRAScanner);
    227     procedure SetVertex(AIndex: Integer; const AValue: IBGRAVertex3D);
     313    procedure SetVertex(AIndex: Integer; AValue: IBGRAVertex3D);
     314    procedure SetNormal(AIndex: Integer; AValue: IBGRANormal3D);
    228315    procedure SetVertexColor(AIndex: Integer; const AValue: TBGRAPixel);
    229316    procedure SetVertexColorOverride(AIndex: Integer; const AValue: boolean);
     
    250337    property MaterialName: string read GetMaterialName write SetMaterialName;
    251338    function GetAsObject: TObject;
    252   end;
     339    property CustomFlags: DWord read GetCustomFlags write SetCustomFlags;
     340  end;
     341
     342  TFace3DCallback = procedure(AFace: IBGRAFace3D) of object;
    253343
    254344  { IBGRAObject3D }
     
    261351    function GetMaterial: IBGRAMaterial3D;
    262352    function GetRefCount: integer;
     353    function GetTotalNormalCount: integer;
    263354    function GetTotalVertexCount: integer;
    264355    function GetLight: Single;
     
    274365    procedure SetParentLighting(const AValue: boolean);
    275366    procedure SetTexture(const AValue: IBGRAScanner);
    276     procedure ComputeWithMatrix(const AMatrix: TMatrix3D; const AProjection: TProjection3D);
     367    procedure ComputeWithMatrix(constref AMatrix: TMatrix3D; constref AProjection: TProjection3D);
    277368    procedure RemoveUnusedVertices;
     369    procedure ForEachVertex(ACallback: TVertex3DCallback);
     370    procedure ForEachFace(ACallback: TFace3DCallback);
    278371    function AddFaceReversed(const AVertices: array of IBGRAVertex3D): IBGRAFace3D;
    279372    function AddFace(const AVertices: array of IBGRAVertex3D): IBGRAFace3D;
     
    293386    property ParentLighting: boolean read GetParentLighting write SetParentLighting;
    294387    property TotalVertexCount: integer read GetTotalVertexCount;
     388    property TotalNormalCount: integer read GetTotalNormalCount;
    295389    property Material: IBGRAMaterial3D read GetMaterial write SetMaterial;
    296390    property Scene: TBGRAScene3D read GetScene;
  • GraphicTest/Packages/bgrabitmap/bgraslicescaling.pas

    r452 r472  
    6666    // or as a local owned copy in other cases
    6767    constructor Create(ABitmap: TBGRABitmap;
    68       AMarginTop, AMarginRight, AMarginBottom, AMarginLeft: integer);
     68      AMarginTop, AMarginRight, AMarginBottom, AMarginLeft: integer; ABitmapOwner: boolean = false);
    6969    constructor Create(ABitmap: TBitmap;
    7070      AMarginTop, AMarginRight, AMarginBottom, AMarginLeft: integer);
    7171    constructor Create(AFilename: string;
    7272      AMarginTop, AMarginRight, AMarginBottom, AMarginLeft: integer);
     73    constructor Create(AFilename: string; AIsUtf8: boolean;
     74      AMarginTop, AMarginRight, AMarginBottom, AMarginLeft: integer);
    7375    constructor Create(AStream: TStream;
    7476      AMarginTop, AMarginRight, AMarginBottom, AMarginLeft: integer);
    75     constructor Create(ABitmap: TBGRABitmap);
     77    constructor Create(ABitmap: TBGRABitmap; ABitmapOwner: boolean = false);
    7678    constructor Create(ABitmap: TBitmap);
    7779    constructor Create(AFilename: string);
     80    constructor Create(AFilename: string; AIsUtf8: boolean);
    7881    constructor Create(AStream: TStream);
    7982    constructor Create;
     
    121124    constructor Create(ABitmap: TBGRABitmap;
    122125      AMarginTop, AMarginRight, AMarginBottom, AMarginLeft, NumberOfItems: integer;
    123       Direction: TSliceScalingDirection);
     126      Direction: TSliceScalingDirection; ABitmapOwner: boolean = false);
    124127    constructor Create(ABitmap: TBitmap;
    125128      AMarginTop, AMarginRight, AMarginBottom, AMarginLeft, NumberOfItems: integer;
    126129      Direction: TSliceScalingDirection);
    127     constructor Create(AFilename: string;
     130    constructor Create(ABitmapFilename: string;
     131      AMarginTop, AMarginRight, AMarginBottom, AMarginLeft, NumberOfItems: integer;
     132      Direction: TSliceScalingDirection);
     133    constructor Create(ABitmapFilename: string; AIsUtf8: boolean;
    128134      AMarginTop, AMarginRight, AMarginBottom, AMarginLeft, NumberOfItems: integer;
    129135      Direction: TSliceScalingDirection);
     
    132138      Direction: TSliceScalingDirection);
    133139    destructor Destroy; override;
    134     constructor Create(Filename, Section: string);
     140    constructor Create(AIniFilename, ASection: string; AIsUtf8Filename: boolean= false);
    135141  public
    136142    procedure Draw(ItemNumber: integer; ABitmap: TBGRABitmap;
     
    166172constructor TBGRAMultiSliceScaling.Create(ABitmap: TBGRABitmap;
    167173  AMarginTop, AMarginRight, AMarginBottom, AMarginLeft, NumberOfItems: integer;
    168   Direction: TSliceScalingDirection);
     174  Direction: TSliceScalingDirection; ABitmapOwner: boolean = false);
    169175var
    170176  i: integer;
     
    172178begin
    173179  FBitmap := ABitmap;
    174   FBitmapOwned := false;
     180  FBitmapOwned := ABitmapOwner;
    175181  ItemWidth := ABitmap.Width;
    176182  ItemHeight := ABitmap.Height;
     
    203209begin
    204210  Create(TBGRABitmap.Create(ABitmap), AMarginTop, AMarginRight, AMarginBottom, AMarginLeft,
    205     NumberOfItems, Direction);
    206   FBitmapOwned := true;
    207 end;
    208 
    209 constructor TBGRAMultiSliceScaling.Create(AFilename: string;
     211    NumberOfItems, Direction, True);
     212end;
     213
     214constructor TBGRAMultiSliceScaling.Create(ABitmapFilename: string;
    210215  AMarginTop, AMarginRight, AMarginBottom, AMarginLeft, NumberOfItems: integer;
    211216  Direction: TSliceScalingDirection);
    212217begin
    213   Create(TBGRABitmap.Create(AFilename), AMarginTop, AMarginRight, AMarginBottom, AMarginLeft,
    214     NumberOfItems, Direction);
    215   FBitmapOwned := true;
     218  Create(TBGRABitmap.Create(ABitmapFilename), AMarginTop, AMarginRight, AMarginBottom, AMarginLeft,
     219    NumberOfItems, Direction, True);
     220end;
     221
     222constructor TBGRAMultiSliceScaling.Create(ABitmapFilename: string; AIsUtf8: boolean;
     223  AMarginTop, AMarginRight, AMarginBottom, AMarginLeft, NumberOfItems: integer;
     224  Direction: TSliceScalingDirection);
     225begin
     226  Create(TBGRABitmap.Create(ABitmapFilename,AIsUtf8), AMarginTop, AMarginRight, AMarginBottom, AMarginLeft,
     227    NumberOfItems, Direction, True);
    216228end;
    217229
     
    221233begin
    222234  Create(TBGRABitmap.Create(AStream), AMarginTop, AMarginRight, AMarginBottom, AMarginLeft,
    223     NumberOfItems, Direction);
    224   FBitmapOwned := true;
     235    NumberOfItems, Direction, True);
    225236end;
    226237
     
    236247end;
    237248
    238 constructor TBGRAMultiSliceScaling.Create(Filename, Section: string);
     249constructor TBGRAMultiSliceScaling.Create(AIniFilename, ASection: string;
     250  AIsUtf8Filename: boolean);
    239251var
    240252  i: integer;
     
    242254  Direction: TSliceScalingDirection;
    243255  defaultRepeat: string;
    244   IniPath,BitmapFilename: string;
    245 begin
    246   if FileExistsUTF8(Filename) then
    247   begin
    248     temp := TMemIniFile.Create(Filename);
    249     IniPath := ExtractFilePath(Filename);
    250 
    251     if temp.ReadBool(Section, 'HorizontalDirection', False) then
    252       Direction := sdHorizontal
    253     else
    254       Direction := sdVertical;
    255 
    256     BitmapFilename := temp.ReadString(Section, 'Bitmap', '');
    257     if (copy(BitmapFilename,1,2) = '.\') or (copy(BitmapFilename,1,2) = './') then
    258       BitmapFilename := IniPath+copy(BitmapFilename,3,Length(BitmapFilename)-2);
    259     Create(
    260       BitmapFilename,
    261       temp.ReadInteger(Section, 'MarginTop', 0),
    262       temp.ReadInteger(Section, 'MarginRight', 0),
    263       temp.ReadInteger(Section, 'MarginBottom', 0),
    264       temp.ReadInteger(Section, 'MarginLeft', 0),
    265       temp.ReadInteger(Section, 'NumberOfItems', 1),
    266       Direction);
    267 
    268     defaultRepeat := temp.ReadString(Section, 'Repeat', 'Auto');
    269     for i := 0 to High(FSliceScalingArray) do
    270       FSliceScalingArray[i].SliceRepeatAsString := temp.ReadString(Section, 'Repeat'+IntToStr(i+1), defaultRepeat);
    271 
    272     temp.Free;
    273   end;
     256  IniPathUTF8,BitmapFilename: string;
     257begin
     258  if AIsUtf8Filename then
     259  begin
     260    if not FileExistsUTF8(AIniFilename) then exit;
     261    temp := TMemIniFile.Create(UTF8ToSys(AIniFilename));
     262    IniPathUTF8 := ExtractFilePath(AIniFilename);
     263  end else
     264  begin
     265    if not FileExists(AIniFilename) then exit;
     266    temp := TMemIniFile.Create(AIniFilename);
     267    IniPathUTF8 := SysToUTF8(ExtractFilePath(AIniFilename));
     268  end;
     269
     270  if temp.ReadBool(ASection, 'HorizontalDirection', False) then
     271    Direction := sdHorizontal
     272  else
     273    Direction := sdVertical;
     274
     275  BitmapFilename := temp.ReadString(ASection, 'Bitmap', '');
     276  if (copy(BitmapFilename,1,2) = '.\') or (copy(BitmapFilename,1,2) = './') then
     277    BitmapFilename := IniPathUTF8+SysToUTF8(copy(BitmapFilename,3,Length(BitmapFilename)-2));
     278  Create(
     279    BitmapFilename,True,
     280    temp.ReadInteger(ASection, 'MarginTop', 0),
     281    temp.ReadInteger(ASection, 'MarginRight', 0),
     282    temp.ReadInteger(ASection, 'MarginBottom', 0),
     283    temp.ReadInteger(ASection, 'MarginLeft', 0),
     284    temp.ReadInteger(ASection, 'NumberOfItems', 1),
     285    Direction);
     286
     287  defaultRepeat := temp.ReadString(ASection, 'Repeat', 'Auto');
     288  for i := 0 to High(FSliceScalingArray) do
     289    FSliceScalingArray[i].SliceRepeatAsString := temp.ReadString(ASection, 'Repeat'+IntToStr(i+1), defaultRepeat);
     290
     291  temp.Free;
    274292end;
    275293
     
    582600
    583601constructor TBGRASliceScaling.Create(ABitmap: TBGRABitmap;
     602  AMarginTop, AMarginRight, AMarginBottom, AMarginLeft: integer; ABitmapOwner: boolean = false);
     603begin
     604  Create(ABitmap, ABitmapOwner);
     605  SetMargins(AMarginTop, AMarginRight, AMarginBottom, AMarginLeft);
     606end;
     607
     608constructor TBGRASliceScaling.Create(ABitmap: TBitmap;
    584609  AMarginTop, AMarginRight, AMarginBottom, AMarginLeft: integer);
    585610begin
     
    588613end;
    589614
    590 constructor TBGRASliceScaling.Create(ABitmap: TBitmap;
    591   AMarginTop, AMarginRight, AMarginBottom, AMarginLeft: integer);
    592 begin
    593   Create(ABitmap);
    594   SetMargins(AMarginTop, AMarginRight, AMarginBottom, AMarginLeft);
    595 end;
    596 
    597615constructor TBGRASliceScaling.Create(AFilename: string;
    598616  AMarginTop, AMarginRight, AMarginBottom, AMarginLeft: integer);
     
    602620end;
    603621
     622constructor TBGRASliceScaling.Create(AFilename: string; AIsUtf8: boolean;
     623  AMarginTop, AMarginRight, AMarginBottom, AMarginLeft: integer);
     624begin
     625  Create(AFilename, AIsUtf8);
     626  SetMargins(AMarginTop, AMarginRight, AMarginBottom, AMarginLeft);
     627end;
     628
    604629constructor TBGRASliceScaling.Create(AStream: TStream;
    605630  AMarginTop, AMarginRight, AMarginBottom, AMarginLeft: integer);
     
    609634end;
    610635
    611 constructor TBGRASliceScaling.Create(ABitmap: TBGRABitmap);
     636constructor TBGRASliceScaling.Create(ABitmap: TBGRABitmap; ABitmapOwner: boolean = false);
    612637begin
    613638  Init;
    614639  FBitmap := ABitmap;
    615   FBitmapOwned := False;
     640  FBitmapOwned := ABitmapOwner;
    616641  FBitmapSourceRect := rect(0,0,FBitmap.Width,FBitmap.Height);
    617642end;
     
    629654  Init;
    630655  FBitmap := TBGRABitmap.Create(AFilename);
     656  FBitmapOwned := True;
     657  FBitmapSourceRect := rect(0,0,FBitmap.Width,FBitmap.Height);
     658end;
     659
     660constructor TBGRASliceScaling.Create(AFilename: string; AIsUtf8: boolean);
     661begin
     662  Init;
     663  FBitmap := TBGRABitmap.Create(AFilename,AIsUtf8);
    631664  FBitmapOwned := True;
    632665  FBitmapSourceRect := rect(0,0,FBitmap.Width,FBitmap.Height);
  • GraphicTest/Packages/bgrabitmap/bgrasse.pas

    r452 r472  
    22
    33{$mode objfpc}{$H+}
     4
     5{$i bgrasse.inc}
    46
    57interface
     
    1921var UseSSE, UseSSE2, UseSSE3 : boolean;
    2022
    21 {$ifdef CPUI386}
     23{$ifdef BGRASSE_AVAILABLE}
    2224  {$asmmode intel}
    2325  //SSE rotate singles
     
    3739  procedure Normalize3D_128_SqLen(var v: TPoint3D_128; out SqLen: single);
    3840  operator * (const v1: TPoint3D_128; const factor: single): TPoint3D_128;
    39   operator + (const v1,v2: TPoint3D_128): TPoint3D_128;
     41  operator + (constref v1,v2: TPoint3D_128): TPoint3D_128;
    4042  operator - (const v1,v2: TPoint3D_128): TPoint3D_128;
    4143  operator - (const v: TPoint3D_128): TPoint3D_128; inline;
    4244  operator = (const v1,v2: TPoint3D_128): boolean; inline;
    4345  procedure ClearPoint3D_128(out v: TPoint3D_128);
    44   {$IFDEF CPUI386}
     46  {$IFDEF BGRASSE_AVAILABLE}
    4547  procedure ClearPoint3D_128_AlignedSSE(out v: TPoint3D_128);
    4648  {$ENDIF}
     
    4850
    4951var
    50   Add3D_Aligned : procedure (var dest: TPoint3D_128; const src: TPoint3D_128);
     52  Add3D_Aligned : procedure (var dest: TPoint3D_128; constref src: TPoint3D_128);
    5153  Normalize3D_128 : procedure (var v: TPoint3D_128);
    5254  VectProduct3D_128 : procedure (const u,v: TPoint3D_128; out w: TPoint3D_128);
    53   DotProduct3D_128 : function (const v1,v2: TPoint3D_128): single;
     55  DotProduct3D_128 : function (constref v1,v2: TPoint3D_128): single;
    5456
    5557const
     
    125127end;
    126128
    127 operator + (const v1,v2: TPoint3D_128): TPoint3D_128;
     129operator + (constref v1,v2: TPoint3D_128): TPoint3D_128;
    128130{$ifdef CPUI386} assembler;
    129131asm
     
    152154{$endif}
    153155
    154 {$ifdef CPUI386}
    155 procedure Add3D_AlignedSSE(var dest: TPoint3D_128; const src: TPoint3D_128); assembler;
    156 asm
    157   movaps xmm0, [eax]
    158   movups xmm1, [edx]
     156{$ifdef BGRASSE_AVAILABLE}
     157procedure Add3D_AlignedSSE(var dest: TPoint3D_128; constref src: TPoint3D_128); assembler;
     158asm
     159  movaps xmm0, [dest]
     160  movups xmm1, [src]
    159161  addps xmm0, xmm1
    160   movaps [eax], xmm0
    161 end;
    162 {$endif}
    163 
    164 procedure Add3D_NoSSE(var dest: TPoint3D_128; const src: TPoint3D_128);
     162  movaps [dest], xmm0
     163end;
     164{$endif}
     165
     166procedure Add3D_NoSSE(var dest: TPoint3D_128; constref src: TPoint3D_128);
    165167{$ifdef CPUI386} assembler;
    166168asm
     
    226228
    227229procedure ClearPoint3D_128(out v: TPoint3D_128);
    228 {$ifdef CPUI386}
    229 begin
     230{$ifdef cpux86_64} assembler;
     231asm
     232  push rbx
     233  mov rax,v
     234  xor rbx,rbx
     235  mov [rax],rbx
     236  mov [rax+8],rbx
     237  pop rbx
     238end;
     239{$else}
     240  {$ifdef CPUI386} assembler;
     241  asm
     242    push ebx
     243    mov eax,v
     244    xor ebx,ebx
     245    mov [eax],ebx
     246    mov [eax+4],ebx
     247    mov [eax+8],ebx
     248    pop ebx
     249  end;
     250  {$else}
     251  var p: pdword;
     252  begin
     253    p := @v;
     254    p^ := 0;
     255    inc(p);
     256    p^ := 0;
     257    inc(p);
     258    p^ := 0;
     259  end;
     260  {$endif}
     261{$endif}
     262
     263procedure ClearPoint3D_128_AlignedSSE(out v: TPoint3D_128);
     264{$ifdef BGRASSE_AVAILABLE} assembler;
    230265 asm
    231    push ebx
    232    mov eax,v
    233    xor ebx,ebx
    234    mov [eax],ebx
    235    mov [eax+4],ebx
    236    mov [eax+8],ebx
    237    pop ebx
     266  xorps xmm0,xmm0
     267  {$ifdef cpux86_64}
     268  mov rax,v
     269  movaps [rax],xmm0
     270  {$else}
     271  mov eax,v
     272  movaps [eax],xmm0
     273  {$endif}
    238274 end;
    239 end;
    240275{$else}
    241276var p: pdword;
     
    250285{$endif}
    251286
    252 procedure ClearPoint3D_128_AlignedSSE(out v: TPoint3D_128);
    253 {$ifdef CPUI386}
    254 begin
    255  asm
    256    xorps xmm0,xmm0
    257    movaps [eax],xmm0
    258  end;
    259 end;
    260 {$else}
    261 var p: pdword;
    262 begin
    263   p := @v;
    264   p^ := 0;
    265   inc(p);
    266   p^ := 0;
    267   inc(p);
    268   p^ := 0;
    269 end;
    270 {$endif}
    271 
    272287function IsPoint3D_128_Zero(const v: TPoint3D_128): boolean;
    273288begin
     
    302317{$endif}
    303318
    304 {$ifdef CPUI386}
    305 function DotProduct3D_128_SSE3(const v1,v2: TPoint3D_128): single; assembler;
     319{$ifdef BGRASSE_AVAILABLE}
     320function DotProduct3D_128_SSE3(constref v1,v2: TPoint3D_128): single; assembler;
    306321asm
    307322  movups xmm0, [v1]
     
    315330{$endif}
    316331
    317 function DotProduct3D_128_NoSSE(const v1,v2: TPoint3D_128): single;
     332function DotProduct3D_128_NoSSE(constref v1,v2: TPoint3D_128): single;
    318333begin
    319334  result := v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
     
    331346end;
    332347
    333 {$ifdef CPUI386}
     348{$ifdef BGRASSE_AVAILABLE}
    334349procedure Normalize3D_128_SSE1(var v: TPoint3D_128);
    335350var len: single;
    336351begin
    337352  asm
    338     mov eax, v
    339     movups xmm0, [eax]
    340     movaps xmm1, xmm0
    341     mulps xmm0, xmm0
     353    {$i sseloadv.inc}
     354    movaps xmm2, xmm1
     355    mulps xmm2, xmm2
    342356
    343357    //mix1
    344     movaps xmm7, xmm0
     358    movaps xmm7, xmm2
    345359    shufps xmm7, xmm7, $4e
    346     addps xmm0, xmm7
     360    addps xmm2, xmm7
    347361    //mix2
    348     movaps xmm7, xmm0
     362    movaps xmm7, xmm2
    349363    shufps xmm7, xmm7, $11
    350     addps xmm0, xmm7
    351 
    352     movss len, xmm0
     364    addps xmm2, xmm7
     365
     366    movss len, xmm2
    353367  end;
    354368  if (len = 0) then exit;
     
    361375  end else
    362376  asm
    363     rsqrtps xmm0, xmm0
    364     mulps xmm0, xmm1  //apply
    365     mov eax, v
    366     movups [eax], xmm0
    367   end;
    368 end;
    369 {$endif}
    370 
    371 {$ifdef CPUI386}
     377    rsqrtps xmm2, xmm2
     378    mulps xmm1, xmm2  //apply
     379    {$i ssesavev.inc}
     380  end;
     381end;
     382{$endif}
     383
     384{$ifdef BGRASSE_AVAILABLE}
    372385procedure Normalize3D_128_SSE3(var v: TPoint3D_128);
    373386var len: single;
    374387begin
    375388  asm
    376     mov eax, v
    377     movups xmm0, [eax]
    378     movaps xmm1, xmm0
    379     mulps xmm0, xmm0
    380 
    381     haddps xmm0,xmm0
    382     haddps xmm0,xmm0
    383 
    384     movss len, xmm0
     389    {$i sseloadv.inc}
     390    movaps xmm2, xmm1
     391    mulps xmm2, xmm2
     392
     393    haddps xmm2,xmm2
     394    haddps xmm2,xmm2
     395
     396    movss len, xmm2
    385397  end;
    386398  if (len = 0) then exit;
     
    393405  end else
    394406  asm
    395     rsqrtps xmm0, xmm0
    396     mulps xmm0, xmm1  //apply
    397     mov eax, v
    398     movups [eax], xmm0
     407    rsqrtps xmm2, xmm2
     408    mulps xmm1, xmm2  //apply
     409    {$i ssesavev.inc}
    399410  end;
    400411end;
     
    404415var InvLen: single;
    405416begin
    406   {$ifdef CPUI386}
     417  {$ifdef BGRASSE_AVAILABLE}
    407418    if UseSSE then
    408419    begin
    409420      asm
    410         mov eax, v
    411         movups xmm0, [eax]
    412         movaps xmm1, xmm0
    413         mulps xmm0, xmm0
     421        {$i sseloadv.inc}
     422        movaps xmm2, xmm1
     423        mulps xmm2, xmm2
    414424      end;
    415425      if UseSSE3 then
    416426      asm
    417         haddps xmm0,xmm0
    418         haddps xmm0,xmm0
    419         movss SqLen, xmm0
     427        haddps xmm2,xmm2
     428        haddps xmm2,xmm2
     429        movss SqLen, xmm2
    420430      end else
    421431      asm
    422432        //mix1
    423         movaps xmm7, xmm0
     433        movaps xmm7, xmm2
    424434        shufps xmm7, xmm7, $4e
    425         addps xmm0, xmm7
     435        addps xmm2, xmm7
    426436        //mix2
    427         movaps xmm7, xmm0
     437        movaps xmm7, xmm2
    428438        shufps xmm7, xmm7, $11
    429         addps xmm0, xmm7
    430         movss SqLen, xmm0
     439        addps xmm2, xmm7
     440        movss SqLen, xmm2
    431441      end;
    432442      if SqLen = 0 then exit;
     
    439449      end else
    440450      asm
    441         rsqrtps xmm0, xmm0
    442         mulps xmm0, xmm1  //apply
    443         mov eax, v
    444         movups [eax], xmm0
     451        rsqrtps xmm2, xmm2
     452        mulps xmm1, xmm2  //apply
     453        {$i ssesavev.inc}
    445454      end;
    446455    end
     
    465474end;
    466475
    467 {$ifdef CPUI386}
    468 procedure VectProduct3D_128_SSE(const u,v: TPoint3D_128; out w: TPoint3D_128); assembler;
    469 asm
    470   mov eax, u
    471   movups xmm6, [eax]
     476{$ifdef BGRASSE_AVAILABLE}
     477procedure VectProduct3D_128_SSE(constref u,v: TPoint3D_128; out w: TPoint3D_128); assembler;
     478asm
     479  {$ifdef cpux86_64}
     480  mov rax,u
     481  movups xmm6,[rax]
     482  {$else}
     483  mov eax,u
     484  movups xmm6,[eax]
     485  {$endif}
    472486  movaps xmm4, xmm6
    473487  shufps xmm6, xmm6, Shift231
    474488
    475   mov eax, v
    476   movups xmm7, [eax]
     489  {$ifdef cpux86_64}
     490  mov rax,v
     491  movups xmm7,[rax]
     492  {$else}
     493  mov eax,v
     494  movups xmm7,[eax]
     495  {$endif}
    477496  movaps xmm5,xmm7
    478497  shufps xmm7, xmm7, Shift312
     
    487506  subps xmm3,xmm4
    488507
     508  {$ifdef cpux86_64}
     509  mov rax,w
     510  movups [rax],xmm3
     511  {$else}
    489512  mov eax,w
    490   movups [eax], xmm3
     513  movups [eax],xmm3
     514  {$endif}
    491515end;
    492516{$endif}
     
    496520{$hints off}
    497521constructor TMemoryBlockAlign128.Create(size: integer);
    498 {$IFDEF CPUI386}
     522{$IFDEF BGRASSE_AVAILABLE}
    499523var
    500   delta: cardinal;
     524  delta: PtrUInt;
    501525begin
    502526  getmem(FContainer, size+15);
    503   delta := cardinal(FContainer) and 15;
     527  delta := PtrUInt(FContainer) and 15;
    504528  if delta <> 0 then delta := 16-delta;
    505529  FData := pbyte(FContainer)+delta;
     
    519543end;
    520544
    521 {$ifdef CPUI386}   {$ASMMODE ATT}
     545{$ifdef BGRASSE_AVAILABLE}
    522546function sse3_support : boolean;
    523547
     
    526550
    527551  begin
     552    {$IFDEF CPUI386}
    528553     if cpuid_support then
    529554       begin
    530555          asm
    531              pushl %ebx
    532              movl $1,%eax
     556             push ebx
     557             mov eax,1
    533558             cpuid
    534              movl %ecx,_ecx
    535              popl %ebx
     559             mov _ecx,ecx
     560             pop ebx
    536561          end;
    537562          sse3_support:=(_ecx and 1)<>0;
     
    539564     else
    540565       sse3_support:=false;
     566    {$ELSE}
     567    asm
     568       push rbx
     569       mov eax,1
     570       cpuid
     571       mov _ecx,ecx
     572       pop rbx
     573    end;
     574    sse3_support:=(_ecx and 1)<>0;
     575    {$ENDIF}
    541576  end;
    542577{$endif}
     
    546581  {$ifdef CPUI386}
    547582  UseSSE := is_sse_cpu and FLAG_ENABLED_SSE;
     583  {$else}
     584    {$ifdef cpux86_64}
     585    UseSSE := FLAG_ENABLED_SSE;
     586    {$else}
     587    UseSSE := false;
     588    {$endif}
     589  {$endif}
     590
     591  {$IFDEF BGRASSE_AVAILABLE}
    548592  if UseSSE then
    549593  begin
     594    {$ifdef cpux86_64}
     595    UseSSE2 := true;
     596    {$else}
    550597    UseSSE2 := is_sse2_cpu;
     598    {$endif}
    551599    UseSSE3 := sse3_support;
    552600
     
    565613  end
    566614  else
    567   {$endif}
     615  {$ENDIF}
    568616  begin
    569617    UseSSE := false;
  • GraphicTest/Packages/bgrabitmap/bgratext.pas

    r452 r472  
    55interface
    66
    7 { Text functions use a temporary bitmap where the operating system text drawing is used.
     7{
     8  Font rendering units : BGRAText, BGRATextFX, BGRAVectorize, BGRAFreeType
     9
     10  This unit provides basic text rendering functions using LCL, and general
     11  text definitions.
     12
     13  Text functions use a temporary bitmap where the operating system text drawing is used.
    814  Then it is scaled down (if antialiasing is activated), and colored.
    915
    10   These routines are rather slow. }
     16  These routines are rather slow, so you may use other font renderers
     17  like TBGRATextEffectFontRenderer in BGRATextFX if you want to use LCL fonts,
     18  or, if you have TrueType fonts files, you may use TBGRAFreeTypeFontRenderer
     19  in BGRAFreeType. }
    1120
    1221uses
    13   Classes, Types, SysUtils, Graphics, BGRABitmapTypes;
    14 
    15 procedure BGRATextOut(bmp: TBGRACustomBitmap; Font: TFont; Quality: TBGRAFontQuality; xf, yf: single; s: string;
     22  Classes, Types, SysUtils, Graphics, BGRABitmapTypes, InterfaceBase, BGRAPen, BGRAGrayscaleMask;
     23
     24type
     25  TWordBreakHandler = procedure(var ABeforeUTF8, AAfterUTF8: string) of object;
     26
     27  { TCustomLCLFontRenderer }
     28
     29  TCustomLCLFontRenderer = class(TBGRACustomFontRenderer)
     30  protected
     31    FFont: TFont;             //font parameters
     32    FWordBreakHandler: TWordBreakHandler;
     33    procedure UpdateFont; virtual;
     34    function TextSizeNoUpdateFont(sUTF8: string): TSize;
     35    procedure InternalTextWordBreak(ADest: TBGRACustomBitmap; ATextUTF8: string; x, y, AMaxWidth: integer; AColor: TBGRAPixel; ATexture: IBGRAScanner; AHorizAlign: TAlignment; AVertAlign: TTextLayout);
     36    procedure InternalTextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; c: TBGRAPixel; ATexture: IBGRAScanner);
     37  public
     38    procedure SplitText(var ATextUTF8: string; AMaxWidth: integer; out ARemainsUTF8: string);
     39    function GetFontPixelMetric: TFontPixelMetric; override;
     40    procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientationTenthDegCCW: integer; sUTF8: string; c: TBGRAPixel; align: TAlignment); override;
     41    procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientationTenthDegCCW: integer; sUTF8: string; texture: IBGRAScanner; align: TAlignment); override;
     42    procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; sUTF8: string; texture: IBGRAScanner; align: TAlignment); override;
     43    procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; sUTF8: string; c: TBGRAPixel; align: TAlignment); override;
     44    procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; c: TBGRAPixel); override;
     45    procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; texture: IBGRAScanner); override;
     46    procedure TextWordBreak(ADest: TBGRACustomBitmap; AText: string; x, y, AMaxWidth: integer; AColor: TBGRAPixel; AHorizAlign: TAlignment; AVertAlign: TTextLayout);
     47    procedure TextWordBreak(ADest: TBGRACustomBitmap; AText: string; x, y, AMaxWidth: integer; ATexture: IBGRAScanner; AHorizAlign: TAlignment; AVertAlign: TTextLayout);
     48    function TextSize(sUTF8: string): TSize; override;
     49    constructor Create;
     50    destructor Destroy; override;
     51    property OnWordBreak: TWordBreakHandler read FWordBreakHandler write FWordBreakHandler;
     52  end;
     53
     54  { TLCLFontRenderer }
     55
     56  TLCLFontRenderer = class(TCustomLCLFontRenderer)
     57  protected
     58    function TextSurfaceSmaller(sUTF8: string; ARect: TRect): boolean;
     59  public
     60    procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; c: TBGRAPixel); override;
     61    procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; texture: IBGRAScanner); override;
     62  end;
     63
     64function CleanTextOutString(s: string): string; //this works with UTF8 strings as well
     65function RemoveLineEnding(var s: string; indexByte: integer): boolean; //this works with UTF8 strings however the index is the byte index
     66function RemoveLineEndingUTF8(var sUTF8: string; indexUTF8: integer): boolean;
     67
     68procedure BGRATextOut(bmp: TBGRACustomBitmap; Font: TFont; Quality: TBGRAFontQuality; xf, yf: single; sUTF8: string;
    1669  c: TBGRAPixel; tex: IBGRAScanner; align: TAlignment; CustomAntialiasingLevel: Integer = 0);
    1770
    18 procedure BGRATextOutAngle(bmp: TBGRACustomBitmap; Font: TFont; Quality: TBGRAFontQuality; xf, yf: single; orientation: integer;
    19   s: string; c: TBGRAPixel; tex: IBGRAScanner; align: TAlignment; CustomAntialiasingLevel: Integer = 0);
     71procedure BGRATextOutAngle(bmp: TBGRACustomBitmap; Font: TFont; Quality: TBGRAFontQuality; xf, yf: single; orientationTenthDegCCW: integer;
     72  sUTF8: string; c: TBGRAPixel; tex: IBGRAScanner; align: TAlignment; CustomAntialiasingLevel: Integer = 0);
    2073
    2174procedure BGRATextRect(bmp: TBGRACustomBitmap; Font: TFont; Quality: TBGRAFontQuality; ARect: TRect; x, y: integer;
    22   s: string; style: TTextStyle; c: TBGRAPixel; tex: IBGRAScanner; CustomAntialiasingLevel: Integer = 0);
    23 
    24 function BGRATextSize(Font: TFont; Quality: TBGRAFontQuality; s: string; CustomAntialiasingLevel: Integer): TSize;
    25 
    26 function BGRAOriginalTextSize(Font: TFont; Quality: TBGRAFontQuality; s: string; CustomAntialiasingLevel: integer): TSize;
    27 
    28 function GetFontHeightSign(AFont: TFont): integer;
     75  sUTF8: string; style: TTextStyle; c: TBGRAPixel; tex: IBGRAScanner; CustomAntialiasingLevel: Integer = 0);
     76
     77function BGRATextSize(Font: TFont; Quality: TBGRAFontQuality; sUTF8: string; CustomAntialiasingLevel: Integer): TSize;
     78function BGRAOriginalTextSize(Font: TFont; Quality: TBGRAFontQuality; sUTF8: string; CustomAntialiasingLevel: integer): TSize;
     79function BGRAOriginalTextSizeEx(Font: TFont; Quality: TBGRAFontQuality; sUTF8: string; CustomAntialiasingLevel: Integer; out actualAntialiasingLevel: integer): TSize;
     80procedure BGRADefaultWordBreakHandler(var ABefore,AAfter: string);
     81
     82function BGRATextUnderline(ATopLeft: TPointF; AWidth: Single; AMetrics: TFontPixelMetric): ArrayOfTPointF; overload;
     83function BGRATextUnderline(ATopLeft: TPointF; AWidth: Single; ABaseline, AEmHeight: single): ArrayOfTPointF; overload;
     84function BGRATextStrikeOut(ATopLeft: TPointF; AWidth: Single; AMetrics: TFontPixelMetric): ArrayOfTPointF; overload;
     85function BGRATextStrikeOut(ATopLeft: TPointF; AWidth: Single; ABaseline, AEmHeight, AXHeight: single): ArrayOfTPointF; overload;
     86
     87function GetFontHeightSign: integer;
    2988function FontEmHeightSign: integer;
    3089function FontFullHeightSign: integer;
    31 
     90function LCLFontAvailable: boolean;
     91
     92procedure BGRAFillClearTypeGrayscaleMask(dest: TBGRACustomBitmap; x,y: integer; xThird: integer; mask: TGrayscaleMask; color: TBGRAPixel; texture: IBGRAScanner = nil; RGBOrder: boolean=true);
    3293procedure BGRAFillClearTypeMask(dest: TBGRACustomBitmap; x,y: integer; xThird: integer; mask: TBGRACustomBitmap; color: TBGRAPixel; texture: IBGRAScanner = nil; RGBOrder: boolean=true);
    3394procedure BGRAFillClearTypeRGBMask(dest: TBGRACustomBitmap; x,y: integer; mask: TBGRACustomBitmap; color: TBGRAPixel; texture: IBGRAScanner = nil; KeepRGBOrder: boolean=true);
    34 
    35 const FontAntialiasingLevel = 6;
     95procedure BGRAInternalRenderText(dest: TBGRACustomBitmap; Quality: TBGRAFontQuality; grayscale: TGrayscaleMask; temp: TBGRACustomBitmap;
     96    x,y,xThird: integer; c: TBGRAPixel; tex: IBGRAScanner);
     97
     98const FontAntialiasingLevel = {$IFDEF LINUX}3{$ELSE}6{$ENDIF}; //linux rendering is already great
    3699const FontDefaultQuality = fqAntialiased;
    37100
     
    40103implementation
    41104
    42 uses Math, BGRABlend;
     105uses GraphType, Math, BGRABlend, LCLProc;
    43106
    44107const MaxPixelMetricCount = 100;
    45108
    46109var
     110  LCLFontDisabledValue: boolean;
    47111  TempBmp: TBitmap;
    48112  FontHeightSignComputed: boolean;
     
    68132  size: TSize;
    69133begin
     134  if not LCLFontAvailable then
     135  begin
     136    top := 0;
     137    bottom := 0;
     138    totalHeight := 0;
     139    exit;
     140  end;
    70141  size := BGRAOriginalTextSize(font,fqSystem,text,FontAntialiasingLevel);
    71142  mask := BGRABitmapFactory.Create(size.cx,size.cy,BGRABlack);
     
    221292end;
    222293
    223 function GetFontHeightSign(AFont: TFont): integer;
     294const DefaultFontHeightSign = -1;
     295
     296function BGRATextUnderline(ATopLeft: TPointF;
     297  AWidth: Single; AMetrics: TFontPixelMetric): ArrayOfTPointF;
     298begin
     299  result := BGRATextUnderline(ATopLeft, AWidth, AMetrics.Baseline,AMetrics.Baseline-AMetrics.CapLine);
     300end;
     301
     302function BGRATextUnderline(ATopLeft: TPointF;
     303  AWidth: Single; ABaseline, AEmHeight: single): ArrayOfTPointF;
     304var height,y: single;
     305begin
     306  height := AEmHeight*0.1;
     307  y := ATopLeft.y+ABaseline+1.5*height;
     308  result := ComputeWidePolylinePoints([PointF(ATopLeft.x,y),
     309                   PointF(ATopLeft.x+AWidth,y)],height,BGRABlack,pecFlat,pjsMiter,
     310                   SolidPenStyle, []);
     311end;
     312
     313function BGRATextStrikeOut(ATopLeft: TPointF; AWidth: Single;
     314  AMetrics: TFontPixelMetric): ArrayOfTPointF;
     315begin
     316  result := BGRATextStrikeOut(ATopLeft, AWidth, AMetrics.Baseline,AMetrics.Baseline-AMetrics.CapLine,AMetrics.Baseline-AMetrics.xLine);
     317end;
     318
     319function BGRATextStrikeOut(ATopLeft: TPointF; AWidth: Single; ABaseline,
     320  AEmHeight, AXHeight: single): ArrayOfTPointF;
     321var height,y: single;
     322begin
     323  height := AEmHeight*0.075;
     324  y := ATopLeft.y+ABaseline-AXHeight*0.5;
     325  result := ComputeWidePolylinePoints([PointF(ATopLeft.x,y),
     326                   PointF(ATopLeft.x+AWidth,y)],height,BGRABlack,pecFlat,pjsMiter,
     327                   SolidPenStyle, []);
     328end;
     329
     330function GetFontHeightSign: integer;
    224331var
    225332  HeightP1, HeightM1: integer;
    226333begin
     334  if LCLFontDisabledValue then
     335  begin
     336    result := DefaultFontHeightSign;
     337    exit;
     338  end;
     339
    227340  if FontHeightSignComputed then
    228341  begin
     
    231344  end;
    232345
    233   if tempBmp = nil then tempBmp := TBitmap.Create;
    234   tempBmp.Canvas.Font.Assign(AFont);
    235   tempBmp.Canvas.Font.Height := 20;
    236   HeightP1  := tempBmp.Canvas.TextExtent('Hg').cy;
    237   tempBmp.Canvas.Font.Height := -20;
    238   HeightM1  := tempBmp.Canvas.TextExtent('Hg').cy;
    239 
    240   if HeightP1 > HeightM1 then
    241     FontHeightSignValue := 1
    242   else
    243     FontHeightSignValue := -1;
     346  if WidgetSet.LCLPlatform = lpNoGUI then
     347  begin
     348    LCLFontDisabledValue:= True;
     349    result := -1;
     350    exit;
     351  end;
     352
     353  try
     354    if tempBmp = nil then tempBmp := TBitmap.Create;
     355    tempBmp.Canvas.Font.Name := 'Arial';
     356    tempBmp.Canvas.Font.Style := [];
     357    tempBmp.Canvas.Font.Height := 20;
     358    HeightP1  := tempBmp.Canvas.TextExtent('Hg').cy;
     359    tempBmp.Canvas.Font.Height := -20;
     360    HeightM1  := tempBmp.Canvas.TextExtent('Hg').cy;
     361
     362    if HeightP1 > HeightM1 then
     363      FontHeightSignValue := 1
     364    else
     365      FontHeightSignValue := -1;
     366  except
     367    on ex: Exception do
     368    begin
     369      LCLFontDisabledValue := True;
     370      result := -1;
     371      exit;
     372    end;
     373  end;
    244374  FontHeightSignComputed := true;
    245375  result := FontHeightSignValue;
     
    247377
    248378function FontEmHeightSign: integer;
    249 var f: TFont;
    250 begin
    251   if FontHeightSignComputed then
    252   begin
    253     result := FontHeightSignValue;
    254     exit;
    255   end;
    256   f:= TFont.Create;
    257   f.Name := 'Arial';
    258   result := GetFontHeightSign(f);
    259   f.Free;
     379begin
     380  result := GetFontHeightSign;
    260381end;
    261382
     
    265386end;
    266387
    267 procedure BGRAFillClearTypeMask(dest: TBGRACustomBitmap; x,y: integer; xThird: integer; mask: TBGRACustomBitmap; color: TBGRAPixel; texture: IBGRAScanner; RGBOrder: boolean);
     388function LCLFontAvailable: boolean;
     389begin
     390  if not FontHeightSignComputed then GetFontHeightSign;
     391  result := not LCLFontDisabledValue;
     392end;
     393
     394procedure BGRAFillClearTypeMaskPtr(dest: TBGRACustomBitmap; x,y: integer; xThird: integer; maskData: PByte; maskPixelSize: NativeInt; maskRowSize: NativeInt; maskWidth,maskHeight: integer; color: TBGRAPixel; texture: IBGRAScanner; RGBOrder: boolean);
    268395var
    269396  pdest: PBGRAPixel;
     
    302429  yMask,n: integer;
    303430  a: byte;
    304   pmask: PBGRAPixel;
     431  pmask: PByte;
    305432  dx:integer;
    306433  miny,maxy,minx,minxThird,maxx,alphaMinX,alphaMaxX,alphaLineLen: integer;
     
    322449
    323450begin
    324   alphaLineLen := mask.Width+2;
     451  alphaLineLen := maskWidth+2;
    325452
    326453  xThird -= 1; //for first subpixel
     
    333460  if y >= dest.ClipRect.Top then miny := 0
    334461    else miny := dest.ClipRect.Top-y;
    335   if y+mask.Height-1 < dest.ClipRect.Bottom then
    336     maxy := mask.Height-1 else
     462  if y+maskHeight-1 < dest.ClipRect.Bottom then
     463    maxy := maskHeight-1 else
    337464      maxy := dest.ClipRect.Bottom-1-y;
    338465
     
    351478  end;
    352479
    353   if x*3+xThird+mask.Width-1 < dest.ClipRect.Right*3 then
    354   begin
    355     maxx := (x*3+xThird+mask.Width-1) div 3;
     480  if x*3+xThird+maskWidth-1 < dest.ClipRect.Right*3 then
     481  begin
     482    maxx := (x*3+xThird+maskWidth-1) div 3;
    356483    alphaMaxX := alphaLineLen-1;
    357484    rightOnSide := false;
     
    373500      if leftOnSide then
    374501      begin
    375         pmask := mask.ScanLine[yMask]+(alphaMinX-1);
    376         a := pmask^.green div 3;
     502        pmask := maskData + (yMask*maskRowSize)+ (alphaMinX-1)*maskPixelSize;
     503        a := pmask^ div 3;
    377504        v1 := a+a;
    378505        v2 := a;
    379506        v3 := 0;
    380         inc(pmask);
     507        inc(pmask, maskPixelSize);
    381508      end else
    382509      begin
    383         pmask := mask.ScanLine[yMask];
     510        pmask := maskData + (yMask*maskRowSize);
    384511        v1 := 0;
    385512        v2 := 0;
     
    389516      for n := countBetween-1 downto 0 do
    390517      begin
    391         a := pmask^.green div 3;
     518        a := pmask^ div 3;
    392519        v1 += a;
    393520        v2 += a;
    394521        v3 += a;
    395         inc(pmask);
     522        inc(pmask, maskPixelSize);
    396523
    397524        NextAlpha(v1);
     
    403530      if rightOnSide then
    404531      begin
    405         a := pmask^.green div 3;
     532        a := pmask^ div 3;
    406533        v1 += a;
    407534        v2 += a+a;
     
    414541    end;
    415542  end;
     543end;
     544
     545procedure BGRAFillClearTypeGrayscaleMask(dest: TBGRACustomBitmap; x,
     546  y: integer; xThird: integer; mask: TGrayscaleMask; color: TBGRAPixel;
     547  texture: IBGRAScanner; RGBOrder: boolean);
     548var delta: NativeInt;
     549begin
     550  delta := mask.Width;
     551  BGRAFillClearTypeMaskPtr(dest,x,y,xThird,mask.ScanLine[0],1,delta,mask.Width,mask.Height,color,texture,RGBOrder);
     552end;
     553
     554procedure BGRAFillClearTypeMask(dest: TBGRACustomBitmap; x,y: integer; xThird: integer; mask: TBGRACustomBitmap; color: TBGRAPixel; texture: IBGRAScanner; RGBOrder: boolean);
     555var delta: NativeInt;
     556begin
     557  delta := mask.Width*sizeof(TBGRAPixel);
     558  if mask.LineOrder = riloBottomToTop then
     559    delta := -delta;
     560  BGRAFillClearTypeMaskPtr(dest,x,y,xThird,pbyte(mask.ScanLine[0])+1,sizeof(TBGRAPixel),delta,mask.Width,mask.Height,color,texture,RGBOrder);
    416561end;
    417562
     
    466611end;
    467612
    468 function BGRAOriginalTextSize(Font: TFont; Quality: TBGRAFontQuality; s: string; CustomAntialiasingLevel: Integer): TSize;
    469 begin
    470   if tempBmp = nil then tempBmp := TBitmap.Create;
    471   tempBmp.Canvas.Font := Font;
    472   if Quality in[fqFineClearTypeBGR,fqFineClearTypeRGB,fqFineAntialiasing] then tempBmp.Canvas.Font.Height := Font.Height*CustomAntialiasingLevel else
    473     tempBmp.Canvas.Font.Height := Font.Height;
    474   Result.cx := 0;
    475   Result.cy := 0;
    476   tempBmp.Canvas.Font.GetTextSize(s, Result.cx, Result.cy);
    477 end;
    478 
    479 function BGRATextSize(Font: TFont; Quality: TBGRAFontQuality; s: string; CustomAntialiasingLevel: Integer): TSize;
    480 begin
    481   result := BGRAOriginalTextSize(Font, Quality, s, CustomAntialiasingLevel);
     613function BGRAOriginalTextSizeEx(Font: TFont; Quality: TBGRAFontQuality; sUTF8: string; CustomAntialiasingLevel: Integer; out actualAntialiasingLevel: integer): TSize;
     614begin
     615  actualAntialiasingLevel:= CustomAntialiasingLevel;
     616  if not LCLFontAvailable then
     617    result := Size(0,0)
     618  else
     619  begin
     620    try
     621      if tempBmp = nil then tempBmp := TBitmap.Create;
     622      tempBmp.Canvas.Font := Font;
     623      if Quality in[fqFineClearTypeBGR,fqFineClearTypeRGB,fqFineAntialiasing] then
     624      begin
     625        tempBmp.Canvas.Font.Height := Font.Height*CustomAntialiasingLevel;
     626      end else
     627      begin
     628        tempBmp.Canvas.Font.Height := Font.Height;
     629        actualAntialiasingLevel:= 1;
     630      end;
     631      Result.cx := 0;
     632      Result.cy := 0;
     633      tempBmp.Canvas.Font.GetTextSize(sUTF8, Result.cx, Result.cy);
     634    except
     635      on ex: exception do
     636      begin
     637        result := Size(0,0);
     638        LCLFontDisabledValue := True;
     639      end;
     640    end;
     641
     642  end;
     643end;
     644
     645function BGRAOriginalTextSize(Font: TFont; Quality: TBGRAFontQuality; sUTF8: string; CustomAntialiasingLevel: Integer): TSize;
     646var actualAntialiasingLevel: integer;
     647begin
     648  result := BGRAOriginalTextSizeEx(Font, Quality, sUTF8, CustomAntialiasingLevel, actualAntialiasingLevel);
     649end;
     650
     651procedure BGRADefaultWordBreakHandler(var ABefore, AAfter: string);
     652var p: integer;
     653begin
     654  if (AAfter <> '') and (ABefore <> '') and (AAfter[1]<> ' ') and (ABefore[length(ABefore)] <> ' ') then
     655  begin
     656    p := length(ABefore);
     657    while (p > 1) and (ABefore[p-1] <> ' ') do dec(p);
     658    if p > 1 then //can put the word after
     659    begin
     660      AAfter := copy(ABefore,p,length(ABefore)-p+1)+AAfter;
     661      ABefore := copy(ABefore,1,p-1);
     662    end else
     663    begin //cannot put the word after, so before
     664
     665    end;
     666  end;
     667  while (ABefore <> '') and (ABefore[length(ABefore)] =' ') do delete(ABefore,length(ABefore),1);
     668  while (AAfter <> '') and (AAfter[1] =' ') do delete(AAfter,1,1);
     669end;
     670
     671function BGRATextSize(Font: TFont; Quality: TBGRAFontQuality; sUTF8: string; CustomAntialiasingLevel: Integer): TSize;
     672begin
     673  result := BGRAOriginalTextSize(Font, Quality, sUTF8, CustomAntialiasingLevel);
    482674  if Quality in[fqFineClearTypeBGR,fqFineClearTypeRGB,fqFineAntialiasing] then
    483675  begin
     
    488680
    489681procedure FilterOriginalText(Quality: TBGRAFontQuality; CustomAntialiasingLevel: Integer; var temp: TBGRACustomBitmap;
    490   c: TBGRAPixel; tex: IBGRAScanner);
     682  out grayscaleMask: TGrayscaleMask);
    491683var
     684  n: integer;
     685  maxAlpha: NativeUint;
     686  pb: PByte;
     687  multiplyX: integer;
    492688  resampled: TBGRACustomBitmap;
    493   P:       PBGRAPixel;
    494   n,xb,yb,v: integer;
    495   alpha, maxAlpha: integer;
    496 begin
     689begin
     690  grayscaleMask := nil;
    497691  case Quality of
    498   fqFineClearTypeBGR,fqFineClearTypeRGB:
    499     begin
     692  fqFineClearTypeBGR,fqFineClearTypeRGB,fqFineAntialiasing:
     693    begin
     694      if Quality in [fqFineClearTypeBGR,fqFineClearTypeRGB] then multiplyX:= 3 else multiplyX:= 1;
    500695      if (temp.Height < CustomAntialiasingLevel*8) and (temp.Height >= CustomAntialiasingLevel*3) then
    501696      begin
    502697        temp.ResampleFilter := rfSpline;
    503         resampled := temp.Resample(round(temp.width/CustomAntialiasingLevel*3),round(temp.Height/CustomAntialiasingLevel),rmFineResample);
     698        resampled := temp.Resample(round(temp.width/CustomAntialiasingLevel*multiplyX),round(temp.Height/CustomAntialiasingLevel),rmFineResample);
     699        grayscaleMask := TGrayscaleMask.Create(resampled,cGreen);
     700        FreeAndNil(resampled);
    504701      end else
    505         resampled := temp.Resample(round(temp.width/CustomAntialiasingLevel*3),round(temp.Height/CustomAntialiasingLevel),rmSimpleStretch);
     702        grayscaleMask := TGrayscaleMask.CreateDownSample(temp, round(temp.width/CustomAntialiasingLevel*multiplyX),round(temp.Height/CustomAntialiasingLevel));
     703      FreeAndNil(temp);
    506704
    507705      maxAlpha := 0;
    508       p := resampled.Data;
    509       for n := resampled.NbPixels - 1 downto 0 do
    510       begin
    511         alpha    := P^.green;
    512         if alpha > maxAlpha then maxAlpha := alpha;
    513         Inc(p);
     706      pb := grayscaleMask.Data;
     707      for n := grayscaleMask.NbPixels - 1 downto 0 do
     708      begin
     709        if Pb^ > maxAlpha then maxAlpha := Pb^;
     710        Inc(pb);
    514711      end;
    515       if maxAlpha <> 0 then
    516       begin
    517         p := resampled.Data;
    518         for n := resampled.NbPixels - 1 downto 0 do
     712      if (maxAlpha <> 0) and (maxAlpha <> 255) then
     713      begin
     714        pb := grayscaleMask.Data;
     715        for n := grayscaleMask.NbPixels - 1 downto 0 do
    519716        begin
    520           v:= integer(p^.green * 255) div maxAlpha;
    521           p^.red := v;
    522           p^.green := v;
    523           p^.blue := v;
    524           Inc(p);
     717          pb^:= pb^ * 255 div maxAlpha;
     718          Inc(pb);
    525719        end;
    526720      end;
    527       temp.Free;
    528       temp := resampled;
    529     end;
    530   fqFineAntialiasing:
    531     begin
    532       if (temp.Height < CustomAntialiasingLevel*8) and (temp.Height >= CustomAntialiasingLevel*3) then
    533       begin
    534         temp.ResampleFilter := rfSpline;
    535         resampled := temp.Resample(round(temp.width/CustomAntialiasingLevel),round(temp.Height/CustomAntialiasingLevel),rmFineResample);
    536       end else
    537         resampled := temp.Resample(round(temp.width/CustomAntialiasingLevel),round(temp.Height/CustomAntialiasingLevel),rmSimpleStretch);
    538 
    539       maxAlpha := 0;
    540       if tex = nil then
    541       begin
    542         p := resampled.Data;
    543         for n := resampled.NbPixels - 1 downto 0 do
    544         begin
    545           alpha    := P^.green;
    546           if alpha > maxAlpha then maxAlpha := alpha;
    547           if alpha = 0 then
    548             p^:= BGRAPixelTransparent else
    549           begin
    550             p^.red   := c.red;
    551             p^.green := c.green;
    552             p^.blue  := c.blue;
    553             p^.alpha := alpha;
    554           end;
    555           Inc(p);
    556         end;
    557 
    558         if maxAlpha <> 0 then
    559         begin
    560           p := resampled.Data;
    561           for n := resampled.NbPixels - 1 downto 0 do
    562           begin
    563             p^.alpha := integer(p^.alpha * c.alpha) div maxAlpha;
    564             Inc(p);
    565           end;
    566         end;
    567       end else
    568       begin
    569         p := resampled.Data;
    570         for n := resampled.NbPixels - 1 downto 0 do
    571         begin
    572           alpha    := P^.green;
    573           if alpha > maxAlpha then maxAlpha := alpha;
    574           Inc(p);
    575         end;
    576         if maxAlpha = 0 then
    577           resampled.FillTransparent
     721    end;
     722  fqSystem:
     723    begin
     724      grayscaleMask := TGrayscaleMask.Create(temp, cGreen);
     725      FreeAndNil(temp);
     726      pb := grayscaleMask.Data;
     727      for n := grayscaleMask.NbPixels - 1 downto 0 do
     728      begin
     729        pb^:= GammaExpansionTab[pb^] shr 8;
     730        Inc(pb);
     731      end;
     732    end;
     733  end;
     734end;
     735
     736function CleanTextOutString(s: string): string;
     737var idxIn, idxOut: integer;
     738begin
     739  setlength(result, length(s));
     740  idxIn := 1;
     741  idxOut := 1;
     742  while IdxIn <= length(s) do
     743  begin
     744    if not (s[idxIn] in[#13,#10,#9]) then //those characters are always 1 byte long so it is the same with UTF8
     745    begin
     746      result[idxOut] := s[idxIn];
     747      inc(idxOut);
     748    end;
     749    inc(idxIn);
     750  end;
     751  setlength(result, idxOut-1);
     752end;
     753
     754function RemoveLineEnding(var s: string; indexByte: integer): boolean;
     755begin //we can ignore UTF8 character length because #13 and #10 are always 1 byte long
     756      //so this function can be applied to UTF8 strings as well
     757  result := false;
     758  if length(s) >= indexByte then
     759  begin
     760    if s[indexByte] in[#13,#10] then
     761    begin
     762      result := true;
     763      if length(s) >= indexByte+1 then
     764      begin
     765        if (s[indexByte+1] <> s[indexByte]) and (s[indexByte+1] in[#13,#10]) then
     766          delete(s,indexByte,2)
    578767        else
    579           for yb := 0 to resampled.Height-1 do
    580           begin
    581             p := resampled.ScanLine[yb];
    582             tex.ScanMoveTo(0,yb);
    583             for xb := 0 to resampled.Width-1 do
    584             begin
    585               c := tex.ScanNextPixel;
    586               alpha    := integer(P^.green*c.alpha) div maxAlpha;
    587               if alpha = 0 then
    588                 p^:= BGRAPixelTransparent else
    589               begin
    590                 c.alpha := alpha;
    591                 p^ := c;
    592               end;
    593               Inc(p);
    594             end;
    595           end;
    596       end;
    597 
    598       temp.Free;
    599       temp := resampled;
    600     end;
    601   fqSystem:
    602     begin
    603       if tex = nil then
    604       begin
    605         p := temp.Data;
    606         for n := temp.NbPixels - 1 downto 0 do
    607         begin
    608           alpha    := GammaExpansionTab[P^.green] shr 8;
    609           alpha    := (c.alpha * alpha) div (255);
    610           if alpha = 0 then p^:= BGRAPixelTransparent else
    611           begin
    612             p^.red   := c.red;
    613             p^.green := c.green;
    614             p^.blue  := c.blue;
    615             p^.alpha := alpha;
    616           end;
    617           Inc(p);
    618         end;
    619       end else
    620       begin
    621         for yb := 0 to temp.Height-1 do
    622         begin
    623           p := temp.Scanline[yb];
    624           tex.ScanMoveTo(0,yb);
    625           for xb := 0 to temp.Width-1 do
    626           begin
    627             c := tex.ScanNextPixel;
    628             alpha    := GammaExpansionTab[P^.green] shr 8;
    629             alpha    := (c.alpha * alpha) div (255);
    630             if alpha = 0 then p^:= BGRAPixelTransparent else
    631             begin
    632               p^.red   := c.red;
    633               p^.green := c.green;
    634               p^.blue  := c.blue;
    635               p^.alpha := alpha;
    636             end;
    637             Inc(p);
    638           end;
    639         end;
    640       end;
    641     end;
    642   end;
    643 end;
    644 
    645 procedure BGRATextOut(bmp: TBGRACustomBitmap; Font: TFont; Quality: TBGRAFontQuality; xf, yf: single; s: string;
     768          delete(s,indexByte,1);
     769      end
     770        else
     771          delete(s,indexByte,1);
     772    end;
     773  end;
     774end;
     775
     776function RemoveLineEndingUTF8(var sUTF8: string; indexUTF8: integer): boolean;
     777var indexByte: integer;
     778    pIndex: PChar;
     779begin
     780  pIndex := UTF8CharStart(@sUTF8[1],length(sUTF8),indexUTF8);
     781  if pIndex = nil then
     782  begin
     783    result := false;
     784    exit;
     785  end;
     786  indexByte := pIndex - @sUTF8[1];
     787  result := RemoveLineEnding(sUTF8, indexByte);
     788end;
     789
     790procedure BGRAInternalRenderText(dest: TBGRACustomBitmap; Quality: TBGRAFontQuality; grayscale: TGrayscaleMask; temp: TBGRACustomBitmap;
     791  x,y,xThird: integer; c: TBGRAPixel; tex: IBGRAScanner);
     792begin
     793  if Quality in [fqFineClearTypeBGR,fqFineClearTypeRGB,fqSystemClearType] then
     794  begin
     795    if grayscale <> nil then
     796      BGRAFillClearTypeGrayscaleMask(dest,x,y,xThird, grayscale,c,tex,Quality=fqFineClearTypeRGB)
     797    else if temp <> nil then
     798      BGRAFillClearTypeRGBMask(dest,x,y, temp,c,tex);
     799  end
     800  else
     801  begin
     802    if grayscale <> nil then
     803    begin
     804      if tex <> nil then
     805        grayscale.DrawAsAlpha(dest, x, y, tex) else
     806        grayscale.DrawAsAlpha(dest, x, y, c);
     807    end
     808    else if temp <> nil then
     809      dest.PutImage(x, y, temp, dmDrawWithTransparency);
     810  end;
     811end;
     812
     813procedure BGRATextOut(bmp: TBGRACustomBitmap; Font: TFont; Quality: TBGRAFontQuality; xf, yf: single; sUTF8: string;
    646814  c: TBGRAPixel; tex: IBGRAScanner; align: TAlignment; CustomAntialiasingLevel: Integer = 0);
    647815var
     
    653821  x,y :integer;
    654822  deltaX: single;
    655 begin
     823  grayscale: TGrayscaleMask;
     824  sizeFactor: integer;
     825begin
     826  if not LCLFontAvailable then exit;
     827
    656828  if CustomAntialiasingLevel = 0 then
    657829    CustomAntialiasingLevel:= FontAntialiasingLevel;
     
    659831  if Font.Orientation mod 3600 <> 0 then
    660832  begin
    661     BGRATextOutAngle(bmp,Font,Quality,xf,yf,Font.Orientation,s,c,tex,align);
    662     exit;
    663   end;
    664 
    665   size := BGRAOriginalTextSize(Font,Quality,s,CustomAntialiasingLevel);
     833    BGRATextOutAngle(bmp,Font,Quality,xf,yf,Font.Orientation,sUTF8,c,tex,align);
     834    exit;
     835  end;
     836
     837  size := BGRAOriginalTextSizeEx(Font,Quality,sUTF8,CustomAntialiasingLevel,sizeFactor);
    666838  if (size.cx = 0) or (size.cy = 0) then
    667839    exit;
     
    669841  if (size.cy >= 144) and (Quality in[fqFineAntialiasing,fqFineClearTypeBGR,fqFineClearTypeRGB]) and (CustomAntialiasingLevel > 4) then
    670842  begin
    671     BGRATextOut(bmp,Font,Quality,xf,yf,s,c,tex,align,4);
    672     exit;
    673   end;
    674 
    675   if Quality in[fqFineAntialiasing,fqFineClearTypeBGR,fqFineClearTypeRGB] then
    676   begin
    677     case align of
    678       taLeftJustify: ;
    679       taCenter: xf -= size.cx/2/CustomAntialiasingLevel;
    680       taRightJustify: xf -= size.cx/CustomAntialiasingLevel;
    681     end;
    682   end else
    683   begin
    684     case align of
    685       taLeftJustify: ;
    686       taCenter: xf -= size.cx/2;
    687       taRightJustify: xf -= size.cx;
    688     end;
     843    BGRATextOut(bmp,Font,Quality,xf,yf,sUTF8,c,tex,align,4);
     844    exit;
     845  end;
     846
     847  case align of
     848    taLeftJustify: ;
     849    taCenter: xf -= size.cx/2/sizeFactor;
     850    taRightJustify: xf -= size.cx/sizeFactor;
    689851  end;
    690852
     
    695857  tempSize.cx := size.cx;
    696858  tempSize.cy := size.cy;
    697   if Quality in[fqFineAntialiasing,fqFineClearTypeBGR,fqFineClearTypeRGB] then
    698   begin
    699     tempSize.cx += CustomAntialiasingLevel-1;
    700     tempSize.cx -= tempSize.cx mod CustomAntialiasingLevel;
    701     tempSize.cy += CustomAntialiasingLevel-1;
    702     tempSize.cy -= tempSize.cy mod CustomAntialiasingLevel;
     859  if sizeFactor <> 1 then
     860  begin
     861    tempSize.cx += sizeFactor-1;
     862    tempSize.cx -= tempSize.cx mod sizeFactor;
     863    tempSize.cy += sizeFactor-1;
     864    tempSize.cy -= tempSize.cy mod sizeFactor;
    703865
    704866    deltaX := xf-floor(xf);
     
    708870      deltaX -= xThird/3;
    709871    end;
    710     subX := round(CustomAntialiasingLevel*deltaX);
     872    subX := round(sizeFactor*deltaX);
    711873    x := round(floor(xf));
    712     if subX <> 0 then inc(tempSize.cx, CustomAntialiasingLevel);
    713     subY := round(CustomAntialiasingLevel*(yf-floor(yf)));
     874    if subX <> 0 then inc(tempSize.cx, sizeFactor);
     875    subY := round(sizeFactor*(yf-floor(yf)));
    714876    y := round(floor(yf));
    715     if subY <> 0 then inc(tempSize.cy, CustomAntialiasingLevel);
     877    if subY <> 0 then inc(tempSize.cy, sizeFactor);
    716878  end else
    717879  begin
     
    721883
    722884  xMargin := size.cy div 2;
    723   if Quality in[fqFineAntialiasing,fqFineClearTypeBGR,fqFineClearTypeRGB] then
    724   begin
    725     xMargin += CustomAntialiasingLevel-1;
    726     xMargin -= xMargin mod CustomAntialiasingLevel;
     885  if sizeFactor <> 1 then
     886  begin
     887    xMargin += sizeFactor-1;
     888    xMargin -= xMargin mod sizeFactor;
    727889  end;
    728890  tempSize.cx += xMargin*2;
     
    730892  temp := bmp.NewBitmap(tempSize.cx, tempSize.cy, BGRABlack);
    731893  temp.Canvas.Font := Font;
    732   if Quality in[fqFineAntialiasing,fqFineClearTypeBGR,fqFineClearTypeRGB] then temp.Canvas.Font.Height := Font.Height*CustomAntialiasingLevel
    733    else temp.Canvas.Font.Height := Font.Height;
     894  temp.Canvas.Font.Height := Font.Height*sizeFactor;
    734895  temp.Canvas.Font.Color := clWhite;
    735896  temp.Canvas.Brush.Style := bsClear;
    736   temp.Canvas.TextOut(xMargin+subX, subY, s);
    737 
    738   FilterOriginalText(Quality,CustomAntialiasingLevel, temp,c,tex);
    739 
    740   if Quality in [fqFineClearTypeBGR,fqFineClearTypeRGB] then
    741     BGRAFillClearTypeMask(bmp,x-round(xMargin/CustomAntialiasingLevel),y,xThird, temp,c,tex,Quality=fqFineClearTypeRGB)
    742   else
    743   begin
    744     if Quality = fqSystemClearType then
    745       BGRAFillClearTypeRGBMask(bmp,x-xMargin,y, temp,c,tex)
    746     else if Quality = fqFineAntialiasing then
    747       bmp.PutImage(x-round(xMargin/CustomAntialiasingLevel), y, temp, dmDrawWithTransparency)
    748     else bmp.PutImage(x-xMargin, y, temp, dmDrawWithTransparency);
    749   end;
    750   temp.Free;
    751 end;
    752 
    753 procedure BGRATextOutAngle(bmp: TBGRACustomBitmap; Font: TFont; Quality: TBGRAFontQuality; xf, yf: single; orientation: integer;
    754   s: string; c: TBGRAPixel; tex: IBGRAScanner; align: TAlignment; CustomAntialiasingLevel: Integer = 0);
     897  temp.Canvas.TextOut(xMargin+subX, subY, sUTF8);
     898
     899  FilterOriginalText(Quality,CustomAntialiasingLevel, temp, grayscale);
     900  dec(x,round(xMargin/sizeFactor));
     901  BGRAInternalRenderText(bmp, Quality, grayscale,temp, x,y,xThird, c,tex);
     902  if temp <> nil then temp.Free;
     903  if grayscale <> nil then grayscale.Free;
     904end;
     905
     906procedure BGRATextOutAngle(bmp: TBGRACustomBitmap; Font: TFont; Quality: TBGRAFontQuality; xf, yf: single;
     907  orientationTenthDegCCW: integer;
     908  sUTF8: string; c: TBGRAPixel; tex: IBGRAScanner; align: TAlignment; CustomAntialiasingLevel: Integer = 0);
    755909var
    756910  x,y: integer;
     
    766920  TempFont: TFont;
    767921  oldOrientation: integer;
     922  grayscale:TGrayscaleMask;
    768923
    769924  procedure rotBoundsAdd(pt: TPointF);
     
    778933
    779934begin
     935  if not LCLFontAvailable then exit;
     936
    780937  if CustomAntialiasingLevel = 0 then
    781938    CustomAntialiasingLevel:= FontAntialiasingLevel;
    782939
    783   if orientation mod 3600 = 0 then
     940  if orientationTenthDegCCW mod 3600 = 0 then
    784941  begin
    785942    oldOrientation := Font.Orientation;
    786943    Font.Orientation := 0;
    787     BGRATextOut(bmp,Font,Quality,xf,yf,s,c,tex,align);
     944    BGRATextOut(bmp,Font,Quality,xf,yf,sUTF8,c,tex,align);
    788945    Font.Orientation := oldOrientation;
    789946    exit;
     
    791948  TempFont := TFont.Create;
    792949  TempFont.Assign(Font);
    793   TempFont.Orientation := orientation;
     950  TempFont.Orientation := orientationTenthDegCCW;
    794951  TempFont.Height := Font.Height;
    795   size := BGRAOriginalTextSize(TempFont,Quality,s,CustomAntialiasingLevel);
     952  size := BGRAOriginalTextSizeEx(TempFont,Quality,sUTF8,CustomAntialiasingLevel,sizeFactor);
    796953  if (size.cx = 0) or (size.cy = 0) then
    797954  begin
     
    799956    exit;
    800957  end;
    801   if Quality in[fqFineAntialiasing,fqFineClearTypeBGR,fqFineClearTypeRGB] then
    802     sizeFactor := CustomAntialiasingLevel
    803   else
    804     sizeFactor := 1;
    805 
    806   cosA := cos(orientation*Pi/1800);
    807   sinA := sin(orientation*Pi/1800);
     958  tempFont.Free;
     959
     960  cosA := cos(orientationTenthDegCCW*Pi/1800);
     961  sinA := sin(orientationTenthDegCCW*Pi/1800);
    808962  TopRight := PointF(cosA*size.cx,-sinA*size.cx);
    809963  BottomRight := PointF(cosA*size.cx+sinA*size.cy,cosA*size.cy-sinA*size.cx);
     
    843997  temp.Canvas.Font := Font;
    844998  temp.Canvas.Font.Color := clWhite;
    845   temp.Canvas.Font.Orientation := orientation;
     999  temp.Canvas.Font.Orientation := orientationTenthDegCCW;
    8461000  temp.Canvas.Font.Height := round(Font.Height*sizeFactor);
    8471001  temp.Canvas.Brush.Style := bsClear;
    848   temp.Canvas.TextOut(-rotBounds.Left+deltaX, -rotBounds.Top+deltaY, s);
    849 
    850   FilterOriginalText(Quality,CustomAntialiasingLevel,temp,c,tex);
    851 
    852   if Quality in [fqFineClearTypeRGB,fqFineClearTypeBGR] then
    853     BGRAFillClearTypeMask(bmp, x, y, 0, temp, c,tex,Quality = fqFineClearTypeRGB) else
    854   begin
    855     if Quality = fqSystemClearType then
    856       BGRAFillClearTypeRGBMask(bmp, x, y, temp, c,tex)
    857     else
    858       bmp.PutImage(x, y, temp, dmDrawWithTransparency);
    859   end;
     1002  temp.Canvas.TextOut(-rotBounds.Left+deltaX, -rotBounds.Top+deltaY, sUTF8);
     1003
     1004  FilterOriginalText(Quality,CustomAntialiasingLevel,temp,grayscale);
     1005  BGRAInternalRenderText(bmp, Quality, grayscale,temp, x,y,0, c,tex);
    8601006  temp.Free;
    861   tempFont.Free;
     1007  grayscale.Free;
    8621008end;
    8631009
    8641010procedure BGRATextRect(bmp: TBGRACustomBitmap; Font: TFont; Quality: TBGRAFontQuality; ARect: TRect; x, y: integer;
    865   s: string; style: TTextStyle; c: TBGRAPixel; tex: IBGRAScanner; CustomAntialiasingLevel: Integer = 0);
     1011  sUTF8: string; style: TTextStyle; c: TBGRAPixel; tex: IBGRAScanner; CustomAntialiasingLevel: Integer = 0);
    8661012var
    8671013  lim: TRect;
     
    8701016  sizeFactor: integer;
    8711017  cr: TRect;
    872 begin
     1018  grayscale:TGrayscaleMask;
     1019begin
     1020  if not LCLFontAvailable then exit;
     1021
    8731022  if CustomAntialiasingLevel = 0 then
    8741023    CustomAntialiasingLevel:= FontAntialiasingLevel;
     
    9011050  temp.Canvas.Font.Color := clWhite;
    9021051  temp.Canvas.Brush.Style := bsClear;
    903   temp.Canvas.TextRect(rect(lim.Left-ARect.Left, lim.Top-ARect.Top, (ARect.Right-ARect.Left)*sizeFactor, (ARect.Bottom-ARect.Top)*sizeFactor), (x - lim.Left)*sizeFactor, (y - lim.Top)*sizeFactor, s, style);
    904 
    905   FilterOriginalText(Quality,CustomAntialiasingLevel,temp,c,tex);
    906   if Quality in [fqFineClearTypeBGR,fqFineClearTypeRGB] then
    907     BGRAFillClearTypeMask(bmp,lim.Left, lim.Top, 0, temp, c,tex,Quality = fqFineClearTypeRGB)
    908   else if Quality = fqSystemClearType then
    909     BGRAFillClearTypeRGBMask(bmp,lim.Left, lim.Top, temp, c,tex)
     1052  temp.Canvas.TextRect(rect(lim.Left-ARect.Left, lim.Top-ARect.Top, (ARect.Right-ARect.Left)*sizeFactor, (ARect.Bottom-ARect.Top)*sizeFactor), (x - lim.Left)*sizeFactor, (y - lim.Top)*sizeFactor, sUTF8, style);
     1053
     1054  FilterOriginalText(Quality,CustomAntialiasingLevel,temp,grayscale);
     1055  BGRAInternalRenderText(bmp, Quality, grayscale,temp, lim.left,lim.top,0, c,tex);
     1056  temp.Free;
     1057  grayscale.Free;
     1058end;
     1059
     1060{ TLCLFontRenderer }
     1061
     1062function TLCLFontRenderer.TextSurfaceSmaller(sUTF8: string; ARect: TRect): boolean;
     1063begin
     1064  with TextSize(sUTF8) do
     1065    result := cx*cy < (ARect.Right-ARect.Left)*(ARect.Bottom-ARect.Top);
     1066end;
     1067
     1068procedure TLCLFontRenderer.TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x,
     1069  y: integer; sUTF8: string; style: TTextStyle; c: TBGRAPixel);
     1070begin
     1071  if not style.Clipping or TextSurfaceSmaller(sUTF8,ARect) then
     1072  begin
     1073    InternalTextRect(ADest,ARect,x,y,sUTF8,style,c,nil);
     1074    exit;
     1075  end;
     1076  UpdateFont;
     1077  BGRAText.BGRATextRect(ADest,FFont,FontQuality,ARect,x,y,sUTF8,style,c,nil);
     1078end;
     1079
     1080procedure TLCLFontRenderer.TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x,
     1081  y: integer; sUTF8: string; style: TTextStyle; texture: IBGRAScanner);
     1082begin
     1083  if not style.Clipping or TextSurfaceSmaller(sUTF8,ARect) then
     1084  begin
     1085    InternalTextRect(ADest,ARect,x,y,sUTF8,style,BGRAPixelTransparent,texture);
     1086    exit;
     1087  end;
     1088  UpdateFont;
     1089  BGRAText.BGRATextRect(ADest,FFont,FontQuality,ARect,x,y,sUTF8,style,BGRAPixelTransparent,texture);
     1090end;
     1091
     1092{ TCustomLCLFontRenderer }
     1093
     1094{ Update font properties to internal TFont object }
     1095procedure TCustomLCLFontRenderer.UpdateFont;
     1096begin
     1097  if FFont.Name <> FontName then
     1098    FFont.Name := FontName;
     1099  if FFont.Style <> FontStyle then
     1100    FFont.Style := FontStyle;
     1101  if FFont.Height <> FontEmHeight * FontEmHeightSign then
     1102    FFont.Height := FontEmHeight * FontEmHeightSign;
     1103  if FFont.Orientation <> FontOrientation then
     1104    FFont.Orientation := FontOrientation;
     1105  if FontQuality = fqSystemClearType then
     1106    FFont.Quality := fqCleartype
    9101107  else
    911     bmp.PutImage(lim.Left, lim.Top, temp, dmDrawWithTransparency);
    912   temp.Free;
     1108    FFont.Quality := FontDefaultQuality;
     1109end;
     1110
     1111function TCustomLCLFontRenderer.TextSizeNoUpdateFont(sUTF8: string): TSize;
     1112begin
     1113  result := BGRAText.BGRATextSize(FFont,FontQuality,sUTF8,FontAntialiasingLevel);
     1114  if (result.cy >= 24) and (FontQuality in[fqFineAntialiasing,fqFineClearTypeBGR,fqFineClearTypeRGB]) then
     1115    result := BGRAText.BGRATextSize(FFont,FontQuality,sUTF8,4);
     1116end;
     1117
     1118procedure TCustomLCLFontRenderer.SplitText(var ATextUTF8: string;
     1119  AMaxWidth: integer; out ARemainsUTF8: string);
     1120var p,totalWidth: integer;
     1121begin
     1122  if ATextUTF8= '' then
     1123  begin
     1124    ARemainsUTF8 := '';
     1125    exit;
     1126  end;
     1127  if RemoveLineEnding(ATextUTF8,1) then
     1128  begin
     1129    ARemainsUTF8:= ATextUTF8;
     1130    ATextUTF8 := '';
     1131    exit;
     1132  end;
     1133  UpdateFont;
     1134
     1135  p := 1;
     1136  inc(p, UTF8CharacterLength(@ATextUTF8[p])); //UTF8 chars may be more than 1 byte long
     1137  while p < length(ATextUTF8)+1 do
     1138  begin
     1139    if RemoveLineEnding(ATextUTF8,p) then
     1140    begin
     1141      ARemainsUTF8:= copy(ATextUTF8,p,length(ATextUTF8)-p+1);
     1142      ATextUTF8 := copy(ATextUTF8,1,p-1);
     1143      exit;
     1144    end;
     1145    totalWidth := TextSizeNoUpdateFont(copy(ATextUTF8,1,p+UTF8CharacterLength(@ATextUTF8[p])-1)).cx; //copy whole last UTF8 char
     1146    if totalWidth > AMaxWidth then
     1147    begin
     1148      ARemainsUTF8:= copy(ATextUTF8,p,length(ATextUTF8)-p+1);
     1149      ATextUTF8 := copy(ATextUTF8,1,p-1); //this includes the whole last UTF8 char
     1150      if Assigned(FWordBreakHandler) then
     1151        FWordBreakHandler(ATextUTF8,ARemainsUTF8) else
     1152          BGRADefaultWordBreakHandler(ATextUTF8,ARemainsUTF8);
     1153      exit;
     1154    end;
     1155    inc(p, UTF8CharacterLength(@ATextUTF8[p]));
     1156  end;
     1157  ARemainsUTF8 := '';
     1158end;
     1159
     1160function TCustomLCLFontRenderer.GetFontPixelMetric: TFontPixelMetric;
     1161var fxFont: TFont;
     1162begin
     1163  UpdateFont;
     1164  if FontQuality in[fqSystem,fqSystemClearType] then
     1165    result := BGRAText.GetFontPixelMetric(FFont)
     1166  else
     1167  begin
     1168    FxFont := TFont.Create;
     1169    FxFont.Assign(FFont);
     1170    FxFont.Height := fxFont.Height*FontAntialiasingLevel;
     1171    Result:= BGRAText.GetFontPixelMetric(FxFont);
     1172    if Result.Baseline <> -1 then Result.Baseline:= round((Result.Baseline-1)/FontAntialiasingLevel);
     1173    if Result.CapLine <> -1 then Result.CapLine:= round(Result.CapLine/FontAntialiasingLevel);
     1174    if Result.DescentLine <> -1 then Result.DescentLine:= round((Result.DescentLine-1)/FontAntialiasingLevel);
     1175    if Result.Lineheight <> -1 then Result.Lineheight:= round(Result.Lineheight/FontAntialiasingLevel);
     1176    if Result.xLine <> -1 then Result.xLine:= round(Result.xLine/FontAntialiasingLevel);
     1177    FxFont.Free;
     1178  end;
     1179end;
     1180
     1181procedure TCustomLCLFontRenderer.TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientationTenthDegCCW: integer;
     1182  sUTF8: string; c: TBGRAPixel; align: TAlignment);
     1183begin
     1184  UpdateFont;
     1185  BGRAText.BGRATextOutAngle(ADest,FFont,FontQuality,x,y,orientationTenthDegCCW,sUTF8,c,nil,align);
     1186end;
     1187
     1188procedure TCustomLCLFontRenderer.TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientationTenthDegCCW: integer;
     1189  sUTF8: string; texture: IBGRAScanner; align: TAlignment);
     1190begin
     1191  UpdateFont;
     1192  BGRAText.BGRATextOutAngle(ADest,FFont,FontQuality,x,y,orientationTenthDegCCW,sUTF8,BGRAPixelTransparent,texture,align);
     1193end;
     1194
     1195procedure TCustomLCLFontRenderer.TextOut(ADest: TBGRACustomBitmap; x, y: single; sUTF8: string;
     1196  texture: IBGRAScanner; align: TAlignment);
     1197var mode : TBGRATextOutImproveReadabilityMode;
     1198begin
     1199  UpdateFont;
     1200
     1201  if Assigned(BGRATextOutImproveReadabilityProc) and (FontQuality in[fqFineAntialiasing,fqFineClearTypeBGR,fqFineClearTypeRGB]) and (FFont.Orientation mod 3600 = 0) then
     1202  begin
     1203    case FontQuality of
     1204      fqFineClearTypeBGR: mode := irClearTypeBGR;
     1205      fqFineClearTypeRGB: mode := irClearTypeRGB;
     1206    else
     1207      mode := irNormal;
     1208    end;
     1209    BGRATextOutImproveReadabilityProc(ADest,FFont,x,y,sUTF8,BGRAPixelTransparent,texture,align,mode);
     1210  end else
     1211    BGRAText.BGRATextOut(ADest,FFont,FontQuality,x,y,sUTF8,BGRAPixelTransparent,texture,align);
     1212end;
     1213
     1214procedure TCustomLCLFontRenderer.TextOut(ADest: TBGRACustomBitmap; x, y: single; sUTF8: string; c: TBGRAPixel;
     1215  align: TAlignment);
     1216var mode : TBGRATextOutImproveReadabilityMode;
     1217begin
     1218  UpdateFont;
     1219
     1220  if Assigned(BGRATextOutImproveReadabilityProc) and (FontQuality in[fqFineAntialiasing,fqFineClearTypeBGR,fqFineClearTypeRGB]) and (FFont.Orientation mod 3600 = 0) then
     1221  begin
     1222    case FontQuality of
     1223      fqFineClearTypeBGR: mode := irClearTypeBGR;
     1224      fqFineClearTypeRGB: mode := irClearTypeRGB;
     1225    else
     1226      mode := irNormal;
     1227    end;
     1228    BGRATextOutImproveReadabilityProc(ADest,FFont,x,y,sUTF8,c,nil,align,mode);
     1229  end else
     1230    BGRAText.BGRATextOut(ADest,FFont,FontQuality,x,y,sUTF8,c,nil,align);
     1231end;
     1232
     1233procedure TCustomLCLFontRenderer.TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string;
     1234  style: TTextStyle; c: TBGRAPixel);
     1235begin
     1236  InternalTextRect(ADest,ARect,x,y,sUTF8,style,c,nil);
     1237end;
     1238
     1239procedure TCustomLCLFontRenderer.TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string;
     1240  style: TTextStyle; texture: IBGRAScanner);
     1241begin
     1242  InternalTextRect(ADest,ARect,x,y,sUTF8,style,BGRAPixelTransparent,texture);
     1243end;
     1244
     1245procedure TCustomLCLFontRenderer.TextWordBreak(ADest: TBGRACustomBitmap;
     1246  AText: string; x, y, AMaxWidth: integer; AColor: TBGRAPixel;
     1247  AHorizAlign: TAlignment; AVertAlign: TTextLayout);
     1248begin
     1249  InternalTextWordBreak(ADest,AText,x,y,AMaxWidth,AColor,nil,AHorizAlign,AVertAlign);
     1250end;
     1251
     1252procedure TCustomLCLFontRenderer.TextWordBreak(ADest: TBGRACustomBitmap;
     1253  AText: string; x, y, AMaxWidth: integer; ATexture: IBGRAScanner;
     1254  AHorizAlign: TAlignment; AVertAlign: TTextLayout);
     1255begin
     1256  InternalTextWordBreak(ADest,AText,x,y,AMaxWidth,BGRAPixelTransparent,ATexture,AHorizAlign,AVertAlign);
     1257end;
     1258
     1259procedure TCustomLCLFontRenderer.InternalTextWordBreak(
     1260  ADest: TBGRACustomBitmap; ATextUTF8: string; x, y, AMaxWidth: integer;
     1261  AColor: TBGRAPixel; ATexture: IBGRAScanner; AHorizAlign: TAlignment; AVertAlign: TTextLayout);
     1262var ARemains: string;
     1263  stepX,stepY: integer;
     1264  lines: TStringList;
     1265  i: integer;
     1266  lineShift: single;
     1267begin
     1268  if (ATextUTF8 = '') or (AMaxWidth <= 0) then exit;
     1269
     1270  stepX := 0;
     1271  stepY := TextSize('Hg').cy;
     1272
     1273  if AVertAlign = tlTop then
     1274  begin
     1275    repeat
     1276      SplitText(ATextUTF8, AMaxWidth, ARemains);
     1277      if ATexture <> nil then
     1278        TextOut(ADest,x,y,ATextUTF8,ATexture,AHorizAlign)
     1279      else
     1280        TextOut(ADest,x,y,ATextUTF8,AColor,AHorizAlign);
     1281      ATextUTF8 := ARemains;
     1282      X+= stepX;
     1283      Y+= stepY;
     1284    until ARemains = '';
     1285  end else
     1286  begin
     1287    lines := TStringList.Create;
     1288    repeat
     1289      SplitText(ATextUTF8, AMaxWidth, ARemains);
     1290      lines.Add(ATextUTF8);
     1291      ATextUTF8 := ARemains;
     1292    until ARemains = '';
     1293    if AVertAlign = tlCenter then lineShift := lines.Count/2
     1294    else if AVertAlign = tlBottom then lineShift := lines.Count
     1295    else lineShift := 0;
     1296
     1297    X -= round(stepX*lineShift);
     1298    Y -= round(stepY*lineShift);
     1299    for i := 0 to lines.Count-1 do
     1300    begin
     1301      if ATexture <> nil then
     1302        TextOut(ADest,x,y,lines[i],ATexture,AHorizAlign)
     1303      else
     1304        TextOut(ADest,x,y,lines[i],AColor,AHorizAlign);
     1305      X+= stepX;
     1306      Y+= stepY;
     1307    end;
     1308    lines.Free;
     1309  end;
     1310end;
     1311
     1312procedure TCustomLCLFontRenderer.InternalTextRect(ADest: TBGRACustomBitmap;
     1313  ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; c: TBGRAPixel;
     1314  ATexture: IBGRAScanner);
     1315var
     1316  previousClip, intersected: TRect;
     1317  oldOrientation: integer;
     1318begin
     1319  previousClip := ADest.ClipRect;
     1320  if style.Clipping then
     1321  begin
     1322    intersected := rect(0,0,0,0);
     1323    if not IntersectRect(intersected, previousClip, ARect) then exit;
     1324    ADest.ClipRect := intersected;
     1325  end;
     1326  oldOrientation:= FontOrientation;
     1327  FontOrientation:= 0;
     1328
     1329  if not (style.Alignment in[taCenter,taRightJustify]) then ARect.Left := x;
     1330  if not (style.Layout in[tlCenter,tlBottom]) then ARect.top := y;
     1331  if ARect.Right <= ARect.Left then exit;
     1332  if style.Layout = tlCenter then Y := (ARect.Top+ARect.Bottom) div 2 else
     1333  if style.Layout = tlBottom then Y := ARect.Bottom else
     1334    Y := ARect.Top;
     1335  if style.Alignment = taCenter then X := (ARect.Left+ARect.Right) div 2 else
     1336  if style.Alignment = taRightJustify then X := ARect.Right else
     1337    X := ARect.Left;
     1338  if style.Wordbreak then
     1339    InternalTextWordBreak(ADest,sUTF8,X,Y,ARect.Right-ARect.Left,c,ATexture,style.Alignment,style.Layout)
     1340  else
     1341  begin
     1342    if style.Layout = tlCenter then Y -= TextSize(sUTF8).cy div 2;
     1343    if style.Layout = tlBottom then Y -= TextSize(sUTF8).cy;
     1344    if ATexture <> nil then
     1345      TextOut(ADest,X,Y,sUTF8,ATexture,style.Alignment)
     1346    else
     1347      TextOut(ADest,X,Y,sUTF8,c,style.Alignment);
     1348  end;
     1349
     1350  FontOrientation:= oldOrientation;
     1351  if style.Clipping then
     1352    ADest.ClipRect := previousClip;
     1353end;
     1354
     1355function TCustomLCLFontRenderer.TextSize(sUTF8: string): TSize;
     1356begin
     1357  UpdateFont;
     1358  result := TextSizeNoUpdateFont(sUTF8);
     1359end;
     1360
     1361constructor TCustomLCLFontRenderer.Create;
     1362begin
     1363  FFont := TFont.Create;
     1364end;
     1365
     1366destructor TCustomLCLFontRenderer.Destroy;
     1367begin
     1368  FFont.Free;
     1369  inherited Destroy;
    9131370end;
    9141371
  • GraphicTest/Packages/bgrabitmap/bgratextfx.pas

    r452 r472  
    33{$mode objfpc}{$H+}
    44
     5{
     6  Font rendering units : BGRAText, BGRATextFX, BGRAVectorize, BGRAFreeType
     7
     8  This unit provide text effects. The simplest way to render effects is to use TBGRATextEffectFontRenderer class.
     9  To do this, create an instance of this class and assign it to a TBGRABitmap.FontRenderer property. Now functions
     10  to draw text like TBGRABitmap.TextOut will use the chosen renderer. To set the effects, keep a variable containing
     11  the TBGRATextEffectFontRenderer class and modify ShadowVisible and other effects parameters.
     12
     13  The TBGRATextEffectFontRenderer class makes use of other classes depending on the situation. For example,
     14  TBGRATextEffect, which is also in this unit, provides effects on a text mask. But the renderer also uses
     15  BGRAVectorize unit in order to have big texts or to rotate them at will.
     16
     17  Note that you may need TBGRATextEffect if you want to have more control over text effects, especially
     18  if you always draw the same text. Keeping the same TBGRATextEffect object will avoid creating the text
     19  mask over and over again.
     20
     21  TextShadow function is a simple function to compute an image containing a text with shadow.
     22
     23}
     24
    525interface
    626
    727uses
    8   Classes, SysUtils, Graphics, Types, BGRABitmapTypes, BGRAPhongTypes;
     28  Classes, SysUtils, Graphics, Types, BGRABitmapTypes, BGRAPhongTypes, BGRAText, BGRAVectorize;
    929
    1030type
     31  TBGRATextEffect = class;
     32
     33  { TBGRATextEffectFontRenderer }
     34
     35  TBGRATextEffectFontRenderer = class(TCustomLCLFontRenderer)
     36  private
     37    function GetShaderLightPosition: TPoint;
     38    function GetVectorizedRenderer: TBGRAVectorizedFontRenderer;
     39    procedure SetShaderLightPosition(AValue: TPoint);
     40  protected
     41    FShaderOwner: boolean;
     42    FShader: TCustomPhongShading;
     43    FVectorizedRenderer: TBGRAVectorizedFontRenderer;
     44    function ShadowActuallyVisible :boolean;
     45    function ShaderActuallyActive: boolean;
     46    function OutlineActuallyVisible: boolean;
     47    procedure Init;
     48    function VectorizedFontNeeded: boolean;
     49    procedure InternalTextOut(ADest: TBGRACustomBitmap; x, y: single; s: string; c: TBGRAPixel; texture: IBGRAScanner; align: TAlignment);
     50  public
     51    ShaderActive: boolean;
     52
     53    ShadowVisible: boolean;
     54    ShadowColor: TBGRAPixel;
     55    ShadowRadius: integer;
     56    ShadowOffset: TPoint;
     57    ShadowQuality: TRadialBlurType;
     58
     59    OutlineColor: TBGRAPixel;
     60    OutlineWidth: single;
     61    OutlineVisible,OuterOutlineOnly: boolean;
     62    OutlineTexture: IBGRAScanner;
     63    constructor Create;
     64    constructor Create(AShader: TCustomPhongShading; AShaderOwner: boolean);
     65    destructor Destroy; override;
     66    procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientation: integer;
     67      s: string; texture: IBGRAScanner; align: TAlignment); override;
     68    procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientation: integer;
     69      s: string; c: TBGRAPixel; align: TAlignment); override;
     70    procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; s: string;
     71      texture: IBGRAScanner; align: TAlignment); override;
     72    procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; s: string; c: TBGRAPixel;
     73      align: TAlignment); override;
     74    function TextSize(sUTF8: string): TSize; override;
     75    property Shader: TCustomPhongShading read FShader;
     76    property ShaderLightPosition: TPoint read GetShaderLightPosition write SetShaderLightPosition;
     77    property VectorizedFontRenderer: TBGRAVectorizedFontRenderer read GetVectorizedRenderer;
     78  end;
    1179
    1280  { TBGRATextEffect }
     
    1482  TBGRATextEffect = class
    1583  private
     84    FShadowQuality: TRadialBlurType;
    1685    function GetBounds: TRect;
    17     function GetHeight: integer;
     86    function GetMaskHeight: integer;
     87    class function GetOutlineWidth: integer; static;
    1888    function GetShadowBounds(ARadius: integer): TRect;
    19     function GetWidth: integer;
     89    function GetMaskWidth: integer;
     90    function GetTextHeight: integer;
     91    function GetTextWidth: integer;
     92    procedure SetShadowQuality(AValue: TRadialBlurType);
    2093  protected
    2194    FTextMask: TBGRACustomBitmap;
     
    2497    FShadingAltitude: integer;
    2598    FShadingRounded: boolean;
    26     FWidth,FHeight: integer;
     99    FTextSize: TSize;
    27100    FOffset: TPoint;
    28     procedure DrawMaskMulticolored(ADest: TBGRACustomBitmap; AMask: TBGRACustomBitmap; X,Y: Integer; const AColors: array of TBGRAPixel);
    29     procedure DrawMask(ADest: TBGRACustomBitmap; AMask: TBGRACustomBitmap; X,Y: Integer; AColor: TBGRAPixel);
    30     procedure DrawMask(ADest: TBGRACustomBitmap; AMask: TBGRACustomBitmap; X,Y: Integer; ATexture: IBGRAScanner);
     101    function DrawMaskMulticolored(ADest: TBGRACustomBitmap; AMask: TBGRACustomBitmap; X,Y: Integer; const AColors: array of TBGRAPixel): TRect;
     102    function DrawMask(ADest: TBGRACustomBitmap; AMask: TBGRACustomBitmap; X,Y: Integer; AColor: TBGRAPixel): TRect;
     103    function DrawMask(ADest: TBGRACustomBitmap; AMask: TBGRACustomBitmap; X,Y: Integer; ATexture: IBGRAScanner): TRect;
    31104    function InternalDrawShaded(ADest: TBGRACustomBitmap; X,Y: integer; Shader: TCustomPhongShading; Altitude: integer; AColor: TBGRAPixel; ATexture: IBGRAScanner; ARounded: Boolean): TRect;
     105    procedure InitImproveReadability(AText: string; Font: TFont; SubOffsetX,SubOffsetY: single);
    32106    procedure Init(AText: string; Font: TFont; Antialiasing: boolean; SubOffsetX,SubOffsetY: single; GrainX, GrainY: Integer);
     107    procedure InitWithFontName(AText: string; AFontName: string; AFullHeight: integer; AStyle: TFontStyles; Antialiasing: boolean; SubOffsetX,SubOffsetY: single);
    33108  public
    34109    constructor Create(AText: string; Font: TFont; Antialiasing: boolean);
    35110    constructor Create(AText: string; Font: TFont; Antialiasing: boolean; SubOffsetX,SubOffsetY: single);
    36111    constructor Create(AText: string; Font: TFont; Antialiasing: boolean; SubOffsetX,SubOffsetY: single; GrainX, GrainY: Integer);
     112    constructor Create(AText: string; AFontName: string; AFullHeight: integer; Antialiasing: boolean);
     113    constructor Create(AText: string; AFontName: string; AFullHeight: integer; Antialiasing: boolean; SubOffsetX,SubOffsetY: single);
     114    constructor Create(AText: string; AFontName: string; AFullHeight: integer; AStyle: TFontStyles; Antialiasing: boolean);
     115    constructor Create(AText: string; AFontName: string; AFullHeight: integer; AStyle: TFontStyles; Antialiasing: boolean; SubOffsetX,SubOffsetY: single);
     116    constructor Create(AMask: TBGRACustomBitmap; AMaskOwner: boolean; AWidth,AHeight: integer; AOffset: TPoint);
    37117    procedure ApplySphere;
    38118    procedure ApplyVerticalCylinder;
    39119    procedure ApplyHorizontalCylinder;
    40     procedure Draw(ADest: TBGRACustomBitmap; X,Y: integer; AColor: TBGRAPixel);
    41     procedure Draw(ADest: TBGRACustomBitmap; X,Y: integer; ATexture: IBGRAScanner);
    42     procedure Draw(ADest: TBGRACustomBitmap; X, Y: integer; AColor: TBGRAPixel; AAlign: TAlignment);
    43     procedure Draw(ADest: TBGRACustomBitmap; X, Y: integer; ATexture: IBGRAScanner; AAlign: TAlignment);
     120    function Draw(ADest: TBGRACustomBitmap; X,Y: integer; AColor: TBGRAPixel): TRect;
     121    function Draw(ADest: TBGRACustomBitmap; X,Y: integer; ATexture: IBGRAScanner): TRect;
     122    function Draw(ADest: TBGRACustomBitmap; X, Y: integer; AColor: TBGRAPixel; AAlign: TAlignment): TRect;
     123    function Draw(ADest: TBGRACustomBitmap; X, Y: integer; ATexture: IBGRAScanner; AAlign: TAlignment): TRect;
    44124
    45125    function DrawShaded(ADest: TBGRACustomBitmap; X,Y: integer; Shader: TCustomPhongShading; Altitude: integer; AColor: TBGRAPixel; ARounded: Boolean = true): TRect;
     
    48128    function DrawShaded(ADest: TBGRACustomBitmap; X, Y: integer; Shader: TCustomPhongShading; Altitude: integer; ATexture: IBGRAScanner; AAlign: TAlignment; ARounded: Boolean = true): TRect;
    49129
    50     procedure DrawMulticolored(ADest: TBGRACustomBitmap; X,Y: integer; const AColors: array of TBGRAPixel);
    51     procedure DrawMulticolored(ADest: TBGRACustomBitmap; X,Y: integer; const AColors: array of TBGRAPixel; AAlign: TAlignment);
    52     procedure DrawOutline(ADest: TBGRACustomBitmap; X,Y: integer; AColor: TBGRAPixel);
    53     procedure DrawOutline(ADest: TBGRACustomBitmap; X,Y: integer; ATexture: IBGRAScanner);
    54     procedure DrawOutline(ADest: TBGRACustomBitmap; X,Y: integer; AColor: TBGRAPixel; AAlign: TAlignment);
    55     procedure DrawOutline(ADest: TBGRACustomBitmap; X,Y: integer; ATexture: IBGRAScanner; AAlign: TAlignment);
    56     procedure DrawShadow(ADest: TBGRACustomBitmap; X,Y,Radius: integer; AColor: TBGRAPixel);
    57     procedure DrawShadow(ADest: TBGRACustomBitmap; X,Y,Radius: integer; AColor: TBGRAPixel; AAlign: TAlignment);
     130    function DrawMulticolored(ADest: TBGRACustomBitmap; X,Y: integer; const AColors: array of TBGRAPixel): TRect;
     131    function DrawMulticolored(ADest: TBGRACustomBitmap; X,Y: integer; const AColors: array of TBGRAPixel; AAlign: TAlignment): TRect;
     132    function DrawOutline(ADest: TBGRACustomBitmap; X,Y: integer; AColor: TBGRAPixel): TRect;
     133    function DrawOutline(ADest: TBGRACustomBitmap; X,Y: integer; ATexture: IBGRAScanner): TRect;
     134    function DrawOutline(ADest: TBGRACustomBitmap; X,Y: integer; AColor: TBGRAPixel; AAlign: TAlignment): TRect;
     135    function DrawOutline(ADest: TBGRACustomBitmap; X,Y: integer; ATexture: IBGRAScanner; AAlign: TAlignment): TRect;
     136    function DrawShadow(ADest: TBGRACustomBitmap; X,Y,Radius: integer; AColor: TBGRAPixel): TRect;
     137    function DrawShadow(ADest: TBGRACustomBitmap; X,Y,Radius: integer; AColor: TBGRAPixel; AAlign: TAlignment): TRect;
    58138    destructor Destroy; override;
    59139    property TextMask: TBGRACustomBitmap read FTextMask;
    60140    property TextMaskOffset: TPoint read FOffset;
    61     property Width: integer read GetWidth;
    62     property Height: integer read GetHeight;
     141    property Width: integer read GetTextWidth; deprecated;
     142    property Height: integer read GetTextHeight; deprecated;
     143    property MaskWidth: integer read GetMaskWidth;
     144    property MaskHeight: integer read GetMaskHeight;
     145    property TextSize: TSize read FTextSize;
     146    property TextWidth: integer read GetTextWidth;
     147    property TextHeight: integer read GetTextHeight;
    63148    property Bounds: TRect read GetBounds;
    64149    property ShadowBounds[ARadius: integer]: TRect read GetShadowBounds;
     150    property ShadowQuality: TRadialBlurType read FShadowQuality write SetShadowQuality;
     151    class property OutlineWidth: integer read GetOutlineWidth;
    65152  end;
    66153
     
    68155    AOffSetX,AOffSetY: Integer; ARadius: Integer = 0; AFontStyle: TFontStyles = []; AFontName: String = 'Default'; AShowText: Boolean = True; AFontQuality: TBGRAFontQuality = fqFineAntialiasing): TBGRACustomBitmap;
    69156
    70 procedure BGRATextOutImproveReadability(bmp: TBGRACustomBitmap; AFont: TFont; xf,yf: single; text: string; color: TBGRAPixel; tex: IBGRAScanner; align: TAlignment; useClearType: boolean; ClearTypeRGBOrder: boolean);
     157procedure BGRATextOutImproveReadability(bmp: TBGRACustomBitmap; AFont: TFont; xf,yf: single; text: string; color: TBGRAPixel; tex: IBGRAScanner; align: TAlignment; mode : TBGRATextOutImproveReadabilityMode);
    71158
    72159implementation
    73160
    74 uses BGRAGradientScanner, BGRAText, GraphType, Math;
    75 
    76 procedure BGRATextOutImproveReadability(bmp: TBGRACustomBitmap; AFont: TFont; xf,yf: single; text: string; color: TBGRAPixel; tex: IBGRAScanner; align: TAlignment; useClearType: boolean; ClearTypeRGBOrder: boolean);
     161uses BGRAGradientScanner, GraphType, Math, BGRAGrayscaleMask;
     162
     163const DefaultOutlineWidth = 3;
     164
     165procedure BGRATextOutImproveReadability(bmp: TBGRACustomBitmap; AFont: TFont; xf,yf: single; text: string; color: TBGRAPixel; tex: IBGRAScanner; align: TAlignment; mode : TBGRATextOutImproveReadabilityMode);
    77166var
     167  useClearType,clearTypeRGBOrder: boolean;
    78168  metric: TFontPixelMetric;
    79169  deltaX: single;
     
    81171  toAdd: integer;
    82172  lines: array[0..3] of integer;
    83   parts: array[0..3] of TBGRACustomBitmap;
    84   n,nbLines,v: integer;
    85   alphaMax: byte;
     173  parts: array[0..3] of TGrayscaleMask;
     174  n,nbLines: integer;
     175  alphaMax: NativeUint;
    86176  ptrPart: TBGRACustomBitmap;
    87   pmask: PBGRAPixel;
     177  pmask: PByte;
    88178  fx: TBGRATextEffect;
    89179  FxFont: TFont;
     
    92182
    93183begin
     184  useClearType:= mode in[irClearTypeRGB,irClearTypeBGR];
     185  clearTypeRGBOrder := mode <> irClearTypeBGR;
    94186  deltaX := xf-floor(xf);
    95187  x := round(floor(xf));
     
    105197    begin
    106198      if ClearTypeRGBOrder then
    107         BGRATextOut(bmp, AFont, fqFineClearTypeRGB, xf,yf, text, color, tex, align) else
     199        BGRATextOut(bmp, AFont, fqFineClearTypeRGB, xf,yf, text, color, tex, align)
     200      else
    108201        BGRATextOut(bmp, AFont, fqFineClearTypeBGR, xf,yf, text, color, tex, align)
    109202    end else
     
    144237    fx := TBGRATextEffect.Create(text,FxFont,False,deltaX*FontAntialiasingLevel,0,FontAntialiasingLevel,FontAntialiasingLevel) else
    145238    fx := TBGRATextEffect.Create(text,FxFont,False,0,0,3,0);
     239
     240  if fx.TextMask = nil then
     241  begin
     242    fx.Free;
     243    FxFont.Free;
     244    exit;
     245  end;
    146246  alphaMax := 0;
    147247  prevCenter := 0;
     
    156256      ptrPart := fx.TextMask.GetPtrBitmap(fromy,lines[yb]);
    157257      if useClearType then
    158         parts[yb] := ptrPart.Resample(round(ptrPart.Width/FontAntialiasingLevel*3),round(ptrPart.Height/FontAntialiasingLevel),rmSimpleStretch)
     258        parts[yb] := TGrayscaleMask.CreateDownSample(ptrPart,round(ptrPart.Width/FontAntialiasingLevel*3),round(ptrPart.Height/FontAntialiasingLevel))
    159259      else
    160         parts[yb] := ptrPart.Resample(round(ptrPart.Width/FontAntialiasingLevel),round(ptrPart.Height/FontAntialiasingLevel),rmSimpleStretch);
     260        parts[yb] := TGrayscaleMask.CreateDownSample(ptrPart,round(ptrPart.Width/FontAntialiasingLevel),round(ptrPart.Height/FontAntialiasingLevel));
    161261      ptrPart.Free;
    162262
    163263      if alphaMax < 255 then
    164264      begin
    165         pmask := parts[yb].data;
     265        pmask := parts[yb].Data;
    166266        for n := parts[yb].NbPixels-1 downto 0 do
    167267        begin
    168           v := pmask^.green;
    169           if v > alphaMax then alphaMax := v;
     268          if pmask^ > alphaMax then alphaMax := pmask^;
    170269          inc(pmask);
    171270        end;
     
    194293  begin
    195294    case align of
    196     taCenter: xThird:= xThird+round(((fx.TextMaskOffset.x-fx.Width/2)/FontAntialiasingLevel+deltaX)*3);
    197     taRightJustify: xThird:= xThird+round(((fx.TextMaskOffset.x-fx.Width)/FontAntialiasingLevel+deltaX)*3);
     295    taCenter: xThird:= xThird+round(((fx.TextMaskOffset.x-fx.TextWidth/2)/FontAntialiasingLevel+deltaX)*3);
     296    taRightJustify: xThird:= xThird+round(((fx.TextMaskOffset.x-fx.TextWidth)/FontAntialiasingLevel+deltaX)*3);
    198297    else xThird:= xThird+round((fx.TextMaskOffset.x/FontAntialiasingLevel+deltaX)*3);
    199298    end;
     
    201300  begin
    202301    case align of
    203     taCenter: x:= x+round((fx.TextMaskOffset.x-fx.Width/2)/FontAntialiasingLevel);
    204     taRightJustify: x:= x+round((fx.TextMaskOffset.x-fx.Width)/FontAntialiasingLevel);
     302    taCenter: x:= x+round((fx.TextMaskOffset.x-fx.TextWidth/2)/FontAntialiasingLevel);
     303    taRightJustify: x:= x+round((fx.TextMaskOffset.x-fx.TextWidth)/FontAntialiasingLevel);
    205304    else x:= x+round(fx.TextMaskOffset.x/FontAntialiasingLevel);
    206305    end;
     
    215314      for n := parts[yb].NbPixels-1 downto 0 do
    216315      begin
    217         v := integer(pmask^.green)*255 div alphaMax;
    218         if v > 255 then v := 255;
    219         pmask^.green := v;
    220         pmask^.red := v;
    221         pmask^.blue := v;
     316        pmask^ := pmask^*255 div alphaMax;
    222317        inc(pmask);
    223318      end;
    224319    end;
    225320    if useClearType then
     321      BGRAFillClearTypeGrayscaleMask(bmp,x,cury,xThird,parts[yb],color,tex,ClearTypeRGBOrder)
     322    else if mode = irMask then
     323      parts[yb].Draw(bmp,x,cury)
     324    else
    226325    begin
    227326      if tex <> nil then
    228         bmp.FillClearTypeMask(x,cury,xThird,parts[yb],tex,ClearTypeRGBOrder) else
    229         bmp.FillClearTypeMask(x,cury,xThird,parts[yb],color,ClearTypeRGBOrder);
    230     end else
    231     begin
    232       if tex <> nil then
    233         bmp.FillMask(x,cury,parts[yb],tex) else
    234         bmp.FillMask(x,cury,parts[yb],color);
     327        parts[yb].DrawAsAlpha(bmp,x,cury,tex) else
     328        parts[yb].DrawAsAlpha(bmp,x,cury,color);
    235329    end;
    236330    inc(cury,parts[yb].Height);
     
    249343
    250344function TextShadow(AWidth,AHeight: Integer; AText: String; AFontHeight: Integer; ATextColor,AShadowColor: TBGRAPixel;
    251   AOffSetX,AOffSetY: Integer; ARadius: Integer = 0; AFontStyle: TFontStyles = []; AFontName: String = 'Default'; AShowText: Boolean = True; AFontQuality: TBGRAFontQuality = fqFineAntialiasing): TBGRACustomBitmap;
     345  AOffSetX,AOffSetY: Integer; ARadius: Integer = 0; AFontStyle: TFontStyles = []; AFontName: String = 'Default'; AShowText: Boolean = True;
     346  AFontQuality: TBGRAFontQuality = fqFineAntialiasing): TBGRACustomBitmap;
    252347var
    253348  bmpOut,bmpSdw: TBGRACustomBitmap; OutTxtSize: TSize; OutX,OutY: Integer;
     
    281376end;
    282377
     378{ TBGRATextEffectFontRenderer }
     379
     380function TBGRATextEffectFontRenderer.GetShaderLightPosition: TPoint;
     381begin
     382  if FShader = nil then
     383    result := point(0,0)
     384  else
     385    result := FShader.LightPosition;
     386end;
     387
     388function TBGRATextEffectFontRenderer.GetVectorizedRenderer: TBGRAVectorizedFontRenderer;
     389begin
     390  FVectorizedRenderer.FontEmHeight := FontEmHeight;
     391  FVectorizedRenderer.FontName := FontName;
     392  FVectorizedRenderer.FontOrientation:= FontOrientation;
     393  FVectorizedRenderer.FontQuality := FontQuality;
     394  FVectorizedRenderer.FontStyle:= FontStyle;
     395
     396  FVectorizedRenderer.ShadowColor := ShadowColor;
     397  FVectorizedRenderer.ShadowVisible := ShadowVisible;
     398  FVectorizedRenderer.ShadowOffset := ShadowOffset;
     399  FVectorizedRenderer.ShadowRadius := ShadowRadius;
     400
     401  FVectorizedRenderer.OutlineColor := OutlineColor;
     402  FVectorizedRenderer.OutlineVisible := OutlineVisible;
     403  FVectorizedRenderer.OutlineWidth := OutlineWidth;
     404  FVectorizedRenderer.OutlineTexture := OutlineTexture;
     405  FVectorizedRenderer.OuterOutlineOnly := OuterOutlineOnly;
     406  result := FVectorizedRenderer;
     407end;
     408
     409procedure TBGRATextEffectFontRenderer.SetShaderLightPosition(AValue: TPoint);
     410begin
     411  if FShader <> nil then
     412    FShader.LightPosition := AValue;
     413end;
     414
     415function TBGRATextEffectFontRenderer.ShadowActuallyVisible: boolean;
     416begin
     417  result := ShadowVisible and (ShadowColor.alpha <> 0);
     418end;
     419
     420function TBGRATextEffectFontRenderer.ShaderActuallyActive: boolean;
     421begin
     422  result := (FShader <> nil) and ShaderActive;
     423end;
     424
     425function TBGRATextEffectFontRenderer.OutlineActuallyVisible: boolean;
     426begin
     427  result := (OutlineWidth <> 0) and ((OutlineTexture <> nil) or (OutlineColor.alpha <> 0)) and OutlineVisible;
     428end;
     429
     430procedure TBGRATextEffectFontRenderer.Init;
     431begin
     432  ShaderActive := true;
     433
     434  ShadowColor := BGRABlack;
     435  ShadowVisible := false;
     436  ShadowOffset := Point(5,5);
     437  ShadowRadius := 5;
     438  ShadowQuality:= rbFast;
     439
     440  OutlineColor := BGRAPixelTransparent;
     441  OutlineVisible := True;
     442  OutlineWidth:= DefaultOutlineWidth;
     443  OuterOutlineOnly:= false;
     444  FVectorizedRenderer := TBGRAVectorizedFontRenderer.Create;
     445end;
     446
     447function TBGRATextEffectFontRenderer.VectorizedFontNeeded: boolean;
     448var bAntialiasing, bBigFont, bSpecialOutline, bOriented, bEffectVectorizedSupported: boolean;
     449  textsz: TSize;
     450begin
     451  bAntialiasing := FontQuality in [fqFineAntialiasing,fqFineClearTypeRGB,fqFineClearTypeBGR];
     452  textsz := inherited TextSize('Hg');
     453  bBigFont := (not OutlineActuallyVisible and (textsz.cy >= 24)) or
     454     (OutlineActuallyVisible and (textsz.cy > 42));
     455  bSpecialOutline:= OutlineActuallyVisible and (abs(OutlineWidth) <> DefaultOutlineWidth);
     456  bOriented := FontOrientation <> 0;
     457  bEffectVectorizedSupported := OutlineActuallyVisible or ShadowActuallyVisible;
     458  if ShaderActuallyActive and (FontOrientation = 0) then
     459    result := false //shader not supported by vectorized font
     460  else
     461    result := bSpecialOutline or
     462              (bAntialiasing and bBigFont) or
     463              (bOriented and bEffectVectorizedSupported);
     464end;
     465
     466procedure TBGRATextEffectFontRenderer.InternalTextOut(ADest: TBGRACustomBitmap;
     467  x, y: single; s: string; c: TBGRAPixel; texture: IBGRAScanner;
     468  align: TAlignment);
     469var fx: TBGRATextEffect;
     470  procedure DoOutline;
     471  begin
     472    if OutlineActuallyVisible then
     473    begin
     474      if OutlineTexture <> nil then
     475        fx.DrawOutline(ADest,round(x),round(y), OutlineTexture, align)
     476      else
     477        fx.DrawOutline(ADest,round(x),round(y), OutlineColor, align);
     478    end;
     479  end;
     480begin
     481  UpdateFont;
     482  if (FFont.Orientation <> 0) or (not ShaderActuallyActive and not ShadowActuallyVisible and not OutlineActuallyVisible) then
     483  begin
     484    if texture <> nil then
     485      inherited TextOut(ADest,x,y,s,texture,align)
     486    else
     487      inherited TextOut(ADest,x,y,s,c,align);
     488    exit;
     489  end;
     490  fx := TBGRATextEffect.Create(s, FFont, FontQuality in[fqFineAntialiasing,fqFineClearTypeBGR,fqFineClearTypeRGB], x-floor(x),y-floor(y));
     491  if ShadowActuallyVisible then
     492  begin
     493    fx.ShadowQuality := ShadowQuality;
     494    fx.DrawShadow(ADest,round(x)+ShadowOffset.X,round(y)+ShadowOffset.Y,ShadowRadius,ShadowColor, align);
     495  end;
     496  if OuterOutlineOnly then DoOutline;
     497  if texture <> nil then
     498  begin
     499    if ShaderActuallyActive then
     500      fx.DrawShaded(ADest,floor(x),floor(y), Shader, round(fx.TextSize.cy*0.05), texture, align)
     501    else
     502      fx.Draw(ADest,round(x),round(y), texture, align);
     503  end else
     504  begin
     505    if ShaderActuallyActive then
     506      fx.DrawShaded(ADest,floor(x),floor(y), Shader, round(fx.TextSize.cy*0.05), c, align)
     507    else
     508      fx.Draw(ADest,round(x),round(y), c, align);
     509  end;
     510  if not OuterOutlineOnly then DoOutline;
     511  fx.Free;
     512end;
     513
     514constructor TBGRATextEffectFontRenderer.Create;
     515begin
     516  inherited Create;
     517  FShader := nil;
     518  FShaderOwner:= false;
     519  Init;
     520end;
     521
     522constructor TBGRATextEffectFontRenderer.Create(AShader: TCustomPhongShading;
     523  AShaderOwner: boolean);
     524begin
     525  inherited Create;
     526  Init;
     527  FShader := AShader;
     528  FShaderOwner := AShaderOwner;
     529end;
     530
     531destructor TBGRATextEffectFontRenderer.Destroy;
     532begin
     533  if FShaderOwner then FShader.Free;
     534  FVectorizedRenderer.Free;
     535  inherited Destroy;
     536end;
     537
     538procedure TBGRATextEffectFontRenderer.TextOutAngle(ADest: TBGRACustomBitmap; x,
     539  y: single; orientation: integer; s: string; texture: IBGRAScanner;
     540  align: TAlignment);
     541begin
     542  VectorizedFontRenderer.TextOutAngle(ADest, x, y, orientation, s, texture, align);
     543end;
     544
     545procedure TBGRATextEffectFontRenderer.TextOutAngle(ADest: TBGRACustomBitmap; x,
     546  y: single; orientation: integer; s: string; c: TBGRAPixel; align: TAlignment);
     547begin
     548  VectorizedFontRenderer.TextOutAngle(ADest, x, y, orientation, s, c, align);
     549end;
     550
     551procedure TBGRATextEffectFontRenderer.TextOut(ADest: TBGRACustomBitmap; x,
     552  y: single; s: string; texture: IBGRAScanner; align: TAlignment);
     553begin
     554  if VectorizedFontNeeded then
     555    VectorizedFontRenderer.TextOut(ADest,x,y,s,texture,align)
     556  else
     557    InternalTextOut(ADest,x,y,s,BGRAPixelTransparent,texture,align);
     558end;
     559
     560procedure TBGRATextEffectFontRenderer.TextOut(ADest: TBGRACustomBitmap; x,
     561  y: single; s: string; c: TBGRAPixel; align: TAlignment);
     562begin
     563  if VectorizedFontNeeded then
     564    VectorizedFontRenderer.TextOut(ADest,x,y,s,c,align)
     565  else
     566    InternalTextOut(ADest,x,y,s,c,nil,align);
     567end;
     568
     569function TBGRATextEffectFontRenderer.TextSize(sUTF8: string): TSize;
     570begin
     571  if VectorizedFontNeeded then
     572    result := VectorizedFontRenderer.TextSize(sUTF8)
     573  else
     574  begin
     575    result := inherited TextSize(sUTF8);
     576  end;
     577end;
     578
    283579{ TBGRATextEffect }
    284580
     
    291587end;
    292588
    293 function TBGRATextEffect.GetHeight: integer;
    294 begin
    295   result := FHeight;
     589function TBGRATextEffect.GetMaskHeight: integer;
     590begin
     591  if FTextMask = nil then
     592    result := 0
     593  else
     594    result := FTextMask.Height;
     595end;
     596
     597class function TBGRATextEffect.GetOutlineWidth: integer; static;
     598begin
     599  result := DefaultOutlineWidth;
    296600end;
    297601
     
    308612end;
    309613
    310 function TBGRATextEffect.GetWidth: integer;
    311 begin
    312   result := FWidth;
    313 end;
    314 
    315 procedure TBGRATextEffect.DrawMaskMulticolored(ADest: TBGRACustomBitmap;
    316   AMask: TBGRACustomBitmap; X, Y: Integer; const AColors: array of TBGRAPixel);
     614function TBGRATextEffect.GetMaskWidth: integer;
     615begin
     616  if FTextMask = nil then
     617    result := 0
     618  else
     619    result := FTextMask.Width;
     620end;
     621
     622function TBGRATextEffect.GetTextHeight: integer;
     623begin
     624  result := FTextSize.cy;
     625end;
     626
     627function TBGRATextEffect.GetTextWidth: integer;
     628begin
     629  result := FTextSize.cx;
     630end;
     631
     632procedure TBGRATextEffect.SetShadowQuality(AValue: TRadialBlurType);
     633begin
     634  if FShadowQuality=AValue then Exit;
     635  FShadowQuality:=AValue;
     636  FreeAndNil(FShadowMask);
     637end;
     638
     639function TBGRATextEffect.DrawMaskMulticolored(ADest: TBGRACustomBitmap;
     640  AMask: TBGRACustomBitmap; X, Y: Integer; const AColors: array of TBGRAPixel
     641  ): TRect;
    317642var
    318643  scan: TBGRASolidColorMaskScanner;
     
    321646  emptyCol, nextCol: boolean;
    322647begin
    323   if (AMask = nil) or (length(AColors)=0) then exit;
     648  if (AMask = nil) or (length(AColors)=0) then
     649  begin
     650    result := EmptyRect;
     651    exit;
     652  end;
    324653  if (length(AColors)=0) then
    325654  begin
    326     DrawMask(ADest,AMask,X,Y,AColors[0]);
     655    result := DrawMask(ADest,AMask,X,Y,AColors[0]);
    327656    exit;
    328657  end;
     
    399728    ADest.FillRect(X+startX,Y,X+AMask.Width,Y+AMask.Height,scan,dmDrawWithTransparency);
    400729  scan.Free;
    401 end;
    402 
    403 procedure TBGRATextEffect.DrawMask(ADest: TBGRACustomBitmap; AMask: TBGRACustomBitmap; X,
    404   Y: Integer; AColor: TBGRAPixel);
     730  result := rect(X,Y,X+AMask.Width,Y+AMask.Height);
     731end;
     732
     733function TBGRATextEffect.DrawMask(ADest: TBGRACustomBitmap;
     734  AMask: TBGRACustomBitmap; X, Y: Integer; AColor: TBGRAPixel): TRect;
    405735var
    406736  scan: TBGRACustomScanner;
    407737begin
    408   if AMask = nil then exit;
     738  if AMask = nil then
     739  begin
     740    result := EmptyRect;
     741    exit;
     742  end;
    409743  scan := TBGRASolidColorMaskScanner.Create(AMask,Point(-X,-Y),AColor);
    410744  ADest.FillRect(X,Y,X+AMask.Width,Y+AMask.Height,scan,dmDrawWithTransparency);
    411745  scan.Free;
    412 end;
    413 
    414 procedure TBGRATextEffect.DrawMask(ADest: TBGRACustomBitmap; AMask: TBGRACustomBitmap; X,
    415   Y: Integer; ATexture: IBGRAScanner);
     746  result := rect(X,Y,X+AMask.Width,Y+AMask.Height);
     747end;
     748
     749function TBGRATextEffect.DrawMask(ADest: TBGRACustomBitmap;
     750  AMask: TBGRACustomBitmap; X, Y: Integer; ATexture: IBGRAScanner): TRect;
    416751var
    417752  scan: TBGRACustomScanner;
    418753begin
    419   if AMask = nil then exit;
     754  if AMask = nil then
     755  begin
     756    result := EmptyRect;
     757    exit;
     758  end;
    420759  scan := TBGRATextureMaskScanner.Create(AMask,Point(-X,-Y),ATexture);
    421760  ADest.FillRect(X,Y,X+AMask.Width,Y+AMask.Height,scan,dmDrawWithTransparency);
    422761  scan.Free;
     762  result := rect(X,Y,X+AMask.Width,Y+AMask.Height);
    423763end;
    424764
     
    433773  iBlurRadius: integer;
    434774begin
    435   if FTextMask = nil then
     775  if (FTextMask = nil) or (FTextMask.Width = 0) or (FTextMask.Height = 0) then
    436776  begin
    437777    result := EmptyRect;
     
    491831  end;
    492832
     833  inc(X, FOffset.X);
     834  Inc(Y, FOffset.Y);
    493835  if ATexture <> nil then
    494     Shader.DrawScan(ADest,FShadingMask,Altitude,X+FOffset.X,Y+FOffset.Y, ATexture)
     836    Shader.DrawScan(ADest,FShadingMask,Altitude,X,Y, ATexture)
    495837  else
    496     Shader.Draw(ADest,FShadingMask,Altitude,X+FOffset.X,Y+FOffset.Y, AColor);
    497   result := rect(X+FOffset.X,Y+FOffset.Y, X+FOffset.X+FShadingMask.Width,Y+FOffset.Y+FShadingMask.Height);
    498 end;
    499 
    500 procedure TBGRATextEffect.Draw(ADest: TBGRACustomBitmap; X, Y: integer;
    501   AColor: TBGRAPixel; AAlign: TAlignment);
     838    Shader.Draw(ADest,FShadingMask,Altitude,X,Y, AColor);
     839  result := rect(X,Y, X+FShadingMask.Width,Y+FShadingMask.Height);
     840end;
     841
     842procedure TBGRATextEffect.InitImproveReadability(AText: string; Font: TFont;
     843  SubOffsetX, SubOffsetY: single);
     844var size: TSize;
     845  overhang: integer;
     846begin
     847  if SubOffsetX < 0 then SubOffsetX := 0;
     848  if SubOffsetY < 0 then SubOffsetY := 0;
     849  size := BGRATextSize(Font, fqFineAntialiasing, AText, FontAntialiasingLevel);
     850  FTextSize := size;
     851  if size.cy = 0 then FTextSize.cy := BGRATextSize(Font, fqFineAntialiasing, 'Hg', FontAntialiasingLevel).cy;
     852  overhang := size.cy div 2;
     853  size.cx += 2*overhang + ceil(SubOffsetX);
     854  size.cy += 2 + ceil(SubOffsetY);
     855
     856  FOffset := Point(-overhang,-1); //include overhang
     857  FTextMask := BGRABitmapFactory.Create(size.cx,size.cy,BGRABlack);
     858  BGRATextOutImproveReadability(FTextMask, Font, overhang+SubOffsetX,1+SubOffsetY, AText, BGRAWhite, nil, taLeftJustify, irMask);
     859end;
     860
     861function TBGRATextEffect.Draw(ADest: TBGRACustomBitmap; X, Y: integer;
     862  AColor: TBGRAPixel; AAlign: TAlignment): TRect;
    502863begin
    503864  Case AAlign of
    504   taLeftJustify: Draw(ADest,X,Y,AColor);
    505   taRightJustify: Draw(ADest,X-Width,Y,AColor);
    506   taCenter: Draw(ADest,X-Width div 2,Y,AColor);
    507   end;
    508 end;
    509 
    510 procedure TBGRATextEffect.Draw(ADest: TBGRACustomBitmap; X, Y: integer;
    511   ATexture: IBGRAScanner; AAlign: TAlignment);
     865  taRightJustify: result := Draw(ADest,X-TextSize.cx,Y,AColor);
     866  taCenter: result := Draw(ADest,X-TextSize.cx div 2,Y,AColor);
     867  else result := Draw(ADest,X,Y,AColor);
     868  end;
     869end;
     870
     871function TBGRATextEffect.Draw(ADest: TBGRACustomBitmap; X, Y: integer;
     872  ATexture: IBGRAScanner; AAlign: TAlignment): TRect;
    512873begin
    513874  Case AAlign of
    514   taLeftJustify: Draw(ADest,X,Y,ATexture);
    515   taRightJustify: Draw(ADest,X-Width,Y,ATexture);
    516   taCenter: Draw(ADest,X-Width div 2,Y,ATexture);
     875  taRightJustify: result := Draw(ADest,X-TextSize.cx,Y,ATexture);
     876  taCenter: result := Draw(ADest,X-TextSize.cx div 2,Y,ATexture);
     877  else result := Draw(ADest,X,Y,ATexture);
    517878  end;
    518879end;
     
    538899  Case AAlign of
    539900  taLeftJustify: result := DrawShaded(ADest,X,Y,Shader,Altitude,AColor,ARounded);
    540   taRightJustify: result := DrawShaded(ADest,X-Width,Y,Shader,Altitude,AColor,ARounded);
    541   taCenter: result := DrawShaded(ADest,X-Width div 2,Y,Shader,Altitude,AColor,ARounded);
     901  taRightJustify: result := DrawShaded(ADest,X-TextSize.cx,Y,Shader,Altitude,AColor,ARounded);
     902  taCenter: result := DrawShaded(ADest,X-TextSize.cx div 2,Y,Shader,Altitude,AColor,ARounded);
    542903  else
    543904    result := EmptyRect;
     
    551912  Case AAlign of
    552913  taLeftJustify: result := DrawShaded(ADest,X,Y,Shader,Altitude,ATexture,ARounded);
    553   taRightJustify: result := DrawShaded(ADest,X-Width,Y,Shader,Altitude,ATexture,ARounded);
    554   taCenter: result := DrawShaded(ADest,X-Width div 2,Y,Shader,Altitude,ATexture,ARounded);
     914  taRightJustify: result := DrawShaded(ADest,X-TextSize.cx,Y,Shader,Altitude,ATexture,ARounded);
     915  taCenter: result := DrawShaded(ADest,X-TextSize.cx div 2,Y,Shader,Altitude,ATexture,ARounded);
    555916  else
    556917    result := EmptyRect;
     
    571932end;
    572933
    573 procedure TBGRATextEffect.Init(AText: string; Font: TFont;
    574   Antialiasing: boolean; SubOffsetX,SubOffsetY: single; GrainX, GrainY: Integer);
     934constructor TBGRATextEffect.Create(AText: string; AFontName: string;
     935  AFullHeight: integer; Antialiasing: boolean);
     936begin
     937  InitWithFontName(AText, AFontName, AFullHeight, [], Antialiasing, 0, 0);
     938end;
     939
     940constructor TBGRATextEffect.Create(AText: string; AFontName: string;
     941  AFullHeight: integer; Antialiasing: boolean; SubOffsetX, SubOffsetY: single);
     942begin
     943  InitWithFontName(AText, AFontName, AFullHeight, [], Antialiasing, SubOffsetX, SubOffsetY);
     944end;
     945
     946constructor TBGRATextEffect.Create(AText: string; AFontName: string;
     947  AFullHeight: integer; AStyle: TFontStyles; Antialiasing: boolean);
     948begin
     949  InitWithFontName(AText, AFontName, AFullHeight, AStyle, Antialiasing, 0, 0);
     950end;
     951
     952constructor TBGRATextEffect.Create(AText: string; AFontName: string;
     953  AFullHeight: integer; AStyle: TFontStyles; Antialiasing: boolean; SubOffsetX,
     954  SubOffsetY: single);
     955begin
     956  InitWithFontName(AText, AFontName, AFullHeight, AStyle, Antialiasing, SubOffsetX, SubOffsetY);
     957end;
     958
     959constructor TBGRATextEffect.Create(AMask: TBGRACustomBitmap; AMaskOwner: boolean; AWidth,
     960  AHeight: integer; AOffset: TPoint);
     961begin
     962  FTextSize := Size(AWidth,AHeight);
     963  FOffset := AOffset;
     964  if not AMaskOwner then
     965    FTextMask := AMask.Duplicate()
     966  else
     967    FTextMask := AMask;
     968end;
     969
     970procedure TBGRATextEffect.Init(AText: string; Font: TFont; Antialiasing: boolean; SubOffsetX,SubOffsetY: single; GrainX, GrainY: Integer);
     971const FXAntialiasingLevel = FontAntialiasingLevel;
    575972var temp: TBGRACustomBitmap;
    576973    size: TSize;
     
    583980    iSubX,iSubY: integer;
    584981begin
     982  FShadowQuality := rbFast;
     983  if Antialiasing and Assigned(BGRATextOutImproveReadabilityProc) then
     984  begin
     985    InitImproveReadability(AText, Font, SubOffsetX,SubOffsetY);
     986    exit;
     987  end;
    585988  if Antialiasing then
    586989    quality := fqFineAntialiasing
    587990  else
    588991    quality := fqSystem;
    589   size := BGRAOriginalTextSize(Font,quality,AText,FontAntialiasingLevel);
     992  size := BGRAOriginalTextSize(Font,quality,AText,FXAntialiasingLevel);
    590993  if (size.cx = 0) or (size.cy = 0) then
    591994  begin
    592     size := BGRATextSize(Font,quality,'Hg',FontAntialiasingLevel);
    593     FWidth := 0;
    594     FHeight := size.cy;
     995    size := BGRATextSize(Font,quality,'Hg',FXAntialiasingLevel);
     996    FTextSize.cx := 0;
     997    FTextSize.cy := size.cy;
    595998    FOffset := Point(0,0);
    596999    exit;
    5971000  end;
     1001  FTextSize := size;
    5981002
    5991003  sizeX := size.cx+size.cy;
     
    6071011  if Antialiasing then
    6081012  begin
    609     sizeX := (sizeX + FontAntialiasingLevel-1);
    610     sizeX -= sizeX mod FontAntialiasingLevel;
    611 
    612     sizeY := (sizeY + FontAntialiasingLevel-1);
    613     sizeY -= sizeY mod FontAntialiasingLevel;
     1013    sizeX := (sizeX + FXAntialiasingLevel-1);
     1014    sizeX -= sizeX mod FXAntialiasingLevel;
     1015
     1016    sizeY := (sizeY + FXAntialiasingLevel-1);
     1017    sizeY -= sizeY mod FXAntialiasingLevel;
    6141018
    6151019    if SubOffsetX <> 0 then
    6161020    begin
    617       sizeX += ceil(SubOffsetX*FontAntialiasingLevel);
    618       iSubX := round(SubOffsetX*FontAntialiasingLevel);
     1021      sizeX += ceil(SubOffsetX*FXAntialiasingLevel);
     1022      iSubX := round(SubOffsetX*FXAntialiasingLevel);
    6191023    end;
    6201024    if SubOffsetY <> 0 then
    6211025    begin
    622       sizeY += ceil(SubOffsetY*FontAntialiasingLevel);
    623       iSubY := round(SubOffsetY*FontAntialiasingLevel);
    624     end;
    625 
    626     OnePixel := FontAntialiasingLevel;
     1026      sizeY += ceil(SubOffsetY*FXAntialiasingLevel);
     1027      iSubY := round(SubOffsetY*FXAntialiasingLevel);
     1028    end;
     1029
     1030    OnePixel := FXAntialiasingLevel;
    6271031  end else
    6281032  begin
     
    6621066  if Antialiasing then
    6631067  begin
    664     FWidth := round(size.cx/FontAntialiasingLevel);
    665     FHeight := round(size.cy/FontAntialiasingLevel);
    666     FOffset := Point(round(FOffset.X/FontAntialiasingLevel),round(FOffset.Y/FontAntialiasingLevel));
    667 
    668     FTextMask := temp.Resample(round(temp.width/FontAntialiasingLevel),round(temp.Height/FontAntialiasingLevel),rmSimpleStretch);
     1068    FTextSize.cx := round(FTextSize.cx/FXAntialiasingLevel);
     1069    FTextSize.cy := round(FTextSize.cy/FXAntialiasingLevel);
     1070    FOffset := Point(round(FOffset.X/FXAntialiasingLevel),round(FOffset.Y/FXAntialiasingLevel));
     1071
     1072    FTextMask := temp.Resample(round(temp.width/FXAntialiasingLevel),round(temp.Height/FXAntialiasingLevel),rmSimpleStretch);
    6691073
    6701074    maxAlpha := 0;
     
    6921096  else
    6931097  begin
    694     FWidth := size.cx;
    695     FHeight := size.cy;
    696 
    6971098    FTextMask := temp;
    6981099    p := FTextMask.data;
     
    7051106    end;
    7061107  end;
     1108end;
     1109
     1110procedure TBGRATextEffect.InitWithFontName(AText: string; AFontName: string;
     1111  AFullHeight: integer; AStyle: TFontStyles; Antialiasing: boolean; SubOffsetX, SubOffsetY: single);
     1112var lFont: TFont;
     1113begin
     1114  lFont := TFont.Create;
     1115  lFont.Name := AFontName;
     1116  lFont.Height := AFullHeight * FontFullHeightSign;
     1117  lFont.Style := AStyle;
     1118  Init(AText, lFont, Antialiasing, SubOffsetX, SubOffsetY, 0,0);
     1119  lFont.Free;
    7071120end;
    7081121
     
    7461159end;
    7471160
    748 procedure TBGRATextEffect.Draw(ADest: TBGRACustomBitmap; X, Y: integer;
    749   AColor: TBGRAPixel);
    750 begin
    751   if FTextMask = nil then exit;
    752   DrawMask(ADest,FTextMask,X+FOffset.X,Y+FOffset.Y,AColor);
    753 end;
    754 
    755 procedure TBGRATextEffect.Draw(ADest: TBGRACustomBitmap; X, Y: integer;
    756   ATexture: IBGRAScanner);
    757 begin
    758   if FTextMask = nil then exit;
    759   DrawMask(ADest,FTextMask,X+FOffset.X,Y+FOffset.Y,ATexture);
    760 end;
    761 
    762 procedure TBGRATextEffect.DrawMulticolored(ADest: TBGRACustomBitmap; X, Y: integer;
    763   const AColors: array of TBGRAPixel);
    764 begin
    765   if FTextMask = nil then exit;
    766   DrawMaskMulticolored(ADest,FTextMask,X+FOffset.X,Y+FOffset.Y,AColors);
    767 end;
    768 
    769 procedure TBGRATextEffect.DrawMulticolored(ADest: TBGRACustomBitmap; X,
    770   Y: integer; const AColors: array of TBGRAPixel; AAlign: TAlignment);
     1161function TBGRATextEffect.Draw(ADest: TBGRACustomBitmap; X, Y: integer;
     1162  AColor: TBGRAPixel): TRect;
     1163begin
     1164  result := DrawMask(ADest,FTextMask,X+FOffset.X,Y+FOffset.Y,AColor);
     1165end;
     1166
     1167function TBGRATextEffect.Draw(ADest: TBGRACustomBitmap; X, Y: integer;
     1168  ATexture: IBGRAScanner): TRect;
     1169begin
     1170  result := DrawMask(ADest,FTextMask,X+FOffset.X,Y+FOffset.Y,ATexture);
     1171end;
     1172
     1173function TBGRATextEffect.DrawMulticolored(ADest: TBGRACustomBitmap; X,
     1174  Y: integer; const AColors: array of TBGRAPixel): TRect;
     1175begin
     1176  result := DrawMaskMulticolored(ADest,FTextMask,X+FOffset.X,Y+FOffset.Y,AColors);
     1177end;
     1178
     1179function TBGRATextEffect.DrawMulticolored(ADest: TBGRACustomBitmap; X,
     1180  Y: integer; const AColors: array of TBGRAPixel; AAlign: TAlignment): TRect;
    7711181begin
    7721182  Case AAlign of
    773   taLeftJustify: DrawMulticolored(ADest,X,Y,AColors);
    774   taRightJustify: DrawMulticolored(ADest,X-Width,Y,AColors);
    775   taCenter: DrawMulticolored(ADest,X-Width div 2,Y,AColors);
    776   end;
    777 end;
    778 
    779 procedure TBGRATextEffect.DrawOutline(ADest: TBGRACustomBitmap; X, Y: integer;
    780   AColor: TBGRAPixel);
    781 begin
    782   if FTextMask = nil then exit;
     1183  taRightJustify: result := DrawMulticolored(ADest,X-TextSize.cx,Y,AColors);
     1184  taCenter: result := DrawMulticolored(ADest,X-TextSize.cx div 2,Y,AColors);
     1185  else result := DrawMulticolored(ADest,X,Y,AColors);
     1186  end;
     1187end;
     1188
     1189function TBGRATextEffect.DrawOutline(ADest: TBGRACustomBitmap; X, Y: integer;
     1190  AColor: TBGRAPixel): TRect;
     1191begin
     1192  if (FTextMask = nil) or (FTextMask.Width = 0) or (FTextMask.Height = 0) then
     1193  begin
     1194    result := EmptyRect;
     1195    exit;
     1196  end;
    7831197  if FOutlineMask = nil then
    7841198  begin
     
    7861200    FOutlineMask.LinearNegative;
    7871201  end;
    788   DrawMask(ADest,FOutlineMask,X+FOffset.X,Y+FOffset.Y,AColor);
    789 end;
    790 
    791 procedure TBGRATextEffect.DrawOutline(ADest: TBGRACustomBitmap; X, Y: integer;
    792   ATexture: IBGRAScanner);
    793 begin
    794   if FTextMask = nil then exit;
     1202  result := DrawMask(ADest,FOutlineMask,X+FOffset.X,Y+FOffset.Y,AColor);
     1203end;
     1204
     1205function TBGRATextEffect.DrawOutline(ADest: TBGRACustomBitmap; X, Y: integer;
     1206  ATexture: IBGRAScanner): TRect;
     1207begin
     1208  if (FTextMask = nil) or (FTextMask.Width = 0) or (FTextMask.Height = 0) then
     1209  begin
     1210    result := EmptyRect;
     1211    exit;
     1212  end;
    7951213  if FOutlineMask = nil then
    7961214  begin
     
    7981216    FOutlineMask.LinearNegative;
    7991217  end;
    800   DrawMask(ADest,FOutlineMask,X+FOffset.X,Y+FOffset.Y,ATexture);
    801 end;
    802 
    803 procedure TBGRATextEffect.DrawOutline(ADest: TBGRACustomBitmap; X, Y: integer;
    804   AColor: TBGRAPixel; AAlign: TAlignment);
     1218  result := DrawMask(ADest,FOutlineMask,X+FOffset.X,Y+FOffset.Y,ATexture);
     1219end;
     1220
     1221function TBGRATextEffect.DrawOutline(ADest: TBGRACustomBitmap; X, Y: integer;
     1222  AColor: TBGRAPixel; AAlign: TAlignment): TRect;
    8051223begin
    8061224  Case AAlign of
    807   taLeftJustify: DrawOutline(ADest,X,Y,AColor);
    808   taRightJustify: DrawOutline(ADest,X-Width,Y,AColor);
    809   taCenter: DrawOutline(ADest,X-Width div 2,Y,AColor);
    810   end;
    811 end;
    812 
    813 procedure TBGRATextEffect.DrawOutline(ADest: TBGRACustomBitmap; X, Y: integer;
    814   ATexture: IBGRAScanner; AAlign: TAlignment);
     1225  taRightJustify: result := DrawOutline(ADest,X-TextSize.cx,Y,AColor);
     1226  taCenter: result := DrawOutline(ADest,X-TextSize.cx div 2,Y,AColor);
     1227  else result := DrawOutline(ADest,X,Y,AColor);
     1228  end;
     1229end;
     1230
     1231function TBGRATextEffect.DrawOutline(ADest: TBGRACustomBitmap; X, Y: integer;
     1232  ATexture: IBGRAScanner; AAlign: TAlignment): TRect;
    8151233begin
    8161234  Case AAlign of
    817   taLeftJustify: DrawOutline(ADest,X,Y,ATexture);
    818   taRightJustify: DrawOutline(ADest,X-Width,Y,ATexture);
    819   taCenter: DrawOutline(ADest,X-Width div 2,Y,ATexture);
    820   end;
    821 end;
    822 
    823 procedure TBGRATextEffect.DrawShadow(ADest: TBGRACustomBitmap; X, Y,Radius: integer;
    824   AColor: TBGRAPixel);
    825 begin
    826   if Radius <= 0 then
    827   begin
    828     Draw(ADest,X,Y,AColor);
     1235  taRightJustify: result := DrawOutline(ADest,X-TextSize.cx,Y,ATexture);
     1236  taCenter: result := DrawOutline(ADest,X-TextSize.cx div 2,Y,ATexture);
     1237  else result := DrawOutline(ADest,X,Y,ATexture);
     1238  end;
     1239end;
     1240
     1241function TBGRATextEffect.DrawShadow(ADest: TBGRACustomBitmap; X, Y,
     1242  Radius: integer; AColor: TBGRAPixel): TRect;
     1243begin
     1244  if (Radius <= 0) or (FTextMask = nil) or (FTextMask.Width = 0) or (FTextMask.Height = 0) then
     1245  begin
     1246    result := Draw(ADest,X,Y,AColor);
    8291247    exit;
    8301248  end;
    831   if FTextMask = nil then exit;
    832   if FShadowRadius <> Radius then
     1249  if (FShadowRadius <> Radius) or (FShadowMask = nil) then
    8331250  begin
    8341251    FShadowRadius := Radius;
     
    8361253    FShadowMask := BGRABitmapFactory.Create(FTextMask.Width+Radius*2,FTextMask.Height+Radius*2,BGRABlack);
    8371254    FShadowMask.PutImage(Radius,Radius,FTextMask,dmSet);
    838     BGRAReplace(FShadowMask, FShadowMask.FilterBlurRadial(Radius,rbFast));
    839   end;
    840   DrawMask(ADest,FShadowMask,X-Radius+FOffset.X,Y-Radius+FOffset.Y,AColor)
    841 end;
    842 
    843 procedure TBGRATextEffect.DrawShadow(ADest: TBGRACustomBitmap; X, Y,
    844   Radius: integer; AColor: TBGRAPixel; AAlign: TAlignment);
     1255    BGRAReplace(FShadowMask, FShadowMask.FilterBlurRadial(Radius,ShadowQuality));
     1256  end;
     1257  Inc(X,FOffset.X-Radius);
     1258  Inc(Y,FOffset.Y-Radius);
     1259  DrawMask(ADest,FShadowMask,X,Y,AColor);
     1260  result := rect(X,Y,X+FShadowMask.Width,Y+FShadowMask.Height);
     1261end;
     1262
     1263function TBGRATextEffect.DrawShadow(ADest: TBGRACustomBitmap; X, Y,
     1264  Radius: integer; AColor: TBGRAPixel; AAlign: TAlignment): TRect;
    8451265begin
    8461266  Case AAlign of
    847   taLeftJustify: DrawShadow(ADest,X,Y,Radius,AColor);
    848   taRightJustify: DrawShadow(ADest,X-Width,Y,Radius,AColor);
    849   taCenter: DrawShadow(ADest,X-Width div 2,Y,Radius,AColor);
     1267  taRightJustify: result := DrawShadow(ADest,X-TextSize.cx,Y,Radius,AColor);
     1268  taCenter: result := DrawShadow(ADest,X-TextSize.cx div 2,Y,Radius,AColor);
     1269  else result := DrawShadow(ADest,X,Y,Radius,AColor);
    8501270  end;
    8511271end;
     
    8601280end;
    8611281
     1282initialization
     1283
     1284  BGRATextOutImproveReadabilityProc := @BGRATextOutImproveReadability;
     1285
    8621286end.
    8631287
  • GraphicTest/Packages/bgrabitmap/bgratransform.pas

    r452 r472  
    2424    TopLeft, TopRight,
    2525    BottomLeft: TPointF;
    26     function EmptyBox: TAffineBox;
    27     function AffineBox(ATopLeft, ATopRight, ABottomLeft: TPointF): TAffineBox;
     26    class function EmptyBox: TAffineBox;
     27    class function AffineBox(ATopLeft, ATopRight, ABottomLeft: TPointF): TAffineBox;
    2828    property BottomRight: TPointF read GetBottomRight;
    2929    property IsEmpty: boolean read GetIsEmpty;
     
    5555    procedure Invert;
    5656    procedure Translate(OfsX,OfsY: Single);
    57     procedure RotateDeg(Angle: Single);
    58     procedure RotateRad(Angle: Single);
     57    procedure RotateDeg(AngleCW: Single);
     58    procedure RotateRad(AngleCCW: Single);
    5959    procedure MultiplyBy(AMatrix: TAffineMatrix);
    6060    procedure Fit(Origin,HAxis,VAxis: TPointF); virtual;
     
    7979    FRepeatImageX,FRepeatImageY: boolean;
    8080    FResampleFilter : TResampleFilter;
     81    FBuffer: PBGRAPixel;
     82    FBufferSize: Int32or64;
    8183    procedure Init(ABitmap: TBGRACustomBitmap; ARepeatImageX: Boolean= false; ARepeatImageY: Boolean= false; AResampleFilter: TResampleFilter = rfLinear);
    8284  public
    8385    constructor Create(ABitmap: TBGRACustomBitmap; ARepeatImage: Boolean= false; AResampleFilter: TResampleFilter = rfLinear);
    8486    constructor Create(ABitmap: TBGRACustomBitmap; ARepeatImageX: Boolean; ARepeatImageY: Boolean; AResampleFilter: TResampleFilter = rfLinear);
     87    destructor Destroy; override;
    8588    function InternalScanCurrentPixel: TBGRAPixel; override;
     89    procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); override;
     90    function IsScanPutPixelsDefined: boolean; override;
    8691    procedure Fit(Origin, HAxis, VAxis: TPointF); override;
    8792  end;
     
    103108  end;
    104109
     110  { TBGRAExtendedBorderScanner }
     111
     112  TBGRAExtendedBorderScanner = class(TBGRACustomScanner)
     113  protected
     114    FSource: IBGRAScanner;
     115    FBounds: TRect;
     116  public
     117    constructor Create(ASource: IBGRAScanner; ABounds: TRect);
     118    function ScanAt(X,Y: Single): TBGRAPixel; override;
     119  end;
     120
    105121  { TBGRAScannerOffset }
    106122
     
    133149function IsAffineMatrixInversible(M: TAffineMatrix): boolean;
    134150
     151//check if the matrix is a translation (including the identity)
     152function IsAffineMatrixTranslation(M: TAffineMatrix): boolean;
     153
     154//check if the matrix is a scaling (including a projection i.e. with factor 0)
     155function IsAffineMatrixScale(M: TAffineMatrix): boolean;
     156
     157//check if the matrix is the identity
     158function IsAffineMatrixIdentity(M: TAffineMatrix): boolean;
     159
    135160//compute inverse (check if inversible before)
    136161function AffineMatrixInverse(M: TAffineMatrix): TAffineMatrix;
     
    145170function AffineMatrixLinear(v1,v2: TPointF): TAffineMatrix;
    146171
    147 //define a rotation matrix (positive radians are counter clock wise)
    148 function AffineMatrixRotationRad(Angle: Single): TAffineMatrix;
    149 
    150 //Positive degrees are clock wise
    151 function AffineMatrixRotationDeg(Angle: Single): TAffineMatrix;
     172//define a rotation matrix (positive radians are counter-clockwise)
     173//(assuming the y-axis is pointing down)
     174function AffineMatrixRotationRad(AngleCCW: Single): TAffineMatrix;
     175
     176//Positive degrees are clockwise
     177//(assuming the y-axis is pointing down)
     178function AffineMatrixRotationDeg(AngleCW: Single): TAffineMatrix;
    152179
    153180//define the identity matrix (that do nothing)
     
    188215    FMatrix: TPerspectiveTransform;
    189216    FScanAtProc: TScanAtFunction;
     217    function GetIncludeOppositePlane: boolean;
     218    procedure SetIncludeOppositePlane(AValue: boolean);
    190219  public
    191220    constructor Create(texture: IBGRAScanner; texCoord1,texCoord2: TPointF; const quad: array of TPointF);
     
    195224    function ScanAt(X, Y: Single): TBGRAPixel; override;
    196225    function ScanNextPixel: TBGRAPixel; override;
     226    property IncludeOppositePlane: boolean read GetIncludeOppositePlane write SetIncludeOppositePlane;
    197227  end;
    198228
     
    203233    sx ,shy ,w0 ,shx ,sy ,w1 ,tx ,ty ,w2 : single;
    204234    scanDenom,scanNumX,scanNumY: single;
     235    FOutsideValue: TPointF;
     236    FIncludeOppositePlane: boolean;
     237    procedure Init;
    205238  public
    206239    constructor Create; overload;
     
    222255    procedure ScanMoveTo(x,y:single);
    223256    function ScanNext: TPointF;
     257    property OutsideValue: TPointF read FOutsideValue write FOutsideValue;
     258    property IncludeOppositePlane: boolean read FIncludeOppositePlane write FIncludeOppositePlane;
    224259  end;
    225260
     
    249284implementation
    250285
    251 uses BGRABlend;
     286uses BGRABlend, GraphType;
    252287
    253288function AffineMatrix(m11, m12, m13, m21, m22, m23: single): TAffineMatrix;
     
    281316begin
    282317  result := M[1,1]*M[2,2]-M[1,2]*M[2,1] <> 0;
     318end;
     319
     320function IsAffineMatrixTranslation(M: TAffineMatrix): boolean;
     321begin
     322  result := (m[1,1]=1) and (m[1,2]=0) and (m[2,1] = 1) and (m[2,2]=0);
     323end;
     324
     325function IsAffineMatrixScale(M: TAffineMatrix): boolean;
     326begin
     327  result := (M[1,3]=0) and (M[2,3]=0) and
     328            (M[1,2]=0) and (M[2,1]=0);
     329end;
     330
     331function IsAffineMatrixIdentity(M: TAffineMatrix): boolean;
     332begin
     333  result := IsAffineMatrixTranslation(M) and (M[1,3]=0) and (M[2,3]=0);
    283334end;
    284335
     
    314365end;
    315366
    316 function AffineMatrixRotationRad(Angle: Single): TAffineMatrix;
    317 begin
    318   result := AffineMatrix(cos(Angle),  sin(Angle), 0,
    319                          -sin(Angle), cos(Angle), 0);
    320 end;
    321 
    322 function AffineMatrixRotationDeg(Angle: Single): TAffineMatrix;
    323 begin
    324   result := AffineMatrixRotationRad(-Angle*Pi/180);
     367function AffineMatrixRotationRad(AngleCCW: Single): TAffineMatrix;
     368begin
     369  result := AffineMatrix(cos(AngleCCW),  sin(AngleCCW), 0,
     370                         -sin(AngleCCW), cos(AngleCCW), 0);
     371end;
     372
     373function AffineMatrixRotationDeg(AngleCW: Single): TAffineMatrix;
     374const DegToRad = -Pi/180;
     375begin
     376  result := AffineMatrixRotationRad(AngleCW*DegToRad);
    325377end;
    326378
     
    334386begin
    335387  result := PointF(M[1,1],M[2,1])*PointF(M[1,2],M[2,2]) = 0;
     388end;
     389
     390{ TBGRAExtendedBorderScanner }
     391
     392constructor TBGRAExtendedBorderScanner.Create(ASource: IBGRAScanner;
     393  ABounds: TRect);
     394begin
     395  FSource := ASource;
     396  FBounds := ABounds;
     397end;
     398
     399function TBGRAExtendedBorderScanner.ScanAt(X, Y: Single): TBGRAPixel;
     400begin
     401  if x < FBounds.Left then x := FBounds.Left;
     402  if y < FBounds.Top then y := FBounds.Top;
     403  if x > FBounds.Right-1 then x := FBounds.Right-1;
     404  if y > FBounds.Bottom-1 then y := FBounds.Bottom-1;
     405  result := FSource.ScanAt(X,Y);
    336406end;
    337407
     
    356426end;
    357427
    358 function TAffineBox.EmptyBox: TAffineBox;
     428class function TAffineBox.EmptyBox: TAffineBox;
    359429begin
    360430  result.TopLeft := EmptyPointF;
     
    363433end;
    364434
    365 function TAffineBox.AffineBox(ATopLeft, ATopRight, ABottomLeft: TPointF): TAffineBox;
     435class function TAffineBox.AffineBox(ATopLeft, ATopRight, ABottomLeft: TPointF): TAffineBox;
    366436begin
    367437  result.TopLeft := ATopLeft;
     
    556626end;
    557627
     628//transformations are inverted because the effect on the resulting image
     629//is the inverse of the transformation. This is due to the fact
     630//that the matrix is applied to source coordinates, not destination coordinates
    558631procedure TBGRAAffineScannerTransform.Translate(OfsX, OfsY: Single);
    559632begin
     
    561634end;
    562635
    563 procedure TBGRAAffineScannerTransform.RotateDeg(Angle: Single);
    564 begin
    565   MultiplyBy(AffineMatrixRotationDeg(-Angle));
    566 end;
    567 
    568 procedure TBGRAAffineScannerTransform.RotateRad(Angle: Single);
    569 begin
    570   MultiplyBy(AffineMatrixRotationRad(-Angle));
     636procedure TBGRAAffineScannerTransform.RotateDeg(AngleCW: Single);
     637begin
     638  MultiplyBy(AffineMatrixRotationDeg(-AngleCW));
     639end;
     640
     641procedure TBGRAAffineScannerTransform.RotateRad(AngleCCW: Single);
     642begin
     643  MultiplyBy(AffineMatrixRotationRad(-AngleCCW));
    571644end;
    572645
     
    651724  FRepeatImageY := ARepeatImageY;
    652725  FResampleFilter:= AResampleFilter;
     726  FBufferSize:= 0;
    653727end;
    654728
     
    666740end;
    667741
     742destructor TBGRAAffineBitmapTransform.Destroy;
     743begin
     744  FreeMem(FBuffer);
     745end;
     746
    668747function TBGRAAffineBitmapTransform.InternalScanCurrentPixel: TBGRAPixel;
    669748begin
    670   if FRepeatImageX or FRepeatImageY then
    671     result := FBitmap.GetPixelCycle(FCurX,FCurY,FResampleFilter,FRepeatImageX,FRepeatImageY)
     749  result := FBitmap.GetPixelCycle(FCurX,FCurY,FResampleFilter,FRepeatImageX,FRepeatImageY);
     750end;
     751
     752procedure TBGRAAffineBitmapTransform.ScanPutPixels(pdest: PBGRAPixel;
     753  count: integer; mode: TDrawMode);
     754var p: PBGRAPixel;
     755  n: integer;
     756  posX4096, posY4096: Int32or64;
     757  deltaX4096,deltaY4096: Int32or64;
     758  ix,iy,shrMask,w,h: Int32or64;
     759  py0: PByte;
     760  deltaRow: Int32or64;
     761begin
     762  w := FBitmap.Width;
     763  h := FBitmap.Height;
     764  if (w = 0) or (h = 0) then exit;
     765
     766  posX4096 := round(FCurX*4096);
     767  deltaX4096:= round(FMatrix[1,1]*4096);
     768  posY4096 := round(FCurY*4096);
     769  deltaY4096:= round(FMatrix[2,1]*4096);
     770  shrMask := -1;
     771  shrMask := shrMask shr 12;
     772  shrMask := not shrMask;
     773
     774  if mode = dmSet then
     775    p := pdest
    672776  else
    673     result := FBitmap.GetPixel(FCurX,FCurY,FResampleFilter);
     777  begin
     778    if count > FBufferSize then
     779    begin
     780      FBufferSize := count;
     781      ReAllocMem(FBuffer, FBufferSize*sizeof(TBGRAPixel));
     782    end;
     783    p := FBuffer;
     784  end;
     785
     786  if FResampleFilter = rfBox then
     787  begin
     788    posX4096 += 2048;
     789    posY4096 += 2048;
     790    py0 := PByte(FBitmap.ScanLine[0]);
     791    if FBitmap.LineOrder = riloTopToBottom then
     792      deltaRow := FBitmap.Width*sizeof(TBGRAPixel) else
     793      deltaRow := -FBitmap.Width*sizeof(TBGRAPixel);
     794    if FRepeatImageX or FRepeatImageY then
     795    begin
     796      for n := count-1 downto 0 do
     797      begin
     798        if posX4096 < 0 then ix := (posX4096 shr 12) or shrMask else ix := posX4096 shr 12;
     799        if posY4096 < 0 then iy := (posY4096 shr 12) or shrMask else iy := posY4096 shr 12;
     800        if FRepeatImageX then ix := PositiveMod(ix,w);
     801        if FRepeatImageY then iy := PositiveMod(iy,h);
     802        if (ix < 0) or (iy < 0) or (ix >= w) or (iy >= h) then
     803          p^ := BGRAPixelTransparent
     804        else
     805          p^ := (PBGRAPixel(py0 + iy*deltaRow)+ix)^;
     806        inc(p);
     807        posX4096 += deltaX4096;
     808        posY4096 += deltaY4096;
     809      end;
     810    end else
     811    begin
     812     for n := count-1 downto 0 do
     813     begin
     814       if posX4096 < 0 then ix := (posX4096 shr 12) or shrMask else ix := posX4096 shr 12;
     815       if posY4096 < 0 then iy := (posY4096 shr 12) or shrMask else iy := posY4096 shr 12;
     816       if (ix < 0) or (iy < 0) or (ix >= w) or (iy >= h) then
     817         p^ := BGRAPixelTransparent
     818       else
     819         p^ := (PBGRAPixel(py0 + iy*deltaRow)+ix)^;
     820       inc(p);
     821       posX4096 += deltaX4096;
     822       posY4096 += deltaY4096;
     823     end;
     824    end;
     825  end else
     826  begin
     827   if FRepeatImageX and FRepeatImageY then
     828   begin
     829     for n := count-1 downto 0 do
     830     begin
     831       if posX4096 < 0 then ix := (posX4096 shr 12) or shrMask else ix := posX4096 shr 12;
     832       if posY4096 < 0 then iy := (posY4096 shr 12) or shrMask else iy := posY4096 shr 12;
     833       p^ := FBitmap.GetPixelCycle256(ix,iy, (posX4096 shr 4) and 255, (posY4096 shr 4) and 255,FResampleFilter);
     834       inc(p);
     835       posX4096 += deltaX4096;
     836       posY4096 += deltaY4096;
     837     end;
     838   end else
     839   if FRepeatImageX or FRepeatImageY then
     840   begin
     841     for n := count-1 downto 0 do
     842     begin
     843       if posX4096 < 0 then ix := (posX4096 shr 12) or shrMask else ix := posX4096 shr 12;
     844       if posY4096 < 0 then iy := (posY4096 shr 12) or shrMask else iy := posY4096 shr 12;
     845       p^ := FBitmap.GetPixelCycle256(ix,iy, (posX4096 shr 4) and 255, (posY4096 shr 4) and 255,FResampleFilter, FRepeatImageX,FRepeatImageY);
     846       inc(p);
     847       posX4096 += deltaX4096;
     848       posY4096 += deltaY4096;
     849     end;
     850   end else
     851   begin
     852    for n := count-1 downto 0 do
     853    begin
     854      if posX4096 < 0 then ix := (posX4096 shr 12) or shrMask else ix := posX4096 shr 12;
     855      if posY4096 < 0 then iy := (posY4096 shr 12) or shrMask else iy := posY4096 shr 12;
     856      p^ := FBitmap.GetPixel256(ix,iy, (posX4096 shr 4) and 255, (posY4096 shr 4) and 255,FResampleFilter);
     857      inc(p);
     858      posX4096 += deltaX4096;
     859      posY4096 += deltaY4096;
     860    end;
     861   end;
     862  end;
     863
     864  if mode <> dmSet then PutPixels(pdest,FBuffer,count,mode,255);
     865end;
     866
     867function TBGRAAffineBitmapTransform.IsScanPutPixelsDefined: boolean;
     868begin
     869  Result:=true;
    674870end;
    675871
     
    684880{ TBGRAPerspectiveScannerTransform }
    685881
     882function TBGRAPerspectiveScannerTransform.GetIncludeOppositePlane: boolean;
     883begin
     884  if FMatrix = nil then
     885    result := false
     886  else
     887    result := FMatrix.IncludeOppositePlane;
     888end;
     889
     890procedure TBGRAPerspectiveScannerTransform.SetIncludeOppositePlane(
     891  AValue: boolean);
     892begin
     893  if FMatrix <> nil then
     894    FMatrix.IncludeOppositePlane := AValue;
     895end;
     896
    686897constructor TBGRAPerspectiveScannerTransform.Create(texture: IBGRAScanner; texCoord1,texCoord2: TPointF; const quad: array of TPointF);
    687898begin
     
    689900    FMatrix := nil
    690901  else
     902  begin
    691903    FMatrix := TPerspectiveTransform.Create(quad,texCoord1.x,texCoord1.y,texCoord2.x,texCoord2.y);
     904    FMatrix.OutsideValue := EmptyPointF;
     905  end;
    692906  FTexture := texture;
    693907  FScanAtProc:= @FTexture.ScanAt;
     
    701915    FMatrix := nil
    702916  else
     917  begin
    703918    FMatrix := TPerspectiveTransform.Create(quad,texCoordsQuad);
     919    FMatrix.OutsideValue := EmptyPointF;
     920  end;
    704921  FTexture := texture;
    705922  FScanAtProc:= @FTexture.ScanAt;
     
    725942  begin
    726943    ptSource := FMatrix.Apply(PointF(X,Y));
    727     Result:= FScanAtProc(ptSource.X, ptSource.Y);
     944    if ptSource.x = EmptySingle then
     945      result := BGRAPixelTransparent
     946    else
     947      Result:= FScanAtProc(ptSource.X, ptSource.Y);
    728948  end;
    729949end;
     
    736956  begin
    737957    ptSource := FMatrix.ScanNext;
    738     Result:= FScanAtProc(ptSource.X, ptSource.Y);
     958    if ptSource.x = EmptySingle then
     959      result := BGRAPixelTransparent
     960    else
     961      Result:= FScanAtProc(ptSource.X, ptSource.Y);
    739962  end;
    740963end;
     
    742965{ TPerspectiveTransform }
    743966
     967procedure TPerspectiveTransform.Init;
     968begin
     969  FOutsideValue := PointF(0,0);
     970  FIncludeOppositePlane:= True;
     971end;
     972
    744973constructor TPerspectiveTransform.Create;
    745974begin
     975  Init;
    746976  AssignIdentity;
    747977end;
     
    750980  const quad: array of TPointF);
    751981begin
     982  Init;
    752983  MapRectToQuad(x1 ,y1 ,x2 ,y2 ,quad );
    753984end;
     
    756987  x2, y2: single);
    757988begin
     989  Init;
    758990  MapQuadToRect(quad, x1,y1,x2,y2);
    759991end;
     
    762994  destQuad: array of TPointF);
    763995begin
     996  Init;
    764997  MapQuadToQuad(srcQuad,destQuad);
    765998end;
     
    9951228  m : single;
    9961229begin
    997   m:= pt.x * w0 + pt.y * w1 + w2 ;
    998   if m=0 then
    999   begin
    1000     result.x := 0;
    1001     result.y := 0;
    1002   end else
     1230  m:= pt.x * w0 + pt.y * w1 + w2;
     1231  if (m=0) or (not FIncludeOppositePlane and (m < 0)) then
     1232    result := FOutsideValue
     1233  else
    10031234  begin
    10041235   m := 1/m;
     
    10181249var m: single;
    10191250begin
    1020   if ScanDenom = 0 then
    1021   begin
    1022     result.x := 0;
    1023     result.y := 0;
    1024   end else
     1251  if (ScanDenom = 0) or (not FIncludeOppositePlane and (ScanDenom < 0)) then
     1252    result := FOutsideValue
     1253  else
    10251254  begin
    10261255   m := 1/scanDenom;
  • GraphicTest/Packages/bgrabitmap/bgratypewriter.pas

    r452 r472  
    1919  protected
    2020    FIdentifier: string;
     21    procedure WriteHeader(AStream: TStream; AName: string; AContentSize: longint);
     22    class procedure ReadHeader(AStream: TStream; out AName: string; out AContentSize: longint);
     23    function ContentSize: integer; virtual;
     24    function HeaderName: string; virtual;
     25    procedure WriteContent(AStream: TStream); virtual;
     26    procedure ReadContent(AStream: TStream); virtual;
    2127  public
    2228    Width,Height: single;
    2329    constructor Create(AIdentifier: string); virtual;
    24     procedure Path(ADest: TBGRACanvas2D; AMatrix: TAffineMatrix); virtual; abstract;
     30    constructor Create(AStream: TStream); virtual;
     31    procedure Path({%H-}ADest: IBGRAPath; {%H-}AMatrix: TAffineMatrix); virtual;
    2532    property Identifier: string read FIdentifier;
    26   end;
     33    procedure SaveToStream(AStream: TStream);
     34    class function LoadFromStream(AStream: TStream): TBGRAGlyph;
     35  end;
     36
     37  TGlyphPointCurveMode= (cmAuto, cmCurve, cmAngle);
    2738
    2839  { TBGRAPolygonalGlyph }
     
    3445    FQuadraticCurves: boolean;
    3546    Points: array of TPointF;
     47    CurveMode: array of TGlyphPointCurveMode;
    3648    Curves: array of record
    3749      isCurvedToNext,isCurvedToPrevious: boolean;
     
    4052    function MaybeCurve(start1,end1,start2,end2: integer): boolean;
    4153    procedure ComputeQuadraticCurves;
     54    function ContentSize: integer; override;
     55    function HeaderName: string; override;
     56    procedure WriteContent(AStream: TStream); override;
     57    procedure ReadContent(AStream: TStream); override;
     58    procedure Init;
    4259  public
    4360    Offset: TPointF;
     61    Closed: boolean;
     62    MinimumDotProduct: single;
    4463    constructor Create(AIdentifier: string); override;
    45     procedure SetPoints(const APoints: array of TPointF);
    46     procedure Path(ADest: TBGRACanvas2D; AMatrix: TAffineMatrix); override;
     64    constructor Create(AStream: TStream); override;
     65    procedure SetPoints(const APoints: array of TPointF); overload;
     66    procedure SetPoints(const APoints: array of TPointF; const ACurveMode: array of TGlyphPointCurveMode); overload;
     67    procedure Path(ADest: IBGRAPath; AMatrix: TAffineMatrix); override;
    4768    property QuadraticCurves: boolean read FQuadraticCurves write SetQuadraticCurves;
     69  end;
     70
     71  TBGRACustomTypeWriterHeader = record
     72    HeaderName: String;
     73    NbGlyphs: integer;
    4874  end;
    4975
     
    5985    function GetGlyph(AIdentifier: string): TBGRAGlyph; virtual;
    6086    procedure SetGlyph(AIdentifier: string; AValue: TBGRAGlyph);
    61     procedure TextPath(ADest: TBGRACanvas2D; AText: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft);
     87    procedure TextPath(ADest: TBGRACanvas2D; ATextUTF8: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment; ADrawEachChar: boolean);
    6288    procedure GlyphPath(ADest: TBGRACanvas2D; AIdentifier: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft);
    6389    procedure DrawLastPath(ADest: TBGRACanvas2D);
     
    6692    procedure AddGlyph(AGlyph: TBGRAGlyph);
    6793    function GetGlyphMatrix(AGlyph: TBGRAGlyph; X,Y: Single; AAlign: TBGRATypeWriterAlignment): TAffineMatrix;
    68     function GetTextMatrix(AText: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment): TAffineMatrix;
     94    function GetTextMatrix(ATextUTF8: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment): TAffineMatrix;
    6995    property Glyph[AIdentifier: string]: TBGRAGlyph read GetGlyph write SetGlyph;
     96    function CustomHeaderSize: integer; virtual;
     97    procedure WriteCustomHeader(AStream: TStream); virtual;
     98    function ReadCustomTypeWriterHeader(AStream: TStream): TBGRACustomTypeWriterHeader;
     99    procedure ReadAdditionalHeader({%H-}AStream: TStream); virtual;
     100    function HeaderName: string; virtual;
    70101  public
    71102    OutlineMode: TBGRATypeWriterOutlineMode;
     103    DrawGlyphsSimultaneously : boolean;
    72104    constructor Create;
     105    procedure SaveGlyphsToFile(AFilenameUTF8: string);
     106    procedure SaveGlyphsToStream(AStream: TStream);
     107    procedure LoadGlyphsFromFile(AFilenameUTF8: string);
     108    procedure LoadGlyphsFromStream(AStream: TStream);
    73109    procedure DrawGlyph(ADest: TBGRACanvas2D; AIdentifier: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft);
    74     procedure DrawText(ADest: TBGRACanvas2D; AText: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft);
     110    procedure DrawText(ADest: TBGRACanvas2D; ATextUTF8: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft); virtual;
     111    procedure CopyTextPathTo(ADest: IBGRAPath; ATextUTF8: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft); virtual;
    75112    function GetGlyphBox(AIdentifier: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft): TAffineBox;
    76     function GetTextBox(AText: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft): TAffineBox;
    77     function GetTextGlyphBoxes(AText: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft): TGlyphBoxes;
     113    function GetTextBox(ATextUTF8: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft): TAffineBox;
     114    function GetTextGlyphBoxes(ATextUTF8: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft): TGlyphBoxes;
     115    procedure NeedGlyphRange(AUnicodeFrom, AUnicodeTo: Cardinal);
     116    procedure NeedGlyphAnsiRange;
    78117    destructor Destroy; override;
    79118  end;
    80119
     120function ComputeEasyBezier(APoints: array of TPointF; AClosed: boolean; AMinimumDotProduct: single = 0.707): ArrayOfTPointF; overload;
     121function ComputeEasyBezier(APoints: array of TPointF; ACurveMode: array of TGlyphPointCurveMode; AClosed: boolean; AMinimumDotProduct: single = 0.707): ArrayOfTPointF; overload;
     122
    81123implementation
    82124
    83 uses LCLProc;
     125uses LCLProc, lazutf8classes;
     126
     127{$i winstream.inc}
     128
     129function ComputeEasyBezier(APoints: array of TPointF; AClosed: boolean; AMinimumDotProduct: single = 0.707): ArrayOfTPointF;
     130var
     131  glyph: TBGRAPolygonalGlyph;
     132  canvas2D: TBGRACanvas2D;
     133  i: integer;
     134begin
     135  if length(APoints) <= 2 then
     136  begin
     137    setlength(result, length(APoints));
     138    for i := 0 to high(result) do
     139      result[i] := APoints[i];
     140    exit;
     141  end;
     142  glyph := TBGRAPolygonalGlyph.Create('');
     143  glyph.QuadraticCurves := true;
     144  glyph.Closed:= AClosed;
     145  glyph.MinimumDotProduct := AMinimumDotProduct;
     146  glyph.SetPoints(APoints);
     147  canvas2D := TBGRACanvas2D.Create(nil);
     148  canvas2D.pixelCenteredCoordinates := true;
     149  glyph.Path(canvas2D,AffineMatrixIdentity);
     150  glyph.Free;
     151  result := canvas2D.currentPath;
     152  canvas2D.free;
     153end;
     154
     155function ComputeEasyBezier(APoints: array of TPointF;
     156  ACurveMode: array of TGlyphPointCurveMode; AClosed: boolean;
     157  AMinimumDotProduct: single): ArrayOfTPointF;
     158var
     159  glyph: TBGRAPolygonalGlyph;
     160  canvas2D: TBGRACanvas2D;
     161  i: integer;
     162begin
     163  if length(APoints) <= 2 then
     164  begin
     165    setlength(result, length(APoints));
     166    for i := 0 to high(result) do
     167      result[i] := APoints[i];
     168    exit;
     169  end;
     170  glyph := TBGRAPolygonalGlyph.Create('');
     171  glyph.QuadraticCurves := true;
     172  glyph.Closed:= AClosed;
     173  glyph.MinimumDotProduct := AMinimumDotProduct;
     174  glyph.SetPoints(APoints, ACurveMode);
     175  canvas2D := TBGRACanvas2D.Create(nil);
     176  canvas2D.pixelCenteredCoordinates := true;
     177  glyph.Path(canvas2D,AffineMatrixIdentity);
     178  glyph.Free;
     179  result := canvas2D.currentPath;
     180  canvas2D.free;
     181end;
    84182
    85183{ TBGRAPolygonalGlyph }
     
    109207  if lv <> 0 then v *= 1/lv;
    110208
    111   result := u*v > 0.707;
     209  result := u*v > MinimumDotProduct;
    112210end;
    113211
     
    119217  FirstPointIndex := 0;
    120218  for i := 0 to high(points) do
     219    Curves[i].isCurvedToPrevious := false;
     220  for i := 0 to high(points) do
    121221  begin
    122222    Curves[i].isCurvedToNext := false;
    123     Curves[i].isCurvedToPrevious := false;
    124223    Curves[i].Center := EmptyPointF;
    125224    Curves[i].ControlPoint := EmptyPointF;
     
    138237      Curves[i].Center := (points[i]+points[NextPt])*0.5;
    139238      Curves[i].NextCenter := (points[NextPt]+points[NextPt2])*0.5;
    140 
    141       Curves[i].isCurvedToNext:= MaybeCurve(i,NextPt,NextPt,NextPt2);
    142       Curves[NextPt].isCurvedToPrevious := Curves[i].isCurvedToNext;
    143239      Curves[i].ControlPoint := points[NextPt];
     240
     241      if (i < high(points)-1) or Closed then
     242      begin
     243        case CurveMode[nextPt] of
     244          cmAuto: Curves[i].isCurvedToNext:= MaybeCurve(i,NextPt,NextPt,NextPt2);
     245          cmCurve: Curves[i].isCurvedToNext:= true;
     246          else Curves[i].isCurvedToNext:= false;
     247        end;
     248        Curves[NextPt].isCurvedToPrevious := Curves[i].isCurvedToNext;
     249      end;
    144250    end;
    145251  end;
     252end;
     253
     254function TBGRAPolygonalGlyph.ContentSize: integer;
     255begin
     256  Result:= (inherited ContentSize) + sizeof(single)*2 + 4 + sizeof(single)*2*length(Points);
     257end;
     258
     259function TBGRAPolygonalGlyph.HeaderName: string;
     260begin
     261  Result:='TBGRAPolygonalGlyph';
     262end;
     263
     264procedure TBGRAPolygonalGlyph.WriteContent(AStream: TStream);
     265var i: integer;
     266begin
     267  inherited WriteContent(AStream);
     268  WinWritePointF(AStream, Offset);
     269  WinWriteLongint(AStream,length(Points));
     270  for i := 0 to high(Points) do
     271    WinWritePointF(AStream, Points[i]);
     272end;
     273
     274procedure TBGRAPolygonalGlyph.ReadContent(AStream: TStream);
     275var i: integer;
     276  tempPts: array of TPointF;
     277begin
     278  inherited ReadContent(AStream);
     279  Offset := WinReadPointF(AStream);
     280  SetLength(tempPts, WinReadLongint(AStream));
     281  for i := 0 to high(tempPts) do
     282    tempPts[i] := WinReadPointF(AStream);
     283  SetPoints(tempPts);
     284end;
     285
     286procedure TBGRAPolygonalGlyph.Init;
     287begin
     288  Closed := True;
     289  MinimumDotProduct := 0.707;
    146290end;
    147291
     
    150294  inherited Create(AIdentifier);
    151295  Offset := PointF(0,0);
     296  Init;
     297end;
     298
     299constructor TBGRAPolygonalGlyph.Create(AStream: TStream);
     300begin
     301  inherited Create(AStream);
     302  Init;
    152303end;
    153304
     
    158309  for i := 0 to high(points) do
    159310    points[i] := APoints[i];
     311  setlength(CurveMode, length(APoints));
     312  for i := 0 to high(CurveMode) do
     313    CurveMode[i] := cmAuto;
    160314  Curves := nil;
    161315end;
    162316
    163 procedure TBGRAPolygonalGlyph.Path(ADest: TBGRACanvas2D; AMatrix: TAffineMatrix);
     317procedure TBGRAPolygonalGlyph.SetPoints(const APoints: array of TPointF;
     318  const ACurveMode: array of TGlyphPointCurveMode);
     319var i: integer;
     320begin
     321  if length(APoints) <> length(ACurveMode) then
     322    raise exception.Create('Dimension mismatch');
     323  SetLength(Points,length(APoints));
     324  for i := 0 to high(points) do
     325    points[i] := APoints[i];
     326  setlength(CurveMode, length(ACurveMode));
     327  for i := 0 to high(CurveMode) do
     328    CurveMode[i] := ACurveMode[i];
     329  Curves := nil;
     330end;
     331
     332procedure TBGRAPolygonalGlyph.Path(ADest: IBGRAPath; AMatrix: TAffineMatrix);
    164333var i: integer;
    165334  nextMove: boolean;
    166335  startCoord: TPointF;
     336
    167337begin
    168338  if Points = nil then exit;
     
    171341  nextMove := true;
    172342  AMatrix := AMatrix*AffineMatrixTranslation(Offset.X,Offset.Y);
     343
    173344  for i := 0 to high(Points) do
    174345    if isEmptyPointF(Points[i]) then
     
    206377        nextMove := false;
    207378      end else
     379      begin
    208380        ADest.lineTo(AMatrix*Points[i]);
     381      end;
    209382    end;
    210   if not nextmove then ADest.closePath;
     383  if not nextmove then
     384    ADest.closePath;
    211385end;
    212386
    213387{ TBGRAGlyph }
    214388
     389procedure TBGRAGlyph.WriteHeader(AStream: TStream; AName: string;
     390  AContentSize: longint);
     391begin
     392  WinWriteByte(AStream, length(AName));
     393  AStream.Write(AName[1],length(AName));
     394  WinWriteLongint(AStream, AContentSize);
     395end;
     396
     397class procedure TBGRAGlyph.ReadHeader(AStream: TStream; out AName: string; out
     398  AContentSize: longint);
     399var NameLength: integer;
     400begin
     401  NameLength := WinReadByte(AStream);
     402  setlength(AName,NameLength);
     403  AStream.Read(AName[1],length(AName));
     404  AContentSize := WinReadLongint(AStream);
     405end;
     406
     407function TBGRAGlyph.ContentSize: integer;
     408begin
     409  result := 4+length(FIdentifier)+sizeof(single)*2;
     410end;
     411
     412function TBGRAGlyph.HeaderName: string;
     413begin
     414  result := 'TBGRAGlyph';
     415end;
     416
     417procedure TBGRAGlyph.WriteContent(AStream: TStream);
     418begin
     419  WinWriteLongint(AStream,length(FIdentifier));
     420  AStream.Write(FIdentifier[1],length(FIdentifier));
     421  WinWriteSingle(AStream,Width);
     422  WinWriteSingle(AStream,Height);
     423end;
     424
     425procedure TBGRAGlyph.ReadContent(AStream: TStream);
     426var lIdentifierLength: integer;
     427begin
     428  lIdentifierLength:= WinReadLongint(AStream);
     429  setlength(FIdentifier, lIdentifierLength);
     430  AStream.Read(FIdentifier[1],length(FIdentifier));
     431  Width := WinReadSingle(AStream);
     432  Height := WinReadSingle(AStream);
     433end;
     434
    215435constructor TBGRAGlyph.Create(AIdentifier: string);
    216436begin
    217437  FIdentifier:= AIdentifier;
     438end;
     439
     440constructor TBGRAGlyph.Create(AStream: TStream);
     441begin
     442  ReadContent(AStream);
     443end;
     444
     445procedure TBGRAGlyph.Path(ADest: IBGRAPath; AMatrix: TAffineMatrix);
     446begin
     447  //nothing
     448end;
     449
     450procedure TBGRAGlyph.SaveToStream(AStream: TStream);
     451begin
     452  WriteHeader(AStream, HeaderName, ContentSize);
     453  WriteContent(AStream);
     454end;
     455
     456class function TBGRAGlyph.LoadFromStream(AStream: TStream) : TBGRAGlyph;
     457var lName: string;
     458  lContentSize: integer;
     459  EndPosition: Int64;
     460begin
     461  ReadHeader(AStream,lName,lContentSize);
     462  EndPosition := AStream.Position + lContentSize;
     463  if lName = 'TBGRAPolygonalGlyph' then
     464    result := TBGRAPolygonalGlyph.Create(AStream)
     465  else if lName = 'TBGRAGlyph' then
     466    result := TBGRAGlyph.Create(AStream)
     467  else
     468    raise exception.Create('Unknown glyph type (' + lName + ')');
     469  AStream.Position:= EndPosition;
    218470end;
    219471
     
    272524  TypeWriterMatrix := AffineMatrixIdentity;
    273525  OutlineMode:= twoFill;
     526  DrawGlyphsSimultaneously := false;
    274527end;
    275528
     
    281534end;
    282535
    283 procedure TBGRACustomTypeWriter.DrawText(ADest: TBGRACanvas2D; AText: string;
     536procedure TBGRACustomTypeWriter.DrawText(ADest: TBGRACanvas2D; ATextUTF8: string;
    284537  X, Y: Single; AAlign: TBGRATypeWriterAlignment);
    285538begin
    286   TextPath(ADest, AText, X,Y, AAlign);
    287   DrawLastPath(ADest);
     539  TextPath(ADest, ATextUTF8, X,Y, AAlign, (OutlineMode <> twoPath) and not DrawGlyphsSimultaneously);
     540end;
     541
     542procedure TBGRACustomTypeWriter.CopyTextPathTo(ADest: IBGRAPath; ATextUTF8: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft);
     543var
     544  pstr: pchar;
     545  left,charlen: integer;
     546  nextchar: string;
     547  g: TBGRAGlyph;
     548  m,m2: TAffineMatrix;
     549begin
     550  if ATextUTF8 = '' then exit;
     551  m := GetTextMatrix(ATextUTF8, X,Y,AAlign);
     552  m2 := m;
     553
     554  pstr := @ATextUTF8[1];
     555  left := length(ATextUTF8);
     556  while left > 0 do
     557  begin
     558    charlen := UTF8CharacterLength(pstr);
     559    setlength(nextchar, charlen);
     560    move(pstr^, nextchar[1], charlen);
     561    inc(pstr,charlen);
     562    dec(left,charlen);
     563
     564    g := GetGlyph(nextchar);
     565    if g <> nil then
     566    begin
     567      if AAlign in [twaLeft,twaMiddle,twaRight] then
     568        m2 := m*AffineMatrixTranslation(0,-g.Height/2) else
     569      if AAlign in [twaBottomLeft,twaBottom,twaBottomRight] then
     570        m2 := m*AffineMatrixTranslation(0,-g.Height)
     571      else
     572        m2 := m;
     573      g.Path(ADest, m2);
     574      m := m*AffineMatrixTranslation(g.Width,0);
     575    end;
     576  end;
    288577end;
    289578
     
    301590end;
    302591
    303 function TBGRACustomTypeWriter.GetTextBox(AText: string; X, Y: Single;
     592function TBGRACustomTypeWriter.GetTextBox(ATextUTF8: string; X, Y: Single;
    304593  AAlign: TBGRATypeWriterAlignment): TAffineBox;
    305594var
     
    313602
    314603begin
    315   if AText = '' then result := TAffineBox.EmptyBox else
    316   begin
    317     m := GetTextMatrix(AText,X,Y,AAlign);
     604  if ATextUTF8 = '' then result := TAffineBox.EmptyBox else
     605  begin
     606    m := GetTextMatrix(ATextUTF8,X,Y,AAlign);
    318607    minY := 0;
    319608    maxY := 0;
    320609    totalWidth := 0;
    321610
    322     pstr := @AText[1];
    323     left := length(AText);
     611    pstr := @ATextUTF8[1];
     612    left := length(ATextUTF8);
    324613    while left > 0 do
    325614    begin
     
    359648end;
    360649
    361 function TBGRACustomTypeWriter.GetTextGlyphBoxes(AText: string; X, Y: Single;
     650function TBGRACustomTypeWriter.GetTextGlyphBoxes(ATextUTF8: string; X, Y: Single;
    362651  AAlign: TBGRATypeWriterAlignment): TGlyphBoxes;
    363652var
     
    372661
    373662begin
    374   if AText = '' then result := nil else
    375   begin
    376     setlength(result, UTF8Length(AText));
    377 
    378     m := GetTextMatrix(AText,X,Y,AAlign);
    379 
    380     pstr := @AText[1];
    381     left := length(AText);
     663  if ATextUTF8 = '' then result := nil else
     664  begin
     665    setlength(result, UTF8Length(ATextUTF8));
     666
     667    m := GetTextMatrix(ATextUTF8,X,Y,AAlign);
     668
     669    pstr := @ATextUTF8[1];
     670    left := length(ATextUTF8);
    382671    numChar := 0;
    383672    while left > 0 do
     
    418707end;
    419708
    420 procedure TBGRACustomTypeWriter.TextPath(ADest: TBGRACanvas2D; AText: string; X,
    421   Y: Single; AAlign: TBGRATypeWriterAlignment);
     709procedure TBGRACustomTypeWriter.NeedGlyphRange(AUnicodeFrom, AUnicodeTo: Cardinal);
     710var c: cardinal;
     711begin
     712  for c := AUnicodeFrom to AUnicodeTo do
     713    GetGlyph(UnicodeToUTF8(c));
     714end;
     715
     716procedure TBGRACustomTypeWriter.NeedGlyphAnsiRange;
     717var i: integer;
     718begin
     719  for i := 0 to 255 do
     720    GetGlyph(AnsiToUtf8(chr(i)));
     721end;
     722
     723procedure TBGRACustomTypeWriter.TextPath(ADest: TBGRACanvas2D; ATextUTF8: string; X,
     724  Y: Single; AAlign: TBGRATypeWriterAlignment; ADrawEachChar: boolean);
    422725var
    423726  pstr: pchar;
     
    427730  m,m2: TAffineMatrix;
    428731begin
    429   ADest.beginPath;
    430   if AText = '' then exit;
    431   m := GetTextMatrix(AText, X,Y,AAlign);
     732  if not ADrawEachChar then ADest.beginPath;
     733  if ATextUTF8 = '' then exit;
     734  m := GetTextMatrix(ATextUTF8, X,Y,AAlign);
    432735  m2 := m;
    433736
    434   pstr := @AText[1];
    435   left := length(AText);
     737  pstr := @ATextUTF8[1];
     738  left := length(ATextUTF8);
    436739  while left > 0 do
    437740  begin
     
    451754      else
    452755        m2 := m;
     756      if ADrawEachChar then ADest.beginPath;
    453757      g.Path(ADest, m2);
     758      if ADrawEachChar then DrawLastPath(ADest);
    454759      m := m*AffineMatrixTranslation(g.Width,0);
    455760    end;
     
    497802end;
    498803
     804procedure TBGRACustomTypeWriter.SaveGlyphsToStream(AStream: TStream);
     805var Enumerator: TAvgLvlTreeNodeEnumerator;
     806begin
     807  WinWriteLongint(AStream,CustomHeaderSize);
     808  WriteCustomHeader(AStream);
     809
     810  Enumerator := FGlyphs.GetEnumerator;
     811  while Enumerator.MoveNext do
     812    TBGRAGlyph(Enumerator.Current.Data).SaveToStream(AStream);
     813  Enumerator.Free;
     814end;
     815
     816procedure TBGRACustomTypeWriter.LoadGlyphsFromFile(AFilenameUTF8: string);
     817var Stream: TFileStreamUTF8;
     818begin
     819  Stream := nil;
     820  try
     821    Stream := TFileStreamUTF8.Create(AFilenameUTF8, fmOpenRead);
     822    LoadGlyphsFromStream(Stream);
     823  finally
     824    Stream.Free;
     825  end;
     826end;
     827
     828procedure TBGRACustomTypeWriter.LoadGlyphsFromStream(AStream: TStream);
     829var Header: TBGRACustomTypeWriterHeader;
     830  i: integer;
     831  g: TBGRAGlyph;
     832  HeaderSize: integer;
     833  GlyphStartPosition: Int64;
     834begin
     835  HeaderSize := WinReadLongint(AStream);
     836  GlyphStartPosition:= AStream.Position+HeaderSize;
     837  Header := ReadCustomTypeWriterHeader(AStream);
     838  if header.HeaderName <> HeaderName then
     839    raise exception.Create('Invalid file format ("'+header.HeaderName+'" should be "'+HeaderName+'")');
     840  ReadAdditionalHeader(AStream);
     841  AStream.Position:= GlyphStartPosition;
     842  for i := 0 to Header.NbGlyphs-1 do
     843  begin
     844    g := TBGRAGlyph.LoadFromStream(AStream);
     845    AddGlyph(g);
     846  end;
     847end;
     848
     849procedure TBGRACustomTypeWriter.SaveGlyphsToFile(AFilenameUTF8: string);
     850var Stream: TFileStreamUTF8;
     851begin
     852  Stream := nil;
     853  try
     854    Stream := TFileStreamUTF8.Create(AFilenameUTF8, fmCreate or fmOpenWrite);
     855    SaveGlyphsToStream(Stream);
     856  finally
     857    Stream.Free;
     858  end;
     859end;
     860
    499861function TBGRACustomTypeWriter.GetGlyphMatrix(AGlyph: TBGRAGlyph; X, Y: Single;
    500862  AAlign: TBGRATypeWriterAlignment): TAffineMatrix;
     
    514876end;
    515877
    516 function TBGRACustomTypeWriter.GetTextMatrix(AText: string; X, Y: Single;
     878function TBGRACustomTypeWriter.GetTextMatrix(ATextUTF8: string; X, Y: Single;
    517879  AAlign: TBGRATypeWriterAlignment): TAffineMatrix;
    518880var
     
    528890  begin
    529891    totalWidth := 0;
    530     pstr := @AText[1];
    531     left := length(AText);
     892    pstr := @ATextUTF8[1];
     893    left := length(ATextUTF8);
    532894    while left > 0 do
    533895    begin
     
    548910end;
    549911
     912function TBGRACustomTypeWriter.CustomHeaderSize: integer;
     913begin
     914  result := 1+length(HeaderName)+4;
     915end;
     916
     917procedure TBGRACustomTypeWriter.WriteCustomHeader(AStream: TStream);
     918var lHeaderName: string;
     919begin
     920  lHeaderName:= HeaderName;
     921  WinWriteByte(AStream,length(lHeaderName));
     922  AStream.Write(lHeaderName[1],length(lHeaderName));
     923  WinWriteLongint(AStream,FGlyphs.Count);
     924end;
     925
     926function TBGRACustomTypeWriter.ReadCustomTypeWriterHeader(AStream: TStream
     927  ): TBGRACustomTypeWriterHeader;
     928begin
     929  setlength(result.HeaderName, WinReadByte(AStream));
     930  AStream.Read(result.HeaderName[1],length(result.HeaderName));
     931  result.NbGlyphs:= WinReadLongint(AStream);
     932end;
     933
     934procedure TBGRACustomTypeWriter.ReadAdditionalHeader(AStream: TStream);
     935begin
     936  //nothing
     937end;
     938
     939function TBGRACustomTypeWriter.HeaderName: string;
     940begin
     941  result := 'TBGRACustomTypeWriter';
     942end;
     943
    550944destructor TBGRACustomTypeWriter.Destroy;
    551945begin
  • GraphicTest/Packages/bgrabitmap/bgravectorize.pas

    r452 r472  
    55interface
    66
     7{
     8  Font rendering units : BGRAText, BGRATextFX, BGRAVectorize, BGRAFreeType
     9
     10  This unit provides vectorizers :
     11  - VectorizeMonochrome function vectorizes a back'n'white image
     12  - TBGRAVectorizedFont allows to vectorize and to load vectorized font and draw them
     13
     14  TBGRAVectorizedFontRenderer class works like other font renderers, i.e., it can
     15  be assigned to the FontRenderer property. You can use it in two different modes :
     16  - if you supply a directory, it will look for *.glyphs files in it to load fonts
     17  - if you don't supply a directory, fonts will be vectorized from LCL
     18
     19  Note that unless you want to supply your own glyphs files, you don't need
     20  to use explicitely this renderer, because TBGRATextEffectFontRenderer will
     21  make use of it if necessary, according to effects parameters used.
     22}
     23
    724uses
    8   Classes, SysUtils, Graphics, BGRABitmapTypes, BGRATypewriter, BGRATransform, BGRACanvas2D;
    9 
     25  Types, Classes, SysUtils, Graphics, BGRABitmapTypes, BGRATypewriter, BGRATransform, BGRACanvas2D, BGRAText;
     26
     27//vectorize a monochrome bitmap
    1028function VectorizeMonochrome(ASource: TBGRACustomBitmap; zoom: single; PixelCenteredCoordinates: boolean): ArrayOfTPointF;
    1129
    1230type
     31  TBGRAVectorizedFont = class;
     32
     33  //this is the class to assign to FontRenderer property of TBGRABitmap
     34  { TBGRAVectorizedFontRenderer }
     35
     36  TBGRAVectorizedFontRenderer = class(TBGRACustomFontRenderer)
     37  protected
     38    FVectorizedFontArray: array of record
     39        FontName: string;
     40        FontStyle: TFontStyles;
     41        VectorizedFont: TBGRAVectorizedFont;
     42      end;
     43    FVectorizedFont: TBGRAVectorizedFont;
     44    FCanvas2D: TBGRACanvas2D;
     45    FDirectoryUTF8: string;
     46    function OutlineActuallyVisible: boolean;
     47    procedure UpdateFont;
     48    function GetCanvas2D(ASurface: TBGRACustomBitmap): TBGRACanvas2D;
     49    procedure InternalTextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; c: TBGRAPixel; texture: IBGRAScanner);
     50    procedure Init;
     51  public
     52    MaxFontResolution: integer;
     53
     54    OutlineVisible: boolean;
     55    OutlineWidth: single;
     56    OutlineColor: TBGRAPixel;
     57    OutlineTexture: IBGRAScanner;
     58    OuterOutlineOnly: boolean;
     59
     60    ShadowVisible: boolean;
     61    ShadowColor: TBGRAPixel;
     62    ShadowRadius: integer;
     63    ShadowOffset: TPoint;
     64
     65    constructor Create;
     66    constructor Create(ADirectoryUTF8: string);
     67    function GetFontPixelMetric: TFontPixelMetric; override;
     68    procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientation: integer; s: string; c: TBGRAPixel; align: TAlignment); override;
     69    procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientation: integer; s: string; texture: IBGRAScanner; align: TAlignment); override;
     70    procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; s: string; texture: IBGRAScanner; align: TAlignment); override;
     71    procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; s: string; c: TBGRAPixel; align: TAlignment); override;
     72    procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; s: string; style: TTextStyle; c: TBGRAPixel); override;
     73    procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; s: string; style: TTextStyle; texture: IBGRAScanner); override;
     74    procedure CopyTextPathTo(ADest: IBGRAPath; x, y: single; s: string; align: TAlignment); override;
     75    function TextSize(s: string): TSize; override;
     76    destructor Destroy; override;
     77  end;
     78
    1379  TGlyphSizes = array of record
    1480            Glyph: String;
     
    1682  end;
    1783
    18   TWordBreakHandler = procedure(var ABefore, AAfter: string) of object;
     84  TBGRAVectorizedFontHeader = record
     85    Name: string;
     86    Style: TFontStyles;
     87    EmHeightRatio: single;
     88    Resolution: integer;
     89    PixelMetric: TFontPixelMetric;
     90  end;
     91  TBGRAGlyphsInfo = record
     92    Name: string;
     93    Style: TFontStyles;
     94    NbGlyphs: integer;
     95  end;
    1996
    2097  { TBGRAVectorizedFont }
     
    33110    FItalicSlope: single;
    34111    FWordBreakHandler: TWordBreakHandler;
     112    FDirectory: string;
     113    FDirectoryContent: array of record
     114      Filename: string;
     115      FontName: string;
     116      FontStyle: TFontStyles;
     117    end;
     118    FFontEmHeightRatioComputed: boolean;
     119    FFontEmHeightRatio: single;
     120    FFontPixelMetric: TFontPixelMetric;
     121    FFontPixelMetricComputed: boolean;
     122    FFontFound: boolean;
     123    function GetEmHeight: single;
     124    function GetFontPixelMetric: TFontPixelMetric;
     125    function GetLCLHeight: single;
     126    function GetVectorizeLCL: boolean;
     127    procedure SetEmHeight(AValue: single);
    35128    procedure SetItalicSlope(AValue: single);
     129    procedure SetLCLHeight(AValue: single);
    36130    procedure SetOrientation(AValue: single);
    37131    procedure SetQuadraticCurves(AValue: boolean);
     
    41135    procedure SetName(AValue: string);
    42136    procedure SetStyle(AValue: TFontStyles);
     137    function GetFontEmHeightRatio: single;
     138    procedure SetVectorizeLCL(AValue: boolean);
    43139  protected
    44140    procedure UpdateFont;
     
    46142    function GetGlyph(AIdentifier: string): TBGRAGlyph; override;
    47143    procedure DefaultWordBreakHandler(var ABefore, AAfter: string);
     144    procedure Init(AVectorize: boolean);
     145    function CustomHeaderSize: integer; override;
     146    procedure WriteCustomHeader(AStream: TStream); override;
     147    procedure ReadAdditionalHeader(AStream: TStream); override;
     148    function ReadVectorizedFontHeader(AStream: TStream): TBGRAVectorizedFontHeader;
     149    function HeaderName: string; override;
     150    procedure SetDirectory(const AValue: string);
    48151  public
     152    UnderlineDecoration,StrikeOutDecoration: boolean;
    49153    constructor Create;
     154    constructor Create(AVectorizeLCL: boolean);
    50155    destructor Destroy; override;
    51156    function GetGlyphSize(AIdentifier:string): TPointF;
    52157    function GetTextGlyphSizes(AText:string): TGlyphSizes;
    53158    function GetTextSize(AText:string): TPointF;
    54     procedure SplitText(var AText: string; AMaxWidth: single; out ARemains: string);
    55     procedure DrawTextWordBreak(ADest: TBGRACanvas2D; AText: string; X, Y, MaxWidth: Single; AAlign: TBGRATypeWriterAlignment=twaTopLeft);
    56     procedure DrawTextRect(ADest: TBGRACanvas2D; AText: string; X1,Y1,X2,Y2: Single; AAlign: TBGRATypeWriterAlignment=twaTopLeft);
    57     procedure DrawTextRect(ADest: TBGRACanvas2D; AText: string; ATopLeft,ABottomRight: TPointF; AAlign: TBGRATypeWriterAlignment=twaTopLeft);
    58     function GetTextWordBreakGlyphBoxes(AText: string; X,Y, MaxWidth: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft): TGlyphBoxes;
    59     function GetTextRectGlyphBoxes(AText: string; X1,Y1,X2,Y2: Single; AAlign: TBGRATypeWriterAlignment=twaTopLeft): TGlyphBoxes;
    60     function GetTextRectGlyphBoxes(AText: string; ATopLeft,ABottomRight: TPointF; AAlign: TBGRATypeWriterAlignment=twaTopLeft): TGlyphBoxes;
     159    procedure SplitText(var ATextUTF8: string; AMaxWidth: single; out ARemainsUTF8: string);
     160    procedure DrawText(ADest: TBGRACanvas2D; ATextUTF8: string; X, Y: Single; AAlign: TBGRATypeWriterAlignment=twaTopLeft); override;
     161    procedure CopyTextPathTo(ADest: IBGRAPath; ATextUTF8: string; X, Y: Single;
     162      AAlign: TBGRATypeWriterAlignment=twaTopLeft); override;
     163    procedure DrawTextWordBreak(ADest: TBGRACanvas2D; ATextUTF8: string; X, Y, MaxWidth: Single; AAlign: TBGRATypeWriterAlignment=twaTopLeft);
     164    procedure DrawTextRect(ADest: TBGRACanvas2D; ATextUTF8: string; X1,Y1,X2,Y2: Single; AAlign: TBGRATypeWriterAlignment=twaTopLeft);
     165    procedure DrawTextRect(ADest: TBGRACanvas2D; ATextUTF8: string; ATopLeft,ABottomRight: TPointF; AAlign: TBGRATypeWriterAlignment=twaTopLeft);
     166    function GetTextWordBreakGlyphBoxes(ATextUTF8: string; X,Y, MaxWidth: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft): TGlyphBoxes;
     167    function GetTextRectGlyphBoxes(ATextUTF8: string; X1,Y1,X2,Y2: Single; AAlign: TBGRATypeWriterAlignment=twaTopLeft): TGlyphBoxes;
     168    function GetTextRectGlyphBoxes(ATextUTF8: string; ATopLeft,ABottomRight: TPointF; AAlign: TBGRATypeWriterAlignment=twaTopLeft): TGlyphBoxes;
     169    procedure UpdateDirectory;
     170    function LoadGlyphsInfo(AFilenameUTF8: string): TBGRAGlyphsInfo;
    61171
    62172    property Resolution: integer read FResolution write SetResolution;
    63173    property Style: TFontStyles read FStyle write SetStyle;
    64174    property Name: string read FName write SetName;
     175    property LCLHeight: single read GetLCLHeight write SetLCLHeight;
     176    property EmHeight: single read GetEmHeight write SetEmHeight;
    65177    property FullHeight: single read FFullHeight write SetFullHeight;
    66178    property FontMatrix: TAffineMatrix read FFontMatrix write SetFontMatrix;
     
    69181    property ItalicSlope: single read FItalicSlope write SetItalicSlope;
    70182    property OnWordBreak: TWordBreakHandler read FWordBreakHandler write FWordBreakHandler;
     183    property Directory: string read FDirectory write SetDirectory;
     184    property FontEmHeightRatio: single read GetFontEmHeightRatio;
     185    property FontPixelMetric: TFontPixelMetric read GetFontPixelMetric;
     186    property FontFound: boolean read FFontFound;
     187    property VectorizeLCL: boolean read GetVectorizeLCL write SetVectorizeLCL;
    71188  end;
    72189
    73190implementation
    74191
    75 uses BGRAText, LCLProc, Types;
    76 
     192uses LCLProc, FileUtil, lazutf8classes;
     193
     194{$i winstream.inc}
    77195function VectorizeMonochrome(ASource: TBGRACustomBitmap; zoom: single; PixelCenteredCoordinates: boolean): ArrayOfTPointF;
    78196const unitShift = 6;
     
    516634          (ord(cur[1])+ord(cur[3])+ord(cur[7])+ord(cur[9]) = 3)) then
    517635      begin
    518         if (not cur[6] and not cur[9] and not cur[8] and ((ASource.getPixel(integer(x-1),integer(y-2)).green <= 128) or (ASource.getPixel(integer(x+2),integer(y+1)).green <= 128)) ) or
    519           (not cur[8] and not cur[7] and not cur[4] and ((ASource.getPixel(integer(x-2),integer(y+1)).green <= 128) or (ASource.getPixel(integer(x+1),integer(y-2)).green <= 128)) ) or
    520           (not cur[4] and not cur[1] and not cur[2] and ((ASource.getPixel(integer(x+1),integer(y+2)).green <= 128) or (ASource.getPixel(integer(x-2),integer(y-1)).green <= 128)) ) or
    521           (not cur[2] and not cur[3] and not cur[6] and ((ASource.getPixel(integer(x-1),integer(y+2)).green <= 128) or (ASource.getPixel(integer(x+2),integer(y-1)).green <= 128)) ) then
     636        if (not cur[6] and not cur[9] and not cur[8] and ((ASource.getPixel(x-1,y-2).green <= 128) or (ASource.getPixel(x+2,y+1).green <= 128)) ) or
     637          (not cur[8] and not cur[7] and not cur[4] and ((ASource.getPixel(x-2,y+1).green <= 128) or (ASource.getPixel(x+1,y-2).green <= 128)) ) or
     638          (not cur[4] and not cur[1] and not cur[2] and ((ASource.getPixel(x+1,y+2).green <= 128) or (ASource.getPixel(x-2,y-1).green <= 128)) ) or
     639          (not cur[2] and not cur[3] and not cur[6] and ((ASource.getPixel(x-1,y+2).green <= 128) or (ASource.getPixel(x+2,y-1).green <= 128)) ) then
    522640            ortho[y,x] := true;
    523641      end;
     
    839957end;
    840958
     959{ TBGRAVectorizedFontRenderer }
     960
     961function TBGRAVectorizedFontRenderer.OutlineActuallyVisible: boolean;
     962begin
     963  result := OutlineVisible and (abs(OutlineWidth) > 0) and (OutlineColor.Alpha <> 0) or (OutlineTexture <> nil);
     964end;
     965
     966procedure TBGRAVectorizedFontRenderer.UpdateFont;
     967var i,neededResolution: integer;
     968begin
     969  FVectorizedFont := nil;
     970  FontName := Trim(FontName);
     971  for i := 0 to high(FVectorizedFontArray) do
     972    if (CompareText(FVectorizedFontArray[i].FontName,FontName)=0) and
     973      (FVectorizedFontArray[i].FontStyle = FontStyle) then
     974    begin
     975      FVectorizedFont := FVectorizedFontArray[i].VectorizedFont;
     976      break;
     977    end;
     978
     979  if FVectorizedFont = nil then
     980  begin
     981    FVectorizedFont:= TBGRAVectorizedFont.Create(False);
     982    FVectorizedFont.Name := FontName;
     983    FVectorizedFont.Style := FontStyle;
     984    FVectorizedFont.Directory := FDirectoryUTF8;
     985    if not FVectorizedFont.FontFound and LCLFontAvailable then
     986      FVectorizedFont.VectorizeLCL := True;
     987    Setlength(FVectorizedFontArray,length(FVectorizedFontArray)+1);
     988    FVectorizedFontArray[high(FVectorizedFontArray)].FontName := FontName;
     989    FVectorizedFontArray[high(FVectorizedFontArray)].FontStyle := FontStyle;
     990    FVectorizedFontArray[high(FVectorizedFontArray)].VectorizedFont := FVectorizedFont;
     991  end;
     992  if FontEmHeight > 0 then
     993    FVectorizedFont.EmHeight := FontEmHeight
     994  else
     995    FVectorizedFont.FullHeight:= -FontEmHeight;
     996  if OutlineActuallyVisible then
     997  begin
     998    if OuterOutlineOnly then
     999      FVectorizedFont.OutlineMode := twoFillOverStroke
     1000    else
     1001      FVectorizedFont.OutlineMode := twoStrokeOverFill;
     1002    FVectorizedFont.QuadraticCurves := False;
     1003  end
     1004  else
     1005  begin
     1006    FVectorizedFont.OutlineMode := twoFill;
     1007    FVectorizedFont.QuadraticCurves := FVectorizedFont.FullHeight > FVectorizedFont.Resolution*0.8;
     1008  end;
     1009  if FVectorizedFont.VectorizeLCL then
     1010  begin
     1011    neededResolution := trunc((FVectorizedFont.FullHeight+80)/50)*50;
     1012    if neededResolution > MaxFontResolution then neededResolution := MaxFontResolution;
     1013    if FVectorizedFont.Resolution < neededResolution then FVectorizedFont.Resolution:= neededResolution;
     1014  end;
     1015end;
     1016
     1017function TBGRAVectorizedFontRenderer.GetCanvas2D(ASurface: TBGRACustomBitmap
     1018  ): TBGRACanvas2D;
     1019begin
     1020  if (FCanvas2D = nil) or (FCanvas2D.surface <> ASurface) then
     1021  begin
     1022    FCanvas2D.Free;
     1023    FCanvas2D := TBGRACanvas2D.Create(ASurface);
     1024  end;
     1025  result := FCanvas2D;
     1026  FCanvas2D.antialiasing:= FontQuality in[fqFineAntialiasing,fqFineClearTypeBGR,fqFineClearTypeRGB];
     1027  if OutlineTexture <> nil then
     1028    FCanvas2D.strokeStyle(OutlineTexture)
     1029  else
     1030    FCanvas2D.strokeStyle(OutlineColor);
     1031  FCanvas2D.lineWidth := abs(OutlineWidth);
     1032  if not ShadowVisible then
     1033    FCanvas2D.shadowColor(BGRAPixelTransparent)
     1034  else
     1035  begin
     1036    FCanvas2D.shadowColor(ShadowColor);
     1037    FCanvas2D.shadowBlur:= ShadowRadius;
     1038    FCanvas2D.shadowOffset := PointF(ShadowOffset.X,ShadowOffset.Y);
     1039  end;
     1040end;
     1041
     1042procedure TBGRAVectorizedFontRenderer.InternalTextRect(
     1043  ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string;
     1044  style: TTextStyle; c: TBGRAPixel; texture: IBGRAScanner);
     1045var
     1046  twAlign : TBGRATypeWriterAlignment;
     1047  c2D: TBGRACanvas2D;
     1048  intersectedClip,previousClip: TRect;
     1049begin
     1050  previousClip := ADest.ClipRect;
     1051  if style.Clipping then
     1052  begin
     1053    intersectedClip := rect(0,0,0,0);
     1054    if not IntersectRect(intersectedClip, previousClip, ARect) then exit;
     1055    ADest.ClipRect := intersectedClip;
     1056  end;
     1057  UpdateFont;
     1058  FVectorizedFont.Orientation := 0;
     1059  case style.Alignment of
     1060    taCenter: case style.Layout of
     1061              tlCenter: twAlign := twaMiddle;
     1062              tlBottom: twAlign := twaBottom;
     1063              else twAlign:= twaTop;
     1064              end;
     1065    taRightJustify:
     1066              case style.Layout of
     1067              tlCenter: twAlign := twaRight;
     1068              tlBottom: twAlign := twaBottomRight;
     1069              else twAlign := twaTopRight;
     1070              end;
     1071    else
     1072              case style.Layout of
     1073              tlCenter: twAlign := twaLeft;
     1074              tlBottom: twAlign := twaBottomLeft;
     1075              else twAlign:= twaTopLeft;
     1076              end;
     1077  end;
     1078  c2D := GetCanvas2D(ADest);
     1079  if texture = nil then
     1080    c2D.fillStyle(c)
     1081  else
     1082    c2D.fillStyle(texture);
     1083  if style.Wordbreak then
     1084    FVectorizedFont.DrawTextRect(c2D, sUTF8, x-0.5,y-0.5,ARect.Right-0.5,ARect.Bottom-0.5, twAlign)
     1085  else
     1086  begin
     1087    case style.Layout of
     1088    tlCenter: y := (ARect.Top+ARect.Bottom) div 2;
     1089    tlBottom: y := ARect.Bottom;
     1090    end;
     1091    case style.Alignment of
     1092    taCenter: FVectorizedFont.DrawText(c2D, sUTF8, (ARect.Left+ARect.Right-1)/2,y-0.5, twAlign);
     1093    taRightJustify: FVectorizedFont.DrawText(c2D, sUTF8, ARect.Right-0.5,y-0.5, twAlign);
     1094    else
     1095      FVectorizedFont.DrawText(c2D, sUTF8, x-0.5,y-0.5, twAlign);
     1096    end;
     1097  end;
     1098  if style.Clipping then
     1099    ADest.ClipRect := previousClip;
     1100end;
     1101
     1102procedure TBGRAVectorizedFontRenderer.Init;
     1103begin
     1104  FVectorizedFontArray := nil;
     1105  FDirectoryUTF8 := '';
     1106
     1107  OutlineVisible:= True;
     1108  OutlineColor := BGRAPixelTransparent;
     1109  OuterOutlineOnly := false;
     1110
     1111  ShadowColor := BGRABlack;
     1112  ShadowVisible := false;
     1113  ShadowOffset := Point(5,5);
     1114  ShadowRadius := 5;
     1115
     1116  MaxFontResolution := 300;
     1117end;
     1118
     1119constructor TBGRAVectorizedFontRenderer.Create;
     1120begin
     1121  Init;
     1122end;
     1123
     1124constructor TBGRAVectorizedFontRenderer.Create(ADirectoryUTF8: string);
     1125begin
     1126  Init;
     1127  FDirectoryUTF8 := ADirectoryUTF8;
     1128end;
     1129
     1130function TBGRAVectorizedFontRenderer.GetFontPixelMetric: TFontPixelMetric;
     1131var factor: single;
     1132begin
     1133  UpdateFont;
     1134  result := FVectorizedFont.FontPixelMetric;
     1135  if FVectorizedFont.Resolution > 0 then
     1136  begin
     1137    factor := FVectorizedFont.FullHeight/FVectorizedFont.Resolution;
     1138    result.Baseline := round(result.Baseline*factor);
     1139    result.CapLine := round(result.CapLine*factor);
     1140    result.Lineheight := round(result.Lineheight*factor);
     1141    result.DescentLine := round(result.DescentLine*factor);
     1142    result.xLine := round(result.xLine*factor);
     1143  end;
     1144end;
     1145
     1146procedure TBGRAVectorizedFontRenderer.TextOutAngle(ADest: TBGRACustomBitmap; x,
     1147  y: single; orientation: integer; s: string; c: TBGRAPixel; align: TAlignment);
     1148var
     1149  twAlign : TBGRATypeWriterAlignment;
     1150  c2D: TBGRACanvas2D;
     1151  ofs: TPointF;
     1152begin
     1153  UpdateFont;
     1154  FVectorizedFont.Orientation := orientation;
     1155  case align of
     1156    taCenter: twAlign:= twaMiddle;
     1157    taRightJustify: twAlign := twaRight;
     1158    else twAlign:= twaLeft;
     1159  end;
     1160  c2D := GetCanvas2D(ADest);
     1161  c2D.fillStyle(c);
     1162  ofs := PointF(x,y);
     1163  ofs += AffineMatrixRotationDeg(-orientation*0.1)*PointF(0,FVectorizedFont.FullHeight*0.5);
     1164  FVectorizedFont.DrawText(c2D, s, ofs.x,ofs.y, twAlign);
     1165end;
     1166
     1167procedure TBGRAVectorizedFontRenderer.TextOutAngle(ADest: TBGRACustomBitmap; x,
     1168  y: single; orientation: integer; s: string; texture: IBGRAScanner;
     1169  align: TAlignment);
     1170var
     1171  twAlign : TBGRATypeWriterAlignment;
     1172  c2D: TBGRACanvas2D;
     1173begin
     1174  UpdateFont;
     1175  FVectorizedFont.Orientation := orientation;
     1176  case align of
     1177    taCenter: twAlign:= twaTop;
     1178    taRightJustify: twAlign := twaTopRight;
     1179    else twAlign:= twaTopLeft;
     1180  end;
     1181  c2D := GetCanvas2D(ADest);
     1182  c2D.fillStyle(texture);
     1183  FVectorizedFont.DrawText(c2D, s, x,y, twAlign);
     1184end;
     1185
     1186procedure TBGRAVectorizedFontRenderer.TextOut(ADest: TBGRACustomBitmap; x,
     1187  y: single; s: string; texture: IBGRAScanner; align: TAlignment);
     1188begin
     1189  TextOutAngle(ADest,x,y,FontOrientation,s,texture,align);
     1190end;
     1191
     1192procedure TBGRAVectorizedFontRenderer.TextOut(ADest: TBGRACustomBitmap; x,
     1193  y: single; s: string; c: TBGRAPixel; align: TAlignment);
     1194begin
     1195  TextOutAngle(ADest,x,y,FontOrientation,s,c,align);
     1196end;
     1197
     1198procedure TBGRAVectorizedFontRenderer.TextRect(ADest: TBGRACustomBitmap;
     1199  ARect: TRect; x, y: integer; s: string; style: TTextStyle; c: TBGRAPixel);
     1200begin
     1201  InternalTextRect(ADest,ARect,x,y,s,style,c,nil);
     1202end;
     1203
     1204procedure TBGRAVectorizedFontRenderer.TextRect(ADest: TBGRACustomBitmap;
     1205  ARect: TRect; x, y: integer; s: string; style: TTextStyle;
     1206  texture: IBGRAScanner);
     1207begin
     1208  InternalTextRect(ADest,ARect,x,y,s,style,BGRAPixelTransparent,texture);
     1209end;
     1210
     1211procedure TBGRAVectorizedFontRenderer.CopyTextPathTo(ADest: IBGRAPath; x, y: single; s: string; align: TAlignment);
     1212var
     1213  twAlign : TBGRATypeWriterAlignment;
     1214  ofs: TPointF;
     1215begin
     1216  UpdateFont;
     1217  FVectorizedFont.Orientation := 0;
     1218  case align of
     1219    taCenter: twAlign:= twaMiddle;
     1220    taRightJustify: twAlign := twaRight;
     1221    else twAlign:= twaLeft;
     1222  end;
     1223  ofs := PointF(x,y);
     1224  ofs += PointF(0,FVectorizedFont.FullHeight*0.5);
     1225  FVectorizedFont.CopyTextPathTo(ADest, s, ofs.x,ofs.y, twAlign);
     1226end;
     1227
     1228function TBGRAVectorizedFontRenderer.TextSize(s: string): TSize;
     1229var sizeF: TPointF;
     1230begin
     1231  UpdateFont;
     1232  sizeF := FVectorizedFont.GetTextSize(s);
     1233  result.cx := round(sizeF.x);
     1234  result.cy := round(sizeF.y);
     1235end;
     1236
     1237destructor TBGRAVectorizedFontRenderer.Destroy;
     1238var i: integer;
     1239begin
     1240  FCanvas2D.Free;
     1241  for i := 0 to high(FVectorizedFontArray) do
     1242    FVectorizedFontArray[i].VectorizedFont.Free;
     1243  FVectorizedFontArray := nil;
     1244  inherited Destroy;
     1245end;
     1246
    8411247{ TBGRAVectorizedFont }
    8421248
     
    8621268end;
    8631269
     1270procedure TBGRAVectorizedFont.SetLCLHeight(AValue: single);
     1271begin
     1272  if (AValue > 0) xor (FontEmHeightSign < 0) then
     1273    EmHeight := abs(AValue)
     1274  else
     1275    FullHeight := abs(AValue);
     1276end;
     1277
     1278function TBGRAVectorizedFont.GetEmHeight: single;
     1279begin
     1280  result := FullHeight * FontEmHeightRatio;
     1281end;
     1282
     1283function TBGRAVectorizedFont.GetFontPixelMetric: TFontPixelMetric;
     1284begin
     1285  if not FFontPixelMetricComputed and (FFont <> nil) then
     1286  begin
     1287    FFontPixelMetric := BGRAText.GetFontPixelMetric(FFont);
     1288    FFontPixelMetricComputed := true;
     1289  end;
     1290  result := FFontPixelMetric;
     1291end;
     1292
     1293function TBGRAVectorizedFont.GetLCLHeight: single;
     1294begin
     1295  result := FullHeight * FontFullHeightSign;
     1296end;
     1297
     1298function TBGRAVectorizedFont.GetVectorizeLCL: boolean;
     1299begin
     1300  result := FFont <> nil;
     1301end;
     1302
     1303procedure TBGRAVectorizedFont.SetEmHeight(AValue: single);
     1304begin
     1305  if FontEmHeightRatio > 0 then
     1306    FullHeight := AValue / FontEmHeightRatio;
     1307end;
     1308
    8641309procedure TBGRAVectorizedFont.SetQuadraticCurves(AValue: boolean);
    8651310begin
     
    8951340end;
    8961341
     1342function TBGRAVectorizedFont.GetFontEmHeightRatio: single;
     1343var
     1344  lEmHeight, lFullHeight: single;
     1345  OldHeight: integer;
     1346begin
     1347  if not FFontEmHeightRatioComputed then
     1348  begin
     1349    if FFont <> nil then
     1350    begin
     1351      OldHeight := FFont.Height;
     1352      FFont.Height := FontEmHeightSign * 100;
     1353      lEmHeight := BGRATextSize(FFont, fqSystem, 'Hg', 1).cy;
     1354      FFont.Height := FontFullHeightSign * 100;
     1355      lFullHeight := BGRATextSize(FFont, fqSystem, 'Hg', 1).cy;
     1356      if lEmHeight = 0 then
     1357        FFontEmHeightRatio := 1
     1358      else
     1359        FFontEmHeightRatio := lFullHeight/lEmHeight;
     1360      FFontEmHeightRatioComputed := true;
     1361      FFont.Height := OldHeight;
     1362    end else
     1363    begin
     1364      result := 1;
     1365      exit;
     1366    end;
     1367  end;
     1368  result := FFontEmHeightRatio;
     1369end;
     1370
     1371procedure TBGRAVectorizedFont.SetVectorizeLCL(AValue: boolean);
     1372begin
     1373  if AValue then
     1374  begin
     1375    if FFont = nil then
     1376      FFont := TFont.Create;
     1377  end else
     1378  begin
     1379    if FFont <> nil then
     1380      FreeAndNil(FFont);
     1381  end;
     1382  UpdateFont;
     1383end;
     1384
    8971385procedure TBGRAVectorizedFont.UpdateFont;
    898 begin
    899   ClearGlyphs;
    900   FFont.Name := FName;
    901   FFont.Style := FStyle;
    902   FFont.Height := FontFullHeightSign * FResolution;
     1386var i: integer;
     1387  bestIndex, bestDistance: integer;
     1388  distance: integer;
     1389begin
     1390  if FFont <> nil then
     1391  begin
     1392    ClearGlyphs;
     1393    FFont.Name := FName;
     1394    FFont.Style := FStyle;
     1395    FFont.Height := FontFullHeightSign * FResolution;
     1396    FFontEmHeightRatio := 1;
     1397    FFontEmHeightRatioComputed := false;
     1398    fillchar(FFontPixelMetric,sizeof(FFontPixelMetric),0);
     1399    FFontPixelMetricComputed := false;
     1400    FFontFound := True;
     1401  end else
     1402  begin
     1403    bestIndex := -1;
     1404    bestDistance := 1000;
     1405    for i := 0 to high(FDirectoryContent) do
     1406    begin
     1407      if CompareText(FDirectoryContent[i].FontName,FName) = 0 then
     1408      begin
     1409        distance := 0;
     1410        if (fsBold in FDirectoryContent[i].FontStyle) xor (fsBold in FStyle) then distance += 10;
     1411        if (fsItalic in FDirectoryContent[i].FontStyle) xor (fsItalic in FStyle) then distance += 5;
     1412        if (fsStrikeOut in FDirectoryContent[i].FontStyle) xor (fsStrikeOut in FStyle) then distance += 1;
     1413        if (fsUnderline in FDirectoryContent[i].FontStyle) xor (fsUnderline in FStyle) then distance += 1;
     1414        if (bestIndex = -1) or (distance < bestDistance) then
     1415        begin
     1416          bestIndex := i;
     1417          bestDistance := distance;
     1418          if FDirectoryContent[i].FontStyle = FStyle then break;
     1419        end;
     1420      end;
     1421    end;
     1422    if bestIndex <> -1 then
     1423    begin
     1424      if not (fsItalic in FDirectoryContent[bestIndex].FontStyle) and (fsItalic in FStyle) then
     1425        ItalicSlope := 0.25
     1426      else if (fsItalic in FDirectoryContent[bestIndex].FontStyle) and not (fsItalic in FStyle) then
     1427        ItalicSlope := -0.25
     1428      else
     1429        ItalicSlope := 0;
     1430
     1431      UnderlineDecoration := not (fsUnderline in FDirectoryContent[bestIndex].FontStyle) and (fsUnderline in FStyle);
     1432      StrikeOutDecoration := not (fsStrikeOut in FDirectoryContent[bestIndex].FontStyle) and (fsStrikeOut in FStyle);
     1433
     1434      ClearGlyphs;
     1435      LoadGlyphsFromFile(FDirectoryContent[bestIndex].Filename);
     1436      FFontFound := True;
     1437    end else
     1438      FFontFound := false;
     1439  end;
    9031440end;
    9041441
     
    9111448begin
    9121449  inherited Create;
    913   FName := 'Arial';
    914   FStyle := [];
    915   FFontMatrix := AffineMatrixIdentity;
    916   FOrientation := 0;
    917   FResolution := 100;
    918   FFont := TFont.Create;
    919   FBuffer := BGRABitmapFactory.Create;
    920   FFullHeight := 20;
    921   FItalicSlope := 0;
    922   UpdateFont;
    923   UpdateMatrix;
    924   FWordBreakHandler:= nil;
     1450  Init(True);
     1451end;
     1452
     1453constructor TBGRAVectorizedFont.Create(AVectorizeLCL: boolean);
     1454begin
     1455  inherited Create;
     1456  Init(AVectorizeLCL);
    9251457end;
    9261458
     
    10121544end;
    10131545
    1014 procedure TBGRAVectorizedFont.SplitText(var AText: string; AMaxWidth: single;
    1015   out ARemains: string);
     1546procedure TBGRAVectorizedFont.SplitText(var ATextUTF8: string; AMaxWidth: single;
     1547  out ARemainsUTF8: string);
    10161548var
    10171549  pstr: pchar;
    1018   left,charlen: integer;
     1550  p,left,charlen: integer;
     1551  totalWidth: single;
     1552  firstChar: boolean;
    10191553  nextchar: string;
    10201554  g: TBGRAGlyph;
    1021   totalWidth: single;
    1022   firstChar: boolean;
    10231555begin
    10241556  totalWidth := 0;
    1025   if AText = '' then
    1026   begin
    1027     ARemains := '';
     1557  if ATextUTF8 = '' then
     1558  begin
     1559    ARemainsUTF8 := '';
    10281560    exit;
    10291561  end else
    10301562  begin
    1031     pstr := @AText[1];
    1032     left := length(AText);
     1563    p := 1;
     1564    pstr := @ATextUTF8[1];
     1565    left := length(ATextUTF8);
    10331566    firstChar := true;
    10341567    while left > 0 do
    10351568    begin
     1569      if RemoveLineEnding(ATextUTF8,p) then
     1570      begin
     1571        ARemainsUTF8 := copy(ATextUTF8,p,length(ATextUTF8)-p+1);
     1572        ATextUTF8 := copy(ATextUTF8,1,p-1);
     1573        exit;
     1574      end;
     1575
    10361576      charlen := UTF8CharacterLength(pstr);
    10371577      setlength(nextchar, charlen);
     
    10431583      begin
    10441584        totalWidth += g.Width*FullHeight;
    1045         if (totalWidth > AMaxWidth) and not firstChar then
     1585        if not firstChar and (totalWidth > AMaxWidth) then
    10461586        begin
    1047           ARemains:= copy(AText,length(AText)-left+1,left);
    1048           AText := copy(AText, 1, length(AText)-left);
     1587          ARemainsUTF8:= copy(ATextUTF8,p,length(ATextUTF8)-p+1);
     1588          ATextUTF8 := copy(ATextUTF8,1,p-1);
    10491589          if Assigned(FWordBreakHandler) then
    1050             FWordBreakHandler(AText,ARemains) else
    1051               DefaultWordBreakHandler(AText,ARemains);
     1590            FWordBreakHandler(ATextUTF8,ARemainsUTF8) else
     1591              DefaultWordBreakHandler(ATextUTF8,ARemainsUTF8);
    10521592          exit;
    10531593        end;
     
    10551595
    10561596      dec(left,charlen);
     1597      inc(p,charlen);
    10571598      firstChar := false;
    10581599    end;
    10591600  end;
    1060   ARemains := ''; //no split
     1601  ARemainsUTF8 := ''; //no split
     1602end;
     1603
     1604procedure TBGRAVectorizedFont.DrawText(ADest: TBGRACanvas2D; ATextUTF8: string; X,
     1605  Y: Single; AAlign: TBGRATypeWriterAlignment);
     1606var underlinePoly: ArrayOfTPointF;
     1607  m: TAffineMatrix;
     1608  i: integer;
     1609  deltaY: single;
     1610begin
     1611  inherited DrawText(ADest, ATextUTF8, X, Y, AAlign);
     1612  if AAlign in [twaBottom,twaBottomLeft,twaBottomRight] then deltaY := -1 else
     1613  if AAlign in [twaLeft,twaMiddle,twaRight] then deltaY := -0.5 else
     1614    deltaY := 0;
     1615  if UnderlineDecoration and (Resolution > 0) then
     1616  begin
     1617    underlinePoly := BGRATextUnderline(PointF(0,deltaY), GetTextSize(ATextUTF8).x/FullHeight, FontPixelMetric.Baseline/Resolution,
     1618      (FontPixelMetric.Baseline-FontPixelMetric.CapLine)/Resolution);
     1619    if underlinePoly <> nil then
     1620    begin
     1621      m := GetTextMatrix(ATextUTF8, X,Y,AAlign);
     1622      for i := 0 to high(underlinePoly) do
     1623        underlinePoly[i] := m*underlinePoly[i];
     1624      if OutlineMode <> twoPath then ADest.beginPath;
     1625      ADest.polylineTo(underlinePoly);
     1626      DrawLastPath(ADest);
     1627    end;
     1628  end;
     1629  if StrikeOutDecoration and (Resolution > 0) then
     1630  begin
     1631    underlinePoly := BGRATextStrikeOut(PointF(0,deltaY), GetTextSize(ATextUTF8).x/FullHeight, FontPixelMetric.Baseline/Resolution,
     1632      (FontPixelMetric.Baseline-FontPixelMetric.CapLine)/Resolution, (FontPixelMetric.Baseline-FontPixelMetric.xLine)/Resolution);
     1633    if underlinePoly <> nil then
     1634    begin
     1635      m := GetTextMatrix(ATextUTF8, X,Y,AAlign);
     1636      for i := 0 to high(underlinePoly) do
     1637        underlinePoly[i] := m*underlinePoly[i];
     1638      if OutlineMode <> twoPath then ADest.beginPath;
     1639      ADest.polylineTo(underlinePoly);
     1640      DrawLastPath(ADest);
     1641    end;
     1642  end;
     1643end;
     1644
     1645procedure TBGRAVectorizedFont.CopyTextPathTo(ADest: IBGRAPath;
     1646  ATextUTF8: string; X, Y: Single; AAlign: TBGRATypeWriterAlignment);
     1647var underlinePoly: ArrayOfTPointF;
     1648  m: TAffineMatrix;
     1649  i: integer;
     1650  deltaY: single;
     1651begin
     1652  inherited CopyTextPathTo(ADest,ATextUTF8, X, Y, AAlign);
     1653  if AAlign in [twaBottom,twaBottomLeft,twaBottomRight] then deltaY := -1 else
     1654  if AAlign in [twaLeft,twaMiddle,twaRight] then deltaY := -0.5 else
     1655    deltaY := 0;
     1656  if UnderlineDecoration and (Resolution > 0) then
     1657  begin
     1658    underlinePoly := BGRATextUnderline(PointF(0,deltaY), GetTextSize(ATextUTF8).x/FullHeight, FontPixelMetric.Baseline/Resolution,
     1659      (FontPixelMetric.Baseline-FontPixelMetric.CapLine)/Resolution);
     1660    if underlinePoly <> nil then
     1661    begin
     1662      m := GetTextMatrix(ATextUTF8, X,Y,AAlign);
     1663      ADest.moveTo(m*underlinePoly[0]);
     1664      for i := 1 to high(underlinePoly) do
     1665        ADest.lineTo(m*underlinePoly[i]);
     1666      ADest.closePath;
     1667    end;
     1668  end;
     1669  if StrikeOutDecoration and (Resolution > 0) then
     1670  begin
     1671    underlinePoly := BGRATextStrikeOut(PointF(0,deltaY), GetTextSize(ATextUTF8).x/FullHeight, FontPixelMetric.Baseline/Resolution,
     1672      (FontPixelMetric.Baseline-FontPixelMetric.CapLine)/Resolution, (FontPixelMetric.Baseline-FontPixelMetric.xLine)/Resolution);
     1673    if underlinePoly <> nil then
     1674    begin
     1675      m := GetTextMatrix(ATextUTF8, X,Y,AAlign);
     1676      ADest.moveTo(m*underlinePoly[0]);
     1677      for i := 1 to high(underlinePoly) do
     1678        ADest.lineTo(m*underlinePoly[i]);
     1679      ADest.closePath;
     1680    end;
     1681  end;
    10611682end;
    10621683
    10631684procedure TBGRAVectorizedFont.DrawTextWordBreak(ADest: TBGRACanvas2D;
    1064   AText: string; X, Y, MaxWidth: Single; AAlign: TBGRATypeWriterAlignment);
     1685  ATextUTF8: string; X, Y, MaxWidth: Single; AAlign: TBGRATypeWriterAlignment);
    10651686var ARemains: string;
    10661687  step: TPointF;
     
    10711692  lineAlignment: TBGRATypeWriterAlignment;
    10721693begin
    1073   if (AText = '') or (MaxWidth <= 0) then exit;
     1694  if (ATextUTF8 = '') or (MaxWidth <= 0) then exit;
    10741695
    10751696  oldItalicSlope:= ItalicSlope;
     
    11041725    Y += step.Y*lineShift;
    11051726    repeat
    1106       SplitText(AText, MaxWidth, ARemains);
    1107       DrawText(ADest,AText,X,Y,lineAlignment);
    1108       AText := ARemains;
     1727      SplitText(ATextUTF8, MaxWidth, ARemains);
     1728      DrawText(ADest,ATextUTF8,X,Y,lineAlignment);
     1729      ATextUTF8 := ARemains;
    11091730      X+= step.X;
    11101731      Y+= step.Y;
     
    11141735    lines := TStringList.Create;
    11151736    repeat
    1116       SplitText(AText, MaxWidth, ARemains);
    1117       lines.Add(AText);
    1118       AText := ARemains;
     1737      SplitText(ATextUTF8, MaxWidth, ARemains);
     1738      lines.Add(ATextUTF8);
     1739      ATextUTF8 := ARemains;
    11191740    until ARemains = '';
    11201741    if AAlign in[twaLeft,twaMiddle,twaRight] then lineShift := lines.Count/2-0.5
     
    11401761end;
    11411762
    1142 procedure TBGRAVectorizedFont.DrawTextRect(ADest: TBGRACanvas2D; AText: string;
     1763procedure TBGRAVectorizedFont.DrawTextRect(ADest: TBGRACanvas2D; ATextUTF8: string;
    11431764  X1, Y1, X2, Y2: Single; AAlign: TBGRATypeWriterAlignment);
    11441765var X,Y: single;
     
    11541775  oldOrientation:= Orientation;
    11551776  Orientation:= 0;
    1156   DrawTextWordBreak(ADest,AText,X,Y,X2-X1,AAlign);
     1777  DrawTextWordBreak(ADest,ATextUTF8,X,Y,X2-X1,AAlign);
    11571778  Orientation:= oldOrientation;
    11581779end;
    11591780
    1160 procedure TBGRAVectorizedFont.DrawTextRect(ADest: TBGRACanvas2D; AText: string;
     1781procedure TBGRAVectorizedFont.DrawTextRect(ADest: TBGRACanvas2D; ATextUTF8: string;
    11611782  ATopLeft, ABottomRight: TPointF; AAlign: TBGRATypeWriterAlignment);
    11621783begin
    1163   DrawTextRect(ADest,AText,ATopLeft.X,ATopLeft.Y,ABottomRight.X,ABottomRight.Y,AAlign);
    1164 end;
    1165 
    1166 function TBGRAVectorizedFont.GetTextWordBreakGlyphBoxes(AText: string; X, Y,
     1784  DrawTextRect(ADest,ATextUTF8,ATopLeft.X,ATopLeft.Y,ABottomRight.X,ABottomRight.Y,AAlign);
     1785end;
     1786
     1787function TBGRAVectorizedFont.GetTextWordBreakGlyphBoxes(ATextUTF8: string; X, Y,
    11671788  MaxWidth: Single; AAlign: TBGRATypeWriterAlignment): TGlyphBoxes;
    11681789var ARemains: string;
     
    11771798begin
    11781799  result := nil;
    1179   if AText = '' then exit;
     1800  if ATextUTF8 = '' then exit;
    11801801
    11811802  oldItalicSlope:= ItalicSlope;
     
    12021823  lines := TStringList.Create;
    12031824  repeat
    1204     SplitText(AText, MaxWidth, ARemains);
    1205     lines.Add(AText);
    1206     AText := ARemains;
     1825    SplitText(ATextUTF8, MaxWidth, ARemains);
     1826    lines.Add(ATextUTF8);
     1827    ATextUTF8 := ARemains;
    12071828  until ARemains = '';
    12081829
     
    12391860end;
    12401861
    1241 function TBGRAVectorizedFont.GetTextRectGlyphBoxes(AText: string; X1, Y1, X2,
     1862function TBGRAVectorizedFont.GetTextRectGlyphBoxes(ATextUTF8: string; X1, Y1, X2,
    12421863  Y2: Single; AAlign: TBGRATypeWriterAlignment): TGlyphBoxes;
    12431864var X,Y,oldOrientation: single;
     
    12561877  oldOrientation:= Orientation;
    12571878  Orientation:= 0;
    1258   result := GetTextWordBreakGlyphBoxes(AText,X,Y,X2-X1,AAlign);
     1879  result := GetTextWordBreakGlyphBoxes(ATextUTF8,X,Y,X2-X1,AAlign);
    12591880  Orientation:= oldOrientation;
    12601881end;
    12611882
    1262 function TBGRAVectorizedFont.GetTextRectGlyphBoxes(AText: string; ATopLeft,
     1883function TBGRAVectorizedFont.GetTextRectGlyphBoxes(ATextUTF8: string; ATopLeft,
    12631884  ABottomRight: TPointF; AAlign: TBGRATypeWriterAlignment): TGlyphBoxes;
    12641885begin
    1265   result := GetTextRectGlyphBoxes(AText,ATopLeft.X,ATopLeft.Y,ABottomRight.X,ABottomRight.Y,AAlign);
     1886  result := GetTextRectGlyphBoxes(ATextUTF8,ATopLeft.X,ATopLeft.Y,ABottomRight.X,ABottomRight.Y,AAlign);
     1887end;
     1888
     1889procedure TBGRAVectorizedFont.UpdateDirectory;
     1890var
     1891  NbFiles: integer;
     1892  SearchRec: TSearchRec;
     1893  Info: TBGRAGlyphsInfo;
     1894  Fullname: string;
     1895begin
     1896  NbFiles := 0;
     1897  FDirectoryContent := nil;
     1898  if FDirectory = '' then exit;
     1899  if (length(FDirectory) > 0) and not (FDirectory[length(FDirectory)] in AllowDirectorySeparators) then
     1900    FDirectory += DirectorySeparator;
     1901  if FindFirstUTF8(FDirectory +'*.glyphs', faAnyFile, SearchRec) = 0 then
     1902  repeat
     1903    if (faDirectory or faVolumeId or faSysFile) and SearchRec.Attr = 0 then
     1904    begin
     1905      Fullname := FDirectory+SearchRec.Name;
     1906      Info := LoadGlyphsInfo(Fullname);
     1907      if (info.Name <> '') and (info.NbGlyphs > 0) then
     1908      begin
     1909        if NbFiles = length(FDirectoryContent) then
     1910          setlength(FDirectoryContent,2*NbFiles+1);
     1911        FDirectoryContent[NbFiles].Filename:= Fullname;
     1912        FDirectoryContent[NbFiles].FontName:= info.Name;
     1913        FDirectoryContent[NbFiles].FontStyle:= info.Style;
     1914        inc(NbFiles);
     1915      end;
     1916    end;
     1917  until FindNext(SearchRec) <> 0;
     1918  SetLength(FDirectoryContent,NbFiles);
     1919end;
     1920
     1921function TBGRAVectorizedFont.LoadGlyphsInfo(AFilenameUTF8: string): TBGRAGlyphsInfo;
     1922var Stream: TFileStreamUTF8;
     1923  twHeader: TBGRACustomTypeWriterHeader;
     1924  vfHeader: TBGRAVectorizedFontHeader;
     1925begin
     1926  result.Name := '';
     1927  result.NbGlyphs := 0;
     1928  result.Style := [];
     1929  Stream := nil;
     1930  try
     1931    Stream := TFileStreamUTF8.Create(AFilenameUTF8,fmOpenRead);
     1932    Stream.Position := 4;
     1933    twHeader := ReadCustomTypeWriterHeader(Stream);
     1934    result.NbGlyphs := twHeader.NbGlyphs;
     1935    if twHeader.HeaderName = HeaderName then
     1936    begin
     1937      vfHeader := ReadVectorizedFontHeader(Stream);
     1938      result.Name := vfHeader.Name;
     1939      result.Style:= vfHeader.Style;
     1940    end;
     1941  except
     1942    on ex:exception do
     1943    begin
     1944
     1945    end;
     1946  end;
     1947  Stream.Free;
    12661948end;
    12671949
     
    12711953begin
    12721954  Result:=inherited GetGlyph(AIdentifier);
    1273   if (result = nil) and (FResolution > 0) then
     1955  if (result = nil) and (FResolution > 0) and (FFont <> nil) then
    12741956  begin
    12751957    g := TBGRAPolygonalGlyph.Create(AIdentifier);
     
    12941976
    12951977procedure TBGRAVectorizedFont.DefaultWordBreakHandler(var ABefore,AAfter: string);
    1296 var p: integer;
    1297 begin
    1298   if (AAfter <> '') and (ABefore <> '') and (AAfter[1]<> ' ') and (ABefore[length(ABefore)] <> ' ') then
    1299   begin
    1300     p := length(ABefore);
    1301     while (p > 1) and (ABefore[p-1] <> ' ') do dec(p);
    1302     if p > 1 then //can put the word after
    1303     begin
    1304       AAfter := copy(ABefore,p,length(ABefore)-p+1)+AAfter;
    1305       ABefore := copy(ABefore,1,p-1);
    1306     end else
    1307     begin //cannot put the word after, so before
    1308 
    1309     end;
    1310   end;
    1311   while (ABefore <> '') and (ABefore[length(ABefore)] =' ') do delete(ABefore,length(ABefore),1);
    1312   while (AAfter <> '') and (AAfter[1] =' ') do delete(AAfter,1,1);
     1978begin
     1979  BGRADefaultWordBreakHandler(ABefore,AAfter);
     1980end;
     1981
     1982procedure TBGRAVectorizedFont.Init(AVectorize: boolean);
     1983begin
     1984  FName := 'Arial';
     1985  FStyle := [];
     1986  FFontMatrix := AffineMatrixIdentity;
     1987  FOrientation := 0;
     1988  FResolution := 100;
     1989  FFontEmHeightRatio := 1;
     1990  FFontEmHeightRatioComputed := false;
     1991  if AVectorize then
     1992    FFont := TFont.Create
     1993  else
     1994    FFont := nil;
     1995  FBuffer := BGRABitmapFactory.Create;
     1996  FFullHeight := 20;
     1997  FItalicSlope := 0;
     1998  UpdateFont;
     1999  UpdateMatrix;
     2000  FWordBreakHandler:= nil;
     2001end;
     2002
     2003function TBGRAVectorizedFont.CustomHeaderSize: integer;
     2004begin
     2005  Result:= (inherited CustomHeaderSize) + 4+length(FName)+4 + sizeof(single) + 4 + 5*4;
     2006end;
     2007
     2008procedure TBGRAVectorizedFont.WriteCustomHeader(AStream: TStream);
     2009var metric: TFontPixelMetric;
     2010begin
     2011  inherited WriteCustomHeader(AStream);
     2012  WinWriteLongint(AStream, length(FName));
     2013  AStream.Write(FName[1],length(FName));
     2014  WinWriteLongint(AStream, integer(FStyle));
     2015  WinWriteSingle(AStream, FontEmHeightRatio);
     2016  WinWriteLongint(AStream, Resolution);
     2017  metric := FontPixelMetric;
     2018  WinWriteLongint(AStream, metric.Baseline);
     2019  WinWriteLongint(AStream, metric.xLine);
     2020  WinWriteLongint(AStream, metric.CapLine);
     2021  WinWriteLongint(AStream, metric.DescentLine);
     2022  WinWriteLongint(AStream, metric.Lineheight);
     2023end;
     2024
     2025procedure TBGRAVectorizedFont.ReadAdditionalHeader(AStream: TStream);
     2026var Header: TBGRAVectorizedFontHeader;
     2027begin
     2028  inherited ReadAdditionalHeader(AStream);
     2029  Header := ReadVectorizedFontHeader(AStream);
     2030  FName := Header.Name;
     2031  FStyle := Header.Style;
     2032  if header.EmHeightRatio <> 0 then
     2033  begin
     2034    FFontEmHeightRatio := Header.EmHeightRatio;
     2035    FFontEmHeightRatioComputed := true;
     2036  end else
     2037  begin
     2038    FFontEmHeightRatio := 1;
     2039    FFontEmHeightRatioComputed := false;
     2040  end;
     2041  FFontPixelMetric := Header.PixelMetric;
     2042  FFontPixelMetricComputed := True;
     2043  if FFont = nil then
     2044    FResolution := Header.Resolution;
     2045end;
     2046
     2047function TBGRAVectorizedFont.ReadVectorizedFontHeader(AStream: TStream): TBGRAVectorizedFontHeader;
     2048var lNameLength: integer;
     2049begin
     2050  lNameLength := WinReadLongint(AStream);
     2051  setlength(result.Name, lNameLength);
     2052  AStream.Read(result.Name[1],length(result.Name));
     2053  result.Style := TFontStyles(WinReadLongint(AStream));
     2054  result.EmHeightRatio:= WinReadSingle(AStream);
     2055  result.Resolution := WinReadLongint(AStream);
     2056  result.PixelMetric.Baseline := WinReadLongint(AStream);
     2057  result.PixelMetric.xLine := WinReadLongint(AStream);
     2058  result.PixelMetric.CapLine := WinReadLongint(AStream);
     2059  result.PixelMetric.DescentLine := WinReadLongint(AStream);
     2060  result.PixelMetric.Lineheight := WinReadLongint(AStream);
     2061  result.PixelMetric.Defined := result.PixelMetric.Lineheight > 0;
     2062end;
     2063
     2064function TBGRAVectorizedFont.HeaderName: string;
     2065begin
     2066  Result:= 'TBGRAVectorizedFont';
     2067end;
     2068
     2069procedure TBGRAVectorizedFont.SetDirectory(const AValue: string);
     2070begin
     2071  if Trim(AValue) = Trim(FDirectory) then exit;
     2072  FDirectory := Trim(AValue);
     2073  UpdateDirectory;
     2074  UpdateFont;
    13132075end;
    13142076
  • GraphicTest/Packages/bgrabitmap/bgrawinbitmap.pas

    r452 r472  
    7070procedure TWinBitmapTracker.Changed(Sender: TObject);
    7171begin
    72   FUser.AlphaCorrectionNeeded;
     72  if FUser <> nil then
     73    FUser.AlphaCorrectionNeeded;
    7374  inherited Changed(Sender);
    7475end;
  • GraphicTest/Packages/bgrabitmap/blendpixelinline.inc

    r452 r472  
    530530  aw := GammaExpansionTab[a];
    531531  bw := GammaExpansionTab[b];
     532  {$HINTS OFF}
    532533  Result := GammaCompressionTab[aw+bw-(longword(aw)*longword(bw) shr 15)];
     534  {$HINTS ON}
    533535end;
    534536
     
    562564function ByteLinearExclusionInline(a, b: byte): byte; inline;
    563565begin
     566  {$HINTS OFF}
    564567  Result := a+b-(a*b shr 7);
     568  {$HINTS ON}
    565569end;
    566570
  • GraphicTest/Packages/bgrabitmap/blurfast.inc

    r452 r472  
    11
    22var
    3   blurRow: array of cardinal;
     3  blurRow: array of UInt32or64;
    44
    55  { Compute weights of pixels in a row }
     
    1919var
    2020  srcDelta,
    21   weightShift: integer;
     21  verticalWeightShift, horizontalWeightShift: integer;
    2222
    2323  { Compute blur result in a vertical direction }
     
    3636      aDiv += w;
    3737
    38       aw := aw shr weightShift;
     38      aw := aw shr verticalWeightShift;
    3939      {$hints off}
    4040      sumR += c.red*aw;
     
    5151  sumStartIndex,curIndex: integer;
    5252  total: TRowSum;
     53  extendedTotal: TExtendedRowSum;
    5354  yb,xb,xs,ys1,ys2,x: integer;
    5455  w: cardinal;
    5556  pdest: PBGRAPixel;
    5657  bmpWidth,bmpHeight : integer;
    57   radiusSquare: integer;
     58  accumulationFactor: double;
    5859  bounds: TRect;
    5960
     
    6162  if radius = 0 then
    6263  begin
    63     result := bmp.Duplicate;
     64    ADestination.PutImage(0,0,bmp,dmSet);
    6465    exit;
    6566  end;
     
    6768  bmpHeight := bmp.Height;
    6869  //create output
    69   result := bmp.NewBitmap(bmpWidth,bmpHeight);
     70  if (ADestination.Width <> bmp.Width) or (ADestination.Height <> bmp.Height) then
     71    raise exception.Create('Dimension mismatch');
    7072  bounds := bmp.GetImageBounds;
    71   if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then
    72     exit;
     73  if IsRectEmpty(bounds) then exit;
    7374  bounds.Left   := max(0, bounds.Left - radius);
    7475  bounds.Top    := max(0, bounds.Top - radius);
    7576  bounds.Right  := min(bmp.Width, bounds.Right + radius);
    7677  bounds.Bottom := min(bmp.Height, bounds.Bottom + radius);
    77 
    78   radiusSquare := sqr((radius+1)*(radius+2) div 2);
    79   weightShift := 0;
    80   while radiusSquare > 16384 do //18496 do
    81   begin
    82     radiusSquare := radiusSquare shr 1;
    83     inc(weightShift);
     78  if not IntersectRect(bounds,bounds,ABounds) then exit;
     79
     80  accumulationFactor := (radius+2)*(radius+1) div 2 + (radius+1)*radius div 2;
     81  verticalWeightShift := 0;
     82  while accumulationFactor > (high(UInt32or64) shr 16) + 1 do
     83  begin
     84    inc(verticalWeightShift);
     85    accumulationFactor *= 0.5;
     86  end;
     87  horizontalWeightShift:= 0;
     88  accumulationFactor *= ((radius+2)*(radius+1) div 2 + (radius+1)*radius div 2);
     89  while accumulationFactor > (high(UInt32or64) shr 16) + 1 do
     90  begin
     91    inc(horizontalWeightShift);
     92    accumulationFactor *= 0.5;
    8493  end;
    8594  ComputeBlurRow;
     
    92101  for yb := bounds.top to bounds.bottom-1 do
    93102  begin
     103    if (ACheckShouldStop <> nil) and ACheckShouldStop(yb) then break;
    94104    //evalute available vertical range
    95105    if yb - radius < 0 then
     
    114124    sumStartIndex := 0;
    115125
    116     pdest := result.scanline[yb]+bounds.left;
     126    pdest := ADestination.scanline[yb]+bounds.left;
    117127    for xb := bounds.left to bounds.right-1 do
    118128    begin
    119129      //add vertical rows
    120       {$hints off}
    121       fillchar(total,sizeof(total),0);
    122       {$hints on}
    123130      curIndex:= sumStartIndex;
    124       for xs := 0 to high(sums) do
    125       with sums[curIndex] do
    126       begin
    127         w := blurRow[xs];
    128         total.sumA += sumA*w;
    129         total.aDiv += aDiv*w;
    130         total.sumR += sumR*w;
    131         total.sumG += sumG*w;
    132         total.sumB += sumB*w;
    133         total.rgbDiv += rgbDiv*w;
    134         inc(curIndex);
    135         if curIndex = length(sums) then curIndex := 0;
     131      if horizontalWeightShift > 4 then
     132      begin //we don't want to loose too much precision
     133        {$hints off}
     134        fillchar(extendedTotal,sizeof(extendedTotal),0);
     135        {$hints on}
     136        for xs := 0 to high(sums) do
     137        with sums[curIndex] do
     138        begin
     139          w := blurRow[xs];
     140          extendedTotal.sumA += TExtendedRowValue(sumA)*w;
     141          extendedTotal.aDiv += TExtendedRowValue(aDiv)*w;
     142          extendedTotal.sumR += TExtendedRowValue(sumR)*w;
     143          extendedTotal.sumG += TExtendedRowValue(sumG)*w;
     144          extendedTotal.sumB += TExtendedRowValue(sumB)*w;
     145          extendedTotal.rgbDiv += TExtendedRowValue(rgbDiv)*w;
     146          inc(curIndex);
     147          if curIndex = length(sums) then curIndex := 0;
     148        end;
     149        if (extendedTotal.aDiv > 0) and (extendedTotal.rgbDiv > 0) then
     150          pdest^:= ComputeExtendedAverage(extendedTotal)
     151        else
     152          pdest^:= BGRAPixelTransparent;
     153      end else
     154      if horizontalWeightShift > 0 then
     155      begin //lossy but efficient way
     156        {$hints off}
     157        fillchar(total,sizeof(total),0);
     158        {$hints on}
     159        for xs := 0 to high(sums) do
     160        with sums[curIndex] do
     161        begin
     162          w := blurRow[xs];
     163          total.sumA += sumA*w shr horizontalWeightShift;
     164          total.aDiv += aDiv*w shr horizontalWeightShift;
     165          total.sumR += sumR*w shr horizontalWeightShift;
     166          total.sumG += sumG*w shr horizontalWeightShift;
     167          total.sumB += sumB*w shr horizontalWeightShift;
     168          total.rgbDiv += rgbDiv*w shr horizontalWeightShift;
     169          inc(curIndex);
     170          if curIndex = length(sums) then curIndex := 0;
     171        end;
     172        if (total.aDiv > 0) and (total.rgbDiv > 0) then
     173          pdest^:= ComputeClampedAverage(total)
     174        else
     175          pdest^:= BGRAPixelTransparent;
     176      end else
     177      begin //normal way
     178        {$hints off}
     179        fillchar(total,sizeof(total),0);
     180        {$hints on}
     181        for xs := 0 to high(sums) do
     182        with sums[curIndex] do
     183        begin
     184          w := blurRow[xs];
     185          total.sumA += sumA*w;
     186          total.aDiv += aDiv*w;
     187          total.sumR += sumR*w;
     188          total.sumG += sumG*w;
     189          total.sumB += sumB*w;
     190          total.rgbDiv += rgbDiv*w;
     191          inc(curIndex);
     192          if curIndex = length(sums) then curIndex := 0;
     193        end;
     194        if (total.aDiv > 0) and (total.rgbDiv > 0) then
     195          pdest^:= ComputeAverage(total)
     196        else
     197          pdest^:= BGRAPixelTransparent;
    136198      end;
    137       if (total.aDiv > 0) and (total.rgbDiv > 0) then
    138         pdest^:= ComputeAverage(total)
    139       else
    140         pdest^:= BGRAPixelTransparent;
    141199      inc(pdest);
    142200      //shift vertical rows
     
    149207    end;
    150208  end;
     209  ADestination.InvalidateBitmap;
    151210end;
    152211
  • GraphicTest/Packages/bgrabitmap/blurnormal.inc

    r452 r472  
    108108    //evaluate required bounds taking blur radius into acount
    109109    bounds := bmp.GetImageBounds;
    110     if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then
     110    if IsRectEmpty(bounds) then
    111111    begin
    112112      result := false;
     
    117117    bounds.Right  := min(bmp.Width, bounds.Right + maskWidth - 1 - blurOfs.X);
    118118    bounds.Bottom := min(bmp.Height, bounds.Bottom + maskHeight - 1 - blurOfs.Y);
     119    if not IntersectRect(bounds, bounds, ABounds) then
     120    begin
     121      result := false;
     122      exit;
     123    end;
    119124
    120125    //init scanlines
     
    157162  LoadMask;
    158163
    159   Result := bmp.NewBitmap(bmp.Width, bmp.Height);
     164  if (ADestination.Width <> bmp.Width) or (ADestination.Height <> bmp.Height) then
     165    raise exception.Create('Dimension mismatch');
     166
    160167  if not PrepareScan then exit; //nothing to do
    161168
     
    164171  for yb := bounds.Top to bounds.Bottom - 1 do
    165172  begin
    166     pdest := Result.ScanLine[yb] + bounds.Left;
     173    if (ACheckShouldStop <> nil) and ACheckShouldStop(yb) then break;
     174    pdest := ADestination.ScanLine[yb] + bounds.Left;
    167175    //compute vertical range
    168176    mindy := max(-blurOfs.Y, -yb);
     
    242250    ShiftScan(yb-blurOfs.Y+maskHeight);
    243251  end;
    244   Result.InvalidateBitmap;
     252  ADestination.InvalidateBitmap;
    245253end;
    246254{$undef PARAM_MASKSHIFT}
  • GraphicTest/Packages/bgrabitmap/csscolorconst.inc

    r452 r472  
     1{$IFDEF INCLUDE_COLOR_CONST}
     2{$UNDEF INCLUDE_COLOR_CONST}
    13const
     4  //VGA colors
     5  VGABlack: TBGRAPixel = (blue: 0; green: 0; red: 0; alpha: 255);
     6  VGAGray: TBGRAPixel = (blue: 128; green: 128; red: 128; alpha: 255);
     7  VGASilver: TBGRAPixel = (blue: 192; green: 192; red: 192; alpha: 255);
     8  VGAWhite: TBGRAPixel = (blue: 255; green: 255; red: 255; alpha: 255);
     9  VGAMaroon: TBGRAPixel = (blue: 0; green: 0; red: 128; alpha: 255);
     10  VGARed: TBGRAPixel = (blue: 0; green: 0; red: 255; alpha: 255);
     11  VGAPurple: TBGRAPixel = (blue: 128; green: 0; red: 128; alpha: 255);
     12  VGAFuchsia: TBGRAPixel = (blue: 255; green: 0; red: 255; alpha: 255);
     13  VGAGreen: TBGRAPixel = (blue: 0; green: 128; red: 0; alpha: 255);
     14  VGALime: TBGRAPixel = (blue: 0; green: 255; red: 0; alpha: 255);
     15  VGAOlive: TBGRAPixel = (blue: 0; green: 128; red: 128; alpha: 255);
     16  VGAYellow: TBGRAPixel = (blue: 0; green: 255; red: 255; alpha: 255);
     17  VGANavy: TBGRAPixel = (blue: 128; green: 0; red: 0; alpha: 255);
     18  VGABlue: TBGRAPixel = (blue: 255; green: 0; red: 0; alpha: 255);
     19  VGATeal: TBGRAPixel = (blue: 128; green: 128; red: 0; alpha: 255);
     20  VGAAqua: TBGRAPixel = (blue: 255; green: 255; red: 0; alpha: 255);
     21
    222  //Red colors
    323  CSSIndianRed: TBGRAPixel = (blue: 92; green: 92; red: 205; alpha: 255);
     
    159179  CSSDarkSlateGray: TBGRAPixel = (blue: 79; green: 79; red: 47; alpha: 255);
    160180  CSSBlack: TBGRAPixel = (blue: 0; green: 0; red: 0; alpha: 255);
    161 
     181{$ENDIF}
     182{$IFDEF INCLUDE_COLOR_LIST}
     183{$UNDEF INCLUDE_COLOR_LIST}
     184  VGAColors := TBGRAColorList.Create;
     185  VGAColors.Add('Black',VGABlack);
     186  VGAColors.Add('Gray',VGAGray);
     187  VGAColors.Add('Silver',VGASilver);
     188  VGAColors.Add('White',VGAWhite);
     189  VGAColors.Add('Maroon',VGAMaroon);
     190  VGAColors.Add('Red',VGARed);
     191  VGAColors.Add('Purple',VGAPurple);
     192  VGAColors.Add('Fuchsia',VGAFuchsia);
     193  VGAColors.Add('Green',VGAGreen);
     194  VGAColors.Add('Lime',VGALime);
     195  VGAColors.Add('Olive',VGAOlive);
     196  VGAColors.Add('Yellow',VGAYellow);
     197  VGAColors.Add('Navy',VGANavy);
     198  VGAColors.Add('Blue',VGABlue);
     199  VGAColors.Add('Teal',VGATeal);
     200  VGAColors.Add('Aqua',VGAAqua);
     201  VGAColors.Finished;
     202
     203  CSSColors := TBGRAColorList.Create;
     204  CSSColors.Add('AliceBlue',CSSAliceBlue);
     205  CSSColors.Add('AntiqueWhite',CSSAntiqueWhite);
     206  CSSColors.Add('Aqua',CSSAqua);
     207  CSSColors.Add('Aquamarine',CSSAquamarine);
     208  CSSColors.Add('Azure',CSSAzure);
     209  CSSColors.Add('Beige',CSSBeige);
     210  CSSColors.Add('Bisque',CSSBisque);
     211  CSSColors.Add('Black',CSSBlack);
     212  CSSColors.Add('BlanchedAlmond',CSSBlanchedAlmond);
     213  CSSColors.Add('Blue',CSSBlue);
     214  CSSColors.Add('BlueViolet',CSSBlueViolet);
     215  CSSColors.Add('Brown',CSSBrown);
     216  CSSColors.Add('BurlyWood',CSSBurlyWood);
     217  CSSColors.Add('CadetBlue',CSSCadetBlue);
     218  CSSColors.Add('Chartreuse',CSSChartreuse);
     219  CSSColors.Add('Chocolate',CSSChocolate);
     220  CSSColors.Add('Coral',CSSCoral);
     221  CSSColors.Add('CornflowerBlue',CSSCornflowerBlue);
     222  CSSColors.Add('Cornsilk',CSSCornsilk);
     223  CSSColors.Add('Crimson',CSSCrimson);
     224  CSSColors.Add('Cyan',CSSCyan);
     225  CSSColors.Add('DarkBlue',CSSDarkBlue);
     226  CSSColors.Add('DarkCyan',CSSDarkCyan);
     227  CSSColors.Add('DarkGoldenrod',CSSDarkGoldenrod);
     228  CSSColors.Add('DarkGray',CSSDarkGray);
     229  CSSColors.Add('DarkGreen',CSSDarkGreen);
     230  CSSColors.Add('DarkKhaki',CSSDarkKhaki);
     231  CSSColors.Add('DarkMagenta',CSSDarkMagenta);
     232  CSSColors.Add('DarkOliveGreen',CSSDarkOliveGreen);
     233  CSSColors.Add('DarkOrange',CSSDarkOrange);
     234  CSSColors.Add('DarkOrchid',CSSDarkOrchid);
     235  CSSColors.Add('DarkRed',CSSDarkRed);
     236  CSSColors.Add('DarkSalmon',CSSDarkSalmon);
     237  CSSColors.Add('DarkSeaGreen',CSSDarkSeaGreen);
     238  CSSColors.Add('DarkSlateBlue',CSSDarkSlateBlue);
     239  CSSColors.Add('DarkSlateGray',CSSDarkSlateGray);
     240  CSSColors.Add('DarkTurquoise',CSSDarkTurquoise);
     241  CSSColors.Add('DarkViolet',CSSDarkViolet);
     242  CSSColors.Add('DeepPink',CSSDeepPink);
     243  CSSColors.Add('DeepSkyBlue',CSSDeepSkyBlue);
     244  CSSColors.Add('DimGray',CSSDimGray);
     245  CSSColors.Add('DodgerBlue',CSSDodgerBlue);
     246  CSSColors.Add('FireBrick',CSSFireBrick);
     247  CSSColors.Add('FloralWhite',CSSFloralWhite);
     248  CSSColors.Add('ForestGreen',CSSForestGreen);
     249  CSSColors.Add('Fuchsia',CSSFuchsia);
     250  CSSColors.Add('Gainsboro',CSSGainsboro);
     251  CSSColors.Add('GhostWhite',CSSGhostWhite);
     252  CSSColors.Add('Gold',CSSGold);
     253  CSSColors.Add('Goldenrod',CSSGoldenrod);
     254  CSSColors.Add('Gray',CSSGray);
     255  CSSColors.Add('Green',CSSGreen);
     256  CSSColors.Add('GreenYellow',CSSGreenYellow);
     257  CSSColors.Add('Honeydew',CSSHoneydew);
     258  CSSColors.Add('HotPink',CSSHotPink);
     259  CSSColors.Add('IndianRed',CSSIndianRed);
     260  CSSColors.Add('Indigo',CSSIndigo);
     261  CSSColors.Add('Ivory',CSSIvory);
     262  CSSColors.Add('Khaki',CSSKhaki);
     263  CSSColors.Add('Lavender',CSSLavender);
     264  CSSColors.Add('LavenderBlush',CSSLavenderBlush);
     265  CSSColors.Add('LawnGreen',CSSLawnGreen);
     266  CSSColors.Add('LemonChiffon',CSSLemonChiffon);
     267  CSSColors.Add('LightBlue',CSSLightBlue);
     268  CSSColors.Add('LightCoral',CSSLightCoral);
     269  CSSColors.Add('LightCyan',CSSLightCyan);
     270  CSSColors.Add('LightGoldenrodYellow',CSSLightGoldenrodYellow);
     271  CSSColors.Add('LightGray',CSSLightGray);
     272  CSSColors.Add('LightGreen',CSSLightGreen);
     273  CSSColors.Add('LightPink',CSSLightPink);
     274  CSSColors.Add('LightSalmon',CSSLightSalmon);
     275  CSSColors.Add('LightSeaGreen',CSSLightSeaGreen);
     276  CSSColors.Add('LightSkyBlue',CSSLightSkyBlue);
     277  CSSColors.Add('LightSlateGray',CSSLightSlateGray);
     278  CSSColors.Add('LightSteelBlue',CSSLightSteelBlue);
     279  CSSColors.Add('LightYellow',CSSLightYellow);
     280  CSSColors.Add('Lime',CSSLime);
     281  CSSColors.Add('LimeGreen',CSSLimeGreen);
     282  CSSColors.Add('Linen',CSSLinen);
     283  CSSColors.Add('Magenta',CSSMagenta);
     284  CSSColors.Add('Maroon',CSSMaroon);
     285  CSSColors.Add('MediumAquamarine',CSSMediumAquamarine);
     286  CSSColors.Add('MediumBlue',CSSMediumBlue);
     287  CSSColors.Add('MediumOrchid',CSSMediumOrchid);
     288  CSSColors.Add('MediumPurple',CSSMediumPurple);
     289  CSSColors.Add('MediumSeaGreen',CSSMediumSeaGreen);
     290  CSSColors.Add('MediumSlateBlue',CSSMediumSlateBlue);
     291  CSSColors.Add('MediumSpringGreen',CSSMediumSpringGreen);
     292  CSSColors.Add('MediumTurquoise',CSSMediumTurquoise);
     293  CSSColors.Add('MediumVioletRed',CSSMediumVioletRed);
     294  CSSColors.Add('MidnightBlue',CSSMidnightBlue);
     295  CSSColors.Add('MintCream',CSSMintCream);
     296  CSSColors.Add('MistyRose',CSSMistyRose);
     297  CSSColors.Add('Moccasin',CSSMoccasin);
     298  CSSColors.Add('NavajoWhite',CSSNavajoWhite);
     299  CSSColors.Add('Navy',CSSNavy);
     300  CSSColors.Add('OldLace',CSSOldLace);
     301  CSSColors.Add('Olive',CSSOlive);
     302  CSSColors.Add('OliveDrab',CSSOliveDrab);
     303  CSSColors.Add('Orange',CSSOrange);
     304  CSSColors.Add('OrangeRed',CSSOrangeRed);
     305  CSSColors.Add('Orchid',CSSOrchid);
     306  CSSColors.Add('PaleGoldenrod',CSSPaleGoldenrod);
     307  CSSColors.Add('PaleGreen',CSSPaleGreen);
     308  CSSColors.Add('PaleTurquoise',CSSPaleTurquoise);
     309  CSSColors.Add('PaleVioletRed',CSSPaleVioletRed);
     310  CSSColors.Add('PapayaWhip',CSSPapayaWhip);
     311  CSSColors.Add('PeachPuff',CSSPeachPuff);
     312  CSSColors.Add('Peru',CSSPeru);
     313  CSSColors.Add('Pink',CSSPink);
     314  CSSColors.Add('Plum',CSSPlum);
     315  CSSColors.Add('PowderBlue',CSSPowderBlue);
     316  CSSColors.Add('Purple',CSSPurple);
     317  CSSColors.Add('Red',CSSRed);
     318  CSSColors.Add('RosyBrown',CSSRosyBrown);
     319  CSSColors.Add('RoyalBlue',CSSRoyalBlue);
     320  CSSColors.Add('SaddleBrown',CSSSaddleBrown);
     321  CSSColors.Add('Salmon',CSSSalmon);
     322  CSSColors.Add('SandyBrown',CSSSandyBrown);
     323  CSSColors.Add('SeaGreen',CSSSeaGreen);
     324  CSSColors.Add('Seashell',CSSSeashell);
     325  CSSColors.Add('Sienna',CSSSienna);
     326  CSSColors.Add('Silver',CSSSilver);
     327  CSSColors.Add('SkyBlue',CSSSkyBlue);
     328  CSSColors.Add('SlateBlue',CSSSlateBlue);
     329  CSSColors.Add('SlateGray',CSSSlateGray);
     330  CSSColors.Add('Snow',CSSSnow);
     331  CSSColors.Add('SpringGreen',CSSSpringGreen);
     332  CSSColors.Add('SteelBlue',CSSSteelBlue);
     333  CSSColors.Add('Tan',CSSTan);
     334  CSSColors.Add('Teal',CSSTeal);
     335  CSSColors.Add('Thistle',CSSThistle);
     336  CSSColors.Add('Tomato',CSSTomato);
     337  CSSColors.Add('Turquoise',CSSTurquoise);
     338  CSSColors.Add('Violet',CSSViolet);
     339  CSSColors.Add('Wheat',CSSWheat);
     340  CSSColors.Add('White',CSSWhite);
     341  CSSColors.Add('WhiteSmoke',CSSWhiteSmoke);
     342  CSSColors.Add('Yellow',CSSYellow);
     343  CSSColors.Add('YellowGreen',CSSYellowGreen);
     344  CSSColors.Finished;
     345{$ENDIF}
     346
  • GraphicTest/Packages/bgrabitmap/filldensitysegment256.inc

    r452 r472  
    1313
    1414      if ix1 = ix2 then
    15         (density + (ix1 - minx))^ += round((x2 - x1)*256)
     15        (density + (ix1 - minx))^ += round((x2-ix2)*256) - round((x1-ix1)*256)
    1616      else
    1717      begin
    18         (density + (ix1 - minx))^ += round((1 - (x1 - ix1))*256);
     18        (density + (ix1 - minx))^ += 256 - round((x1 - ix1)*256);
    1919        if (ix2 <= maxx) then
    2020          (density + (ix2 - minx))^ += round((x2 - ix2)*256);
  • GraphicTest/Packages/bgrabitmap/lightingclasses3d.inc

    r452 r472  
    55  TBGRAMaterial3D = class(TInterfacedObject, IBGRAMaterial3D)
    66  private
    7     FDiffuseColorInt: TColorInt65536;
     7    FName: string;
     8    FTexture: IBGRAScanner;
     9    FAutoSimpleColor,FAutoAmbiantColor,FAutoDiffuseColor,FAutoSpecularColor: boolean;
     10    FSimpleColorInt, FAmbiantColorInt, FDiffuseColorInt: TColorInt65536;
    811    FDiffuseLightness: integer;
     12    FTextureZoom: TPointF;
     13
    914    FSpecularColorInt: TColorInt65536;
    10     FAutoDiffuseColor,FAutoSpecularColor: boolean;
    1115    FSpecularIndex: integer;
    1216    FSpecularOn: boolean;
     17
    1318    FSaturationLowF: single;
    1419    FSaturationHighF: single;
     
    2126
    2227    procedure UpdateSpecular;
     28    procedure UpdateSimpleColor;
    2329    procedure ComputePowerTable;
    2430  public
     
    2632    destructor Destroy; override;
    2733
     34    function GetAutoAmbiantColor: boolean;
    2835    function GetAutoDiffuseColor: boolean;
    2936    function GetAutoSpecularColor: boolean;
     37    function GetAutoSimpleColor: boolean;
     38    function GetAmbiantAlpha: byte;
     39    function GetAmbiantColor: TBGRAPixel;
     40    function GetAmbiantColorF: TColorF;
     41    function GetAmbiantColorInt: TColorInt65536;
     42    function GetDiffuseAlpha: byte;
    3043    function GetDiffuseColor: TBGRAPixel;
    3144    function GetDiffuseColorF: TColorF;
     
    3851    function GetSaturationHigh: single;
    3952    function GetSaturationLow: single;
     53    function GetSimpleAlpha: byte;
     54    function GetSimpleColor: TBGRAPixel;
     55    function GetSimpleColorF: TColorF;
     56    function GetSimpleColorInt: TColorInt65536;
     57    function GetTexture: IBGRAScanner;
     58    function GetTextureZoom: TPointF;
     59    procedure SetAutoAmbiantColor(const AValue: boolean);
    4060    procedure SetAutoDiffuseColor(const AValue: boolean);
    4161    procedure SetAutoSpecularColor(const AValue: boolean);
     62    procedure SetAmbiantAlpha(AValue: byte);
     63    procedure SetAmbiantColor(const AValue: TBGRAPixel);
     64    procedure SetAmbiantColorF(const AValue: TColorF);
     65    procedure SetAmbiantColorInt(const AValue: TColorInt65536);
     66    procedure SetDiffuseAlpha(AValue: byte);
    4267    procedure SetDiffuseColor(const AValue: TBGRAPixel);
    4368    procedure SetDiffuseColorF(const AValue: TColorF);
     
    5075    procedure SetSaturationHigh(const AValue: single);
    5176    procedure SetSaturationLow(const AValue: single);
     77    procedure SetSimpleAlpha(AValue: byte);
     78    procedure SetSimpleColor(AValue: TBGRAPixel);
     79    procedure SetSimpleColorF(AValue: TColorF);
     80    procedure SetSimpleColorInt(AValue: TColorInt65536);
     81    procedure SetTexture(AValue: IBGRAScanner);
     82    procedure SetTextureZoom(AValue: TPointF);
     83    function GetName: string;
     84    procedure SetName(const AValue: string);
    5285
    5386    function GetSpecularOn: boolean;
     
    6396procedure TBGRAMaterial3D.UpdateSpecular;
    6497begin
     98  FAutoSpecularColor := (FSpecularColorInt.r = 65536) and (FSpecularColorInt.g = 65536) and (FSpecularColorInt.b = 65536) and (FSpecularColorInt.a = 65536);
    6599  FSpecularOn := (FSpecularIndex > 0) and ((FSpecularColorInt.r <> 0) or (FSpecularColorInt.g <> 0) or (FSpecularColorInt.b <> 0) or
    66100                                            FAutoSpecularColor);
     101end;
     102
     103procedure TBGRAMaterial3D.UpdateSimpleColor;
     104begin
     105  FSimpleColorInt := (FAmbiantColorInt+FDiffuseColorInt)*32768;
     106  FAutoSimpleColor := (FSimpleColorInt.r = 65536) and (FSimpleColorInt.g = 65536) and (FSimpleColorInt.b = 65536) and (FSimpleColorInt.a = 65536);
    67107end;
    68108
     
    91131constructor TBGRAMaterial3D.Create;
    92132begin
     133  SetAmbiantColorInt(ColorInt65536(65536,65536,65536));
    93134  SetDiffuseColorInt(ColorInt65536(65536,65536,65536));
    94   FAutoDiffuseColor:= True;
    95   FSpecularColorInt := ColorInt65536(0,0,0);
    96   FAutoSpecularColor:= True;
    97135  FSpecularIndex := 10;
    98   FSpecularOn := false;
     136  SetSpecularColorInt(ColorInt65536(0,0,0));
    99137  FLightThroughFactor:= 0;
    100138  SetSaturationLow(2);
    101139  SetSaturationHigh(3);
    102140
     141  FTexture := nil;
     142  FTextureZoom := PointF(1,1);
     143
    103144  FPowerTableSize := 128;
    104145  FPowerTableSizeF := FPowerTableSize;
     
    111152end;
    112153
     154function TBGRAMaterial3D.GetAutoAmbiantColor: boolean;
     155begin
     156  result := FAutoAmbiantColor;
     157end;
     158
     159procedure TBGRAMaterial3D.SetDiffuseAlpha(AValue: byte);
     160begin
     161  if AValue = 0 then
     162    FDiffuseColorInt.a := 0
     163  else
     164    FDiffuseColorInt.a := AValue*257+1;
     165  UpdateSimpleColor;
     166end;
     167
    113168function TBGRAMaterial3D.GetAutoDiffuseColor: boolean;
    114169begin
     
    121176end;
    122177
     178function TBGRAMaterial3D.GetAutoSimpleColor: boolean;
     179begin
     180  result := FAutoSimpleColor;
     181end;
     182
     183function TBGRAMaterial3D.GetAmbiantAlpha: byte;
     184var v: integer;
     185begin
     186  if FAmbiantColorInt.a < 128 then
     187    result := 0
     188  else
     189  begin
     190    v := (FAmbiantColorInt.a-128) shr 8;
     191    if v > 255 then v := 255;
     192    result := v;
     193  end;
     194end;
     195
     196function TBGRAMaterial3D.GetAmbiantColor: TBGRAPixel;
     197begin
     198  result := ColorIntToBGRA(FAmbiantColorInt);
     199end;
     200
     201function TBGRAMaterial3D.GetAmbiantColorF: TColorF;
     202begin
     203  result := ColorInt65536ToColorF(FAmbiantColorInt);
     204end;
     205
     206function TBGRAMaterial3D.GetAmbiantColorInt: TColorInt65536;
     207begin
     208  result := FAmbiantColorInt;
     209end;
     210
     211function TBGRAMaterial3D.GetDiffuseAlpha: byte;
     212var v: integer;
     213begin
     214  if FDiffuseColorInt.a < 128 then
     215    result := 0
     216  else
     217  begin
     218    v := (FDiffuseColorInt.a-128) shr 8;
     219    if v > 255 then v := 255;
     220    result := v;
     221  end;
     222end;
     223
    123224function TBGRAMaterial3D.GetDiffuseColor: TBGRAPixel;
    124225begin
     
    171272end;
    172273
     274function TBGRAMaterial3D.GetSimpleAlpha: byte;
     275begin
     276  result := (GetAmbiantAlpha + GetDiffuseAlpha) shr 1;
     277end;
     278
     279function TBGRAMaterial3D.GetSimpleColor: TBGRAPixel;
     280begin
     281  result := ColorIntToBGRA(GetSimpleColorInt);
     282end;
     283
     284function TBGRAMaterial3D.GetSimpleColorF: TColorF;
     285begin
     286  result := ColorInt65536ToColorF(GetSimpleColorInt);
     287end;
     288
     289function TBGRAMaterial3D.GetSimpleColorInt: TColorInt65536;
     290begin
     291  result := (GetAmbiantColorInt + GetDiffuseColorInt)*32768;
     292end;
     293
     294function TBGRAMaterial3D.GetTexture: IBGRAScanner;
     295begin
     296  result := FTexture;
     297end;
     298
     299function TBGRAMaterial3D.GetTextureZoom: TPointF;
     300begin
     301  result := FTextureZoom;
     302end;
     303
     304procedure TBGRAMaterial3D.SetAutoAmbiantColor(const AValue: boolean);
     305begin
     306  If AValue then
     307    SetAmbiantColorInt(ColorInt65536(65536,65536,65536));
     308end;
     309
    173310procedure TBGRAMaterial3D.SetAutoDiffuseColor(const AValue: boolean);
    174311begin
    175   FAutoDiffuseColor:= AValue;
     312  If AValue then
     313    SetDiffuseColorInt(ColorInt65536(65536,65536,65536));
    176314end;
    177315
    178316procedure TBGRAMaterial3D.SetAutoSpecularColor(const AValue: boolean);
    179317begin
    180   FAutoSpecularColor:= AValue;
    181   UpdateSpecular;
     318  If AValue then
     319    SetSpecularColorInt(ColorInt65536(65536,65536,65536));
     320end;
     321
     322procedure TBGRAMaterial3D.SetAmbiantAlpha(AValue: byte);
     323begin
     324  if AValue = 0 then
     325    FAmbiantColorInt.a := 0
     326  else
     327    FAmbiantColorInt.a := AValue*257+1;
     328  UpdateSimpleColor;
     329end;
     330
     331procedure TBGRAMaterial3D.SetAmbiantColor(const AValue: TBGRAPixel);
     332begin
     333  FAmbiantColorInt := BGRAToColorInt(AValue);
     334  FAutoAmbiantColor := (FAmbiantColorInt.r = 65536) and (FAmbiantColorInt.g = 65536) and (FAmbiantColorInt.b = 65536) and (FAmbiantColorInt.a = 65536);
     335  UpdateSimpleColor;
     336end;
     337
     338procedure TBGRAMaterial3D.SetAmbiantColorF(const AValue: TColorF);
     339begin
     340  FAmbiantColorInt := ColorFToColorInt65536(AValue);
     341  FAutoAmbiantColor := (FAmbiantColorInt.r = 65536) and (FAmbiantColorInt.g = 65536) and (FAmbiantColorInt.b = 65536) and (FAmbiantColorInt.a = 65536);
     342  UpdateSimpleColor;
     343end;
     344
     345procedure TBGRAMaterial3D.SetAmbiantColorInt(const AValue: TColorInt65536);
     346begin
     347  FAmbiantColorInt := AValue;
     348  FAutoAmbiantColor := (FAmbiantColorInt.r = 65536) and (FAmbiantColorInt.g = 65536) and (FAmbiantColorInt.b = 65536) and (FAmbiantColorInt.a = 65536);
     349  UpdateSimpleColor;
    182350end;
    183351
     
    186354  FDiffuseColorInt := BGRAToColorInt(AValue);
    187355  FDiffuseLightness := (FDiffuseColorInt.r + FDiffuseColorInt.g + FDiffuseColorInt.b) div 6;
     356  FAutoDiffuseColor:= (FDiffuseColorInt.r = 65536) and (FDiffuseColorInt.g = 65536) and (FDiffuseColorInt.b = 65536);
     357  UpdateSimpleColor;
    188358end;
    189359
     
    192362  FDiffuseColorInt := ColorFToColorInt65536(AValue);
    193363  FDiffuseLightness := (FDiffuseColorInt.r + FDiffuseColorInt.g + FDiffuseColorInt.b) div 6;
     364  FAutoDiffuseColor:= (FDiffuseColorInt.r = 65536) and (FDiffuseColorInt.g = 65536) and (FDiffuseColorInt.b = 65536);
     365  UpdateSimpleColor;
    194366end;
    195367
     
    198370  FDiffuseColorInt := AValue;
    199371  FDiffuseLightness := (FDiffuseColorInt.r + FDiffuseColorInt.g + FDiffuseColorInt.b) div 6;
     372  FAutoDiffuseColor:= (FDiffuseColorInt.r = 65536) and (FDiffuseColorInt.g = 65536) and (FDiffuseColorInt.b = 65536);
     373  UpdateSimpleColor;
    200374end;
    201375
     
    238412begin
    239413  FSaturationLowF:= AValue;
     414end;
     415
     416procedure TBGRAMaterial3D.SetSimpleAlpha(AValue: byte);
     417begin
     418  SetAmbiantAlpha(AValue);
     419  SetDiffuseAlpha(AValue);
     420end;
     421
     422procedure TBGRAMaterial3D.SetSimpleColor(AValue: TBGRAPixel);
     423begin
     424  SetAmbiantColor(AValue);
     425  SetDiffuseColor(AValue);
     426end;
     427
     428procedure TBGRAMaterial3D.SetSimpleColorF(AValue: TColorF);
     429begin
     430  SetAmbiantColorF(AValue);
     431  SetDiffuseColorF(AValue);
     432end;
     433
     434procedure TBGRAMaterial3D.SetSimpleColorInt(AValue: TColorInt65536);
     435begin
     436  SetAmbiantColorInt(AValue);
     437  SetDiffuseColorInt(AValue);
     438end;
     439
     440procedure TBGRAMaterial3D.SetTexture(AValue: IBGRAScanner);
     441begin
     442  FTexture := AValue;
     443end;
     444
     445procedure TBGRAMaterial3D.SetTextureZoom(AValue: TPointF);
     446begin
     447  FTextureZoom := AValue;
     448end;
     449
     450function TBGRAMaterial3D.GetName: string;
     451begin
     452  result := FName;
     453end;
     454
     455procedure TBGRAMaterial3D.SetName(const AValue: string);
     456begin
     457  FName := AValue;
    240458end;
    241459
     
    283501    end
    284502    else
    285       NH *= FPowerTableSize;
     503      PowerTablePos := NH*FPowerTableSize;
    286504    {$ELSE}
    287505    PowerTablePos := NH;
     
    298516    Context^.diffuseColor += ALightColor*round(DiffuseIntensity*65536)
    299517  else
    300     Context^.diffuseColor += FDiffuseColorInt*round(DiffuseIntensity*65536);
     518    Context^.diffuseColor += ALightColor*FDiffuseColorInt*round(DiffuseIntensity*65536);
    301519
    302520  if FAutoSpecularColor then
    303521    Context^.specularColor += ALightColor*round(SpecularIntensity* NnH*65536)
    304522  else
    305     Context^.specularColor += FSpecularColorInt*round(SpecularIntensity* NnH*65536);
     523    Context^.specularColor += ALightColor*FSpecularColorInt*round(SpecularIntensity* NnH*65536);
    306524end;
    307525
     
    312530    Context^.diffuseColor += ALightColor*round(DiffuseIntensity*65536)
    313531  else
    314     Context^.diffuseColor += FDiffuseColorInt*round(DiffuseIntensity*65536);
     532    Context^.diffuseColor += ALightColor*FDiffuseColorInt*round(DiffuseIntensity*65536);
    315533end;
    316534
     
    327545  begin
    328546    if FDiffuseLightness <> 32768 then
    329       Context^.lightness += CombineLightness(DiffuseLightnessTerm32768,FDiffuseLightness)
     547      Context^.lightness += CombineLightness(DiffuseLightnessTerm32768,CombineLightness(FDiffuseLightness,ALightLightness))
    330548    else
    331       Context^.lightness += DiffuseLightnessTerm32768;
     549      Context^.lightness += CombineLightness(DiffuseLightnessTerm32768,ALightLightness);
    332550  end;
    333551end;
     
    561779function TBGRADirectionalLight3D.GetDirection: TPoint3D;
    562780begin
    563   result := Point3D(FDirection.x,FDirection.y,FDirection.z);
     781  result := Point3D(-FDirection.x,-FDirection.y,-FDirection.z);
    564782end;
    565783
  • GraphicTest/Packages/bgrabitmap/lineartexscan.inc

    r452 r472  
     1{$i bgrasse.inc}
     2
    13  var
    24    xLen: single;       //horizontal length in pixels
     
    5860  {$ENDIF}
    5961
    60     {$IFDEF CPUI386}
     62    {$IFDEF BGRASSE_AVAILABLE}
    6163    if UseSSE then
    6264    begin
  • GraphicTest/Packages/bgrabitmap/perspectivescan.inc

    r452 r472  
     1{$i bgrasse.inc}
     2
    13  var
    24    //loop variables
     
    106108  {$ENDIF}
    107109
    108   {$IFDEF CPUI386}
     110  {$IFDEF BGRASSE_AVAILABLE}
    109111    if UseSSE then
    110112    begin
  • GraphicTest/Packages/bgrabitmap/perspectivescan2.inc

    r452 r472  
    1 {$asmmode intel}
     1{$i bgrasse.inc}
     2
     3{$ifdef BGRASSE_AVAILABLE}{$asmmode intel}{$endif}
    24    {$IFDEF PARAM_USESSE}
    35      asm
    46      {$IFDEF PARAM_USESHADER}
    5          mov eax, ShaderContext
    6          movaps xmm2, [eax+32] //positionInvZ
    7          movaps xmm3, [eax+48] //normalInvZ
     7         {$IFDEF cpux86_64}
     8          mov rax, ShaderContext
     9          movaps xmm2, [rax+32] //positionInvZ
     10          movaps xmm3, [rax+48] //normalInvZ
     11         {$ELSE}
     12          mov eax, ShaderContext
     13          movaps xmm2, [eax+32] //positionInvZ
     14          movaps xmm3, [eax+48] //normalInvZ
     15         {$ENDIF}
    816      {$ENDIF}
    917      {$IFNDEF PARAM_USESOLIDCOLOR}
     
    2735
    2836           {$IFDEF PARAM_USESHADER}
     37             {$ifdef cpux86_64}
     38             mov rax, ShaderContext
     39             {$else}
    2940             mov eax, ShaderContext
     41             {$endif}
    3042
    3143             mulps xmm2, xmm4       //positionInvZ*zPos (A)
    32 
    3344             mulps xmm3, xmm4       //normalInvZ*zPos
    34 
     45             {$ifdef cpux86_64}
     46             movaps [rax+0], xmm2   //(A) Position
     47             {$else}
    3548             movaps [eax+0], xmm2   //(A) Position
     49             {$endif}
    3650
    3751             //normalize
     
    5367             {$ENDIF}
    5468
     69             xorps xmm7,xmm7
     70             comiss xmm3,xmm7
     71             jna @skipnormal
     72
    5573             rsqrtps xmm3,xmm3
    56 
    5774             mulps xmm3, xmm1  //apply
    58 
     75             @skipnormal:
     76
     77             {$ifdef cpux86_64}
     78             movaps [rax+16], xmm3  //Normal
     79             {$else}
    5980             movaps [eax+16], xmm3  //Normal
     81             {$endif}
    6082           {$ENDIF}
    6183
     
    142164      {$IFDEF PARAM_USESHADER}
    143165        {$IFDEF PARAM_USESSE}
     166           {$ifdef cpux86_64}
    144167           asm
    145              mov eax, ShaderContext
    146              movaps xmm2, [eax+32] //PositionInvZ
    147              movaps xmm1, [eax+64] //PositionStepInvZ
    148              movaps xmm3, [eax+48] //NormalInvZ
    149              movaps xmm0, [eax+80] //NormalStepInvZ
     168             mov rax, ShaderContext
     169             movaps xmm2, [rax+32] //PositionInvZ
     170             movaps xmm1, [rax+64] //PositionStepInvZ
     171             movaps xmm3, [rax+48] //NormalInvZ
     172             movaps xmm0, [rax+80] //NormalStepInvZ
    150173             addps xmm2, xmm1
    151174             addps xmm3, xmm0
    152              movaps [eax+32], xmm2
    153              movaps [eax+48], xmm3
     175             movaps [rax+32], xmm2
     176             movaps [rax+48], xmm3
    154177           end;
    155 {asm
    156   mov eax, ShaderContext
    157   movaps xmm2, [eax+32] //PositionInvZ
    158   movaps xmm1, [eax+64] //PositionStepInvZ
    159   addps xmm2, xmm1
    160   movaps [eax+32], xmm2
    161 
    162   movaps xmm3, [eax+48] //NormalInvZ
    163   movaps xmm1, [eax+80] //NormalStepInvZ
    164   addps xmm3, xmm1
    165   movaps [eax+48], xmm3
    166 end;}
     178           {$else}
     179            asm
     180              mov eax, ShaderContext
     181              movaps xmm2, [eax+32] //PositionInvZ
     182              movaps xmm1, [eax+64] //PositionStepInvZ
     183              movaps xmm3, [eax+48] //NormalInvZ
     184              movaps xmm0, [eax+80] //NormalStepInvZ
     185              addps xmm2, xmm1
     186              addps xmm3, xmm0
     187              movaps [eax+32], xmm2
     188              movaps [eax+48], xmm3
     189            end;
     190           {$endif}
    167191        {$ELSE}
    168192          with ShaderContext^ do
  • GraphicTest/Packages/bgrabitmap/phongdraw.inc

    r452 r472  
    7373  x,y : integer;      // Coordinates of point in height map.
    7474  vS1,vS2: TPoint3D_128; // surface vectors (plane)
     75  deltaDown: Int32or64;
     76  IsLineUp,IsLineDown: boolean;
    7577
    7678begin
     79  if map = nil then exit;
    7780  {$ifndef PARAM_SIMPLECOLOR}
    7881    {$ifndef PARAM_SCANNER}
     
    106109                  LightPosition.Y-ofsY,
    107110                  LightPositionZ);
    108   {$ifdef PARAM_PHONGSSE}
    109   asm
    110     movups xmm1, vLS
    111   end;
    112   LightDestFactor4 := Point3D_128(LightDestFactor,LightDestFactor,LightDestFactor,LightDestFactor);
    113   {$endif}
    114111
    115112  //surface vectors
     
    119116  vV := Point3D_128(0,0,1);
    120117
    121 
    122118  dist := 0;
    123119  LdotN := 0;
    124120  NnH := 0;
    125121
     122  {$ifdef PARAM_PHONGSSE}
     123  LightDestFactor4 := Point3D_128(LightDestFactor,LightDestFactor,LightDestFactor,LightDestFactor);
     124  {$endif}
     125
     126  if map.LineOrder = riloTopToBottom then
     127    deltaDown := map.Width*sizeof(TBGRAPixel)
     128  else
     129    deltaDown := -map.Width*sizeof(TBGRAPixel);
    126130  for y := miny to maxy do
    127131  begin
     
    138142      {$endif}
    139143    {$endif}
     144    IsLineUp := y > 0;
     145    IsLineDown := y < map.Height-1;
     146    mcTop := BGRAPixelTransparent;
     147    mcBottom := BGRAPixelTransparent;
    140148    for x := minx to maxx do
    141149    begin
    142150      mcLeft := mc;
    143151      mc := mcRight;
    144       inc(pmap);
    145152      if x < map.width-1 then
    146         mcRight := pmap^ else
     153        mcRight := (pmap+1)^ else
    147154        mcRight := BGRAPixelTransparent;
    148155      if mc.alpha = 0 then
     
    156163        {$endif}
    157164        inc(pdest);
     165        inc(pmap);
    158166        continue;
    159167      end;
    160168
    161169      //compute surface vectors
    162       mcTop := map.GetPixel(x,y-1);
    163       mcBottom := map.GetPixel(x,y+1);
     170      if IsLineUp then mcTop := pbgrapixel(pbyte(pmap)-deltaDown)^;
     171      if IsLineDown then mcBottom := pbgrapixel(pbyte(pmap)+deltaDown)^;
     172      inc(pmap);
     173
    164174      z := MapHeight(mc)*mapAltitude;
    165175      if mcLeft.alpha = 0 then
     
    196206      begin
    197207        {$DEFINE PARAM_USESSE3}
     208        asm
     209          movups xmm1, vLS
     210        end;
    198211        {$i phongdrawsse.inc}
    199212        {$UNDEF PARAM_USESSE3}
    200213      end else
    201214      begin
     215        asm
     216          movups xmm1, vLS
     217        end;
    202218        {$i phongdrawsse.inc}
    203219      end;
     
    221237
    222238        NH := DotProduct3D_128(vH,vN);
    223         if NH <= 0 then
    224           NnH := 0
    225         else
    226           NnH := exp(SpecularIndex*ln(NH));
    227239      {$endif}
    228240
     
    230242        NnH := 0
    231243      else
    232         NnH := exp(SpecularIndex*ln(NH));
     244        NnH := exp(SpecularIndex*ln(NH));  //to be optimized
    233245
    234246      distfactor := LightSourceIntensity / (dist*LightSourceDistanceFactor + LightSourceDistanceTerm);
  • GraphicTest/Packages/bgrabitmap/phongdrawsse.inc

    r452 r472  
    22        //vL := vLS- vP*LightDestFactor;
    33        movups xmm4, vP
    4         movss xmm6,LightDestFactor4
    5         mulps xmm6, xmm4
     4        movups xmm6,LightDestFactor4
     5        mulps xmm6, xmm4   //keep xmm4 = vP
    66        movaps xmm0, xmm1
    77        subps xmm0, xmm6
  • GraphicTest/Packages/bgrabitmap/phonglight.inc

    r452 r472  
     1{$i bgrasse.inc}
    12var
    2   dist2,LdotN,NdotH,lightEnergy,diffuse : single;
     3  {%H-}dist2,LdotN,NdotH,lightEnergy,diffuse : single;
    34const
    45  minus_05 = -0.5;
    56begin
    6   {$IFDEF CPUI386}If UseSSE then
     7  {$IFDEF BGRASSE_AVAILABLE}If UseSSE then
    78  begin
    89    with Context^ do
  • GraphicTest/Packages/bgrabitmap/phonglightsse.inc

    r452 r472  
    11    {$asmmode intel}
    22    asm
     3      {$ifdef cpux86_64}
     4      mov rax, Context
     5      movaps xmm0,[rax+160] //Context^.vL
     6      movaps xmm2,[rax+192] //Context^.vH
     7      movaps xmm1,[rax+16] //Context^.Normal
     8      {$else}
    39      mov eax, Context
    410      movaps xmm0,[eax+160] //Context^.vL
    511      movaps xmm2,[eax+192] //Context^.vH
    612      movaps xmm1,[eax+16] //Context^.Normal
     13      {$endif}
    714
    815      {$IFDEF PARAM_POINTLIGHT}
     16        {$ifdef cpux86_64}
     17        movaps xmm6,[rax+0]  //Context^.Position
     18        {$else}
    919        movaps xmm6,[eax+0]  //Context^.Position
     20        {$endif}
    1021        subps xmm0,xmm6      //xmm0 = vL
    1122        movaps xmm6, xmm0
  • GraphicTest/Packages/bgrabitmap/polyaliaspersp.inc

    r452 r472  
    33
    44{ TPolygonPerspectiveTextureMappingInfo }
     5
     6procedure TPolygonPerspectiveTextureMappingInfo.SetIntersectionValues(
     7  AInter: TIntersectionInfo; AInterX: Single; AWinding, ANumSegment: integer;
     8  dy: single; AData: pointer);
     9var info: PPerspectiveTextureInfo;
     10begin
     11  AInter.SetValues(AInterX,AWinding,ANumSegment);
     12  info := PPerspectiveTextureInfo(AData);
     13  TPerspectiveTextureMappingIntersectionInfo(AInter).coordInvZ := dy*info^.InvZSlope + info^.InvZ;
     14  TPerspectiveTextureMappingIntersectionInfo(AInter).texCoordDivByZ := info^.TexCoordDivByZ + info^.TexCoordDivByZSlopes*dy;
     15  if FLightnesses<>nil then
     16    TPerspectiveTextureMappingIntersectionInfo(AInter).lightness := round(info^.lightness + info^.lightnessSlope*dy)
     17  else
     18    TPerspectiveTextureMappingIntersectionInfo(AInter).lightness := 32768;
     19end;
    520
    621constructor TPolygonPerspectiveTextureMappingInfo.Create(
     
    108123begin
    109124  Result:= TPerspectiveTextureMappingIntersectionInfo.Create;
    110 end;
    111 
    112 procedure TPolygonPerspectiveTextureMappingInfo.ComputeIntersection(cury: single;
    113       var inter: ArrayOfTIntersectionInfo; var nbInter: integer);
    114 var
    115   j: integer;
    116   dy: single;
    117   info: PPerspectiveTextureInfo;
    118 begin
    119   if length(FSlices)=0 then exit;
    120 
    121   while (cury < FSlices[FCurSlice].y1) and (FCurSlice > 0) do dec(FCurSlice);
    122   while (cury > FSlices[FCurSlice].y2) and (FCurSlice < high(FSlices)) do inc(FCurSlice);
    123   with FSlices[FCurSlice] do
    124   if (cury >= y1) and (cury <= y2) then
    125   begin
    126     for j := 0 to nbSegments-1 do
    127     begin
    128       dy := cury - segments[j].y1;
    129       inter[nbinter].interX := dy * segments[j].slope + segments[j].x1;
    130       inter[nbinter].winding := segments[j].winding;
    131       info := PPerspectiveTextureInfo(segments[j].data);
    132       TPerspectiveTextureMappingIntersectionInfo(inter[nbinter]).coordInvZ := dy*info^.InvZSlope + info^.InvZ;
    133       TPerspectiveTextureMappingIntersectionInfo(inter[nbinter]).texCoordDivByZ := info^.TexCoordDivByZ + info^.TexCoordDivByZSlopes*dy;
    134       if FLightnesses<>nil then
    135         TPerspectiveTextureMappingIntersectionInfo(inter[nbinter]).lightness := round(info^.lightness + info^.lightnessSlope*dy)
    136       else
    137         TPerspectiveTextureMappingIntersectionInfo(inter[nbinter]).lightness := 32768;
    138       Inc(nbinter);
    139     end;
    140   end;
    141125end;
    142126
     
    495479{ TPolygonPerspectiveMappingShaderInfo }
    496480
     481procedure TPolygonPerspectiveMappingShaderInfo.SetIntersectionValues(
     482  AInter: TIntersectionInfo; AInterX: Single; AWinding, ANumSegment: integer;
     483  dy: single; AData: pointer);
     484var info : PPerspectiveTextureInfo;
     485begin
     486  AInter.SetValues(AInterX,AWinding,ANumSegment);
     487  info := PPerspectiveTextureInfo(AData);
     488  TPerspectiveTextureMappingIntersectionInfo(AInter).coordInvZ := dy*info^.InvZSlope + info^.InvZ;
     489  TPerspectiveTextureMappingIntersectionInfo(AInter).texCoordDivByZ := info^.TexCoordDivByZ + info^.TexCoordDivByZSlopes*dy;
     490  TPerspectiveTextureMappingIntersectionInfo(AInter).Position3D := info^.Position3D + info^.Position3DSlope*dy;
     491  TPerspectiveTextureMappingIntersectionInfo(AInter).Normal3D := info^.Normal3D + info^.Normal3DSlope*dy;
     492end;
     493
    497494constructor TPolygonPerspectiveMappingShaderInfo.Create(
    498495  const points: array of TPointF; const points3D: array of TPoint3D;
     
    608605end;
    609606
    610 procedure TPolygonPerspectiveMappingShaderInfo.ComputeIntersection(
    611   cury: single; var inter: ArrayOfTIntersectionInfo; var nbInter: integer);
    612 var
    613   j: integer;
    614   dy: single;
    615   info: PPerspectiveTextureInfo;
    616 begin
    617   if length(FSlices)=0 then exit;
    618 
    619   while (cury < FSlices[FCurSlice].y1) and (FCurSlice > 0) do dec(FCurSlice);
    620   while (cury > FSlices[FCurSlice].y2) and (FCurSlice < high(FSlices)) do inc(FCurSlice);
    621   with FSlices[FCurSlice] do
    622   if (cury >= y1) and (cury <= y2) then
    623   begin
    624     for j := 0 to nbSegments-1 do
    625     begin
    626       dy := cury - segments[j].y1;
    627       inter[nbinter].interX := dy * segments[j].slope + segments[j].x1;
    628       inter[nbinter].winding := segments[j].winding;
    629       info := PPerspectiveTextureInfo(segments[j].data);
    630       TPerspectiveTextureMappingIntersectionInfo(inter[nbinter]).coordInvZ := dy*info^.InvZSlope + info^.InvZ;
    631       TPerspectiveTextureMappingIntersectionInfo(inter[nbinter]).texCoordDivByZ := info^.TexCoordDivByZ + info^.TexCoordDivByZSlopes*dy;
    632       TPerspectiveTextureMappingIntersectionInfo(inter[nbinter]).Position3D := info^.Position3D + info^.Position3DSlope*dy;
    633       TPerspectiveTextureMappingIntersectionInfo(inter[nbinter]).Normal3D := info^.Normal3D + info^.Normal3DSlope*dy;
    634       Inc(nbinter);
    635     end;
    636   end;
    637 end;
    638 
  • GraphicTest/Packages/bgrabitmap/readme.txt

    r452 r472  
    1 BGRABitmap v6.2 - Drawing routines with alpha blending and antialiasing with Lazarus.
     1BGRABitmap - Drawing routines with alpha blending and antialiasing with Lazarus.
    22
    33These routines allow to manipulate 32bit images in BGRA format.
  • GraphicTest/Packages/bgrabitmap/renderdensity256.inc

    r452 r472  
    1717          for xb := densMinX to densMaxX do
    1818          begin
    19             j := pdens^;
     19            tempDensity := pdens^;
    2020            Inc(pdens);
    2121            c := pscan^;
    2222            inc(pscan);
    23             if j <> 0 then
    24               DrawPixelInlineWithAlphaCheck(pdest, BGRA(c.red, c.green, c.blue,
     23            if tempDensity <> 0 then
     24              {$ifdef PARAM_LINEARANTIALIASING}
     25              FastBlendPixelInline
     26              {$else}
     27              DrawPixelInlineWithAlphaCheck{$endif}(pdest, BGRA(c.red, c.green, c.blue,
    2528              {$ifdef PARAM_ANTIALIASINGFACTOR}DivByAntialiasPrecision256{$endif}
    26               (c.alpha * j
     29              (c.alpha * tempDensity
    2730              {$ifdef PARAM_ANTIALIASINGFACTOR} ) {$else} +128) shr 8 {$endif}
    2831              ));
     
    3336          for xb := densMinX to densMaxX do
    3437          begin
    35             j := pdens^;
     38            tempDensity := pdens^;
    3639            Inc(pdens);
    3740            c := ScanNextPixelProc();
    38             if j <> 0 then
    39               DrawPixelInlineWithAlphaCheck(pdest, BGRA(c.red, c.green, c.blue,
     41            if tempDensity <> 0 then
     42              {$ifdef PARAM_LINEARANTIALIASING}
     43              FastBlendPixelInline
     44              {$else}
     45              DrawPixelInlineWithAlphaCheck{$endif}(pdest, BGRA(c.red, c.green, c.blue,
    4046              {$ifdef PARAM_ANTIALIASINGFACTOR}DivByAntialiasPrecision256{$endif}
    41               (c.alpha * j
     47              (c.alpha * tempDensity
    4248              {$ifdef PARAM_ANTIALIASINGFACTOR} ) {$else} +128) shr 8 {$endif}
    4349              ));
     
    5157        for xb := densMinX to densMaxX do
    5258        begin
    53           j := pdens^;
     59          tempDensity := pdens^;
    5460          Inc(pdens);
    55           if j <> 0 then
     61          if tempDensity <> 0 then
    5662            ErasePixelInline(pdest,
    5763            {$ifdef PARAM_ANTIALIASINGFACTOR}DivByAntialiasPrecision256{$endif}
    58             (c.alpha * j
     64            (c.alpha * tempDensity
    5965            {$ifdef PARAM_ANTIALIASINGFACTOR} ) {$else} +128) shr 8 {$endif}
    6066            );
     
    6672        for xb := densMinX to densMaxX do
    6773        begin
    68           j := pdens^;
     74          tempDensity := pdens^;
    6975          Inc(pdens);
    70           if j <> 0 then
     76          if tempDensity <> 0 then
    7177          begin
    7278            c2.alpha :=
    7379              {$ifdef PARAM_ANTIALIASINGFACTOR}DivByAntialiasPrecision256{$endif}
    74               (c.alpha * j
     80              (c.alpha * tempDensity
    7581              {$ifdef PARAM_ANTIALIASINGFACTOR} ) {$else} +128) shr 8 {$endif}
    7682              ;
     83            {$ifdef PARAM_LINEARANTIALIASING}
     84            FastBlendPixelInline(pdest, c2);
     85            {$else}
    7786            DrawPixelInlineExpandedOrNotWithAlphaCheck(pdest, ec, c2);
     87            {$endif}
    7888          end;
    7989          Inc(pdest);
     
    8393  end
    8494{$undef PARAM_ANTIALIASINGFACTOR}
     95{$undef PARAM_LINEARANTIALIASING}
  • GraphicTest/UDrawForm.lfm

    r444 r472  
    44  Top = 208
    55  Width = 327
    6   BorderStyle = bsDialog
    76  Caption = 'Draw frame'
    87  FormStyle = fsStayOnTop
    98  OnCreate = FormCreate
    10   LCLVersion = '1.1'
     9  LCLVersion = '1.5'
    1110end
  • GraphicTest/UDrawForm.pas

    r445 r472  
    1616    procedure FormCreate(Sender: TObject);
    1717  private
    18     { private declarations }
     18
    1919  public
     20    FrameSize: TPoint;
    2021    EraseBackgroundEnabled: Boolean;
    2122    procedure EraseBackground(DC: HDC); override;
  • GraphicTest/UDrawMethod.pas

    r471 r472  
    2121    FControl: TControl;
    2222    FFPS: Real;
     23    FParent: TWinControl;
    2324  public
    2425    Caption: string;
     
    3940    procedure DrawFrame(FastBitmap: TFastBitmap); virtual;
    4041    procedure DrawFrameTiming(FastBitmap: TFastBitmap);
     42    procedure UpdateSettings; virtual;
    4143    property Control: TControl read FControl;
    4244  end;
     
    4850  TDrawMethodImage = class(TDrawMethod)
    4951    Image: TImage;
     52    procedure UpdateSettings; override;
    5053    procedure Init(Parent: TWinControl; Size: TPoint; PixelFormat: TPixelFormat); override;
    5154    procedure Done; override;
     
    5760    PaintBox: TPaintBox;
    5861    procedure Paint(Sender: TObject); virtual;
     62    procedure UpdateSettings; override;
    5963    procedure Init(Parent: TWinControl; Size: TPoint; PixelFormat: TPixelFormat); override;
    6064    procedure Done; override;
     
    6973    TextureId: GLuint;
    7074    OpenGLBitmap: Pointer;
     75    procedure UpdateSettings; override;
    7176    procedure InitGL;
    7277    procedure OpenGLControlResize(Sender: TObject);
     
    8691begin
    8792
     93end;
     94
     95procedure TDrawMethodPaintBox.UpdateSettings;
     96begin
     97  inherited UpdateSettings;
     98  PaintBox.ControlStyle := FParent.ControlStyle;
    8899end;
    89100
     
    96107  PaintBox.OnPaint := Paint;
    97108  PaintBox.Show;
     109  UpdateSettings;
    98110end;
    99111
     
    105117
    106118{ TDrawMethodImage }
     119
     120procedure TDrawMethodImage.UpdateSettings;
     121begin
     122  inherited;
     123  Image.ControlStyle := FParent.ControlStyle;
     124end;
    107125
    108126procedure TDrawMethodImage.Init(Parent: TWinControl; Size: TPoint; PixelFormat: TPixelFormat);
     
    115133  Image.Picture.Bitmap.SetSize(Size.X, Size.Y);
    116134  Image.Show;
     135  UpdateSettings;
    117136end;
    118137
     
    141160  end;
    142161  GetMem(OpenGLBitmap, OpenGLControl.Width * OpenGLControl.Height * SizeOf(Integer));
     162  UpdateSettings;
    143163end;
    144164
     
    153173begin
    154174  glViewport(0, 0, OpenGLControl.Width, OpenGLControl.Height);
     175end;
     176
     177procedure TDrawMethodOpenGL.UpdateSettings;
     178begin
     179  inherited UpdateSettings;
     180  OpenGLControl.ControlStyle := FParent.ControlStyle;
    155181end;
    156182
     
    195221procedure TDrawMethod.Init(Parent: TWinControl; Size: TPoint; PixelFormat: TPixelFormat);
    196222begin
     223  FParent := Parent;
    197224end;
    198225
     
    227254end;
    228255
     256procedure TDrawMethod.UpdateSettings;
     257begin
     258end;
     259
    229260end.
    230261
  • GraphicTest/UMainForm.lfm

    r470 r472  
    699699      TabOrder = 8
    700700    end
    701     object CheckBox1: TCheckBox
     701    object CheckBoxOpaque: TCheckBox
    702702      Left = 400
    703703      Height = 27
     
    706706      Anchors = [akLeft, akBottom]
    707707      Caption = 'Opaque'
    708       OnChange = CheckBox1Change
     708      OnChange = CheckBoxOpaqueChange
    709709      TabOrder = 9
    710710    end
     
    718718      ParentColor = False
    719719    end
    720     object ComboBox1: TComboBox
     720    object ComboBoxPixelFormat: TComboBox
    721721      Left = 136
    722722      Height = 37
     
    725725      Anchors = [akLeft, akBottom]
    726726      ItemHeight = 0
    727       OnChange = ComboBox1Change
     727      OnChange = ComboBoxPixelFormatChange
    728728      Style = csDropDownList
    729729      TabOrder = 10
     
    836836    end
    837837  end
     838  object TimerUpdateSettings: TTimer
     839    OnTimer = TimerUpdateSettingsTimer
     840    left = 272
     841    top = 264
     842  end
    838843end
  • GraphicTest/UMainForm.pas

    r471 r472  
    2828    ButtonSingleTest: TButton;
    2929    ButtonStop: TButton;
    30     CheckBox1: TCheckBox;
     30    CheckBoxOpaque: TCheckBox;
    3131    CheckBoxDoubleBuffered: TCheckBox;
    3232    CheckBoxEraseBackground: TCheckBox;
    33     ComboBox1: TComboBox;
     33    ComboBoxPixelFormat: TComboBox;
    3434    FileExit1: TFileExit;
    3535    FloatSpinEdit1: TFloatSpinEdit;
     
    6565    TabSheet1: TTabSheet;
    6666    TabSheet2: TTabSheet;
     67    TimerUpdateSettings: TTimer;
    6768    TimerUpdateList: TTimer;
    6869    procedure AExportAsWikiTextExecute(Sender: TObject);
     
    7172    procedure ATestOneMethodExecute(Sender: TObject);
    7273    procedure ATestStopExecute(Sender: TObject);
    73     procedure CheckBox1Change(Sender: TObject);
     74    procedure CheckBoxOpaqueChange(Sender: TObject);
    7475    procedure CheckBoxDoubleBufferedChange(Sender: TObject);
    7576    procedure CheckBoxEraseBackgroundChange(Sender: TObject);
    76     procedure ComboBox1Change(Sender: TObject);
     77    procedure ComboBoxPixelFormatChange(Sender: TObject);
    7778    procedure FormCreate(Sender: TObject);
    7879    procedure FormDestroy(Sender: TObject);
     
    8586    procedure SpinEditWidthChange(Sender: TObject);
    8687    procedure TimerUpdateListTimer(Sender: TObject);
     88    procedure TimerUpdateSettingsTimer(Sender: TObject);
    8789  private
     90    FCurrentMethod: TDrawMethod;
    8891    MethodIndex: Integer;
    8992    SingleTestActive: Boolean;
     
    9699    procedure UpdateMethodList;
    97100    procedure UpdateInterface;
    98     procedure UpdateFrameSize;
    99101    procedure RegisterDrawMethods;
    100102    procedure RegisterDrawMethod(MethodClass: TDrawMethodClass);
     
    105107    Scenes: TObjectList; // TObjectList<TFastBitmap>
    106108    SceneIndex: Integer;
     109    property CurrentMethod: TDrawMethod read FCurrentMethod;
    107110  end;
    108111
     
    141144
    142145  for PF := Low(TPixelFormat) to High(TPixelFormat) do
    143     ComboBox1.Items.Add(GetEnumName(TypeInfo(TPixelFormat), Integer(PF)));
     146    ComboBoxPixelFormat.Items.Add(GetEnumName(TypeInfo(TPixelFormat), Integer(PF)));
    144147
    145148  PageControl1.TabIndex := 0;
     
    151154  StartTime: TDateTime;
    152155begin
     156  FCurrentMethod := Method;
    153157  with Method do begin
    154158    Init(DrawForm, FrameSize, PixelFormat);
     
    171175    Done;
    172176  end;
     177  FCurrentMethod := nil;
    173178end;
    174179
     
    255260end;
    256261
    257 procedure TMainForm.CheckBox1Change(Sender: TObject);
    258 begin
    259   if CheckBox1.Checked then
     262procedure TMainForm.CheckBoxOpaqueChange(Sender: TObject);
     263begin
     264  if CheckBoxOpaque.Checked then
    260265    DrawForm.ControlStyle := DrawForm.ControlStyle + [csOpaque]
    261266    else DrawForm.ControlStyle := DrawForm.ControlStyle - [csOpaque];
     267  if Assigned(FCurrentMethod) then
     268    FCurrentMethod.UpdateSettings;
    262269end;
    263270
     
    272279end;
    273280
    274 procedure TMainForm.ComboBox1Change(Sender: TObject);
    275 begin
    276   PixelFormat := TPixelFormat(ComboBox1.ItemIndex);
     281procedure TMainForm.ComboBoxPixelFormatChange(Sender: TObject);
     282begin
     283  PixelFormat := TPixelFormat(ComboBoxPixelFormat.ItemIndex);
    277284  UpdateInterface;
    278285end;
     
    292299procedure TMainForm.FormShow(Sender: TObject);
    293300begin
    294   UpdateFrameSize;
    295301  UpdateMethodList;
    296302  UpdateInterface;
    297303  DrawForm.Show;
     304  DrawForm.Left := Left + Width;
     305  DrawForm.Top := Top;
    298306end;
    299307
     
    339347begin
    340348  FrameSize.Y := SpinEditHeight.Value;
    341   UpdateFrameSize;
    342349end;
    343350
     
    345352begin
    346353  FrameSize.X := SpinEditWidth.Value;
    347   UpdateFrameSize;
    348354end;
    349355
     
    351357begin
    352358  UpdateMethodList;
     359end;
     360
     361procedure TMainForm.TimerUpdateSettingsTimer(Sender: TObject);
     362begin
     363  if (FrameSize.X <> DrawForm.FrameSize.X) or
     364    (FrameSize.Y <> DrawForm.FrameSize.Y) then begin
     365      DrawForm.FrameSize := FrameSize;
     366      DrawForm.ClientWidth := FrameSize.X;
     367      DrawForm.ClientHeight := FrameSize.Y;
     368      GenerateSceneFrames;
     369    end;
    353370end;
    354371
     
    382399  CheckBoxDoubleBuffered.Checked := DrawForm.DoubleBuffered;
    383400  CheckBoxEraseBackground.Checked := DrawForm.EraseBackgroundEnabled;
    384   CheckBox1.Checked := csOpaque in DrawForm.ControlStyle;
    385   ComboBox1.ItemIndex := Integer(PixelFormat);
    386 end;
    387 
    388 procedure TMainForm.UpdateFrameSize;
    389 begin
    390   DrawForm.ClientWidth := FrameSize.X;
    391   DrawForm.ClientHeight := FrameSize.Y;
    392   GenerateSceneFrames;
     401  CheckBoxOpaque.Checked := csOpaque in DrawForm.ControlStyle;
     402  ComboBoxPixelFormat.ItemIndex := Integer(PixelFormat);
    393403end;
    394404
Note: See TracChangeset for help on using the changeset viewer.