unit F_PE_Graphics;

////////////////////////////////////////////////////////////////////////////////////////////////////
interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls,
  U_PreviewEngine;

type
  TfrmPEGraphics = class(TCustomPreviewEngineForm)
    scbContainer: TScrollBox;
    imgPreview: TImage;
  private
    { Private declarations }
  protected
    function GetRenderers: TRendererList; override;
  public
    { Public declarations }
    procedure Clear; override;
  end;

var
  frmPEGraphics: TfrmPEGraphics;

////////////////////////////////////////////////////////////////////////////////////////////////////
implementation

uses
  Vcl.Imaging.jpeg, Vcl.Imaging.pngimage, Vcl.Imaging.GIFImg, System.Masks,
  System.Types, Winapi.Wincodec;

{$R *.dfm}

type
  { ------------------------------------------------------------------------------------------------ }
  TGraphicRenderer = class(TRenderer)
  private
    FGraphicClass: TGraphicClass;
  public
    constructor Create(const Parent: TCustomPreviewEngineForm;
                       const GraphicClass: TGraphicClass);
    function  CouldShowFile(const FileName: string): boolean; override;
    function  TryShowFile(const FileName: string): boolean; override;
    function  SummarizeInfo: string; override;
    procedure PopulateInfo(const Lines: IInfoLineDisplayer); override;
  end {TGraphicRenderer};

  { ------------------------------------------------------------------------------------------------ }
  TWICRenderer = class(TGraphicRenderer)
  public
    constructor Create(const Parent: TCustomPreviewEngineForm);
    function  CouldShowFile(const FileName: string): boolean; override;
  private
    class var FFactoryHolder: TWICImage;
  public
    class constructor Create;
    class destructor  Destroy;
  end {TWICRenderer};


{ ================================================================================================ }
{ TfrmPEGraphics }

{ ------------------------------------------------------------------------------------------------ }
function TfrmPEGraphics.GetRenderers: TRendererList;
begin
  Result := inherited;
  if Result.Count = 0 then begin
    Result.Add(TGraphicRenderer.Create(Self, TJPEGImage));
    Result.Add(TGraphicRenderer.Create(Self, TPNGImage));
    Result.Add(TGraphicRenderer.Create(Self, TGIFImage));
    Result.Add(TGraphicRenderer.Create(Self, TBitmap));
    Result.Add(TGraphicRenderer.Create(Self, TIcon));
    Result.Add(TGraphicRenderer.Create(Self, TMetafile));
    Result.Add(TWICRenderer.Create(Self));
  end;
end {TfrmPEGraphics.GetPreviewEngines};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmPEGraphics.Clear;
begin
  inherited;
  imgPreview.Picture.Graphic := nil;
end {TfrmPEGraphics.Clear};


{ ================================================================================================ }
{ TGraphicPreviewEngine }

{ ------------------------------------------------------------------------------------------------ }
constructor TGraphicRenderer.Create(const Parent: TCustomPreviewEngineForm;
                                         const GraphicClass: TGraphicClass);
begin
  inherited Create(Parent, GraphicClass.ClassName);
  FGraphicClass := GraphicClass;
  FDisplayName := FGraphicClass.ClassName.Substring(1);
end {TGraphicPreviewEngine.Create};

{ ------------------------------------------------------------------------------------------------ }
function TGraphicRenderer.CouldShowFile(const FileName: string): boolean;
var
  Masks: TArray<string>;
  Mask: string;
begin
  Masks := GraphicFileMask(FGraphicClass).Split([';']);
  for Mask in Masks do begin
    if MatchesMask(FileName, Mask) then begin
      Result := True;
      Exit;
    end;
  end;
  Result := False;
end {TGraphicPreviewEngine.CouldShowFile};

{ ------------------------------------------------------------------------------------------------ }
function TGraphicRenderer.TryShowFile(const FileName: string): boolean;
var
  G: TGraphic;
begin
  G := FGraphicClass.Create;
  try
    G.LoadFromFile(FileName);
    with TfrmPEGraphics(Form).imgPreview.Picture do begin
      try
        Graphic := G;
        if Graphic is TGIFImage then begin
          TGIFImage(Graphic).Animate := True;
        end;
        Result := True;
      except
        Result := False;
        Graphic := nil;
      end;
    end {with};
  finally
    G.Free;
  end;
end {TGraphicPreviewEngine.TryShowFile};

{ ------------------------------------------------------------------------------------------------ }
function TGraphicRenderer.SummarizeInfo: string;
var
  G: TGraphic;
begin
  G := TfrmPEGraphics(Form).imgPreview.Picture.Graphic;
  if G = nil then begin
    Result := '';
  end else begin
    Result := Format('%d x %d', [G.Width, G.Height]);
    if G is TGIFImage then begin
      if TGIFImage(G).Images.Count > 1 then
        Result := Result + Format('; %d frames', [TGIFImage(G).Images.Count]);
    end;
  end;
end {TGraphicRenderer.SummarizeInfo};

{ ------------------------------------------------------------------------------------------------ }
procedure TGraphicRenderer.PopulateInfo(const Lines: IInfoLineDisplayer);
const
  scGIFVersions: array[TGIFVersion] of string = ('Unknown', '87a', '89a');
  scJPEGPixelFormats: array[TJPEGPixelFormat] of string = ('24-bits', '8-bits');
  scWICImageFormats: array[TWICImageFormat] of string = ('BMP', 'PNG', 'JPEG', 'GIF', 'TIFF', 'WMPhoto', '(other)');
