|
@@ -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.
|