{Version 9.45}

{$i htmlcons.inc}

unit GDIPL2A;

interface

uses 
{$IFNDEF LCL} Windows, ActiveX, {$ELSE} LclIntf, LclType, Types, DynLibs, HtmlMisc, {$ENDIF} 
SysUtils, Graphics;

var
  GDIPlusActive: boolean;

type
  TGpImage = class(TObject)
  private
    fHandle: integer;
    fWidth, fHeight: integer;
    fFilename: string;
    function GetHeight: integer;
    function GetWidth: integer;
  public
    constructor Create(Filename: string; TmpFile: boolean = False); overload;
    constructor Create(IStr: IStream); overload;
    destructor Destroy; override;
    function GetTBitmap: TBitmap;
    property Height: integer read GetHeight;
    property Width: integer read GetWidth;
  end;

  TGpGraphics = class;

  TGpBitmap = class(TGpImage)
  public
    constructor Create(W, H: integer); overload;
    constructor Create(IStr: IStream); overload;
    constructor Create(W, H: integer; Graphics: TGpGraphics); overload;
    function GetPixel(X, Y: integer): DWord;
    procedure SetPixel(X, Y: integer; Color: DWord);
  end;

  TGpGraphics = class(TObject)
  private
    fGraphics: integer;
    procedure DrawSmallStretchedImage (Image: TGPImage; X, Y, Width, Height: Integer);
  public
    constructor Create(Handle: HDC); overload;
    constructor Create(Image: TGpImage); overload;
    destructor Destroy; override;
    procedure DrawImage (Image: TGPImage; X, Y: Integer); overload;
    procedure DrawImage (Image: TGPImage; X, Y, Width, Height: Integer); overload;
    procedure DrawImage(Image: TGpImage; x, y, srcx, srcy, srcwidth, srcheight: integer); overload;
    procedure DrawImage(Image: TGpImage; dx, dy, dw, dh, sx, sy, sw, sh: integer); overload;
    procedure Clear(Color: Cardinal);
    procedure ScaleTransform(sx, sy: Single);
 end;

procedure CheckInitGDIPlus; 
procedure CheckExitGDIPlus;

implementation

const
    GdiPlusLib = 'GdiPlus.dll';

type
    EGDIPlus = class (Exception);
    TRectF = record
        X: Single;
        Y: Single;
        Width: Single;
        Height: Single;
    end;

  ImageCodecInfo = packed record
    Clsid             : TGUID;
    FormatID          : TGUID;
    CodecName         : PWCHAR;
    DllName           : PWCHAR;
    FormatDescription : PWCHAR;
    FilenameExtension : PWCHAR;
    MimeType          : PWCHAR;
    Flags             : DWORD;
    Version           : DWORD;
    SigCount          : DWORD;
    SigSize           : DWORD;
    SigPattern        : PBYTE;
    SigMask           : PBYTE;
  end;
  TImageCodecInfo = ImageCodecInfo;
  PImageCodecInfo = ^TImageCodecInfo;

