Browse Source

add ios code for bitmaphelper(Thanks for the code from 凌风)

KngStr 3 years ago
parent
commit
25236eb379
1 changed files with 302 additions and 8 deletions
  1. 302 8
      Source/ksBitmapHelper.pas

+ 302 - 8
Source/ksBitmapHelper.pas

@@ -4,11 +4,13 @@
 {                                                       }
 {       CopyRight (C) 2018-2020 KngStr                  }
 {                                                       }
+{       Some from FlyUtils.TBitmapHelper                }
+{       IOS code from 凌风                              }
+{                                                       }
 {*******************************************************}
 
-unit ksBitmapHelper;
 
-// 部分拷贝自 FlyUtils.TBitmapHelper
+unit ksBitmapHelper;
 
 {$DEFINE FMX}
 
@@ -192,6 +194,10 @@ uses
   Androidapi.JNI.GraphicsContentViewText,
   Androidapi.JNI.JavaTypes,
   {$ENDIF}
+  {$IFDEF IOS}
+  FMX.Graphics.iOS, iOSapi.UIKit, iOSapi.Foundation, iOSapi.CoreGraphics,
+  Macapi.ObjectiveC, FMX.Helpers.iOS, iOSapi.CocoaTypes,
+  {$ENDIF}
   {$IFDEF MSWINDOWS}
   FMX.Canvas.D2D, Winapi.Wincodec, Winapi.Windows, Winapi.ActiveX,
   {$ENDIF}
@@ -274,6 +280,29 @@ type
     function Rotate(const AStream: TStream; const Angle: Single): Boolean; overload;
   end;
   {$ENDIF}
