ksBitmapHelper.pas 83 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674
  1. {*******************************************************}
  2. { }
  3. { Methods of Image }
  4. { }
  5. { CopyRight (C) 2018-2020 KngStr }
  6. { }
  7. { Some from FlyUtils.TBitmapHelper }
  8. { IOS code from 凌风 }
  9. { macOS code from ying32 }
  10. { }
  11. {*******************************************************}
  12. unit ksBitmapHelper;
  13. {$DEFINE FMX}
  14. interface
  15. {$SCOPEDENUMS ON}
  16. uses
  17. {$IFDEF FMX}
  18. FMX.Graphics,
  19. FMX.Utils,
  20. FMX.Types,
  21. FMX.Surfaces,
  22. {$ELSE}
  23. Vcl.Graphics,
  24. {$ENDIF}
  25. System.Types,
  26. System.UITypes,
  27. System.Classes;
  28. {$IFDEF FMX}
  29. {$ELSE}
  30. resourcestring
  31. SBitmapSavingFailed = 'Saving bitmap failed.';
  32. SBitmapSavingFailedNamed = 'Saving bitmap failed (%s).';
  33. {$ENDIF}
  34. var
  35. MonochromeChange_Threshold: Byte = 120;
  36. MonochromeChange_Weighting_Red: Double = 0.3;
  37. MonochromeChange_Weighting_Green: Double = 0.59;
  38. MonochromeChange_Weighting_Blue: Double = 0.11;
  39. type
  40. /// <summary>
  41. /// 转黑白的方法
  42. /// </summary>
  43. TMonochromeChangeType = (
  44. /// <summary>
  45. /// 平均值
  46. /// </summary>
  47. Average,
  48. /// <summary>
  49. /// 权值
  50. /// </summary>
  51. Weighting,
  52. /// <summary>
  53. /// 最大值
  54. /// </summary>
  55. Max);
  56. /// <summary>
  57. /// <para>
  58. /// TBitmap Save As BMP
  59. /// </para>
  60. /// <para>
  61. /// BytesPerPixel = -1 表示自动
  62. /// </para>
  63. /// </summary>
  64. TKngStrBitmapHelper = class helper for TBitmap
  65. public
  66. function SaveAsBMPToFile(const AFileName: string; const BytesPerPixel: Integer = 3;
  67. const MonochromeChangeType: TMonochromeChangeType = TMonochromeChangeType.Average): Boolean; overload;
  68. procedure SaveAsBMPToFileDef(const AFileName: string); overload;
  69. function SaveAsBMPToStream(const AStream: TStream; const BytesPerPixel: Integer = 3;
  70. const MonochromeChangeType: TMonochromeChangeType = TMonochromeChangeType.Average): Boolean; overload;
  71. procedure SaveAsBMPToStreamDef(Stream: TStream); overload;
  72. {$IFDEF FMX}
  73. /// <summary>
  74. /// 用于在线程中代替 LoadFromFile ,不用自己调用 Synchronize 了。
  75. /// </summary>
  76. procedure SyncLoadFromFile(const AFileName: string);
  77. /// <summary>
  78. /// 用于在线程中代替 LoadThumbnailFromFile ,不用自己调用 Synchronize 了。
  79. /// </summary>
  80. /// <remarks>
  81. /// UseEmbedded 只有Windows下可用,而且缩略图大小不可控
  82. /// </remarks>
  83. procedure SyncLoadThumbnailFromFile(const AFileName: string; const AFitWidth, AFitHeight: Single;
  84. const UseEmbedded: Boolean = True);
  85. /// <summary>
  86. /// 用于在线程中代替 LoadFromStream ,不用自己调用 Synchronize 了。
  87. /// </summary>
  88. procedure SyncLoadFromStream(Stream: TStream);
  89. /// <summary>
  90. /// 用于在线程中代替 LoadThumbnailFromStream ,不用自己调用 Synchronize 了。
  91. /// </summary>
  92. /// <remarks>
  93. /// UseEmbedded 只有Windows下可用,而且缩略图大小不可控
  94. /// </remarks>
  95. procedure SyncLoadThumbnailFromStream(const Stream: TStream; const AFitWidth, AFitHeight: Single;
  96. const UseEmbedded: Boolean = True; const AutoCut: Boolean = True); overload;
  97. function SyncLoadThumbnailFromStream(const AStream: TStream; const ASrc, ADest: TRectF;
  98. const UseEmbedded: Boolean): Boolean; overload;
  99. /// <summary>
  100. /// 用于在线程中代替 Assign ,不用自己调用 Synchronize 了。
  101. /// </summary>
  102. procedure SyncAssign(Source: TPersistent);
  103. procedure SaveToStream(Stream: TStream; const Extension: string; SaveParams: PBitmapCodecSaveParams = nil); overload;
  104. {$ENDIF}
  105. end;
  106. {$IFDEF FMX}
  107. TBitmapCodecDescriptorField = (Extension, Description);
  108. TKngStrBitmapCodecManager = class helper for TBitmapCodecManager
  109. strict private
  110. class function GuessCodecClass(const Name: string; const Field: TBitmapCodecDescriptorField): TCustomBitmapCodecClass;
  111. public
  112. class function GetImageSize(const AFileName: string): TPointF; overload;
  113. class function GetImageSize(const AStream: TStream): TPointF; overload;
  114. class function LoadFromStream(const AStream: TStream; const Bitmap: TBitmapSurface;
  115. const MaxSizeLimit: Cardinal = 0): Boolean;
  116. /// <remarks>
  117. /// UseEmbedded 只有Windows下可用,而且缩略图大小不可控
  118. /// </remarks>
  119. class function LoadThumbnailFromStream(const AStream: TStream; const AFitWidth, AFitHeight: Single;
  120. const UseEmbedded: Boolean; const AutoCut: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
  121. /// <remarks>
  122. /// UseEmbedded 只有Windows下可用,而且缩略图大小不可控
  123. /// </remarks>
  124. class function LoadThumbnailFromStream(const AStream: TStream; const ASrc, ADest: TRectF;
  125. const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
  126. class function Rotate(const Extension: string; const Angle: Single; const Bitmap: TBitmapSurface): Boolean; overload;
  127. class function Rotate(const AStream: TStream; const Angle: Single): Boolean; overload;
  128. end;
  129. {$ENDIF}
  130. {$IFDEF FMX}
  131. {$ELSE}
  132. procedure GraphicToBitmap(const Src: Vcl.Graphics.TGraphic;
  133. const Dest: Vcl.Graphics.TBitmap; const TransparentColor: Vcl.Graphics.TColor = Vcl.Graphics.clNone);
  134. {$ENDIF}
  135. const
  136. SizeOftagBITMAPFILEHEADER = 14;
  137. SizeOftagBITMAPINFOHEADER = 40;
  138. RGB565ExtDataLen = 12;
  139. {$IFDEF FMX}
  140. function FillScanLineFormMemory(BitmapData: TBitmapData; ScanLineIndex, Width: integer;
  141. InputData: Pointer; InputFormat: TPixelFormat): Boolean;
  142. type
  143. // copy form [广州]庾伟洪<ywthegod@qq.com>
  144. TBitmap16bitFiler = class
  145. class var
  146. Color: array [0 .. $FFFF] of TAlphaColor;
  147. class constructor Create;
  148. class procedure FillScanLine(D: TBitmapData; ScanLine, Width: integer;
  149. data: Pointer); inline;
  150. end;
  151. // PAlphaColorArray = ^TAlphaColorArray; //FMX.Utils,
  152. // TWordArray = array [0 .. 0] of Word;
  153. // PWordArray = ^TWordArray; //System.SysUtils
  154. // y 行号 h 高度 w 宽度 b 一个像素用字节数
  155. // FVideoBuf 数据内存
  156. // bm.Map(TMapAccess.Write, bd);
  157. // try
  158. // for y := 0 to h - 1 do
  159. // begin
  160. // TBitmap16bitFiler.FillScanLine(bd, y, w, Addr(FVideoBuf[y * w * b]));
  161. // end;
  162. // finally
  163. // bm.Unmap(bd);
  164. // end;
  165. {$ENDIF}
  166. implementation
  167. uses
  168. {$IFDEF FMX}
  169. FMX.Consts,
  170. {$IFDEF ANDROID}
  171. FMX.Graphics.Android,
  172. Androidapi.JNIBridge,
  173. Androidapi.Helpers,
  174. FMX.Helpers.Android,
  175. Androidapi.JNI.GraphicsContentViewText,
  176. Androidapi.JNI.JavaTypes,
  177. {$ENDIF}
  178. {$IFDEF IOS}
  179. FMX.Graphics.iOS, iOSapi.UIKit, iOSapi.Foundation, iOSapi.CoreGraphics,
  180. Macapi.ObjectiveC, FMX.Helpers.iOS, iOSapi.CocoaTypes,
  181. {$ENDIF}
  182. {$IFDEF MACOS}
  183. Macapi.AppKit, Macapi.ObjectiveC, FMX.Helpers.Mac, Macapi.Foundation,
  184. Macapi.CocoaTypes, Macapi.CoreGraphics,
  185. FMX.Canvas.Mac,
  186. {$ENDIF}
  187. {$IFDEF MSWINDOWS}
  188. FMX.Canvas.D2D, Winapi.Wincodec, Winapi.Windows, Winapi.ActiveX,
  189. {$ENDIF}
  190. {$ELSE}
  191. Vcl.Consts,
  192. {$ENDIF}
  193. System.SysConst,
  194. System.SysUtils,
  195. System.Math;
  196. {$IFDEF MACOS}
  197. // copy from FMX.Canvas.Mac
  198. var
  199. GlobalColorSpace: CGColorSpaceRef;
  200. function ColorSpace: CGColorSpaceRef;
  201. begin
  202. if GlobalColorSpace = nil then
  203. GlobalColorSpace := CGColorSpaceCreateDeviceRGB;
  204. Result := GlobalColorSpace;
  205. end;
  206. {$ENDIF}
  207. //add by 爱吃猪头肉。
  208. type
  209. //感谢 yu 273637089 的测试和 提供 packed 信息。
  210. {$IFDEF FMX}
  211. {$ELSE}
  212. tagRGBTRIPLE = packed record
  213. rgbtBlue: Byte;
  214. rgbtGreen: Byte;
  215. rgbtRed: Byte;
  216. end;
  217. PRGBTripleArray = ^TRGBTripleArray;
  218. TRGBTripleArray = array [Byte] of tagRGBTRIPLE;
  219. {$ENDIF}
  220. tagBITMAPFILEHEADER = packed record
  221. bfType: Word;
  222. bfSize: DWORD;
  223. bfReserved1: Word;
  224. bfReserved2: Word;
  225. bfOffBits: DWORD;
  226. end;
  227. tagBITMAPINFOHEADER = packed record
  228. biSize: DWORD;
  229. biWidth: Longint;
  230. biHeight: Longint;
  231. biPlanes: Word;
  232. biBitCount: Word;
  233. biCompression: DWORD;
  234. biSizeImage: DWORD;
  235. biXPelsPerMeter: Longint;
  236. biYPelsPerMeter: Longint;
  237. biClrUsed: DWORD;
  238. biClrImportant: DWORD;
  239. end;
  240. tagRGBQUAD = packed record
  241. rgbBlue: Byte;
  242. rgbGreen: Byte;
  243. rgbRed: Byte;
  244. rgbReserved: Byte;
  245. end;
  246. {$IFDEF FMX}
  247. {$IFDEF ANDROID}
  248. TBitmapCodecAndroid = class(FMX.Graphics.Android.TBitmapCodecAndroid)
  249. private const
  250. DefaultSaveQuality = 75;
  251. private
  252. class function IsGIFStream(const Stream: TStream): Boolean;
  253. function LoadMovieFromStreamScaled(const AStream: TStream;
  254. const Surface: TBitmapSurface; const FitSize: TPoint): Boolean;
  255. function LoadMovieFromStream(const Stream: TStream;
  256. const Surface: TBitmapSurface): Boolean;
  257. function StretchIfNeed(const SrcBitmap: JBitmap;
  258. const Bitmap: TBitmapSurface; const LoadOptions: JBitmapFactory_Options;
  259. const MaxSizeLimit: Cardinal): Boolean;
  260. class function GetMovieSize(const Stream: TStream): TPoint;
  261. public
  262. class function GetImageSize(const AFileName: string): TPointF; overload;
  263. class function GetImageSize(const AStream: TStream): TPointF; overload;
  264. function LoadFromStream(const AStream: TStream; const Bitmap: TBitmapSurface;
  265. const MaxSizeLimit: Cardinal): Boolean; override;
  266. function LoadThumbnailFromStream(const AStream: TStream; const AFitWidth, AFitHeight: Single;
  267. const UseEmbedded: Boolean; const AutoCut: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
  268. function LoadThumbnailFromStream(const AStream: TStream; const ASrc, ADest: TRectF;
  269. const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
  270. function Rotate(const Extension: string; const Angle: Single; const Bitmap: TBitmapSurface): Boolean; overload;
  271. function Rotate(const AStream: TStream; const Angle: Single): Boolean; overload;
  272. end;
  273. {$ENDIF}
  274. {$IFDEF iOS}
  275. TBitmapCodecIOS = class(FMX.Graphics.iOS.TBitmapCodecQuartz)
  276. private const
  277. DefaultSaveQuality = 75;
  278. private
  279. class function IsGIFStream(const Stream: TStream): Boolean;
  280. function LoadMovieFromStreamScaled(const AStream: TStream;
  281. const Surface: TBitmapSurface; const FitSize: TPoint): Boolean;
  282. function LoadMovieFromStream(const Stream: TStream;
  283. const Surface: TBitmapSurface): Boolean;
  284. public
  285. class function GetImageSize(const AFileName: string): TPointF; overload;
  286. class function GetImageSize(const AStream: TStream): TPointF; overload;
  287. function LoadFromStream(const AStream: TStream; const Bitmap: TBitmapSurface;
  288. const MaxSizeLimit: Cardinal): Boolean; override;
  289. function LoadThumbnailFromStream(const AStream: TStream; const AFitWidth, AFitHeight: Single;
  290. const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
  291. function LoadThumbnailFromStream(const AStream: TStream; const ASrc, ADest: TRectF;
  292. const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
  293. function Rotate(const Extension: string; const Angle: Single; const Bitmap: TBitmapSurface): Boolean; overload;
  294. function Rotate(const AStream: TStream; const Angle: Single): Boolean; overload;
  295. end;
  296. {$ENDIF}
  297. {$IFDEF MACOS}
  298. TBitmapCodecMacOS = class(FMX.Canvas.Mac.TBitmapCodecQuartz)
  299. private const
  300. DefaultSaveQuality = 75;
  301. private
  302. class function IsGIFStream(const Stream: TStream): Boolean;
  303. function LoadMovieFromStreamScaled(const AStream: TStream;
  304. const Surface: TBitmapSurface; const FitSize: TPoint): Boolean;
  305. function LoadMovieFromStream(const Stream: TStream;
  306. const Surface: TBitmapSurface): Boolean;
  307. public
  308. class function GetImageSize(const AFileName: string): TPointF; overload;
  309. class function GetImageSize(const AStream: TStream): TPointF; overload;
  310. function LoadFromStream(const AStream: TStream; const Bitmap: TBitmapSurface;
  311. const MaxSizeLimit: Cardinal): Boolean; override;
  312. function LoadThumbnailFromStream(const AStream: TStream; const AFitWidth, AFitHeight: Single;
  313. const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
  314. function LoadThumbnailFromStream(const AStream: TStream; const ASrc, ADest: TRectF;
  315. const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
  316. function Rotate(const Extension: string; const Angle: Single; const Bitmap: TBitmapSurface): Boolean; overload;
  317. function Rotate(const AStream: TStream; const Angle: Single): Boolean; overload;
  318. end;
  319. {$ENDIF}
  320. {$IFDEF MSWINDOWS}
  321. TBitmapCodecWIC = class(FMX.Canvas.D2D.TCustomBitmapCodecWIC)
  322. private
  323. function DecodeFrame(const Frame: IWICBitmapFrameDecode; const Bitmap: TBitmapSurface;
  324. const MaxSizeLimit: Cardinal = 0): Boolean;
  325. public
  326. class function GetImageSize(const AStream: TStream): TPointF; overload;
  327. function LoadThumbnailFromStream(const AStream: TStream; const AFitWidth, AFitHeight: Single;
  328. const UseEmbedded: Boolean; const AutoCut: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
  329. function LoadThumbnailFromStream(const AStream: TStream; const ASrc, ADest: TRectF;
  330. const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
  331. function Rotate(const Extension: string; const Angle: Single; const Bitmap: TBitmapSurface): Boolean; overload;
  332. function Rotate(const AStream: TStream; const Angle: Single): Boolean; overload;
  333. end;
  334. {$ENDIF}
  335. {$ENDIF}
  336. const
  337. { constants for the biCompression field }
  338. {$EXTERNALSYM BI_RGB}
  339. BI_RGB = 0;
  340. {$EXTERNALSYM BI_RLE8}
  341. BI_RLE8 = 1;
  342. {$EXTERNALSYM BI_RLE4}
  343. BI_RLE4 = 2;
  344. {$EXTERNALSYM BI_BITFIELDS}
  345. BI_BITFIELDS = 3;
  346. const
  347. RGB565_MASK_RED = $F800;
  348. RGB565_MASK_GREEN = $07E0;
  349. RGB565_MASK_BLUE = $001F;
  350. // RGB565ExtDataLen = 12;
  351. function rgb_24_2_565(r,g,b: Byte): UInt16;
  352. begin
  353. // r := r * 31 div 255;
  354. // g := g * 64 div 255;
  355. // b := b * 31 div 255;
  356. // Result := r *2048 or g *32 or b;
  357. // Result := ((r shl 8) and RGB565_MASK_RED) or ((g shl 3) and RGB565_MASK_GREEN) or (b shr 3);
  358. Result := ((r shr 3) shl 11) or ((g shr 2 ) shl 5) or (b shr 3);
  359. end;
  360. {
  361. return (USHORT)(((unsigned(r) << 8) & 0xF800) |
  362. ((unsigned(g) << 3) & 0x7E0) |
  363. ((unsigned(b) >> 3)));
  364. }
  365. procedure rgb565_2_rgb24(rgb24: TBytes; rgb565: UInt16);
  366. begin
  367. //extract RGB
  368. rgb24[2] := (rgb565 and RGB565_MASK_RED) shr 11;
  369. rgb24[1] := (rgb565 and RGB565_MASK_GREEN) shr 5;
  370. rgb24[0] := (rgb565 and RGB565_MASK_BLUE);
  371. //amplify the image
  372. rgb24[2] := rgb24[2] shl 3;
  373. rgb24[1] := rgb24[2] shl 2;
  374. rgb24[0] := rgb24[2] shl 3;
  375. end;
  376. {
  377. //extract RGB
  378. rgb24[2] = (rgb565 & RGB565_MASK_RED) >> 11;
  379. rgb24[1] = (rgb565 & RGB565_MASK_GREEN) >> 5;
  380. rgb24[0] = (rgb565 & RGB565_MASK_BLUE);
  381. //amplify the image
  382. rgb24[2] <<= 3;
  383. rgb24[1] <<= 2;
  384. rgb24[0] <<= 3;
  385. }
  386. const
  387. RGB555_MASK_RED = $7C00;
  388. RGB555_MASK_GREEN = $03E0;
  389. RGB555_MASK_BLUE = $001F;
  390. function rgb_24_2_555(r,g,b: Byte): UInt16;
  391. begin
  392. Result := ((r shl 7) and RGB555_MASK_RED) or ((g shl 2) and RGB555_MASK_GREEN) or (b shr 3);
  393. end;
  394. procedure rgb555_2_rgb24(rgb24: TBytes; rgb555: UInt16);
  395. begin
  396. //extract RGB
  397. rgb24[0] := (rgb555 shl 3) and $00F8;
  398. rgb24[1] := (rgb555 shr 2) and $00F8;
  399. rgb24[2] := (rgb555 shr 7) and $00F8;
  400. end;
  401. {$IFDEF FMX}
  402. {$ELSE}
  403. procedure GraphicToBitmap(const Src: Vcl.Graphics.TGraphic;
  404. const Dest: Vcl.Graphics.TBitmap; const TransparentColor: Vcl.Graphics.TColor = Vcl.Graphics.clNone);
  405. begin
  406. // Do nothing if either source or destination are nil
  407. if not Assigned(Src) or not Assigned(Dest) then
  408. Exit;
  409. if Src.Empty then exit;
  410. // Size the bitmap
  411. Dest.Width := Src.Width;
  412. Dest.Height := Src.Height;
  413. if Src.Transparent then
  414. begin
  415. // Source graphic is transparent, make bitmap behave transparently
  416. Dest.Transparent := True;
  417. if (TransparentColor <> Vcl.Graphics.clNone) then
  418. begin
  419. // Set destination as transparent using required colour key
  420. Dest.TransparentColor := TransparentColor;
  421. Dest.TransparentMode := Vcl.Graphics.tmFixed;
  422. // Set background colour of bitmap to transparent colour
  423. Dest.Canvas.Brush.Color := TransparentColor;
  424. end
  425. else
  426. // No transparent colour: set transparency to automatic
  427. Dest.TransparentMode := Vcl.Graphics.tmAuto;
  428. end;
  429. // Clear bitmap to required background colour and draw bitmap
  430. Dest.Canvas.FillRect(System.Classes.Rect(0, 0, Dest.Width, Dest.Height));
  431. Dest.Canvas.Draw(0, 0, Src);
  432. end;
  433. {$ENDIF}
  434. //该代码片段来自于: http://www.sharejs.com/codes/delphi/2248
  435. { TKngStrBitmapHelper }
  436. function TKngStrBitmapHelper.SaveAsBMPToFile(const AFileName: string; const BytesPerPixel: Integer = 3;
  437. const MonochromeChangeType: TMonochromeChangeType = TMonochromeChangeType.Average): Boolean;
  438. var
  439. AStream: TStream;
  440. begin
  441. Result := False;
  442. {$IFDEF FMX}
  443. if IsEmpty then exit;
  444. {$ELSE}
  445. if Empty then exit;
  446. {$ENDIF}
  447. AStream := TFileStream.Create(AFileName, fmCreate);// or fmOpenReadWrite);
  448. try
  449. Result := SaveAsBMPToStream(AStream, BytesPerPixel);
  450. // if Result then
  451. // AStream.Size := AStream.Position;
  452. finally
  453. FreeAndNil(AStream);
  454. end;
  455. end;
  456. procedure TKngStrBitmapHelper.SaveAsBMPToFileDef(const AFileName: string);
  457. begin
  458. if not SaveAsBMPToFile(AFileName) then
  459. begin
  460. {$IFDEF FMX}
  461. raise EBitmapSavingFailed.CreateFMT(SBitmapSavingFailed, [AFileName]);
  462. {$ELSE}
  463. raise EInvalidGraphicOperation.CreateFmt(SBitmapSavingFailed, [AFileName]);
  464. {$ENDIF}
  465. end;
  466. end;
  467. //http://blog.csdn.net/pjpsmile/article/details/8985523
  468. function TKngStrBitmapHelper.SaveAsBMPToStream(const AStream: TStream; const BytesPerPixel: Integer = 3;
  469. const MonochromeChangeType: TMonochromeChangeType = TMonochromeChangeType.Average): Boolean;
  470. var
  471. I, CurrBytesPerPixel,
  472. wWidth, nCol, wRow, wByteIdex,
  473. bfReserved1, bfReserved2,
  474. nBmpWidth, nBmpHeight, bufferSize: Integer;
  475. BitmapInfo: tagBITMAPINFOHEADER;
  476. BMF: tagBITMAPFILEHEADER;
  477. {$IFDEF FMX}
  478. Data: TBitmapData;
  479. clr: TAlphaColor;
  480. AlphaColorBuffer: PAlphaColor;
  481. {$ELSE}
  482. Pixs : pRGBTripleArray;
  483. {$ENDIF}
  484. bmpData: TBytes;
  485. A32BitData: UInt32;
  486. ABitIndex,
  487. A8BitData: Byte;
  488. RGBQUAD: tagRGBQUAD;
  489. // FileSizeFix,
  490. A16BitData,
  491. GrayeData: UInt16;
  492. begin
  493. Result := False;
  494. {$IFDEF FMX}
  495. if IsEmpty then exit;
  496. {$ELSE}
  497. if Empty then exit;
  498. {$ENDIF}
  499. if not Assigned(AStream) then exit;
  500. CurrBytesPerPixel := BytesPerPixel;
  501. {$IFDEF FMX}
  502. Map(TMapAccess.Read, Data);
  503. {$ELSE}
  504. {$ENDIF}
  505. try
  506. if CurrBytesPerPixel = -1 then
  507. begin
  508. {$IFDEF FMX}
  509. CurrBytesPerPixel := Data.BytesPerPixel;
  510. {$ELSE}
  511. CurrBytesPerPixel := 3;
  512. if PixelFormat = pf1bit then
  513. CurrBytesPerPixel := 0;
  514. if PixelFormat = pf4bit then
  515. CurrBytesPerPixel := 2;
  516. if PixelFormat = pf8bit then
  517. CurrBytesPerPixel := 2;
  518. if PixelFormat = pf15bit then
  519. CurrBytesPerPixel := 2;
  520. if PixelFormat = pf16bit then
  521. CurrBytesPerPixel := 2;
  522. if PixelFormat = pf24bit then
  523. CurrBytesPerPixel := 3;
  524. if PixelFormat = pf32bit then
  525. CurrBytesPerPixel := 4;
  526. {$ENDIF}
  527. if not (CurrBytesPerPixel in [0,1,2,4]) then
  528. CurrBytesPerPixel := 3;
  529. end;
  530. if not (CurrBytesPerPixel in [0,1,2,3,4]) then
  531. exit;
  532. //不打算支持 8 位的。
  533. if CurrBytesPerPixel = 1 then exit;
  534. {$IFDEF FMX}
  535. nBmpWidth := Data.Width;
  536. nBmpHeight := Data.Height;
  537. {$ELSE}
  538. nBmpWidth := Width;
  539. nBmpHeight := Height;
  540. {$ENDIF}
  541. // 像素扫描
  542. if CurrBytesPerPixel > 0 then
  543. begin
  544. wWidth := nBmpWidth * CurrBytesPerPixel;
  545. if (wWidth mod 4) > 0 then
  546. begin
  547. wWidth := wWidth + 4 - (wWidth mod 4);
  548. end;
  549. end
  550. else if (nBmpWidth mod 32) > 0 then
  551. begin
  552. wWidth := ((nBmpWidth div 32) + 1) * 4;;
  553. end
  554. else
  555. begin
  556. wWidth := (nBmpWidth div 8);
  557. end;
  558. bufferSize := nBmpHeight * wWidth;
  559. // bmp文件头
  560. BMF.bfType := $4D42;
  561. BMF.bfSize := 14 + 40 + bufferSize;
  562. BMF.bfReserved1 := 0;
  563. BMF.bfReserved2 := 0;
  564. BMF.bfOffBits := 14 + 40;
  565. if (CurrBytesPerPixel = 0) then
  566. begin
  567. BMF.bfOffBits := BMF.bfOffBits + 2 * Sizeof(RGBQUAD);
  568. BMF.bfSize := BMF.bfSize + 2 * Sizeof(RGBQUAD);
  569. end;
  570. if (CurrBytesPerPixel = 1) then
  571. begin
  572. BMF.bfOffBits := BMF.bfOffBits + 256 * Sizeof(RGBQUAD);
  573. BMF.bfSize := BMF.bfSize + 256 * Sizeof(RGBQUAD);
  574. end;
  575. if (CurrBytesPerPixel = 2) then
  576. begin
  577. //多谢 [西安]老一门(yyimen@foxmail.com) 提供的 565 格式说明。
  578. BMF.bfOffBits := BMF.bfOffBits + RGB565ExtDataLen;
  579. BMF.bfSize := BMF.bfSize + RGB565ExtDataLen;
  580. end;
  581. // FileSizeFix := 0;
  582. // if (BMF.bfSize mod 4) > 0 then
  583. // begin
  584. // FileSizeFix := 4 - BMF.bfSize mod 4;
  585. // end;
  586. // bufferSize := bufferSize + FileSizeFix;
  587. // BMF.bfSize := BMF.bfSize + FileSizeFix;
  588. // 保存bmp文件头
  589. AStream.WriteBuffer(BMF, Sizeof(BMF));
  590. // bmp信息头
  591. FillChar(BitmapInfo, Sizeof(BitmapInfo), 0);
  592. BitmapInfo.biSize := 40;
  593. // if (CurrBytesPerPixel = 2) then
  594. // begin
  595. // //AcdSee 不支持这种大小的 BitmapInfo
  596. // BitmapInfo.biSize := 40 + RGB565ExtDataLen;
  597. // end;
  598. BitmapInfo.biWidth := nBmpWidth;
  599. BitmapInfo.biHeight := nBmpHeight;
  600. BitmapInfo.biPlanes := 1;
  601. if CurrBytesPerPixel > 0 then
  602. begin
  603. BitmapInfo.biBitCount := CurrBytesPerPixel * 8;
  604. end
  605. else
  606. begin
  607. BitmapInfo.biBitCount := 1;
  608. end;
  609. BitmapInfo.biSizeImage := bufferSize;
  610. // if True then
  611. // begin
  612. // //96
  613. // BitmapInfo.biXPelsPerMeter := $0EC4;
  614. // BitmapInfo.biYPelsPerMeter := $0EC4;
  615. // end
  616. // else
  617. // begin
  618. // //72
  619. // BitmapInfo.biXPelsPerMeter := $0B12;
  620. // BitmapInfo.biYPelsPerMeter := $0B12;
  621. // end;
  622. BitmapInfo.biXPelsPerMeter := 0;
  623. BitmapInfo.biYPelsPerMeter := 0;
  624. if (CurrBytesPerPixel = 2) then
  625. begin
  626. //可以采用 RGB555 代替 RGB565
  627. BitmapInfo.biCompression := BI_BITFIELDS; //0是 555 3 是 565
  628. end;
  629. // 保存bmp信息头
  630. AStream.WriteBuffer(BitmapInfo, Sizeof(BitmapInfo));
  631. if (CurrBytesPerPixel = 2) then
  632. begin
  633. // 保存 565 RGB Mask
  634. A32BitData := RGB565_MASK_RED;
  635. AStream.WriteBuffer(A32BitData, Sizeof(A32BitData));
  636. A32BitData := RGB565_MASK_GREEN;
  637. AStream.WriteBuffer(A32BitData, Sizeof(A32BitData));
  638. A32BitData := RGB565_MASK_BLUE;
  639. AStream.WriteBuffer(A32BitData, Sizeof(A32BitData));
  640. if RGB565ExtDataLen >= 16 then
  641. begin
  642. A32BitData := 0;
  643. AStream.WriteBuffer(A32BitData, Sizeof(A32BitData));
  644. end;
  645. end;
  646. if (CurrBytesPerPixel = 0) or (CurrBytesPerPixel = 1) then
  647. begin
  648. //颜色表
  649. RGBQUAD.rgbBlue := 0;
  650. RGBQUAD.rgbGreen := 0;
  651. RGBQUAD.rgbRed := 0;
  652. RGBQUAD.rgbReserved := 0;
  653. if (CurrBytesPerPixel = 1) then
  654. begin
  655. for I := 0 to 255 do
  656. begin
  657. AStream.WriteBuffer(RGBQUAD, Sizeof(RGBQUAD));
  658. end;
  659. BitmapInfo.biClrUsed := $FF;
  660. end
  661. else
  662. begin
  663. AStream.WriteBuffer(RGBQUAD, Sizeof(RGBQUAD));
  664. RGBQUAD.rgbBlue := $FF;
  665. RGBQUAD.rgbGreen := $FF;
  666. RGBQUAD.rgbRed := $FF;
  667. RGBQUAD.rgbReserved := 0;
  668. AStream.WriteBuffer(RGBQUAD, Sizeof(RGBQUAD));
  669. BitmapInfo.biClrUsed := 2;
  670. end;
  671. end;
  672. // 像素扫描
  673. SetLength(bmpData, wWidth);
  674. {$IFDEF FMX}
  675. AlphaColorBuffer := GetMemory(nBmpWidth * SizeOf(TAlphaColor));
  676. try
  677. {$ENDIF}
  678. for nCol := nBmpHeight - 1 downto 0 do
  679. begin
  680. FillChar(bmpData[0], wWidth, 0);
  681. wByteIdex := 0;
  682. //0 是单色图
  683. if (CurrBytesPerPixel = 0) or (CurrBytesPerPixel = 1) then
  684. begin
  685. A8BitData := 0;
  686. ABitIndex := 0;
  687. {$IFDEF FMX}
  688. {$ELSE}
  689. Pixs := ScanLine[nCol];
  690. {$ENDIF}
  691. for wRow := 0 to nBmpWidth - 1 do
  692. begin
  693. {$IFDEF FMX}
  694. //X 是行坐标,Y 是 列坐标。
  695. clr := Data.GetPixel(wRow, nCol);
  696. {$ELSE}
  697. {$ENDIF}
  698. GrayeData := 0;
  699. {$IFDEF FMX}
  700. if MonochromeChangeType = TMonochromeChangeType.Average then
  701. begin
  702. GrayeData := TAlphaColorRec(clr).R + TAlphaColorRec(clr).G + TAlphaColorRec(clr).B;
  703. GrayeData := GrayeData div 3;
  704. end;
  705. if MonochromeChangeType = TMonochromeChangeType.Weighting then
  706. begin
  707. GrayeData := Round((TAlphaColorRec(clr).R * MonochromeChange_Weighting_Red +
  708. TAlphaColorRec(clr).G * MonochromeChange_Weighting_Green +
  709. TAlphaColorRec(clr).B * MonochromeChange_Weighting_Blue) / 3);
  710. end;
  711. if MonochromeChangeType = TMonochromeChangeType.Max then
  712. begin
  713. GrayeData := System.Math.Max(System.Math.Max(TAlphaColorRec(clr).R, TAlphaColorRec(clr).G),
  714. TAlphaColorRec(clr).B);
  715. end;
  716. {$ELSE}
  717. if MonochromeChangeType = TMonochromeChangeType.Average then
  718. begin
  719. GrayeData := Pixs[wRow].rgbtRed + Pixs[wRow].rgbtGreen + Pixs[wRow].rgbtBlue;
  720. GrayeData := GrayeData div 3;
  721. end;
  722. if MonochromeChangeType = TMonochromeChangeType.Weighting then
  723. begin
  724. GrayeData := Round((Pixs[wRow].rgbtRed * MonochromeChange_Weighting_Red +
  725. Pixs[wRow].rgbtGreen * MonochromeChange_Weighting_Green +
  726. Pixs[wRow].rgbtBlue * MonochromeChange_Weighting_Blue) / 3);
  727. end;
  728. if MonochromeChangeType = TMonochromeChangeType.Max then
  729. begin
  730. GrayeData := System.Math.Max(System.Math.Max(Pixs[wRow].rgbtRed, Pixs[wRow].rgbtGreen),
  731. Pixs[wRow].rgbtBlue);
  732. end;
  733. {$ENDIF}
  734. if GrayeData > MonochromeChange_Threshold then
  735. begin
  736. A8BitData := A8BitData or ($80 shr ABitIndex);
  737. end;
  738. inc(ABitIndex);
  739. if ABitIndex > 7 then
  740. begin
  741. ABitIndex := 0;
  742. if (CurrBytesPerPixel = 0) then
  743. begin
  744. bmpData[wByteIdex] := A8BitData;
  745. A8BitData := 0;
  746. inc(wByteIdex);
  747. end;
  748. end;
  749. end;
  750. if (CurrBytesPerPixel = 0) then
  751. begin
  752. if ABitIndex > 0 then
  753. begin
  754. bmpData[wByteIdex] := A8BitData;
  755. A8BitData := 0;
  756. end;
  757. end;
  758. end
  759. else
  760. begin
  761. {$IFDEF FMX}
  762. // for wRow := 0 to nBmpWidth - 1 do
  763. // begin
  764. // //X 是行坐标,Y 是 列坐标。
  765. // clr := Data.GetPixel(wRow, nCol);
  766. // case CurrBytesPerPixel of
  767. // 1:
  768. // begin
  769. // //不支持。
  770. // end;
  771. // 2:
  772. // begin
  773. // A16BitData := rgb_24_2_565(TAlphaColorRec(clr).R, TAlphaColorRec(clr).G, TAlphaColorRec(clr).B);
  774. // bmpData[wByteIdex + 0] := WordRec(A16BitData).Lo;
  775. // bmpData[wByteIdex + 1] := WordRec(A16BitData).Hi;
  776. // end;
  777. // 3,4:
  778. // begin
  779. // bmpData[wByteIdex + 0] := TAlphaColorRec(clr).B;
  780. // bmpData[wByteIdex + 1] := TAlphaColorRec(clr).G;
  781. // bmpData[wByteIdex + 2] := TAlphaColorRec(clr).R;
  782. // end;
  783. // end;
  784. // if CurrBytesPerPixel = 4 then
  785. // begin
  786. // bmpData[wByteIdex + 3] := TAlphaColorRec(clr).A;
  787. // end;
  788. // Inc(wByteIdex, CurrBytesPerPixel);
  789. // end;
  790. case CurrBytesPerPixel of
  791. 1:
  792. begin
  793. //不支持。
  794. end;
  795. 2:
  796. begin
  797. ScanlineToAlphaColor(Data.GetScanline(nCol), AlphaColorBuffer, nBmpWidth, Data.PixelFormat);
  798. AlphaColorToScanline(AlphaColorBuffer, Addr(bmpData[0]), nBmpWidth, TPixelFormat.BGR_565);
  799. end;
  800. 3:
  801. begin
  802. ScanlineToAlphaColor(Data.GetScanline(nCol), AlphaColorBuffer, nBmpWidth, Data.PixelFormat);
  803. AlphaColorToScanline(AlphaColorBuffer, Addr(bmpData[0]), nBmpWidth, TPixelFormat.BGR);
  804. end;
  805. 4:
  806. begin
  807. if Data.PixelFormat = TPixelFormat.BGRA then
  808. begin
  809. Move(PByte(Data.GetScanline(nCol))[0], bmpData[0], Data.BytesPerPixel * nBmpWidth);
  810. end
  811. else
  812. begin
  813. ScanlineToAlphaColor(Data.GetScanline(nCol), AlphaColorBuffer, nBmpWidth, Data.PixelFormat);
  814. AlphaColorToScanline(AlphaColorBuffer, Addr(bmpData[0]), nBmpWidth, TPixelFormat.BGRA);
  815. end;
  816. end;
  817. end;
  818. {$ELSE}
  819. Pixs := ScanLine[nCol];
  820. for wRow := 0 to nBmpWidth - 1 do
  821. begin
  822. case CurrBytesPerPixel of
  823. 1:
  824. begin
  825. //不支持。
  826. end;
  827. 2:
  828. begin
  829. A16BitData := rgb_24_2_565(Pixs[wRow].rgbtRed, Pixs[wRow].rgbtGreen, Pixs[wRow].rgbtBlue);
  830. bmpData[wByteIdex + 0] := WordRec(A16BitData).Lo;
  831. bmpData[wByteIdex + 1] := WordRec(A16BitData).Hi;
  832. end;
  833. 3,4:
  834. begin
  835. bmpData[wByteIdex + 0] := Pixs[wRow].rgbtBlue;
  836. bmpData[wByteIdex + 1] := Pixs[wRow].rgbtGreen;
  837. bmpData[wByteIdex + 2] := Pixs[wRow].rgbtRed;
  838. end;
  839. end;
  840. if CurrBytesPerPixel = 4 then
  841. begin
  842. bmpData[wByteIdex + 3] := $FF;
  843. end;
  844. Inc(wByteIdex, CurrBytesPerPixel);
  845. end;
  846. {$ENDIF}
  847. end;
  848. AStream.WriteBuffer(bmpData, wWidth);
  849. end;
  850. {$IFDEF FMX}
  851. finally
  852. FreeMemory(AlphaColorBuffer);
  853. end;
  854. {$ENDIF}
  855. // A8BitData := 0;
  856. // for I := 0 to FileSizeFix - 1 do
  857. // begin
  858. // AStream.WriteBuffer(A8BitData, 1);
  859. // end;
  860. Result := True;
  861. finally
  862. {$IFDEF FMX}
  863. Unmap(Data);
  864. {$ELSE}
  865. {$ENDIF}
  866. end;
  867. end;
  868. procedure TKngStrBitmapHelper.SaveAsBMPToStreamDef(Stream: TStream);
  869. begin
  870. if not SaveAsBMPToStream(Stream) then
  871. begin
  872. {$IFDEF FMX}
  873. raise EBitmapSavingFailed.Create(SBitmapSavingFailed);
  874. {$ELSE}
  875. raise EInvalidGraphicOperation.Create(SBitmapSavingFailed);
  876. {$ENDIF}
  877. end;
  878. end;
  879. procedure TKngStrBitmapHelper.SaveToStream(Stream: TStream;
  880. const Extension: string; SaveParams: PBitmapCodecSaveParams);
  881. var
  882. Surf: TBitmapSurface;
  883. begin
  884. TMonitor.Enter(Self);
  885. try
  886. Surf := TBitmapSurface.Create;
  887. try
  888. Surf.Assign(Self);
  889. if not TBitmapCodecManager.SaveToStream(Stream, Surf, Extension, SaveParams) then
  890. raise EBitmapSavingFailed.Create(SBitmapSavingFailed);
  891. finally
  892. Surf.Free;
  893. end;
  894. finally
  895. TMonitor.Exit(Self);
  896. end;
  897. end;
  898. {$IFDEF FMX}
  899. procedure TKngStrBitmapHelper.SyncAssign(Source: TPersistent);
  900. begin
  901. TThread.Synchronize(nil,
  902. procedure
  903. begin
  904. Assign(Source);
  905. end)
  906. end;
  907. procedure TKngStrBitmapHelper.SyncLoadFromFile(const AFileName: string);
  908. var
  909. Surf: TBitmapSurface;
  910. begin
  911. Surf := TBitmapSurface.Create;
  912. try
  913. if TBitmapCodecManager.LoadFromFile(AFileName, Surf, CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then
  914. TThread.Synchronize(nil,
  915. procedure
  916. begin
  917. Assign(Surf);
  918. end)
  919. else
  920. raise EBitmapLoadingFailed.CreateFMT(SBitmapLoadingFailedNamed, [AFileName]);
  921. finally
  922. Surf.Free;
  923. end;
  924. end;
  925. procedure TKngStrBitmapHelper.SyncLoadFromStream(Stream: TStream);
  926. var
  927. S: TStream;
  928. Surf: TBitmapSurface;
  929. begin
  930. if Stream.Position > 0 then
  931. begin
  932. // need to create temp stream
  933. S := TMemoryStream.Create;
  934. try
  935. S.CopyFrom(Stream, Stream.Size - Stream.Position);
  936. S.Position := 0;
  937. Surf := TBitmapSurface.Create;
  938. try
  939. if TBitmapCodecManager.LoadFromStream(S, Surf, CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then
  940. TThread.Synchronize(nil,
  941. procedure
  942. begin
  943. Assign(Surf);
  944. end)
  945. else
  946. raise EBitmapLoadingFailed.Create(SBitmapLoadingFailed);
  947. finally
  948. Surf.Free;
  949. end;
  950. finally
  951. S.Free;
  952. end;
  953. end
  954. else
  955. if Stream.Size = 0 then
  956. Clear(0)
  957. else begin
  958. Surf := TBitmapSurface.Create;
  959. try
  960. if TBitmapCodecManager.LoadFromStream(Stream, Surf, CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then
  961. TThread.Synchronize(nil,
  962. procedure
  963. begin
  964. Assign(Surf);
  965. end)
  966. else
  967. raise EBitmapLoadingFailed.Create(SBitmapLoadingFailed);
  968. finally
  969. Surf.Free;
  970. end;
  971. end;
  972. end;
  973. procedure TKngStrBitmapHelper.SyncLoadThumbnailFromFile(const AFileName: string; const AFitWidth, AFitHeight: Single;
  974. const UseEmbedded: Boolean = True);
  975. var
  976. Surf: TBitmapSurface;
  977. begin
  978. Surf := TBitmapSurface.Create;
  979. try
  980. if TBitmapCodecManager.LoadThumbnailFromFile(AFileName, AFitWidth, AFitHeight, UseEmbedded, Surf) then
  981. TThread.Synchronize(nil,
  982. procedure
  983. begin
  984. Assign(Surf);
  985. end)
  986. else
  987. raise EThumbnailLoadingFailed.CreateFMT(SThumbnailLoadingFailedNamed, [AFileName]);
  988. finally
  989. Surf.Free;
  990. end;
  991. end;
  992. function TKngStrBitmapHelper.SyncLoadThumbnailFromStream(const AStream: TStream;
  993. const ASrc, ADest: TRectF; const UseEmbedded: Boolean): Boolean;
  994. var
  995. S: TStream;
  996. Surf: TBitmapSurface;
  997. begin
  998. if AStream.Position > 0 then
  999. begin
  1000. // need to create temp stream
  1001. S := TMemoryStream.Create;
  1002. try
  1003. S.CopyFrom(AStream, AStream.Size - AStream.Position);
  1004. S.Position := 0;
  1005. Surf := TBitmapSurface.Create;
  1006. try
  1007. if TBitmapCodecManager.LoadThumbnailFromStream(S, ASrc, ADest, UseEmbedded, Surf) then
  1008. TThread.Synchronize(nil,
  1009. procedure
  1010. begin
  1011. Assign(Surf);
  1012. end)
  1013. else
  1014. raise EBitmapLoadingFailed.Create(SThumbnailLoadingFailed);
  1015. finally
  1016. Surf.Free;
  1017. end;
  1018. finally
  1019. S.Free;
  1020. end;
  1021. end
  1022. else
  1023. if AStream.Size = 0 then
  1024. Clear(0)
  1025. else begin
  1026. Surf := TBitmapSurface.Create;
  1027. try
  1028. if TBitmapCodecManager.LoadThumbnailFromStream(AStream, ASrc, ADest, UseEmbedded, Surf) then
  1029. TThread.Synchronize(nil,
  1030. procedure
  1031. begin
  1032. Assign(Surf);
  1033. end)
  1034. else
  1035. raise EBitmapLoadingFailed.Create(SThumbnailLoadingFailed);
  1036. finally
  1037. Surf.Free;
  1038. end;
  1039. end;
  1040. end;
  1041. procedure TKngStrBitmapHelper.SyncLoadThumbnailFromStream(const Stream: TStream;
  1042. const AFitWidth, AFitHeight: Single; const UseEmbedded: Boolean; const AutoCut: Boolean);
  1043. var
  1044. S: TStream;
  1045. Surf: TBitmapSurface;
  1046. begin
  1047. if Stream.Position > 0 then
  1048. begin
  1049. // need to create temp stream
  1050. S := TMemoryStream.Create;
  1051. try
  1052. S.CopyFrom(Stream, Stream.Size - Stream.Position);
  1053. S.Position := 0;
  1054. Surf := TBitmapSurface.Create;
  1055. try
  1056. if TBitmapCodecManager.LoadThumbnailFromStream(S, AFitWidth, AFitHeight, UseEmbedded, AutoCut, Surf) then
  1057. TThread.Synchronize(nil,
  1058. procedure
  1059. begin
  1060. Assign(Surf);
  1061. end)
  1062. else
  1063. raise EBitmapLoadingFailed.Create(SThumbnailLoadingFailed);
  1064. finally
  1065. Surf.Free;
  1066. end;
  1067. finally
  1068. S.Free;
  1069. end;
  1070. end
  1071. else
  1072. if Stream.Size = 0 then
  1073. Clear(0)
  1074. else begin
  1075. Surf := TBitmapSurface.Create;
  1076. try
  1077. if TBitmapCodecManager.LoadThumbnailFromStream(Stream, AFitWidth, AFitHeight, UseEmbedded, AutoCut, Surf) then
  1078. TThread.Synchronize(nil,
  1079. procedure
  1080. begin
  1081. Assign(Surf);
  1082. end)
  1083. else
  1084. raise EBitmapLoadingFailed.Create(SThumbnailLoadingFailed);
  1085. finally
  1086. Surf.Free;
  1087. end;
  1088. end;
  1089. end;
  1090. { TKngStrBitmapCodecManager }
  1091. class function TKngStrBitmapCodecManager.GetImageSize(const AStream: TStream): TPointF;
  1092. var
  1093. CodecClass: TCustomBitmapCodecClass;
  1094. DataType: String;
  1095. begin
  1096. DataType := TImageTypeChecker.GetType(AStream);
  1097. CodecClass := GuessCodecClass(DataType, TBitmapCodecDescriptorField.Extension);
  1098. if CodecClass <> nil then
  1099. {$IFDEF ANDROID}
  1100. Result := TBitmapCodecAndroid(CodecClass).GetImageSize(AStream)
  1101. {$ENDIF}
  1102. {$IFDEF IOS}
  1103. Result := TBitmapCodecIOS(CodecClass).GetImageSize(AStream)
  1104. {$ENDIF}
  1105. {$IFDEF MSWINDOWS}
  1106. Result := TBitmapCodecWIC(CodecClass).GetImageSize(AStream)
  1107. {$ENDIF}
  1108. {$IFDEF MACOS}
  1109. Result := TBitmapCodecMacOS(CodecClass).GetImageSize(AStream)
  1110. {$ENDIF}
  1111. else
  1112. Result := TPointF.Zero;
  1113. end;
  1114. class function TKngStrBitmapCodecManager.GetImageSize(const AFileName: string): TPointF;
  1115. begin
  1116. Result := inherited GetImageSize(AFileName);
  1117. end;
  1118. class function TKngStrBitmapCodecManager.GuessCodecClass(const Name: string;
  1119. const Field: TBitmapCodecDescriptorField): TCustomBitmapCodecClass;
  1120. type
  1121. TPrivateMethodType = function (const Name: string; const Field: TBitmapCodecDescriptorField):
  1122. TCustomBitmapCodecClass of object;
  1123. var
  1124. AMethod: TMethod;
  1125. AInvoke: TPrivateMethodType absolute AMethod;
  1126. begin
  1127. AMethod.Code := @TBitmapCodecManager.GuessCodecClass;
  1128. AMethod.Data := Self;
  1129. Result := AInvoke(Name, Field);
  1130. end;
  1131. class function TKngStrBitmapCodecManager.LoadFromStream(const AStream: TStream;
  1132. const Bitmap: TBitmapSurface; const MaxSizeLimit: Cardinal): Boolean;
  1133. var
  1134. CodecClass: TCustomBitmapCodecClass;
  1135. Codec: TCustomBitmapCodec;
  1136. DataType: String;
  1137. begin
  1138. Result := False;
  1139. DataType := TImageTypeChecker.GetType(AStream);
  1140. CodecClass := GuessCodecClass(DataType, TBitmapCodecDescriptorField.Extension);
  1141. if CodecClass <> nil then
  1142. begin
  1143. Codec := CodecClass.Create;
  1144. try
  1145. {$IFDEF ANDROID}
  1146. Result := TBitmapCodecAndroid(Codec).LoadFromStream(AStream, Bitmap, MaxSizeLimit);
  1147. {$ENDIF}
  1148. {$IFDEF IOS}
  1149. Result := TBitmapCodecIOS(Codec).LoadFromStream(AStream, Bitmap, MaxSizeLimit);
  1150. {$ENDIF}
  1151. {$IFDEF MSWINDOWS}
  1152. Result := TBitmapCodecWIC(Codec).LoadFromStream(AStream, Bitmap, MaxSizeLimit);
  1153. {$ENDIF}
  1154. {$IFDEF MACOS}
  1155. Result := TBitmapCodecMacOS(Codec).LoadFromStream(AStream, Bitmap, MaxSizeLimit);
  1156. {$ENDIF}
  1157. finally
  1158. Codec.Free;
  1159. end;
  1160. end
  1161. end;
  1162. class function TKngStrBitmapCodecManager.LoadThumbnailFromStream(
  1163. const AStream: TStream; const ASrc, ADest: TRectF; const UseEmbedded: Boolean;
  1164. const Bitmap: TBitmapSurface): Boolean;
  1165. var
  1166. CodecClass: TCustomBitmapCodecClass;
  1167. Codec: TCustomBitmapCodec;
  1168. DataType: String;
  1169. begin
  1170. Result := False;
  1171. DataType := TImageTypeChecker.GetType(AStream);
  1172. CodecClass := GuessCodecClass(DataType, TBitmapCodecDescriptorField.Extension);
  1173. if CodecClass <> nil then
  1174. begin
  1175. Codec := CodecClass.Create;
  1176. try
  1177. {$IFDEF ANDROID}
  1178. Result := TBitmapCodecAndroid(Codec).LoadThumbnailFromStream(AStream, ASrc, ADest, UseEmbedded, Bitmap);
  1179. {$ENDIF}
  1180. {$IFDEF IOS}
  1181. Result := TBitmapCodecIOS(Codec).LoadThumbnailFromStream(AStream, ASrc, ADest, UseEmbedded, Bitmap);
  1182. {$ENDIF}
  1183. {$IFDEF MSWINDOWS}
  1184. Result := TBitmapCodecWIC(Codec).LoadThumbnailFromStream(AStream, ASrc, ADest, UseEmbedded, Bitmap);
  1185. {$ENDIF}
  1186. {$IFDEF MACOS}
  1187. Result := TBitmapCodecMacOS(Codec).LoadThumbnailFromStream(AStream, ASrc, ADest, UseEmbedded, Bitmap);
  1188. {$ENDIF}
  1189. finally
  1190. Codec.Free;
  1191. end;
  1192. end
  1193. end;
  1194. class function TKngStrBitmapCodecManager.Rotate(const AStream: TStream;
  1195. const Angle: Single): Boolean;
  1196. var
  1197. CodecClass: TCustomBitmapCodecClass;
  1198. Codec: TCustomBitmapCodec;
  1199. DataType: String;
  1200. begin
  1201. Result := False;
  1202. if (not Assigned(AStream)) or (AStream.Size - AStream.Position <= 0) then
  1203. Exit;
  1204. DataType := TImageTypeChecker.GetType(AStream);
  1205. CodecClass := GuessCodecClass(DataType, TBitmapCodecDescriptorField.Extension);
  1206. if CodecClass <> nil then
  1207. begin
  1208. Codec := CodecClass.Create;
  1209. try
  1210. {$IFDEF ANDROID}
  1211. Result := TBitmapCodecAndroid(Codec).Rotate(AStream, Angle);
  1212. {$ENDIF}
  1213. {$IFDEF IOS}
  1214. Result := TBitmapCodecIOS(Codec).Rotate(AStream, Angle);
  1215. {$ENDIF}
  1216. {$IFDEF MSWINDOWS}
  1217. Result := TBitmapCodecWIC(Codec).Rotate(AStream, Angle);
  1218. {$ENDIF}
  1219. {$IFDEF MACOS}
  1220. Result := TBitmapCodecMacOS(Codec).Rotate(AStream, Angle);
  1221. {$ENDIF}
  1222. finally
  1223. Codec.Free;
  1224. end;
  1225. end
  1226. end;
  1227. class function TKngStrBitmapCodecManager.Rotate(const Extension: string;
  1228. const Angle: Single; const Bitmap: TBitmapSurface): Boolean;
  1229. var
  1230. CodecClass: TCustomBitmapCodecClass;
  1231. Codec: TCustomBitmapCodec;
  1232. begin
  1233. Result := False;
  1234. if Extension = '' then
  1235. Exit;
  1236. CodecClass := GuessCodecClass(Extension, TBitmapCodecDescriptorField.Extension);
  1237. if CodecClass <> nil then
  1238. begin
  1239. Codec := CodecClass.Create;
  1240. try
  1241. {$IFDEF ANDROID}
  1242. Result := TBitmapCodecAndroid(Codec).Rotate(Extension, Angle, Bitmap);
  1243. {$ENDIF}
  1244. {$IFDEF IOS}
  1245. Result := TBitmapCodecIOS(Codec).Rotate(Extension, Angle, Bitmap);
  1246. {$ENDIF}
  1247. {$IFDEF MSWINDOWS}
  1248. Result := TBitmapCodecWIC(Codec).Rotate(Extension, Angle, Bitmap);
  1249. {$ENDIF}
  1250. {$IFDEF MACOS}
  1251. Result := TBitmapCodecMacOS(Codec).Rotate(Extension, Angle, Bitmap);
  1252. {$ENDIF}
  1253. finally
  1254. Codec.Free;
  1255. end;
  1256. end
  1257. end;
  1258. class function TKngStrBitmapCodecManager.LoadThumbnailFromStream(
  1259. const AStream: TStream; const AFitWidth, AFitHeight: Single;
  1260. const UseEmbedded: Boolean; const AutoCut: Boolean;
  1261. const Bitmap: TBitmapSurface): Boolean;
  1262. var
  1263. CodecClass: TCustomBitmapCodecClass;
  1264. Codec: TCustomBitmapCodec;
  1265. DataType: String;
  1266. begin
  1267. Result := False;
  1268. DataType := TImageTypeChecker.GetType(AStream);
  1269. CodecClass := GuessCodecClass(DataType, TBitmapCodecDescriptorField.Extension);
  1270. if CodecClass <> nil then
  1271. begin
  1272. Codec := CodecClass.Create;
  1273. try
  1274. {$IFDEF ANDROID}
  1275. Result := TBitmapCodecAndroid(Codec).LoadThumbnailFromStream(AStream, AFitWidth, AFitHeight, UseEmbedded, AutoCut, Bitmap);
  1276. {$ENDIF}
  1277. {$IFDEF IOS}
  1278. Result := TBitmapCodecIOS(Codec).LoadThumbnailFromStream(AStream, AFitWidth, AFitHeight, UseEmbedded, Bitmap);
  1279. {$ENDIF}
  1280. {$IFDEF MSWINDOWS}
  1281. Result := TBitmapCodecWIC(Codec).LoadThumbnailFromStream(AStream, AFitWidth, AFitHeight, UseEmbedded, AutoCut, Bitmap);
  1282. {$ENDIF}
  1283. {$IFDEF MACOS}
  1284. Result := TBitmapCodecMacOS(Codec).LoadThumbnailFromStream(AStream, AFitWidth, AFitHeight, UseEmbedded, Bitmap);
  1285. {$ENDIF}
  1286. finally
  1287. Codec.Free;
  1288. end;
  1289. end
  1290. end;
  1291. {$IFDEF ANDROID}
  1292. { TBitmapCodecAndroid }
  1293. class function TBitmapCodecAndroid.GetMovieSize(const Stream: TStream): TPoint;
  1294. type
  1295. TPrivateMethodType = function (const Stream: TStream): TPoint of object;
  1296. var
  1297. AMethod: TMethod;
  1298. AInvoke: TPrivateMethodType absolute AMethod;
  1299. begin
  1300. AMethod.Code := @TBitmapCodecAndroid.GetMovieSize;
  1301. AMethod.Data := Self;
  1302. Result := AInvoke(Stream);
  1303. end;
  1304. class function TBitmapCodecAndroid.GetImageSize(const AStream: TStream): TPointF;
  1305. var
  1306. TempStream: TMemoryStream;
  1307. TempArray: TJavaArray<Byte>;
  1308. NativeBitmap: JBitmap;
  1309. LoadOptions: JBitmapFactory_Options;
  1310. SavePosition: Int64;
  1311. begin
  1312. if IsGIFStream(AStream) then
  1313. Result := GetMovieSize(AStream)
  1314. else begin
  1315. SavePosition := AStream.Position;
  1316. try
  1317. TempStream := TMemoryStream.Create;
  1318. try
  1319. TempStream.CopyFrom(AStream, AStream.Size);
  1320. TempArray := TJavaArray<Byte>.Create(TempStream.Size);
  1321. Move(TempStream.Memory^, TempArray.Data^, TempStream.Size);
  1322. finally
  1323. TempStream.Free;
  1324. end;
  1325. LoadOptions := TJBitmapFactory_Options.JavaClass.init;
  1326. LoadOptions.inJustDecodeBounds := True;
  1327. TJBitmapFactory.JavaClass.decodeByteArray(TempArray, 0, TempArray.Length, LoadOptions);
  1328. TempArray := nil;
  1329. Result := TPointF.Create(LoadOptions.outWidth, LoadOptions.outHeight);
  1330. finally
  1331. AStream.Position := SavePosition;
  1332. end;
  1333. end;
  1334. end;
  1335. class function TBitmapCodecAndroid.GetImageSize(const AFileName: string): TPointF;
  1336. begin
  1337. Result := inherited GetImageSize(AFileName);
  1338. end;
  1339. class function TBitmapCodecAndroid.IsGIFStream(const Stream: TStream): Boolean;
  1340. begin
  1341. Result := TImageTypeChecker.GetType(Stream) = SGIFImageExtension;
  1342. end;
  1343. function TBitmapCodecAndroid.LoadMovieFromStream(const Stream: TStream; const Surface: TBitmapSurface): Boolean;
  1344. var
  1345. PrevPosition: Int64;
  1346. TempArray: TJavaArray<Byte>;
  1347. Movie: JMovie;
  1348. Bitmap: JBitmap;
  1349. Canvas: JCanvas;
  1350. begin
  1351. PrevPosition := Stream.Position;
  1352. try
  1353. TempArray := TJavaArray<Byte>.Create(Stream.Size - Stream.Position);
  1354. Stream.ReadBuffer(TempArray.Data^, TempArray.Length);
  1355. finally
  1356. Stream.Position := PrevPosition;
  1357. end;
  1358. Movie := TJMovie.JavaClass.decodeByteArray(TempArray, 0, TempArray.Length);
  1359. TempArray := nil;
  1360. Bitmap := TJBitmap.JavaClass.createBitmap(Movie.width, Movie.height, TJBitmap_Config.JavaClass.ARGB_8888);
  1361. //kngstr
  1362. if Bitmap = nil then
  1363. Exit(False);
  1364. try
  1365. Canvas := TJCanvas.JavaClass.init(Bitmap);
  1366. try
  1367. Movie.setTime(0);
  1368. Movie.draw(Canvas, 0, 0);
  1369. finally
  1370. Canvas := nil;
  1371. end;
  1372. Result := JBitmapToSurface(Bitmap, Surface);
  1373. finally
  1374. Bitmap.recycle;
  1375. end;
  1376. end;
  1377. function TBitmapCodecAndroid.StretchIfNeed(const SrcBitmap: JBitmap; const Bitmap: TBitmapSurface;
  1378. const LoadOptions: JBitmapFactory_Options; const MaxSizeLimit: Cardinal): Boolean;
  1379. var
  1380. R: TRectF;
  1381. ScaledBitmap: JBitmap;
  1382. begin
  1383. if (MaxSizeLimit > 0) and ((LoadOptions.outWidth > Integer(MaxSizeLimit)) or
  1384. (LoadOptions.outHeight > Integer(MaxSizeLimit))) then
  1385. begin
  1386. R := TRectF.Create(0, 0, LoadOptions.outWidth, LoadOptions.outHeight);
  1387. R.Fit(TRectF.Create(0, 0, MaxSizeLimit, MaxSizeLimit));
  1388. ScaledBitmap := TJBitmap.JavaClass.createScaledBitmap(SrcBitmap, R.Truncate.Width, R.Truncate.Height, True);
  1389. //kngstr
  1390. if ScaledBitmap = nil then
  1391. Exit(False);
  1392. try
  1393. Result := JBitmapToSurface(ScaledBitmap, Bitmap);
  1394. finally
  1395. ScaledBitmap.recycle;
  1396. end;
  1397. end
  1398. else
  1399. Result := JBitmapToSurface(SrcBitmap, Bitmap);
  1400. end;
  1401. function TBitmapCodecAndroid.LoadFromStream(const AStream: TStream;
  1402. const Bitmap: TBitmapSurface; const MaxSizeLimit: Cardinal): Boolean;
  1403. var
  1404. TempStream: TMemoryStream;
  1405. TempArray: TJavaArray<Byte>;
  1406. NativeBitmap: JBitmap;
  1407. LoadOptions: JBitmapFactory_Options;
  1408. SavePosition: Int64;
  1409. begin
  1410. if IsGIFStream(AStream) then
  1411. Result := LoadMovieFromStream(AStream, Bitmap)
  1412. else
  1413. begin
  1414. SavePosition := AStream.Position;
  1415. try
  1416. TempStream := TMemoryStream.Create;
  1417. try
  1418. TempStream.CopyFrom(AStream, AStream.Size);
  1419. TempArray := TJavaArray<Byte>.Create(TempStream.Size);
  1420. Move(TempStream.Memory^, TempArray.Data^, TempStream.Size);
  1421. finally
  1422. TempStream.Free;
  1423. end;
  1424. LoadOptions := TJBitmapFactory_Options.JavaClass.init;
  1425. NativeBitmap := TJBitmapFactory.JavaClass.decodeByteArray(TempArray, 0, TempArray.Length, LoadOptions);
  1426. TempArray := nil;
  1427. //kngstr
  1428. if NativeBitmap = nil then
  1429. Exit(False);
  1430. try
  1431. if (LoadOptions.outWidth < 1) or (LoadOptions.outHeight < 1) then
  1432. Exit(False);
  1433. Result := StretchIfNeed(NativeBitmap, Bitmap, LoadOptions, MaxSizeLimit);
  1434. finally
  1435. NativeBitmap.recycle;
  1436. end;
  1437. finally
  1438. AStream.Position := SavePosition;
  1439. end;
  1440. end;
  1441. end;
  1442. function TBitmapCodecAndroid.LoadMovieFromStreamScaled(const AStream: TStream; const Surface: TBitmapSurface;
  1443. const FitSize: TPoint): Boolean;
  1444. var
  1445. TempStream: TMemoryStream;
  1446. TempArray: TJavaArray<Byte>;
  1447. Movie: JMovie;
  1448. OrigBitmap, Bitmap: JBitmap;
  1449. Canvas: JCanvas;
  1450. OrigSize: TPoint;
  1451. LoadOptions: JBitmapFactory_Options;
  1452. SavePosition: Int64;
  1453. begin
  1454. SavePosition := AStream.Position;
  1455. try
  1456. TempStream := TMemoryStream.Create;
  1457. try
  1458. TempStream.CopyFrom(AStream, AStream.Size);
  1459. TempArray := TJavaArray<Byte>.Create(TempStream.Size);
  1460. Move(TempStream.Memory^, TempArray.Data^, TempStream.Size);
  1461. finally
  1462. TempStream.Free;
  1463. end;
  1464. Movie := TJMovie.JavaClass.decodeByteArray(TempArray, 0, TempArray.Length);
  1465. TempArray := nil;
  1466. OrigSize := TPoint.Create(Movie.width, Movie.height);
  1467. OrigBitmap := TJBitmap.JavaClass.createBitmap(OrigSize.X, OrigSize.Y, TJBitmap_Config.JavaClass.ARGB_8888);
  1468. if OrigBitmap = nil then //kngstr fixed
  1469. Exit(False);
  1470. try
  1471. Canvas := TJCanvas.JavaClass.init(OrigBitmap);
  1472. try
  1473. Movie.setTime(0);
  1474. Movie.draw(Canvas, 0, 0);
  1475. finally
  1476. Canvas := nil;
  1477. end;
  1478. Movie := nil;
  1479. Bitmap := TJBitmap.JavaClass.createBitmap(FitSize.X, FitSize.Y, TJBitmap_Config.JavaClass.ARGB_8888);
  1480. if Bitmap = nil then //kngstr fixed
  1481. Exit(False);
  1482. try
  1483. Canvas := TJCanvas.JavaClass.init(Bitmap);
  1484. try
  1485. Canvas.drawBitmap(OrigBitmap, TJRect.JavaClass.init(0, 0, OrigSize.X, OrigSize.Y), TJRect.JavaClass.init(0, 0,
  1486. FitSize.X, FitSize.y), nil);
  1487. finally
  1488. Canvas := nil;
  1489. end;
  1490. Result := JBitmapToSurface(Bitmap, Surface);
  1491. finally
  1492. Bitmap.recycle; //kngstr fixed
  1493. end;
  1494. finally
  1495. OrigBitmap.recycle;
  1496. end;
  1497. finally
  1498. AStream.Position := SavePosition;
  1499. end;
  1500. end;
  1501. //连续decode需要reset
  1502. //https://blog.csdn.net/boystray/article/details/77725648
  1503. //多图加载的优化
  1504. //https://blog.csdn.net/Android_app/article/details/45815093
  1505. //inSampleSize优化
  1506. //https://www.jianshu.com/p/f15cd2ed6ec0
  1507. procedure calculateInSampleSize(options: JBitmapFactory_Options; reqWidth, reqHeight: Integer);
  1508. var
  1509. width, height, suitedValue: Integer;
  1510. widthRatio, heightRatio: Integer;
  1511. begin
  1512. options.inSampleSize := 1;
  1513. width := options.outWidth;
  1514. height := options.outHeight;
  1515. if (height > reqHeight) or (width > reqWidth) then begin
  1516. //使用需要的宽高的最大值来计算比率
  1517. if reqHeight > reqWidth then
  1518. suitedValue := reqHeight
  1519. else
  1520. suitedValue := reqWidth;
  1521. heightRatio := height div suitedValue;
  1522. widthRatio := width div suitedValue;
  1523. if heightRatio > widthRatio then //用最大
  1524. options.inSampleSize := heightRatio
  1525. else
  1526. options.inSampleSize := widthRatio;
  1527. end;
  1528. end;
  1529. function TBitmapCodecAndroid.LoadThumbnailFromStream(const AStream: TStream;
  1530. const ASrc, ADest: TRectF; const UseEmbedded: Boolean;
  1531. const Bitmap: TBitmapSurface): Boolean;
  1532. var
  1533. TempStream: TMemoryStream;
  1534. TempArray: TJavaArray<Byte>;
  1535. NativeBitmap1, NativeBitmap2: JBitmap;
  1536. LoadOptions: JBitmapFactory_Options;
  1537. SavePosition: Int64;
  1538. begin
  1539. if IsGIFStream(AStream) then
  1540. Result := LoadMovieFromStreamScaled(AStream, Bitmap, TPoint.Create(Round(ADest.Width), Round(ADest.Height)))
  1541. else
  1542. begin
  1543. SavePosition := AStream.Position;
  1544. try
  1545. TempStream := TMemoryStream.Create;
  1546. try
  1547. TempStream.CopyFrom(AStream, AStream.Size);
  1548. TempArray := TJavaArray<Byte>.Create(TempStream.Size);
  1549. Move(TempStream.Memory^, TempArray.Data^, TempStream.Size);
  1550. finally
  1551. TempStream.Free;
  1552. end;
  1553. LoadOptions := TJBitmapFactory_Options.JavaClass.init;
  1554. LoadOptions.inJustDecodeBounds := False;
  1555. NativeBitmap1 := TJBitmapFactory.JavaClass.decodeByteArray(TempArray, 0, TempArray.Length, LoadOptions);
  1556. TempArray := nil;
  1557. //kngstr
  1558. if NativeBitmap1 = nil then
  1559. Exit(False);
  1560. try
  1561. if (LoadOptions.outWidth < 1) or (LoadOptions.outHeight < 1) then
  1562. Exit(False);
  1563. // 剪裁
  1564. if (not ASrc.IsEmpty) and (ASrc.Width < LoadOptions.outWidth) and (ASrc.Height < LoadOptions.outHeight) then begin
  1565. NativeBitmap2 := TJBitmap.JavaClass.createBitmap(NativeBitmap1, Round(ASrc.Left), Round(ASrc.Top), Round(ASrc.Width), Round(ASrc.Height));
  1566. //kngstr
  1567. if NativeBitmap2 = nil then
  1568. Exit(False);
  1569. NativeBitmap1.recycle;
  1570. NativeBitmap1 := nil;
  1571. NativeBitmap1 := NativeBitmap2;
  1572. end;
  1573. //缩放
  1574. NativeBitmap2 := TJBitmap.JavaClass.createScaledBitmap(NativeBitmap1, Round(ADest.Width), Round(ADest.Height), True);
  1575. //kngstr
  1576. if NativeBitmap2 = nil then
  1577. Exit(False);
  1578. try
  1579. Result := JBitmapToSurface(NativeBitmap2, Bitmap)
  1580. finally
  1581. NativeBitmap2.recycle;
  1582. end;
  1583. finally
  1584. NativeBitmap1.recycle;
  1585. end;
  1586. finally
  1587. AStream.Position := SavePosition;
  1588. end;
  1589. end;
  1590. end;
  1591. function TBitmapCodecAndroid.Rotate(const AStream: TStream;
  1592. const Angle: Single): Boolean;
  1593. var
  1594. NativeBitmap1, NativeBitmap2: JBitmap;
  1595. SaveFormat: JBitmap_CompressFormat;
  1596. Matrix: JMatrix;
  1597. TempStream: TMemoryStream;
  1598. TempArray: TJavaArray<Byte>;
  1599. LoadOptions: JBitmapFactory_Options;
  1600. SavePosition: Int64;
  1601. OutByteStream: JByteArrayOutputStream;
  1602. ContentBytes: TJavaArray<Byte>;
  1603. DataType: string;
  1604. begin
  1605. DataType := TImageTypeChecker.GetType(AStream);
  1606. if DataType = SGIFImageExtension then begin
  1607. Result := False;
  1608. Exit;
  1609. end;
  1610. SavePosition := AStream.Position;
  1611. try
  1612. TempStream := TMemoryStream.Create;
  1613. try
  1614. TempStream.CopyFrom(AStream, AStream.Size);
  1615. TempArray := TJavaArray<Byte>.Create(TempStream.Size);
  1616. Move(TempStream.Memory^, TempArray.Data^, TempStream.Size);
  1617. finally
  1618. TempStream.Free;
  1619. end;
  1620. LoadOptions := TJBitmapFactory_Options.JavaClass.init;
  1621. LoadOptions.inJustDecodeBounds := False;
  1622. NativeBitmap1 := TJBitmapFactory.JavaClass.decodeByteArray(TempArray, 0, TempArray.Length, LoadOptions);
  1623. TempArray := nil;
  1624. if NativeBitmap1 = nil then
  1625. Exit(False);
  1626. try
  1627. Matrix := TJMatrix.JavaClass.init;
  1628. Matrix.postRotate(Angle);
  1629. NativeBitmap2 := TJBitmap.JavaClass.createBitmap(NativeBitmap1, 0, 0, NativeBitmap1.getWidth, NativeBitmap1.getHeight, Matrix, True);
  1630. finally
  1631. NativeBitmap1.recycle;
  1632. end;
  1633. if NativeBitmap2 = nil then
  1634. Exit(False);
  1635. try
  1636. if SameText(DataType, SPNGImageExtension) then
  1637. SaveFormat := TJBitmap_CompressFormat.JavaClass.PNG
  1638. else
  1639. SaveFormat := TJBitmap_CompressFormat.JavaClass.JPEG;
  1640. OutByteStream := TJByteArrayOutputStream.JavaClass.init(0);
  1641. Result := NativeBitmap2.compress(SaveFormat, 100, OutByteStream);
  1642. finally
  1643. NativeBitmap2.recycle;
  1644. end;
  1645. if Result and (OutByteStream.size > 0) then begin
  1646. ContentBytes := OutByteStream.toByteArray;
  1647. AStream.Size := 0;
  1648. AStream.WriteBuffer(ContentBytes.Data^, OutByteStream.size);
  1649. end;
  1650. Result := Result and (OutByteStream.size > 0);
  1651. finally
  1652. AStream.Position := SavePosition;
  1653. end;
  1654. end;
  1655. function TBitmapCodecAndroid.Rotate(const Extension: string; const Angle: Single; const Bitmap: TBitmapSurface): Boolean;
  1656. var
  1657. NativeBitmap1, NativeBitmap2: JBitmap;
  1658. SaveFormat: JBitmap_CompressFormat;
  1659. Matrix: JMatrix;
  1660. SaveQuality: Integer;
  1661. begin
  1662. if Extension = SGIFImageExtension then begin
  1663. Result := False;
  1664. Exit;
  1665. end;
  1666. NativeBitmap1 := TJBitmap.JavaClass.createBitmap(Bitmap.Width, Bitmap.Height, TJBitmap_Config.JavaClass.ARGB_8888);
  1667. if NativeBitmap1 = nil then
  1668. Exit(False);
  1669. try
  1670. Result := SurfaceToJBitmap(Bitmap, NativeBitmap1);
  1671. if not Result then
  1672. Exit;
  1673. Matrix := TJMatrix.JavaClass.init;
  1674. Matrix.postRotate(Angle);
  1675. NativeBitmap2 := TJBitmap.JavaClass.createBitmap(NativeBitmap1, 0, 0, Bitmap.Width, Bitmap.Height, Matrix, True);
  1676. finally
  1677. NativeBitmap1.recycle;
  1678. end;
  1679. if NativeBitmap2 = nil then
  1680. Exit(False);
  1681. try
  1682. Result := JBitmapToSurface(NativeBitmap2, Bitmap);
  1683. finally
  1684. NativeBitmap2.recycle;
  1685. end;
  1686. end;
  1687. function TBitmapCodecAndroid.LoadThumbnailFromStream(const AStream: TStream;
  1688. const AFitWidth, AFitHeight: Single; const UseEmbedded: Boolean;
  1689. const AutoCut: Boolean; const Bitmap: TBitmapSurface): Boolean;
  1690. var
  1691. TempStream: TMemoryStream;
  1692. TempArray: TJavaArray<Byte>;
  1693. NativeBitmap1, NativeBitmap2, NativeBitmap3: JBitmap;
  1694. LoadOptions: JBitmapFactory_Options;
  1695. SavePosition: Int64;
  1696. X, Y, W, H: Integer;
  1697. begin
  1698. if IsGIFStream(AStream) then
  1699. Result := LoadMovieFromStreamScaled(AStream, Bitmap, TPoint.Create(Round(AFitWidth), Round(AFitHeight)))
  1700. else
  1701. begin
  1702. SavePosition := AStream.Position;
  1703. try
  1704. TempStream := TMemoryStream.Create;
  1705. try
  1706. TempStream.CopyFrom(AStream, AStream.Size);
  1707. TempArray := TJavaArray<Byte>.Create(TempStream.Size);
  1708. Move(TempStream.Memory^, TempArray.Data^, TempStream.Size);
  1709. finally
  1710. TempStream.Free;
  1711. end;
  1712. LoadOptions := TJBitmapFactory_Options.JavaClass.init;
  1713. //读取文件大小
  1714. LoadOptions.inJustDecodeBounds := True;
  1715. TJBitmapFactory.JavaClass.decodeByteArray(TempArray, 0, TempArray.Length, LoadOptions);
  1716. //计算缩略尺寸
  1717. calculateInSampleSize(LoadOptions, Round(AFitWidth), Round(AFitHeight));
  1718. LoadOptions.inJustDecodeBounds := False;
  1719. NativeBitmap1 := TJBitmapFactory.JavaClass.decodeByteArray(TempArray, 0, TempArray.Length, LoadOptions);
  1720. TempArray := nil;
  1721. //kngstr
  1722. if NativeBitmap1 = nil then
  1723. Exit(False);
  1724. try
  1725. if (LoadOptions.outWidth < 1) or (LoadOptions.outHeight < 1) then
  1726. Exit(False);
  1727. NativeBitmap2 := TJBitmap.JavaClass.createScaledBitmap(NativeBitmap1, Round(AFitWidth), Round(AFitHeight), True);
  1728. //kngstr
  1729. if NativeBitmap2 = nil then
  1730. Exit(False);
  1731. try
  1732. if not AutoCut then
  1733. Result := JBitmapToSurface(NativeBitmap2, Bitmap)
  1734. else begin //临时处理下,截取大图的中间部分,尤其是分辨率特别大的
  1735. X := 0;
  1736. Y := 0;
  1737. W := Round(AFitWidth);
  1738. H := Round(AFitHeight);
  1739. if W > H then begin
  1740. X := (W - H) div 2;
  1741. W := H;
  1742. end
  1743. else if W < H then begin
  1744. Y := (H - W) div 2;
  1745. H := W;
  1746. end;
  1747. NativeBitmap3 := TJBitmap.JavaClass.createBitmap(NativeBitmap2, X, Y, W, H);
  1748. //kngstr
  1749. if NativeBitmap3 = nil then
  1750. Exit(False);
  1751. try
  1752. Result := JBitmapToSurface(NativeBitmap3, Bitmap);
  1753. finally
  1754. NativeBitmap3.recycle;
  1755. end;
  1756. end;
  1757. finally
  1758. NativeBitmap2.recycle;
  1759. end;
  1760. finally
  1761. NativeBitmap1.recycle;
  1762. end;
  1763. finally
  1764. AStream.Position := SavePosition;
  1765. end;
  1766. end;
  1767. end;
  1768. {$ENDIF}
  1769. {$ENDIF}
  1770. {$IFDEF FMX}
  1771. { TBitmap16bitFiler }
  1772. class constructor TBitmap16bitFiler.Create;
  1773. var
  1774. i: integer;
  1775. begin
  1776. i := 0;
  1777. while i < $10000 do
  1778. begin
  1779. // 不用for因为优化会将循环变量的值保存在寄存器不更新到内存
  1780. Color[i] := PixelToAlphaColor(@i, TPixelFormat.BGR_565);
  1781. inc(i);
  1782. end;
  1783. end;
  1784. class procedure TBitmap16bitFiler.FillScanLine(D: TBitmapData;
  1785. ScanLine, Width: integer; data: Pointer);
  1786. var
  1787. SC: PAlphaColorArray;
  1788. DA: PWordArray;
  1789. I: Integer;
  1790. MinWidth: Integer;
  1791. begin
  1792. SC := D.GetScanline(ScanLine);
  1793. DA := data;
  1794. MinWidth := D.Width;
  1795. if (Width > 0) and (Width < MinWidth) then
  1796. MinWidth := Width;
  1797. for I := 0 to MinWidth - 1 do
  1798. begin
  1799. if D.PixelFormat = TPixelFormat.BGRA then
  1800. begin
  1801. SC[I] := Color[DA[I]];
  1802. end
  1803. else
  1804. begin
  1805. AlphaColorToPixel(Color[DA[I]], Addr(SC[I]), D.PixelFormat);
  1806. end;
  1807. end;
  1808. end;
  1809. function FillScanLineFormMemory(BitmapData: TBitmapData; ScanLineIndex, Width: integer;
  1810. InputData: Pointer; InputFormat: TPixelFormat): Boolean;
  1811. var
  1812. SC: PAlphaColorArray;
  1813. Buffer: PAlphaColor;
  1814. MinWidth: Integer;
  1815. begin
  1816. Result := False;
  1817. if ScanLineIndex < 0 then exit;
  1818. if ScanLineIndex >= BitmapData.Height then exit;
  1819. SC := BitmapData.GetScanline(ScanLineIndex);
  1820. MinWidth := BitmapData.Width;
  1821. if (Width > 0) and (Width < MinWidth) then
  1822. MinWidth := Width;
  1823. if InputFormat = BitmapData.PixelFormat then
  1824. begin
  1825. Move(PByte(InputData)[0], SC[0], BitmapData.BytesPerPixel * MinWidth);
  1826. end
  1827. else
  1828. begin
  1829. Buffer := GetMemory(MinWidth * SizeOf(TAlphaColor));
  1830. try
  1831. ScanlineToAlphaColor(InputData, Buffer, MinWidth, InputFormat);
  1832. AlphaColorToScanline(Buffer, SC, MinWidth, BitmapData.PixelFormat);
  1833. finally
  1834. FreeMemory(Buffer);
  1835. end;
  1836. end;
  1837. Result := True;
  1838. end;
  1839. {$ENDIF}
  1840. { TBitmapCodecWIC }
  1841. {$IFDEF MSWINDOWS}
  1842. function TBitmapCodecWIC.DecodeFrame(const Frame: IWICBitmapFrameDecode;
  1843. const Bitmap: TBitmapSurface; const MaxSizeLimit: Cardinal): Boolean;
  1844. type
  1845. TPrivateMethodType = function (const Frame: IWICBitmapFrameDecode;
  1846. const Bitmap: TBitmapSurface; const MaxSizeLimit: Cardinal): Boolean of object;
  1847. var
  1848. AMethod: TMethod;
  1849. AInvoke: TPrivateMethodType absolute AMethod;
  1850. begin
  1851. AMethod.Code := @TBitmapCodecWIC.DecodeFrame;
  1852. AMethod.Data := Self;
  1853. Result := AInvoke(Frame, Bitmap, MaxSizeLimit);
  1854. end;
  1855. class function TBitmapCodecWIC.GetImageSize(const AStream: TStream): TPointF;
  1856. var
  1857. Decoder: IWICBitmapDecoder;
  1858. Frame: IWICBitmapFrameDecode;
  1859. W, H: UINT;
  1860. CopyStream: TMemoryStream;
  1861. Stream: IWICStream;
  1862. SavePosition: Int64;
  1863. begin
  1864. W := 0;
  1865. H := 0;
  1866. SavePosition := AStream.Position;
  1867. try
  1868. CopyStream := TMemoryStream.Create;
  1869. try
  1870. CopyStream.CopyFrom(AStream, AStream.Size);
  1871. ImagingFactory.CreateStream(Stream);
  1872. Stream.InitializeFromMemory(CopyStream.Memory, CopyStream.Size);
  1873. ImagingFactory.CreateDecoderFromStream(stream, GUID_NULL, WICDecodeMetadataCacheOnDemand, Decoder);
  1874. if Decoder <> nil then
  1875. begin
  1876. Decoder.GetFrame(0, Frame);
  1877. if Frame <> nil then
  1878. Frame.GetSize(W, H);
  1879. end;
  1880. Result := PointF(W, H);
  1881. finally
  1882. CopyStream.Free;
  1883. end;
  1884. finally
  1885. AStream.Position := SavePosition;
  1886. end;
  1887. end;
  1888. function TBitmapCodecWIC.LoadThumbnailFromStream(const AStream: TStream;
  1889. const ASrc, ADest: TRectF; const UseEmbedded: Boolean;
  1890. const Bitmap: TBitmapSurface): Boolean;
  1891. var
  1892. Decoder: IWICBitmapDecoder;
  1893. CopyStream: TMemoryStream;
  1894. Stream: IWICStream;
  1895. Frame: IWICBitmapFrameDecode;
  1896. Bmp: IWICBitmapSource;
  1897. Converter: IWICFormatConverter;
  1898. Scaler: IWICBitmapScaler;
  1899. Clipper: IWICBitmapClipper;
  1900. Width, Height: UINT;
  1901. WRect: WICRect;
  1902. LResult: HRESULT;
  1903. begin
  1904. Result := False;
  1905. CopyStream := TMemoryStream.Create;
  1906. try
  1907. CopyStream.CopyFrom(AStream, AStream.Size);
  1908. ImagingFactory.CreateStream(Stream);
  1909. Stream.InitializeFromMemory(CopyStream.Memory, CopyStream.Size);
  1910. //kngstr
  1911. ImagingFactory.CreateDecoderFromStream(Stream, GUID_NULL, WICDecodeMetadataCacheOnDemand, Decoder);
  1912. if Decoder <> nil then begin
  1913. Decoder.GetFrame(0, Frame);
  1914. if UseEmbedded then
  1915. Frame.GetThumbnail(Bmp);
  1916. if Bmp <> nil then begin
  1917. ImagingFactory.CreateFormatConverter(Converter);
  1918. if Succeeded(Converter.Initialize(Bmp, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, nil, 0, 0)) then
  1919. begin
  1920. Converter.GetSize(Width, Height);
  1921. Bitmap.SetSize(Width, Height, TPixelFormat.BGRA);
  1922. Result := Succeeded(Converter.CopyPixels(nil, Bitmap.Pitch, Height * Cardinal(Bitmap.Pitch),
  1923. PByte(Bitmap.Bits)));
  1924. end;
  1925. end
  1926. else if Frame <> nil then begin
  1927. Frame.GetSize(Width, Height);
  1928. Clipper := nil;
  1929. if (not ASrc.IsEmpty) and ((ASrc.Width < Width) or (ASrc.Height < Height)) then begin
  1930. WRect.X := Trunc(ASrc.Left);
  1931. WRect.Y := Trunc(ASrc.Top);
  1932. WRect.Width := Trunc(ASrc.Width);
  1933. WRect.Height := Trunc(ASrc.Height);
  1934. // 剪裁
  1935. ImagingFactory.CreateBitmapClipper(Clipper);
  1936. if not Succeeded(Clipper.Initialize(frame, WRect)) then
  1937. Clipper := nil;
  1938. end;
  1939. // 缩放
  1940. LResult := ImagingFactory.CreateBitmapScaler(Scaler);
  1941. if not Succeeded(LResult) then
  1942. Exit;
  1943. LResult := E_FAIL;
  1944. if (Clipper <> nil) then
  1945. LResult := Scaler.Initialize(Clipper, Trunc(ADest.Width), Trunc(ADest.Height), WICBitmapInterpolationModeLinear);
  1946. if not Succeeded(LResult) then
  1947. LResult := Scaler.Initialize(Frame, Trunc(ADest.Width), Trunc(ADest.Height), WICBitmapInterpolationModeLinear);
  1948. if Succeeded(LResult) then begin
  1949. ImagingFactory.CreateFormatConverter(Converter);
  1950. if Succeeded(Converter.Initialize(scaler, GUID_WICPixelFormat32bppPBGRA,
  1951. WICBitmapDitherTypeNone, nil, 0, 0)) then begin
  1952. Converter.GetSize(Width, Height);
  1953. Bitmap.SetSize(Width, Height, TPixelFormat.BGRA);
  1954. Result := Succeeded(Converter.CopyPixels(nil, Bitmap.Pitch, Height * Cardinal(Bitmap.Pitch),
  1955. PByte(Bitmap.Bits)));
  1956. end;
  1957. end;
  1958. end;
  1959. end;
  1960. finally
  1961. CopyStream.Free;
  1962. end;
  1963. end;
  1964. function TBitmapCodecWIC.Rotate(const AStream: TStream;
  1965. const Angle: Single): Boolean;
  1966. begin
  1967. //未完成
  1968. Result := False;
  1969. end;
  1970. function TBitmapCodecWIC.Rotate(const Extension: string; const Angle: Single;
  1971. const Bitmap: TBitmapSurface): Boolean;
  1972. begin
  1973. //未完成
  1974. Result := False;
  1975. end;
  1976. function TBitmapCodecWIC.LoadThumbnailFromStream(const AStream: TStream;
  1977. const AFitWidth, AFitHeight: Single; const UseEmbedded, AutoCut: Boolean;
  1978. const Bitmap: TBitmapSurface): Boolean;
  1979. var
  1980. Decoder: IWICBitmapDecoder;
  1981. CopyStream: TMemoryStream;
  1982. Stream: IWICStream;
  1983. Frame: IWICBitmapFrameDecode;
  1984. Bmp: IWICBitmapSource;
  1985. Converter: IWICFormatConverter;
  1986. Scaler: IWICBitmapScaler;
  1987. R: TRectF;
  1988. Width, Height: UINT;
  1989. begin
  1990. Result := False;
  1991. CopyStream := TMemoryStream.Create;
  1992. try
  1993. CopyStream.CopyFrom(AStream, AStream.Size);
  1994. ImagingFactory.CreateStream(Stream);
  1995. Stream.InitializeFromMemory(CopyStream.Memory, CopyStream.Size);
  1996. //kngstr
  1997. ImagingFactory.CreateDecoderFromStream(Stream, GUID_NULL, WICDecodeMetadataCacheOnDemand, Decoder);
  1998. if Decoder <> nil then
  1999. begin
  2000. Decoder.GetFrame(0, Frame);
  2001. if UseEmbedded then
  2002. Frame.GetThumbnail(Bmp);
  2003. if Bmp <> nil then
  2004. begin
  2005. ImagingFactory.CreateFormatConverter(Converter);
  2006. if Succeeded(Converter.Initialize(Bmp, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, nil, 0, 0)) then
  2007. begin
  2008. Converter.GetSize(Width, Height);
  2009. Bitmap.SetSize(Width, Height, TPixelFormat.BGRA);
  2010. Result := Succeeded(Converter.CopyPixels(nil, Bitmap.Pitch, Height * Cardinal(Bitmap.Pitch),
  2011. PByte(Bitmap.Bits)));
  2012. end;
  2013. end
  2014. else
  2015. if Frame <> nil then
  2016. begin
  2017. Frame.GetSize(Width, Height);
  2018. R := TRectF.Create(0, 0, Width, Height);
  2019. R.Fit(TRectF.Create(0, 0, AFitWidth, AFitHeight));
  2020. ImagingFactory.CreateBitmapScaler(Scaler);
  2021. if Succeeded(Scaler.Initialize(frame, Trunc(R.Width), Trunc(R.Height), WICBitmapInterpolationModeLinear)) then
  2022. begin
  2023. ImagingFactory.CreateFormatConverter(Converter);
  2024. if Succeeded(Converter.Initialize(scaler, GUID_WICPixelFormat32bppPBGRA,
  2025. WICBitmapDitherTypeNone, nil, 0, 0)) then
  2026. begin
  2027. Converter.GetSize(Width, Height);
  2028. Bitmap.SetSize(Width, Height, TPixelFormat.BGRA);
  2029. Result := Succeeded(Converter.CopyPixels(nil, Bitmap.Pitch, Height * Cardinal(Bitmap.Pitch),
  2030. PByte(Bitmap.Bits)));
  2031. end;
  2032. end;
  2033. end;
  2034. end;
  2035. finally
  2036. CopyStream.Free;
  2037. end;
  2038. end;
  2039. {$ENDIF}
  2040. { TBitmapCodecIOS }
  2041. {$IFDEF IOS}
  2042. class function TBitmapCodecIOS.GetImageSize(const AFileName: string): TPointF;
  2043. begin
  2044. Result := inherited GetImageSize(AFileName);
  2045. end;
  2046. class function TBitmapCodecIOS.GetImageSize(const AStream: TStream): TPointF;
  2047. var
  2048. Img: UIImage;
  2049. TempStream: TMemoryStream;
  2050. aData: NSData;
  2051. SavePosition: Int64;
  2052. begin
  2053. SavePosition := AStream.Position;
  2054. try
  2055. TempStream := TMemoryStream.Create;
  2056. try
  2057. AStream.Position := 0;
  2058. TempStream.CopyFrom(AStream, AStream.Size);
  2059. aData := TNSData.Wrap(TNSData.alloc.initWithBytesNoCopy(TempStream.Memory,TempStream.Size,False));
  2060. if aData.length > 0 then
  2061. begin
  2062. Img := TUIImage.Wrap(TUIImage.alloc.initWithData(aData));
  2063. if Img <> nil then
  2064. try
  2065. Result := PointF(Img.Size.width, Img.Size.height);
  2066. finally
  2067. Img.release;
  2068. end
  2069. else
  2070. Result := TPointF.Zero;
  2071. end else
  2072. Result := TPointF.Zero;
  2073. finally
  2074. TempStream.free;
  2075. end;
  2076. finally
  2077. AStream.Position := SavePosition;
  2078. end;
  2079. end;
  2080. class function TBitmapCodecIOS.IsGIFStream(const Stream: TStream): Boolean;
  2081. begin
  2082. Result := TImageTypeChecker.GetType(Stream) = SGIFImageExtension;
  2083. end;
  2084. function TBitmapCodecIOS.LoadFromStream(const AStream: TStream;
  2085. const Bitmap: TBitmapSurface; const MaxSizeLimit: Cardinal): Boolean;
  2086. begin
  2087. Result := inherited LoadFromStream(AStream, Bitmap, MaxSizeLimit);
  2088. end;
  2089. function TBitmapCodecIOS.LoadMovieFromStream(const Stream: TStream;
  2090. const Surface: TBitmapSurface): Boolean;
  2091. begin
  2092. Result := False;
  2093. end;
  2094. function TBitmapCodecIOS.LoadMovieFromStreamScaled(const AStream: TStream;
  2095. const Surface: TBitmapSurface; const FitSize: TPoint): Boolean;
  2096. begin
  2097. Result := False;
  2098. end;
  2099. function TBitmapCodecIOS.LoadThumbnailFromStream(const AStream: TStream; const AFitWidth, AFitHeight: Single;
  2100. const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean;
  2101. var
  2102. Img: UIImage;
  2103. ImgRef: CGImageRef;
  2104. CtxRef: CGContextRef;
  2105. R: TRectF;
  2106. TempStream: TMemoryStream;
  2107. aData: NSData;
  2108. SavePosition: Int64;
  2109. begin
  2110. Result := False;
  2111. SavePosition := AStream.Position;
  2112. try
  2113. TempStream := TMemoryStream.Create;
  2114. try
  2115. AStream.Position := 0;
  2116. TempStream.CopyFrom(AStream, AStream.Size);
  2117. aData := TNSData.Wrap(TNSData.alloc.initWithBytesNoCopy(TempStream.Memory,TempStream.Size,False));
  2118. if aData.length > 0 then
  2119. begin
  2120. Img := TUIImage.Wrap(TUIImage.alloc.initWithData(aData));
  2121. if Img <> nil then
  2122. try
  2123. ImgRef := Img.cGImage;
  2124. if ImgRef <> nil then
  2125. begin
  2126. R := TRectF.Create(0, 0, CGImageGetWidth(ImgRef), CGImageGetHeight(ImgRef));
  2127. R.Fit(TRectF.Create(0, 0, AFitWidth, AFitHeight));
  2128. Bitmap.SetSize(Round(R.Width), Round(R.Height), TPixelFormat.RGBA);
  2129. CtxRef := CGBitmapContextCreate(Bitmap.Bits, Bitmap.Width, Bitmap.Height, 8, Bitmap.Pitch, ColorSpace,
  2130. kCGImageAlphaPremultipliedLast);
  2131. try
  2132. CGContextDrawImage(CtxRef, CGRectMake(0, 0, Bitmap.Width, Bitmap.Height), imgRef);
  2133. finally
  2134. CGContextRelease(CtxRef);
  2135. end;
  2136. Result := True;
  2137. end;
  2138. finally
  2139. Img.release;
  2140. end;
  2141. end;
  2142. finally
  2143. TempStream.free;
  2144. end;
  2145. finally
  2146. AStream.Position := SavePosition;
  2147. end;
  2148. end;
  2149. function TBitmapCodecIOS.LoadThumbnailFromStream(const AStream: TStream;
  2150. const ASrc, ADest: TRectF; const UseEmbedded: Boolean;
  2151. const Bitmap: TBitmapSurface): Boolean;
  2152. var
  2153. Img: UIImage;
  2154. ImgRef: CGImageRef;
  2155. CtxRef: CGContextRef;
  2156. R: TRectF;
  2157. TempStream: TMemoryStream;
  2158. aData: NSData;
  2159. SavePosition: Int64;
  2160. begin
  2161. Result := False;
  2162. SavePosition := AStream.Position;
  2163. try
  2164. TempStream := TMemoryStream.Create;
  2165. try
  2166. AStream.Position := 0;
  2167. TempStream.CopyFrom(AStream, AStream.Size);
  2168. aData := TNSData.Wrap(TNSData.alloc.initWithBytesNoCopy(TempStream.Memory,TempStream.Size,False));
  2169. if aData.length > 0 then
  2170. begin
  2171. Img := TUIImage.Wrap(TUIImage.alloc.initWithData(aData));
  2172. if Img <> nil then
  2173. try
  2174. ImgRef := Img.cGImage;
  2175. if ImgRef <> nil then
  2176. begin
  2177. R := TRectF.Create(0, 0, ASrc.Width, ASrc.Height);
  2178. R.Fit(TRectF.Create(0, 0, ADest.Width, ADest.Height));
  2179. Bitmap.SetSize(Round(R.Width), Round(R.Height), TPixelFormat.RGBA);
  2180. CtxRef := CGBitmapContextCreate(Bitmap.Bits, Bitmap.Width, Bitmap.Height, 8, Bitmap.Pitch, ColorSpace,
  2181. kCGImageAlphaPremultipliedLast);
  2182. try
  2183. CGContextDrawImage(CtxRef, CGRectMake(0, 0, Bitmap.Width, Bitmap.Height), imgRef);
  2184. finally
  2185. CGContextRelease(CtxRef);
  2186. end;
  2187. Result := True;
  2188. end;
  2189. finally
  2190. Img.release;
  2191. end;
  2192. end;
  2193. finally
  2194. TempStream.free;
  2195. end;
  2196. finally
  2197. AStream.Position := SavePosition;
  2198. end;
  2199. end;
  2200. function TBitmapCodecIOS.Rotate(const Extension: string; const Angle: Single;
  2201. const Bitmap: TBitmapSurface): Boolean;
  2202. var
  2203. Img: UIImage;
  2204. ImageRef: CGImageRef;
  2205. CtxRef: CGContextRef;
  2206. BitmapSize: TSize;
  2207. ColorSpace: CGColorSpaceRef;
  2208. bp: TBitmap;
  2209. begin
  2210. Result := False;
  2211. bp := UIImageToBitmap(BitmapSurfaceToUIImage(Bitmap), Angle, TSize.Create(Bitmap.Width, Bitmap.Height));
  2212. try
  2213. ImageRef := BitmapToUIImage(bp).CGImage;
  2214. if ImageRef <> nil then
  2215. begin
  2216. BitmapSize := TSize.Create(CGImageGetWidth(ImageRef), CGImageGetHeight(ImageRef));
  2217. Bitmap.Clear(TAlphaColorRec.Null);
  2218. Bitmap.SetSize(BitmapSize.cx, BitmapSize.cy);
  2219. ColorSpace := CGColorSpaceCreateDeviceRGB;
  2220. try
  2221. CtxRef := CGBitmapContextCreate(Bitmap.Bits, Bitmap.Width, Bitmap.Height, 8,
  2222. Bitmap.Pitch, ColorSpace, kCGImageAlphaPremultipliedLast or kCGBitmapByteOrder32Big);
  2223. try
  2224. CGContextDrawImage(CtxRef, CGRectMake(0, 0, Bitmap.Width, Bitmap.Height), ImageRef);
  2225. finally
  2226. CGContextRelease(CtxRef);
  2227. end;
  2228. Result := True;
  2229. finally
  2230. CGColorSpaceRelease(ColorSpace);
  2231. end;
  2232. end
  2233. finally
  2234. bp.Free;
  2235. end;
  2236. end;
  2237. function TBitmapCodecIOS.Rotate(const AStream: TStream;
  2238. const Angle: Single): Boolean;
  2239. var bp: TBitmap;
  2240. DataType: string;
  2241. begin
  2242. Result := False;
  2243. DataType := TImageTypeChecker.GetType(AStream);
  2244. if DataType = SGIFImageExtension then
  2245. Exit;
  2246. bp:= TBitmap.Create;
  2247. try
  2248. AStream.Position := 0;
  2249. bp.LoadFromStream(AStream);
  2250. bp.Rotate(Angle);
  2251. AStream.Size := 0;
  2252. bp.SaveToStream(AStream, DataType);
  2253. Result := True;
  2254. finally
  2255. bp.Free;
  2256. end;
  2257. end;
  2258. {$ENDIF}
  2259. { TBitmapCodecMacOS }
  2260. {$IFDEF MacOS}
  2261. class function TBitmapCodecMacOS.GetImageSize(const AFileName: string): TPointF;
  2262. begin
  2263. Result := inherited GetImageSize(AFileName);
  2264. end;
  2265. class function TBitmapCodecMacOS.GetImageSize(const AStream: TStream): TPointF;
  2266. var
  2267. Img: NSImage;
  2268. TempStream: TMemoryStream;
  2269. aData: NSData;
  2270. SavePosition: Int64;
  2271. begin
  2272. SavePosition := AStream.Position;
  2273. try
  2274. TempStream := TMemoryStream.Create;
  2275. try
  2276. AStream.Position := 0;
  2277. TempStream.CopyFrom(AStream, AStream.Size);
  2278. aData := TNSData.Wrap(TNSData.alloc.initWithBytesNoCopy(TempStream.Memory,TempStream.Size,False));
  2279. if aData.length > 0 then
  2280. begin
  2281. Img := TNSImage.Wrap(TNSImage.alloc.initWithData(aData));
  2282. if Img <> nil then
  2283. try
  2284. Result := PointF(Img.Size.width, Img.Size.height);
  2285. finally
  2286. Img.release;
  2287. end
  2288. else
  2289. Result := TPointF.Zero;
  2290. end else
  2291. Result := TPointF.Zero;
  2292. finally
  2293. TempStream.free;
  2294. end;
  2295. finally
  2296. AStream.Position := SavePosition;
  2297. end;
  2298. end;
  2299. class function TBitmapCodecMacOS.IsGIFStream(const Stream: TStream): Boolean;
  2300. begin
  2301. Result := TImageTypeChecker.GetType(Stream) = SGIFImageExtension;
  2302. end;
  2303. function TBitmapCodecMacOS.LoadFromStream(const AStream: TStream;
  2304. const Bitmap: TBitmapSurface; const MaxSizeLimit: Cardinal): Boolean;
  2305. begin
  2306. Result := inherited LoadFromStream(AStream, Bitmap, MaxSizeLimit);
  2307. end;
  2308. function TBitmapCodecMacOS.LoadMovieFromStream(const Stream: TStream;
  2309. const Surface: TBitmapSurface): Boolean;
  2310. begin
  2311. Result := False;
  2312. end;
  2313. function TBitmapCodecMacOS.LoadMovieFromStreamScaled(const AStream: TStream;
  2314. const Surface: TBitmapSurface; const FitSize: TPoint): Boolean;
  2315. begin
  2316. Result := False;
  2317. end;
  2318. function TBitmapCodecMacOS.LoadThumbnailFromStream(const AStream: TStream; const AFitWidth, AFitHeight: Single;
  2319. const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean;
  2320. var
  2321. Img: NSImage;
  2322. ImgRef: CGImageRef;
  2323. CtxRef: CGContextRef;
  2324. R: TRectF;
  2325. TempStream: TMemoryStream;
  2326. aData: NSData;
  2327. SavePosition: Int64;
  2328. begin
  2329. Result := False;
  2330. SavePosition := AStream.Position;
  2331. try
  2332. TempStream := TMemoryStream.Create;
  2333. try
  2334. AStream.Position := 0;
  2335. TempStream.CopyFrom(AStream, AStream.Size);
  2336. aData := TNSData.Wrap(TNSData.alloc.initWithBytesNoCopy(TempStream.Memory,TempStream.Size,False));
  2337. if aData.length > 0 then
  2338. begin
  2339. Img := TNSImage.Wrap(TNSImage.alloc.initWithData(aData));
  2340. if Img <> nil then
  2341. try
  2342. ImgRef := CGImageRef(Img);
  2343. if ImgRef <> nil then
  2344. begin
  2345. R := TRectF.Create(0, 0, CGImageGetWidth(ImgRef), CGImageGetHeight(ImgRef));
  2346. R.Fit(TRectF.Create(0, 0, AFitWidth, AFitHeight));
  2347. Bitmap.SetSize(Round(R.Width), Round(R.Height), TPixelFormat.RGBA);
  2348. CtxRef := CGBitmapContextCreate(Bitmap.Bits, Bitmap.Width, Bitmap.Height, 8, Bitmap.Pitch, ColorSpace,
  2349. kCGImageAlphaPremultipliedLast);
  2350. try
  2351. CGContextDrawImage(CtxRef, CGRectMake(0, 0, Bitmap.Width, Bitmap.Height), imgRef);
  2352. finally
  2353. CGContextRelease(CtxRef);
  2354. end;
  2355. Result := True;
  2356. end;
  2357. finally
  2358. Img.release;
  2359. end;
  2360. end;
  2361. finally
  2362. TempStream.free;
  2363. end;
  2364. finally
  2365. AStream.Position := SavePosition;
  2366. end;
  2367. end;
  2368. function TBitmapCodecMacOS.LoadThumbnailFromStream(const AStream: TStream;
  2369. const ASrc, ADest: TRectF; const UseEmbedded: Boolean;
  2370. const Bitmap: TBitmapSurface): Boolean;
  2371. var
  2372. Img: NSImage;
  2373. ImgRef: CGImageRef;
  2374. CtxRef: CGContextRef;
  2375. R: TRectF;
  2376. TempStream: TMemoryStream;
  2377. aData: NSData;
  2378. SavePosition: Int64;
  2379. begin
  2380. Result := False;
  2381. SavePosition := AStream.Position;
  2382. try
  2383. TempStream := TMemoryStream.Create;
  2384. try
  2385. AStream.Position := 0;
  2386. TempStream.CopyFrom(AStream, AStream.Size);
  2387. aData := TNSData.Wrap(TNSData.alloc.initWithBytesNoCopy(TempStream.Memory,TempStream.Size,False));
  2388. if aData.length > 0 then
  2389. begin
  2390. Img := TNSImage.Wrap(TNSImage.alloc.initWithData(aData));
  2391. if Img <> nil then
  2392. try
  2393. ImgRef := CGImageRef(Img);
  2394. if ImgRef <> nil then
  2395. begin
  2396. R := TRectF.Create(0, 0, ASrc.Width, ASrc.Height);
  2397. R.Fit(TRectF.Create(0, 0, ADest.Width, ADest.Height));
  2398. Bitmap.SetSize(Round(R.Width), Round(R.Height), TPixelFormat.RGBA);
  2399. CtxRef := CGBitmapContextCreate(Bitmap.Bits, Bitmap.Width, Bitmap.Height, 8, Bitmap.Pitch, ColorSpace,
  2400. kCGImageAlphaPremultipliedLast);
  2401. try
  2402. CGContextDrawImage(CtxRef, CGRectMake(0, 0, Bitmap.Width, Bitmap.Height), imgRef);
  2403. finally
  2404. CGContextRelease(CtxRef);
  2405. end;
  2406. Result := True;
  2407. end;
  2408. finally
  2409. Img.release;
  2410. end;
  2411. end;
  2412. finally
  2413. TempStream.free;
  2414. end;
  2415. finally
  2416. AStream.Position := SavePosition;
  2417. end;
  2418. end;
  2419. function TBitmapCodecMacOS.Rotate(const Extension: string; const Angle: Single;
  2420. const Bitmap: TBitmapSurface): Boolean;
  2421. //var
  2422. // Img: NSImage;
  2423. // ImageRef: CGImageRef;
  2424. // CtxRef: CGContextRef;
  2425. // BitmapSize: TSize;
  2426. // ColorSpace: CGColorSpaceRef;
  2427. // bp: TBitmap;
  2428. begin
  2429. //未完成
  2430. Result := False;
  2431. // inherited;
  2432. // bp := UIImageToBitmap(BitmapSurfaceToMacImage(Bitmap), Angle, TSize.Create(Bitmap.Width, Bitmap.Height));
  2433. // try
  2434. // ImageRef := CGImageRef(BitmapToMacBitmap(Bitmap));
  2435. // if ImageRef <> nil then
  2436. // begin
  2437. // BitmapSize := TSize.Create(CGImageGetWidth(ImageRef), CGImageGetHeight(ImageRef));
  2438. // Bitmap.Clear(TAlphaColorRec.Null);
  2439. // Bitmap.SetSize(BitmapSize.cx, BitmapSize.cy);
  2440. // ColorSpace := CGColorSpaceCreateDeviceRGB;
  2441. // try
  2442. // CtxRef := CGBitmapContextCreate(Bitmap.Bits, Bitmap.Width, Bitmap.Height, 8,
  2443. // Bitmap.Pitch, ColorSpace, kCGImageAlphaPremultipliedLast or kCGBitmapByteOrder32Big);
  2444. // try
  2445. // CGContextDrawImage(CtxRef, CGRectMake(0, 0, Bitmap.Width, Bitmap.Height), ImageRef);
  2446. // finally
  2447. // CGContextRelease(CtxRef);
  2448. // end;
  2449. // Result := True;
  2450. // finally
  2451. // CGColorSpaceRelease(ColorSpace);
  2452. // end;
  2453. // end
  2454. // finally
  2455. // bp.Free;
  2456. // end;
  2457. end;
  2458. function TBitmapCodecMacOS.Rotate(const AStream: TStream;
  2459. const Angle: Single): Boolean;
  2460. var bp: TBitmap;
  2461. DataType: string;
  2462. begin
  2463. Result := False;
  2464. DataType := TImageTypeChecker.GetType(AStream);
  2465. if DataType = SGIFImageExtension then
  2466. Exit;
  2467. bp:= TBitmap.Create;
  2468. try
  2469. AStream.Position := 0;
  2470. bp.LoadFromStream(AStream);
  2471. bp.Rotate(Angle);
  2472. AStream.Size := 0;
  2473. bp.SaveToStream(AStream, DataType);
  2474. Result := True;
  2475. finally
  2476. bp.Free;
  2477. end;
  2478. end;
  2479. {$ENDIF}
  2480. {$IFDEF MACOS}
  2481. initialization
  2482. finalization
  2483. if GlobalColorSpace <> nil then
  2484. CGColorSpaceRelease(GlobalColorSpace);
  2485. {$ENDIF}
  2486. end.