1 //
2 // (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use.
3 //
4 #ifndef CHARLS_PROCESSLINE
5 #define CHARLS_PROCESSLINE
6 
7 #include "util.h"
8 #include "publictypes.h"
9 #include <vector>
10 #include <sstream>
11 #include <cstring>
12 #include <algorithm>
13 
14 
15 //
16 // This file defines the ProcessLine base class, its derivatives and helper functions.
17 // During coding/decoding, CharLS process one line at a time. The different Processline implementations
18 // convert the uncompressed format to and from the internal format for encoding.
19 // Conversions include color transforms, line interleaved vs sample interleaved, masking out unused bits,
20 // accounting for line padding etc.
21 // This mechanism could be used to encode/decode images as they are received.
22 //
23 
24 class ProcessLine
25 {
26 public:
27     virtual ~ProcessLine() = default;
28 
29     ProcessLine(const ProcessLine&) = delete;
30     ProcessLine(ProcessLine&&) = delete;
31     ProcessLine& operator=(const ProcessLine&) = delete;
32     ProcessLine& operator=(ProcessLine&&) = delete;
33 
34     virtual void NewLineDecoded(const void* pSrc, int pixelCount, int sourceStride) = 0;
35     virtual void NewLineRequested(void* pDest, int pixelCount, int destStride) = 0;
36 
37 protected:
38     ProcessLine() = default;
39 };
40 
41 
42 class PostProcesSingleComponent : public ProcessLine
43 {
44 public:
PostProcesSingleComponent(void * rawData,const JlsParameters & params,size_t bytesPerPixel)45     PostProcesSingleComponent(void* rawData, const JlsParameters& params, size_t bytesPerPixel) noexcept :
46         _rawData(static_cast<uint8_t*>(rawData)),
47         _bytesPerPixel(bytesPerPixel),
48         _bytesPerLine(params.stride)
49     {
50     }
51 
52     WARNING_SUPPRESS(26440)
NewLineRequested(void * dest,int pixelCount,int)53     void NewLineRequested(void* dest, int pixelCount, int /*byteStride*/) override
54     {
55         std::memcpy(dest, _rawData, pixelCount * _bytesPerPixel);
56         _rawData += _bytesPerLine;
57     }
58 
NewLineDecoded(const void * pSrc,int pixelCount,int)59     void NewLineDecoded(const void* pSrc, int pixelCount, int /*sourceStride*/) override
60     {
61         std::memcpy(_rawData, pSrc, pixelCount * _bytesPerPixel);
62         _rawData += _bytesPerLine;
63     }
64     WARNING_UNSUPPRESS()
65 
66 private:
67     uint8_t* _rawData;
68     size_t _bytesPerPixel;
69     size_t _bytesPerLine;
70 };
71 
72 
ByteSwap(unsigned char * data,int count)73 inline void ByteSwap(unsigned char* data, int count)
74 {
75     if (static_cast<unsigned int>(count) & 1u)
76     {
77         std::ostringstream message;
78         message << "An odd number of bytes (" << count << ") cannot be swapped.";
79         throw charls_error(charls::ApiResult::InvalidJlsParameters, message.str());
80     }
81 
82     const auto data32 = reinterpret_cast<unsigned int*>(data);
83     for(auto i = 0; i < count / 4; i++)
84     {
85         const auto value = data32[i];
86         data32[i] = ((value >> 8u) & 0x00FF00FFu) | ((value & 0x00FF00FFu) << 8u);
87     }
88 
89     if ((count % 4) != 0)
90     {
91         std::swap(data[count-2], data[count-1]);
92     }
93 }
94 
95 class PostProcesSingleStream : public ProcessLine
96 {
97 public:
PostProcesSingleStream(std::basic_streambuf<char> * rawData,const JlsParameters & params,size_t bytesPerPixel)98     PostProcesSingleStream(std::basic_streambuf<char>* rawData, const JlsParameters& params, size_t bytesPerPixel) noexcept :
99         _rawData(rawData),
100         _bytesPerPixel(bytesPerPixel),
101         _bytesPerLine(params.stride)
102     {
103     }
104 
NewLineRequested(void * dest,int pixelCount,int)105     void NewLineRequested(void* dest, int pixelCount, int /*destStride*/) override
106     {
107         auto bytesToRead = pixelCount * _bytesPerPixel;
108         while (bytesToRead != 0)
109         {
110             const auto bytesRead = _rawData->sgetn(static_cast<char*>(dest), bytesToRead);
111             if (bytesRead == 0)
112                 throw charls_error(charls::ApiResult::UncompressedBufferTooSmall);
113 
114             bytesToRead = static_cast<std::size_t>(bytesToRead - bytesRead);
115         }
116 
117         if (_bytesPerPixel == 2)
118         {
119             ByteSwap(static_cast<unsigned char*>(dest), 2 * pixelCount);
120         }
121 
122         if (_bytesPerLine - pixelCount * _bytesPerPixel > 0)
123         {
124             _rawData->pubseekoff(static_cast<std::streamoff>(_bytesPerLine - bytesToRead), std::ios_base::cur);
125         }
126     }
127 
NewLineDecoded(const void * pSrc,int pixelCount,int)128     void NewLineDecoded(const void* pSrc, int pixelCount, int /*sourceStride*/) override
129     {
130         const auto bytesToWrite = pixelCount * _bytesPerPixel;
131         const auto bytesWritten = static_cast<size_t>(_rawData->sputn(static_cast<const char*>(pSrc), bytesToWrite));
132         if (bytesWritten != bytesToWrite)
133             throw charls_error(charls::ApiResult::UncompressedBufferTooSmall);
134     }
135 
136 private:
137     std::basic_streambuf<char>* _rawData;
138     size_t _bytesPerPixel;
139     size_t _bytesPerLine;
140 };
141 
142 
143 template<typename TRANSFORM, typename T>
TransformLineToQuad(const T * ptypeInput,int32_t pixelStrideIn,Quad<T> * pbyteBuffer,int32_t pixelStride,TRANSFORM & transform)144 void TransformLineToQuad(const T* ptypeInput, int32_t pixelStrideIn, Quad<T>* pbyteBuffer, int32_t pixelStride, TRANSFORM& transform) noexcept
145 {
146     const int cpixel = std::min(pixelStride, pixelStrideIn);
147     Quad<T>* ptypeBuffer = pbyteBuffer;
148 
149     for (auto x = 0; x < cpixel; ++x)
150     {
151         const Quad<T> pixel(transform(ptypeInput[x], ptypeInput[x + pixelStrideIn], ptypeInput[x + 2*pixelStrideIn]), ptypeInput[x + 3 * pixelStrideIn]);
152         ptypeBuffer[x] = pixel;
153     }
154 }
155 
156 
157 template<typename TRANSFORM, typename T>
TransformQuadToLine(const Quad<T> * pbyteInput,int32_t pixelStrideIn,T * ptypeBuffer,int32_t pixelStride,TRANSFORM & transform)158 void TransformQuadToLine(const Quad<T>* pbyteInput, int32_t pixelStrideIn, T* ptypeBuffer, int32_t pixelStride, TRANSFORM& transform) noexcept
159 {
160     const auto cpixel = std::min(pixelStride, pixelStrideIn);
161     const Quad<T>* ptypeBufferIn = pbyteInput;
162 
163     for (auto x = 0; x < cpixel; ++x)
164     {
165         const Quad<T> color = ptypeBufferIn[x];
166         const Quad<T> colorTranformed(transform(color.v1, color.v2, color.v3), color.v4);
167 
168         ptypeBuffer[x] = colorTranformed.v1;
169         ptypeBuffer[x + pixelStride] = colorTranformed.v2;
170         ptypeBuffer[x + 2 * pixelStride] = colorTranformed.v3;
171         ptypeBuffer[x + 3 * pixelStride] = colorTranformed.v4;
172     }
173 }
174 
175 
176 template<typename T>
TransformRgbToBgr(T * pDest,int samplesPerPixel,int pixelCount)177 void TransformRgbToBgr(T* pDest, int samplesPerPixel, int pixelCount) noexcept
178 {
179     for (auto i = 0; i < pixelCount; ++i)
180     {
181         std::swap(pDest[0], pDest[2]);
182         pDest += samplesPerPixel;
183     }
184 }
185 
186 
187 template<typename TRANSFORM, typename T>
TransformLine(Triplet<T> * pDest,const Triplet<T> * pSrc,int pixelCount,TRANSFORM & transform)188 void TransformLine(Triplet<T>* pDest, const Triplet<T>* pSrc, int pixelCount, TRANSFORM& transform) noexcept
189 {
190     for (auto i = 0; i < pixelCount; ++i)
191     {
192         pDest[i] = transform(pSrc[i].v1, pSrc[i].v2, pSrc[i].v3);
193     }
194 }
195 
196 
197 template<typename TRANSFORM, typename T>
TransformLineToTriplet(const T * ptypeInput,int32_t pixelStrideIn,Triplet<T> * pbyteBuffer,int32_t pixelStride,TRANSFORM & transform)198 void TransformLineToTriplet(const T* ptypeInput, int32_t pixelStrideIn, Triplet<T>* pbyteBuffer, int32_t pixelStride, TRANSFORM& transform) noexcept
199 {
200     const auto cpixel = std::min(pixelStride, pixelStrideIn);
201     Triplet<T>* ptypeBuffer = pbyteBuffer;
202 
203     for (auto x = 0; x < cpixel; ++x)
204     {
205         ptypeBuffer[x] = transform(ptypeInput[x], ptypeInput[x + pixelStrideIn], ptypeInput[x + 2*pixelStrideIn]);
206     }
207 }
208 
209 
210 template<typename TRANSFORM, typename T>
TransformTripletToLine(const Triplet<T> * pbyteInput,int32_t pixelStrideIn,T * ptypeBuffer,int32_t pixelStride,TRANSFORM & transform)211 void TransformTripletToLine(const Triplet<T>* pbyteInput, int32_t pixelStrideIn, T* ptypeBuffer, int32_t pixelStride, TRANSFORM& transform) noexcept
212 {
213     const auto cpixel = std::min(pixelStride, pixelStrideIn);
214     const Triplet<T>* ptypeBufferIn = pbyteInput;
215 
216     for (auto x = 0; x < cpixel; ++x)
217     {
218         const Triplet<T> color = ptypeBufferIn[x];
219         const Triplet<T> colorTranformed = transform(color.v1, color.v2, color.v3);
220 
221         ptypeBuffer[x] = colorTranformed.v1;
222         ptypeBuffer[x + pixelStride] = colorTranformed.v2;
223         ptypeBuffer[x + 2 *pixelStride] = colorTranformed.v3;
224     }
225 }
226 
227 
228 template<typename TRANSFORM>
229 class ProcessTransformed : public ProcessLine
230 {
231 public:
ProcessTransformed(ByteStreamInfo rawStream,const JlsParameters & info,TRANSFORM transform)232     ProcessTransformed(ByteStreamInfo rawStream, const JlsParameters& info, TRANSFORM transform) :
233         _params(info),
234         _templine(static_cast<size_t>(info.width) * info.components),
235         _buffer(static_cast<size_t>(info.width) * info.components * sizeof(size_type)),
236         _transform(transform),
237         _inverseTransform(transform),
238         _rawPixels(rawStream)
239     {
240     }
241 
NewLineRequested(void * dest,int pixelCount,int destStride)242     void NewLineRequested(void* dest, int pixelCount, int destStride) override
243     {
244         if (!_rawPixels.rawStream)
245         {
246             Transform(_rawPixels.rawData, dest, pixelCount, destStride);
247             _rawPixels.rawData += _params.stride;
248             return;
249         }
250 
251         Transform(_rawPixels.rawStream, dest, pixelCount, destStride);
252     }
253 
Transform(std::basic_streambuf<char> * rawStream,void * dest,int pixelCount,int destStride)254     void Transform(std::basic_streambuf<char>* rawStream, void* dest, int pixelCount, int destStride)
255     {
256         std::streamsize bytesToRead = static_cast<std::streamsize>(pixelCount) * _params.components * sizeof(size_type);
257         while (bytesToRead != 0)
258         {
259             const auto read = rawStream->sgetn(reinterpret_cast<char*>(_buffer.data()), bytesToRead);
260             if (read == 0)
261             {
262                 std::ostringstream message;
263                 message << "No more bytes available in input buffer, still needing " << read;
264                 throw charls_error(charls::ApiResult::UncompressedBufferTooSmall, message.str());
265             }
266 
267             bytesToRead -= read;
268         }
269         Transform(_buffer.data(), dest, pixelCount, destStride);
270     }
271 
Transform(const void * source,void * dest,int pixelCount,int destStride)272     void Transform(const void* source, void* dest, int pixelCount, int destStride) noexcept
273     {
274         if (_params.outputBgr)
275         {
276             memcpy(_templine.data(), source, sizeof(Triplet<size_type>) * pixelCount);
277             TransformRgbToBgr(_templine.data(), _params.components, pixelCount);
278             source = _templine.data();
279         }
280 
281         if (_params.components == 3)
282         {
283             if (_params.interleaveMode == charls::InterleaveMode::Sample)
284             {
285                 TransformLine(static_cast<Triplet<size_type>*>(dest), static_cast<const Triplet<size_type>*>(source), pixelCount, _transform);
286             }
287             else
288             {
289                 TransformTripletToLine(static_cast<const Triplet<size_type>*>(source), pixelCount, static_cast<size_type*>(dest), destStride, _transform);
290             }
291         }
292         else if (_params.components == 4 && _params.interleaveMode == charls::InterleaveMode::Line)
293         {
294             TransformQuadToLine(static_cast<const Quad<size_type>*>(source), pixelCount, static_cast<size_type*>(dest), destStride, _transform);
295         }
296     }
297 
DecodeTransform(const void * pSrc,void * rawData,int pixelCount,int byteStride)298     void DecodeTransform(const void* pSrc, void* rawData, int pixelCount, int byteStride) noexcept
299     {
300         if (_params.components == 3)
301         {
302             if (_params.interleaveMode == charls::InterleaveMode::Sample)
303             {
304                 TransformLine(static_cast<Triplet<size_type>*>(rawData), static_cast<const Triplet<size_type>*>(pSrc), pixelCount, _inverseTransform);
305             }
306             else
307             {
308                 TransformLineToTriplet(static_cast<const size_type*>(pSrc), byteStride, static_cast<Triplet<size_type>*>(rawData), pixelCount, _inverseTransform);
309             }
310         }
311         else if (_params.components == 4 && _params.interleaveMode == charls::InterleaveMode::Line)
312         {
313             TransformLineToQuad(static_cast<const size_type*>(pSrc), byteStride, static_cast<Quad<size_type>*>(rawData), pixelCount, _inverseTransform);
314         }
315 
316         if (_params.outputBgr)
317         {
318             TransformRgbToBgr(static_cast<size_type*>(rawData), _params.components, pixelCount);
319         }
320     }
321 
NewLineDecoded(const void * pSrc,int pixelCount,int sourceStride)322     void NewLineDecoded(const void* pSrc, int pixelCount, int sourceStride) override
323     {
324         if (_rawPixels.rawStream)
325         {
326             const std::streamsize bytesToWrite = static_cast<std::streamsize>(pixelCount) * _params.components * sizeof(size_type);
327             DecodeTransform(pSrc, _buffer.data(), pixelCount, sourceStride);
328 
329             const auto bytesWritten = _rawPixels.rawStream->sputn(reinterpret_cast<char*>(_buffer.data()), bytesToWrite);
330             if (bytesWritten != bytesToWrite)
331                 throw charls_error(charls::ApiResult::UncompressedBufferTooSmall);
332         }
333         else
334         {
335             DecodeTransform(pSrc, _rawPixels.rawData, pixelCount, sourceStride);
336             _rawPixels.rawData += _params.stride;
337         }
338     }
339 
340 private:
341     using size_type = typename TRANSFORM::size_type;
342 
343     const JlsParameters& _params;
344     std::vector<size_type> _templine;
345     std::vector<uint8_t> _buffer;
346     TRANSFORM _transform;
347     typename TRANSFORM::Inverse _inverseTransform;
348     ByteStreamInfo _rawPixels;
349 };
350 
351 
352 #endif
353