1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674 |
- {*******************************************************}
- { }
- { Methods of Image }
- { }
- { CopyRight (C) 2018-2020 KngStr }
- { }
- { Some from FlyUtils.TBitmapHelper }
- { IOS code from 凌风 }
- { macOS code from ying32 }
- { }
- {*******************************************************}
- unit ksBitmapHelper;
- {$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 IOS}
- FMX.Graphics.iOS, iOSapi.UIKit, iOSapi.Foundation, iOSapi.CoreGraphics,
- Macapi.ObjectiveC, FMX.Helpers.iOS, iOSapi.CocoaTypes,
- {$ENDIF}
- {$IFDEF MACOS}
- Macapi.AppKit, Macapi.ObjectiveC, FMX.Helpers.Mac, Macapi.Foundation,
- Macapi.CocoaTypes, Macapi.CoreGraphics,
- FMX.Canvas.Mac,
- {$ENDIF}
- {$IFDEF MSWINDOWS}
- FMX.Canvas.D2D, Winapi.Wincodec, Winapi.Windows, Winapi.ActiveX,
- {$ENDIF}
- {$ELSE}
- Vcl.Consts,
- {$ENDIF}
- System.SysConst,
- System.SysUtils,
- System.Math;
- {$IFDEF MACOS}
- // copy from FMX.Canvas.Mac
- var
- GlobalColorSpace: CGColorSpaceRef;
- function ColorSpace: CGColorSpaceRef;
- begin
- if GlobalColorSpace = nil then
- GlobalColorSpace := CGColorSpaceCreateDeviceRGB;
- Result := GlobalColorSpace;
- end;
- {$ENDIF}
- //add by 爱吃猪头肉。
- type
- //感谢 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 iOS}
- TBitmapCodecIOS = class(FMX.Graphics.iOS.TBitmapCodecQuartz)
- private const
- DefaultSaveQuality = 75;
- private
- class function IsGIFStream(const Stream: TStream): Boolean;
- function LoadMovieFromStreamScaled(const AStream: TStream;
- const Surface: TBitmapSurface; const FitSize: TPoint): Boolean;
- function LoadMovieFromStream(const Stream: TStream;
- const Surface: TBitmapSurface): Boolean;
- public
- class function GetImageSize(const AFileName: string): TPointF; overload;
- class function GetImageSize(const AStream: TStream): TPointF; overload;
- function LoadFromStream(const AStream: TStream; const Bitmap: TBitmapSurface;
- const MaxSizeLimit: Cardinal): Boolean; override;
- function LoadThumbnailFromStream(const AStream: TStream; const AFitWidth, AFitHeight: Single;
- const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
- function LoadThumbnailFromStream(const AStream: TStream; const ASrc, ADest: TRectF;
- const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
- function Rotate(const Extension: string; const Angle: Single; const Bitmap: TBitmapSurface): Boolean; overload;
- function Rotate(const AStream: TStream; const Angle: Single): Boolean; overload;
- end;
- {$ENDIF}
- {$IFDEF MACOS}
- TBitmapCodecMacOS = class(FMX.Canvas.Mac.TBitmapCodecQuartz)
- private const
- DefaultSaveQuality = 75;
- private
- class function IsGIFStream(const Stream: TStream): Boolean;
- function LoadMovieFromStreamScaled(const AStream: TStream;
- const Surface: TBitmapSurface; const FitSize: TPoint): Boolean;
- function LoadMovieFromStream(const Stream: TStream;
- const Surface: TBitmapSurface): Boolean;
- public
- class function GetImageSize(const AFileName: string): TPointF; overload;
- class function GetImageSize(const AStream: TStream): TPointF; overload;
- function LoadFromStream(const AStream: TStream; const Bitmap: TBitmapSurface;
- const MaxSizeLimit: Cardinal): Boolean; override;
- function LoadThumbnailFromStream(const AStream: TStream; const AFitWidth, AFitHeight: Single;
- const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
- function LoadThumbnailFromStream(const AStream: TStream; const ASrc, ADest: TRectF;
- const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
- function Rotate(const Extension: string; const Angle: Single; const Bitmap: TBitmapSurface): Boolean; overload;
- function Rotate(const AStream: TStream; const Angle: Single): Boolean; overload;
- end;
- {$ENDIF}
- {$IFDEF MSWINDOWS}
- TBitmapCodecWIC = class(FMX.Canvas.D2D.TCustomBitmapCodecWIC)
- private
- 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)
- {$ENDIF}
- {$IFDEF IOS}
- Result := TBitmapCodecIOS(CodecClass).GetImageSize(AStream)
- {$ENDIF}
- {$IFDEF MSWINDOWS}
- Result := TBitmapCodecWIC(CodecClass).GetImageSize(AStream)
- {$ENDIF}
- {$IFDEF MACOS}
- Result := TBitmapCodecMacOS(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);
- {$ENDIF}
- {$IFDEF IOS}
- Result := TBitmapCodecIOS(Codec).LoadFromStream(AStream, Bitmap, MaxSizeLimit);
- {$ENDIF}
- {$IFDEF MSWINDOWS}
- Result := TBitmapCodecWIC(Codec).LoadFromStream(AStream, Bitmap, MaxSizeLimit);
- {$ENDIF}
- {$IFDEF MACOS}
- Result := TBitmapCodecMacOS(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);
- {$ENDIF}
- {$IFDEF IOS}
- Result := TBitmapCodecIOS(Codec).LoadThumbnailFromStream(AStream, ASrc, ADest, UseEmbedded, Bitmap);
- {$ENDIF}
- {$IFDEF MSWINDOWS}
- Result := TBitmapCodecWIC(Codec).LoadThumbnailFromStream(AStream, ASrc, ADest, UseEmbedded, Bitmap);
- {$ENDIF}
- {$IFDEF MACOS}
- Result := TBitmapCodecMacOS(Codec).LoadThumbnailFromStream(AStream, ASrc, ADest, UseEmbedded, Bitmap);
- {$ENDIF}
- finally
- Codec.Free;
- end;
- 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);
- {$ENDIF}
- {$IFDEF IOS}
- Result := TBitmapCodecIOS(Codec).Rotate(AStream, Angle);
- {$ENDIF}
- {$IFDEF MSWINDOWS}
- Result := TBitmapCodecWIC(Codec).Rotate(AStream, Angle);
- {$ENDIF}
- {$IFDEF MACOS}
- Result := TBitmapCodecMacOS(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);
- {$ENDIF}
- {$IFDEF IOS}
- Result := TBitmapCodecIOS(Codec).Rotate(Extension, Angle, Bitmap);
- {$ENDIF}
- {$IFDEF MSWINDOWS}
- Result := TBitmapCodecWIC(Codec).Rotate(Extension, Angle, Bitmap);
- {$ENDIF}
- {$IFDEF MACOS}
- Result := TBitmapCodecMacOS(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);
- {$ENDIF}
- {$IFDEF IOS}
- Result := TBitmapCodecIOS(Codec).LoadThumbnailFromStream(AStream, AFitWidth, AFitHeight, UseEmbedded, Bitmap);
- {$ENDIF}
- {$IFDEF MSWINDOWS}
- Result := TBitmapCodecWIC(Codec).LoadThumbnailFromStream(AStream, AFitWidth, AFitHeight, UseEmbedded, AutoCut, Bitmap);
- {$ENDIF}
- {$IFDEF MACOS}
- Result := TBitmapCodecMacOS(Codec).LoadThumbnailFromStream(AStream, AFitWidth, AFitHeight, UseEmbedded, Bitmap);
- {$ENDIF}
- finally
- Codec.Free;
- end;
- 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}
- { TBitmapCodecIOS }
- {$IFDEF IOS}
- class function TBitmapCodecIOS.GetImageSize(const AFileName: string): TPointF;
- begin
- Result := inherited GetImageSize(AFileName);
- end;
- class function TBitmapCodecIOS.GetImageSize(const AStream: TStream): TPointF;
- var
- Img: UIImage;
- TempStream: TMemoryStream;
- aData: NSData;
- SavePosition: Int64;
- begin
- SavePosition := AStream.Position;
- try
- TempStream := TMemoryStream.Create;
- try
- AStream.Position := 0;
- TempStream.CopyFrom(AStream, AStream.Size);
- aData := TNSData.Wrap(TNSData.alloc.initWithBytesNoCopy(TempStream.Memory,TempStream.Size,False));
- if aData.length > 0 then
- begin
- Img := TUIImage.Wrap(TUIImage.alloc.initWithData(aData));
- if Img <> nil then
- try
- Result := PointF(Img.Size.width, Img.Size.height);
- finally
- Img.release;
- end
- else
- Result := TPointF.Zero;
- end else
- Result := TPointF.Zero;
- finally
- TempStream.free;
- end;
- finally
- AStream.Position := SavePosition;
- end;
- end;
- class function TBitmapCodecIOS.IsGIFStream(const Stream: TStream): Boolean;
- begin
- Result := TImageTypeChecker.GetType(Stream) = SGIFImageExtension;
- end;
- function TBitmapCodecIOS.LoadFromStream(const AStream: TStream;
- const Bitmap: TBitmapSurface; const MaxSizeLimit: Cardinal): Boolean;
- begin
- Result := inherited LoadFromStream(AStream, Bitmap, MaxSizeLimit);
- end;
- function TBitmapCodecIOS.LoadMovieFromStream(const Stream: TStream;
- const Surface: TBitmapSurface): Boolean;
- begin
- Result := False;
- end;
- function TBitmapCodecIOS.LoadMovieFromStreamScaled(const AStream: TStream;
- const Surface: TBitmapSurface; const FitSize: TPoint): Boolean;
- begin
- Result := False;
- end;
- function TBitmapCodecIOS.LoadThumbnailFromStream(const AStream: TStream; const AFitWidth, AFitHeight: Single;
- const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean;
- var
- Img: UIImage;
- ImgRef: CGImageRef;
- CtxRef: CGContextRef;
- R: TRectF;
- TempStream: TMemoryStream;
- aData: NSData;
- SavePosition: Int64;
- begin
- Result := False;
- SavePosition := AStream.Position;
- try
- TempStream := TMemoryStream.Create;
- try
- AStream.Position := 0;
- TempStream.CopyFrom(AStream, AStream.Size);
- aData := TNSData.Wrap(TNSData.alloc.initWithBytesNoCopy(TempStream.Memory,TempStream.Size,False));
- if aData.length > 0 then
- begin
- Img := TUIImage.Wrap(TUIImage.alloc.initWithData(aData));
- if Img <> nil then
- try
- ImgRef := Img.cGImage;
- if ImgRef <> nil then
- begin
- R := TRectF.Create(0, 0, CGImageGetWidth(ImgRef), CGImageGetHeight(ImgRef));
- R.Fit(TRectF.Create(0, 0, AFitWidth, AFitHeight));
- Bitmap.SetSize(Round(R.Width), Round(R.Height), TPixelFormat.RGBA);
- CtxRef := CGBitmapContextCreate(Bitmap.Bits, Bitmap.Width, Bitmap.Height, 8, Bitmap.Pitch, ColorSpace,
- kCGImageAlphaPremultipliedLast);
- try
- CGContextDrawImage(CtxRef, CGRectMake(0, 0, Bitmap.Width, Bitmap.Height), imgRef);
- finally
- CGContextRelease(CtxRef);
- end;
- Result := True;
- end;
- finally
- Img.release;
- end;
- end;
- finally
- TempStream.free;
- end;
- finally
- AStream.Position := SavePosition;
- end;
- end;
- function TBitmapCodecIOS.LoadThumbnailFromStream(const AStream: TStream;
- const ASrc, ADest: TRectF; const UseEmbedded: Boolean;
- const Bitmap: TBitmapSurface): Boolean;
- var
- Img: UIImage;
- ImgRef: CGImageRef;
- CtxRef: CGContextRef;
- R: TRectF;
- TempStream: TMemoryStream;
- aData: NSData;
- SavePosition: Int64;
- begin
- Result := False;
- SavePosition := AStream.Position;
- try
- TempStream := TMemoryStream.Create;
- try
- AStream.Position := 0;
- TempStream.CopyFrom(AStream, AStream.Size);
- aData := TNSData.Wrap(TNSData.alloc.initWithBytesNoCopy(TempStream.Memory,TempStream.Size,False));
- if aData.length > 0 then
- begin
- Img := TUIImage.Wrap(TUIImage.alloc.initWithData(aData));
- if Img <> nil then
- try
- ImgRef := Img.cGImage;
- if ImgRef <> nil then
- begin
- R := TRectF.Create(0, 0, ASrc.Width, ASrc.Height);
- R.Fit(TRectF.Create(0, 0, ADest.Width, ADest.Height));
- Bitmap.SetSize(Round(R.Width), Round(R.Height), TPixelFormat.RGBA);
- CtxRef := CGBitmapContextCreate(Bitmap.Bits, Bitmap.Width, Bitmap.Height, 8, Bitmap.Pitch, ColorSpace,
- kCGImageAlphaPremultipliedLast);
- try
- CGContextDrawImage(CtxRef, CGRectMake(0, 0, Bitmap.Width, Bitmap.Height), imgRef);
- finally
- CGContextRelease(CtxRef);
- end;
- Result := True;
- end;
- finally
- Img.release;
- end;
- end;
- finally
- TempStream.free;
- end;
- finally
- AStream.Position := SavePosition;
- end;
- end;
- function TBitmapCodecIOS.Rotate(const Extension: string; const Angle: Single;
- const Bitmap: TBitmapSurface): Boolean;
- var
- Img: UIImage;
- ImageRef: CGImageRef;
- CtxRef: CGContextRef;
- BitmapSize: TSize;
- ColorSpace: CGColorSpaceRef;
- bp: TBitmap;
- begin
- Result := False;
- bp := UIImageToBitmap(BitmapSurfaceToUIImage(Bitmap), Angle, TSize.Create(Bitmap.Width, Bitmap.Height));
- try
- ImageRef := BitmapToUIImage(bp).CGImage;
- if ImageRef <> nil then
- begin
- BitmapSize := TSize.Create(CGImageGetWidth(ImageRef), CGImageGetHeight(ImageRef));
- Bitmap.Clear(TAlphaColorRec.Null);
- Bitmap.SetSize(BitmapSize.cx, BitmapSize.cy);
- ColorSpace := CGColorSpaceCreateDeviceRGB;
- try
- CtxRef := CGBitmapContextCreate(Bitmap.Bits, Bitmap.Width, Bitmap.Height, 8,
- Bitmap.Pitch, ColorSpace, kCGImageAlphaPremultipliedLast or kCGBitmapByteOrder32Big);
- try
- CGContextDrawImage(CtxRef, CGRectMake(0, 0, Bitmap.Width, Bitmap.Height), ImageRef);
- finally
- CGContextRelease(CtxRef);
- end;
- Result := True;
- finally
- CGColorSpaceRelease(ColorSpace);
- end;
- end
- finally
- bp.Free;
- end;
- end;
- function TBitmapCodecIOS.Rotate(const AStream: TStream;
- const Angle: Single): Boolean;
- var bp: TBitmap;
- DataType: string;
- begin
- Result := False;
- DataType := TImageTypeChecker.GetType(AStream);
- if DataType = SGIFImageExtension then
- Exit;
- bp:= TBitmap.Create;
- try
- AStream.Position := 0;
- bp.LoadFromStream(AStream);
- bp.Rotate(Angle);
- AStream.Size := 0;
- bp.SaveToStream(AStream, DataType);
- Result := True;
- finally
- bp.Free;
- end;
- end;
- {$ENDIF}
- { TBitmapCodecMacOS }
- {$IFDEF MacOS}
- class function TBitmapCodecMacOS.GetImageSize(const AFileName: string): TPointF;
- begin
- Result := inherited GetImageSize(AFileName);
- end;
- class function TBitmapCodecMacOS.GetImageSize(const AStream: TStream): TPointF;
- var
- Img: NSImage;
- TempStream: TMemoryStream;
- aData: NSData;
- SavePosition: Int64;
- begin
- SavePosition := AStream.Position;
- try
- TempStream := TMemoryStream.Create;
- try
- AStream.Position := 0;
- TempStream.CopyFrom(AStream, AStream.Size);
- aData := TNSData.Wrap(TNSData.alloc.initWithBytesNoCopy(TempStream.Memory,TempStream.Size,False));
- if aData.length > 0 then
- begin
- Img := TNSImage.Wrap(TNSImage.alloc.initWithData(aData));
- if Img <> nil then
- try
- Result := PointF(Img.Size.width, Img.Size.height);
- finally
- Img.release;
- end
- else
- Result := TPointF.Zero;
- end else
- Result := TPointF.Zero;
- finally
- TempStream.free;
- end;
- finally
- AStream.Position := SavePosition;
- end;
- end;
- class function TBitmapCodecMacOS.IsGIFStream(const Stream: TStream): Boolean;
- begin
- Result := TImageTypeChecker.GetType(Stream) = SGIFImageExtension;
- end;
- function TBitmapCodecMacOS.LoadFromStream(const AStream: TStream;
- const Bitmap: TBitmapSurface; const MaxSizeLimit: Cardinal): Boolean;
- begin
- Result := inherited LoadFromStream(AStream, Bitmap, MaxSizeLimit);
- end;
- function TBitmapCodecMacOS.LoadMovieFromStream(const Stream: TStream;
- const Surface: TBitmapSurface): Boolean;
- begin
- Result := False;
- end;
- function TBitmapCodecMacOS.LoadMovieFromStreamScaled(const AStream: TStream;
- const Surface: TBitmapSurface; const FitSize: TPoint): Boolean;
- begin
- Result := False;
- end;
- function TBitmapCodecMacOS.LoadThumbnailFromStream(const AStream: TStream; const AFitWidth, AFitHeight: Single;
- const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean;
- var
- Img: NSImage;
- ImgRef: CGImageRef;
- CtxRef: CGContextRef;
- R: TRectF;
- TempStream: TMemoryStream;
- aData: NSData;
- SavePosition: Int64;
- begin
- Result := False;
- SavePosition := AStream.Position;
- try
- TempStream := TMemoryStream.Create;
- try
- AStream.Position := 0;
- TempStream.CopyFrom(AStream, AStream.Size);
- aData := TNSData.Wrap(TNSData.alloc.initWithBytesNoCopy(TempStream.Memory,TempStream.Size,False));
- if aData.length > 0 then
- begin
- Img := TNSImage.Wrap(TNSImage.alloc.initWithData(aData));
- if Img <> nil then
- try
- ImgRef := CGImageRef(Img);
- if ImgRef <> nil then
- begin
- R := TRectF.Create(0, 0, CGImageGetWidth(ImgRef), CGImageGetHeight(ImgRef));
- R.Fit(TRectF.Create(0, 0, AFitWidth, AFitHeight));
- Bitmap.SetSize(Round(R.Width), Round(R.Height), TPixelFormat.RGBA);
- CtxRef := CGBitmapContextCreate(Bitmap.Bits, Bitmap.Width, Bitmap.Height, 8, Bitmap.Pitch, ColorSpace,
- kCGImageAlphaPremultipliedLast);
- try
- CGContextDrawImage(CtxRef, CGRectMake(0, 0, Bitmap.Width, Bitmap.Height), imgRef);
- finally
- CGContextRelease(CtxRef);
- end;
- Result := True;
- end;
- finally
- Img.release;
- end;
- end;
- finally
- TempStream.free;
- end;
- finally
- AStream.Position := SavePosition;
- end;
- end;
- function TBitmapCodecMacOS.LoadThumbnailFromStream(const AStream: TStream;
- const ASrc, ADest: TRectF; const UseEmbedded: Boolean;
- const Bitmap: TBitmapSurface): Boolean;
- var
- Img: NSImage;
- ImgRef: CGImageRef;
- CtxRef: CGContextRef;
- R: TRectF;
- TempStream: TMemoryStream;
- aData: NSData;
- SavePosition: Int64;
- begin
- Result := False;
- SavePosition := AStream.Position;
- try
- TempStream := TMemoryStream.Create;
- try
- AStream.Position := 0;
- TempStream.CopyFrom(AStream, AStream.Size);
- aData := TNSData.Wrap(TNSData.alloc.initWithBytesNoCopy(TempStream.Memory,TempStream.Size,False));
- if aData.length > 0 then
- begin
- Img := TNSImage.Wrap(TNSImage.alloc.initWithData(aData));
- if Img <> nil then
- try
- ImgRef := CGImageRef(Img);
- if ImgRef <> nil then
- begin
- R := TRectF.Create(0, 0, ASrc.Width, ASrc.Height);
- R.Fit(TRectF.Create(0, 0, ADest.Width, ADest.Height));
- Bitmap.SetSize(Round(R.Width), Round(R.Height), TPixelFormat.RGBA);
- CtxRef := CGBitmapContextCreate(Bitmap.Bits, Bitmap.Width, Bitmap.Height, 8, Bitmap.Pitch, ColorSpace,
- kCGImageAlphaPremultipliedLast);
- try
- CGContextDrawImage(CtxRef, CGRectMake(0, 0, Bitmap.Width, Bitmap.Height), imgRef);
- finally
- CGContextRelease(CtxRef);
- end;
- Result := True;
- end;
- finally
- Img.release;
- end;
- end;
- finally
- TempStream.free;
- end;
- finally
- AStream.Position := SavePosition;
- end;
- end;
- function TBitmapCodecMacOS.Rotate(const Extension: string; const Angle: Single;
- const Bitmap: TBitmapSurface): Boolean;
- //var
- // Img: NSImage;
- // ImageRef: CGImageRef;
- // CtxRef: CGContextRef;
- // BitmapSize: TSize;
- // ColorSpace: CGColorSpaceRef;
- // bp: TBitmap;
- begin
- //未完成
- Result := False;
- // inherited;
- // bp := UIImageToBitmap(BitmapSurfaceToMacImage(Bitmap), Angle, TSize.Create(Bitmap.Width, Bitmap.Height));
- // try
- // ImageRef := CGImageRef(BitmapToMacBitmap(Bitmap));
- // if ImageRef <> nil then
- // begin
- // BitmapSize := TSize.Create(CGImageGetWidth(ImageRef), CGImageGetHeight(ImageRef));
- // Bitmap.Clear(TAlphaColorRec.Null);
- // Bitmap.SetSize(BitmapSize.cx, BitmapSize.cy);
- // ColorSpace := CGColorSpaceCreateDeviceRGB;
- // try
- // CtxRef := CGBitmapContextCreate(Bitmap.Bits, Bitmap.Width, Bitmap.Height, 8,
- // Bitmap.Pitch, ColorSpace, kCGImageAlphaPremultipliedLast or kCGBitmapByteOrder32Big);
- // try
- // CGContextDrawImage(CtxRef, CGRectMake(0, 0, Bitmap.Width, Bitmap.Height), ImageRef);
- // finally
- // CGContextRelease(CtxRef);
- // end;
- // Result := True;
- // finally
- // CGColorSpaceRelease(ColorSpace);
- // end;
- // end
- // finally
- // bp.Free;
- // end;
- end;
- function TBitmapCodecMacOS.Rotate(const AStream: TStream;
- const Angle: Single): Boolean;
- var bp: TBitmap;
- DataType: string;
- begin
- Result := False;
- DataType := TImageTypeChecker.GetType(AStream);
- if DataType = SGIFImageExtension then
- Exit;
- bp:= TBitmap.Create;
- try
- AStream.Position := 0;
- bp.LoadFromStream(AStream);
- bp.Rotate(Angle);
- AStream.Size := 0;
- bp.SaveToStream(AStream, DataType);
- Result := True;
- finally
- bp.Free;
- end;
- end;
- {$ENDIF}
- {$IFDEF MACOS}
- initialization
- finalization
- if GlobalColorSpace <> nil then
- CGColorSpaceRelease(GlobalColorSpace);
- {$ENDIF}
- end.
|