2
0
Эх сурвалжийг харах

ADD: HEIC image format support (via libheif)

Alexander Koblov 3 жил өмнө
parent
commit
ed797fe36d

+ 288 - 0
src/platform/udcreadheif.pas

@@ -0,0 +1,288 @@
+{
+   Double Commander
+   -------------------------------------------------------------------------
+   High Efficiency Image reader implementation (via libheif)
+
+   Copyright (C) 2021 Alexander Koblov ([email protected])
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this program. If not, see <http://www.gnu.org/licenses/>.
+}
+
+unit uDCReadHEIF;
+
+{$mode delphi}
+{$packrecords c}
+{$packenum 4}
+
+interface
+
+uses
+  Classes, SysUtils, Graphics, FPImage;
+
+type
+
+  { TDCReaderHEIF }
+
+  TDCReaderHEIF = class (TFPCustomImageReader)
+  private
+    FContext: Pointer;
+  protected
+    function  InternalCheck (Stream: TStream): boolean;override;
+    procedure InternalRead({%H-}Stream: TStream; Img: TFPCustomImage);override;
+  public
+    constructor Create; override;
+    destructor Destroy; override;
+  end;
+
+  { THighEfficiencyImage }
+
+  THighEfficiencyImage = class(TFPImageBitmap)
+  protected
+    class function GetReaderClass: TFPCustomImageReaderClass; override;
+    class function GetSharedImageClass: TSharedRasterImageClass; override;
+  public
+    class function GetFileExtensions: string; override;
+  end;
+
+implementation
+
+uses
+  DynLibs, IntfGraphics, GraphType, Types, CTypes, LazUTF8, DCOSUtils, uDebug;
+
+const
+  HEIF_EXT = 'heif;heic;avif';
+
+type
+
+  Theif_error_code =
+  (
+    heif_error_Ok = 0,
+    heif_error_Input_does_not_exist = 1,
+    heif_error_Invalid_input = 2,
+    heif_error_Unsupported_filetype = 3,
+    heif_error_Unsupported_feature = 4,
+    heif_error_Usage_error = 5,
+    heif_error_Memory_allocation_error = 6,
+    heif_error_Decoder_plugin_error = 7,
+    heif_error_Encoder_plugin_error = 8,
+    heif_error_Encoding_error = 9,
+    heif_error_Color_profile_does_not_exist = 10
+  );
+
+  Theif_colorspace =
+  (
+    heif_colorspace_YCbCr = 0,
+    heif_colorspace_RGB = 1,
+    heif_colorspace_monochrome = 2,
+    heif_colorspace_undefined = 99
+  );
+
+  Theif_channel =
+  (
+    heif_channel_Y = 0,
+    heif_channel_Cb = 1,
+    heif_channel_Cr = 2,
+    heif_channel_R = 3,
+    heif_channel_G = 4,
+    heif_channel_B = 5,
+    heif_channel_Alpha = 6,
+    heif_channel_interleaved = 10
+  );
+
+  Theif_chroma =
+  (
+    heif_chroma_monochrome = 0,
+    heif_chroma_420 = 1,
+    heif_chroma_422 = 2,
+    heif_chroma_444 = 3,
+    heif_chroma_interleaved_RGB = 10,
+    heif_chroma_interleaved_RGBA = 11,
+    heif_chroma_interleaved_RRGGBB_BE = 12,
+    heif_chroma_interleaved_RRGGBBAA_BE = 13,
+    heif_chroma_interleaved_RRGGBB_LE = 14,
+    heif_chroma_interleaved_RRGGBBAA_LE = 15,
+    heif_chroma_undefined = 99
+  );
+
+  Theif_context = record end;
+  Pheif_context = ^Theif_context;
+
+  Theif_error = record
+    code: Theif_error_code;
+    subcode: UInt32;
+    message: PAnsiChar;
+  end;
+
+var
+  heif_context_alloc: function(): Pheif_context; cdecl;
+  heif_context_free: procedure(context: Pheif_context); cdecl;
+
+  heif_context_read_from_memory_without_copy: function(context: Pheif_context;
+                                                       mem: Pointer; size: csize_t;
+                                                       options: Pointer): Theif_error; cdecl;
+
+  heif_context_get_primary_image_handle: function(ctx: Pheif_context;
+                                                  image_handle: PPointer): Theif_error; cdecl;
+  heif_image_handle_release: procedure(heif_image_handle: Pointer); cdecl;
+
+  heif_image_handle_has_alpha_channel: function(image_handle: Pointer): cint; cdecl;
+
+  heif_decode_image: function(in_handle: Pointer; out_img: PPointer;
+                              colorspace: Theif_colorspace; chroma: Theif_chroma;
+                              options: Pointer): Theif_error; cdecl;
+  heif_image_release: procedure(heif_image: Pointer); cdecl;
+
+  heif_image_get_width: function(heif_image: Pointer; channel: Theif_channel): cint; cdecl;
+  heif_image_get_height: function(heif_image: Pointer; channel: Theif_channel): cint; cdecl;
+
+  heif_image_get_plane_readonly: function(heif_image: Pointer;
+                                          channel: Theif_channel;
+                                          out_stride: pcint): pcuint8; cdecl;
+
+{ THighEfficiencyImage }
+
+class function THighEfficiencyImage.GetReaderClass: TFPCustomImageReaderClass;
+begin
+  Result:= TDCReaderHEIF;
+end;
+
+class function THighEfficiencyImage.GetSharedImageClass: TSharedRasterImageClass;
+begin
+  Result:= TSharedBitmap;
+end;
+
+class function THighEfficiencyImage.GetFileExtensions: string;
+begin
+  Result:= HEIF_EXT;
+end;
+
+{ TDCReaderHEIF }
+
+function TDCReaderHEIF.InternalCheck(Stream: TStream): boolean;
+var
+  Err: Theif_error;
+  MemoryStream: TMemoryStream absolute Stream;
+begin
+  Err:= heif_context_read_from_memory_without_copy(FContext, MemoryStream.Memory, MemoryStream.Size, nil);
+  Result:= (Err.code = heif_error_Ok);
+end;
+
+procedure TDCReaderHEIF.InternalRead(Stream: TStream; Img: TFPCustomImage);
+var
+  Alpha: cint;
+  AData: PByte;
+  AStride: cint;
+  Err: Theif_error;
+  Chroma: Theif_chroma;
+  AWidth, AHeight: cint;
+  AImage: Pointer = nil;
+  AHandle: Pointer = nil;
+  Description: TRawImageDescription;
+begin
+  Err:= heif_context_get_primary_image_handle(FContext, @AHandle);
+  if (Err.code <> heif_error_Ok) then raise Exception.Create(Err.message);
+
+  try
+    Alpha:= heif_image_handle_has_alpha_channel(AHandle);
+
+    if (Alpha <> 0) then
+      Chroma:= heif_chroma_interleaved_RGBA
+    else begin
+      Chroma:= heif_chroma_interleaved_RGB;
+    end;
+
+    Err:= heif_decode_image(AHandle, @AImage, heif_colorspace_RGB, Chroma, nil);
+    if (Err.code <> heif_error_Ok) then raise Exception.Create(Err.message);
+
+    try
+      AWidth:= heif_image_get_width(AImage, heif_channel_interleaved);
+      AHeight:= heif_image_get_height(AImage, heif_channel_interleaved);
+      AData:= heif_image_get_plane_readonly(AImage, heif_channel_interleaved, @AStride);
+
+      if (Alpha <> 0) then
+        Description.Init_BPP32_R8G8B8A8_BIO_TTB(AWidth, AHeight)
+      else begin
+        Description.Init_BPP24_R8G8B8_BIO_TTB(AWidth, AHeight);
+      end;
+      TLazIntfImage(Img).DataDescription:= Description;
+      Move(AData^, TLazIntfImage(Img).PixelData^, AStride * AHeight);
+    finally
+      heif_image_release(AImage);
+    end;
+  finally
+    heif_image_handle_release(AHandle);
+  end;
+end;
+
+constructor TDCReaderHEIF.Create;
+begin
+  inherited Create;
+  FContext:= heif_context_alloc();
+end;
+
+destructor TDCReaderHEIF.Destroy;
+begin
+  inherited Destroy;
+  if Assigned(FContext) then heif_context_free(FContext);
+end;
+
+const
+{$IF DEFINED(UNIX)}
+  heiflib   = 'libheif.so.1';
+{$ELSEIF DEFINED(MSWINDOWS)}
+  heiflib   = 'libheif-1.dll';
+{$ENDIF}
+
+var
+  libheif: TLibHandle;
+
+procedure Initialize;
+begin
+  libheif:= LoadLibrary(heiflib);
+
+  if (libheif <> NilHandle) then
+  try
+    @heif_context_alloc:= SafeGetProcAddress(libheif, 'heif_context_alloc');
+    @heif_context_free:= SafeGetProcAddress(libheif, 'heif_context_free');
+    @heif_decode_image:= SafeGetProcAddress(libheif, 'heif_decode_image');
+    @heif_image_release:= SafeGetProcAddress(libheif, 'heif_image_release');
+    @heif_image_get_width:= SafeGetProcAddress(libheif, 'heif_image_get_width');
+    @heif_image_get_height:= SafeGetProcAddress(libheif, 'heif_image_get_height');
+    @heif_image_handle_release:= SafeGetProcAddress(libheif, 'heif_image_handle_release');
+    @heif_image_get_plane_readonly:= SafeGetProcAddress(libheif, 'heif_image_get_plane_readonly');
+    @heif_image_handle_has_alpha_channel:= SafeGetProcAddress(libheif, 'heif_image_handle_has_alpha_channel');
+    @heif_context_get_primary_image_handle:= SafeGetProcAddress(libheif, 'heif_context_get_primary_image_handle');
+    @heif_context_read_from_memory_without_copy:= SafeGetProcAddress(libheif, 'heif_context_read_from_memory_without_copy');
+
+    // Register image handler and format
+    ImageHandlers.RegisterImageReader ('High Efficiency Image', HEIF_EXT, TDCReaderHEIF);
+    TPicture.RegisterFileFormat(HEIF_EXT, 'High Efficiency Image', THighEfficiencyImage);
+  except
+    on E: Exception do DCDebug(E.Message);
+  end;
+end;
+
+procedure Finalize;
+begin
+  if (libheif <> NilHandle) then FreeLibrary(libheif);
+end;
+
+initialization
+  Initialize;
+
+finalization
+  Finalize;
+
+end.
+

+ 2 - 2
src/platform/uosforms.pas

@@ -131,7 +131,7 @@ uses
   , uWinNetFileSource, uVfsModule, uLng, uMyWindows, DCStrUtils
   , uDCReadSVG, uFileSourceUtil, uGdiPlusJPEG, uListGetPreviewBitmap
   , Dialogs, Clipbrd, uShowMsg, uDebug, JwaDbt, uThumbnailProvider
-  , uRecycleBinFileSource
+  , uRecycleBinFileSource, uDCReadHEIF
     {$IFDEF LCLQT5}
     , qt5, qtwidgets, uDarkStyle
     {$ENDIF}
@@ -140,7 +140,7 @@ uses
   , BaseUnix, Errors, fFileProperties, uJpegThumb
     {$IF NOT DEFINED(DARWIN)}
     , uDCReadSVG, uMagickWand, uGio, uGioFileSource, uVfsModule, uVideoThumb
-    , uDCReadWebP, uFolderThumb, uAudioThumb, uDefaultTerminal
+    , uDCReadWebP, uFolderThumb, uAudioThumb, uDefaultTerminal, uDCReadHEIF
     {$ELSE}
     , MacOSAll, uQuickLook, uMyDarwin, uShowMsg, uLng
     {$ENDIF}