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