ソースを参照

add macOS code from ying32

KngStr 3 年 前
コミット
7e09f46489
1 ファイル変更311 行追加0 行削除
  1. 311 0
      Source/ksBitmapHelper.pas

+ 311 - 0
Source/ksBitmapHelper.pas

@@ -6,6 +6,7 @@
 {                                                       }
 {       Some from FlyUtils.TBitmapHelper                }
 {       IOS code from 凌风                              }
+{       macOS code from ying32                          }
 {                                                       }
 {*******************************************************}
 
@@ -198,6 +199,11 @@ uses
   FMX.Graphics.iOS, iOSapi.UIKit, iOSapi.Foundation, iOSapi.CoreGraphics,
   Macapi.ObjectiveC, FMX.Helpers.iOS, iOSapi.CocoaTypes,
   {$ENDIF}
+  {$IFDEF MACOS}
+  Macapi.AppKit, Macapi.ObjectiveC, FMX.Helpers.Mac, Macapi.Foundation,
+  Macapi.CocoaTypes, Macapi.CoreGraphics,
+  FMX.Canvas.Mac,
+  {$ENDIF}
   {$IFDEF MSWINDOWS}
   FMX.Canvas.D2D, Winapi.Wincodec, Winapi.Windows, Winapi.ActiveX,
   {$ENDIF}
@@ -208,6 +214,19 @@ uses
   System.SysUtils,
   System.Math;
 
+{$IFDEF MACOS}
+// copy from FMX.Canvas.Mac
+var
+  GlobalColorSpace: CGColorSpaceRef;
+
+function ColorSpace: CGColorSpaceRef;
+begin
+  if GlobalColorSpace = nil then
+    GlobalColorSpace := CGColorSpaceCreateDeviceRGB;
+  Result := GlobalColorSpace;
+end;
+{$ENDIF}
+
 
 //add by 爱吃猪头肉。
 type
@@ -303,6 +322,29 @@ type
     function Rotate(const AStream: TStream; const Angle: Single): Boolean; overload;
   end;
   {$ENDIF}