+  {$IFDEF iOS}
+  TBitmapCodecIOS = class(FMX.Graphics.iOS.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
@@ -1081,7 +1110,11 @@ begin
   if CodecClass <> nil then
     {$IFDEF ANDROID}
     Result := TBitmapCodecAndroid(CodecClass).GetImageSize(AStream)
-    {$ELSE}
+    {$ENDIF}
+    {$IFDEF IOS}
+    Result := TBitmapCodecIOS(CodecClass).GetImageSize(AStream)
+    {$ENDIF}
+    {$IFDEF MSWINDOWS}
     Result := TBitmapCodecWIC(CodecClass).GetImageSize(AStream)
     {$ENDIF}
   else
@@ -1123,7 +1156,11 @@ begin
     try
       {$IFDEF ANDROID}
       Result := TBitmapCodecAndroid(Codec).LoadFromStream(AStream, Bitmap, MaxSizeLimit);
-      {$ELSE}
+      {$ENDIF}
+      {$IFDEF IOS}
+      Result := TBitmapCodecIOS(Codec).LoadFromStream(AStream, Bitmap, MaxSizeLimit);
+      {$ENDIF}
+      {$IFDEF MSWINDOWS}
       Result := TBitmapCodecWIC(Codec).LoadFromStream(AStream, Bitmap, MaxSizeLimit);
       {$ENDIF}
     finally
@@ -1149,7 +1186,11 @@ begin
     try
       {$IFDEF ANDROID}
       Result := TBitmapCodecAndroid(Codec).LoadThumbnailFromStream(AStream, ASrc, ADest, UseEmbedded, Bitmap);
-      {$ELSE}
+      {$ENDIF}
+      {$IFDEF IOS}
+      Result := TBitmapCodecIOS(Codec).LoadThumbnailFromStream(AStream, ASrc, ADest, UseEmbedded, Bitmap);
+      {$ENDIF}
+      {$IFDEF MSWINDOWS}
       Result := TBitmapCodecWIC(Codec).LoadThumbnailFromStream(AStream, ASrc, ADest, UseEmbedded, Bitmap);
       {$ENDIF}
     finally
@@ -1176,7 +1217,11 @@ begin
     try
       {$IFDEF ANDROID}
       Result := TBitmapCodecAndroid(Codec).Rotate(AStream, Angle);
-      {$ELSE}
+      {$ENDIF}
+      {$IFDEF IOS}
+      Result := TBitmapCodecIOS(Codec).Rotate(AStream, Angle);
+      {$ENDIF}
+      {$IFDEF MSWINDOWS}
       Result := TBitmapCodecWIC(Codec).Rotate(AStream, Angle);
       {$ENDIF}
     finally
@@ -1201,7 +1246,11 @@ begin
     try
       {$IFDEF ANDROID}
       Result := TBitmapCodecAndroid(Codec).Rotate(Extension, Angle, Bitmap);
-      {$ELSE}
+      {$ENDIF}
+      {$IFDEF IOS}
+      Result := TBitmapCodecIOS(Codec).Rotate(Extension, Angle, Bitmap);
+      {$ENDIF}
+      {$IFDEF MSWINDOWS}
       Result := TBitmapCodecWIC(Codec).Rotate(Extension, Angle, Bitmap);
       {$ENDIF}
     finally
@@ -1228,7 +1277,11 @@ begin
     try
       {$IFDEF ANDROID}
       Result := TBitmapCodecAndroid(Codec).LoadThumbnailFromStream(AStream, AFitWidth, AFitHeight, UseEmbedded, AutoCut, Bitmap);
-      {$ELSE}
+      {$ENDIF}
+      {$IFDEF IOS}
+      Result := TBitmapCodecIOS(Codec).LoadThumbnailFromStream(AStream, AFitWidth, AFitHeight, UseEmbedded, Bitmap);
+      {$ENDIF}
+      {$IFDEF MSWINDOWS}
       Result := TBitmapCodecWIC(Codec).LoadThumbnailFromStream(AStream, AFitWidth, AFitHeight, UseEmbedded, AutoCut, Bitmap);
       {$ENDIF}
     finally
@@ -2064,4 +2117,245 @@ begin
 end;
 {$ENDIF}
 
+{ TBitmapCodecIOS }
+{$IFDEF IOS}
+class function TBitmapCodecIOS.GetImageSize(const AFileName: string): TPointF;
+begin
+  Result := inherited GetImageSize(AFileName);
+end;
+
+class function TBitmapCodecIOS.GetImageSize(const AStream: TStream): TPointF;
+var
+  Img: UIImage;
+  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 := TUIImage.Wrap(TUIImage.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 TBitmapCodecIOS.IsGIFStream(const Stream: TStream): Boolean;
+begin
+  Result := TImageTypeChecker.GetType(Stream) = SGIFImageExtension;
+end;
+
+function TBitmapCodecIOS.LoadFromStream(const AStream: TStream;
+  const Bitmap: TBitmapSurface; const MaxSizeLimit: Cardinal): Boolean;
+begin
+  Result := inherited LoadFromStream(AStream, Bitmap, MaxSizeLimit);
+end;
+
+function TBitmapCodecIOS.LoadMovieFromStream(const Stream: TStream;
+  const Surface: TBitmapSurface): Boolean;
+begin
+  Result := False;
+end;
+
+function TBitmapCodecIOS.LoadMovieFromStreamScaled(const AStream: TStream;
+  const Surface: TBitmapSurface; const FitSize: TPoint): Boolean;
+begin
+  Result := False;
+end;
+
+function TBitmapCodecIOS.LoadThumbnailFromStream(const AStream: TStream; const AFitWidth, AFitHeight: Single;
+  const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean;
+var
+  Img: UIImage;
+  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 := TUIImage.Wrap(TUIImage.alloc.initWithData(aData));
+        if Img <> nil then
+        try
+          ImgRef := Img.cGImage;
+          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 TBitmapCodecIOS.LoadThumbnailFromStream(const AStream: TStream;
+  const ASrc, ADest: TRectF; const UseEmbedded: Boolean;
+  const Bitmap: TBitmapSurface): Boolean;
+var
+  Img: UIImage;
+  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 := TUIImage.Wrap(TUIImage.alloc.initWithData(aData));
+        if Img <> nil then
+        try
+          ImgRef := Img.cGImage;
+          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 TBitmapCodecIOS.Rotate(const Extension: string; const Angle: Single;
+  const Bitmap: TBitmapSurface): Boolean;
+var
+  Img: UIImage;
+  ImageRef: CGImageRef;
+  CtxRef: CGContextRef;
+  BitmapSize: TSize;
+  ColorSpace: CGColorSpaceRef;
+  bp: TBitmap;
+begin
+  Result := False;
+
+  bp := UIImageToBitmap(BitmapSurfaceToUIImage(Bitmap), Angle, TSize.Create(Bitmap.Width, Bitmap.Height));
+  try
+    ImageRef := BitmapToUIImage(bp).CGImage;
+    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 TBitmapCodecIOS.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}
+
 end.