ImagingJpeg2000.pas 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. {
  2. $Id$
  3. Vampyre Imaging Library
  4. by Marek Mauder
  5. http://imaginglib.sourceforge.net
  6. The contents of this file are used with permission, subject to the Mozilla
  7. Public License Version 1.1 (the "License"); you may not use this file except
  8. in compliance with the License. You may obtain a copy of the License at
  9. http://www.mozilla.org/MPL/MPL-1.1.html
  10. Software distributed under the License is distributed on an "AS IS" basis,
  11. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
  12. the specific language governing rights and limitations under the License.
  13. Alternatively, the contents of this file may be used under the terms of the
  14. GNU Lesser General Public License (the "LGPL License"), in which case the
  15. provisions of the LGPL License are applicable instead of those above.
  16. If you wish to allow use of your version of this file only under the terms
  17. of the LGPL License and not to allow others to use your version of this file
  18. under the MPL, indicate your decision by deleting the provisions above and
  19. replace them with the notice and other provisions required by the LGPL
  20. License. If you do not delete the provisions above, a recipient may use
  21. your version of this file under either the MPL or the LGPL License.
  22. For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html
  23. }
  24. { This unit contains image format loader/saver for Jpeg 2000 images.}
  25. unit ImagingJpeg2000;
  26. {$I ImagingOptions.inc}
  27. interface
  28. uses
  29. SysUtils, ImagingTypes, Imaging, ImagingColors, ImagingIO, ImagingUtility,
  30. ImagingExtras, OpenJpeg;
  31. type
  32. { Type Jpeg 2000 file (needed for OpenJPEG codec settings).}
  33. TJpeg2000FileType = (jtInvalid, jtJP2, jtJ2K, jtJPT);
  34. { Class for loading/saving Jpeg 2000 images. It uses OpenJPEG library
  35. compiled to object files and linked to Object Pascal program. Jpeg 2000
  36. supports wide variety of data formats. You can have arbitrary number
  37. of components/channels, each with different bitdepth and optional
  38. "signedness". Jpeg 2000 images can be lossy or lossless compressed.
  39. Imaging can load most data formats (except images
  40. with componenet bitdepth > 16 => no Imaging data format equivalents).
  41. Components with sample separation are loaded correctly, ICC profiles
  42. or palettes are not used, YCbCr images are translated to RGB.
  43. You can set various options when saving Jpeg-2000 images. Look at
  44. properties of TJpeg2000FileFormat for details.}
  45. TJpeg2000FileFormat = class(TImageFileFormat)
  46. protected
  47. FQuality: LongInt;
  48. FCodeStreamOnly: LongBool;
  49. FLosslessCompression: LongBool;
  50. function GetFileType(Handle: TImagingHandle): TJpeg2000FileType;
  51. function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray;
  52. OnlyFirstLevel: Boolean): Boolean; override;
  53. function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray;
  54. Index: LongInt): Boolean; override;
  55. procedure ConvertToSupported(var Image: TImageData;
  56. const Info: TImageFormatInfo); override;
  57. public
  58. constructor Create; override;
  59. function TestFormat(Handle: TImagingHandle): Boolean; override;
  60. procedure CheckOptionsValidity; override;
  61. published
  62. { Controls JPEG 2000 lossy compression quality. It is number in range 1..100.
  63. 1 means small/ugly file, 100 means large/nice file. Accessible trough
  64. ImagingJpeg2000Quality option. Default value is 80.}
  65. property Quality: LongInt read FQuality write FQuality;
  66. { Controls whether JPEG 2000 image is saved with full file headers or just
  67. as code stream. Default value is False. Accessible trough
  68. ImagingJpeg2000CodeStreamOnly option.}
  69. property CodeStreamOnly: LongBool read FCodeStreamOnly write FCodeStreamOnly;
  70. { Specifies JPEG 2000 image compression type. If True, saved JPEG 2000 files
  71. will be losslessly compressed. Otherwise lossy compression is used.
  72. Default value is False. Accessible trough
  73. ImagingJpeg2000LosslessCompression option.}
  74. property LosslessCompression: LongBool read FLosslessCompression write FLosslessCompression;
  75. end;
  76. implementation
  77. const
  78. SJpeg2000FormatName = 'JPEG 2000 Image';
  79. SJpeg2000Masks = '*.jp2,*.j2k,*.j2c,*.jpx,*.jpc';
  80. Jpeg2000SupportedFormats: TImageFormats = [ifGray8, ifGray16,
  81. ifA8Gray8, ifA16Gray16, ifR8G8B8, ifR16G16B16, ifA8R8G8B8, ifA16R16G16B16];
  82. Jpeg2000DefaultQuality = 80;
  83. Jpeg2000DefaultCodeStreamOnly = False;
  84. Jpeg2000DefaultLosslessCompression = False;
  85. const
  86. JP2Signature: TChar8 = #0#0#0#$0C#$6A#$50#$20#$20;
  87. J2KSignature: TChar4 = #$FF#$4F#$FF#$51;
  88. constructor TJpeg2000FileFormat.Create;
  89. begin
  90. inherited Create;
  91. FName := SJpeg2000FormatName;
  92. FCanLoad := True;
  93. FCanSave := True;
  94. FIsMultiImageFormat := False;
  95. FSupportedFormats := Jpeg2000SupportedFormats;
  96. FQuality := Jpeg2000DefaultQuality;
  97. FCodeStreamOnly := Jpeg2000DefaultCodeStreamOnly;
  98. FLosslessCompression := Jpeg2000DefaultLosslessCompression;
  99. AddMasks(SJpeg2000Masks);
  100. RegisterOption(ImagingJpeg2000Quality, @FQuality);
  101. RegisterOption(ImagingJpeg2000CodeStreamOnly, @FCodeStreamOnly);
  102. RegisterOption(ImagingJpeg2000LosslessCompression, @FLosslessCompression);
  103. end;
  104. procedure TJpeg2000FileFormat.CheckOptionsValidity;
  105. begin
  106. // Check if option values are valid
  107. if not (FQuality in [1..100]) then
  108. FQuality := Jpeg2000DefaultQuality;
  109. end;
  110. function TJpeg2000FileFormat.GetFileType(Handle: TImagingHandle): TJpeg2000FileType;
  111. var
  112. ReadCount: LongInt;
  113. Id: TChar8;
  114. begin
  115. Result := jtInvalid;
  116. with GetIO do
  117. begin
  118. ReadCount := Read(Handle, @Id, SizeOf(Id));
  119. if ReadCount = SizeOf(Id) then
  120. begin
  121. // Check if we have full JP2 file format or just J2K code stream
  122. if CompareMem(@Id, @JP2Signature, SizeOf(JP2Signature)) then
  123. Result := jtJP2
  124. else if CompareMem(@Id, @J2KSignature, SizeOf(J2KSignature)) then
  125. Result := jtJ2K;
  126. end;
  127. Seek(Handle, -ReadCount, smFromCurrent);
  128. end;
  129. end;
  130. function TJpeg2000FileFormat.LoadData(Handle: TImagingHandle;
  131. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  132. var
  133. FileType: TJpeg2000FileType;
  134. Buffer, Pix, PixUp: PByte;
  135. X, Y, Z, InvZ, SX, SY, WidthBytes, BufferSize, ChannelSize, Channel,
  136. CY, CB, CR: LongInt;
  137. Info: TImageFormatInfo;
  138. Signed: Boolean;
  139. Col24: PColor24Rec;
  140. Col48: PColor48Rec;
  141. dinfo: popj_dinfo_t;
  142. parameters: opj_dparameters_t;
  143. cio: popj_cio_t;
  144. image: popj_image_t;
  145. StartPos: Int64;
  146. begin
  147. Result := False;
  148. image := nil;
  149. cio := nil;
  150. opj_set_default_decoder_parameters(@parameters);
  151. // Determine which codec to use
  152. FileType := GetFileType(Handle);
  153. case FileType of
  154. jtJP2: dinfo := opj_create_decompress(CODEC_JP2);
  155. jtJ2K: dinfo := opj_create_decompress(CODEC_J2K);
  156. jtJPT: dinfo := opj_create_decompress(CODEC_JPT);
  157. else
  158. Exit;
  159. end;
  160. // Currently OpenJPEG can load images only from memory so we have to
  161. // preload whole input to mem buffer. Not good but no other way now.
  162. // At least we set stream pos to end of JP2 data after loading (we will now
  163. // the exact size by then).
  164. StartPos := GetIO.Tell(Handle);
  165. BufferSize := ImagingIO.GetInputSize(GetIO, Handle);
  166. GetMem(Buffer, BufferSize);
  167. SetLength(Images, 1);
  168. with GetIO, Images[0] do
  169. try
  170. Read(Handle, Buffer, BufferSize);
  171. cio := opj_cio_open(opj_common_ptr(dinfo), Buffer, BufferSize);
  172. opj_setup_decoder(dinfo, @parameters);
  173. // Decode image
  174. image := opj_decode(dinfo, cio);
  175. if image = nil then
  176. Exit;
  177. // Determine which Imaging data format to use accorsing to
  178. // decoded image components
  179. case image.numcomps of
  180. 2: case image.comps[0].prec of
  181. 1..8: Format := ifA8Gray8;
  182. 9..16: Format := ifA16Gray16;
  183. end;
  184. 3: case image.comps[0].prec of
  185. 1..8: Format := ifR8G8B8;
  186. 9..16: Format := ifR16G16B16;
  187. end;
  188. 4: case image.comps[0].prec of
  189. 1..8: Format := ifA8R8G8B8;
  190. 9..16: Format := ifA16R16G16B16;
  191. end;
  192. else
  193. // There is only one component or there is more than four =>
  194. // just load the first one as gray
  195. case image.comps[0].prec of
  196. 1..8: Format := ifGray8;
  197. 9..16: Format := ifGray16;
  198. 17..32: Format := ifGray32;
  199. end;
  200. end;
  201. // Exit if no compatible format was found
  202. if Format = ifUnknown then
  203. Exit;
  204. NewImage(image.x1, image.y1, Format, Images[0]);
  205. Info := GetFormatInfo(Format);
  206. ChannelSize := Info.BytesPerPixel div Info.ChannelCount;
  207. // Images components are stored separately in JP2, each can have
  208. // different dimensions, bitdepth, ...
  209. for Channel := 0 to Info.ChannelCount - 1 do
  210. begin
  211. // Z and InvZ are used as component indices to output image channels and
  212. // decoded image components. Following settings prevent later need for
  213. // Red/Blue switch. Alpha channel is special case, channel orders
  214. // are ARGB <-> ABGR (Channel at the lowest address of output image is Blue
  215. // where as decoded image component at the lowest index is Red).
  216. Z := Channel;
  217. InvZ := Info.ChannelCount - 1 - Z;
  218. if Info.HasAlphaChannel then
  219. begin
  220. if Channel = Info.ChannelCount - 1 then
  221. InvZ := Z
  222. else
  223. InvZ := Info.ChannelCount - 2 - Z;
  224. end;
  225. // Signed componets must be scaled to [0, 1] (later)
  226. Signed := image.comps[Z].sgnd = 1;
  227. if (image.comps[Z].dx = 1) and (image.comps[Z].dy = 1) then
  228. begin
  229. // X and Y sample separation is 1 so just need to assign component values
  230. // to image pixels one by one
  231. Pix := @PByteArray(Bits)[InvZ * ChannelSize];
  232. for Y := 0 to Height - 1 do
  233. for X := 0 to Width - 1 do
  234. begin
  235. case ChannelSize of
  236. 1: Pix^ := image.comps[Z].data[Y * Width + X] + Iff(Signed, $80, 0);
  237. 2: PWord(Pix)^ := image.comps[Z].data[Y * Width + X] + Iff(Signed, $8000, 0);
  238. 4: PLongWord(Pix)^ := image.comps[Z].data[Y * Width + X] + IffUnsigned(Signed, $80000000, 0);
  239. end;
  240. Inc(Pix, Info.BytesPerPixel);
  241. end;
  242. end
  243. else
  244. begin
  245. // Sample separation is active - component is sub-sampled. Real component
  246. // dimensions are [image.comps[Z].w * image.comps[Z].dx,
  247. // image.comps[Z].h * image.comps[Z].dy
  248. WidthBytes := Width * Info.BytesPerPixel;
  249. for Y := 0 to image.comps[Z].h - 1 do
  250. begin
  251. Pix := @PByteArray(Bits)[Y * image.comps[Z].dy * WidthBytes + InvZ * ChannelSize];
  252. for X := 0 to image.comps[Z].w - 1 do
  253. for SX := 0 to image.comps[Z].dx - 1 do
  254. begin
  255. // Replicate pixels on line
  256. case ChannelSize of
  257. 1: Pix^ := image.comps[Z].data[Y * image.comps[Z].w + X] + Iff(Signed, $80, 0);
  258. 2: PWord(Pix)^ := image.comps[Z].data[Y * image.comps[Z].w + X] + Iff(Signed, $8000, 0);
  259. 4: PLongWord(Pix)^ := image.comps[Z].data[Y * image.comps[Z].w + X] + IffUnsigned(Signed, $80000000, 0);
  260. end;
  261. Inc(Pix, Info.BytesPerPixel);
  262. end;
  263. for SY := 1 to image.comps[Z].dy - 1 do
  264. begin
  265. // Replicate lines
  266. PixUp := @PByteArray(Bits)[Y * image.comps[Z].dy * WidthBytes + InvZ * ChannelSize];
  267. Pix := @PByteArray(Bits)[(Y * image.comps[Z].dy + SY) * WidthBytes + InvZ * ChannelSize];
  268. for X := 0 to Width - 1 do
  269. begin
  270. case ChannelSize of
  271. 1: Pix^ := PixUp^;
  272. 2: PWord(Pix)^ := PWord(PixUp)^;
  273. 4: PLongWord(Pix)^ := PLongWord(PixUp)^;
  274. end;
  275. Inc(Pix, Info.BytesPerPixel);
  276. Inc(PixUp, Info.BytesPerPixel);
  277. end;
  278. end;
  279. end;
  280. end;
  281. end;
  282. if (Info.ChannelCount = 3) and (image.color_space = CLRSPC_SYCC) then
  283. begin
  284. // Convert image from YCbCr colorspace to RGB if needed.
  285. // Not exactly sure which channel is Y (OpenJpeg's fault - no "cdef" detection).
  286. Pix := Bits;
  287. if Info.BytesPerPixel = 3 then
  288. begin
  289. for X := 0 to Width * Height - 1 do
  290. with PColor24Rec(Pix)^ do
  291. begin
  292. CY := R;
  293. CB := G;
  294. CR := B;
  295. YCbCrToRGB(CY, CB, CR, R, G, B);
  296. Inc(Pix, Info.BytesPerPixel);
  297. end;
  298. end
  299. else
  300. begin
  301. for X := 0 to Width * Height - 1 do
  302. with PColor48Rec(Pix)^ do
  303. begin
  304. CY := R;
  305. CB := G;
  306. CR := B;
  307. YCbCrToRGB16(CY, CB, CR, R, G, B);
  308. Inc(Pix, Info.BytesPerPixel);
  309. end;
  310. end;
  311. end;
  312. // Set the input position just after end of image
  313. Seek(Handle, StartPos + LongWord(cio.bp) - LongWord(cio.start), smFromBeginning);
  314. Result := True;
  315. finally
  316. opj_image_destroy(image);
  317. opj_destroy_decompress(dinfo);
  318. opj_cio_close(cio);
  319. FreeMem(Buffer);
  320. end;
  321. end;
  322. function TJpeg2000FileFormat.SaveData(Handle: TImagingHandle;
  323. const Images: TDynImageDataArray; Index: LongInt): Boolean;
  324. var
  325. TargetSize, Rate: Single;
  326. ImageToSave: TImageData;
  327. MustBeFreed: Boolean;
  328. Info: TImageFormatInfo;
  329. I, Z, InvZ, Channel, ChannelSize, NumPixels: LongInt;
  330. Pix: PByte;
  331. image: popj_image_t;
  332. cio: popj_cio_t;
  333. cinfo: popj_cinfo_t;
  334. parameters: opj_cparameters_t;
  335. compparams: popj_image_cmptparm_array;
  336. begin
  337. Result := False;
  338. image := nil;
  339. compparams := nil;
  340. cinfo := nil;
  341. cio := nil;
  342. // Makes image to save compatible with Jpeg 2000 saving capabilities
  343. if MakeCompatible(Images[Index], ImageToSave, MustBeFreed) then
  344. with GetIO, ImageToSave do
  345. try
  346. Info := GetFormatInfo(Format);
  347. ChannelSize := Info.BytesPerPixel div Info.ChannelCount;
  348. // Fill component info structures and then create OpenJPEG image
  349. GetMem(compparams, Info.ChannelCount * SizeOf(opj_image_comptparm));
  350. for I := 0 to Info.ChannelCount - 1 do
  351. with compparams[I] do
  352. begin
  353. dx := 1;
  354. dy := 1;
  355. w := Width;
  356. h := Height;
  357. bpp := (Info.BytesPerPixel div Info.ChannelCount) * 8;
  358. prec := bpp;
  359. sgnd := 0;
  360. x0 := 0;
  361. y0 := 0;
  362. end;
  363. image := opj_image_create(Info.ChannelCount, @compparams[0], CLRSPC_SRGB);
  364. if image = nil then Exit;
  365. image.x1 := Width;
  366. image.y1 := Height;
  367. if FCodeStreamOnly then
  368. cinfo := opj_create_compress(CODEC_J2K)
  369. else
  370. cinfo := opj_create_compress(CODEC_JP2);
  371. // Set compression parameters based current file format properties
  372. opj_set_default_encoder_parameters(@parameters);
  373. parameters.cod_format := Iff(FCodeStreamOnly, 0, 1);
  374. parameters.numresolution := 6;
  375. parameters.tcp_numlayers := 1;
  376. parameters.cp_disto_alloc := 1;
  377. if FLosslessCompression then
  378. begin
  379. // Set rate to 0 -> lossless
  380. parameters.tcp_rates[0] := 0;
  381. end
  382. else
  383. begin
  384. // Quality -> Rate computation taken from ImageMagick
  385. Rate := 100.0 / Sqr(115 - FQuality);
  386. NumPixels := Width * Height * Info.BytesPerPixel;
  387. TargetSize := (NumPixels * Rate) + 550 + (Info.ChannelCount - 1) * 142;
  388. parameters.tcp_rates[0] := 1.0 / (TargetSize / NumPixels);
  389. end;
  390. // Setup encoder
  391. opj_setup_encoder(cinfo, @parameters, image);
  392. // Fill component samples in data with values taken from
  393. // image pixels
  394. for Channel := 0 to Info.ChannelCount - 1 do
  395. begin
  396. Z := Channel;
  397. InvZ := Info.ChannelCount - 1 - Z;
  398. if Info.HasAlphaChannel then
  399. begin
  400. if Channel = Info.ChannelCount - 1 then
  401. InvZ := Z
  402. else
  403. InvZ := Info.ChannelCount - 2 - Z;
  404. end;
  405. Pix := @PByteArray(Bits)[InvZ * ChannelSize];
  406. for I := 0 to Width * Height - 1 do
  407. begin
  408. case ChannelSize of
  409. 1: image.comps[Z].data[I] := Pix^;
  410. 2: image.comps[Z].data[I] := PWord(Pix)^;
  411. 4: LongWord(image.comps[Z].data[I]) := PLongWord(Pix)^;
  412. end;
  413. Inc(Pix, Info.BytesPerPixel);
  414. end;
  415. end;
  416. // Open OpenJPEG output
  417. cio := opj_cio_open(opj_common_ptr(cinfo), nil, 0);
  418. // Try to encode the image
  419. if not opj_encode(cinfo, cio, image, nil) then
  420. Exit;
  421. // Finally write buffer with encoded image to output
  422. Write(Handle, cio.buffer, cio_tell(cio));
  423. Result := True;
  424. finally
  425. if MustBeFreed then
  426. FreeImage(ImageToSave);
  427. opj_destroy_compress(cinfo);
  428. opj_image_destroy(image);
  429. opj_cio_close(cio);
  430. FreeMem(compparams);
  431. end;
  432. end;
  433. procedure TJpeg2000FileFormat.ConvertToSupported(var Image: TImageData;
  434. const Info: TImageFormatInfo);
  435. var
  436. ConvFormat: TImageFormat;
  437. begin
  438. if Info.IsFloatingPoint then
  439. ConvFormat := IffFormat(Info.ChannelCount = 1, ifGray16, ifA16R16G16B16)
  440. else if Info.HasGrayChannel then
  441. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA16Gray16, ifGray16)
  442. else if Info.IsIndexed then
  443. ConvFormat := ifA8R8G8B8
  444. else if Info.BytesPerPixel div Info.ChannelCount > 1 then
  445. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA16R16G16B16, ifR16G16B16)
  446. else
  447. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8R8G8B8, ifR8G8B8);
  448. ConvertImage(Image, ConvFormat);
  449. end;
  450. function TJpeg2000FileFormat.TestFormat(Handle: TImagingHandle): Boolean;
  451. begin
  452. Result := False;
  453. if Handle <> nil then
  454. Result := GetFileType(Handle) <> jtInvalid;
  455. end;
  456. initialization
  457. RegisterImageFileFormat(TJpeg2000FileFormat);
  458. {
  459. File Notes:
  460. -- TODOS ----------------------------------------------------
  461. - nothing now
  462. -- 0.21 Changes/Bug Fixes -----------------------------------
  463. - Removed ifGray32 from supported formats, OpenJPEG crashes when saving them.
  464. - Added Seek after loading to set input pos to the end of image.
  465. - Saving added losy/lossless, quality option added.
  466. - Initial loading-only version created.
  467. }
  468. end.