Mercurial > hg > orthanc-wsi
annotate Framework/Jpeg2000Writer.cpp @ 288:a1efc5c39615
fix LSB build
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 12 Jul 2023 23:37:38 +0200 |
parents | 20a730889ae2 |
children | 7020852a8fa9 |
rev | line source |
---|---|
0 | 1 /** |
2 * Orthanc - A Lightweight, RESTful DICOM Store | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
254 | 5 * Copyright (C) 2017-2023 Osimis S.A., Belgium |
6 * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium | |
0 | 7 * |
8 * This program is free software: you can redistribute it and/or | |
9 * modify it under the terms of the GNU Affero General Public License | |
10 * as published by the Free Software Foundation, either version 3 of | |
11 * the License, or (at your option) any later version. | |
12 * | |
13 * This program is distributed in the hope that it will be useful, but | |
14 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 * Affero General Public License for more details. | |
17 * | |
18 * You should have received a copy of the GNU Affero General Public License | |
19 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
20 **/ | |
21 | |
22 | |
16
7a88c614be04
preparing for precompiled headers
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
23 #include "PrecompiledHeadersWSI.h" |
0 | 24 #include "Jpeg2000Writer.h" |
25 | |
192 | 26 #include <ChunkedBuffer.h> |
27 #include <OrthancException.h> | |
0 | 28 |
29 #include <openjpeg.h> | |
30 #include <string.h> | |
31 #include <vector> | |
32 | |
33 #if ORTHANC_OPENJPEG_MAJOR_VERSION == 1 | |
34 # define OPJ_CLRSPC_GRAY CLRSPC_GRAY | |
35 # define OPJ_CLRSPC_SRGB CLRSPC_SRGB | |
36 # define OPJ_CODEC_J2K CODEC_J2K | |
37 typedef opj_cinfo_t opj_codec_t; | |
38 typedef opj_cio_t opj_stream_t; | |
39 #elif ORTHANC_OPENJPEG_MAJOR_VERSION == 2 | |
40 #else | |
41 #error Unsupported version of OpenJpeg | |
42 #endif | |
43 | |
44 namespace OrthancWSI | |
45 { | |
46 namespace | |
47 { | |
48 class OpenJpegImage : public boost::noncopyable | |
49 { | |
50 private: | |
51 std::vector<opj_image_cmptparm_t> components_; | |
52 COLOR_SPACE colorspace_; | |
53 opj_image_t* image_; | |
54 | |
55 void SetupComponents(unsigned int width, | |
56 unsigned int height, | |
57 Orthanc::PixelFormat format) | |
58 { | |
59 switch (format) | |
60 { | |
61 case Orthanc::PixelFormat_Grayscale8: | |
62 colorspace_ = OPJ_CLRSPC_GRAY; | |
63 components_.resize(1); | |
64 break; | |
65 | |
66 case Orthanc::PixelFormat_RGB24: | |
67 colorspace_ = OPJ_CLRSPC_SRGB; | |
68 components_.resize(3); | |
69 break; | |
70 | |
71 default: | |
72 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
73 } | |
74 | |
75 for (size_t i = 0; i < components_.size(); i++) | |
76 { | |
77 memset(&components_[i], 0, sizeof(opj_image_cmptparm_t)); | |
78 components_[i].dx = 1; | |
79 components_[i].dy = 1; | |
80 components_[i].x0 = 0; | |
81 components_[i].y0 = 0; | |
82 components_[i].w = width; | |
83 components_[i].h = height; | |
84 components_[i].prec = 8; | |
85 components_[i].bpp = 8; | |
86 components_[i].sgnd = 0; | |
87 } | |
88 } | |
89 | |
90 void CopyRGB24(unsigned int width, | |
91 unsigned int height, | |
92 unsigned int pitch, | |
93 const void* buffer) | |
94 { | |
95 int32_t* r = image_->comps[0].data; | |
96 int32_t* g = image_->comps[1].data; | |
97 int32_t* b = image_->comps[2].data; | |
98 | |
99 for (unsigned int y = 0; y < height; y++) | |
100 { | |
101 const uint8_t *p = reinterpret_cast<const uint8_t*>(buffer) + y * pitch; | |
102 | |
103 for (unsigned int x = 0; x < width; x++) | |
104 { | |
105 *r = p[0]; | |
106 *g = p[1]; | |
107 *b = p[2]; | |
108 p += 3; | |
109 r++; | |
110 g++; | |
111 b++; | |
112 } | |
113 } | |
114 } | |
115 | |
116 void CopyGrayscale8(unsigned int width, | |
117 unsigned int height, | |
118 unsigned int pitch, | |
119 const void* buffer) | |
120 { | |
121 int32_t* q = image_->comps[0].data; | |
122 | |
123 for (unsigned int y = 0; y < height; y++) | |
124 { | |
125 const uint8_t *p = reinterpret_cast<const uint8_t*>(buffer) + y * pitch; | |
126 | |
127 for (unsigned int x = 0; x < width; x++) | |
128 { | |
129 *q = *p; | |
130 p++; | |
131 q++; | |
132 } | |
133 } | |
134 } | |
135 | |
136 public: | |
137 OpenJpegImage(unsigned int width, | |
138 unsigned int height, | |
139 unsigned int pitch, | |
140 Orthanc::PixelFormat format, | |
141 const void* buffer) : image_(NULL) | |
142 { | |
143 SetupComponents(width, height, format); | |
144 | |
145 image_ = opj_image_create(components_.size(), &components_[0], colorspace_); | |
146 if (!image_) | |
147 { | |
148 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
149 } | |
150 | |
151 image_->x0 = 0; | |
152 image_->y0 = 0; | |
153 image_->x1 = width; | |
154 image_->y1 = height; | |
155 | |
156 switch (format) | |
157 { | |
158 case Orthanc::PixelFormat_Grayscale8: | |
159 CopyGrayscale8(width, height, pitch, buffer); | |
160 break; | |
161 | |
162 case Orthanc::PixelFormat_RGB24: | |
163 CopyRGB24(width, height, pitch, buffer); | |
164 break; | |
165 | |
166 default: | |
167 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
168 } | |
169 } | |
170 | |
171 | |
172 ~OpenJpegImage() | |
173 { | |
174 if (image_) | |
175 { | |
176 opj_image_destroy(image_); | |
177 image_ = NULL; | |
178 } | |
179 } | |
180 | |
181 | |
182 opj_image_t* GetObject() | |
183 { | |
184 return image_; | |
185 } | |
186 }; | |
187 | |
188 | |
189 class OpenJpegEncoder : public boost::noncopyable | |
190 { | |
191 private: | |
192 opj_codec_t* cinfo_; | |
193 | |
194 public: | |
195 OpenJpegEncoder(opj_cparameters_t& parameters, | |
196 OpenJpegImage& image) : cinfo_(NULL) | |
197 { | |
198 cinfo_ = opj_create_compress(OPJ_CODEC_J2K); | |
199 if (!cinfo_) | |
200 { | |
201 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
202 } | |
203 | |
204 opj_setup_encoder(cinfo_, ¶meters, image.GetObject()); | |
205 } | |
206 | |
207 ~OpenJpegEncoder() | |
208 { | |
209 if (cinfo_ != NULL) | |
210 { | |
211 #if ORTHANC_OPENJPEG_MAJOR_VERSION == 1 | |
212 opj_destroy_compress(cinfo_); | |
213 #else | |
214 opj_destroy_codec(cinfo_); | |
215 #endif | |
216 cinfo_ = NULL; | |
217 } | |
218 } | |
219 | |
220 opj_codec_t* GetObject() | |
221 { | |
222 return cinfo_; | |
223 } | |
224 }; | |
225 | |
226 | |
227 class OpenJpegOutput : public boost::noncopyable | |
228 { | |
229 private: | |
230 opj_stream_t* cio_; | |
231 | |
232 #if ORTHANC_OPENJPEG_MAJOR_VERSION == 2 | |
233 Orthanc::ChunkedBuffer buffer_; | |
234 | |
235 static void Free(void *userData) | |
236 { | |
237 } | |
238 | |
239 static OPJ_SIZE_T Write(void *buffer, | |
240 OPJ_SIZE_T size, | |
241 void *userData) | |
242 { | |
243 OpenJpegOutput* that = reinterpret_cast<OpenJpegOutput*>(userData); | |
244 that->buffer_.AddChunk(reinterpret_cast<const char*>(buffer), size); | |
245 return size; | |
246 } | |
247 #endif | |
248 | |
249 public: | |
145 | 250 explicit OpenJpegOutput(OpenJpegEncoder& encoder) : |
251 cio_(NULL) | |
0 | 252 { |
253 #if ORTHANC_OPENJPEG_MAJOR_VERSION == 1 | |
254 cio_ = opj_cio_open(reinterpret_cast<opj_common_ptr>(encoder.GetObject()), NULL, 0); | |
255 #else | |
256 cio_ = opj_stream_default_create(0 /* output stream */); | |
257 if (!cio_) | |
258 { | |
259 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
260 } | |
261 | |
262 opj_stream_set_user_data(cio_, this, Free); | |
263 opj_stream_set_write_function(cio_, Write); | |
264 #endif | |
265 } | |
266 | |
267 ~OpenJpegOutput() | |
268 { | |
269 if (cio_) | |
270 { | |
271 #if ORTHANC_OPENJPEG_MAJOR_VERSION == 1 | |
272 opj_cio_close(cio_); | |
273 #else | |
274 opj_stream_destroy(cio_); | |
275 #endif | |
276 cio_ = NULL; | |
277 } | |
278 } | |
279 | |
280 opj_stream_t* GetObject() | |
281 { | |
282 return cio_; | |
283 } | |
284 | |
285 void Flatten(std::string& target) | |
286 { | |
287 #if ORTHANC_OPENJPEG_MAJOR_VERSION == 1 | |
288 target.assign(reinterpret_cast<const char*>(cio_->buffer), cio_tell(cio_)); | |
289 #else | |
290 buffer_.Flatten(target); | |
291 #endif | |
292 } | |
293 }; | |
294 } | |
295 | |
296 | |
297 static void SetupParameters(opj_cparameters_t& parameters, | |
298 Orthanc::PixelFormat format, | |
299 bool isLossless) | |
300 { | |
301 opj_set_default_encoder_parameters(¶meters); | |
302 parameters.cp_disto_alloc = 1; | |
303 | |
304 if (isLossless) | |
305 { | |
306 parameters.tcp_numlayers = 1; | |
307 parameters.tcp_rates[0] = 0; | |
308 } | |
309 else | |
310 { | |
311 parameters.tcp_numlayers = 5; | |
312 parameters.tcp_rates[0] = 1920; | |
313 parameters.tcp_rates[1] = 480; | |
314 parameters.tcp_rates[2] = 120; | |
315 parameters.tcp_rates[3] = 30; | |
316 parameters.tcp_rates[4] = 10; | |
317 parameters.irreversible = 1; | |
318 | |
319 if (format == Orthanc::PixelFormat_RGB24 || | |
320 format == Orthanc::PixelFormat_RGBA32) | |
321 { | |
322 // This must be set to 1 if the number of color channels is >= 3 | |
323 parameters.tcp_mct = 1; | |
324 } | |
325 } | |
326 | |
327 parameters.cp_comment = const_cast<char*>(""); | |
328 } | |
329 | |
330 | |
331 void Jpeg2000Writer::WriteToMemoryInternal(std::string& compressed, | |
332 unsigned int width, | |
333 unsigned int height, | |
334 unsigned int pitch, | |
335 Orthanc::PixelFormat format, | |
336 const void* buffer) | |
337 { | |
338 if (format != Orthanc::PixelFormat_Grayscale8 && | |
339 format != Orthanc::PixelFormat_RGB24 && | |
340 format != Orthanc::PixelFormat_RGBA32) | |
341 { | |
342 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
343 } | |
344 | |
345 opj_cparameters_t parameters; | |
346 SetupParameters(parameters, format, isLossless_); | |
347 | |
348 OpenJpegImage image(width, height, pitch, format, buffer); | |
349 OpenJpegEncoder encoder(parameters, image); | |
350 OpenJpegOutput output(encoder); | |
351 | |
352 #if ORTHANC_OPENJPEG_MAJOR_VERSION == 1 | |
353 if (!opj_encode(encoder.GetObject(), output.GetObject(), image.GetObject(), parameters.index)) | |
354 { | |
355 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
356 } | |
357 #else | |
358 if (!opj_start_compress(encoder.GetObject(), image.GetObject(), output.GetObject()) || | |
359 !opj_encode(encoder.GetObject(), output.GetObject()) || | |
360 !opj_end_compress(encoder.GetObject(), output.GetObject())) | |
361 { | |
362 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
363 } | |
364 #endif | |
365 | |
366 output.Flatten(compressed); | |
367 } | |
368 } |