1 /**
2  * Orthanc - A Lightweight, RESTful DICOM Store
3  * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4  * Department, University Hospital of Liege, Belgium
5  * Copyright (C) 2017-2021 Osimis S.A., Belgium
6  *
7  * This program is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * as published by the Free Software Foundation, either version 3 of
10  * the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this program. If not, see
19  * <http://www.gnu.org/licenses/>.
20  **/
21 
22 
23 #include "../PrecompiledHeaders.h"
24 
25 #ifndef NOMINMAX
26 #define NOMINMAX
27 #endif
28 
29 #include "DicomIntegerPixelAccessor.h"
30 
31 #include "../OrthancException.h"
32 #include <boost/lexical_cast.hpp>
33 #include <limits>
34 #include <cassert>
35 #include <stdio.h>
36 
37 namespace Orthanc
38 {
DicomIntegerPixelAccessor(const DicomMap & values,const void * pixelData,size_t size)39   DicomIntegerPixelAccessor::DicomIntegerPixelAccessor(const DicomMap& values,
40                                                        const void* pixelData,
41                                                        size_t size) :
42     information_(values),
43     pixelData_(pixelData),
44     size_(size)
45   {
46     if (information_.GetBitsAllocated() > 32 ||
47         information_.GetBitsStored() >= 32)
48     {
49       // Not available, as the accessor internally uses int32_t values
50       throw OrthancException(ErrorCode_NotImplemented);
51     }
52 
53     frame_ = 0;
54     frameOffset_ = information_.GetFrameSize();
55 
56     if (information_.GetNumberOfFrames() * frameOffset_ > size)
57     {
58       throw OrthancException(ErrorCode_BadFileFormat);
59     }
60 
61     if (information_.IsSigned())
62     {
63       // Pixels are signed
64       mask_ = (1 << (information_.GetBitsStored() - 1)) - 1;
65       signMask_ = (1 << (information_.GetBitsStored() - 1));
66     }
67     else
68     {
69       // Pixels are unsigned
70       mask_ = (1 << information_.GetBitsStored()) - 1;
71       signMask_ = 0;
72     }
73 
74     if (information_.IsPlanar())
75     {
76       /**
77        * Each color plane shall be sent contiguously. For RGB images,
78        * this means the order of the pixel values sent is R1, R2, R3,
79        * ..., G1, G2, G3, ..., B1, B2, B3, etc.
80        **/
81       rowOffset_ = information_.GetWidth() * information_.GetBytesPerValue();
82     }
83     else
84     {
85       /**
86        * The sample values for the first pixel are followed by the
87        * sample values for the second pixel, etc. For RGB images, this
88        * means the order of the pixel values sent shall be R1, G1, B1,
89        * R2, G2, B2, ..., etc.
90        **/
91       rowOffset_ = information_.GetWidth() * information_.GetBytesPerValue() * information_.GetChannelCount();
92     }
93   }
94 
95 
GetExtremeValues(int32_t & min,int32_t & max) const96   void DicomIntegerPixelAccessor::GetExtremeValues(int32_t& min,
97                                                    int32_t& max) const
98   {
99     if (information_.GetHeight() == 0 || information_.GetWidth() == 0)
100     {
101       min = max = 0;
102       return;
103     }
104 
105     min = std::numeric_limits<int32_t>::max();
106     max = std::numeric_limits<int32_t>::min();
107 
108     const unsigned int height = information_.GetHeight();
109     const unsigned int width = information_.GetWidth();
110     const unsigned int channels = information_.GetChannelCount();
111 
112     for (unsigned int y = 0; y < height; y++)
113     {
114       for (unsigned int x = 0; x < width; x++)
115       {
116         for (unsigned int c = 0; c < channels; c++)
117         {
118           int32_t v = GetValue(x, y);
119           if (v < min)
120             min = v;
121           if (v > max)
122             max = v;
123         }
124       }
125     }
126   }
127 
128 
GetValue(unsigned int x,unsigned int y,unsigned int channel) const129   int32_t DicomIntegerPixelAccessor::GetValue(unsigned int x,
130                                               unsigned int y,
131                                               unsigned int channel) const
132   {
133     assert(x < information_.GetWidth() &&
134            y < information_.GetHeight() &&
135            channel < information_.GetChannelCount());
136 
137     const uint8_t* pixel = reinterpret_cast<const uint8_t*>(pixelData_) +
138       y * rowOffset_ + frame_ * frameOffset_;
139 
140     // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#sect_C.7.6.3.1.3
141     if (information_.IsPlanar())
142     {
143       /**
144        * Each color plane shall be sent contiguously. For RGB images,
145        * this means the order of the pixel values sent is R1, R2, R3,
146        * ..., G1, G2, G3, ..., B1, B2, B3, etc.
147        **/
148       assert(frameOffset_ % information_.GetChannelCount() == 0);
149       pixel += channel * frameOffset_ / information_.GetChannelCount() + x * information_.GetBytesPerValue();
150     }
151     else
152     {
153       /**
154        * The sample values for the first pixel are followed by the
155        * sample values for the second pixel, etc. For RGB images, this
156        * means the order of the pixel values sent shall be R1, G1, B1,
157        * R2, G2, B2, ..., etc.
158        **/
159       pixel += channel * information_.GetBytesPerValue() + x * information_.GetChannelCount() * information_.GetBytesPerValue();
160     }
161 
162     uint32_t v;
163     v = pixel[0];
164     if (information_.GetBytesPerValue() >= 2)
165       v = v + (static_cast<uint32_t>(pixel[1]) << 8);
166     if (information_.GetBytesPerValue() >= 3)
167       v = v + (static_cast<uint32_t>(pixel[2]) << 16);
168     if (information_.GetBytesPerValue() >= 4)
169       v = v + (static_cast<uint32_t>(pixel[3]) << 24);
170 
171     v = v >> information_.GetShift();
172 
173     if (v & signMask_)
174     {
175       // Signed value
176       // http://en.wikipedia.org/wiki/Two%27s_complement#Subtraction_from_2N
177       return -static_cast<int32_t>(mask_) + static_cast<int32_t>(v & mask_) - 1;
178     }
179     else
180     {
181       // Unsigned value
182       return static_cast<int32_t>(v & mask_);
183     }
184   }
185 
186 
SetCurrentFrame(unsigned int frame)187   void DicomIntegerPixelAccessor::SetCurrentFrame(unsigned int frame)
188   {
189     if (frame >= information_.GetNumberOfFrames())
190     {
191       throw OrthancException(ErrorCode_ParameterOutOfRange);
192     }
193 
194     frame_ = frame;
195   }
196 
197 }
198