ksBitmapHelper.pas 64 KB

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