+  {$IFDEF MACOS}
+  TBitmapCodecMacOS = class(FMX.Canvas.Mac.TBitmapCodecQuartz)
+  private const
+    DefaultSaveQuality = 75;
+  private
+    class function IsGIFStream(const Stream: TStream): Boolean;
+    function LoadMovieFromStreamScaled(const AStream: TStream;
+      const Surface: TBitmapSurface; const FitSize: TPoint): Boolean;
+    function LoadMovieFromStream(const Stream: TStream;
+      const Surface: TBitmapSurface): Boolean;
+  public
+    class function GetImageSize(const AFileName: string): TPointF; overload;
+    class function GetImageSize(const AStream: TStream): TPointF; overload;
+    function LoadFromStream(const AStream: TStream; const Bitmap: TBitmapSurface;
+      const MaxSizeLimit: Cardinal): Boolean; override;
+    function LoadThumbnailFromStream(const AStream: TStream; const AFitWidth, AFitHeight: Single;
+      const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
+    function LoadThumbnailFromStream(const AStream: TStream; const ASrc, ADest: TRectF;
+      const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
+    function Rotate(const Extension: string; const Angle: Single; const Bitmap: TBitmapSurface): Boolean; overload;
+    function Rotate(const AStream: TStream; const Angle: Single): Boolean; overload;
+  end;
+  {$ENDIF}
   {$IFDEF MSWINDOWS}
   TBitmapCodecWIC = class(FMX.Canvas.D2D.TCustomBitmapCodecWIC)
   private
@@ -1117,6 +1159,9 @@ begin
     {$IFDEF MSWINDOWS}
     Result := TBitmapCodecWIC(CodecClass).GetImageSize(AStream)
     {$ENDIF}
+    {$IFDEF MACOS}
+    Result := TBitmapCodecMacOS(CodecClass).GetImageSize(AStream)
+    {$ENDIF}
   else
     Result := TPointF.Zero;
 end;
@@ -1163,6 +1208,9 @@ begin
       {$IFDEF MSWINDOWS}
       Result := TBitmapCodecWIC(Codec).LoadFromStream(AStream, Bitmap, MaxSizeLimit);
       {$ENDIF}
+      {$IFDEF MACOS}
+      Result := TBitmapCodecMacOS(Codec).LoadFromStream(AStream, Bitmap, MaxSizeLimit);
+      {$ENDIF}
     finally
       Codec.Free;
     end;
@@ -1193,6 +1241,9 @@ begin
       {$IFDEF MSWINDOWS}
       Result := TBitmapCodecWIC(Codec).LoadThumbnailFromStream(AStream, ASrc, ADest, UseEmbedded, Bitmap);
       {$ENDIF}
+      {$IFDEF MACOS}
+      Result := TBitmapCodecMacOS(Codec).LoadThumbnailFromStream(AStream, ASrc, ADest, UseEmbedded, Bitmap);
+      {$ENDIF}
     finally
       Codec.Free;
     end;
@@ -1224,6 +1275,9 @@ begin
       {$IFDEF MSWINDOWS}
       Result := TBitmapCodecWIC(Codec).Rotate(AStream, Angle);
       {$ENDIF}
+      {$IFDEF MACOS}
+      Result := TBitmapCodecMacOS(Codec).Rotate(AStream, Angle);
+      {$ENDIF}
     finally
       Codec.Free;
     end;
@@ -1253,6 +1307,9 @@ begin
       {$IFDEF MSWINDOWS}
       Result := TBitmapCodecWIC(Codec).Rotate(Extension, Angle, Bitmap);
       {$ENDIF}
+      {$IFDEF MACOS}
+      Result := TBitmapCodecMacOS(Codec).Rotate(Extension, Angle, Bitmap);
+      {$ENDIF}
     finally
       Codec.Free;
     end;
@@ -1284,6 +1341,9 @@ begin
       {$IFDEF MSWINDOWS}
       Result := TBitmapCodecWIC(Codec).LoadThumbnailFromStream(AStream, AFitWidth, AFitHeight, UseEmbedded, AutoCut, Bitmap);
       {$ENDIF}
+      {$IFDEF MACOS}
+      Result := TBitmapCodecMacOS(Codec).LoadThumbnailFromStream(AStream, AFitWidth, AFitHeight, UseEmbedded, Bitmap);
+      {$ENDIF}
     finally
       Codec.Free;
     end;
@@ -2358,4 +2418,255 @@ begin
 end;
 {$ENDIF}
 
+{ TBitmapCodecMacOS }
+{$IFDEF MacOS}
+class function TBitmapCodecMacOS.GetImageSize(const AFileName: string): TPointF;
+begin
+  Result := inherited GetImageSize(AFileName);
+end;
+
+class function TBitmapCodecMacOS.GetImageSize(const AStream: TStream): TPointF;
+var
+  Img: NSImage;
+  TempStream: TMemoryStream;
+  aData: NSData;
+  SavePosition: Int64;
+begin
+  SavePosition := AStream.Position;
+  try
+    TempStream := TMemoryStream.Create;
+    try
+      AStream.Position := 0;
+      TempStream.CopyFrom(AStream, AStream.Size);
+      aData := TNSData.Wrap(TNSData.alloc.initWithBytesNoCopy(TempStream.Memory,TempStream.Size,False));
+      if aData.length > 0 then
+      begin
+        Img := TNSImage.Wrap(TNSImage.alloc.initWithData(aData));
+        if Img <> nil then
+        try
+          Result := PointF(Img.Size.width, Img.Size.height);
+        finally
+          Img.release;
+        end
+        else
+          Result := TPointF.Zero;
+      end else
+        Result := TPointF.Zero;
+    finally
+      TempStream.free;
+    end;
+  finally
+    AStream.Position := SavePosition;
+  end;
+end;
+
+class function TBitmapCodecMacOS.IsGIFStream(const Stream: TStream): Boolean;
+begin
+  Result := TImageTypeChecker.GetType(Stream) = SGIFImageExtension;
+end;
+
+function TBitmapCodecMacOS.LoadFromStream(const AStream: TStream;
+  const Bitmap: TBitmapSurface; const MaxSizeLimit: Cardinal): Boolean;
+begin
+  Result := inherited LoadFromStream(AStream, Bitmap, MaxSizeLimit);
+end;
+
+function TBitmapCodecMacOS.LoadMovieFromStream(const Stream: TStream;
+  const Surface: TBitmapSurface): Boolean;
+begin
+  Result := False;
+end;
+
+function TBitmapCodecMacOS.LoadMovieFromStreamScaled(const AStream: TStream;
+  const Surface: TBitmapSurface; const FitSize: TPoint): Boolean;
+begin
+  Result := False;
+end;
+
+function TBitmapCodecMacOS.LoadThumbnailFromStream(const AStream: TStream; const AFitWidth, AFitHeight: Single;
+  const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean;
+var
+  Img: NSImage;
+  ImgRef: CGImageRef;
+  CtxRef: CGContextRef;
+  R: TRectF;
+
+  TempStream: TMemoryStream;
+  aData: NSData;
+  SavePosition: Int64;
+begin
+  Result := False;
+
+  SavePosition := AStream.Position;
+  try
+    TempStream := TMemoryStream.Create;
+    try
+      AStream.Position := 0;
+      TempStream.CopyFrom(AStream, AStream.Size);
+      aData := TNSData.Wrap(TNSData.alloc.initWithBytesNoCopy(TempStream.Memory,TempStream.Size,False));
+      if aData.length > 0 then
+      begin
+        Img := TNSImage.Wrap(TNSImage.alloc.initWithData(aData));
+        if Img <> nil then
+        try
+          ImgRef := CGImageRef(Img);
+          if ImgRef <> nil then
+          begin
+            R := TRectF.Create(0, 0, CGImageGetWidth(ImgRef), CGImageGetHeight(ImgRef));
+            R.Fit(TRectF.Create(0, 0, AFitWidth, AFitHeight));
+
+            Bitmap.SetSize(Round(R.Width), Round(R.Height), TPixelFormat.RGBA);
+
+            CtxRef := CGBitmapContextCreate(Bitmap.Bits, Bitmap.Width, Bitmap.Height, 8, Bitmap.Pitch, ColorSpace,
+              kCGImageAlphaPremultipliedLast);
+            try
+              CGContextDrawImage(CtxRef, CGRectMake(0, 0, Bitmap.Width, Bitmap.Height), imgRef);
+            finally
+              CGContextRelease(CtxRef);
+            end;
+
+            Result := True;
+          end;
+        finally
+          Img.release;
+        end;
+      end;
+    finally
+      TempStream.free;
+    end;
+  finally
+    AStream.Position := SavePosition;
+  end;
+end;
+
+function TBitmapCodecMacOS.LoadThumbnailFromStream(const AStream: TStream;
+  const ASrc, ADest: TRectF; const UseEmbedded: Boolean;
+  const Bitmap: TBitmapSurface): Boolean;
+var
+  Img: NSImage;
+  ImgRef: CGImageRef;
+  CtxRef: CGContextRef;
+  R: TRectF;
+
+  TempStream: TMemoryStream;
+  aData: NSData;
+  SavePosition: Int64;
+begin
+  Result := False;
+
+  SavePosition := AStream.Position;
+  try
+    TempStream := TMemoryStream.Create;
+    try
+      AStream.Position := 0;
+      TempStream.CopyFrom(AStream, AStream.Size);
+      aData := TNSData.Wrap(TNSData.alloc.initWithBytesNoCopy(TempStream.Memory,TempStream.Size,False));
+      if aData.length > 0 then
+      begin
+        Img := TNSImage.Wrap(TNSImage.alloc.initWithData(aData));
+        if Img <> nil then
+        try
+          ImgRef := CGImageRef(Img);
+          if ImgRef <> nil then
+          begin
+            R := TRectF.Create(0, 0, ASrc.Width, ASrc.Height);
+            R.Fit(TRectF.Create(0, 0, ADest.Width, ADest.Height));
+
+            Bitmap.SetSize(Round(R.Width), Round(R.Height), TPixelFormat.RGBA);
+
+            CtxRef := CGBitmapContextCreate(Bitmap.Bits, Bitmap.Width, Bitmap.Height, 8, Bitmap.Pitch, ColorSpace,
+              kCGImageAlphaPremultipliedLast);
+            try
+              CGContextDrawImage(CtxRef, CGRectMake(0, 0, Bitmap.Width, Bitmap.Height), imgRef);
+            finally
+              CGContextRelease(CtxRef);
+            end;
+
+            Result := True;
+          end;
+        finally
+          Img.release;
+        end;
+
+      end;
+    finally
+      TempStream.free;
+    end;
+  finally
+    AStream.Position := SavePosition;
+  end;
+end;
+
+function TBitmapCodecMacOS.Rotate(const Extension: string; const Angle: Single;
+  const Bitmap: TBitmapSurface): Boolean;
+//var
+//  Img: NSImage;
+//  ImageRef: CGImageRef;
+//  CtxRef: CGContextRef;
+//  BitmapSize: TSize;
+//  ColorSpace: CGColorSpaceRef;
+//  bp: TBitmap;
+begin
+//  Result := False;
+  Result := inherited;
+
+//  bp := UIImageToBitmap(BitmapSurfaceToMacImage(Bitmap), Angle, TSize.Create(Bitmap.Width, Bitmap.Height));
+//  try
+//    ImageRef := CGImageRef(BitmapToMacBitmap(Bitmap));
+//    if ImageRef <> nil then
+//    begin
+//      BitmapSize := TSize.Create(CGImageGetWidth(ImageRef), CGImageGetHeight(ImageRef));
+//      Bitmap.Clear(TAlphaColorRec.Null);
+//      Bitmap.SetSize(BitmapSize.cx, BitmapSize.cy);
+//      ColorSpace := CGColorSpaceCreateDeviceRGB;
+//      try
+//        CtxRef := CGBitmapContextCreate(Bitmap.Bits, Bitmap.Width, Bitmap.Height, 8,
+//          Bitmap.Pitch, ColorSpace, kCGImageAlphaPremultipliedLast or kCGBitmapByteOrder32Big);
+//        try
+//          CGContextDrawImage(CtxRef, CGRectMake(0, 0, Bitmap.Width, Bitmap.Height), ImageRef);
+//        finally
+//          CGContextRelease(CtxRef);
+//        end;
+//        Result := True;
+//      finally
+//        CGColorSpaceRelease(ColorSpace);
+//      end;
+//    end
+//  finally
+//    bp.Free;
+//  end;
+end;
+
+function TBitmapCodecMacOS.Rotate(const AStream: TStream;
+  const Angle: Single): Boolean;
+var bp: TBitmap;
+    DataType: string;
+begin
+  Result := False;
+  DataType := TImageTypeChecker.GetType(AStream);
+  if DataType = SGIFImageExtension then
+    Exit;
+
+  bp:= TBitmap.Create;
+  try
+    AStream.Position := 0;
+    bp.LoadFromStream(AStream);
+    bp.Rotate(Angle);
+    AStream.Size := 0;
+    bp.SaveToStream(AStream, DataType);
+  finally
+    bp.Free;
+  end;
+end;
+{$ENDIF}
+
+
+{$IFDEF MACOS}
+initialization
+
+finalization
+  if GlobalColorSpace <> nil then
+    CGColorSpaceRelease(GlobalColorSpace);
+{$ENDIF}
+
 end.