|
@@ -1,6 +1,6 @@
|
|
|
unit ksBitmapHelper;
|
|
|
|
|
|
-// 非安卓部分拷贝自 FlyUtils.TBitmapHelper
|
|
|
+// 部分拷贝自 FlyUtils.TBitmapHelper
|
|
|
|
|
|
{$DEFINE FMX}
|
|
|
|
|
@@ -74,6 +74,9 @@ type
|
|
|
/// <summary>
|
|
|
/// 用于在线程中代替 LoadThumbnailFromFile ,不用自己调用 Synchronize 了。
|
|
|
/// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// UseEmbedded 只有Windows下可用,而且缩略图大小不可控
|
|
|
+ /// </remarks>
|
|
|
procedure SyncLoadThumbnailFromFile(const AFileName: string; const AFitWidth, AFitHeight: Single;
|
|
|
const UseEmbedded: Boolean = True);
|
|
|
/// <summary>
|
|
@@ -83,8 +86,13 @@ type
|
|
|
/// <summary>
|
|
|
/// 用于在线程中代替 LoadThumbnailFromStream ,不用自己调用 Synchronize 了。
|
|
|
/// </summary>
|
|
|
- procedure SyncLoadThumbnailFromStream(Stream: TStream; const AFitWidth, AFitHeight: Single;
|
|
|
- const UseEmbedded: Boolean = True; const AutoCut: Boolean = True);
|
|
|
+ /// <remarks>
|
|
|
+ /// UseEmbedded 只有Windows下可用,而且缩略图大小不可控
|
|
|
+ /// </remarks>
|
|
|
+ procedure SyncLoadThumbnailFromStream(const Stream: TStream; const AFitWidth, AFitHeight: Single;
|
|
|
+ const UseEmbedded: Boolean = True; const AutoCut: Boolean = True); overload;
|
|
|
+ function SyncLoadThumbnailFromStream(const AStream: TStream; const ASrc, ADest: TRectF;
|
|
|
+ const UseEmbedded: Boolean): Boolean; overload;
|
|
|
/// <summary>
|
|
|
/// 用于在线程中代替 Assign ,不用自己调用 Synchronize 了。
|
|
|
/// </summary>
|
|
@@ -102,8 +110,16 @@ type
|
|
|
class function GetImageSize(const AStream: TStream): TPointF; overload;
|
|
|
class function LoadFromStream(const AStream: TStream; const Bitmap: TBitmapSurface;
|
|
|
const MaxSizeLimit: Cardinal = 0): Boolean;
|
|
|
+ /// <remarks>
|
|
|
+ /// UseEmbedded 只有Windows下可用,而且缩略图大小不可控
|
|
|
+ /// </remarks>
|
|
|
class function LoadThumbnailFromStream(const AStream: TStream; const AFitWidth, AFitHeight: Single;
|
|
|
- const UseEmbedded: Boolean; const AutoCut: Boolean; const Bitmap: TBitmapSurface): Boolean;
|
|
|
+ const UseEmbedded: Boolean; const AutoCut: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
|
|
|
+ /// <remarks>
|
|
|
+ /// UseEmbedded 只有Windows下可用,而且缩略图大小不可控
|
|
|
+ /// </remarks>
|
|
|
+ class function LoadThumbnailFromStream(const AStream: TStream; const ASrc, ADest: TRectF;
|
|
|
+ const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
|
|
|
end;
|
|
|
{$ENDIF}
|
|
|
|
|
@@ -235,7 +251,9 @@ type
|
|
|
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 AutoCut: Boolean; const Bitmap: TBitmapSurface): Boolean;
|
|
|
+ const UseEmbedded: Boolean; const AutoCut: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
|
|
|
+ function LoadThumbnailFromStream(const AStream: TStream; const ASrc, ADest: TRectF;
|
|
|
+ const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
|
|
|
end;
|
|
|
{$ENDIF}
|
|
|
{$IFDEF MSWINDOWS}
|
|
@@ -246,7 +264,9 @@ type
|
|
|
public
|
|
|
class function GetImageSize(const AStream: TStream): TPointF; overload;
|
|
|
function LoadThumbnailFromStream(const AStream: TStream; const AFitWidth, AFitHeight: Single;
|
|
|
- const UseEmbedded: Boolean; const AutoCut: Boolean; const Bitmap: TBitmapSurface): Boolean;
|
|
|
+ const UseEmbedded: Boolean; const AutoCut: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
|
|
|
+ function LoadThumbnailFromStream(const AStream: TStream; const ASrc, ADest: TRectF;
|
|
|
+ const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
|
|
|
end;
|
|
|
{$ENDIF}
|
|
|
{$ENDIF}
|
|
@@ -909,7 +929,57 @@ begin
|
|
|
end;
|
|
|
end;
|
|
|
|
|
|
-procedure TKngStrBitmapHelper.SyncLoadThumbnailFromStream(Stream: TStream;
|
|
|
+function TKngStrBitmapHelper.SyncLoadThumbnailFromStream(const AStream: TStream;
|
|
|
+ const ASrc, ADest: TRectF; const UseEmbedded: Boolean): Boolean;
|
|
|
+var
|
|
|
+ S: TStream;
|
|
|
+ Surf: TBitmapSurface;
|
|
|
+begin
|
|
|
+ if AStream.Position > 0 then
|
|
|
+ begin
|
|
|
+ // need to create temp stream
|
|
|
+ S := TMemoryStream.Create;
|
|
|
+ try
|
|
|
+ S.CopyFrom(AStream, AStream.Size - AStream.Position);
|
|
|
+ S.Position := 0;
|
|
|
+ Surf := TBitmapSurface.Create;
|
|
|
+ try
|
|
|
+ if TBitmapCodecManager.LoadThumbnailFromStream(S, ASrc, ADest, UseEmbedded, Surf) then
|
|
|
+ TThread.Synchronize(nil,
|
|
|
+ procedure
|
|
|
+ begin
|
|
|
+ Assign(Surf);
|
|
|
+ end)
|
|
|
+ else
|
|
|
+ raise EBitmapLoadingFailed.Create(SThumbnailLoadingFailed);
|
|
|
+ finally
|
|
|
+ Surf.Free;
|
|
|
+ end;
|
|
|
+ finally
|
|
|
+ S.Free;
|
|
|
+ end;
|
|
|
+ end
|
|
|
+ else
|
|
|
+ if AStream.Size = 0 then
|
|
|
+ Clear(0)
|
|
|
+ else begin
|
|
|
+ Surf := TBitmapSurface.Create;
|
|
|
+ try
|
|
|
+ if TBitmapCodecManager.LoadThumbnailFromStream(AStream, ASrc, ADest, UseEmbedded, Surf) then
|
|
|
+ TThread.Synchronize(nil,
|
|
|
+ procedure
|
|
|
+ begin
|
|
|
+ Assign(Surf);
|
|
|
+ end)
|
|
|
+ else
|
|
|
+ raise EBitmapLoadingFailed.Create(SThumbnailLoadingFailed);
|
|
|
+ finally
|
|
|
+ Surf.Free;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TKngStrBitmapHelper.SyncLoadThumbnailFromStream(const Stream: TStream;
|
|
|
const AFitWidth, AFitHeight: Single; const UseEmbedded: Boolean; const AutoCut: Boolean);
|
|
|
var
|
|
|
S: TStream;
|
|
@@ -1023,6 +1093,32 @@ begin
|
|
|
end;
|
|
|
|
|
|
class function TKngStrBitmapCodecManager.LoadThumbnailFromStream(
|
|
|
+ const AStream: TStream; const ASrc, ADest: TRectF; const UseEmbedded: Boolean;
|
|
|
+ const Bitmap: TBitmapSurface): Boolean;
|
|
|
+var
|
|
|
+ CodecClass: TCustomBitmapCodecClass;
|
|
|
+ Codec: TCustomBitmapCodec;
|
|
|
+ DataType: String;
|
|
|
+begin
|
|
|
+ Result := False;
|
|
|
+ DataType := TImageTypeChecker.GetType(AStream);
|
|
|
+ CodecClass := GuessCodecClass(DataType, TBitmapCodecDescriptorField.Extension);
|
|
|
+ if CodecClass <> nil then
|
|
|
+ begin
|
|
|
+ Codec := CodecClass.Create;
|
|
|
+ try
|
|
|
+ {$IFDEF ANDROID}
|
|
|
+ Result := TBitmapCodecAndroid(Codec).LoadThumbnailFromStream(AStream, ASrc, ADest, UseEmbedded, Bitmap);
|
|
|
+ {$ELSE}
|
|
|
+ Result := TBitmapCodecWIC(Codec).LoadThumbnailFromStream(AStream, ASrc, ADest, UseEmbedded, Bitmap);
|
|
|
+ {$ENDIF}
|
|
|
+ finally
|
|
|
+ Codec.Free;
|
|
|
+ end;
|
|
|
+ end
|
|
|
+end;
|
|
|
+
|
|
|
+class function TKngStrBitmapCodecManager.LoadThumbnailFromStream(
|
|
|
const AStream: TStream; const AFitWidth, AFitHeight: Single;
|
|
|
const UseEmbedded: Boolean; const AutoCut: Boolean;
|
|
|
const Bitmap: TBitmapSurface): Boolean;
|
|
@@ -1314,6 +1410,72 @@ begin
|
|
|
end;
|
|
|
|
|
|
function TBitmapCodecAndroid.LoadThumbnailFromStream(const AStream: TStream;
|
|
|
+ const ASrc, ADest: TRectF; const UseEmbedded: Boolean;
|
|
|
+ const Bitmap: TBitmapSurface): Boolean;
|
|
|
+var
|
|
|
+ TempStream: TMemoryStream;
|
|
|
+ TempArray: TJavaArray<Byte>;
|
|
|
+ NativeBitmap1, NativeBitmap2: JBitmap;
|
|
|
+ LoadOptions: JBitmapFactory_Options;
|
|
|
+ SavePosition: Int64;
|
|
|
+begin
|
|
|
+ if IsGIFStream(AStream) then
|
|
|
+ Result := LoadMovieFromStreamScaled(AStream, Bitmap, TPoint.Create(Round(ADest.Width), Round(ADest.Height)))
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ SavePosition := AStream.Position;
|
|
|
+ try
|
|
|
+ TempStream := TMemoryStream.Create;
|
|
|
+ try
|
|
|
+ TempStream.CopyFrom(AStream, AStream.Size);
|
|
|
+ TempArray := TJavaArray<Byte>.Create(TempStream.Size);
|
|
|
+ Move(TempStream.Memory^, TempArray.Data^, TempStream.Size);
|
|
|
+ finally
|
|
|
+ TempStream.Free;
|
|
|
+ end;
|
|
|
+
|
|
|
+ LoadOptions := TJBitmapFactory_Options.JavaClass.init;
|
|
|
+ LoadOptions.inJustDecodeBounds := False;
|
|
|
+ NativeBitmap1 := TJBitmapFactory.JavaClass.decodeByteArray(TempArray, 0, TempArray.Length, LoadOptions);
|
|
|
+ TempArray := nil;
|
|
|
+ //kngstr
|
|
|
+ if NativeBitmap1 = nil then
|
|
|
+ Exit(False);
|
|
|
+ try
|
|
|
+ if (LoadOptions.outWidth < 1) or (LoadOptions.outHeight < 1) then
|
|
|
+ Exit(False);
|
|
|
+
|
|
|
+ // 剪裁
|
|
|
+ if (not ASrc.IsEmpty) and (ASrc.Width < LoadOptions.outWidth) and (ASrc.Height < LoadOptions.outHeight) then begin
|
|
|
+ NativeBitmap2 := TJBitmap.JavaClass.createBitmap(NativeBitmap1, Round(ASrc.Left), Round(ASrc.Top), Round(ASrc.Width), Round(ASrc.Height));
|
|
|
+ //kngstr
|
|
|
+ if NativeBitmap2 = nil then
|
|
|
+ Exit(False);
|
|
|
+ NativeBitmap1.recycle;
|
|
|
+ NativeBitmap1 := nil;
|
|
|
+ NativeBitmap1 := NativeBitmap2;
|
|
|
+ end;
|
|
|
+
|
|
|
+ //缩放
|
|
|
+ NativeBitmap2 := TJBitmap.JavaClass.createScaledBitmap(NativeBitmap1, Round(ADest.Width), Round(ADest.Height), True);
|
|
|
+ //kngstr
|
|
|
+ if NativeBitmap2 = nil then
|
|
|
+ Exit(False);
|
|
|
+ try
|
|
|
+ Result := JBitmapToSurface(NativeBitmap2, Bitmap)
|
|
|
+ finally
|
|
|
+ NativeBitmap2.recycle;
|
|
|
+ end;
|
|
|
+ finally
|
|
|
+ NativeBitmap1.recycle;
|
|
|
+ end;
|
|
|
+ finally
|
|
|
+ AStream.Position := SavePosition;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+function TBitmapCodecAndroid.LoadThumbnailFromStream(const AStream: TStream;
|
|
|
const AFitWidth, AFitHeight: Single; const UseEmbedded: Boolean;
|
|
|
const AutoCut: Boolean; const Bitmap: TBitmapSurface): Boolean;
|
|
|
var
|
|
@@ -1532,6 +1694,89 @@ begin
|
|
|
end;
|
|
|
|
|
|
function TBitmapCodecWIC.LoadThumbnailFromStream(const AStream: TStream;
|
|
|
+ const ASrc, ADest: TRectF; const UseEmbedded: Boolean;
|
|
|
+ const Bitmap: TBitmapSurface): Boolean;
|
|
|
+var
|
|
|
+ Decoder: IWICBitmapDecoder;
|
|
|
+ CopyStream: TMemoryStream;
|
|
|
+ Stream: IWICStream;
|
|
|
+ Frame: IWICBitmapFrameDecode;
|
|
|
+ Bmp: IWICBitmapSource;
|
|
|
+ Converter: IWICFormatConverter;
|
|
|
+ Scaler: IWICBitmapScaler;
|
|
|
+ Clipper: IWICBitmapClipper;
|
|
|
+ Width, Height: UINT;
|
|
|
+ WRect: WICRect;
|
|
|
+ LResult: HRESULT;
|
|
|
+begin
|
|
|
+ Result := False;
|
|
|
+ CopyStream := TMemoryStream.Create;
|
|
|
+ try
|
|
|
+ CopyStream.CopyFrom(AStream, AStream.Size);
|
|
|
+
|
|
|
+ ImagingFactory.CreateStream(Stream);
|
|
|
+ Stream.InitializeFromMemory(CopyStream.Memory, CopyStream.Size);
|
|
|
+
|
|
|
+ //kngstr
|
|
|
+ ImagingFactory.CreateDecoderFromStream(Stream, GUID_NULL, WICDecodeMetadataCacheOnDemand, Decoder);
|
|
|
+ if Decoder <> nil then begin
|
|
|
+ Decoder.GetFrame(0, Frame);
|
|
|
+ if UseEmbedded then
|
|
|
+ Frame.GetThumbnail(Bmp);
|
|
|
+ if Bmp <> nil then begin
|
|
|
+ ImagingFactory.CreateFormatConverter(Converter);
|
|
|
+ if Succeeded(Converter.Initialize(Bmp, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, nil, 0, 0)) then
|
|
|
+ begin
|
|
|
+ Converter.GetSize(Width, Height);
|
|
|
+
|
|
|
+ Bitmap.SetSize(Width, Height, TPixelFormat.BGRA);
|
|
|
+ Result := Succeeded(Converter.CopyPixels(nil, Bitmap.Pitch, Height * Cardinal(Bitmap.Pitch),
|
|
|
+ PByte(Bitmap.Bits)));
|
|
|
+ end;
|
|
|
+ end
|
|
|
+ else if Frame <> nil then begin
|
|
|
+ Frame.GetSize(Width, Height);
|
|
|
+
|
|
|
+ Clipper := nil;
|
|
|
+ if (not ASrc.IsEmpty) and (ASrc.Width < Width) and (ASrc.Height < Height) then begin
|
|
|
+ WRect.X := Trunc(ASrc.Left);
|
|
|
+ WRect.Y := Trunc(ASrc.Top);
|
|
|
+ WRect.Width := Trunc(ASrc.Width);
|
|
|
+ WRect.Height := Trunc(ASrc.Height);
|
|
|
+
|
|
|
+ // 剪裁
|
|
|
+ ImagingFactory.CreateBitmapClipper(Clipper);
|
|
|
+ if not Succeeded(Clipper.Initialize(frame, WRect)) then
|
|
|
+ Clipper := nil;
|
|
|
+ end;
|
|
|
+
|
|
|
+ LResult := S_FALSE;
|
|
|
+ // 缩放
|
|
|
+ ImagingFactory.CreateBitmapScaler(Scaler);
|
|
|
+ if (Clipper <> nil) then
|
|
|
+ LResult := Scaler.Initialize(Clipper, Trunc(ADest.Width), Trunc(ADest.Height), WICBitmapInterpolationModeLinear);
|
|
|
+ if not Succeeded(LResult) then
|
|
|
+ LResult := Scaler.Initialize(Frame, Trunc(ADest.Width), Trunc(ADest.Height), WICBitmapInterpolationModeLinear);
|
|
|
+
|
|
|
+ if Succeeded(LResult) then begin
|
|
|
+ ImagingFactory.CreateFormatConverter(Converter);
|
|
|
+ if Succeeded(Converter.Initialize(scaler, GUID_WICPixelFormat32bppPBGRA,
|
|
|
+ WICBitmapDitherTypeNone, nil, 0, 0)) then begin
|
|
|
+ Converter.GetSize(Width, Height);
|
|
|
+
|
|
|
+ Bitmap.SetSize(Width, Height, TPixelFormat.BGRA);
|
|
|
+ Result := Succeeded(Converter.CopyPixels(nil, Bitmap.Pitch, Height * Cardinal(Bitmap.Pitch),
|
|
|
+ PByte(Bitmap.Bits)));
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ finally
|
|
|
+ CopyStream.Free;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+function TBitmapCodecWIC.LoadThumbnailFromStream(const AStream: TStream;
|
|
|
const AFitWidth, AFitHeight: Single; const UseEmbedded, AutoCut: Boolean;
|
|
|
const Bitmap: TBitmapSurface): Boolean;
|
|
|
var
|