var
{$ifndef NoGDIPlus}
  GdiplusStartup: function(var Token: DWord; const Input, Output: Pointer): Integer; stdcall;
  GdiplusShutdown: procedure(Token: DWord); stdcall;
  GdipDrawImageI: function(Graphics, Image, X, Y: Integer): Integer; stdcall;
  GdipCreateHBITMAPFromBitmap: function(bitmap: integer; out hbmReturn: HBITMAP;
    background: DWord): integer; stdcall;
  GdipGetInterpolationMode: function(graphics: integer; var interpolationMode: integer): integer; stdcall;
{$endif$}
  GdipDeleteGraphics: function(Graphics: Integer): Integer; stdcall;
  GdipCreateFromHDC: function(hdc: HDC; var Graphics: Integer): Integer; stdcall;
  GdipDrawImageRectI: function (Graphics, Image, X, Y, Width, Height: Integer): Integer; stdcall;
  GdipLoadImageFromFile: function (const FileName: PWideChar; var Image: Integer): Integer; stdcall;
  GdipLoadImageFromStream: function(stream: ISTREAM;
              out image: integer): integer; stdcall;
  GdipCreateBitmapFromStream: function(stream: ISTREAM; out bitmap: integer): integer; stdcall;
  GdipDisposeImage: function (Image: Integer): Integer; stdcall;
  GdipGetImageWidth: function (Image: Integer; var Width: Integer): Integer; stdcall;

  GdipGetImageHeight: function(Image: Integer; var Height: Integer): Integer; stdcall;
  GdipGetImageGraphicsContext: function(Image: integer; out graphics: integer): integer; stdcall;
  GdipGraphicsClear: function(Graphics: Integer; Color: Cardinal): Integer; stdcall;
  GdipCreateBitmapFromScan0: function(width: Integer; height: Integer;
         stride: Integer; pixelformat: dword; scan0: Pointer;
         out bitmap: integer): integer; stdcall;
  GdipDrawImagePointRect: function(graphics: integer; image: integer;
    x: Single; y: Single; srcx: Single; srcy: Single; srcwidth: Single;
    srcheight: Single; srcUnit: integer): integer; stdcall;
  GdipScaleWorldTransform: function(graphics: integer; sx: Single; sy: Single;
    order: integer): integer; stdcall;
  GdipCreateBitmapFromGraphics: function(width, height: Integer;
    Graphics: integer; out Bitmap: integer): integer; stdcall;
  GdipBitmapGetPixel: function(bitmap, x, y: Integer; var color: DWord): integer; stdcall;
  GdipDrawImageRectRectI: function(graphics, image,
    dstx, dsty, dstwidth, dstheight, srcx, srcy, srcwidth, srcheight,
    srcUnit, imageAttributes: integer;
    callback: Pointer; callbackData: integer): integer; stdcall;

  GdipSetInterpolationMode: function(graphics, interpolationMode: integer): integer; stdcall;
  GdipBitmapSetPixel: function(bitmap, x, y: Integer; color: DWord): Integer;  stdcall;

type
  TGDIStartup = packed record
      Version: Integer;                       // Must be one
      DebugEventCallback: Pointer;            // Only for debug builds
      SuppressBackgroundThread: Bool;         // True if replacing GDI+ background processing
      SuppressExternalCodecs: Bool;           // True if only using internal codecs
  end;

var
  Err: Integer;

{ TGpGraphics }

constructor TGpGraphics.Create(Handle: HDC);
var
  err: integer;
begin
inherited Create;
err := GdipCreateFromHDC (Handle, fGraphics);
if err <> 0 then
  raise EGDIPlus.Create('Can''t Create Graphics');
end;

constructor TGpGraphics.Create(Image: TGpImage);
var
  err: integer;
begin
inherited Create;
err := GdipGetImageGraphicsContext(image.fHandle, fgraphics);
if err <> 0 then
  raise EGDIPlus.Create('Can''t Create Graphics');
end;

destructor TGpGraphics.Destroy;
begin
if fGraphics <> 0 then
    GdipDeleteGraphics  (fGraphics);
inherited;
end;

procedure TGpGraphics.DrawImage(Image: TGPImage; X, Y, Width, Height: Integer);
begin
if ((Image.Width <= 10) and (Width > Image.Width)) or           
   ((Image.Height <= 10) and (Height > Image.Height))  then
     DrawSmallStretchedImage(Image, X, Y, Width, Height)
else
  GdipDrawImageRectI (fGraphics, Image.fHandle, X, Y, Width, Height);
end;

procedure TGpGraphics.DrawSmallStretchedImage (Image: TGPImage; X, Y, Width, Height: Integer);    
{when a small image is getting enlarged, add a row and column to it copying
 the last row/column to the new row/column.  This gives much better interpolation.}
const
  NearestNeighbor = 5;
var
  g1, g2: TGpGraphics;
  BM1, BM2: TGpBitmap;
  W, H: integer;
  Pixel: DWord;
begin
W := Image.Width+1;  {new dimensions}
H := Image.Height+1;
BM1 := TGpBitmap.Create(W, H);  {new bitmap with extra row and column}
try
  g1 := TGpGraphics.Create(BM1);
  try
    g1.DrawImage(Image, 0, 0);  {draw the original image}
    g1.DrawImage(Image, W-1, 0, 1, H,   {copy the column, then the row}
                       W-2, 0, 1, H);
    g1.DrawImage(Image, 0, H-1, W, 1,
                       0, H-2, W, 1);
    Pixel := BM1.GetPixel(W-2, H-2);   {for some reason also need to set the lower right pixel}
    BM1.SetPixel(W-1, H-1, Pixel);
    BM2 := TGpBitmap.Create(Width, Height);
    try
      g2 := TGpGraphics.Create(BM2);
      try
        GdipSetInterpolationMode(g2.fGraphics, NearestNeighbor);
        g2.DrawImage(BM1, 0, 0, Width, Height,   {now draw the image stretched where needed}
                      0, 0, Image.Width, Image.Height);
        DrawImage(BM2, X, Y);     {now draw the image stretched where needed}
      finally
        g2.Free;
        end;
    finally
      BM2.Free;
      end;
  finally
    g1.Free;
    end;
