123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067 |
- {*******************************************************}
- { }
- { Methods of Image }
- { }
- { CopyRight (C) 2018-2020 KngStr }
- { }
- {*******************************************************}
- unit ksBitmapHelper;
- // 部分拷贝自 FlyUtils.TBitmapHelper
- {$DEFINE FMX}
- interface
- {$SCOPEDENUMS ON}
- uses
- {$IFDEF FMX}
- FMX.Graphics,
- FMX.Utils,
- FMX.Types,
- FMX.Surfaces,
- {$ELSE}
- Vcl.Graphics,
- {$ENDIF}
- System.Types,
- System.UITypes,
- System.Classes;
- {$IFDEF FMX}
- {$ELSE}
- resourcestring
- SBitmapSavingFailed = 'Saving bitmap failed.';
- SBitmapSavingFailedNamed = 'Saving bitmap failed (%s).';
- {$ENDIF}
- var
- MonochromeChange_Threshold: Byte = 120;
- MonochromeChange_Weighting_Red: Double = 0.3;
- MonochromeChange_Weighting_Green: Double = 0.59;
- MonochromeChange_Weighting_Blue: Double = 0.11;
- type
- /// <summary>
- /// 转黑白的方法
- /// </summary>
- TMonochromeChangeType = (
- /// <summary>
- /// 平均值
- /// </summary>
- Average,
- /// <summary>
- /// 权值
- /// </summary>
- Weighting,
- /// <summary>
- /// 最大值
- /// </summary>
- Max);
- /// <summary>
- /// <para>
- /// TBitmap Save As BMP
- /// </para>
- /// <para>
- /// BytesPerPixel = -1 表示自动
- /// </para>
- /// </summary>
- TKngStrBitmapHelper = class helper for TBitmap
- public
- function SaveAsBMPToFile(const AFileName: string; const BytesPerPixel: Integer = 3;
- const MonochromeChangeType: TMonochromeChangeType = TMonochromeChangeType.Average): Boolean; overload;
- procedure SaveAsBMPToFileDef(const AFileName: string); overload;
- function SaveAsBMPToStream(const AStream: TStream; const BytesPerPixel: Integer = 3;
- const MonochromeChangeType: TMonochromeChangeType = TMonochromeChangeType.Average): Boolean; overload;
- procedure SaveAsBMPToStreamDef(Stream: TStream); overload;
- {$IFDEF FMX}
- /// <summary>
- /// 用于在线程中代替 LoadFromFile ,不用自己调用 Synchronize 了。
- /// </summary>
- procedure SyncLoadFromFile(const AFileName: string);
- /// <summary>
- /// 用于在线程中代替 LoadThumbnailFromFile ,不用自己调用 Synchronize 了。
- /// </summary>
- /// <remarks>
- /// UseEmbedded 只有Windows下可用,而且缩略图大小不可控
- /// </remarks>
- procedure SyncLoadThumbnailFromFile(const AFileName: string; const AFitWidth, AFitHeight: Single;
- const UseEmbedded: Boolean = True);
- /// <summary>
- /// 用于在线程中代替 LoadFromStream ,不用自己调用 Synchronize 了。
- /// </summary>
- procedure SyncLoadFromStream(Stream: TStream);
- /// <summary>
- /// 用于在线程中代替 LoadThumbnailFromStream ,不用自己调用 Synchronize 了。
- /// </summary>
- /// <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>
- procedure SyncAssign(Source: TPersistent);
- procedure SaveToStream(Stream: TStream; const Extension: string; SaveParams: PBitmapCodecSaveParams = nil); overload;
- {$ENDIF}
- end;
- {$IFDEF FMX}
- TBitmapCodecDescriptorField = (Extension, Description);
- TKngStrBitmapCodecManager = class helper for TBitmapCodecManager
- strict private
- class function GuessCodecClass(const Name: string; const Field: TBitmapCodecDescriptorField): TCustomBitmapCodecClass;
- public
- class function GetImageSize(const AFileName: string): TPointF; overload;
- 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; overload;
- /// <remarks>
- /// UseEmbedded 只有Windows下可用,而且缩略图大小不可控
- /// </remarks>
- class function LoadThumbnailFromStream(const AStream: TStream; const ASrc, ADest: TRectF;
- const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
- class function Rotate(const Extension: string; const Angle: Single; const Bitmap: TBitmapSurface): Boolean; overload;
- class function Rotate(const AStream: TStream; const Angle: Single): Boolean; overload;
- end;
- {$ENDIF}
- {$IFDEF FMX}
- {$ELSE}
- procedure GraphicToBitmap(const Src: Vcl.Graphics.TGraphic;
- const Dest: Vcl.Graphics.TBitmap; const TransparentColor: Vcl.Graphics.TColor = Vcl.Graphics.clNone);
- {$ENDIF}
- const
- SizeOftagBITMAPFILEHEADER = 14;
- SizeOftagBITMAPINFOHEADER = 40;
- RGB565ExtDataLen = 12;
- {$IFDEF FMX}
- function FillScanLineFormMemory(BitmapData: TBitmapData; ScanLineIndex, Width: integer;
- InputData: Pointer; InputFormat: TPixelFormat): Boolean;
- type
- // copy form [广州]庾伟洪<ywthegod@qq.com>
- TBitmap16bitFiler = class
- class var
- Color: array [0 .. $FFFF] of TAlphaColor;
- class constructor Create;
- class procedure FillScanLine(D: TBitmapData; ScanLine, Width: integer;
- data: Pointer); inline;
- end;
- // PAlphaColorArray = ^TAlphaColorArray; //FMX.Utils,
- // TWordArray = array [0 .. 0] of Word;
- // PWordArray = ^TWordArray; //System.SysUtils
- // y 行号 h 高度 w 宽度 b 一个像素用字节数
- // FVideoBuf 数据内存
- // bm.Map(TMapAccess.Write, bd);
- // try
- // for y := 0 to h - 1 do
- // begin
- // TBitmap16bitFiler.FillScanLine(bd, y, w, Addr(FVideoBuf[y * w * b]));
- // end;
- // finally
- // bm.Unmap(bd);
- // end;
- {$ENDIF}
- implementation
- uses
- {$IFDEF FMX}
- FMX.Consts,
- {$IFDEF ANDROID}
- FMX.Graphics.Android,
- Androidapi.JNIBridge,
- Androidapi.Helpers,
- FMX.Helpers.Android,
- Androidapi.JNI.GraphicsContentViewText,
- Androidapi.JNI.JavaTypes,
- {$ENDIF}
- {$IFDEF MSWINDOWS}
- FMX.Canvas.D2D, Winapi.Wincodec, Winapi.Windows, Winapi.ActiveX,
- {$ENDIF}
- {$ELSE}
- Vcl.Consts,
- {$ENDIF}
- System.SysConst,
- System.SysUtils,
- System.Math;
- //add by 爱吃猪头肉。
- type
- //感谢 yu 273637089 的测试和 提供 packed 信息。
- {$IFDEF FMX}
- {$ELSE}
- tagRGBTRIPLE = packed record
- rgbtBlue: Byte;
- rgbtGreen: Byte;
- rgbtRed: Byte;
- end;
- PRGBTripleArray = ^TRGBTripleArray;
- TRGBTripleArray = array [Byte] of tagRGBTRIPLE;
- {$ENDIF}
- tagBITMAPFILEHEADER = packed record
- bfType: Word;
- bfSize: DWORD;
- bfReserved1: Word;
- bfReserved2: Word;
- bfOffBits: DWORD;
- end;
- tagBITMAPINFOHEADER = packed record
- biSize: DWORD;
- biWidth: Longint;
- biHeight: Longint;
- biPlanes: Word;
- biBitCount: Word;
- biCompression: DWORD;
- biSizeImage: DWORD;
- biXPelsPerMeter: Longint;
- biYPelsPerMeter: Longint;
- biClrUsed: DWORD;
- biClrImportant: DWORD;
- end;
- tagRGBQUAD = packed record
- rgbBlue: Byte;
- rgbGreen: Byte;
- rgbRed: Byte;
- rgbReserved: Byte;
- end;
- {$IFDEF FMX}
- {$IFDEF ANDROID}
- TBitmapCodecAndroid = class(FMX.Graphics.Android.TBitmapCodecAndroid)
- 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;
- function StretchIfNeed(const SrcBitmap: JBitmap;
- const Bitmap: TBitmapSurface; const LoadOptions: JBitmapFactory_Options;
- const MaxSizeLimit: Cardinal): Boolean;
- class function GetMovieSize(const Stream: TStream): TPoint;
- 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 AutoCut: 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
- function DecodeFrame(const Frame: IWICBitmapFrameDecode; const Bitmap: TBitmapSurface;
- const MaxSizeLimit: Cardinal = 0): Boolean;
- 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; 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}
- {$ENDIF}
- const
- { constants for the biCompression field }
- {$EXTERNALSYM BI_RGB}
- BI_RGB = 0;
- {$EXTERNALSYM BI_RLE8}
- BI_RLE8 = 1;
- {$EXTERNALSYM BI_RLE4}
- BI_RLE4 = 2;
- {$EXTERNALSYM BI_BITFIELDS}
- BI_BITFIELDS = 3;
- const
- RGB565_MASK_RED = $F800;
- RGB565_MASK_GREEN = $07E0;
- RGB565_MASK_BLUE = $001F;
- // RGB565ExtDataLen = 12;
- function rgb_24_2_565(r,g,b: Byte): UInt16;
- begin
- // r := r * 31 div 255;
- // g := g * 64 div 255;
- // b := b * 31 div 255;
- // Result := r *2048 or g *32 or b;
- // Result := ((r shl 8) and RGB565_MASK_RED) or ((g shl 3) and RGB565_MASK_GREEN) or (b shr 3);
- Result := ((r shr 3) shl 11) or ((g shr 2 ) shl 5) or (b shr 3);
- end;
- {
- return (USHORT)(((unsigned(r) << 8) & 0xF800) |
- ((unsigned(g) << 3) & 0x7E0) |
- ((unsigned(b) >> 3)));
- }
- procedure rgb565_2_rgb24(rgb24: TBytes; rgb565: UInt16);
- begin
- //extract RGB
- rgb24[2] := (rgb565 and RGB565_MASK_RED) shr 11;
- rgb24[1] := (rgb565 and RGB565_MASK_GREEN) shr 5;
- rgb24[0] := (rgb565 and RGB565_MASK_BLUE);
- //amplify the image
- rgb24[2] := rgb24[2] shl 3;
- rgb24[1] := rgb24[2] shl 2;
- rgb24[0] := rgb24[2] shl 3;
- end;
- {
- //extract RGB
- rgb24[2] = (rgb565 & RGB565_MASK_RED) >> 11;
- rgb24[1] = (rgb565 & RGB565_MASK_GREEN) >> 5;
- rgb24[0] = (rgb565 & RGB565_MASK_BLUE);
- //amplify the image
- rgb24[2] <<= 3;
- rgb24[1] <<= 2;
- rgb24[0] <<= 3;
- }
- const
- RGB555_MASK_RED = $7C00;
- RGB555_MASK_GREEN = $03E0;
- RGB555_MASK_BLUE = $001F;
- function rgb_24_2_555(r,g,b: Byte): UInt16;
- begin
- Result := ((r shl 7) and RGB555_MASK_RED) or ((g shl 2) and RGB555_MASK_GREEN) or (b shr 3);
- end;
- procedure rgb555_2_rgb24(rgb24: TBytes; rgb555: UInt16);
- begin
- //extract RGB
- rgb24[0] := (rgb555 shl 3) and $00F8;
- rgb24[1] := (rgb555 shr 2) and $00F8;
- rgb24[2] := (rgb555 shr 7) and $00F8;
- end;
- {$IFDEF FMX}
- {$ELSE}
- procedure GraphicToBitmap(const Src: Vcl.Graphics.TGraphic;
- const Dest: Vcl.Graphics.TBitmap; const TransparentColor: Vcl.Graphics.TColor = Vcl.Graphics.clNone);
- begin
- // Do nothing if either source or destination are nil
- if not Assigned(Src) or not Assigned(Dest) then
- Exit;
- if Src.Empty then exit;
- // Size the bitmap
- Dest.Width := Src.Width;
- Dest.Height := Src.Height;
- if Src.Transparent then
- begin
- // Source graphic is transparent, make bitmap behave transparently
- Dest.Transparent := True;
- if (TransparentColor <> Vcl.Graphics.clNone) then
- begin
- // Set destination as transparent using required colour key
- Dest.TransparentColor := TransparentColor;
- Dest.TransparentMode := Vcl.Graphics.tmFixed;
- // Set background colour of bitmap to transparent colour
- Dest.Canvas.Brush.Color := TransparentColor;
- end
- else
- // No transparent colour: set transparency to automatic
- Dest.TransparentMode := Vcl.Graphics.tmAuto;
- end;
- // Clear bitmap to required background colour and draw bitmap
- Dest.Canvas.FillRect(System.Classes.Rect(0, 0, Dest.Width, Dest.Height));
- Dest.Canvas.Draw(0, 0, Src);
- end;
- {$ENDIF}
- //该代码片段来自于: http://www.sharejs.com/codes/delphi/2248
- { TKngStrBitmapHelper }
- function TKngStrBitmapHelper.SaveAsBMPToFile(const AFileName: string; const BytesPerPixel: Integer = 3;
- const MonochromeChangeType: TMonochromeChangeType = TMonochromeChangeType.Average): Boolean;
- var
- AStream: TStream;
- begin
- Result := False;
- {$IFDEF FMX}
- if IsEmpty then exit;
- {$ELSE}
- if Empty then exit;
- {$ENDIF}
- AStream := TFileStream.Create(AFileName, fmCreate);// or fmOpenReadWrite);
- try
- Result := SaveAsBMPToStream(AStream, BytesPerPixel);
- // if Result then
- // AStream.Size := AStream.Position;
- finally
- FreeAndNil(AStream);
- end;
- end;
- procedure TKngStrBitmapHelper.SaveAsBMPToFileDef(const AFileName: string);
- begin
- if not SaveAsBMPToFile(AFileName) then
- begin
- {$IFDEF FMX}
- raise EBitmapSavingFailed.CreateFMT(SBitmapSavingFailed, [AFileName]);
- {$ELSE}
- raise EInvalidGraphicOperation.CreateFmt(SBitmapSavingFailed, [AFileName]);
- {$ENDIF}
- end;
- end;
- //http://blog.csdn.net/pjpsmile/article/details/8985523
- function TKngStrBitmapHelper.SaveAsBMPToStream(const AStream: TStream; const BytesPerPixel: Integer = 3;
- const MonochromeChangeType: TMonochromeChangeType = TMonochromeChangeType.Average): Boolean;
- var
- I, CurrBytesPerPixel,
- wWidth, nCol, wRow, wByteIdex,
- bfReserved1, bfReserved2,
- nBmpWidth, nBmpHeight, bufferSize: Integer;
- BitmapInfo: tagBITMAPINFOHEADER;
- BMF: tagBITMAPFILEHEADER;
- {$IFDEF FMX}
- Data: TBitmapData;
- clr: TAlphaColor;
- AlphaColorBuffer: PAlphaColor;
- {$ELSE}
- Pixs : pRGBTripleArray;
- {$ENDIF}
- bmpData: TBytes;
- A32BitData: UInt32;
- ABitIndex,
- A8BitData: Byte;
- RGBQUAD: tagRGBQUAD;
- // FileSizeFix,
- A16BitData,
- GrayeData: UInt16;
- begin
- Result := False;
- {$IFDEF FMX}
- if IsEmpty then exit;
- {$ELSE}
- if Empty then exit;
- {$ENDIF}
- if not Assigned(AStream) then exit;
- CurrBytesPerPixel := BytesPerPixel;
- {$IFDEF FMX}
- Map(TMapAccess.Read, Data);
- {$ELSE}
- {$ENDIF}
- try
- if CurrBytesPerPixel = -1 then
- begin
- {$IFDEF FMX}
- CurrBytesPerPixel := Data.BytesPerPixel;
- {$ELSE}
- CurrBytesPerPixel := 3;
- if PixelFormat = pf1bit then
- CurrBytesPerPixel := 0;
- if PixelFormat = pf4bit then
- CurrBytesPerPixel := 2;
- if PixelFormat = pf8bit then
- CurrBytesPerPixel := 2;
- if PixelFormat = pf15bit then
- CurrBytesPerPixel := 2;
- if PixelFormat = pf16bit then
- CurrBytesPerPixel := 2;
- if PixelFormat = pf24bit then
- CurrBytesPerPixel := 3;
- if PixelFormat = pf32bit then
- CurrBytesPerPixel := 4;
- {$ENDIF}
- if not (CurrBytesPerPixel in [0,1,2,4]) then
- CurrBytesPerPixel := 3;
- end;
- if not (CurrBytesPerPixel in [0,1,2,3,4]) then
- exit;
- //不打算支持 8 位的。
- if CurrBytesPerPixel = 1 then exit;
- {$IFDEF FMX}
- nBmpWidth := Data.Width;
- nBmpHeight := Data.Height;
- {$ELSE}
- nBmpWidth := Width;
- nBmpHeight := Height;
- {$ENDIF}
- // 像素扫描
- if CurrBytesPerPixel > 0 then
- begin
- wWidth := nBmpWidth * CurrBytesPerPixel;
- if (wWidth mod 4) > 0 then
- begin
- wWidth := wWidth + 4 - (wWidth mod 4);
- end;
- end
- else if (nBmpWidth mod 32) > 0 then
- begin
- wWidth := ((nBmpWidth div 32) + 1) * 4;;
- end
- else
- begin
- wWidth := (nBmpWidth div 8);
- end;
- bufferSize := nBmpHeight * wWidth;
- // bmp文件头
- BMF.bfType := $4D42;
- BMF.bfSize := 14 + 40 + bufferSize;
- BMF.bfReserved1 := 0;
- BMF.bfReserved2 := 0;
- BMF.bfOffBits := 14 + 40;
- if (CurrBytesPerPixel = 0) then
- begin
- BMF.bfOffBits := BMF.bfOffBits + 2 * Sizeof(RGBQUAD);
- BMF.bfSize := BMF.bfSize + 2 * Sizeof(RGBQUAD);
- end;
- if (CurrBytesPerPixel = 1) then
- begin
- BMF.bfOffBits := BMF.bfOffBits + 256 * Sizeof(RGBQUAD);
- BMF.bfSize := BMF.bfSize + 256 * Sizeof(RGBQUAD);
- end;
- if (CurrBytesPerPixel = 2) then
- begin
- //多谢 [西安]老一门(yyimen@foxmail.com) 提供的 565 格式说明。
- BMF.bfOffBits := BMF.bfOffBits + RGB565ExtDataLen;
- BMF.bfSize := BMF.bfSize + RGB565ExtDataLen;
- end;
- // FileSizeFix := 0;
- // if (BMF.bfSize mod 4) > 0 then
- // begin
- // FileSizeFix := 4 - BMF.bfSize mod 4;
- // end;
- // bufferSize := bufferSize + FileSizeFix;
- // BMF.bfSize := BMF.bfSize + FileSizeFix;
- // 保存bmp文件头
- AStream.WriteBuffer(BMF, Sizeof(BMF));
- // bmp信息头
- FillChar(BitmapInfo, Sizeof(BitmapInfo), 0);
- BitmapInfo.biSize := 40;
- // if (CurrBytesPerPixel = 2) then
- // begin
- // //AcdSee 不支持这种大小的 BitmapInfo
- // BitmapInfo.biSize := 40 + RGB565ExtDataLen;
- // end;
- BitmapInfo.biWidth := nBmpWidth;
- BitmapInfo.biHeight := nBmpHeight;
- BitmapInfo.biPlanes := 1;
- if CurrBytesPerPixel > 0 then
- begin
- BitmapInfo.biBitCount := CurrBytesPerPixel * 8;
- end
- else
- begin
- BitmapInfo.biBitCount := 1;
- end;
- BitmapInfo.biSizeImage := bufferSize;
- // if True then
- // begin
- // //96
- // BitmapInfo.biXPelsPerMeter := $0EC4;
- // BitmapInfo.biYPelsPerMeter := $0EC4;
- // end
- // else
- // begin
- // //72
- // BitmapInfo.biXPelsPerMeter := $0B12;
- // BitmapInfo.biYPelsPerMeter := $0B12;
- // end;
- BitmapInfo.biXPelsPerMeter := 0;
- BitmapInfo.biYPelsPerMeter := 0;
- if (CurrBytesPerPixel = 2) then
- begin
- //可以采用 RGB555 代替 RGB565
- BitmapInfo.biCompression := BI_BITFIELDS; //0是 555 3 是 565
- end;
- // 保存bmp信息头
- AStream.WriteBuffer(BitmapInfo, Sizeof(BitmapInfo));
- if (CurrBytesPerPixel = 2) then
- begin
- // 保存 565 RGB Mask
- A32BitData := RGB565_MASK_RED;
- AStream.WriteBuffer(A32BitData, Sizeof(A32BitData));
- A32BitData := RGB565_MASK_GREEN;
- AStream.WriteBuffer(A32BitData, Sizeof(A32BitData));
- A32BitData := RGB565_MASK_BLUE;
- AStream.WriteBuffer(A32BitData, Sizeof(A32BitData));
- if RGB565ExtDataLen >= 16 then
- begin
- A32BitData := 0;
- AStream.WriteBuffer(A32BitData, Sizeof(A32BitData));
- end;
- end;
- if (CurrBytesPerPixel = 0) or (CurrBytesPerPixel = 1) then
- begin
- //颜色表
- RGBQUAD.rgbBlue := 0;
- RGBQUAD.rgbGreen := 0;
- RGBQUAD.rgbRed := 0;
- RGBQUAD.rgbReserved := 0;
- if (CurrBytesPerPixel = 1) then
- begin
- for I := 0 to 255 do
- begin
- AStream.WriteBuffer(RGBQUAD, Sizeof(RGBQUAD));
- end;
- BitmapInfo.biClrUsed := $FF;
- end
- else
- begin
- AStream.WriteBuffer(RGBQUAD, Sizeof(RGBQUAD));
- RGBQUAD.rgbBlue := $FF;
- RGBQUAD.rgbGreen := $FF;
- RGBQUAD.rgbRed := $FF;
- RGBQUAD.rgbReserved := 0;
- AStream.WriteBuffer(RGBQUAD, Sizeof(RGBQUAD));
- BitmapInfo.biClrUsed := 2;
- end;
- end;
- // 像素扫描
- SetLength(bmpData, wWidth);
- {$IFDEF FMX}
- AlphaColorBuffer := GetMemory(nBmpWidth * SizeOf(TAlphaColor));
- try
- {$ENDIF}
- for nCol := nBmpHeight - 1 downto 0 do
- begin
- FillChar(bmpData[0], wWidth, 0);
- wByteIdex := 0;
- //0 是单色图
- if (CurrBytesPerPixel = 0) or (CurrBytesPerPixel = 1) then
- begin
- A8BitData := 0;
- ABitIndex := 0;
- {$IFDEF FMX}
- {$ELSE}
- Pixs := ScanLine[nCol];
- {$ENDIF}
- for wRow := 0 to nBmpWidth - 1 do
- begin
- {$IFDEF FMX}
- //X 是行坐标,Y 是 列坐标。
- clr := Data.GetPixel(wRow, nCol);
- {$ELSE}
- {$ENDIF}
- GrayeData := 0;
- {$IFDEF FMX}
- if MonochromeChangeType = TMonochromeChangeType.Average then
- begin
- GrayeData := TAlphaColorRec(clr).R + TAlphaColorRec(clr).G + TAlphaColorRec(clr).B;
- GrayeData := GrayeData div 3;
- end;
- if MonochromeChangeType = TMonochromeChangeType.Weighting then
- begin
- GrayeData := Round((TAlphaColorRec(clr).R * MonochromeChange_Weighting_Red +
- TAlphaColorRec(clr).G * MonochromeChange_Weighting_Green +
- TAlphaColorRec(clr).B * MonochromeChange_Weighting_Blue) / 3);
- end;
- if MonochromeChangeType = TMonochromeChangeType.Max then
- begin
- GrayeData := System.Math.Max(System.Math.Max(TAlphaColorRec(clr).R, TAlphaColorRec(clr).G),
- TAlphaColorRec(clr).B);
- end;
- {$ELSE}
- if MonochromeChangeType = TMonochromeChangeType.Average then
- begin
- GrayeData := Pixs[wRow].rgbtRed + Pixs[wRow].rgbtGreen + Pixs[wRow].rgbtBlue;
- GrayeData := GrayeData div 3;
- end;
- if MonochromeChangeType = TMonochromeChangeType.Weighting then
- begin
- GrayeData := Round((Pixs[wRow].rgbtRed * MonochromeChange_Weighting_Red +
- Pixs[wRow].rgbtGreen * MonochromeChange_Weighting_Green +
- Pixs[wRow].rgbtBlue * MonochromeChange_Weighting_Blue) / 3);
- end;
- if MonochromeChangeType = TMonochromeChangeType.Max then
- begin
- GrayeData := System.Math.Max(System.Math.Max(Pixs[wRow].rgbtRed, Pixs[wRow].rgbtGreen),
- Pixs[wRow].rgbtBlue);
- end;
- {$ENDIF}
- if GrayeData > MonochromeChange_Threshold then
- begin
- A8BitData := A8BitData or ($80 shr ABitIndex);
- end;
- inc(ABitIndex);
- if ABitIndex > 7 then
- begin
- ABitIndex := 0;
- if (CurrBytesPerPixel = 0) then
- begin
- bmpData[wByteIdex] := A8BitData;
- A8BitData := 0;
- inc(wByteIdex);
- end;
- end;
- end;
- if (CurrBytesPerPixel = 0) then
- begin
- if ABitIndex > 0 then
- begin
- bmpData[wByteIdex] := A8BitData;
- A8BitData := 0;
- end;
- end;
- end
- else
- begin
- {$IFDEF FMX}
- // for wRow := 0 to nBmpWidth - 1 do
- // begin
- // //X 是行坐标,Y 是 列坐标。
- // clr := Data.GetPixel(wRow, nCol);
- // case CurrBytesPerPixel of
- // 1:
- // begin
- // //不支持。
- // end;
- // 2:
- // begin
- // A16BitData := rgb_24_2_565(TAlphaColorRec(clr).R, TAlphaColorRec(clr).G, TAlphaColorRec(clr).B);
- // bmpData[wByteIdex + 0] := WordRec(A16BitData).Lo;
- // bmpData[wByteIdex + 1] := WordRec(A16BitData).Hi;
- // end;
- // 3,4:
- // begin
- // bmpData[wByteIdex + 0] := TAlphaColorRec(clr).B;
- // bmpData[wByteIdex + 1] := TAlphaColorRec(clr).G;
- // bmpData[wByteIdex + 2] := TAlphaColorRec(clr).R;
- // end;
- // end;
- // if CurrBytesPerPixel = 4 then
- // begin
- // bmpData[wByteIdex + 3] := TAlphaColorRec(clr).A;
- // end;
- // Inc(wByteIdex, CurrBytesPerPixel);
- // end;
- case CurrBytesPerPixel of
- 1:
- begin
- //不支持。
- end;
- 2:
- begin
- ScanlineToAlphaColor(Data.GetScanline(nCol), AlphaColorBuffer, nBmpWidth, Data.PixelFormat);
- AlphaColorToScanline(AlphaColorBuffer, Addr(bmpData[0]), nBmpWidth, TPixelFormat.BGR_565);
- end;
- 3:
- begin
- ScanlineToAlphaColor(Data.GetScanline(nCol), AlphaColorBuffer, nBmpWidth, Data.PixelFormat);
- AlphaColorToScanline(AlphaColorBuffer, Addr(bmpData[0]), nBmpWidth, TPixelFormat.BGR);
- end;
- 4:
- begin
- if Data.PixelFormat = TPixelFormat.BGRA then
- begin
- Move(PByte(Data.GetScanline(nCol))[0], bmpData[0], Data.BytesPerPixel * nBmpWidth);
- end
- else
- begin
- ScanlineToAlphaColor(Data.GetScanline(nCol), AlphaColorBuffer, nBmpWidth, Data.PixelFormat);
- AlphaColorToScanline(AlphaColorBuffer, Addr(bmpData[0]), nBmpWidth, TPixelFormat.BGRA);
- end;
- end;
- end;
- {$ELSE}
- Pixs := ScanLine[nCol];
- for wRow := 0 to nBmpWidth - 1 do
- begin
- case CurrBytesPerPixel of
- 1:
- begin
- //不支持。
- end;
- 2:
- begin
- A16BitData := rgb_24_2_565(Pixs[wRow].rgbtRed, Pixs[wRow].rgbtGreen, Pixs[wRow].rgbtBlue);
- bmpData[wByteIdex + 0] := WordRec(A16BitData).Lo;
- bmpData[wByteIdex + 1] := WordRec(A16BitData).Hi;
- end;
- 3,4:
- begin
- bmpData[wByteIdex + 0] := Pixs[wRow].rgbtBlue;
- bmpData[wByteIdex + 1] := Pixs[wRow].rgbtGreen;
- bmpData[wByteIdex + 2] := Pixs[wRow].rgbtRed;
- end;
- end;
- if CurrBytesPerPixel = 4 then
- begin
- bmpData[wByteIdex + 3] := $FF;
- end;
- Inc(wByteIdex, CurrBytesPerPixel);
- end;
- {$ENDIF}
- end;
- AStream.WriteBuffer(bmpData, wWidth);
- end;
- {$IFDEF FMX}
- finally
- FreeMemory(AlphaColorBuffer);
- end;
- {$ENDIF}
- // A8BitData := 0;
- // for I := 0 to FileSizeFix - 1 do
- // begin
- // AStream.WriteBuffer(A8BitData, 1);
- // end;
- Result := True;
- finally
- {$IFDEF FMX}
- Unmap(Data);
- {$ELSE}
- {$ENDIF}
- end;
- end;
- procedure TKngStrBitmapHelper.SaveAsBMPToStreamDef(Stream: TStream);
- begin
- if not SaveAsBMPToStream(Stream) then
- begin
- {$IFDEF FMX}
- raise EBitmapSavingFailed.Create(SBitmapSavingFailed);
- {$ELSE}
- raise EInvalidGraphicOperation.Create(SBitmapSavingFailed);
- {$ENDIF}
- end;
- end;
- procedure TKngStrBitmapHelper.SaveToStream(Stream: TStream;
- const Extension: string; SaveParams: PBitmapCodecSaveParams);
- var
- Surf: TBitmapSurface;
- begin
- TMonitor.Enter(Self);
- try
- Surf := TBitmapSurface.Create;
- try
- Surf.Assign(Self);
- if not TBitmapCodecManager.SaveToStream(Stream, Surf, Extension, SaveParams) then
- raise EBitmapSavingFailed.Create(SBitmapSavingFailed);
- finally
- Surf.Free;
- end;
- finally
- TMonitor.Exit(Self);
- end;
- end;
- {$IFDEF FMX}
- procedure TKngStrBitmapHelper.SyncAssign(Source: TPersistent);
- begin
- TThread.Synchronize(nil,
- procedure
- begin
- Assign(Source);
- end)
- end;
- procedure TKngStrBitmapHelper.SyncLoadFromFile(const AFileName: string);
- var
- Surf: TBitmapSurface;
- begin
- Surf := TBitmapSurface.Create;
- try
- if TBitmapCodecManager.LoadFromFile(AFileName, Surf, CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then
- TThread.Synchronize(nil,
- procedure
- begin
- Assign(Surf);
- end)
- else
- raise EBitmapLoadingFailed.CreateFMT(SBitmapLoadingFailedNamed, [AFileName]);
- finally
- Surf.Free;
- end;
- end;
- procedure TKngStrBitmapHelper.SyncLoadFromStream(Stream: TStream);
- var
- S: TStream;
- Surf: TBitmapSurface;
- begin
- if Stream.Position > 0 then
- begin
- // need to create temp stream
- S := TMemoryStream.Create;
- try
- S.CopyFrom(Stream, Stream.Size - Stream.Position);
- S.Position := 0;
- Surf := TBitmapSurface.Create;
- try
- if TBitmapCodecManager.LoadFromStream(S, Surf, CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then
- TThread.Synchronize(nil,
- procedure
- begin
- Assign(Surf);
- end)
- else
- raise EBitmapLoadingFailed.Create(SBitmapLoadingFailed);
- finally
- Surf.Free;
- end;
- finally
- S.Free;
- end;
- end
- else
- if Stream.Size = 0 then
- Clear(0)
- else begin
- Surf := TBitmapSurface.Create;
- try
- if TBitmapCodecManager.LoadFromStream(Stream, Surf, CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then
- TThread.Synchronize(nil,
- procedure
- begin
- Assign(Surf);
- end)
- else
- raise EBitmapLoadingFailed.Create(SBitmapLoadingFailed);
- finally
- Surf.Free;
- end;
- end;
- end;
- procedure TKngStrBitmapHelper.SyncLoadThumbnailFromFile(const AFileName: string; const AFitWidth, AFitHeight: Single;
- const UseEmbedded: Boolean = True);
- var
- Surf: TBitmapSurface;
- begin
- Surf := TBitmapSurface.Create;
- try
- if TBitmapCodecManager.LoadThumbnailFromFile(AFileName, AFitWidth, AFitHeight, UseEmbedded, Surf) then
- TThread.Synchronize(nil,
- procedure
- begin
- Assign(Surf);
- end)
- else
- raise EThumbnailLoadingFailed.CreateFMT(SThumbnailLoadingFailedNamed, [AFileName]);
- finally
- Surf.Free;
- end;
- end;
- 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;
- Surf: TBitmapSurface;
- begin
- if Stream.Position > 0 then
- begin
- // need to create temp stream
- S := TMemoryStream.Create;
- try
- S.CopyFrom(Stream, Stream.Size - Stream.Position);
- S.Position := 0;
- Surf := TBitmapSurface.Create;
- try
- if TBitmapCodecManager.LoadThumbnailFromStream(S, AFitWidth, AFitHeight, UseEmbedded, AutoCut, 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 Stream.Size = 0 then
- Clear(0)
- else begin
- Surf := TBitmapSurface.Create;
- try
- if TBitmapCodecManager.LoadThumbnailFromStream(Stream, AFitWidth, AFitHeight, UseEmbedded, AutoCut, Surf) then
- TThread.Synchronize(nil,
- procedure
- begin
- Assign(Surf);
- end)
- else
- raise EBitmapLoadingFailed.Create(SThumbnailLoadingFailed);
- finally
- Surf.Free;
- end;
- end;
- end;
- { TKngStrBitmapCodecManager }
- class function TKngStrBitmapCodecManager.GetImageSize(const AStream: TStream): TPointF;
- var
- CodecClass: TCustomBitmapCodecClass;
- DataType: String;
- begin
- DataType := TImageTypeChecker.GetType(AStream);
- CodecClass := GuessCodecClass(DataType, TBitmapCodecDescriptorField.Extension);
- if CodecClass <> nil then
- {$IFDEF ANDROID}
- Result := TBitmapCodecAndroid(CodecClass).GetImageSize(AStream)
- {$ELSE}
- Result := TBitmapCodecWIC(CodecClass).GetImageSize(AStream)
- {$ENDIF}
- else
- Result := TPointF.Zero;
- end;
- class function TKngStrBitmapCodecManager.GetImageSize(const AFileName: string): TPointF;
- begin
- Result := inherited GetImageSize(AFileName);
- end;
- class function TKngStrBitmapCodecManager.GuessCodecClass(const Name: string;
- const Field: TBitmapCodecDescriptorField): TCustomBitmapCodecClass;
- type
- TPrivateMethodType = function (const Name: string; const Field: TBitmapCodecDescriptorField):
- TCustomBitmapCodecClass of object;
- var
- AMethod: TMethod;
- AInvoke: TPrivateMethodType absolute AMethod;
- begin
- AMethod.Code := @TBitmapCodecManager.GuessCodecClass;
- AMethod.Data := Self;
- Result := AInvoke(Name, Field);
- end;
- class function TKngStrBitmapCodecManager.LoadFromStream(const AStream: TStream;
- const Bitmap: TBitmapSurface; const MaxSizeLimit: Cardinal): 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).LoadFromStream(AStream, Bitmap, MaxSizeLimit);
- {$ELSE}
- Result := TBitmapCodecWIC(Codec).LoadFromStream(AStream, Bitmap, MaxSizeLimit);
- {$ENDIF}
- finally
- Codec.Free;
- end;
- end
- 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.Rotate(const AStream: TStream;
- const Angle: Single): Boolean;
- var
- CodecClass: TCustomBitmapCodecClass;
- Codec: TCustomBitmapCodec;
- DataType: String;
- begin
- Result := False;
- if (not Assigned(AStream)) or (AStream.Size - AStream.Position <= 0) then
- Exit;
- DataType := TImageTypeChecker.GetType(AStream);
- CodecClass := GuessCodecClass(DataType, TBitmapCodecDescriptorField.Extension);
- if CodecClass <> nil then
- begin
- Codec := CodecClass.Create;
- try
- {$IFDEF ANDROID}
- Result := TBitmapCodecAndroid(Codec).Rotate(AStream, Angle);
- {$ELSE}
- Result := TBitmapCodecWIC(Codec).Rotate(AStream, Angle);
- {$ENDIF}
- finally
- Codec.Free;
- end;
- end
- end;
- class function TKngStrBitmapCodecManager.Rotate(const Extension: string;
- const Angle: Single; const Bitmap: TBitmapSurface): Boolean;
- var
- CodecClass: TCustomBitmapCodecClass;
- Codec: TCustomBitmapCodec;
- begin
- Result := False;
- if Extension = '' then
- Exit;
- CodecClass := GuessCodecClass(Extension, TBitmapCodecDescriptorField.Extension);
- if CodecClass <> nil then
- begin
- Codec := CodecClass.Create;
- try
- {$IFDEF ANDROID}
- Result := TBitmapCodecAndroid(Codec).Rotate(Extension, Angle, Bitmap);
- {$ELSE}
- Result := TBitmapCodecWIC(Codec).Rotate(Extension, Angle, 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;
- 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, AFitWidth, AFitHeight, UseEmbedded, AutoCut, Bitmap);
- {$ELSE}
- Result := TBitmapCodecWIC(Codec).LoadThumbnailFromStream(AStream, AFitWidth, AFitHeight, UseEmbedded, AutoCut, Bitmap);
- {$ENDIF}
- finally
- Codec.Free;
- end;
- end
- end;
- {$IFDEF ANDROID}
- { TBitmapCodecAndroid }
- class function TBitmapCodecAndroid.GetMovieSize(const Stream: TStream): TPoint;
- type
- TPrivateMethodType = function (const Stream: TStream): TPoint of object;
- var
- AMethod: TMethod;
- AInvoke: TPrivateMethodType absolute AMethod;
- begin
- AMethod.Code := @TBitmapCodecAndroid.GetMovieSize;
- AMethod.Data := Self;
- Result := AInvoke(Stream);
- end;
- class function TBitmapCodecAndroid.GetImageSize(const AStream: TStream): TPointF;
- var
- TempStream: TMemoryStream;
- TempArray: TJavaArray<Byte>;
- NativeBitmap: JBitmap;
- LoadOptions: JBitmapFactory_Options;
- SavePosition: Int64;
- begin
- if IsGIFStream(AStream) then
- Result := GetMovieSize(AStream)
- 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 := True;
- TJBitmapFactory.JavaClass.decodeByteArray(TempArray, 0, TempArray.Length, LoadOptions);
- TempArray := nil;
- Result := TPointF.Create(LoadOptions.outWidth, LoadOptions.outHeight);
- finally
- AStream.Position := SavePosition;
- end;
- end;
- end;
- class function TBitmapCodecAndroid.GetImageSize(const AFileName: string): TPointF;
- begin
- Result := inherited GetImageSize(AFileName);
- end;
- class function TBitmapCodecAndroid.IsGIFStream(const Stream: TStream): Boolean;
- begin
- Result := TImageTypeChecker.GetType(Stream) = SGIFImageExtension;
- end;
- function TBitmapCodecAndroid.LoadMovieFromStream(const Stream: TStream; const Surface: TBitmapSurface): Boolean;
- var
- PrevPosition: Int64;
- TempArray: TJavaArray<Byte>;
- Movie: JMovie;
- Bitmap: JBitmap;
- Canvas: JCanvas;
- begin
- PrevPosition := Stream.Position;
- try
- TempArray := TJavaArray<Byte>.Create(Stream.Size - Stream.Position);
- Stream.ReadBuffer(TempArray.Data^, TempArray.Length);
- finally
- Stream.Position := PrevPosition;
- end;
- Movie := TJMovie.JavaClass.decodeByteArray(TempArray, 0, TempArray.Length);
- TempArray := nil;
- Bitmap := TJBitmap.JavaClass.createBitmap(Movie.width, Movie.height, TJBitmap_Config.JavaClass.ARGB_8888);
- //kngstr
- if Bitmap = nil then
- Exit(False);
- try
- Canvas := TJCanvas.JavaClass.init(Bitmap);
- try
- Movie.setTime(0);
- Movie.draw(Canvas, 0, 0);
- finally
- Canvas := nil;
- end;
- Result := JBitmapToSurface(Bitmap, Surface);
- finally
- Bitmap.recycle;
- end;
- end;
- function TBitmapCodecAndroid.StretchIfNeed(const SrcBitmap: JBitmap; const Bitmap: TBitmapSurface;
- const LoadOptions: JBitmapFactory_Options; const MaxSizeLimit: Cardinal): Boolean;
- var
- R: TRectF;
- ScaledBitmap: JBitmap;
- begin
- if (MaxSizeLimit > 0) and ((LoadOptions.outWidth > Integer(MaxSizeLimit)) or
- (LoadOptions.outHeight > Integer(MaxSizeLimit))) then
- begin
- R := TRectF.Create(0, 0, LoadOptions.outWidth, LoadOptions.outHeight);
- R.Fit(TRectF.Create(0, 0, MaxSizeLimit, MaxSizeLimit));
- ScaledBitmap := TJBitmap.JavaClass.createScaledBitmap(SrcBitmap, R.Truncate.Width, R.Truncate.Height, True);
- //kngstr
- if ScaledBitmap = nil then
- Exit(False);
- try
- Result := JBitmapToSurface(ScaledBitmap, Bitmap);
- finally
- ScaledBitmap.recycle;
- end;
- end
- else
- Result := JBitmapToSurface(SrcBitmap, Bitmap);
- end;
- function TBitmapCodecAndroid.LoadFromStream(const AStream: TStream;
- const Bitmap: TBitmapSurface; const MaxSizeLimit: Cardinal): Boolean;
- var
- TempStream: TMemoryStream;
- TempArray: TJavaArray<Byte>;
- NativeBitmap: JBitmap;
- LoadOptions: JBitmapFactory_Options;
- SavePosition: Int64;
- begin
- if IsGIFStream(AStream) then
- Result := LoadMovieFromStream(AStream, Bitmap)
- 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;
- NativeBitmap := TJBitmapFactory.JavaClass.decodeByteArray(TempArray, 0, TempArray.Length, LoadOptions);
- TempArray := nil;
- //kngstr
- if NativeBitmap = nil then
- Exit(False);
- try
- if (LoadOptions.outWidth < 1) or (LoadOptions.outHeight < 1) then
- Exit(False);
- Result := StretchIfNeed(NativeBitmap, Bitmap, LoadOptions, MaxSizeLimit);
- finally
- NativeBitmap.recycle;
- end;
- finally
- AStream.Position := SavePosition;
- end;
- end;
- end;
- function TBitmapCodecAndroid.LoadMovieFromStreamScaled(const AStream: TStream; const Surface: TBitmapSurface;
- const FitSize: TPoint): Boolean;
- var
- TempStream: TMemoryStream;
- TempArray: TJavaArray<Byte>;
- Movie: JMovie;
- OrigBitmap, Bitmap: JBitmap;
- Canvas: JCanvas;
- OrigSize: TPoint;
- LoadOptions: JBitmapFactory_Options;
- SavePosition: Int64;
- 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;
- Movie := TJMovie.JavaClass.decodeByteArray(TempArray, 0, TempArray.Length);
- TempArray := nil;
- OrigSize := TPoint.Create(Movie.width, Movie.height);
- OrigBitmap := TJBitmap.JavaClass.createBitmap(OrigSize.X, OrigSize.Y, TJBitmap_Config.JavaClass.ARGB_8888);
- if OrigBitmap = nil then //kngstr fixed
- Exit(False);
- try
- Canvas := TJCanvas.JavaClass.init(OrigBitmap);
- try
- Movie.setTime(0);
- Movie.draw(Canvas, 0, 0);
- finally
- Canvas := nil;
- end;
- Movie := nil;
- Bitmap := TJBitmap.JavaClass.createBitmap(FitSize.X, FitSize.Y, TJBitmap_Config.JavaClass.ARGB_8888);
- if Bitmap = nil then //kngstr fixed
- Exit(False);
- try
- Canvas := TJCanvas.JavaClass.init(Bitmap);
- try
- Canvas.drawBitmap(OrigBitmap, TJRect.JavaClass.init(0, 0, OrigSize.X, OrigSize.Y), TJRect.JavaClass.init(0, 0,
- FitSize.X, FitSize.y), nil);
- finally
- Canvas := nil;
- end;
- Result := JBitmapToSurface(Bitmap, Surface);
- finally
- Bitmap.recycle; //kngstr fixed
- end;
- finally
- OrigBitmap.recycle;
- end;
- finally
- AStream.Position := SavePosition;
- end;
- end;
- //连续decode需要reset
- //https://blog.csdn.net/boystray/article/details/77725648
- //多图加载的优化
- //https://blog.csdn.net/Android_app/article/details/45815093
- //inSampleSize优化
- //https://www.jianshu.com/p/f15cd2ed6ec0
- procedure calculateInSampleSize(options: JBitmapFactory_Options; reqWidth, reqHeight: Integer);
- var
- width, height, suitedValue: Integer;
- widthRatio, heightRatio: Integer;
- begin
- options.inSampleSize := 1;
- width := options.outWidth;
- height := options.outHeight;
- if (height > reqHeight) or (width > reqWidth) then begin
- //使用需要的宽高的最大值来计算比率
- if reqHeight > reqWidth then
- suitedValue := reqHeight
- else
- suitedValue := reqWidth;
- heightRatio := height div suitedValue;
- widthRatio := width div suitedValue;
- if heightRatio > widthRatio then //用最大
- options.inSampleSize := heightRatio
- else
- options.inSampleSize := widthRatio;
- end;
- 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.Rotate(const AStream: TStream;
- const Angle: Single): Boolean;
- var
- NativeBitmap1, NativeBitmap2: JBitmap;
- SaveFormat: JBitmap_CompressFormat;
- Matrix: JMatrix;
- TempStream: TMemoryStream;
- TempArray: TJavaArray<Byte>;
- LoadOptions: JBitmapFactory_Options;
- SavePosition: Int64;
- OutByteStream: JByteArrayOutputStream;
- ContentBytes: TJavaArray<Byte>;
- DataType: string;
- begin
- DataType := TImageTypeChecker.GetType(AStream);
- if DataType = SGIFImageExtension then begin
- Result := False;
- Exit;
- end;
- 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;
- if NativeBitmap1 = nil then
- Exit(False);
- try
- Matrix := TJMatrix.JavaClass.init;
- Matrix.postRotate(Angle);
- NativeBitmap2 := TJBitmap.JavaClass.createBitmap(NativeBitmap1, 0, 0, NativeBitmap1.getWidth, NativeBitmap1.getHeight, Matrix, True);
- finally
- NativeBitmap1.recycle;
- end;
- if NativeBitmap2 = nil then
- Exit(False);
- try
- if SameText(DataType, SPNGImageExtension) then
- SaveFormat := TJBitmap_CompressFormat.JavaClass.PNG
- else
- SaveFormat := TJBitmap_CompressFormat.JavaClass.JPEG;
- OutByteStream := TJByteArrayOutputStream.JavaClass.init(0);
- Result := NativeBitmap2.compress(SaveFormat, 100, OutByteStream);
- finally
- NativeBitmap2.recycle;
- end;
- if Result and (OutByteStream.size > 0) then begin
- ContentBytes := OutByteStream.toByteArray;
- AStream.Size := 0;
- AStream.WriteBuffer(ContentBytes.Data^, OutByteStream.size);
- end;
- Result := Result and (OutByteStream.size > 0);
- finally
- AStream.Position := SavePosition;
- end;
- end;
- function TBitmapCodecAndroid.Rotate(const Extension: string; const Angle: Single; const Bitmap: TBitmapSurface): Boolean;
- var
- NativeBitmap1, NativeBitmap2: JBitmap;
- SaveFormat: JBitmap_CompressFormat;
- Matrix: JMatrix;
- SaveQuality: Integer;
- begin
- if Extension = SGIFImageExtension then begin
- Result := False;
- Exit;
- end;
- NativeBitmap1 := TJBitmap.JavaClass.createBitmap(Bitmap.Width, Bitmap.Height, TJBitmap_Config.JavaClass.ARGB_8888);
- if NativeBitmap1 = nil then
- Exit(False);
- try
- Result := SurfaceToJBitmap(Bitmap, NativeBitmap1);
- if not Result then
- Exit;
- Matrix := TJMatrix.JavaClass.init;
- Matrix.postRotate(Angle);
- NativeBitmap2 := TJBitmap.JavaClass.createBitmap(NativeBitmap1, 0, 0, Bitmap.Width, Bitmap.Height, Matrix, True);
- finally
- NativeBitmap1.recycle;
- end;
- if NativeBitmap2 = nil then
- Exit(False);
- try
- Result := JBitmapToSurface(NativeBitmap2, Bitmap);
- finally
- NativeBitmap2.recycle;
- end;
- end;
- function TBitmapCodecAndroid.LoadThumbnailFromStream(const AStream: TStream;
- const AFitWidth, AFitHeight: Single; const UseEmbedded: Boolean;
- const AutoCut: Boolean; const Bitmap: TBitmapSurface): Boolean;
- var
- TempStream: TMemoryStream;
- TempArray: TJavaArray<Byte>;
- NativeBitmap1, NativeBitmap2, NativeBitmap3: JBitmap;
- LoadOptions: JBitmapFactory_Options;
- SavePosition: Int64;
- X, Y, W, H: Integer;
- begin
- if IsGIFStream(AStream) then
- Result := LoadMovieFromStreamScaled(AStream, Bitmap, TPoint.Create(Round(AFitWidth), Round(AFitHeight)))
- 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 := True;
- TJBitmapFactory.JavaClass.decodeByteArray(TempArray, 0, TempArray.Length, LoadOptions);
- //计算缩略尺寸
- calculateInSampleSize(LoadOptions, Round(AFitWidth), Round(AFitHeight));
- 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);
- NativeBitmap2 := TJBitmap.JavaClass.createScaledBitmap(NativeBitmap1, Round(AFitWidth), Round(AFitHeight), True);
- //kngstr
- if NativeBitmap2 = nil then
- Exit(False);
- try
- if not AutoCut then
- Result := JBitmapToSurface(NativeBitmap2, Bitmap)
- else begin //临时处理下,截取大图的中间部分,尤其是分辨率特别大的
- X := 0;
- Y := 0;
- W := Round(AFitWidth);
- H := Round(AFitHeight);
- if W > H then begin
- X := (W - H) div 2;
- W := H;
- end
- else if W < H then begin
- Y := (H - W) div 2;
- H := W;
- end;
- NativeBitmap3 := TJBitmap.JavaClass.createBitmap(NativeBitmap2, X, Y, W, H);
- //kngstr
- if NativeBitmap3 = nil then
- Exit(False);
- try
- Result := JBitmapToSurface(NativeBitmap3, Bitmap);
- finally
- NativeBitmap3.recycle;
- end;
- end;
- finally
- NativeBitmap2.recycle;
- end;
- finally
- NativeBitmap1.recycle;
- end;
- finally
- AStream.Position := SavePosition;
- end;
- end;
- end;
- {$ENDIF}
- {$ENDIF}
- {$IFDEF FMX}
- { TBitmap16bitFiler }
- class constructor TBitmap16bitFiler.Create;
- var
- i: integer;
- begin
- i := 0;
- while i < $10000 do
- begin
- // 不用for因为优化会将循环变量的值保存在寄存器不更新到内存
- Color[i] := PixelToAlphaColor(@i, TPixelFormat.BGR_565);
- inc(i);
- end;
- end;
- class procedure TBitmap16bitFiler.FillScanLine(D: TBitmapData;
- ScanLine, Width: integer; data: Pointer);
- var
- SC: PAlphaColorArray;
- DA: PWordArray;
- I: Integer;
- MinWidth: Integer;
- begin
- SC := D.GetScanline(ScanLine);
- DA := data;
- MinWidth := D.Width;
- if (Width > 0) and (Width < MinWidth) then
- MinWidth := Width;
- for I := 0 to MinWidth - 1 do
- begin
- if D.PixelFormat = TPixelFormat.BGRA then
- begin
- SC[I] := Color[DA[I]];
- end
- else
- begin
- AlphaColorToPixel(Color[DA[I]], Addr(SC[I]), D.PixelFormat);
- end;
- end;
- end;
- function FillScanLineFormMemory(BitmapData: TBitmapData; ScanLineIndex, Width: integer;
- InputData: Pointer; InputFormat: TPixelFormat): Boolean;
- var
- SC: PAlphaColorArray;
- Buffer: PAlphaColor;
- MinWidth: Integer;
- begin
- Result := False;
- if ScanLineIndex < 0 then exit;
- if ScanLineIndex >= BitmapData.Height then exit;
-
- SC := BitmapData.GetScanline(ScanLineIndex);
- MinWidth := BitmapData.Width;
- if (Width > 0) and (Width < MinWidth) then
- MinWidth := Width;
- if InputFormat = BitmapData.PixelFormat then
- begin
- Move(PByte(InputData)[0], SC[0], BitmapData.BytesPerPixel * MinWidth);
- end
- else
- begin
- Buffer := GetMemory(MinWidth * SizeOf(TAlphaColor));
- try
- ScanlineToAlphaColor(InputData, Buffer, MinWidth, InputFormat);
- AlphaColorToScanline(Buffer, SC, MinWidth, BitmapData.PixelFormat);
- finally
- FreeMemory(Buffer);
- end;
- end;
- Result := True;
- end;
- {$ENDIF}
- { TBitmapCodecWIC }
- {$IFDEF MSWINDOWS}
- function TBitmapCodecWIC.DecodeFrame(const Frame: IWICBitmapFrameDecode;
- const Bitmap: TBitmapSurface; const MaxSizeLimit: Cardinal): Boolean;
- type
- TPrivateMethodType = function (const Frame: IWICBitmapFrameDecode;
- const Bitmap: TBitmapSurface; const MaxSizeLimit: Cardinal): Boolean of object;
- var
- AMethod: TMethod;
- AInvoke: TPrivateMethodType absolute AMethod;
- begin
- AMethod.Code := @TBitmapCodecWIC.DecodeFrame;
- AMethod.Data := Self;
- Result := AInvoke(Frame, Bitmap, MaxSizeLimit);
- end;
- class function TBitmapCodecWIC.GetImageSize(const AStream: TStream): TPointF;
- var
- Decoder: IWICBitmapDecoder;
- Frame: IWICBitmapFrameDecode;
- W, H: UINT;
- CopyStream: TMemoryStream;
- Stream: IWICStream;
- SavePosition: Int64;
- begin
- W := 0;
- H := 0;
- SavePosition := AStream.Position;
- try
- CopyStream := TMemoryStream.Create;
- try
- CopyStream.CopyFrom(AStream, AStream.Size);
- ImagingFactory.CreateStream(Stream);
- Stream.InitializeFromMemory(CopyStream.Memory, CopyStream.Size);
- ImagingFactory.CreateDecoderFromStream(stream, GUID_NULL, WICDecodeMetadataCacheOnDemand, Decoder);
- if Decoder <> nil then
- begin
- Decoder.GetFrame(0, Frame);
- if Frame <> nil then
- Frame.GetSize(W, H);
- end;
- Result := PointF(W, H);
- finally
- CopyStream.Free;
- end;
- finally
- AStream.Position := SavePosition;
- end;
- 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) or (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 := ImagingFactory.CreateBitmapScaler(Scaler);
- if not Succeeded(LResult) then
- Exit;
- LResult := E_FAIL;
- 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.Rotate(const AStream: TStream;
- const Angle: Single): Boolean;
- begin
- //未完成
- Result := False;
- end;
- function TBitmapCodecWIC.Rotate(const Extension: string; const Angle: Single;
- const Bitmap: TBitmapSurface): Boolean;
- begin
- //未完成
- Result := False;
- end;
- function TBitmapCodecWIC.LoadThumbnailFromStream(const AStream: TStream;
- const AFitWidth, AFitHeight: Single; const UseEmbedded, AutoCut: Boolean;
- const Bitmap: TBitmapSurface): Boolean;
- var
- Decoder: IWICBitmapDecoder;
- CopyStream: TMemoryStream;
- Stream: IWICStream;
- Frame: IWICBitmapFrameDecode;
- Bmp: IWICBitmapSource;
- Converter: IWICFormatConverter;
- Scaler: IWICBitmapScaler;
- R: TRectF;
- Width, Height: UINT;
- 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);
- R := TRectF.Create(0, 0, Width, Height);
- R.Fit(TRectF.Create(0, 0, AFitWidth, AFitHeight));
- ImagingFactory.CreateBitmapScaler(Scaler);
- if Succeeded(Scaler.Initialize(frame, Trunc(R.Width), Trunc(R.Height), WICBitmapInterpolationModeLinear)) 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;
- {$ENDIF}
- end.
|