ksBitmapHelper.pas 73 KB

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