ksBitmapHelper.pas 48 KB

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