ImagingJpegIJL.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. {
  2. Vampyre Imaging Library
  3. by Marek Mauder
  4. https://github.com/galfar/imaginglib
  5. https://imaginglib.sourceforge.io
  6. - - - - -
  7. This Source Code Form is subject to the terms of the Mozilla Public
  8. License, v. 2.0. If a copy of the MPL was not distributed with this
  9. file, You can obtain one at https://mozilla.org/MPL/2.0.
  10. }
  11. { This unit contains image format alternative loader/saver for Jpeg images
  12. using Intel Jpeg Library (Win32 only).}
  13. unit ImagingJpegIJL;
  14. {$I ImagingOptions.inc}
  15. {$IFNDEF WIN32}
  16. {$ERROR 'IJL 1.5 only for Win32'}
  17. {$ENDIF}
  18. interface
  19. uses
  20. SysUtils, ImagingTypes, Imaging, ImagingUtility, ImagingIO;
  21. type
  22. { Class for loading/saving Jpeg images. This is alternative to
  23. default built-in Jpeg handler (which uses JpegLib).
  24. This handler uses Intel Jpeg Library 1.5 (DLL needed) and is
  25. much faster than JpegLib (2-4x). Also supports reading and writing of
  26. alpha channels in Jpeg files.}
  27. TJpegFileFormatIJL = class(TImageFileFormat)
  28. private
  29. FQuality: LongInt;
  30. procedure JpegError(Code: Integer);
  31. protected
  32. procedure Define; override;
  33. function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray;
  34. OnlyFirstLevel: Boolean): Boolean; override;
  35. function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray;
  36. Index: LongInt): Boolean; override;
  37. procedure ConvertToSupported(var Image: TImageData;
  38. const Info: TImageFormatInfo); override;
  39. public
  40. function TestFormat(Handle: TImagingHandle): Boolean; override;
  41. procedure CheckOptionsValidity; override;
  42. published
  43. { Controls Jpeg save compression quality. It is number in range 1..100.
  44. 1 means small/ugly file, 100 means large/nice file. Accessible trough
  45. ImagingJpegQuality option.}
  46. property Quality: LongInt read FQuality write FQuality;
  47. end;
  48. implementation
  49. {$MINENUMSIZE 4} // Min enum size: 4 B
  50. uses
  51. Types;
  52. const
  53. SJpegFormatName = 'JPEG Image (IJL)';
  54. SJpegMasks = '*.jpg,*.jpeg,*.jfif,*.jpe,*.jif,*.jpa';
  55. JpegSupportedFormats: TImageFormats = [ifGray8, ifR8G8B8, ifA8R8G8B8];
  56. JpegDefaultQuality = 90;
  57. JpegDefaultProgressive = False;
  58. resourcestring
  59. SJpegError = 'JPEG Error';
  60. const
  61. { Jpeg file identifiers.}
  62. JpegMagic: TChar2 = #$FF#$D8;
  63. SIJLLibrary = 'ijl15.dll';
  64. const
  65. IJL_SETUP = -1;
  66. IJL_OK = 0;
  67. IJL_NONE = 0;
  68. IJL_OTHER = 255;
  69. JBUFSIZE = 4096; // Size of file I/O buffer (4K).
  70. type
  71. {
  72. Purpose: Possible types of data read/write/other operations to be
  73. performed by the functions IJL_Read and IJL_Write.
  74. See the Developer's Guide for details on appropriate usage.
  75. Fields:
  76. IJL_JFILE_XXXXXXX Indicates JPEG data in a stdio file.
  77. IJL_JBUFF_XXXXXXX Indicates JPEG data in an addressable buffer.
  78. }
  79. TIJLIOType = (
  80. // Read JPEG parameters (i.e., height, width, channels, sampling, etc.)
  81. // from a JPEG bit stream.
  82. IJL_JFILE_READPARAMS = 0,
  83. IJL_JBUFF_READPARAMS = 1,
  84. // Read a JPEG Interchange Format image.
  85. IJL_JFILE_READWHOLEIMAGE = 2,
  86. IJL_JBUFF_READWHOLEIMAGE = 3,
  87. // Read JPEG tables from a JPEG Abbreviated Format bit stream.
  88. IJL_JFILE_READHEADER = 4,
  89. IJL_JBUFF_READHEADER = 5,
  90. // Read image info from a JPEG Abbreviated Format bit stream.
  91. IJL_JFILE_READENTROPY = 6,
  92. IJL_JBUFF_READENTROPY = 7,
  93. // Write an entire JFIF bit stream.
  94. IJL_JFILE_WRITEWHOLEIMAGE = 8,
  95. IJL_JBUFF_WRITEWHOLEIMAGE = 9,
  96. // Write a JPEG Abbreviated Format bit stream.
  97. IJL_JFILE_WRITEHEADER = 10,
  98. IJL_JBUFF_WRITEHEADER = 11,
  99. // Write image info to a JPEG Abbreviated Format bit stream.
  100. IJL_JFILE_WRITEENTROPY = 12,
  101. IJL_JBUFF_WRITEENTROPY = 13,
  102. // Scaled Decoding Options:
  103. // Reads a JPEG image scaled to 1/2 size.
  104. IJL_JFILE_READONEHALF = 14,
  105. IJL_JBUFF_READONEHALF = 15,
  106. // Reads a JPEG image scaled to 1/4 size.
  107. IJL_JFILE_READONEQUARTER = 16,
  108. IJL_JBUFF_READONEQUARTER = 17,
  109. // Reads a JPEG image scaled to 1/8 size.
  110. IJL_JFILE_READONEEIGHTH = 18,
  111. IJL_JBUFF_READONEEIGHTH = 19,
  112. // Reads an embedded thumbnail from a JFIF bit stream.
  113. IJL_JFILE_READTHUMBNAIL = 20,
  114. IJL_JBUFF_READTHUMBNAIL = 21
  115. );
  116. {
  117. Purpose: Possible color space formats.
  118. Note these formats do *not* necessarily denote
  119. the number of channels in the color space.
  120. There exists separate "channel" fields in the
  121. JPEG_CORE_PROPERTIES data structure specifically
  122. for indicating the number of channels in the
  123. JPEG and/or DIB color spaces.}
  124. TIJL_COLOR = (
  125. IJL_RGB = 1, // Red-Green-Blue color space.
  126. IJL_BGR = 2, // Reversed channel ordering from IJL_RGB.
  127. IJL_YCBCR = 3, // Luminance-Chrominance color space as defined
  128. // by CCIR Recommendation 601.
  129. IJL_G = 4, // Grayscale color space.
  130. IJL_RGBA_FPX = 5, // FlashPix RGB 4 channel color space that
  131. // has pre-multiplied opacity.
  132. IJL_YCBCRA_FPX = 6 // FlashPix YCbCr 4 channel color space that
  133. // has pre-multiplied opacity.
  134. //IJL_OTHER = 255 // Some other color space not defined by the IJL.
  135. // (This means no color space conversion will
  136. // be done by the IJL.)
  137. );
  138. { Purpose: Possible subsampling formats used in the JPEG.}
  139. TIJL_JPGSUBSAMPLING = (
  140. IJL_NOSUBSAMP = 0,
  141. IJL_411 = 1, // Valid on a JPEG w/ 3 channels.
  142. IJL_422 = 2, // Valid on a JPEG w/ 3 channels.
  143. IJL_4114 = 3, // Valid on a JPEG w/ 4 channels.
  144. IJL_4224 = 4 // Valid on a JPEG w/ 4 channels.
  145. );
  146. { Purpose: Possible subsampling formats used in the DIB. }
  147. TIJL_DIBSUBSAMPLING = TIJL_JPGSUBSAMPLING;
  148. { Purpose: This is the primary data structure between the IJL and
  149. the external user. It stores JPEG state information
  150. and controls the IJL. It is user-modifiable.
  151. Context: Used by all low-level IJL routines to store
  152. pseudo-global information.}
  153. TJpegCoreProperties = packed record
  154. UseJPEGPROPERTIES : LongBool; // default = 0
  155. // DIB specific I/O data specifiers.
  156. DIBBytes : PByte; // default = NULL
  157. DIBWidth : UInt32; // default = 0
  158. DIBHeight : UInt32; // default = 0
  159. DIBPadBytes : UInt32; // default = 0
  160. DIBChannels : UInt32; // default = 3
  161. DIBColor : TIJL_COLOR; // default = IJL_BGR
  162. DIBSubsampling : TIJL_DIBSUBSAMPLING; // default = IJL_NONE
  163. // JPEG specific I/O data specifiers.
  164. JPGFile : PAnsiChar; // default = NULL
  165. JPGBytes : PByte; // default = NULL
  166. JPGSizeBytes : UInt32; // default = 0
  167. JPGWidth : UInt32; // default = 0
  168. JPGHeight : UInt32; // default = 0
  169. JPGChannels : UInt32; // default = 3
  170. JPGColor : TIJL_COLOR; // default = IJL_YCBCR
  171. JPGSubsampling : TIJL_JPGSUBSAMPLING; // default = IJL_411
  172. JPGThumbWidth : UInt32; // default = 0
  173. JPGThumbHeight : UInt32; // default = 0
  174. // JPEG conversion properties.
  175. NeedsConvert : LongBool; // default = TRUE
  176. NeedsResample : LongBool; // default = TRUE
  177. Quality : UInt32; // default = 75
  178. // Low-level properties.
  179. PropsAndUnused : array[0..19987] of Byte;
  180. end;
  181. PJpegCoreProperties = ^TJpegCoreProperties;
  182. function ijlInit(var Props: TJpegCoreProperties): Integer; stdcall; external SIJLLibrary;
  183. function ijlFree(var Props: TJpegCoreProperties): Integer; stdcall; external SIJLLibrary;
  184. function ijlRead(var Props: TJpegCoreProperties; IoType : TIJLIOTYPE): Integer; stdcall; external SIJLLibrary;
  185. function ijlWrite(var Props: TJpegCoreProperties; IoType : TIJLIOTYPE): Integer; stdcall; external SIJLLibrary;
  186. function ijlErrorStr(Code : Integer) : PAnsiChar; stdcall; external SIJLLibrary;
  187. { TJpegFileFormatIJL class implementation }
  188. procedure TJpegFileFormatIJL.Define;
  189. begin
  190. inherited;
  191. FName := SJpegFormatName;
  192. FCanLoad := True;
  193. FCanSave := True;
  194. FIsMultiImageFormat := False;
  195. FSupportedFormats := JpegSupportedFormats;
  196. FQuality := JpegDefaultQuality;
  197. AddMasks(SJpegMasks);
  198. RegisterOption(ImagingJpegQuality, @FQuality);
  199. end;
  200. procedure TJpegFileFormatIJL.CheckOptionsValidity;
  201. begin
  202. // Check if option values are valid
  203. if not (FQuality in [1..100]) then
  204. FQuality := JpegDefaultQuality;
  205. end;
  206. procedure TJpegFileFormatIJL.ConvertToSupported(var Image: TImageData;
  207. const Info: TImageFormatInfo);
  208. begin
  209. if Info.HasAlphaChannel then
  210. ConvertImage(Image, ifA8R8G8B8)
  211. else if Info.HasGrayChannel then
  212. ConvertImage(Image, ifGray8)
  213. else
  214. ConvertImage(Image, ifR8G8B8);
  215. end;
  216. function TJpegFileFormatIJL.TestFormat(Handle: TImagingHandle): Boolean;
  217. var
  218. ReadCount: LongInt;
  219. ID: array[0..9] of AnsiChar;
  220. begin
  221. Result := False;
  222. if Handle <> nil then
  223. with GetIO do
  224. begin
  225. FillChar(ID, SizeOf(ID), 0);
  226. ReadCount := Read(Handle, @ID, SizeOf(ID));
  227. Seek(Handle, -ReadCount, smFromCurrent);
  228. Result := (ReadCount = SizeOf(ID)) and
  229. CompareMem(@ID, @JpegMagic, SizeOf(JpegMagic));
  230. end;
  231. end;
  232. procedure TJpegFileFormatIJL.JpegError(Code: Integer);
  233. begin
  234. raise EImagingError.Create(SJpegError + ': ' + ijlErrorStr(Code));
  235. end;
  236. function TJpegFileFormatIJL.LoadData(Handle: TImagingHandle;
  237. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  238. var
  239. Props: TJpegCoreProperties;
  240. Status: Integer;
  241. Buffer: TDynByteArray;
  242. InputLen: Integer;
  243. JpegFmt: TImageFormat;
  244. begin
  245. // Copy IO functions to global var used in JpegLib callbacks
  246. Result := False;
  247. SetLength(Images, 1);
  248. with Images[0] do
  249. try
  250. InputLen := GetInputSize(GetIO, Handle);
  251. Status := IjlInit(Props);
  252. if Status = IJL_OK then
  253. begin
  254. // Load input to memory and read Jpeg props
  255. SetLength(Buffer, InputLen);
  256. Props.JPGSizeBytes := InputLen;
  257. Props.JPGBytes := @Buffer[0];
  258. GetIO.Read(Handle, @Buffer[0], InputLen);
  259. Status := ijlRead(Props, IJL_JBUFF_READPARAMS);
  260. end;
  261. if Status = IJL_OK then
  262. begin
  263. // Set image and DIB props based on Jpeg params read from input
  264. case Props.JPGChannels of
  265. 1:
  266. begin
  267. JpegFmt := ifGray8;
  268. Props.DIBColor := IJL_G;
  269. end;
  270. 3:
  271. begin
  272. JpegFmt := ifR8G8B8;
  273. Props.DIBColor := IJL_BGR;
  274. end;
  275. 4:
  276. begin
  277. JpegFmt := ifA8R8G8B8;
  278. Props.DIBColor := IJL_RGBA_FPX;
  279. end
  280. else
  281. Exit;
  282. end;
  283. NewImage(Props.JPGWidth, Props.JPGHeight, JpegFmt, Images[0]);
  284. Props.DIBWidth := Props.JPGWidth;
  285. Props.DIBHeight := Props.JPGHeight;
  286. Props.DIBChannels := Props.JPGChannels;
  287. Props.DIBPadBytes := 0;
  288. Props.DIBBytes := Bits;
  289. // Now read the image bits
  290. Status := ijlRead(Props, IJL_JBUFF_READWHOLEIMAGE);
  291. end;
  292. if Status <> IJL_OK then
  293. JpegError(Status);
  294. // Decoded images with alpha are in ABGR format so R and B channels are switched
  295. if JpegFmt = ifA8R8G8B8 then
  296. SwapChannels(Images[0], ChannelRed, ChannelBlue);
  297. Result := True;
  298. finally
  299. ijlFree(Props);
  300. end;
  301. end;
  302. function TJpegFileFormatIJL.SaveData(Handle: TImagingHandle;
  303. const Images: TDynImageDataArray; Index: LongInt): Boolean;
  304. var
  305. Props: TJpegCoreProperties;
  306. Status: Integer;
  307. Info: TImageFormatInfo;
  308. ImageToSave: TImageData;
  309. MustBeFreed: Boolean;
  310. Buffer: TDynByteArray;
  311. begin
  312. Result := False;
  313. // Makes image to save compatible with Jpeg saving capabilities
  314. if MakeCompatible(Images[Index], ImageToSave, MustBeFreed) then
  315. with ImageToSave do
  316. try
  317. Status := ijlInit(Props);
  318. if Status = IJL_OK then
  319. begin
  320. Info := GetFormatInfo(Format);
  321. // Set all the needed props
  322. Props.DIBWidth := Width;
  323. Props.DIBHeight := Height;
  324. Props.DIBChannels := Info.ChannelCount;
  325. Props.DIBPadBytes := 0;
  326. Props.DIBBytes := Bits;
  327. Props.Quality := FQuality;
  328. Props.JPGWidth := Width;
  329. Props.JPGHeight := Height;
  330. Props.JPGChannels := Info.ChannelCount;
  331. SetLength(Buffer, Size);
  332. Props.JPGSizeBytes := Size;
  333. Props.JPGBytes := @Buffer[0];
  334. case Info.ChannelCount of
  335. 1:
  336. begin
  337. Props.DIBColor := IJL_G;
  338. Props.JPGColor := IJL_G;
  339. Props.JPGSubsampling := IJL_NOSUBSAMP;
  340. end;
  341. 3:
  342. begin
  343. Props.DIBColor := IJL_BGR;
  344. Props.JPGColor := IJL_YCBCR;
  345. Props.JPGSubsampling := IJL_411;
  346. end;
  347. 4:
  348. begin
  349. Props.DIBColor := IJL_RGBA_FPX;
  350. Props.JPGColor := IJL_YCBCRA_FPX;
  351. Props.JPGSubsampling := IJL_4114;
  352. SwapChannels(ImageToSave, ChannelRed, ChannelBlue); // IJL expects ABGR order
  353. end;
  354. end;
  355. // Encode image
  356. Status := ijlWrite(Props, IJL_JBUFF_WRITEWHOLEIMAGE);
  357. end;
  358. if Status <> IJL_OK then
  359. JpegError(Status);
  360. // Write temp buffer to file
  361. GetIO.Write(Handle, @Buffer[0], Props.JPGSizeBytes);
  362. Result := True;
  363. finally
  364. ijlFree(Props);
  365. if MustBeFreed then
  366. FreeImage(ImageToSave)
  367. else if Format = ifA8R8G8B8 then
  368. SwapChannels(ImageToSave, ChannelRed, ChannelBlue); // Swap image back to ARGB if not temp
  369. end;
  370. end;
  371. initialization
  372. RegisterImageFileFormat(TJpegFileFormatIJL);
  373. {
  374. File Notes:
  375. -- TODOS ----------------------------------------------------
  376. - nothing now
  377. -- 0.26.3 Changes/Bug Fixes ---------------------------------
  378. - Initial version created.
  379. }
  380. end.