comparison Framework/Toolbox/OrthancSlicesLoader.cpp @ 252:40b21c1f8b8d am-2

more usage of IObservable/IObserver
author am@osimis.io
date Tue, 03 Jul 2018 10:26:56 +0200
parents 192e6e349e69
children 9afafb192180
comparison
equal deleted inserted replaced
251:192e6e349e69 252:40b21c1f8b8d
11 * 11 *
12 * This program is distributed in the hope that it will be useful, but 12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of 13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Affero General Public License for more details. 15 * Affero General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Affero General Public License 17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **/ 19 **/
20 20
21 21
45 * https://stackoverflow.com/a/34571089/881731 45 * https://stackoverflow.com/a/34571089/881731
46 **/ 46 **/
47 static std::string base64_decode(const std::string &in) 47 static std::string base64_decode(const std::string &in)
48 { 48 {
49 std::string out; 49 std::string out;
50 50
51 std::vector<int> T(256,-1); 51 std::vector<int> T(256,-1);
52 for (int i=0; i<64; i++) T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i; 52 for (int i=0; i<64; i++) T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
53 53
54 int val=0, valb=-8; 54 int val=0, valb=-8;
55 for (size_t i = 0; i < in.size(); i++) { 55 for (size_t i = 0; i < in.size(); i++) {
56 unsigned char c = in[i]; 56 unsigned char c = in[i];
57 if (T[c] == -1) break; 57 if (T[c] == -1) break;
58 val = (val<<6) + T[c]; 58 val = (val<<6) + T[c];
76 unsigned int frame_; 76 unsigned int frame_;
77 unsigned int sliceIndex_; 77 unsigned int sliceIndex_;
78 const Slice* slice_; 78 const Slice* slice_;
79 std::string instanceId_; 79 std::string instanceId_;
80 SliceImageQuality quality_; 80 SliceImageQuality quality_;
81 81
82 Operation(Mode mode) : 82 Operation(Mode mode) :
83 mode_(mode) 83 mode_(mode)
84 { 84 {
85 } 85 }
86 86
87 public: 87 public:
88 Mode GetMode() const 88 Mode GetMode() const
89 { 89 {
90 return mode_; 90 return mode_;
91 } 91 }
92 92
93 SliceImageQuality GetQuality() const 93 SliceImageQuality GetQuality() const
94 { 94 {
95 assert(mode_ == Mode_LoadImage || 95 assert(mode_ == Mode_LoadImage ||
96 mode_ == Mode_LoadRawImage); 96 mode_ == Mode_LoadRawImage);
97 return quality_; 97 return quality_;
98 } 98 }
99 99
100 unsigned int GetSliceIndex() const 100 unsigned int GetSliceIndex() const
101 { 101 {
102 assert(mode_ == Mode_LoadImage || 102 assert(mode_ == Mode_LoadImage ||
103 mode_ == Mode_LoadRawImage); 103 mode_ == Mode_LoadRawImage);
104 return sliceIndex_; 104 return sliceIndex_;
105 } 105 }
106 106
107 const Slice& GetSlice() const 107 const Slice& GetSlice() const
108 { 108 {
109 assert(mode_ == Mode_LoadImage || 109 assert(mode_ == Mode_LoadImage ||
110 mode_ == Mode_LoadRawImage); 110 mode_ == Mode_LoadRawImage);
111 assert(slice_ != NULL); 111 assert(slice_ != NULL);
112 return *slice_; 112 return *slice_;
113 } 113 }
114 114
115 unsigned int GetFrame() const 115 unsigned int GetFrame() const
116 { 116 {
117 assert(mode_ == Mode_FrameGeometry); 117 assert(mode_ == Mode_FrameGeometry);
118 return frame_; 118 return frame_;
119 } 119 }
120 120
121 const std::string& GetInstanceId() const 121 const std::string& GetInstanceId() const
122 { 122 {
123 assert(mode_ == Mode_FrameGeometry || 123 assert(mode_ == Mode_FrameGeometry ||
124 mode_ == Mode_InstanceGeometry); 124 mode_ == Mode_InstanceGeometry);
125 return instanceId_; 125 return instanceId_;
126 } 126 }
127 127
128 static Operation* DownloadSeriesGeometry() 128 static Operation* DownloadSeriesGeometry()
129 { 129 {
130 return new Operation(Mode_SeriesGeometry); 130 return new Operation(Mode_SeriesGeometry);
131 } 131 }
132 132
133 static Operation* DownloadInstanceGeometry(const std::string& instanceId) 133 static Operation* DownloadInstanceGeometry(const std::string& instanceId)
134 { 134 {
135 std::auto_ptr<Operation> operation(new Operation(Mode_InstanceGeometry)); 135 std::auto_ptr<Operation> operation(new Operation(Mode_InstanceGeometry));
136 operation->instanceId_ = instanceId; 136 operation->instanceId_ = instanceId;
137 return operation.release(); 137 return operation.release();
138 } 138 }
139 139
140 static Operation* DownloadFrameGeometry(const std::string& instanceId, 140 static Operation* DownloadFrameGeometry(const std::string& instanceId,
141 unsigned int frame) 141 unsigned int frame)
142 { 142 {
143 std::auto_ptr<Operation> operation(new Operation(Mode_FrameGeometry)); 143 std::auto_ptr<Operation> operation(new Operation(Mode_FrameGeometry));
144 operation->instanceId_ = instanceId; 144 operation->instanceId_ = instanceId;
145 operation->frame_ = frame; 145 operation->frame_ = frame;
146 return operation.release(); 146 return operation.release();
147 } 147 }
148 148
149 static Operation* DownloadSliceImage(unsigned int sliceIndex, 149 static Operation* DownloadSliceImage(unsigned int sliceIndex,
150 const Slice& slice, 150 const Slice& slice,
151 SliceImageQuality quality) 151 SliceImageQuality quality)
152 { 152 {
153 std::auto_ptr<Operation> tmp(new Operation(Mode_LoadImage)); 153 std::auto_ptr<Operation> tmp(new Operation(Mode_LoadImage));
154 tmp->sliceIndex_ = sliceIndex; 154 tmp->sliceIndex_ = sliceIndex;
155 tmp->slice_ = &slice; 155 tmp->slice_ = &slice;
156 tmp->quality_ = quality; 156 tmp->quality_ = quality;
157 return tmp.release(); 157 return tmp.release();
158 } 158 }
159 159
160 static Operation* DownloadSliceRawImage(unsigned int sliceIndex, 160 static Operation* DownloadSliceRawImage(unsigned int sliceIndex,
161 const Slice& slice) 161 const Slice& slice)
162 { 162 {
163 std::auto_ptr<Operation> tmp(new Operation(Mode_LoadRawImage)); 163 std::auto_ptr<Operation> tmp(new Operation(Mode_LoadRawImage));
164 tmp->sliceIndex_ = sliceIndex; 164 tmp->sliceIndex_ = sliceIndex;
165 tmp->slice_ = &slice; 165 tmp->slice_ = &slice;
166 tmp->quality_ = SliceImageQuality_Full; 166 tmp->quality_ = SliceImageQuality_Full;
167 return tmp.release(); 167 return tmp.release();
168 } 168 }
169 }; 169 };
170 170
171 171
172 class OrthancSlicesLoader::WebCallback : public IWebService::ICallback 172 class OrthancSlicesLoader::WebCallback : public IWebService::ICallback
173 { 173 {
174 private: 174 private:
175 OrthancSlicesLoader& that_; 175 OrthancSlicesLoader& that_;
176 176
177 public: 177 public:
178 WebCallback(MessageBroker& broker, OrthancSlicesLoader& that) : 178 WebCallback(MessageBroker& broker, OrthancSlicesLoader& that) :
179 IWebService::ICallback(broker), 179 IWebService::ICallback(broker),
180 that_(that) 180 that_(that)
181 { 181 {
182 } 182 }
183 183
184 virtual void OnHttpRequestSuccess(const std::string& uri, 184 virtual void OnHttpRequestSuccess(const std::string& uri,
185 const void* answer, 185 const void* answer,
186 size_t answerSize, 186 size_t answerSize,
187 Orthanc::IDynamicObject* payload) 187 Orthanc::IDynamicObject* payload)
188 { 188 {
189 std::auto_ptr<Operation> operation(dynamic_cast<Operation*>(payload)); 189 std::auto_ptr<Operation> operation(dynamic_cast<Operation*>(payload));
190 190
191 switch (operation->GetMode()) 191 switch (operation->GetMode())
192 { 192 {
193 case Mode_SeriesGeometry: 193 case Mode_SeriesGeometry:
194 that_.ParseSeriesGeometry(answer, answerSize); 194 that_.ParseSeriesGeometry(answer, answerSize);
195 break;
196
197 case Mode_InstanceGeometry:
198 that_.ParseInstanceGeometry(operation->GetInstanceId(), answer, answerSize);
199 break;
200
201 case Mode_FrameGeometry:
202 that_.ParseFrameGeometry(operation->GetInstanceId(),
203 operation->GetFrame(), answer, answerSize);
204 break;
205
206 case Mode_LoadImage:
207 switch (operation->GetQuality())
208 {
209 case SliceImageQuality_Full:
210 that_.ParseSliceImagePng(*operation, answer, answerSize);
195 break; 211 break;
196 212
197 case Mode_InstanceGeometry: 213 case SliceImageQuality_Jpeg50:
198 that_.ParseInstanceGeometry(operation->GetInstanceId(), answer, answerSize); 214 case SliceImageQuality_Jpeg90:
199 break; 215 case SliceImageQuality_Jpeg95:
200 216 that_.ParseSliceImageJpeg(*operation, answer, answerSize);
201 case Mode_FrameGeometry:
202 that_.ParseFrameGeometry(operation->GetInstanceId(),
203 operation->GetFrame(), answer, answerSize);
204 break;
205
206 case Mode_LoadImage:
207 switch (operation->GetQuality())
208 {
209 case SliceImageQuality_Full:
210 that_.ParseSliceImagePng(*operation, answer, answerSize);
211 break;
212
213 case SliceImageQuality_Jpeg50:
214 case SliceImageQuality_Jpeg90:
215 case SliceImageQuality_Jpeg95:
216 that_.ParseSliceImageJpeg(*operation, answer, answerSize);
217 break;
218
219 default:
220 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
221 }
222
223 break;
224
225 case Mode_LoadRawImage:
226 that_.ParseSliceRawImage(*operation, answer, answerSize);
227 break;
228
229 default:
230 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
231 }
232 }
233
234 virtual void OnHttpRequestError(const std::string& uri,
235 Orthanc::IDynamicObject* payload)
236 {
237 std::auto_ptr<Operation> operation(dynamic_cast<Operation*>(payload));
238 LOG(ERROR) << "Cannot download " << uri;
239
240 switch (operation->GetMode())
241 {
242 case Mode_FrameGeometry:
243 case Mode_SeriesGeometry:
244 that_.userCallback_.NotifyGeometryError(that_);
245 that_.state_ = State_Error;
246 break;
247
248 case Mode_LoadImage:
249 that_.userCallback_.NotifySliceImageError(that_, operation->GetSliceIndex(),
250 operation->GetSlice(),
251 operation->GetQuality());
252 break; 217 break;
253 218
254 default: 219 default:
255 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); 220 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
256 } 221 }
257 } 222
223 break;
224
225 case Mode_LoadRawImage:
226 that_.ParseSliceRawImage(*operation, answer, answerSize);
227 break;
228
229 default:
230 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
231 }
232 }
233
234 virtual void OnHttpRequestError(const std::string& uri,
235 Orthanc::IDynamicObject* payload)
236 {
237 std::auto_ptr<Operation> operation(dynamic_cast<Operation*>(payload));
238 LOG(ERROR) << "Cannot download " << uri;
239
240 switch (operation->GetMode())
241 {
242 case Mode_FrameGeometry:
243 case Mode_SeriesGeometry:
244 that_.userCallback_.OnSliceGeometryError(that_);
245 that_.state_ = State_Error;
246 break;
247
248 case Mode_LoadImage:
249 that_.userCallback_.OnSliceImageError(that_, operation->GetSliceIndex(),
250 operation->GetSlice(),
251 operation->GetQuality());
252 break;
253
254 default:
255 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
256 }
257 }
258 }; 258 };
259 259
260 void OrthancSlicesLoader::ISliceLoaderObserver::HandleMessage(IObservable& from, const IMessage& message)
261 {
262 switch (message.GetType())
263 {
264 case MessageType_SliceGeometryReady:
265 OnSliceGeometryReady(dynamic_cast<OrthancSlicesLoader&>(from));
266 break;
267 case MessageType_SliceGeometryError:
268 OnSliceGeometryError(dynamic_cast<OrthancSlicesLoader&>(from));
269 break;
270 case MessageType_SliceImageReady:
271 {
272 const SliceImageReadyMessage& msg = dynamic_cast<const SliceImageReadyMessage&>(message);
273 OnSliceImageReady(dynamic_cast<OrthancSlicesLoader&>(from),
274 msg.sliceIndex_,
275 msg.slice_,
276 msg.image_,
277 msg.effectiveQuality_);
278 }; break;
279 case MessageType_SliceImageError:
280 {
281 const SliceImageErrorMessage& msg = dynamic_cast<const SliceImageErrorMessage&>(message);
282 OnSliceImageError(dynamic_cast<OrthancSlicesLoader&>(from),
283 msg.sliceIndex_,
284 msg.slice_,
285 msg.effectiveQuality_);
286 }; break;
287 default:
288 VLOG("unhandled message type" << message.GetType());
289 }
290 }
260 291
261 292
262 void OrthancSlicesLoader::NotifySliceImageSuccess(const Operation& operation, 293 void OrthancSlicesLoader::NotifySliceImageSuccess(const Operation& operation,
263 std::auto_ptr<Orthanc::ImageAccessor>& image) const 294 std::auto_ptr<Orthanc::ImageAccessor>& image) const
264 { 295 {
266 { 297 {
267 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); 298 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
268 } 299 }
269 else 300 else
270 { 301 {
271 userCallback_.NotifySliceImageReady 302 userCallback_.OnSliceImageReady
272 (*this, operation.GetSliceIndex(), operation.GetSlice(), image, operation.GetQuality()); 303 (*this, operation.GetSliceIndex(), operation.GetSlice(), image, operation.GetQuality());
273 } 304 }
274 } 305 }
275 306
276 307
277 void OrthancSlicesLoader::NotifySliceImageError(const Operation& operation) const 308 void OrthancSlicesLoader::NotifySliceImageError(const Operation& operation) const
278 { 309 {
279 userCallback_.NotifySliceImageError 310 userCallback_.OnSliceImageError
280 (*this, operation.GetSliceIndex(), operation.GetSlice(), operation.GetQuality()); 311 (*this, operation.GetSliceIndex(), operation.GetSlice(), operation.GetQuality());
281 } 312 }
282 313
283 314
284 void OrthancSlicesLoader::SortAndFinalizeSlices() 315 void OrthancSlicesLoader::SortAndFinalizeSlices()
285 { 316 {
286 bool ok = false; 317 bool ok = false;
287 318
288 if (slices_.GetSliceCount() > 0) 319 if (slices_.GetSliceCount() > 0)
294 slices_.SetNormal(normal); 325 slices_.SetNormal(normal);
295 slices_.Sort(); 326 slices_.Sort();
296 ok = true; 327 ok = true;
297 } 328 }
298 } 329 }
299 330
300 state_ = State_GeometryReady; 331 state_ = State_GeometryReady;
301 332
302 if (ok) 333 if (ok)
303 { 334 {
304 LOG(INFO) << "Loaded a series with " << slices_.GetSliceCount() << " slice(s)"; 335 LOG(INFO) << "Loaded a series with " << slices_.GetSliceCount() << " slice(s)";
305 userCallback_.NotifyGeometryReady(*this); 336 userCallback_.OnSliceGeometryReady(*this);
306 } 337 }
307 else 338 else
308 { 339 {
309 LOG(ERROR) << "This series is empty"; 340 LOG(ERROR) << "This series is empty";
310 userCallback_.NotifyGeometryError(*this); 341 userCallback_.OnSliceGeometryError(*this);
311 } 342 }
312 } 343 }
313 344
314 345
315 void OrthancSlicesLoader::ParseSeriesGeometry(const void* answer, 346 void OrthancSlicesLoader::ParseSeriesGeometry(const void* answer,
316 size_t size) 347 size_t size)
317 { 348 {
318 Json::Value series; 349 Json::Value series;
319 if (!MessagingToolbox::ParseJson(series, answer, size) || 350 if (!MessagingToolbox::ParseJson(series, answer, size) ||
320 series.type() != Json::objectValue) 351 series.type() != Json::objectValue)
321 { 352 {
322 userCallback_.NotifyGeometryError(*this); 353 userCallback_.OnSliceGeometryError(*this);
323 return; 354 return;
324 } 355 }
325 356
326 Json::Value::Members instances = series.getMemberNames(); 357 Json::Value::Members instances = series.getMemberNames();
327 358
328 slices_.Reserve(instances.size()); 359 slices_.Reserve(instances.size());
329 360
330 for (size_t i = 0; i < instances.size(); i++) 361 for (size_t i = 0; i < instances.size(); i++)
331 { 362 {
332 OrthancPlugins::FullOrthancDataset dataset(series[instances[i]]); 363 OrthancPlugins::FullOrthancDataset dataset(series[instances[i]]);
333 364
334 Orthanc::DicomMap dicom; 365 Orthanc::DicomMap dicom;
335 MessagingToolbox::ConvertDataset(dicom, dataset); 366 MessagingToolbox::ConvertDataset(dicom, dataset);
336 367
337 unsigned int frames; 368 unsigned int frames;
338 if (!dicom.ParseUnsignedInteger32(frames, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES)) 369 if (!dicom.ParseUnsignedInteger32(frames, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES))
339 { 370 {
340 frames = 1; 371 frames = 1;
341 } 372 }
342 373
343 for (unsigned int frame = 0; frame < frames; frame++) 374 for (unsigned int frame = 0; frame < frames; frame++)
344 { 375 {
345 std::auto_ptr<Slice> slice(new Slice); 376 std::auto_ptr<Slice> slice(new Slice);
346 if (slice->ParseOrthancFrame(dicom, instances[i], frame)) 377 if (slice->ParseOrthancFrame(dicom, instances[i], frame))
347 { 378 {
351 { 382 {
352 LOG(WARNING) << "Skipping invalid frame " << frame << " within instance " << instances[i]; 383 LOG(WARNING) << "Skipping invalid frame " << frame << " within instance " << instances[i];
353 } 384 }
354 } 385 }
355 } 386 }
356 387
357 SortAndFinalizeSlices(); 388 SortAndFinalizeSlices();
358 } 389 }
359 390
360 391
361 void OrthancSlicesLoader::ParseInstanceGeometry(const std::string& instanceId, 392 void OrthancSlicesLoader::ParseInstanceGeometry(const std::string& instanceId,
362 const void* answer, 393 const void* answer,
363 size_t size) 394 size_t size)
364 { 395 {
365 Json::Value tags; 396 Json::Value tags;
366 if (!MessagingToolbox::ParseJson(tags, answer, size) || 397 if (!MessagingToolbox::ParseJson(tags, answer, size) ||
367 tags.type() != Json::objectValue) 398 tags.type() != Json::objectValue)
368 { 399 {
369 userCallback_.NotifyGeometryError(*this); 400 userCallback_.OnSliceGeometryError(*this);
370 return; 401 return;
371 } 402 }
372 403
373 OrthancPlugins::FullOrthancDataset dataset(tags); 404 OrthancPlugins::FullOrthancDataset dataset(tags);
374 405
375 Orthanc::DicomMap dicom; 406 Orthanc::DicomMap dicom;
376 MessagingToolbox::ConvertDataset(dicom, dataset); 407 MessagingToolbox::ConvertDataset(dicom, dataset);
377 408
378 unsigned int frames; 409 unsigned int frames;
379 if (!dicom.ParseUnsignedInteger32(frames, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES)) 410 if (!dicom.ParseUnsignedInteger32(frames, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES))
380 { 411 {
381 frames = 1; 412 frames = 1;
382 } 413 }
383 414
384 LOG(INFO) << "Instance " << instanceId << " contains " << frames << " frame(s)"; 415 LOG(INFO) << "Instance " << instanceId << " contains " << frames << " frame(s)";
385 416
386 for (unsigned int frame = 0; frame < frames; frame++) 417 for (unsigned int frame = 0; frame < frames; frame++)
387 { 418 {
388 std::auto_ptr<Slice> slice(new Slice); 419 std::auto_ptr<Slice> slice(new Slice);
389 if (slice->ParseOrthancFrame(dicom, instanceId, frame)) 420 if (slice->ParseOrthancFrame(dicom, instanceId, frame))
390 { 421 {
391 slices_.AddSlice(slice.release()); 422 slices_.AddSlice(slice.release());
392 } 423 }
393 else 424 else
394 { 425 {
395 LOG(WARNING) << "Skipping invalid multi-frame instance " << instanceId; 426 LOG(WARNING) << "Skipping invalid multi-frame instance " << instanceId;
396 userCallback_.NotifyGeometryError(*this); 427 userCallback_.OnSliceGeometryError(*this);
397 return; 428 return;
398 } 429 }
399 } 430 }
400 431
401 SortAndFinalizeSlices(); 432 SortAndFinalizeSlices();
402 } 433 }
403 434
404 435
405 void OrthancSlicesLoader::ParseFrameGeometry(const std::string& instanceId, 436 void OrthancSlicesLoader::ParseFrameGeometry(const std::string& instanceId,
406 unsigned int frame, 437 unsigned int frame,
407 const void* answer, 438 const void* answer,
408 size_t size) 439 size_t size)
409 { 440 {
410 Json::Value tags; 441 Json::Value tags;
411 if (!MessagingToolbox::ParseJson(tags, answer, size) || 442 if (!MessagingToolbox::ParseJson(tags, answer, size) ||
412 tags.type() != Json::objectValue) 443 tags.type() != Json::objectValue)
413 { 444 {
414 userCallback_.NotifyGeometryError(*this); 445 userCallback_.OnSliceGeometryError(*this);
415 return; 446 return;
416 } 447 }
417 448
418 OrthancPlugins::FullOrthancDataset dataset(tags); 449 OrthancPlugins::FullOrthancDataset dataset(tags);
419 450
420 state_ = State_GeometryReady; 451 state_ = State_GeometryReady;
421 452
422 Orthanc::DicomMap dicom; 453 Orthanc::DicomMap dicom;
423 MessagingToolbox::ConvertDataset(dicom, dataset); 454 MessagingToolbox::ConvertDataset(dicom, dataset);
424 455
425 std::auto_ptr<Slice> slice(new Slice); 456 std::auto_ptr<Slice> slice(new Slice);
426 if (slice->ParseOrthancFrame(dicom, instanceId, frame)) 457 if (slice->ParseOrthancFrame(dicom, instanceId, frame))
427 { 458 {
428 LOG(INFO) << "Loaded instance " << instanceId; 459 LOG(INFO) << "Loaded instance " << instanceId;
429 slices_.AddSlice(slice.release()); 460 slices_.AddSlice(slice.release());
430 userCallback_.NotifyGeometryReady(*this); 461 userCallback_.OnSliceGeometryReady(*this);
431 } 462 }
432 else 463 else
433 { 464 {
434 LOG(WARNING) << "Skipping invalid instance " << instanceId; 465 LOG(WARNING) << "Skipping invalid instance " << instanceId;
435 userCallback_.NotifyGeometryError(*this); 466 userCallback_.OnSliceGeometryError(*this);
436 } 467 }
437 } 468 }
438 469
439 470
440 void OrthancSlicesLoader::ParseSliceImagePng(const Operation& operation, 471 void OrthancSlicesLoader::ParseSliceImagePng(const Operation& operation,
441 const void* answer, 472 const void* answer,
442 size_t size) 473 size_t size)
443 { 474 {
444 std::auto_ptr<Orthanc::ImageAccessor> image; 475 std::auto_ptr<Orthanc::ImageAccessor> image;
445 476
446 try 477 try
447 { 478 {
448 image.reset(new Orthanc::PngReader); 479 image.reset(new Orthanc::PngReader);
449 dynamic_cast<Orthanc::PngReader&>(*image).ReadFromMemory(answer, size); 480 dynamic_cast<Orthanc::PngReader&>(*image).ReadFromMemory(answer, size);
450 } 481 }
451 catch (Orthanc::OrthancException&) 482 catch (Orthanc::OrthancException&)
452 { 483 {
453 NotifySliceImageError(operation); 484 NotifySliceImageError(operation);
454 return; 485 return;
455 } 486 }
456 487
457 if (image->GetWidth() != operation.GetSlice().GetWidth() || 488 if (image->GetWidth() != operation.GetSlice().GetWidth() ||
458 image->GetHeight() != operation.GetSlice().GetHeight()) 489 image->GetHeight() != operation.GetSlice().GetHeight())
459 { 490 {
460 NotifySliceImageError(operation); 491 NotifySliceImageError(operation);
461 return; 492 return;
462 } 493 }
463 494
464 if (operation.GetSlice().GetConverter().GetExpectedPixelFormat() == 495 if (operation.GetSlice().GetConverter().GetExpectedPixelFormat() ==
465 Orthanc::PixelFormat_SignedGrayscale16) 496 Orthanc::PixelFormat_SignedGrayscale16)
466 { 497 {
467 if (image->GetFormat() == Orthanc::PixelFormat_Grayscale16) 498 if (image->GetFormat() == Orthanc::PixelFormat_Grayscale16)
468 { 499 {
472 { 503 {
473 NotifySliceImageError(operation); 504 NotifySliceImageError(operation);
474 return; 505 return;
475 } 506 }
476 } 507 }
477 508
478 NotifySliceImageSuccess(operation, image); 509 NotifySliceImageSuccess(operation, image);
479 } 510 }
480 511
481 512
482 void OrthancSlicesLoader::ParseSliceImageJpeg(const Operation& operation, 513 void OrthancSlicesLoader::ParseSliceImageJpeg(const Operation& operation,
483 const void* answer, 514 const void* answer,
484 size_t size) 515 size_t size)
485 { 516 {
490 encoded["Orthanc"].type() != Json::objectValue) 521 encoded["Orthanc"].type() != Json::objectValue)
491 { 522 {
492 NotifySliceImageError(operation); 523 NotifySliceImageError(operation);
493 return; 524 return;
494 } 525 }
495 526
496 Json::Value& info = encoded["Orthanc"]; 527 Json::Value& info = encoded["Orthanc"];
497 if (!info.isMember("PixelData") || 528 if (!info.isMember("PixelData") ||
498 !info.isMember("Stretched") || 529 !info.isMember("Stretched") ||
499 !info.isMember("Compression") || 530 !info.isMember("Compression") ||
500 info["Compression"].type() != Json::stringValue || 531 info["Compression"].type() != Json::stringValue ||
503 info["Compression"].asString() != "Jpeg") 534 info["Compression"].asString() != "Jpeg")
504 { 535 {
505 NotifySliceImageError(operation); 536 NotifySliceImageError(operation);
506 return; 537 return;
507 } 538 }
508 539
509 bool isSigned = false; 540 bool isSigned = false;
510 bool isStretched = info["Stretched"].asBool(); 541 bool isStretched = info["Stretched"].asBool();
511 542
512 if (info.isMember("IsSigned")) 543 if (info.isMember("IsSigned"))
513 { 544 {
514 if (info["IsSigned"].type() != Json::booleanValue) 545 if (info["IsSigned"].type() != Json::booleanValue)
515 { 546 {
516 NotifySliceImageError(operation); 547 NotifySliceImageError(operation);
517 return; 548 return;
518 } 549 }
519 else 550 else
520 { 551 {
521 isSigned = info["IsSigned"].asBool(); 552 isSigned = info["IsSigned"].asBool();
522 } 553 }
523 } 554 }
524 555
525 std::auto_ptr<Orthanc::ImageAccessor> reader; 556 std::auto_ptr<Orthanc::ImageAccessor> reader;
526 557
527 { 558 {
528 std::string jpeg; 559 std::string jpeg;
529 //Orthanc::Toolbox::DecodeBase64(jpeg, info["PixelData"].asString()); 560 //Orthanc::Toolbox::DecodeBase64(jpeg, info["PixelData"].asString());
530 jpeg = base64_decode(info["PixelData"].asString()); 561 jpeg = base64_decode(info["PixelData"].asString());
531 562
532 try 563 try
533 { 564 {
534 reader.reset(new Orthanc::JpegReader); 565 reader.reset(new Orthanc::JpegReader);
535 dynamic_cast<Orthanc::JpegReader&>(*reader).ReadFromMemory(jpeg); 566 dynamic_cast<Orthanc::JpegReader&>(*reader).ReadFromMemory(jpeg);
536 } 567 }
538 { 569 {
539 NotifySliceImageError(operation); 570 NotifySliceImageError(operation);
540 return; 571 return;
541 } 572 }
542 } 573 }
543 574
544 Orthanc::PixelFormat expectedFormat = 575 Orthanc::PixelFormat expectedFormat =
545 operation.GetSlice().GetConverter().GetExpectedPixelFormat(); 576 operation.GetSlice().GetConverter().GetExpectedPixelFormat();
546 577
547 if (reader->GetFormat() == Orthanc::PixelFormat_RGB24) // This is a color image 578 if (reader->GetFormat() == Orthanc::PixelFormat_RGB24) // This is a color image
548 { 579 {
549 if (expectedFormat != Orthanc::PixelFormat_RGB24) 580 if (expectedFormat != Orthanc::PixelFormat_RGB24)
550 { 581 {
551 NotifySliceImageError(operation); 582 NotifySliceImageError(operation);
552 return; 583 return;
553 } 584 }
554 585
555 if (isSigned || isStretched) 586 if (isSigned || isStretched)
556 { 587 {
557 NotifySliceImageError(operation); 588 NotifySliceImageError(operation);
558 return; 589 return;
559 } 590 }
561 { 592 {
562 NotifySliceImageSuccess(operation, reader); 593 NotifySliceImageSuccess(operation, reader);
563 return; 594 return;
564 } 595 }
565 } 596 }
566 597
567 if (reader->GetFormat() != Orthanc::PixelFormat_Grayscale8) 598 if (reader->GetFormat() != Orthanc::PixelFormat_Grayscale8)
568 { 599 {
569 NotifySliceImageError(operation); 600 NotifySliceImageError(operation);
570 return; 601 return;
571 } 602 }
572 603
573 if (!isStretched) 604 if (!isStretched)
574 { 605 {
575 if (expectedFormat != reader->GetFormat()) 606 if (expectedFormat != reader->GetFormat())
576 { 607 {
577 NotifySliceImageError(operation); 608 NotifySliceImageError(operation);
581 { 612 {
582 NotifySliceImageSuccess(operation, reader); 613 NotifySliceImageSuccess(operation, reader);
583 return; 614 return;
584 } 615 }
585 } 616 }
586 617
587 int32_t stretchLow = 0; 618 int32_t stretchLow = 0;
588 int32_t stretchHigh = 0; 619 int32_t stretchHigh = 0;
589 620
590 if (!info.isMember("StretchLow") || 621 if (!info.isMember("StretchLow") ||
591 !info.isMember("StretchHigh") || 622 !info.isMember("StretchHigh") ||
592 info["StretchLow"].type() != Json::intValue || 623 info["StretchLow"].type() != Json::intValue ||
593 info["StretchHigh"].type() != Json::intValue) 624 info["StretchHigh"].type() != Json::intValue)
594 { 625 {
595 NotifySliceImageError(operation); 626 NotifySliceImageError(operation);
596 return; 627 return;
597 } 628 }
598 629
599 stretchLow = info["StretchLow"].asInt(); 630 stretchLow = info["StretchLow"].asInt();
600 stretchHigh = info["StretchHigh"].asInt(); 631 stretchHigh = info["StretchHigh"].asInt();
601 632
602 if (stretchLow < -32768 || 633 if (stretchLow < -32768 ||
603 stretchHigh > 65535 || 634 stretchHigh > 65535 ||
604 (stretchLow < 0 && stretchHigh > 32767)) 635 (stretchLow < 0 && stretchHigh > 32767))
605 { 636 {
606 // This range cannot be represented with a uint16_t or an int16_t 637 // This range cannot be represented with a uint16_t or an int16_t
607 NotifySliceImageError(operation); 638 NotifySliceImageError(operation);
608 return; 639 return;
609 } 640 }
610 641
611 // Decode a grayscale JPEG 8bpp image coming from the Web viewer 642 // Decode a grayscale JPEG 8bpp image coming from the Web viewer
612 std::auto_ptr<Orthanc::ImageAccessor> image 643 std::auto_ptr<Orthanc::ImageAccessor> image
613 (new Orthanc::Image(expectedFormat, reader->GetWidth(), reader->GetHeight(), false)); 644 (new Orthanc::Image(expectedFormat, reader->GetWidth(), reader->GetHeight(), false));
614 645
615 Orthanc::ImageProcessing::Convert(*image, *reader); 646 Orthanc::ImageProcessing::Convert(*image, *reader);
616 reader.reset(NULL); 647 reader.reset(NULL);
617 648
618 float scaling = static_cast<float>(stretchHigh - stretchLow) / 255.0f; 649 float scaling = static_cast<float>(stretchHigh - stretchLow) / 255.0f;
619 650
620 if (!LinearAlgebra::IsCloseToZero(scaling)) 651 if (!LinearAlgebra::IsCloseToZero(scaling))
621 { 652 {
622 float offset = static_cast<float>(stretchLow) / scaling; 653 float offset = static_cast<float>(stretchLow) / scaling;
623 Orthanc::ImageProcessing::ShiftScale(*image, offset, scaling, true); 654 Orthanc::ImageProcessing::ShiftScale(*image, offset, scaling, true);
624 } 655 }
625 656
626 NotifySliceImageSuccess(operation, image); 657 NotifySliceImageSuccess(operation, image);
627 } 658 }
628 659
629 660
630 class StringImage : 661 class StringImage :
631 public Orthanc::ImageAccessor, 662 public Orthanc::ImageAccessor,
632 public boost::noncopyable 663 public boost::noncopyable
633 { 664 {
634 private: 665 private:
635 std::string buffer_; 666 std::string buffer_;
636 667
637 public: 668 public:
642 { 673 {
643 if (buffer.size() != Orthanc::GetBytesPerPixel(format) * width * height) 674 if (buffer.size() != Orthanc::GetBytesPerPixel(format) * width * height)
644 { 675 {
645 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); 676 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
646 } 677 }
647 678
648 buffer_.swap(buffer); // The source buffer is now empty 679 buffer_.swap(buffer); // The source buffer is now empty
649 680
650 void* data = (buffer_.empty() ? NULL : &buffer_[0]); 681 void* data = (buffer_.empty() ? NULL : &buffer_[0]);
651 682
652 AssignWritable(format, width, height, 683 AssignWritable(format, width, height,
653 Orthanc::GetBytesPerPixel(format) * width, data); 684 Orthanc::GetBytesPerPixel(format) * width, data);
654 } 685 }
655 }; 686 };
656 687
657 688
658 void OrthancSlicesLoader::ParseSliceRawImage(const Operation& operation, 689 void OrthancSlicesLoader::ParseSliceRawImage(const Operation& operation,
659 const void* answer, 690 const void* answer,
660 size_t size) 691 size_t size)
661 { 692 {
662 Orthanc::GzipCompressor compressor; 693 Orthanc::GzipCompressor compressor;
663 694
664 std::string raw; 695 std::string raw;
665 compressor.Uncompress(raw, answer, size); 696 compressor.Uncompress(raw, answer, size);
666 697
667 const Orthanc::DicomImageInformation& info = operation.GetSlice().GetImageInformation(); 698 const Orthanc::DicomImageInformation& info = operation.GetSlice().GetImageInformation();
668 699
675 raw.size() == info.GetWidth() * info.GetHeight() * 4) 706 raw.size() == info.GetWidth() * info.GetHeight() * 4)
676 { 707 {
677 // This is the case of RT-DOSE (uint32_t values) 708 // This is the case of RT-DOSE (uint32_t values)
678 709
679 std::auto_ptr<Orthanc::ImageAccessor> image 710 std::auto_ptr<Orthanc::ImageAccessor> image
680 (new StringImage(Orthanc::PixelFormat_Grayscale32, info.GetWidth(), 711 (new StringImage(Orthanc::PixelFormat_Grayscale32, info.GetWidth(),
681 info.GetHeight(), raw)); 712 info.GetHeight(), raw));
682 713
683 // TODO - Only for big endian 714 // TODO - Only for big endian
684 for (unsigned int y = 0; y < image->GetHeight(); y++) 715 for (unsigned int y = 0; y < image->GetHeight(); y++)
685 { 716 {
686 uint32_t *p = reinterpret_cast<uint32_t*>(image->GetRow(y)); 717 uint32_t *p = reinterpret_cast<uint32_t*>(image->GetRow(y));
687 for (unsigned int x = 0; x < image->GetWidth(); x++, p++) 718 for (unsigned int x = 0; x < image->GetWidth(); x++, p++)
688 { 719 {
689 *p = le32toh(*p); 720 *p = le32toh(*p);
690 } 721 }
691 } 722 }
692 723
693 NotifySliceImageSuccess(operation, image); 724 NotifySliceImageSuccess(operation, image);
694 } 725 }
695 else if (info.GetBitsAllocated() == 16 && 726 else if (info.GetBitsAllocated() == 16 &&
696 info.GetBitsStored() == 16 && 727 info.GetBitsStored() == 16 &&
697 info.GetHighBit() == 15 && 728 info.GetHighBit() == 15 &&
699 !info.IsSigned() && 730 !info.IsSigned() &&
700 info.GetPhotometricInterpretation() == Orthanc::PhotometricInterpretation_Monochrome2 && 731 info.GetPhotometricInterpretation() == Orthanc::PhotometricInterpretation_Monochrome2 &&
701 raw.size() == info.GetWidth() * info.GetHeight() * 2) 732 raw.size() == info.GetWidth() * info.GetHeight() * 2)
702 { 733 {
703 std::auto_ptr<Orthanc::ImageAccessor> image 734 std::auto_ptr<Orthanc::ImageAccessor> image
704 (new StringImage(Orthanc::PixelFormat_Grayscale16, info.GetWidth(), 735 (new StringImage(Orthanc::PixelFormat_Grayscale16, info.GetWidth(),
705 info.GetHeight(), raw)); 736 info.GetHeight(), raw));
706 737
707 // TODO - Big endian ? 738 // TODO - Big endian ?
708 739
709 NotifySliceImageSuccess(operation, image); 740 NotifySliceImageSuccess(operation, image);
710 } 741 }
711 else 742 else
712 { 743 {
713 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); 744 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
714 } 745 }
715 746
716 } 747 }
717 748
718 749
719 OrthancSlicesLoader::OrthancSlicesLoader(MessageBroker& broker, 750 OrthancSlicesLoader::OrthancSlicesLoader(MessageBroker& broker,
720 ICallback& callback, 751 ISliceLoaderObserver& callback,
721 IWebService& orthanc) : 752 IWebService& orthanc) :
722 webCallback_(new WebCallback(broker, *this)), 753 webCallback_(new WebCallback(broker, *this)),
723 userCallback_(callback), 754 userCallback_(callback),
724 orthanc_(orthanc), 755 orthanc_(orthanc),
725 state_(State_Initialization) 756 state_(State_Initialization)
726 { 757 {
727 } 758 }
728 759
729 760
730 void OrthancSlicesLoader::ScheduleLoadSeries(const std::string& seriesId) 761 void OrthancSlicesLoader::ScheduleLoadSeries(const std::string& seriesId)
731 { 762 {
732 if (state_ != State_Initialization) 763 if (state_ != State_Initialization)
733 { 764 {
738 state_ = State_LoadingGeometry; 769 state_ = State_LoadingGeometry;
739 std::string uri = "/series/" + seriesId + "/instances-tags"; 770 std::string uri = "/series/" + seriesId + "/instances-tags";
740 orthanc_.ScheduleGetRequest(*webCallback_, uri, Operation::DownloadSeriesGeometry()); 771 orthanc_.ScheduleGetRequest(*webCallback_, uri, Operation::DownloadSeriesGeometry());
741 } 772 }
742 } 773 }
743 774
744 775
745 void OrthancSlicesLoader::ScheduleLoadInstance(const std::string& instanceId) 776 void OrthancSlicesLoader::ScheduleLoadInstance(const std::string& instanceId)
746 { 777 {
747 if (state_ != State_Initialization) 778 if (state_ != State_Initialization)
748 { 779 {
749 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 780 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
750 } 781 }
751 else 782 else
752 { 783 {
753 state_ = State_LoadingGeometry; 784 state_ = State_LoadingGeometry;
754 785
755 // Tag "3004-000c" is "Grid Frame Offset Vector", which is 786 // Tag "3004-000c" is "Grid Frame Offset Vector", which is
756 // mandatory to read RT DOSE, but is too long to be returned by default 787 // mandatory to read RT DOSE, but is too long to be returned by default
757 std::string uri = "/instances/" + instanceId + "/tags?ignore-length=3004-000c"; 788 std::string uri = "/instances/" + instanceId + "/tags?ignore-length=3004-000c";
758 orthanc_.ScheduleGetRequest 789 orthanc_.ScheduleGetRequest
759 (*webCallback_, uri, Operation::DownloadInstanceGeometry(instanceId)); 790 (*webCallback_, uri, Operation::DownloadInstanceGeometry(instanceId));
760 } 791 }
761 } 792 }
762 793
763 794
764 void OrthancSlicesLoader::ScheduleLoadFrame(const std::string& instanceId, 795 void OrthancSlicesLoader::ScheduleLoadFrame(const std::string& instanceId,
765 unsigned int frame) 796 unsigned int frame)
766 { 797 {
767 if (state_ != State_Initialization) 798 if (state_ != State_Initialization)
768 { 799 {
771 else 802 else
772 { 803 {
773 state_ = State_LoadingGeometry; 804 state_ = State_LoadingGeometry;
774 std::string uri = "/instances/" + instanceId + "/tags"; 805 std::string uri = "/instances/" + instanceId + "/tags";
775 orthanc_.ScheduleGetRequest 806 orthanc_.ScheduleGetRequest
776 (*webCallback_, uri, Operation::DownloadFrameGeometry(instanceId, frame)); 807 (*webCallback_, uri, Operation::DownloadFrameGeometry(instanceId, frame));
777 } 808 }
778 } 809 }
779 810
780 811
781 bool OrthancSlicesLoader::IsGeometryReady() const 812 bool OrthancSlicesLoader::IsGeometryReady() const
782 { 813 {
783 return state_ == State_GeometryReady; 814 return state_ == State_GeometryReady;
784 } 815 }
785 816
786 817
787 size_t OrthancSlicesLoader::GetSliceCount() const 818 size_t OrthancSlicesLoader::GetSliceCount() const
788 { 819 {
789 if (state_ != State_GeometryReady) 820 if (state_ != State_GeometryReady)
790 { 821 {
791 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 822 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
792 } 823 }
793 824
794 return slices_.GetSliceCount(); 825 return slices_.GetSliceCount();
795 } 826 }
796 827
797 828
798 const Slice& OrthancSlicesLoader::GetSlice(size_t index) const 829 const Slice& OrthancSlicesLoader::GetSlice(size_t index) const
799 { 830 {
800 if (state_ != State_GeometryReady) 831 if (state_ != State_GeometryReady)
801 { 832 {
802 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 833 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
803 } 834 }
804 835
805 return slices_.GetSlice(index); 836 return slices_.GetSlice(index);
806 } 837 }
807 838
808 839
809 bool OrthancSlicesLoader::LookupSlice(size_t& index, 840 bool OrthancSlicesLoader::LookupSlice(size_t& index,
810 const CoordinateSystem3D& plane) const 841 const CoordinateSystem3D& plane) const
811 { 842 {
812 if (state_ != State_GeometryReady) 843 if (state_ != State_GeometryReady)
813 { 844 {
814 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 845 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
815 } 846 }
816 847
817 return slices_.LookupSlice(index, plane); 848 return slices_.LookupSlice(index, plane);
818 } 849 }
819 850
820 851
821 void OrthancSlicesLoader::ScheduleSliceImagePng(const Slice& slice, 852 void OrthancSlicesLoader::ScheduleSliceImagePng(const Slice& slice,
822 size_t index) 853 size_t index)
823 { 854 {
824 std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + 855 std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" +
825 boost::lexical_cast<std::string>(slice.GetFrame())); 856 boost::lexical_cast<std::string>(slice.GetFrame()));
826 857
827 switch (slice.GetConverter().GetExpectedPixelFormat()) 858 switch (slice.GetConverter().GetExpectedPixelFormat())
828 { 859 {
829 case Orthanc::PixelFormat_RGB24: 860 case Orthanc::PixelFormat_RGB24:
830 uri += "/preview"; 861 uri += "/preview";
831 break; 862 break;
832 863
833 case Orthanc::PixelFormat_Grayscale16: 864 case Orthanc::PixelFormat_Grayscale16:
834 uri += "/image-uint16"; 865 uri += "/image-uint16";
835 break; 866 break;
836 867
837 case Orthanc::PixelFormat_SignedGrayscale16: 868 case Orthanc::PixelFormat_SignedGrayscale16:
838 uri += "/image-int16"; 869 uri += "/image-int16";
839 break; 870 break;
840 871
841 default: 872 default:
842 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); 873 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
843 } 874 }
844 875
845 orthanc_.ScheduleGetRequest(*webCallback_, uri, 876 orthanc_.ScheduleGetRequest(*webCallback_, uri,
846 Operation::DownloadSliceImage(index, slice, SliceImageQuality_Full)); 877 Operation::DownloadSliceImage(index, slice, SliceImageQuality_Full));
847 } 878 }
848 879
849 880
850 void OrthancSlicesLoader::ScheduleSliceImageJpeg(const Slice& slice, 881 void OrthancSlicesLoader::ScheduleSliceImageJpeg(const Slice& slice,
851 size_t index, 882 size_t index,
852 SliceImageQuality quality) 883 SliceImageQuality quality)
853 { 884 {
854 unsigned int value; 885 unsigned int value;
855 886
856 switch (quality) 887 switch (quality)
857 { 888 {
858 case SliceImageQuality_Jpeg50: 889 case SliceImageQuality_Jpeg50:
859 value = 50; 890 value = 50;
860 break; 891 break;
861 892
862 case SliceImageQuality_Jpeg90: 893 case SliceImageQuality_Jpeg90:
863 value = 90; 894 value = 90;
864 break; 895 break;
865 896
866 case SliceImageQuality_Jpeg95: 897 case SliceImageQuality_Jpeg95:
867 value = 95; 898 value = 95;
868 break; 899 break;
869 900
870 default: 901 default:
871 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); 902 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
872 } 903 }
873 904
874 // This requires the official Web viewer plugin to be installed! 905 // This requires the official Web viewer plugin to be installed!
875 std::string uri = ("/web-viewer/instances/jpeg" + 906 std::string uri = ("/web-viewer/instances/jpeg" +
876 boost::lexical_cast<std::string>(value) + 907 boost::lexical_cast<std::string>(value) +
877 "-" + slice.GetOrthancInstanceId() + "_" + 908 "-" + slice.GetOrthancInstanceId() + "_" +
878 boost::lexical_cast<std::string>(slice.GetFrame())); 909 boost::lexical_cast<std::string>(slice.GetFrame()));
879 910
880 orthanc_.ScheduleGetRequest(*webCallback_, uri, 911 orthanc_.ScheduleGetRequest(*webCallback_, uri,
881 Operation::DownloadSliceImage(index, slice, quality)); 912 Operation::DownloadSliceImage(index, slice, quality));
882 } 913 }
883 914
884 915
885 916
886 void OrthancSlicesLoader::ScheduleLoadSliceImage(size_t index, 917 void OrthancSlicesLoader::ScheduleLoadSliceImage(size_t index,
887 SliceImageQuality quality) 918 SliceImageQuality quality)
888 { 919 {
889 if (state_ != State_GeometryReady) 920 if (state_ != State_GeometryReady)
890 { 921 {
891 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 922 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
892 } 923 }
893 924
894 const Slice& slice = GetSlice(index); 925 const Slice& slice = GetSlice(index);
895 926
896 if (slice.HasOrthancDecoding()) 927 if (slice.HasOrthancDecoding())
897 { 928 {
898 if (quality == SliceImageQuality_Full) 929 if (quality == SliceImageQuality_Full)
899 { 930 {
900 ScheduleSliceImagePng(slice, index); 931 ScheduleSliceImagePng(slice, index);
904 ScheduleSliceImageJpeg(slice, index, quality); 935 ScheduleSliceImageJpeg(slice, index, quality);
905 } 936 }
906 } 937 }
907 else 938 else
908 { 939 {
909 std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + 940 std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" +
910 boost::lexical_cast<std::string>(slice.GetFrame()) + "/raw.gz"); 941 boost::lexical_cast<std::string>(slice.GetFrame()) + "/raw.gz");
911 orthanc_.ScheduleGetRequest(*webCallback_, uri, 942 orthanc_.ScheduleGetRequest(*webCallback_, uri,
912 Operation::DownloadSliceRawImage(index, slice)); 943 Operation::DownloadSliceRawImage(index, slice));
913 } 944 }
914 } 945 }