var
  G: TGraphic;
  Group: string;
  Chunk: TChunk;
  s: string;
  i: Integer;
begin
  G := TfrmPEGraphics(Form).imgPreview.Picture.Graphic;

  Group := 'Image';
  Lines.Add(Group, 'Width', 'px', G.Width);
  Lines.Add(Group, 'Height', 'px', G.Height);
  Lines.Add(Group, 'Graphic Class', G.ClassName);

  if G is TGIFImage then begin
    Group := 'Graphic Image Format';
    Lines.Add(Group, 'GIF version', Ord(TGIFImage(G).Version), scGIFVersions);
    Lines.Add(Group, 'Frames', TGIFImage(G).Images.Count);

  end else if G is TJPEGImage then begin
    Group := 'JPEG';
    Lines.Add(Group, 'Quality', TJPEGImage(G).CompressionQuality);
    Lines.Add(Group, 'Pixel format', Ord(TJPEGImage(G).PixelFormat), scJPEGPixelFormats);
    Lines.Add(Group, 'Smoothing', Ord(TJPEGImage(G).Smoothing), [DefaultFalseBoolStr, DefaultTrueBoolStr]);

  end else if G is TPngImage then begin
    Group := 'Portable Network Graphic';
    for i := 0 to TPngImage(G).Chunks.Count - 1 do begin
      Chunk := TPngImage(G).Chunks.Item[i];
      if Chunk is TChunkIHDR then begin
        with TChunkIHDR(Chunk) do begin
          Lines.Add(Group, 'Bit depth', BitDepth);
          Lines.Add(Group, 'Color type', ColorType);
          Lines.Add(Group, 'Compression method', CompressionMethod);
          Lines.Add(Group, 'Filter method', FilterMethod);
          Lines.Add(Group, 'Interlace method', InterlaceMethod);
        end {with};
      end else if Chunk is TChunkpHYs then begin
        with TChunkpHYs(Chunk) do begin
          if UnitType = utMeter then
            s := 'meter'
          else
            s := 'unit';
          Lines.Add(Group, 'Horizontal pixels per ' + s, PPUnitX);
          Lines.Add(Group, 'Vertical pixels per ' + s, PPUnitY);
        end {with};
      end else if Chunk is TChunkgAMA then begin
        Lines.Add(Group, 'Gamma', TChunkgAMA(Chunk).Gamma);
      end else if Chunk is TChunkPLTE then begin
        Lines.Add(Group, 'Palette colors', TChunkPLTE(Chunk).Count);
      end else if Chunk is TChunktIME then begin
        with TChunktIME(Chunk) do begin
          Lines.Add(Group, string(Chunk.Name), FormatDateTime('yyyy-mm-dd hh:nn:ss', EncodeDate(Year, Month, Day) + EncodeTime(Hour, Minute, Second, 0)));
        end;
      end else if Chunk is TChunktEXt then begin
        Lines.Add(Group, string(TChunktEXt(Chunk).Keyword), string(TChunktEXt(Chunk).Text));
      end;
    end {for Chunk};

  end else if G is TMetafile then begin
    Group := 'Metafile';
    Lines.Add(Group, 'Description', TMetafile(G).Description);
    Lines.Add(Group, 'Created By', TMetafile(G).CreatedBy);

  end else if G is TWICImage then begin
    Group := 'Windows Imaging Component';
    Lines.Add(Group, 'Image format', Ord(TWICImage(G).ImageFormat), scWICImageFormats);
    Lines.Add(Group, 'Encoder container format', GUIDToString(TWICImage(G).EncoderContainerFormat));

  end;
end {TGraphicPreviewEngine.PopulateInfo};

{ ================================================================================================ }
{ TWICPreviewEngine }

{ ------------------------------------------------------------------------------------------------ }
class constructor TWICRenderer.Create;
begin
  // The TWICImage.ImagingFactory is only initialized if there is at least one
  //  TWICImage extant, so we make sure to create one.
  FFactoryHolder := TWICImage.Create;
end {class TWICPreviewEngine.Create};
{ ------------------------------------------------------------------------------------------------ }
class destructor TWICRenderer.Destroy;
begin
  FreeAndNil(FFactoryHolder);
end {class TWICPreviewEngine.Destroy};

{ ------------------------------------------------------------------------------------------------ }
constructor TWICRenderer.Create(const Parent: TCustomPreviewEngineForm);
begin
  inherited Create(Parent, TWICImage);
end {TWICPreviewEngine.Create};

{ ------------------------------------------------------------------------------------------------ }
function TWICRenderer.CouldShowFile(const FileName: string): boolean;
var
  ppIDecoder: IWICBitmapDecoder;
begin
  Result := Succeeded(TWICImage.ImagingFactory
                      .CreateDecoderFromFilename(PChar(FileName),
                                                 GUID_NULL,
                                                 $FFFFFFFF,
                                                 WICDecodeMetadataCacheOnDemand,
                                                 ppIDecoder));
  // Clean up the decoder; we just needed to know if we could create one
  ppIDecoder := nil;
end {TWICPreviewEngine.CouldShowFile};



////////////////////////////////////////////////////////////////////////////////////////////////////
initialization
  TCustomPreviewEngineForm.RegisterForm(TfrmPEGraphics);

end.