finally
  BM1.Free;
  end;
end;

procedure TGpGraphics.DrawImage(Image: TGPImage; X, Y: Integer);
begin
GdipDrawImageRectI (fGraphics, Image.fHandle, X, Y, Image.Width, Image.Height);   
end;

procedure TGPGraphics.DrawImage(Image: TGpImage; x, y,
            srcx, srcy, srcwidth, srcheight: integer);
const
  UnitPixel = 2;
begin
GdipDrawImagePointRect(fGraphics, Image.fHandle, x, y,
                              srcx, srcy, srcwidth, srcheight, UnitPixel);
end;

procedure TGPGraphics.DrawImage(Image: TGpImage; dx, dy, dw, dh, sx, sy, sw, sh: integer);
const
  UnitPixel = 2;
begin
GdipDrawImageRectRectI(fGraphics, Image.fHandle, dx, dy, dw, dh,
                              sx, sy, sw, sh, UnitPixel, 0, Nil, 0);

end;

procedure TGpGraphics.Clear (Color: Cardinal);
begin
GdipGraphicsClear (fGraphics, Color);
end;

procedure TGPGraphics.ScaleTransform(sx, sy: Single);
const
  MatrixOrderPrepend = 0;
begin
GdipScaleWorldTransform(fGraphics, sx, sy, MatrixOrderPrepend);
end;

{ TGpImage }

constructor TGpImage.Create(Filename: string; TmpFile: boolean = False);
var
    err: Integer;
    Buffer: array [0..511] of WideChar;
begin
Inherited Create;
if not FileExists (FileName) then
  raise EGDIPlus.Create (Format ('Image file %s not found.', [FileName]));
err := GdipLoadImageFromFile (StringToWideChar (FileName, Buffer, sizeof (Buffer)), fHandle);
if err <> 0 then
  raise EGDIPlus.Create(Format ('Can''t load image file %s.', [FileName]));
if TmpFile then
  fFilename := Filename;
end;

constructor TGpImage.Create(IStr: IStream);
var
    err: Integer;
begin
Inherited Create;
err := GdipLoadImageFromStream(IStr, fHandle);
if err <> 0 then
  raise EGDIPlus.Create('Can''t load image stream');
end;

destructor TGpImage.Destroy;
begin
GdipDisposeImage (fHandle);
if Length(fFilename) > 0 then
  try
    DeleteFile(fFilename);
  except
    end;
inherited;
end;

function TGpImage.GetWidth: integer;
begin
if fWidth = 0 then
  GdipGetImageWidth (fHandle, fWidth);
Result := fWidth;
end;

function TGpImage.GetHeight: integer;
begin
if fHeight = 0 then
  GdipGetImageHeight (fHandle, fHeight);
Result := fHeight;
end;

function TGpImage.GetTBitmap: TBitmap;
var
  g: TGpGraphics;
begin
Result := TBitmap.Create;
Result.Width := GetWidth;
Result.Height := GetHeight;
PatBlt(Result.Canvas.Handle,  0, 0, Result.Width, Result.Height, Whiteness);
g := TGpGraphics.Create(Result.Canvas.Handle);
g.DrawImage(Self, 0, 0, Result.Width, Result.Height);
g.Free;
end;

constructor TGpBitmap.Create(W, H: integer);
const
  PixelFormatGDI = $00020000; // Is a GDI-supported format
  PixelFormatAlpha = $00040000; // Has an alpha component
  PixelFormatCanonical = $00200000;
  PixelFormat32bppARGB = (10 or (32 shl 8) or PixelFormatAlpha or PixelFormatGDI
                       or PixelFormatCanonical);
var
  err: integer;
begin
inherited Create;
err := GdipCreateBitmapFromScan0(W, H, 0, PixelFormat32bppARGB, nil, fHandle);
if err <> 0 then
  raise EGDIPlus.Create('Can''t create bitmap');
end;

constructor TGpBitmap.Create(IStr: IStream);
var
  err: integer;
begin
inherited Create;
err := GdipCreateBitmapFromStream(IStr, fHandle);
if err <> 0 then
  raise EGDIPlus.Create('Can''t create bitmap');
end;

