ImagingJpeg2000.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  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. Pix := Bits;
  288. if Info.BytesPerPixel = 3 then
  289. begin
  290. for X := 0 to Width * Height - 1 do
  291. with PColor24Rec(Pix)^ do
  292. begin
  293. CY := R;
  294. CB := G;
  295. CR := B;
  296. YCbCrToRGB(CY, CB, CR, R, G, B);
  297. Inc(Pix, Info.BytesPerPixel);
  298. end;
  299. end
  300. else
  301. begin
  302. for X := 0 to Width * Height - 1 do
  303. with PColor48Rec(Pix)^ do
  304. begin
  305. CY := R;
  306. CB := G;
  307. CR := B;
  308. YCbCrToRGB16(CY, CB, CR, R, G, B);
  309. Inc(Pix, Info.BytesPerPixel);
  310. end;
  311. end;
  312. end;
  313. // Set the input position just after end of image
  314. Seek(Handle, StartPos + Cardinal(cio.bp) - Cardinal(cio.start), smFromBeginning);
  315. Result := True;
  316. finally
  317. opj_image_destroy(image);
  318. opj_destroy_decompress(dinfo);
  319. opj_cio_close(cio);
  320. FreeMem(Buffer);
  321. end;
  322. end;
  323. function TJpeg2000FileFormat.SaveData(Handle: TImagingHandle;
  324. const Images: TDynImageDataArray; Index: LongInt): Boolean;
  325. var
  326. TargetSize, Rate: Single;
  327. ImageToSave: TImageData;
  328. MustBeFreed: Boolean;
  329. Info: TImageFormatInfo;
  330. I, Z, InvZ, Channel, ChannelSize, NumPixels: Integer;
  331. Pix: PByte;
  332. image: popj_image_t;
  333. cio: popj_cio_t;
  334. cinfo: popj_cinfo_t;
  335. parameters: opj_cparameters_t;
  336. compparams: popj_image_cmptparm_array;
  337. ColorSpace: OPJ_COLOR_SPACE;
  338. function GetComponentType(Comp: Integer): OPJ_COMPONENT_TYPE;
  339. begin
  340. if Info.HasAlphaChannel and (Comp = Info.ChannelCount - 1) then
  341. Result := COMPTYPE_OPACITY
  342. else if Info.HasGrayChannel then
  343. Result := COMPTYPE_Y
  344. else if Comp = 2 then
  345. Result := COMPTYPE_B
  346. else if Comp = 1 then
  347. Result := COMPTYPE_G
  348. else if Comp = 0 then
  349. Result := COMPTYPE_R
  350. else
  351. Result := COMPTYPE_UNKNOWN;
  352. end;
  353. begin
  354. Result := False;
  355. image := nil;
  356. compparams := nil;
  357. cinfo := nil;
  358. cio := nil;
  359. // Makes image to save compatible with Jpeg 2000 saving capabilities
  360. if MakeCompatible(Images[Index], ImageToSave, MustBeFreed) then
  361. with GetIO, ImageToSave do
  362. try
  363. Info := GetFormatInfo(Format);
  364. ChannelSize := Info.BytesPerPixel div Info.ChannelCount;
  365. // Fill component info structures and then create OpenJPEG image
  366. GetMem(compparams, Info.ChannelCount * SizeOf(opj_image_comptparm));
  367. for I := 0 to Info.ChannelCount - 1 do
  368. with compparams[I] do
  369. begin
  370. dx := 1;
  371. dy := 1;
  372. w := Width;
  373. h := Height;
  374. bpp := (Info.BytesPerPixel div Info.ChannelCount) * 8;
  375. prec := bpp;
  376. sgnd := 0;
  377. comp_type := GetComponentType(I);
  378. x0 := 0;
  379. y0 := 0;
  380. end;
  381. if Info.HasGrayChannel then
  382. ColorSpace := CLRSPC_GRAY
  383. else
  384. ColorSpace := CLRSPC_SRGB;
  385. image := opj_image_create(Info.ChannelCount, @compparams[0], ColorSpace);
  386. if image = nil then Exit;
  387. image.x1 := Width;
  388. image.y1 := Height;
  389. if FCodeStreamOnly then
  390. cinfo := opj_create_compress(CODEC_J2K)
  391. else
  392. cinfo := opj_create_compress(CODEC_JP2);
  393. // Set event manager to nil to avoid getting messages
  394. cinfo.event_mgr := nil;
  395. // Set compression parameters based current file format properties
  396. opj_set_default_encoder_parameters(@parameters);
  397. parameters.cod_format := Iff(FCodeStreamOnly, 0, 1);
  398. parameters.numresolution := 6;
  399. parameters.tcp_numlayers := 1;
  400. parameters.cp_disto_alloc := 1;
  401. if FLosslessCompression then
  402. begin
  403. // Set rate to 0 -> lossless
  404. parameters.tcp_rates[0] := 0;
  405. end
  406. else
  407. begin
  408. // Quality -> Rate computation taken from ImageMagick
  409. Rate := 100.0 / Sqr(115 - FQuality);
  410. NumPixels := Width * Height * Info.BytesPerPixel;
  411. TargetSize := (NumPixels * Rate) + 550 + (Info.ChannelCount - 1) * 142;
  412. parameters.tcp_rates[0] := 1.0 / (TargetSize / NumPixels);
  413. end;
  414. // Setup encoder
  415. opj_setup_encoder(cinfo, @parameters, image);
  416. // Fill component samples in data with values taken from
  417. // image pixels.
  418. // Components should be ordered like this: RGBA, YA, RGB, etc.
  419. for Channel := 0 to Info.ChannelCount - 1 do
  420. begin
  421. Z := Channel;
  422. InvZ := Info.ChannelCount - 1 - Z;
  423. if Info.HasAlphaChannel then
  424. begin
  425. if Channel = Info.ChannelCount - 1 then
  426. InvZ := Z
  427. else
  428. InvZ := Info.ChannelCount - 2 - Z;
  429. end;
  430. Pix := @PByteArray(Bits)[InvZ * ChannelSize];
  431. for I := 0 to Width * Height - 1 do
  432. begin
  433. case ChannelSize of
  434. 1: image.comps[Z].data[I] := Pix^;
  435. 2: image.comps[Z].data[I] := PWord(Pix)^;
  436. 4: LongWord(image.comps[Z].data[I]) := PLongWord(Pix)^;
  437. end;
  438. Inc(Pix, Info.BytesPerPixel);
  439. end;
  440. end;
  441. // Open OpenJPEG output
  442. cio := opj_cio_open(opj_common_ptr(cinfo), nil, 0);
  443. // Try to encode the image
  444. if not opj_encode(cinfo, cio, image, nil) then
  445. Exit;
  446. // Finally write buffer with encoded image to output
  447. Write(Handle, cio.buffer, cio_tell(cio));
  448. Result := True;
  449. finally
  450. if MustBeFreed then
  451. FreeImage(ImageToSave);
  452. opj_destroy_compress(cinfo);
  453. opj_image_destroy(image);
  454. opj_cio_close(cio);
  455. FreeMem(compparams);
  456. end;
  457. end;
  458. procedure TJpeg2000FileFormat.ConvertToSupported(var Image: TImageData;
  459. const Info: TImageFormatInfo);
  460. var
  461. ConvFormat: TImageFormat;
  462. begin
  463. if Info.IsFloatingPoint then
  464. ConvFormat := IffFormat(Info.ChannelCount = 1, ifGray16, ifA16R16G16B16)
  465. else if Info.HasGrayChannel then
  466. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA16Gray16, ifGray16)
  467. else if Info.IsIndexed then
  468. ConvFormat := ifA8R8G8B8
  469. else if Info.BytesPerPixel div Info.ChannelCount > 1 then
  470. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA16R16G16B16, ifR16G16B16)
  471. else
  472. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8R8G8B8, ifR8G8B8);
  473. ConvertImage(Image, ConvFormat);
  474. end;
  475. function TJpeg2000FileFormat.TestFormat(Handle: TImagingHandle): Boolean;
  476. begin
  477. Result := False;
  478. if Handle <> nil then
  479. Result := GetFileType(Handle) <> jtInvalid;
  480. end;
  481. initialization
  482. RegisterImageFileFormat(TJpeg2000FileFormat);
  483. {
  484. File Notes:
  485. -- TODOS ----------------------------------------------------
  486. - nothing now
  487. -- 0.24.3 Changes/Bug Fixes -----------------------------------
  488. - Alpha channels are now saved properly in FPC (GCC optimization issue),
  489. FPC lossy compression enabled again!
  490. - Added handling of component types (CDEF Box), JP2 images with alpha
  491. are now properly recognized by other applications.
  492. - Fixed wrong color space when saving grayscale images
  493. -- 0.21 Changes/Bug Fixes -----------------------------------
  494. - Removed ifGray32 from supported formats, OpenJPEG crashes when saving them.
  495. - Added Seek after loading to set input pos to the end of image.
  496. - Saving added losy/lossless, quality option added.
  497. - Initial loading-only version created.
  498. }
  499. end.