ImagingJpeg2000.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  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. // Set event manager to nil to avoid getting messages
  161. dinfo.event_mgr := nil;
  162. // Currently OpenJPEG can load images only from memory so we have to
  163. // preload whole input to mem buffer. Not good but no other way now.
  164. // At least we set stream pos to end of JP2 data after loading (we will now
  165. // the exact size by then).
  166. StartPos := GetIO.Tell(Handle);
  167. BufferSize := ImagingIO.GetInputSize(GetIO, Handle);
  168. GetMem(Buffer, BufferSize);
  169. SetLength(Images, 1);
  170. with GetIO, Images[0] do
  171. try
  172. Read(Handle, Buffer, BufferSize);
  173. cio := opj_cio_open(opj_common_ptr(dinfo), Buffer, BufferSize);
  174. opj_setup_decoder(dinfo, @parameters);
  175. // Decode image
  176. image := opj_decode(dinfo, cio);
  177. if image = nil then
  178. Exit;
  179. // Determine which Imaging data format to use accorsing to
  180. // decoded image components
  181. case image.numcomps of
  182. 2: case image.comps[0].prec of
  183. 1..8: Format := ifA8Gray8;
  184. 9..16: Format := ifA16Gray16;
  185. end;
  186. 3: case image.comps[0].prec of
  187. 1..8: Format := ifR8G8B8;
  188. 9..16: Format := ifR16G16B16;
  189. end;
  190. 4: case image.comps[0].prec of
  191. 1..8: Format := ifA8R8G8B8;
  192. 9..16: Format := ifA16R16G16B16;
  193. end;
  194. else
  195. // There is only one component or there is more than four =>
  196. // just load the first one as gray
  197. case image.comps[0].prec of
  198. 1..8: Format := ifGray8;
  199. 9..16: Format := ifGray16;
  200. 17..32: Format := ifGray32;
  201. end;
  202. end;
  203. // Exit if no compatible format was found
  204. if Format = ifUnknown then
  205. Exit;
  206. NewImage(image.x1, image.y1, Format, Images[0]);
  207. Info := GetFormatInfo(Format);
  208. ChannelSize := Info.BytesPerPixel div Info.ChannelCount;
  209. // Images components are stored separately in JP2, each can have
  210. // different dimensions, bitdepth, ...
  211. for Channel := 0 to Info.ChannelCount - 1 do
  212. begin
  213. // Z and InvZ are used as component indices to output image channels and
  214. // decoded image components. Following settings prevent later need for
  215. // Red/Blue switch. Alpha channel is special case, channel orders
  216. // are ARGB <-> ABGR (Channel at the lowest address of output image is Blue
  217. // where as decoded image component at the lowest index is Red).
  218. Z := Channel;
  219. InvZ := Info.ChannelCount - 1 - Z;
  220. if Info.HasAlphaChannel then
  221. begin
  222. if Channel = Info.ChannelCount - 1 then
  223. InvZ := Z
  224. else
  225. InvZ := Info.ChannelCount - 2 - Z;
  226. end;
  227. // Signed componets must be scaled to [0, 1] (later)
  228. Signed := image.comps[Z].sgnd = 1;
  229. if (image.comps[Z].dx = 1) and (image.comps[Z].dy = 1) then
  230. begin
  231. // X and Y sample separation is 1 so just need to assign component values
  232. // to image pixels one by one
  233. Pix := @PByteArray(Bits)[InvZ * ChannelSize];
  234. for Y := 0 to Height - 1 do
  235. for X := 0 to Width - 1 do
  236. begin
  237. case ChannelSize of
  238. 1: Pix^ := image.comps[Z].data[Y * Width + X] + Iff(Signed, $80, 0);
  239. 2: PWord(Pix)^ := image.comps[Z].data[Y * Width + X] + Iff(Signed, $8000, 0);
  240. 4: PLongWord(Pix)^ := image.comps[Z].data[Y * Width + X] + IffUnsigned(Signed, $80000000, 0);
  241. end;
  242. Inc(Pix, Info.BytesPerPixel);
  243. end;
  244. end
  245. else
  246. begin
  247. // Sample separation is active - component is sub-sampled. Real component
  248. // dimensions are [image.comps[Z].w * image.comps[Z].dx,
  249. // image.comps[Z].h * image.comps[Z].dy
  250. WidthBytes := Width * Info.BytesPerPixel;
  251. for Y := 0 to image.comps[Z].h - 1 do
  252. begin
  253. Pix := @PByteArray(Bits)[Y * image.comps[Z].dy * WidthBytes + InvZ * ChannelSize];
  254. for X := 0 to image.comps[Z].w - 1 do
  255. for SX := 0 to image.comps[Z].dx - 1 do
  256. begin
  257. // Replicate pixels on line
  258. case ChannelSize of
  259. 1: Pix^ := image.comps[Z].data[Y * image.comps[Z].w + X] + Iff(Signed, $80, 0);
  260. 2: PWord(Pix)^ := image.comps[Z].data[Y * image.comps[Z].w + X] + Iff(Signed, $8000, 0);
  261. 4: PLongWord(Pix)^ := image.comps[Z].data[Y * image.comps[Z].w + X] + IffUnsigned(Signed, $80000000, 0);
  262. end;
  263. Inc(Pix, Info.BytesPerPixel);
  264. end;
  265. for SY := 1 to image.comps[Z].dy - 1 do
  266. begin
  267. // Replicate lines
  268. PixUp := @PByteArray(Bits)[Y * image.comps[Z].dy * WidthBytes + InvZ * ChannelSize];
  269. Pix := @PByteArray(Bits)[(Y * image.comps[Z].dy + SY) * WidthBytes + InvZ * ChannelSize];
  270. for X := 0 to Width - 1 do
  271. begin
  272. case ChannelSize of
  273. 1: Pix^ := PixUp^;
  274. 2: PWord(Pix)^ := PWord(PixUp)^;
  275. 4: PLongWord(Pix)^ := PLongWord(PixUp)^;
  276. end;
  277. Inc(Pix, Info.BytesPerPixel);
  278. Inc(PixUp, Info.BytesPerPixel);
  279. end;
  280. end;
  281. end;
  282. end;
  283. end;
  284. if (Info.ChannelCount = 3) and (image.color_space = CLRSPC_SYCC) then
  285. begin
  286. // Convert image from YCbCr colorspace to RGB if needed.
  287. // Not exactly sure which channel is Y (OpenJpeg's fault - no "cdef" detection).
  288. Pix := Bits;
  289. if Info.BytesPerPixel = 3 then
  290. begin
  291. for X := 0 to Width * Height - 1 do
  292. with PColor24Rec(Pix)^ do
  293. begin
  294. CY := R;
  295. CB := G;
  296. CR := B;
  297. YCbCrToRGB(CY, CB, CR, R, G, B);
  298. Inc(Pix, Info.BytesPerPixel);
  299. end;
  300. end
  301. else
  302. begin
  303. for X := 0 to Width * Height - 1 do
  304. with PColor48Rec(Pix)^ do
  305. begin
  306. CY := R;
  307. CB := G;
  308. CR := B;
  309. YCbCrToRGB16(CY, CB, CR, R, G, B);
  310. Inc(Pix, Info.BytesPerPixel);
  311. end;
  312. end;
  313. end;
  314. // Set the input position just after end of image
  315. Seek(Handle, StartPos + Cardinal(cio.bp) - Cardinal(cio.start), smFromBeginning);
  316. Result := True;
  317. finally
  318. opj_image_destroy(image);
  319. opj_destroy_decompress(dinfo);
  320. opj_cio_close(cio);
  321. FreeMem(Buffer);
  322. end;
  323. end;
  324. function TJpeg2000FileFormat.SaveData(Handle: TImagingHandle;
  325. const Images: TDynImageDataArray; Index: LongInt): Boolean;
  326. var
  327. TargetSize, Rate: Single;
  328. ImageToSave: TImageData;
  329. MustBeFreed: Boolean;
  330. Info: TImageFormatInfo;
  331. I, Z, InvZ, Channel, ChannelSize, NumPixels: LongInt;
  332. Pix: PByte;
  333. image: popj_image_t;
  334. cio: popj_cio_t;
  335. cinfo: popj_cinfo_t;
  336. parameters: opj_cparameters_t;
  337. compparams: popj_image_cmptparm_array;
  338. begin
  339. Result := False;
  340. image := nil;
  341. compparams := nil;
  342. cinfo := nil;
  343. cio := nil;
  344. // Makes image to save compatible with Jpeg 2000 saving capabilities
  345. if MakeCompatible(Images[Index], ImageToSave, MustBeFreed) then
  346. with GetIO, ImageToSave do
  347. try
  348. Info := GetFormatInfo(Format);
  349. ChannelSize := Info.BytesPerPixel div Info.ChannelCount;
  350. // Fill component info structures and then create OpenJPEG image
  351. GetMem(compparams, Info.ChannelCount * SizeOf(opj_image_comptparm));
  352. for I := 0 to Info.ChannelCount - 1 do
  353. with compparams[I] do
  354. begin
  355. dx := 1;
  356. dy := 1;
  357. w := Width;
  358. h := Height;
  359. bpp := (Info.BytesPerPixel div Info.ChannelCount) * 8;
  360. prec := bpp;
  361. sgnd := 0;
  362. x0 := 0;
  363. y0 := 0;
  364. end;
  365. image := opj_image_create(Info.ChannelCount, @compparams[0], CLRSPC_SRGB);
  366. if image = nil then Exit;
  367. image.x1 := Width;
  368. image.y1 := Height;
  369. if FCodeStreamOnly then
  370. cinfo := opj_create_compress(CODEC_J2K)
  371. else
  372. cinfo := opj_create_compress(CODEC_JP2);
  373. // Set event manager to nil to avoid getting messages
  374. cinfo.event_mgr := nil;
  375. // Set compression parameters based current file format properties
  376. opj_set_default_encoder_parameters(@parameters);
  377. parameters.cod_format := Iff(FCodeStreamOnly, 0, 1);
  378. parameters.numresolution := 6;
  379. parameters.tcp_numlayers := 1;
  380. parameters.cp_disto_alloc := 1;
  381. if FLosslessCompression then
  382. begin
  383. // Set rate to 0 -> lossless
  384. parameters.tcp_rates[0] := 0;
  385. end
  386. else
  387. begin
  388. // Quality -> Rate computation taken from ImageMagick
  389. Rate := 100.0 / Sqr(115 - FQuality);
  390. NumPixels := Width * Height * Info.BytesPerPixel;
  391. TargetSize := (NumPixels * Rate) + 550 + (Info.ChannelCount - 1) * 142;
  392. parameters.tcp_rates[0] := 1.0 / (TargetSize / NumPixels);
  393. {$IF Defined(FPC)}
  394. // Only lossless compression for images with alpha in FPC.
  395. // OpenJPEG sets whole chanel to 128 somehow when compiled with GCC.
  396. if Info.HasAlphaChannel then
  397. parameters.tcp_rates[0] := 0;
  398. {$IFEND}
  399. end;
  400. // Setup encoder
  401. opj_setup_encoder(cinfo, @parameters, image);
  402. // Fill component samples in data with values taken from
  403. // image pixels
  404. for Channel := 0 to Info.ChannelCount - 1 do
  405. begin
  406. Z := Channel;
  407. InvZ := Info.ChannelCount - 1 - Z;
  408. if Info.HasAlphaChannel then
  409. begin
  410. if Channel = Info.ChannelCount - 1 then
  411. InvZ := Z
  412. else
  413. InvZ := Info.ChannelCount - 2 - Z;
  414. end;
  415. Pix := @PByteArray(Bits)[InvZ * ChannelSize];
  416. for I := 0 to Width * Height - 1 do
  417. begin
  418. case ChannelSize of
  419. 1: image.comps[Z].data[I] := Pix^;
  420. 2: image.comps[Z].data[I] := PWord(Pix)^;
  421. 4: LongWord(image.comps[Z].data[I]) := PLongWord(Pix)^;
  422. end;
  423. Inc(Pix, Info.BytesPerPixel);
  424. end;
  425. end;
  426. // Open OpenJPEG output
  427. cio := opj_cio_open(opj_common_ptr(cinfo), nil, 0);
  428. // Try to encode the image
  429. if not opj_encode(cinfo, cio, image, nil) then
  430. Exit;
  431. // Finally write buffer with encoded image to output
  432. Write(Handle, cio.buffer, cio_tell(cio));
  433. Result := True;
  434. finally
  435. if MustBeFreed then
  436. FreeImage(ImageToSave);
  437. opj_destroy_compress(cinfo);
  438. opj_image_destroy(image);
  439. opj_cio_close(cio);
  440. FreeMem(compparams);
  441. end;
  442. end;
  443. procedure TJpeg2000FileFormat.ConvertToSupported(var Image: TImageData;
  444. const Info: TImageFormatInfo);
  445. var
  446. ConvFormat: TImageFormat;
  447. begin
  448. if Info.IsFloatingPoint then
  449. ConvFormat := IffFormat(Info.ChannelCount = 1, ifGray16, ifA16R16G16B16)
  450. else if Info.HasGrayChannel then
  451. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA16Gray16, ifGray16)
  452. else if Info.IsIndexed then
  453. ConvFormat := ifA8R8G8B8
  454. else if Info.BytesPerPixel div Info.ChannelCount > 1 then
  455. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA16R16G16B16, ifR16G16B16)
  456. else
  457. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8R8G8B8, ifR8G8B8);
  458. ConvertImage(Image, ConvFormat);
  459. end;
  460. function TJpeg2000FileFormat.TestFormat(Handle: TImagingHandle): Boolean;
  461. begin
  462. Result := False;
  463. if Handle <> nil then
  464. Result := GetFileType(Handle) <> jtInvalid;
  465. end;
  466. initialization
  467. RegisterImageFileFormat(TJpeg2000FileFormat);
  468. {
  469. File Notes:
  470. -- TODOS ----------------------------------------------------
  471. - nothing now
  472. -- 0.21 Changes/Bug Fixes -----------------------------------
  473. - Removed ifGray32 from supported formats, OpenJPEG crashes when saving them.
  474. - Added Seek after loading to set input pos to the end of image.
  475. - Saving added losy/lossless, quality option added.
  476. - Initial loading-only version created.
  477. }
  478. end.