constructor TGpBitmap.Create(W, H: integer; Graphics: TGpGraphics);
begin
inherited Create;
err := GdipCreateBitmapFromGraphics(W, H, Graphics.fGraphics, fHandle);
if err <> 0 then
  raise EGDIPlus.Create('Can''t create bitmap');
end;

function TGpBitmap.GetPixel(X, Y: integer): DWord;
begin
GdipBitmapGetPixel(fHandle, X, Y, Result);
end;

procedure TGpBitmap.SetPixel(X, Y: integer; Color: DWord);   
begin
GdipBitmapSetPixel(fHandle, X, Y, Color);
end;

{$ifndef NoGDIPlus}
var
  InitToken: DWord;
  Startup: TGDIStartup;
  LibHandle: THandle;
  GDIPlusCount: integer;  
{$endif}

procedure CheckInitGDIPlus;   
begin
{$ifndef NoGDIPlus}
if GDIPlusCount = 0 then
  begin
  LibHandle := LoadLibrary(GdiPlusLib);
  if LibHandle <> 0 then
    begin
    @GdiplusStartup := GetProcAddress(LibHandle, 'GdiplusStartup');
    @GdiplusShutdown := GetProcAddress(LibHandle, 'GdiplusShutdown');
    @GdipDeleteGraphics := GetProcAddress(LibHandle, 'GdipDeleteGraphics');
    @GdipCreateFromHDC := GetProcAddress(LibHandle, 'GdipCreateFromHDC');
    @GdipDrawImageI := GetProcAddress(LibHandle, 'GdipDrawImageI');
    @GdipDrawImageRectI := GetProcAddress(LibHandle, 'GdipDrawImageRectI');
    @GdipLoadImageFromFile := GetProcAddress(LibHandle, 'GdipLoadImageFromFile');
    @GdipLoadImageFromStream := GetProcAddress(LibHandle, 'GdipLoadImageFromStream');
    @GdipCreateBitmapFromStream:= GetProcAddress(LibHandle, 'GdipCreateBitmapFromStream');
    @GdipDisposeImage := GetProcAddress(LibHandle, 'GdipDisposeImage');
    @GdipGetImageWidth := GetProcAddress(LibHandle, 'GdipGetImageWidth');
    @GdipGetImageHeight := GetProcAddress(LibHandle, 'GdipGetImageHeight');
    @GdipGetImageGraphicsContext := GetProcAddress(LibHandle, 'GdipGetImageGraphicsContext');
    @GdipGraphicsClear := GetProcAddress(LibHandle, 'GdipGraphicsClear');
    @GdipCreateBitmapFromScan0 := GetProcAddress(LibHandle, 'GdipCreateBitmapFromScan0');
    @GdipDrawImagePointRect := GetProcAddress(LibHandle, 'GdipDrawImagePointRect');
    @GdipScaleWorldTransform := GetProcAddress(LibHandle,  'GdipScaleWorldTransform');
    @GdipCreateBitmapFromGraphics := GetProcAddress(LibHandle,  'GdipCreateBitmapFromGraphics');
    @GdipBitmapGetPixel := GetProcAddress(LibHandle,  'GdipBitmapGetPixel');
    @GdipDrawImageRectRectI := GetProcAddress(LibHandle,  'GdipDrawImageRectRectI');
    @GdipCreateHBITMAPFromBitmap := GetProcAddress(LibHandle,  'GdipCreateHBITMAPFromBitmap');

    @GdipSetInterpolationMode := GetProcAddress(LibHandle, 'GdipSetInterpolationMode');
    @GdipGetInterpolationMode := GetProcAddress(LibHandle, 'GdipGetInterpolationMode');
    @GdipBitmapSetPixel := GetProcAddress(LibHandle, 'GdipBitmapSetPixel');

    FillChar (Startup, sizeof (Startup), 0);
    Startup.Version := 1;
    Err := GdiPlusStartup (InitToken, @Startup, nil);
    GDIPlusActive := Err = 0;
    if not GDIPlusActive then
      FreeLibrary(LibHandle);
    end;
  end;
Inc(GDIPlusCount);
{$endif}
end;

procedure CheckExitGDIPlus;   
begin
{$ifndef NoGDIPlus}
Dec(GDIPlusCount);
if GDIPlusCount = 0 then
  if GDIPlusActive then
    begin
    GdiplusShutdown (InitToken);
    FreeLibrary(LibHandle);
    GDIPlusActive := False;
    end;
{$endif}
end;

end.

