ImagingJpeg2000.pas 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  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 loader/saver for Jpeg 2000 images.}
  12. unit ImagingJpeg2000;
  13. {$I ImagingOptions.inc}
  14. interface
  15. {
  16. JPEG2000 support needs precompiled C object files and only some targets are
  17. available.
  18. Delphi targets: Windows 32b
  19. FPC targets: Windows 32b, Linux 32+64b, OSX 32b
  20. }
  21. {$IF (Defined(DCC) and Defined(MSWINDOWS) and Defined(CPUX86)) or
  22. (Defined(FPC) and Defined(MSWINDOWS) and Defined(CPUX86)) or
  23. (Defined(FPC) and Defined(LINUX) and (Defined(CPUX86) or Defined(CPUX64))) or
  24. (Defined(FPC) and Defined(MACOS) and Defined(CPUX86))}
  25. uses
  26. SysUtils, ImagingTypes, Imaging, ImagingColors, ImagingIO, ImagingUtility,
  27. ImagingExtFileFormats, OpenJpeg;
  28. type
  29. { Type Jpeg 2000 file (needed for OpenJPEG codec settings).}
  30. TJpeg2000FileType = (jtInvalid, jtJP2, jtJ2K, jtJPT);
  31. { Class for loading/saving Jpeg 2000 images. It uses OpenJPEG library
  32. compiled to object files and linked to Object Pascal program. Jpeg 2000
  33. supports wide variety of data formats. You can have arbitrary number
  34. of components/channels, each with different bitdepth and optional
  35. "signedness". Jpeg 2000 images can be lossy or lossless compressed.
  36. Imaging can load most data formats (except images
  37. with component bitdepth > 16 => no Imaging data format equivalents).
  38. Components with sample separation are loaded correctly, ICC profiles
  39. or palettes are not used, YCbCr images are translated to RGB.
  40. You can set various options when saving Jpeg-2000 images. Look at
  41. properties of TJpeg2000FileFormat for details.}
  42. TJpeg2000FileFormat = class(TImageFileFormat)
  43. private
  44. FQuality: LongInt;
  45. FCodeStreamOnly: LongBool;
  46. FLosslessCompression: LongBool;
  47. FScaleOutput: LongBool;
  48. function GetFileType(Handle: TImagingHandle): TJpeg2000FileType;
  49. protected
  50. procedure Define; override;
  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. function TestFormat(Handle: TImagingHandle): Boolean; override;
  59. procedure CheckOptionsValidity; override;
  60. published
  61. { Controls JPEG 2000 lossy compression quality. It is number in range 1..100.
  62. 1 means small/ugly file, 100 means large/nice file. Accessible trough
  63. ImagingJpeg2000Quality option. Default value is 80.}
  64. property Quality: LongInt read FQuality write FQuality;
  65. { Controls whether JPEG 2000 image is saved with full file headers or just
  66. as code stream. Default value is False. Accessible trough
  67. ImagingJpeg2000CodeStreamOnly option.}
  68. property CodeStreamOnly: LongBool read FCodeStreamOnly write FCodeStreamOnly;
  69. { Specifies JPEG 2000 image compression type. If True, saved JPEG 2000 files
  70. will be losslessly compressed. Otherwise lossy compression is used.
  71. Default value is False. Accessible trough
  72. ImagingJpeg2000LosslessCompression option.}
  73. property LosslessCompression: LongBool read FLosslessCompression write FLosslessCompression;
  74. { Specifies JPEG 2000 output scaling. Since JPEG 2000 supports arbitrary Bit Depths,
  75. the default behaviour is to scale the images up tp the next 8^n bit depth.
  76. This can be disabled by setting this option to False.
  77. Default value is True. Accessible through
  78. ImagingJpeg2000ScaleOutput option.}
  79. property ScaleOutput: LongBool read FScaleOutput write FScaleOutput;
  80. end;
  81. implementation
  82. const
  83. SJpeg2000FormatName = 'JPEG 2000 Image';
  84. SJpeg2000Masks = '*.jp2,*.j2k,*.j2c,*.jpx,*.jpc';
  85. Jpeg2000SupportedFormats: TImageFormats = [ifGray8, ifGray16,
  86. ifA8Gray8, ifA16Gray16, ifR8G8B8, ifR16G16B16, ifA8R8G8B8, ifA16R16G16B16];
  87. Jpeg2000DefaultQuality = 80;
  88. Jpeg2000DefaultCodeStreamOnly = False;
  89. Jpeg2000DefaultLosslessCompression = False;
  90. Jpeg2000DefaultScaleOutput = True;
  91. const
  92. JP2Signature: TChar8 = #0#0#0#$0C#$6A#$50#$20#$20;
  93. J2KSignature: TChar4 = #$FF#$4F#$FF#$51;
  94. procedure TJpeg2000FileFormat.Define;
  95. begin
  96. inherited;
  97. FName := SJpeg2000FormatName;
  98. FFeatures := [ffLoad, ffSave];
  99. FSupportedFormats := Jpeg2000SupportedFormats;
  100. FQuality := Jpeg2000DefaultQuality;
  101. FCodeStreamOnly := Jpeg2000DefaultCodeStreamOnly;
  102. FLosslessCompression := Jpeg2000DefaultLosslessCompression;
  103. FScaleOutput := Jpeg2000DefaultScaleOutput;
  104. AddMasks(SJpeg2000Masks);
  105. RegisterOption(ImagingJpeg2000Quality, @FQuality);
  106. RegisterOption(ImagingJpeg2000CodeStreamOnly, @FCodeStreamOnly);
  107. RegisterOption(ImagingJpeg2000LosslessCompression, @FLosslessCompression);
  108. RegisterOption(ImagingJpeg2000ScaleOutput, @FScaleOutput);
  109. end;
  110. procedure TJpeg2000FileFormat.CheckOptionsValidity;
  111. begin
  112. // Check if option values are valid
  113. if not (FQuality in [1..100]) then
  114. FQuality := Jpeg2000DefaultQuality;
  115. end;
  116. function TJpeg2000FileFormat.GetFileType(Handle: TImagingHandle): TJpeg2000FileType;
  117. var
  118. ReadCount: LongInt;
  119. Id: TChar8;
  120. begin
  121. Result := jtInvalid;
  122. with GetIO do
  123. begin
  124. ReadCount := Read(Handle, @Id, SizeOf(Id));
  125. if ReadCount = SizeOf(Id) then
  126. begin
  127. // Check if we have full JP2 file format or just J2K code stream
  128. if CompareMem(@Id, @JP2Signature, SizeOf(JP2Signature)) then
  129. Result := jtJP2
  130. else if CompareMem(@Id, @J2KSignature, SizeOf(J2KSignature)) then
  131. Result := jtJ2K;
  132. end;
  133. Seek(Handle, -ReadCount, smFromCurrent);
  134. end;
  135. end;
  136. function TJpeg2000FileFormat.LoadData(Handle: TImagingHandle;
  137. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  138. type
  139. TChannelInfo = record
  140. DestOffset: Integer;
  141. CompType: OPJ_COMPONENT_TYPE;
  142. Shift: Integer;
  143. SrcMaxValue: Integer;
  144. DestMaxValue: Integer;
  145. end;
  146. var
  147. FileType: TJpeg2000FileType;
  148. Buffer: PByte;
  149. BufferSize, ChannelSize, I: Integer;
  150. Info: TImageFormatInfo;
  151. dinfo: popj_dinfo_t;
  152. parameters: opj_dparameters_t;
  153. cio: popj_cio_t;
  154. image: popj_image_t;
  155. StartPos: Int64;
  156. Channels: array of TChannelInfo;
  157. procedure WriteSample(Dest: PByte; ChannelSize, Value: Integer); {$IFDEF USE_INLINE}inline;{$ENDIF}
  158. begin
  159. case ChannelSize of
  160. 1: Dest^ := Value;
  161. 2: PWord(Dest)^ := Value;
  162. 4: PUInt32(Dest)^ := Value;
  163. end;
  164. end;
  165. procedure CopySample(Src, Dest: PByte; ChannelSize: Integer); {$IFDEF USE_INLINE}inline;{$ENDIF}
  166. begin
  167. case ChannelSize of
  168. 1: Dest^ := Src^;
  169. 2: PWord(Dest)^ := PWord(Src)^;
  170. 4: PUInt32(Dest)^ := PUInt32(Src)^;
  171. end;
  172. end;
  173. procedure ReadChannel(const Image: TImageData; const Info: TChannelInfo; const Comp: opj_image_comp; BytesPerPixel: Integer);
  174. var
  175. X, Y, SX, SY, SrcIdx, LineBytes: Integer;
  176. DestPtr, NewPtr, LineUpPtr: PByte;
  177. DontScaleSamples: Boolean;
  178. begin
  179. DontScaleSamples := (Info.SrcMaxValue = Info.DestMaxValue) or not FScaleOutput;
  180. LineBytes := Image.Width * BytesPerPixel;
  181. DestPtr := @PByteArray(Image.Bits)[Info.DestOffset];
  182. SrcIdx := 0;
  183. if (Comp.dx = 1) and (Comp.dy = 1) then
  184. begin
  185. // X and Y sample separation is 1 so just need to assign component values
  186. // to image pixels one by one
  187. for Y := 0 to Image.Height * Image.Width - 1 do
  188. begin
  189. if DontScaleSamples then
  190. WriteSample(DestPtr, ChannelSize, Comp.data[SrcIdx] + Info.Shift)
  191. else
  192. WriteSample(DestPtr, ChannelSize, MulDiv(Comp.data[SrcIdx] + Info.Shift, Info.DestMaxValue, Info.SrcMaxValue));
  193. Inc(SrcIdx);
  194. Inc(DestPtr, BytesPerPixel);
  195. end;
  196. end
  197. else
  198. begin
  199. // Sample separation is active - component is sub-sampled. Real component
  200. // dimensions are [Comp.w * Comp.dx, Comp.h * Comp.dy]
  201. for Y := 0 to Comp.h - 1 do
  202. begin
  203. LineUpPtr := @PByteArray(Image.Bits)[Y * Comp.dy * LineBytes + Info.DestOffset];
  204. DestPtr := LineUpPtr;
  205. for X := 0 to Comp.w - 1 do
  206. begin
  207. if DontScaleSamples then
  208. WriteSample(DestPtr, ChannelSize, Comp.data[SrcIdx] + Info.Shift)
  209. else
  210. WriteSample(DestPtr, ChannelSize, MulDiv(Comp.data[SrcIdx] + Info.Shift, Info.DestMaxValue, Info.SrcMaxValue));
  211. NewPtr := DestPtr;
  212. for SX := 1 to Comp.dx - 1 do
  213. begin
  214. if X * Comp.dx + SX >= Image.Width then Break;
  215. // Replicate pixels on line
  216. Inc(NewPtr, BytesPerPixel);
  217. CopySample(DestPtr, NewPtr, ChannelSize);
  218. end;
  219. Inc(SrcIdx);
  220. Inc(DestPtr, BytesPerPixel * Comp.dx);
  221. end;
  222. for SY := 1 to Comp.dy - 1 do
  223. begin
  224. if Y * Comp.dy + SY >= Image.Height then Break;
  225. // Replicate line
  226. NewPtr := @PByteArray(Image.Bits)[(Y * Comp.dy + SY) * LineBytes + Info.DestOffset];
  227. for X := 0 to Image.Width - 1 do
  228. begin
  229. CopySample(LineUpPtr, NewPtr, ChannelSize);
  230. Inc(LineUpPtr, BytesPerPixel);
  231. Inc(NewPtr, BytesPerPixel);
  232. end;
  233. end;
  234. end;
  235. end;
  236. end;
  237. procedure ConvertYCbCrToRGB(Pixels: PByte; NumPixels, BytesPerPixel: Integer);
  238. var
  239. I: Integer;
  240. PixPtr: PByte;
  241. CY, CB, CR: Byte;
  242. CYW, CBW, CRW: Word;
  243. begin
  244. PixPtr := Pixels;
  245. for I := 0 to NumPixels - 1 do
  246. begin
  247. if BytesPerPixel in [3, 4] then
  248. with PColor24Rec(PixPtr)^ do
  249. begin
  250. CY := R;
  251. CB := G;
  252. CR := B;
  253. YCbCrToRGB(CY, CB, CR, R, G, B);
  254. end
  255. else
  256. with PColor48Rec(PixPtr)^ do
  257. begin
  258. CYW := R;
  259. CBW := G;
  260. CRW := B;
  261. YCbCrToRGB16(CYW, CBW, CRW, R, G, B);
  262. end;
  263. Inc(PixPtr, BytesPerPixel);
  264. end;
  265. end;
  266. begin
  267. Result := False;
  268. image := nil;
  269. cio := nil;
  270. opj_set_default_decoder_parameters(@parameters);
  271. // Determine which codec to use
  272. FileType := GetFileType(Handle);
  273. case FileType of
  274. jtJP2: dinfo := opj_create_decompress(CODEC_JP2);
  275. jtJ2K: dinfo := opj_create_decompress(CODEC_J2K);
  276. jtJPT: dinfo := opj_create_decompress(CODEC_JPT);
  277. else
  278. Exit;
  279. end;
  280. // Set event manager to nil to avoid getting messages
  281. dinfo.event_mgr := nil;
  282. // Currently OpenJPEG can load images only from memory so we have to
  283. // preload whole input to mem buffer. Not good but no other way now.
  284. // At least we set stream pos to end of JP2 data after loading (we will now
  285. // the exact size by then).
  286. StartPos := GetIO.Tell(Handle);
  287. BufferSize := ImagingIO.GetInputSize(GetIO, Handle);
  288. GetMem(Buffer, BufferSize);
  289. SetLength(Images, 1);
  290. with GetIO, Images[0] do
  291. try
  292. Read(Handle, Buffer, BufferSize);
  293. cio := opj_cio_open(opj_common_ptr(dinfo), Buffer, BufferSize);
  294. opj_setup_decoder(dinfo, @parameters);
  295. // Decode image
  296. image := opj_decode(dinfo, cio);
  297. if image = nil then
  298. Exit;
  299. // Determine which Imaging data format to use according to
  300. // decoded image components
  301. case image.numcomps of
  302. 2: case image.comps[0].prec of
  303. 1..8: Format := ifA8Gray8;
  304. 9..16: Format := ifA16Gray16;
  305. end;
  306. 3: case image.comps[0].prec of
  307. 1..8: Format := ifR8G8B8;
  308. 9..16: Format := ifR16G16B16;
  309. end;
  310. 4: case image.comps[0].prec of
  311. 1..8: Format := ifA8R8G8B8;
  312. 9..16: Format := ifA16R16G16B16;
  313. end;
  314. else
  315. // There is only one component or there is more than four =>
  316. // just load the first one as gray
  317. case image.comps[0].prec of
  318. 1..8: Format := ifGray8;
  319. 9..16: Format := ifGray16;
  320. 17..32: Format := ifGray32;
  321. end;
  322. end;
  323. // Exit if no compatible format was found
  324. if Format = ifUnknown then
  325. Exit;
  326. NewImage(image.x1 - image.x0, image.y1 - image.y0, Format, Images[0]);
  327. Info := GetFormatInfo(Format);
  328. ChannelSize := Info.BytesPerPixel div Info.ChannelCount;
  329. SetLength(Channels, Info.ChannelCount);
  330. // Get information about all channels/components of JP2 file
  331. for I := 0 to Info.ChannelCount - 1 do
  332. begin
  333. // Get component type for this channel and based on this
  334. // determine where in dest image bits write this channel's data
  335. Channels[I].CompType := image.comps[I].comp_type;
  336. case Channels[I].CompType of
  337. COMPTYPE_UNKNOWN:
  338. begin
  339. if Info.ChannelCount <> 4 then
  340. begin
  341. // Missing CDEF box in file - usually BGR order
  342. Channels[I].DestOffset := image.numcomps - I - 1
  343. end
  344. else
  345. begin
  346. // Missing CDEF box in file - usually ABGR order
  347. if I = 3 then
  348. Channels[I].DestOffset := 3
  349. else
  350. Channels[I].DestOffset := image.numcomps - I - 2
  351. end;
  352. end;
  353. COMPTYPE_R: Channels[I].DestOffset := 2;
  354. COMPTYPE_G: Channels[I].DestOffset := 1;
  355. COMPTYPE_B: Channels[I].DestOffset := 0;
  356. COMPTYPE_CB: Channels[I].DestOffset := 1;
  357. COMPTYPE_CR: Channels[I].DestOffset := 0;
  358. COMPTYPE_OPACITY: Channels[I].DestOffset := 3;
  359. COMPTYPE_Y:
  360. case image.color_space of
  361. CLRSPC_SYCC: Channels[I].DestOffset := 2; // Y is intensity part of YCC
  362. CLRSPC_GRAY: Channels[I].DestOffset := 0; // Y is independent gray channel
  363. end;
  364. end;
  365. // Scale channel offset
  366. Channels[I].DestOffset := Channels[I].DestOffset * ChannelSize;
  367. // Signed componets must be scaled to [0, 1] interval (depends on precision)
  368. if image.comps[I].sgnd = 1 then
  369. Channels[I].Shift := 1 shl (image.comps[I].prec - 1);
  370. // Max channel values used to easier scaling of precisions
  371. // not supported by Imaging to supported ones (like 12bits etc.).
  372. Channels[I].SrcMaxValue := 1 shl image.comps[I].prec - 1;
  373. Channels[I].DestMaxValue := 1 shl (ChannelSize * 8) - 1;
  374. end;
  375. // Images components are stored separately in JP2, each can have
  376. // different dimensions, bitdepth, ...
  377. for I := 0 to Info.ChannelCount - 1 do
  378. ReadChannel(Images[0], Channels[I], image.comps[I], Info.BytesPerPixel);
  379. // If we have YCbCr image we need to convert it to RGB
  380. if (image.color_space = CLRSPC_SYCC) and (Info.ChannelCount in [3, 4]) then
  381. ConvertYCbCrToRGB(Bits, Width * Height, Info.BytesPerPixel);
  382. // Set the input position just after end of image
  383. Seek(Handle, StartPos + Cardinal(cio.bp) - Cardinal(cio.start), smFromBeginning);
  384. Result := True;
  385. finally
  386. opj_image_destroy(image);
  387. opj_destroy_decompress(dinfo);
  388. opj_cio_close(cio);
  389. FreeMem(Buffer);
  390. end;
  391. end;
  392. function TJpeg2000FileFormat.SaveData(Handle: TImagingHandle;
  393. const Images: TDynImageDataArray; Index: LongInt): Boolean;
  394. var
  395. TargetSize, Rate: Single;
  396. ImageToSave: TImageData;
  397. MustBeFreed: Boolean;
  398. Info: TImageFormatInfo;
  399. I, Z, InvZ, Channel, ChannelSize, NumPixels: Integer;
  400. Pix: PByte;
  401. image: popj_image_t;
  402. cio: popj_cio_t;
  403. cinfo: popj_cinfo_t;
  404. parameters: opj_cparameters_t;
  405. compparams: popj_image_cmptparm_array;
  406. ColorSpace: OPJ_COLOR_SPACE;
  407. function GetComponentType(Comp: Integer): OPJ_COMPONENT_TYPE;
  408. begin
  409. if Info.HasAlphaChannel and (Comp = Info.ChannelCount - 1) then
  410. Result := COMPTYPE_OPACITY
  411. else if Info.HasGrayChannel then
  412. Result := COMPTYPE_Y
  413. else if Comp = 2 then
  414. Result := COMPTYPE_B
  415. else if Comp = 1 then
  416. Result := COMPTYPE_G
  417. else if Comp = 0 then
  418. Result := COMPTYPE_R
  419. else
  420. Result := COMPTYPE_UNKNOWN;
  421. end;
  422. begin
  423. Result := False;
  424. image := nil;
  425. compparams := nil;
  426. cinfo := nil;
  427. cio := nil;
  428. // Makes image to save compatible with Jpeg 2000 saving capabilities
  429. if MakeCompatible(Images[Index], ImageToSave, MustBeFreed) then
  430. with GetIO, ImageToSave do
  431. try
  432. Info := GetFormatInfo(Format);
  433. ChannelSize := Info.BytesPerPixel div Info.ChannelCount;
  434. // Fill component info structures and then create OpenJPEG image
  435. GetMem(compparams, Info.ChannelCount * SizeOf(opj_image_comptparm));
  436. for I := 0 to Info.ChannelCount - 1 do
  437. with compparams[I] do
  438. begin
  439. dx := 1;
  440. dy := 1;
  441. w := Width;
  442. h := Height;
  443. prec := (Info.BytesPerPixel div Info.ChannelCount) * 8;
  444. bpp := prec;
  445. sgnd := 0;
  446. comp_type := GetComponentType(I);
  447. x0 := 0;
  448. y0 := 0;
  449. end;
  450. if Info.HasGrayChannel then
  451. ColorSpace := CLRSPC_GRAY
  452. else
  453. ColorSpace := CLRSPC_SRGB;
  454. image := opj_image_create(Info.ChannelCount, @compparams[0], ColorSpace);
  455. if image = nil then Exit;
  456. image.x1 := Width;
  457. image.y1 := Height;
  458. if FCodeStreamOnly then
  459. cinfo := opj_create_compress(CODEC_J2K)
  460. else
  461. cinfo := opj_create_compress(CODEC_JP2);
  462. // Set event manager to nil to avoid getting messages
  463. cinfo.event_mgr := nil;
  464. // Set compression parameters based current file format properties
  465. opj_set_default_encoder_parameters(@parameters);
  466. parameters.cod_format := Iff(FCodeStreamOnly, 0, 1);
  467. parameters.numresolution := 6;
  468. parameters.tcp_numlayers := 1;
  469. parameters.cp_disto_alloc := 1;
  470. if FLosslessCompression then
  471. begin
  472. // Set rate to 0 -> lossless
  473. parameters.tcp_rates[0] := 0;
  474. end
  475. else
  476. begin
  477. // Quality -> Rate computation taken from ImageMagick
  478. Rate := 100.0 / Sqr(115 - FQuality);
  479. NumPixels := Width * Height * Info.BytesPerPixel;
  480. TargetSize := (NumPixels * Rate) + 550 + (Info.ChannelCount - 1) * 142;
  481. parameters.tcp_rates[0] := 1.0 / (TargetSize / NumPixels);
  482. end;
  483. // Setup encoder
  484. opj_setup_encoder(cinfo, @parameters, image);
  485. // Fill component samples in data with values taken from
  486. // image pixels.
  487. // Components should be ordered like this: RGBA, YA, RGB, etc.
  488. for Channel := 0 to Info.ChannelCount - 1 do
  489. begin
  490. Z := Channel;
  491. InvZ := Info.ChannelCount - 1 - Z;
  492. if Info.HasAlphaChannel then
  493. begin
  494. if Channel = Info.ChannelCount - 1 then
  495. InvZ := Z
  496. else
  497. InvZ := Info.ChannelCount - 2 - Z;
  498. end;
  499. Pix := @PByteArray(Bits)[InvZ * ChannelSize];
  500. for I := 0 to Width * Height - 1 do
  501. begin
  502. case ChannelSize of
  503. 1: image.comps[Z].data[I] := Pix^;
  504. 2: image.comps[Z].data[I] := PWord(Pix)^;
  505. 4: UInt32(image.comps[Z].data[I]) := PUInt32(Pix)^;
  506. end;
  507. Inc(Pix, Info.BytesPerPixel);
  508. end;
  509. end;
  510. // Open OpenJPEG output
  511. cio := opj_cio_open(opj_common_ptr(cinfo), nil, 0);
  512. // Try to encode the image
  513. if not opj_encode(cinfo, cio, image, nil) then
  514. Exit;
  515. // Finally write buffer with encoded image to output
  516. Write(Handle, cio.buffer, cio_tell(cio));
  517. Result := True;
  518. finally
  519. if MustBeFreed then
  520. FreeImage(ImageToSave);
  521. opj_destroy_compress(cinfo);
  522. opj_image_destroy(image);
  523. opj_cio_close(cio);
  524. FreeMem(compparams);
  525. end;
  526. end;
  527. procedure TJpeg2000FileFormat.ConvertToSupported(var Image: TImageData;
  528. const Info: TImageFormatInfo);
  529. var
  530. ConvFormat: TImageFormat;
  531. begin
  532. if Info.IsFloatingPoint then
  533. ConvFormat := IffFormat(Info.ChannelCount = 1, ifGray16, ifA16R16G16B16)
  534. else if Info.HasGrayChannel then
  535. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA16Gray16, ifGray16)
  536. else if Info.IsIndexed then
  537. ConvFormat := ifA8R8G8B8
  538. else if Info.BytesPerPixel div Info.ChannelCount > 1 then
  539. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA16R16G16B16, ifR16G16B16)
  540. else
  541. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8R8G8B8, ifR8G8B8);
  542. ConvertImage(Image, ConvFormat);
  543. end;
  544. function TJpeg2000FileFormat.TestFormat(Handle: TImagingHandle): Boolean;
  545. begin
  546. Result := False;
  547. if Handle <> nil then
  548. Result := GetFileType(Handle) <> jtInvalid;
  549. end;
  550. initialization
  551. RegisterImageFileFormat(TJpeg2000FileFormat);
  552. {$ELSE}
  553. implementation
  554. begin
  555. {$IFEND}
  556. {
  557. File Notes:
  558. -- TODOS ----------------------------------------------------
  559. - nothing now
  560. -- 0.27 Changes ---------------------------------------------
  561. - by Hanno Hugenberg <[email protected]>
  562. - introduced the ImagingJpeg2000ScaleOutput parameter for keeping
  563. the original decoded images by avoiding upscaling of output images
  564. -- 0.26.3 Changes/Bug Fixes -----------------------------------
  565. - Rewritten JP2 loading part (based on PasJpeg2000) to be
  566. more readable (it's a bit faster too) and handled more JP2 files better:
  567. components with precisions like 12bit (not direct Imaging equivalent)
  568. are properly scaled, images/components with offsets are loaded ok.
  569. -- 0.24.3 Changes/Bug Fixes -----------------------------------
  570. - Alpha channels are now saved properly in FPC (GCC optimization issue),
  571. FPC lossy compression enabled again!
  572. - Added handling of component types (CDEF Box), JP2 images with alpha
  573. are now properly recognized by other applications.
  574. - Fixed wrong color space when saving grayscale images
  575. -- 0.21 Changes/Bug Fixes -----------------------------------
  576. - Removed ifGray32 from supported formats, OpenJPEG crashes when saving them.
  577. - Added Seek after loading to set input pos to the end of image.
  578. - Saving added lossy/lossless, quality option added.
  579. - Initial loading-only version created.
  580. }
  581. end.