1 /*
2 *
3 * Copyright (C) 1996-2019, OFFIS e.V.
4 * All rights reserved. See COPYRIGHT file for details.
5 *
6 * This software and supporting documentation were developed by
7 *
8 * OFFIS e.V.
9 * R&D Division Health
10 * Escherweg 2
11 * D-26121 Oldenburg, Germany
12 *
13 *
14 * Module: dcmimgle
15 *
16 * Author: Joerg Riesmeier
17 *
18 * Purpose: DicomOverlayPlane (Source)
19 *
20 */
21
22
23 #include "dcmtk/config/osconfig.h"
24
25 #include "dcmtk/dcmdata/dctypes.h"
26 #include "dcmtk/dcmdata/dcdeftag.h"
27 #include "dcmtk/dcmdata/dctagkey.h"
28 #include "dcmtk/dcmdata/dcpixel.h"
29 #include "dcmtk/ofstd/ofbmanip.h"
30 #include "dcmtk/ofstd/ofutil.h"
31
32 #include "dcmtk/dcmimgle/diovpln.h"
33 #include "dcmtk/dcmimgle/didocu.h"
34
35
36 /*----------------*
37 * constructors *
38 *----------------*/
39
DiOverlayPlane(const DiDocument * docu,const unsigned int group,Uint16 alloc,const Uint16 stored,const Uint16 high)40 DiOverlayPlane::DiOverlayPlane(const DiDocument *docu,
41 const unsigned int group,
42 Uint16 alloc,
43 const Uint16 stored,
44 const Uint16 high)
45 : NumberOfFrames(0),
46 ImageFrameOrigin(0),
47 FirstFrame(0),
48 Top(0),
49 Left(0),
50 Height(0),
51 Width(0),
52 Rows(0),
53 Columns(0),
54 BitsAllocated(0),
55 BitPosition(0),
56 Foreground(1),
57 Threshold(1),
58 PValue(0),
59 Mode(EMO_Graphic),
60 DefaultMode(EMO_Graphic),
61 Label(),
62 Description(),
63 GroupNumber(group),
64 Valid(0),
65 Visible(0),
66 BitPos(0),
67 StartBitPos(0),
68 StartLeft(0),
69 StartTop(0),
70 EmbeddedData(0),
71 Ptr(NULL),
72 StartPtr(NULL),
73 Data(NULL)
74 {
75 if (docu != NULL)
76 {
77 /* determine first frame to be processed */
78 FirstFrame = docu->getFrameStart();
79 /* specify overlay group number */
80 DcmTagKey tag(group, DCM_OverlayRows.getElement() /* dummy */);
81 /* get descriptive data */
82 tag.setElement(DCM_OverlayLabel.getElement());
83 docu->getValue(tag, Label);
84 tag.setElement(DCM_OverlayDescription.getElement());
85 docu->getValue(tag, Description);
86 /* get overlay type */
87 tag.setElement(DCM_OverlayType.getElement());
88 const char *str;
89 if ((docu->getValue(tag, str) > 0) && (str != NULL) && (strcmp(str, "R") == 0))
90 DefaultMode = Mode = EMO_RegionOfInterest;
91 Sint32 sl = 0;
92 /* multi-frame overlays */
93 tag.setElement(DCM_NumberOfFramesInOverlay.getElement());
94 docu->getValue(tag, sl);
95 NumberOfFrames = (sl < 1) ? 1 : OFstatic_cast(Uint32, sl);
96 tag.setElement(DCM_ImageFrameOrigin.getElement());
97 docu->getValue(tag, ImageFrameOrigin);
98 if (ImageFrameOrigin > 0) // image frame origin is numbered from 1
99 --ImageFrameOrigin;
100 tag.setElement(DCM_OverlayOrigin.getElement());
101 #ifdef REVERSE_OVERLAY_ORIGIN_ORDER
102 Valid = (docu->getValue(tag, Left, 0) > 0);
103 if (Valid)
104 {
105 DCMIMGLE_DEBUG("processing overlay plane in group 0x" << STD_NAMESPACE hex << group);
106 if (docu->getValue(tag, Top, 1) < 2)
107 DCMIMGLE_WARN("missing second value for 'OverlayOrigin' ... assuming 'Top' = " << Top);
108 }
109 #else
110 Valid = (docu->getValue(tag, Top, 0) > 0);
111 if (Valid)
112 {
113 DCMIMGLE_DEBUG("processing overlay plane in group 0x" << STD_NAMESPACE hex << group);
114 if (docu->getValue(tag, Left, 1) < 2)
115 DCMIMGLE_WARN("missing second value for 'OverlayOrigin' ... assuming 'Left' = " << Left);
116 }
117 #endif
118 /* overlay origin is numbered from 1 */
119 --Top;
120 --Left;
121 /* check overlay resolution */
122 tag.setElement(DCM_OverlayRows.getElement());
123 Valid &= (docu->getValue(tag, Rows) > 0);
124 Height = Rows;
125 tag.setElement(DCM_OverlayColumns.getElement());
126 Valid &= (docu->getValue(tag, Columns) > 0);
127 Width = Columns;
128 /* check overlay encoding */
129 tag.setElement(DCM_OverlayBitsAllocated.getElement());
130 Valid &= (docu->getValue(tag, BitsAllocated) > 0);
131 tag.setElement(DCM_OverlayBitPosition.getElement());
132 Valid &= (docu->getValue(tag, BitPosition) > 0);
133 tag.setElement(DCM_OverlayData.getElement());
134 /* final validity checks */
135 if (Valid)
136 {
137 /* separate overlay data? */
138 unsigned long length = docu->getValue(tag, Data) * 2 /* bytes */;
139 if (length == 0)
140 {
141 if (!(docu->getFlags() & CIF_NeverAccessEmbeddedOverlays))
142 {
143 if (!docu->isCompressed())
144 {
145 /* if not, check for embedded overlay data */
146 DcmPixelData *pixelData = docu->getPixelData();
147 if (pixelData != NULL)
148 {
149 ImageFrameOrigin = 0; // see supplement 4
150 const OFBool loaded = pixelData->valueLoaded();
151 if (pixelData->getUint16Array(OFconst_cast(Uint16 *&, Data)).good())
152 {
153 length = pixelData->getLength(docu->getTransferSyntax());
154 EmbeddedData = (Data != NULL);
155 if (!loaded)
156 DCMIMGLE_DEBUG("loaded complete pixel data into memory for embedded overlay data: " << length << " bytes");
157 }
158 }
159 } else
160 DCMIMGLE_ERROR("embedded overlay data cannot be accessed since pixel data is still compressed");
161 } else
162 DCMIMGLE_WARN("ignoring possibly embedded overlay data by configuration");
163 } else
164 alloc = 1; // separately stored overlay data
165 /* check for correct value of BitsAllocated */
166 if (BitsAllocated != alloc) // see correction proposal 87
167 {
168 DCMIMGLE_WARN("invalid value for 'OverlayBitsAllocated' (" << BitsAllocated << ") ... assuming " << alloc);
169 BitsAllocated = alloc;
170 }
171 /* check for correct value of BitPosition */
172 if (BitPosition >= BitsAllocated)
173 {
174 DCMIMGLE_WARN("invalid value for 'OverlayBitPosition' (" << BitPosition << ") ... assuming " << (BitsAllocated - 1));
175 BitPosition = BitsAllocated - 1;
176 }
177 if (EmbeddedData && (BitPosition <= high) && (BitPosition + stored > high))
178 {
179 DCMIMGLE_WARN("invalid value for 'OverlayBitPosition' (" << BitPosition << "), refers to bit position within stored pixel value");
180 Data = NULL; // invalid plane
181 }
182 /* expected length of overlay data */
183 const unsigned long expLen = (OFstatic_cast(unsigned long, NumberOfFrames) * OFstatic_cast(unsigned long, Rows) *
184 OFstatic_cast(unsigned long, Columns) * OFstatic_cast(unsigned long, BitsAllocated) + 7) / 8;
185 if ((Data != NULL) && ((length == 0) || (length < expLen)))
186 {
187 DCMIMGLE_ERROR("overlay data length is too short, " << expLen << " bytes expected but " << length << " bytes found");
188 Valid = 0;
189 Data = NULL;
190 } else
191 Valid = (Data != NULL);
192 }
193 if (Valid)
194 {
195 /* report that this group contains a valid overlay plane */
196 DCMIMGLE_TRACE("overlay plane in group 0x" << STD_NAMESPACE hex << group << " is present and can be processed");
197 } else {
198 /* report that this group does not contain a valid overlay plane */
199 DCMIMGLE_TRACE("overlay plane in group 0x" << STD_NAMESPACE hex << group << " is missing or incomplete");
200 }
201 }
202 }
203
204
DiOverlayPlane(const unsigned int group,const Sint16 left_pos,const Sint16 top_pos,const Uint16 columns,const Uint16 rows,const DcmOverlayData & data,const DcmLongString & label,const DcmLongString & description,const EM_Overlay mode)205 DiOverlayPlane::DiOverlayPlane(const unsigned int group,
206 const Sint16 left_pos,
207 const Sint16 top_pos,
208 const Uint16 columns,
209 const Uint16 rows,
210 const DcmOverlayData &data,
211 const DcmLongString &label,
212 const DcmLongString &description,
213 const EM_Overlay mode)
214 : NumberOfFrames(1),
215 ImageFrameOrigin(0),
216 FirstFrame(0),
217 Top(top_pos),
218 Left(left_pos),
219 Height(rows),
220 Width(columns),
221 Rows(rows),
222 Columns(columns),
223 BitsAllocated(1),
224 BitPosition(0),
225 Foreground(1),
226 Threshold(1),
227 PValue(0),
228 Mode(mode),
229 DefaultMode(mode),
230 Label(),
231 Description(),
232 GroupNumber(group),
233 Valid(0),
234 Visible((mode == EMO_BitmapShutter) ? 1 : 0),
235 BitPos(0),
236 StartBitPos(0),
237 StartLeft(0),
238 StartTop(0),
239 EmbeddedData(0),
240 Ptr(NULL),
241 StartPtr(NULL),
242 Data(NULL)
243 {
244 DiDocument::getElemValue(OFreinterpret_cast(const DcmElement *, &label), Label);
245 DiDocument::getElemValue(OFreinterpret_cast(const DcmElement *, &description), Description);
246 if ((Columns > 0) && (Rows > 0))
247 {
248 const unsigned long length = DiDocument::getElemValue(OFreinterpret_cast(const DcmElement *, &data), Data) * 2 /* Bytes */;
249 /* expected length of overlay data */
250 const unsigned long expLen = (OFstatic_cast(unsigned long, Rows) * OFstatic_cast(unsigned long, Columns) + 7) / 8;
251 if ((length == 0) || (length < expLen))
252 {
253 DCMIMGLE_ERROR("overlay data length is too short, " << expLen << " bytes expected but " << length << " bytes found");
254 /* Valid = 0; => This is the default. */
255 Data = NULL;
256 } else
257 Valid = (Data != NULL);
258 }
259 --Top; // overlay origin is numbered from 1
260 --Left;
261 }
262
263
DiOverlayPlane(DiOverlayPlane * plane,const unsigned int bit,const Uint16 * data,Uint16 * temp,const Uint16 width,const Uint16 height,const Uint16 columns,const Uint16 rows)264 DiOverlayPlane::DiOverlayPlane(DiOverlayPlane *plane,
265 const unsigned int bit,
266 const Uint16 *data,
267 Uint16 *temp,
268 const Uint16 width,
269 const Uint16 height,
270 const Uint16 columns,
271 const Uint16 rows)
272 : NumberOfFrames(plane->NumberOfFrames),
273 ImageFrameOrigin(plane->ImageFrameOrigin),
274 FirstFrame(plane->FirstFrame),
275 Top(plane->Top),
276 Left(plane->Left),
277 Height(plane->Height),
278 Width(plane->Width),
279 Rows(rows),
280 Columns(columns),
281 BitsAllocated(16),
282 BitPosition(bit),
283 Foreground(plane->Foreground),
284 Threshold(plane->Threshold),
285 PValue(0),
286 Mode(plane->Mode),
287 DefaultMode(plane->DefaultMode),
288 Label(plane->Label),
289 Description(plane->Description),
290 GroupNumber(plane->GroupNumber),
291 Valid(0),
292 Visible(plane->Visible),
293 BitPos(0),
294 StartBitPos(0),
295 StartLeft(plane->StartLeft),
296 StartTop(plane->StartTop),
297 EmbeddedData(0),
298 Ptr(NULL),
299 StartPtr(NULL),
300 Data(data)
301 {
302 if (temp != NULL)
303 {
304 Uint16 x;
305 Uint16 y;
306 Uint16 *q = temp;
307 const Uint16 mask = 1 << bit;
308 const Uint16 skip_x = width - plane->Columns;
309 const unsigned long skip_f = OFstatic_cast(unsigned long, height - plane->Rows) * OFstatic_cast(unsigned long, width);
310 for (unsigned long f = 0; f < NumberOfFrames; ++f)
311 {
312 if (plane->reset(f + ImageFrameOrigin))
313 {
314 for (y = 0; y < plane->Rows; ++y)
315 {
316 for (x = 0; x < plane->Columns; ++x, ++q)
317 {
318 if (plane->getNextBit())
319 *q |= mask; // set corresponding bit
320 else
321 *q &= ~mask; // unset ... bit
322 }
323 q += skip_x; // skip to next line start
324 }
325 q += skip_f; // skip to next frame start
326 }
327 }
328 }
329 Valid = (Data != NULL);
330 }
331
332
333 /*--------------*
334 * destructor *
335 *--------------*/
336
~DiOverlayPlane()337 DiOverlayPlane::~DiOverlayPlane()
338 {
339 }
340
341
342 /********************************************************************/
343
344
getData(const unsigned long frame,const Uint16 xmin,const Uint16 ymin,const Uint16 xmax,const Uint16 ymax,const int bits,const Uint16 fore,const Uint16 back,const OFBool useOrigin)345 void *DiOverlayPlane::getData(const unsigned long frame,
346 const Uint16 xmin,
347 const Uint16 ymin,
348 const Uint16 xmax,
349 const Uint16 ymax,
350 const int bits,
351 const Uint16 fore,
352 const Uint16 back,
353 const OFBool useOrigin)
354 {
355 const unsigned long count = OFstatic_cast(unsigned long, xmax - xmin) * OFstatic_cast(unsigned long, ymax - ymin);
356 if (Valid && (count > 0))
357 {
358 const Uint16 mask = OFstatic_cast(Uint16, DicomImageClass::maxval(bits));
359 if (bits == 1)
360 {
361 const unsigned long count8 = (count + 7) / 8; // round value: 8 bit padding
362 Uint8 *data = new Uint8[count8];
363 if (data != NULL)
364 {
365 if ((fore & mask) != (back & mask))
366 {
367 OFBitmanipTemplate<Uint8>::setMem(data, 0x0, count8);
368 Uint16 x;
369 Uint16 y;
370 Uint8 value = 0;
371 Uint8 *q = data;
372 int bit = 0;
373 if (reset(frame + ImageFrameOrigin))
374 {
375 for (y = ymin; y < ymax; ++y)
376 {
377 setStart(xmin, y, useOrigin);
378 for (x = xmin; x < xmax; ++x)
379 {
380 if (getNextBit())
381 {
382 if (fore)
383 value |= (1 << bit);
384 } else if (back)
385 value |= (1 << bit);
386 if (bit == 7)
387 {
388 *(q++) = value;
389 value = 0;
390 bit = 0;
391 } else {
392 ++bit;
393 }
394 }
395 }
396 if (bit != 0)
397 *(q++) = value;
398 }
399 } else {
400 OFBitmanipTemplate<Uint8>::setMem(data, (fore) ? 0xff : 0x0, count8);
401 }
402 }
403 return OFstatic_cast(void *, data);
404 }
405 else if ((bits > 1) && (bits <= 8))
406 {
407 Uint8 *data = new Uint8[count];
408 if (data != NULL)
409 {
410 const Uint8 fore8 = OFstatic_cast(Uint8, fore & mask);
411 const Uint8 back8 = OFstatic_cast(Uint8, back & mask);
412 OFBitmanipTemplate<Uint8>::setMem(data, back8, count);
413 if (fore8 != back8) // optimization
414 {
415 Uint16 x;
416 Uint16 y;
417 Uint8 *q = data;
418 if (reset(frame + ImageFrameOrigin))
419 {
420 for (y = ymin; y < ymax; ++y)
421 {
422 setStart(xmin, y, useOrigin);
423 for (x = xmin; x < xmax; ++x, ++q)
424 {
425 if (getNextBit())
426 *q = fore8; // set pixel value (default: 0xff)
427 }
428 }
429 }
430 }
431 }
432 return OFstatic_cast(void *, data);
433 }
434 else if ((bits > 8) && (bits <= 16))
435 {
436 Uint16 *data = new Uint16[count];
437 if (data != NULL)
438 {
439 const Uint16 fore16 = fore & mask;
440 const Uint16 back16 = back & mask;
441 OFBitmanipTemplate<Uint16>::setMem(data, back16, count);
442 if (fore16 != back16) // optimization
443 {
444 Uint16 x;
445 Uint16 y;
446 Uint16 *q = data;
447 if (reset(frame + ImageFrameOrigin))
448 {
449 for (y = ymin; y < ymax; ++y)
450 {
451 setStart(xmin, y, useOrigin);
452 for (x = xmin; x < xmax; ++x, ++q)
453 {
454 if (getNextBit())
455 *q = fore16; // set pixel value (default: 0xff)
456 }
457 }
458 }
459 }
460 }
461 return OFstatic_cast(void *, data);
462 }
463 }
464 return NULL;
465 }
466
467
create6xxx3000Data(Uint8 * & buffer,unsigned int & width,unsigned int & height,unsigned long & frames)468 unsigned long DiOverlayPlane::create6xxx3000Data(Uint8 *&buffer,
469 unsigned int &width,
470 unsigned int &height,
471 unsigned long &frames)
472 {
473 buffer = NULL;
474 width = Width;
475 height = Height;
476 frames = NumberOfFrames;
477 const unsigned long count = OFstatic_cast(unsigned long, Width) * OFstatic_cast(unsigned long, Height) * NumberOfFrames;
478 if (Valid && (count > 0))
479 {
480 const unsigned long count8 = ((count + 15) / 16) * 2; // round value: 16 bit padding
481 buffer = new Uint8[count8];
482 if (buffer != NULL)
483 {
484 OFBitmanipTemplate<Uint8>::setMem(buffer, 0x0, count8);
485 Uint16 x;
486 Uint16 y;
487 Uint8 value = 0;
488 Uint8 *q = buffer;
489 int bit = 0;
490 for (unsigned long f = 0; f < NumberOfFrames; ++f)
491 {
492 if (reset(f + ImageFrameOrigin))
493 {
494 for (y = 0; y < Height; ++y)
495 {
496 for (x = 0; x < Width; ++x)
497 {
498 if (getNextBit())
499 value |= (1 << bit);
500 if (bit == 7)
501 {
502 *(q++) = value;
503 value = 0;
504 bit = 0;
505 } else {
506 ++bit;
507 }
508 }
509 }
510 }
511 if (bit != 0)
512 *(q++) = value;
513 }
514 return count8; // number of bytes
515 }
516 }
517 return 0;
518 }
519
520
show(const double fore,const double thresh,const EM_Overlay mode)521 void DiOverlayPlane::show(const double fore,
522 const double thresh,
523 const EM_Overlay mode)
524 {
525 Foreground = (fore < 0) ? 0 : (fore > 1) ? 1 : fore;
526 Threshold = (thresh < 0) ? 0 : (thresh > 1) ? 1 : thresh;
527 Mode = (mode == EMO_Default) ? DefaultMode : mode;
528 Visible = 1;
529 }
530
531
show(const Uint16 pvalue)532 int DiOverlayPlane::show(const Uint16 pvalue)
533 {
534 if (Mode == EMO_BitmapShutter)
535 {
536 PValue = pvalue;
537 Visible = 1;
538 return 1;
539 }
540 return 0;
541 }
542
543
place(const signed int left_pos,const signed int top_pos)544 void DiOverlayPlane::place(const signed int left_pos,
545 const signed int top_pos)
546 {
547 Left = OFstatic_cast(Sint16, left_pos);
548 Top = OFstatic_cast(Sint16, top_pos);
549 }
550
551
setScaling(const double xfactor,const double yfactor)552 void DiOverlayPlane::setScaling(const double xfactor,
553 const double yfactor)
554 {
555 Left = OFstatic_cast(Sint16, xfactor * Left);
556 Top = OFstatic_cast(Sint16, yfactor * Top);
557 Width = OFstatic_cast(Uint16, xfactor * Width);
558 Height = OFstatic_cast(Uint16, yfactor * Height);
559 StartLeft = OFstatic_cast(unsigned int, xfactor * StartLeft);
560 StartTop = OFstatic_cast(unsigned int, yfactor * StartTop);
561 }
562
563
setFlipping(const int horz,const int vert,const signed long columns,const signed long rows)564 void DiOverlayPlane::setFlipping(const int horz,
565 const int vert,
566 const signed long columns,
567 const signed long rows)
568 {
569 if (horz)
570 {
571 Left = OFstatic_cast(Sint16, columns - Width - Left);
572 StartLeft = OFstatic_cast(unsigned int, OFstatic_cast(signed long, Columns) - Width - StartLeft);
573 }
574 if (vert)
575 {
576 Top = OFstatic_cast(Sint16, rows - Height - Top);
577 StartTop = OFstatic_cast(unsigned int, OFstatic_cast(signed long, Rows) - Height - StartTop);
578 }
579 }
580
581
setRotation(const int degree,const signed long left_pos,const signed long top_pos,const Uint16 columns,const Uint16 rows)582 void DiOverlayPlane::setRotation(const int degree,
583 const signed long left_pos,
584 const signed long top_pos,
585 const Uint16 columns,
586 const Uint16 rows)
587 {
588 if (degree == 180) // equal to v/h flip
589 setFlipping(1, 1, left_pos + columns, top_pos + rows);
590 else if ((degree == 90) || (degree == 270))
591 {
592 OFswap(Width, Height); // swap visible width/height
593 /*
594 OFswap(Columns, Rows); // swap stored columns/rows -> already done in the constructor !
595 */
596 if (degree == 90) // rotate right
597 {
598 const Sint16 ss = Left;
599 const unsigned int ui = StartLeft;
600 Left = OFstatic_cast(Sint16, OFstatic_cast(signed long, columns) - Width - Top + top_pos);
601 StartLeft = OFstatic_cast(unsigned int, OFstatic_cast(signed long, Columns) - Width - StartTop);
602 Top = OFstatic_cast(Sint16, ss - left_pos);
603 StartTop = ui;
604 } else { // rotate left
605 const Sint16 ss = Left;
606 const unsigned int ui = StartLeft;
607 Left = OFstatic_cast(Sint16, Top - top_pos);
608 StartLeft = StartTop;
609 Top = OFstatic_cast(Sint16, OFstatic_cast(signed long, rows) - Height - ss + left_pos);
610 StartTop = OFstatic_cast(unsigned int, OFstatic_cast(signed long, Rows) - Height - ui);
611 }
612 }
613 }
614