Просмотр исходного кода

add LoadThumbnailFromStream overload

KngStr 4 лет назад
Родитель
Сommit
abd876bb3a
1 измененных файлов с 252 добавлено и 7 удалено
  1. 252 7
      Source/ksBitmapHelper.pas

+ 252 - 7
Source/ksBitmapHelper.pas

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