1 /*
2  *  This file is part of RawTherapee.
3  *
4  *  Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
5  *
6  *  RawTherapee is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  RawTherapee is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with RawTherapee.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 #pragma once
20 
21 #include <vector>
22 
23 #include <glibmm/ustring.h>
24 #include <lcms2.h>
25 
26 #include "alignedbuffer.h"
27 #include "coord2d.h"
28 #include "imagedimensions.h"
29 #include "LUT.h"
30 #include "rt_math.h"
31 
32 #include "../rtgui/threadutils.h"
33 
34 #define TR_NONE     0
35 #define TR_R90      1
36 #define TR_R180     2
37 #define TR_R270     3
38 #define TR_VFLIP    4
39 #define TR_HFLIP    8
40 #define TR_ROT      3
41 
42 #define CHECK_BOUNDS 0
43 
44 namespace rtengine
45 {
46 
47 namespace procparams
48 {
49 
50 struct CoarseTransformParams;
51 
52 }
53 
54 class ProgressListener;
55 class Color;
56 
57 extern const char sImage8[];
58 extern const char sImage16[];
59 extern const char sImagefloat[];
60 
61 int getCoarseBitMask(const procparams::CoarseTransformParams& coarse);
62 const LUTf& getigammatab();
63 
64 enum TypeInterpolation { TI_Nearest, TI_Bilinear };
65 
66 // --------------------------------------------------------------------
67 //                       Generic classes
68 // --------------------------------------------------------------------
69 
70 class ImageDatas : virtual public ImageDimensions
71 {
72 public:
73     template<class S, class D>
convertTo(S src,D & dst)74     void convertTo(S src, D& dst) const
75     {
76         dst = src;
77     }
78 
79     // parameters that will never be used, replaced by the subclasses r, g and b parameters!
80     // they are still necessary to implement operator() in this parent class
~ImageDatas()81     virtual ~ImageDatas() {}
allocate(int W,int H)82     virtual void allocate (int W, int H) {}
rotate(int deg)83     virtual void rotate (int deg) {}
84     // free the memory allocated for the image data without deleting the object.
flushData()85     virtual void flushData ()
86     {
87         allocate(0, 0);
88     }
89 
hflip()90     virtual void hflip () {}
vflip()91     virtual void vflip () {}
92 
93     // Read the raw dump of the data
readData(FILE * fh)94     void readData  (FILE *fh) {}
95     // Write a raw dump of the data
writeData(FILE * fh)96     void writeData (FILE *fh) const {}
97 
normalizeInt(int srcMinVal,int srcMaxVal)98     virtual void normalizeInt (int srcMinVal, int srcMaxVal) {};
normalizeFloat(float srcMinVal,float srcMaxVal)99     virtual void normalizeFloat (float srcMinVal, float srcMaxVal) {};
computeHistogramAutoWB(double & avg_r,double & avg_g,double & avg_b,int & n,LUTu & histogram,int compression)100     virtual void computeHistogramAutoWB (double &avg_r, double &avg_g, double &avg_b, int &n, LUTu &histogram, int compression) const {}
getSpotWBData(double & reds,double & greens,double & blues,int & rn,int & gn,int & bn,std::vector<Coord2D> & red,std::vector<Coord2D> & green,std::vector<Coord2D> & blue,int tran)101     virtual void getSpotWBData (double &reds, double &greens, double &blues, int &rn, int &gn, int &bn,
102                                 std::vector<Coord2D> &red, std::vector<Coord2D> &green, std::vector<Coord2D> &blue,
103                                 int tran) const {}
getAutoWBMultipliers(double & rm,double & gm,double & bm)104     virtual void getAutoWBMultipliers (double &rm, double &gm, double &bm) const
105     {
106         rm = gm = bm = 1.0;
107     }
108 
109 };
110 
111 template<>
convertTo(unsigned short src,unsigned char & dst)112 inline void ImageDatas::convertTo(unsigned short src, unsigned char& dst) const
113 {
114     dst = uint16ToUint8Rounded(src);
115 }
116 template<>
convertTo(unsigned char src,int & dst)117 inline void ImageDatas::convertTo(unsigned char src, int& dst) const
118 {
119     dst = src * 257;
120 }
121 template<>
convertTo(unsigned char src,unsigned short & dst)122 inline void ImageDatas::convertTo(unsigned char src, unsigned short& dst) const
123 {
124     dst = src * 257;
125 }
126 template<>
convertTo(float src,unsigned char & dst)127 inline void ImageDatas::convertTo(float src, unsigned char& dst) const
128 {
129     dst = uint16ToUint8Rounded(CLIP(src));
130 }
131 template<>
convertTo(unsigned char src,float & dst)132 inline void ImageDatas::convertTo(unsigned char src, float& dst) const
133 {
134     dst = src * 257;
135 }
136 template<>
convertTo(float src,float & dst)137 inline void ImageDatas::convertTo(float src, float& dst) const
138 {
139     dst = std::isnan(src) ? 0.f : src;
140 }
141 
142 // --------------------------------------------------------------------
143 //                       Planar order classes
144 // --------------------------------------------------------------------
145 
146 template <class T>
147 class PlanarPtr
148 {
149 protected:
150     AlignedBuffer<T*> ab;
151 public:
152 #if CHECK_BOUNDS
153     size_t width_, height_;
154 #endif
155     T** ptrs;
156 
157 #if CHECK_BOUNDS
PlanarPtr()158     PlanarPtr() : width_(0), height_(0), ptrs (NULL) {}
159 #else
PlanarPtr()160     PlanarPtr() : ptrs (nullptr) {}
161 #endif
162 
resize(size_t newSize)163     bool resize(size_t newSize)
164     {
165         if (ab.resize(newSize)) {
166             ptrs = ab.data;
167             return true;
168         } else {
169             ptrs = nullptr;
170             return false;
171         }
172     }
swap(PlanarPtr<T> & other)173     void swap (PlanarPtr<T> &other)
174     {
175         ab.swap(other.ab);
176         T** tmpsPtrs = other.ptrs;
177         other.ptrs = ptrs;
178         ptrs = tmpsPtrs;
179 
180 #if CHECK_BOUNDS
181         size_t tmp = other.width_;
182         other.width_ = width_;
183         width_ = tmp;
184         tmp = other.height_;
185         other.height_ = height_;
186         height_ = tmp;
187 #endif
188     }
189 
operator()190     T*&       operator() (size_t row)
191     {
192 #if CHECK_BOUNDS
193         assert (row < height_);
194 #endif
195         return ptrs[row];
196     }
197     // Will send back the start of a row, starting with a red, green or blue value
operator()198     T*        operator() (size_t row) const
199     {
200 #if CHECK_BOUNDS
201         assert (row < height_);
202 #endif
203         return ptrs[row];
204     }
205     // Will send back a value at a given row, col position
operator()206     T&        operator() (size_t row, size_t col)
207     {
208 #if CHECK_BOUNDS
209         assert (row < height_ && col < width_);
210 #endif
211         return ptrs[row][col];
212     }
operator()213     const T   operator() (size_t row, size_t col) const
214     {
215 #if CHECK_BOUNDS
216         assert (row < height_ && col < width_);
217 #endif
218         return ptrs[row][col];
219     }
220 };
221 
222 template <class T>
223 class PlanarWhateverData : virtual public ImageDatas
224 {
225 
226 private:
227     AlignedBuffer<T> abData;
228 
229     size_t rowstride;    // Plan size, in bytes (all padding bytes included)
230 
231 public:
232     T* data;
233     PlanarPtr<T> v;  // v stands for "value", whatever it represent
234 
PlanarWhateverData()235     PlanarWhateverData() : rowstride(0), data (nullptr) {}
PlanarWhateverData(int w,int h)236     PlanarWhateverData(int w, int h) : rowstride(0), data (nullptr)
237     {
238         allocate(w, h);
239     }
240 
241     // Send back the row stride. WARNING: unit = byte, not element!
getRowStride()242     size_t getRowStride () const
243     {
244         return rowstride;
245     }
246 
swap(PlanarWhateverData<T> & other)247     void swap(PlanarWhateverData<T> &other)
248     {
249         abData.swap(other.abData);
250         v.swap(other.v);
251         T* tmpData = other.data;
252         other.data = data;
253         data = tmpData;
254         int tmpWidth = other.width;
255         other.width = width;
256         width = tmpWidth;
257         int tmpHeight = other.height;
258         other.height = height;
259         height = tmpHeight;
260 #if CHECK_BOUNDS
261         v.width_ = width;
262         v.height_ = height;
263 #endif
264     }
265 
266     // use as pointer to data
267     //operator void*() { return data; };
268 
269     /* If any of the required allocation fails, "width" and "height" are set to -1, and all remaining buffer are freed
270      * Can be safely used to reallocate an existing image */
allocate(int W,int H)271     void allocate (int W, int H) override
272     {
273         if (W == width && H == height) {
274             return;
275         }
276 
277         width = W;
278         height = H;
279 #if CHECK_BOUNDS
280         v.width_ = width;
281         v.height_ = height;
282 #endif
283 
284         if (sizeof(T) > 1) {
285             // 128 bits memory alignment for >8bits data
286             rowstride = ( width * sizeof(T) + 15 ) / 16 * 16;
287         } else {
288             // No memory alignment for 8bits data
289             rowstride = width * sizeof(T);
290         }
291 
292         // find the padding length to ensure a 128 bits alignment for each row
293         size_t size = rowstride * height;
294 
295         if (!width) {
296             size = 0;
297             rowstride = 0;
298         }
299 
300         if (size && abData.resize(size, 1)
301                 && v.resize(height) ) {
302             data   = abData.data;
303         } else {
304             // asking for a new size of 0 is safe and will free memory, if any!
305             abData.resize(0);
306             data = nullptr;
307             v.resize(0);
308             width = height = -1;
309 #if CHECK_BOUNDS
310             v.width_ = v.height_ = -1;
311 #endif
312 
313             return;
314         }
315 
316         char *start   = (char*)(data);
317 
318         for (int i = 0; i < height; ++i) {
319             int k = i * rowstride;
320             v(i) = (T*)(start   + k);
321         }
322     }
323 
324     /** Copy the data to another PlanarWhateverData */
copyData(PlanarWhateverData<T> * dest)325     void copyData(PlanarWhateverData<T> *dest) const
326     {
327         assert (dest != NULL);
328         // Make sure that the size is the same, reallocate if necessary
329         dest->allocate(width, height);
330 
331         if (dest->width == -1) {
332             return;
333         }
334 
335         for (int i = 0; i < height; i++) {
336             memcpy (dest->v(i), v(i), width * sizeof(T));
337         }
338     }
339 
rotate(int deg)340     void rotate (int deg) override
341     {
342 
343         if (deg == 90) {
344             PlanarWhateverData<T> rotatedImg(height, width);  // New image, rotated
345 
346             for (int ny = 0; ny < rotatedImg.height; ny++) {
347                 int ox = ny;
348                 int oy = height - 1;
349 
350                 for (int nx = 0; nx < rotatedImg.width; nx++) {
351                     rotatedImg.v(ny, nx) = v(oy, ox);
352                     --oy;
353                 }
354             }
355 
356             swap(rotatedImg);
357         } else if (deg == 270) {
358             PlanarWhateverData<T> rotatedImg(height, width);  // New image, rotated
359 
360             for (int nx = 0; nx < rotatedImg.width; nx++) {
361                 int oy = nx;
362                 int ox = width - 1;
363 
364                 for (int ny = 0; ny < rotatedImg.height; ny++) {
365                     rotatedImg.v(ny, nx) = v(oy, ox);
366                     --ox;
367                 }
368             }
369 
370             swap(rotatedImg);
371         } else if (deg == 180) {
372             int height2 = height / 2 + (height & 1);
373 
374 #ifdef _OPENMP
375             // difficult to find a cutoff value where parallelization is counter productive because of processor's data cache collision...
376             bool bigImage = width > 32 && height > 50;
377             #pragma omp parallel for schedule(static) if(bigImage)
378 #endif
379 
380             for (int i = 0; i < height2; i++) {
381                 for (int j = 0; j < width; j++) {
382                     T tmp;
383                     int x = width - 1 - j;
384                     int y = height - 1 - i;
385 
386                     tmp = v(i, j);
387                     v(i, j) = v(y, x);
388                     v(y, x) = tmp;
389                 }
390             }
391 #ifdef _OPENMP
392             static_cast<void>(bigImage); // to silence cppcheck warning
393 #endif
394         }
395     }
396 
397     template <class IC>
resizeImgTo(int nw,int nh,TypeInterpolation interp,PlanarWhateverData<IC> * imgPtr)398     void resizeImgTo (int nw, int nh, TypeInterpolation interp, PlanarWhateverData<IC> *imgPtr) const
399     {
400         //printf("resizeImgTo: resizing %s image data (%d x %d) to %s (%d x %d)\n", getType(), width, height, imgPtr->getType(), imgPtr->width, imgPtr->height);
401         if (width == nw && height == nh) {
402             // special case where no resizing is necessary, just type conversion....
403             for (int i = 0; i < height; i++) {
404                 for (int j = 0; j < width; j++) {
405                     convertTo(v(i, j), imgPtr->v(i, j));
406                 }
407             }
408         } else if (interp == TI_Nearest) {
409             for (int i = 0; i < nh; i++) {
410                 int ri = i * height / nh;
411 
412                 for (int j = 0; j < nw; j++) {
413                     int ci = j * width / nw;
414                     convertTo(v(ri, ci), imgPtr->v(i, j));
415                 }
416             }
417         } else if (interp == TI_Bilinear) {
418             for (int i = 0; i < nh; i++) {
419                 int sy = i * height / nh;
420 
421                 if (sy >= height) {
422                     sy = height - 1;
423                 }
424 
425                 float dy = float(i) * float(height) / float(nh) - float(sy);
426                 int ny = sy + 1;
427 
428                 if (ny >= height) {
429                     ny = sy;
430                 }
431 
432                 for (int j = 0; j < nw; j++) {
433                     int sx = j * width / nw;
434 
435                     if (sx >= width) {
436                         sx = width;
437                     }
438 
439                     float dx = float(j) * float(width) / float(nw) - float(sx);
440                     int nx = sx + 1;
441 
442                     if (nx >= width) {
443                         nx = sx;
444                     }
445 
446                     convertTo(v(sy, sx) * (1.f - dx) * (1.f - dy) + v(sy, nx)*dx * (1.f - dy) + v(ny, sx) * (1.f - dx)*dy + v(ny, nx)*dx * dy, imgPtr->v(i, j));
447                 }
448             }
449         } else {
450             // This case should never occur!
451             for (int i = 0; i < nh; i++) {
452                 for (int j = 0; j < nw; j++) {
453                     v(i, j) = 0;
454                 }
455             }
456         }
457     }
458 
hflip()459     void hflip () override
460     {
461         int width2 = width / 2;
462 
463 #ifdef _OPENMP
464         // difficult to find a cutoff value where parallelization is counter productive because of processor's data cache collision...
465         bool bigImage = width > 32 && height > 50;
466         #pragma omp parallel for schedule(static) if(bigImage)
467 #endif
468 
469         for (int i = 0; i < height; i++)
470             for (int j = 0; j < width2; j++) {
471                 float temp;
472                 int x = width - 1 - j;
473 
474                 temp = v(i, j);
475                 v(i, j) = v(i, x);
476                 v(i, x) = temp;
477             }
478 #ifdef _OPENMP
479         static_cast<void>(bigImage); // to silence cppcheck warning
480 #endif
481     }
482 
vflip()483     void vflip () override
484     {
485 
486         int height2 = height / 2;
487 
488 #ifdef _OPENMP
489         // difficult to find a cutoff value where parallelization is counter productive because of processor's data cache collision...
490         bool bigImage = width > 32 && height > 50;
491         #pragma omp parallel for schedule(static) if(bigImage)
492 #endif
493 
494         for (int i = 0; i < height2; i++)
495             for (int j = 0; j < width; j++) {
496                 T temp;
497                 int y = height - 1 - i;
498 
499                 temp = v(i, j);
500                 v(i, j) = v(y, j);
501                 v(y, j) = temp;
502             }
503 #ifdef _OPENMP
504         static_cast<void>(bigImage); // to silence cppcheck warning
505 #endif
506     }
507 
transformPixel(int x,int y,int tran,int & tx,int & ty)508     void transformPixel (int x, int y, int tran, int& tx, int& ty) const
509     {
510 
511         if (!tran) {
512             tx = x;
513             ty = y;
514             return;
515         }
516 
517         int W = width;
518         int H = height;
519         int sw = W, sh = H;
520 
521         if ((tran & TR_ROT) == TR_R90 || (tran & TR_ROT) == TR_R270) {
522             sw = H;
523             sh = W;
524         }
525 
526         int ppx = x, ppy = y;
527 
528         if (tran & TR_HFLIP) {
529             ppx = sw - 1 - x;
530         }
531 
532         if (tran & TR_VFLIP) {
533             ppy = sh - 1 - y;
534         }
535 
536         tx = ppx;
537         ty = ppy;
538 
539         if ((tran & TR_ROT) == TR_R180) {
540             tx = W - 1 - ppx;
541             ty = H - 1 - ppy;
542         } else if ((tran & TR_ROT) == TR_R90) {
543             tx = ppy;
544             ty = H - 1 - ppx;
545         } else if ((tran & TR_ROT) == TR_R270) {
546             tx = W - 1 - ppy;
547             ty = ppx;
548         }
549     }
550 
getPipetteData(T & value,int posX,int posY,int squareSize,int tran)551     void getPipetteData (T &value, int posX, int posY, int squareSize, int tran) const
552     {
553         int x;
554         int y;
555         float accumulator = 0.f;  // using float to avoid range overflow; -> please creates specialization if necessary
556         unsigned long int n = 0;
557         int halfSquare = squareSize / 2;
558         transformPixel (posX, posY, tran, x, y);
559 
560         for (int iy = y - halfSquare; iy < y - halfSquare + squareSize; ++iy) {
561             for (int ix = x - halfSquare; ix < x - halfSquare + squareSize; ++ix) {
562                 if (ix >= 0 && iy >= 0 && ix < width && iy < height) {
563                     accumulator += float(this->v(iy, ix));
564                     ++n;
565                 }
566             }
567         }
568 
569         value = n ? T(accumulator / float(n)) : T(0);
570     }
571 
readData(FILE * f)572     void readData (FILE *f)
573     {
574         for (int i = 0; i < height; i++) {
575             fread (v(i), sizeof(T), width, f);
576         }
577     }
578 
writeData(FILE * f)579     void writeData (FILE *f) const
580     {
581         for (int i = 0; i < height; i++) {
582             fwrite (v(i), sizeof(T), width, f);
583         }
584     }
585 
fill(T value)586     void fill (T value) {
587         for (int i = 0; i < height; i++) {
588             for (int j = 0; j < width; j++) {
589                 v(i, j) = value;
590             }
591         }
592     }
593 
594 };
595 
596 
597 template <class T>
598 class PlanarRGBData : virtual public ImageDatas
599 {
600 
601 private:
602     AlignedBuffer<T> abData;
603 
604     size_t rowstride;    // Plan size, in bytes (all padding bytes included)
605     size_t planestride;  // Row length, in bytes (padding bytes included)
606 protected:
607     T* data;
608 
609 public:
610     PlanarPtr<T> r;
611     PlanarPtr<T> g;
612     PlanarPtr<T> b;
613 
PlanarRGBData()614     PlanarRGBData() : rowstride(0), planestride(0), data (nullptr) {}
PlanarRGBData(size_t w,size_t h)615     PlanarRGBData(size_t w, size_t h) : rowstride(0), planestride(0), data (nullptr)
616     {
617         allocate(w, h);
618     }
619 
620     // Send back the row stride. WARNING: unit = byte, not element!
getRowStride()621     size_t getRowStride () const
622     {
623         return rowstride;
624     }
625     // Send back the plane stride. WARNING: unit = byte, not element!
getPlaneStride()626     size_t getPlaneStride () const
627     {
628         return planestride;
629     }
630 
swap(PlanarRGBData<T> & other)631     void swap(PlanarRGBData<T> &other)
632     {
633         abData.swap(other.abData);
634         r.swap(other.r);
635         g.swap(other.g);
636         b.swap(other.b);
637         T* tmpData = other.data;
638         other.data = data;
639         data = tmpData;
640         int tmpWidth = other.width;
641         other.width = width;
642         width = tmpWidth;
643         int tmpHeight = other.height;
644         other.height = height;
645         height = tmpHeight;
646 #if CHECK_BOUNDS
647         r.width_ = width;
648         r.height_ = height;
649         g.width_ = width;
650         g.height_ = height;
651         b.width_ = width;
652         b.height_ = height;
653 #endif
654     }
655 
656     // use as pointer to data
657     //operator void*() { return data; };
658 
659     /* If any of the required allocation fails, "width" and "height" are set to -1, and all remaining buffer are freed
660      * Can be safely used to reallocate an existing image */
allocate(int W,int H)661     void allocate (int W, int H) override
662     {
663 
664         if (W == width && H == height) {
665             return;
666         }
667 
668         width = W;
669         height = H;
670 #if CHECK_BOUNDS
671         r.width_ = width;
672         r.height_ = height;
673         g.width_ = width;
674         g.height_ = height;
675         b.width_ = width;
676         b.height_ = height;
677 #endif
678 
679         if (sizeof(T) > 1) {
680             // 128 bits memory alignment for >8bits data
681             rowstride = ( width * sizeof(T) + 15 ) / 16 * 16;
682             planestride = rowstride * height;
683         } else {
684             // No memory alignment for 8bits data
685             rowstride = width * sizeof(T);
686             planestride = rowstride * height;
687         }
688 
689         // find the padding length to ensure a 128 bits alignment for each row
690         size_t size = (size_t)rowstride * 3 * (size_t)height;
691 
692         if (!width) {
693             size = 0;
694             rowstride = 0;
695         }
696 
697         if (size && abData.resize(size, 1)
698                 && r.resize(height)
699                 && g.resize(height)
700                 && b.resize(height) ) {
701             data   = abData.data;
702         } else {
703             // asking for a new size of 0 is safe and will free memory, if any!
704             abData.resize(0);
705             data = nullptr;
706             r.resize(0);
707             g.resize(0);
708             b.resize(0);
709             width = height = -1;
710 #if CHECK_BOUNDS
711             r.width_ = r.height_ = -1;
712             g.width_ = g.height_ = -1;
713             b.width_ = b.height_ = -1;
714 #endif
715             return;
716         }
717 
718         char *redstart   = (char*)(data);
719         char *greenstart = (char*)(data) +   planestride;
720         char *bluestart  = (char*)(data) + 2 * planestride;
721 
722         for (int i = 0; i < height; ++i) {
723             size_t k = i * rowstride;
724             r(i) = (T*)(redstart   + k);
725             g(i) = (T*)(greenstart + k);
726             b(i) = (T*)(bluestart  + k);
727         }
728     }
729 
730     /** Copy the data to another PlanarRGBData */
copyData(PlanarRGBData<T> * dest)731     void copyData(PlanarRGBData<T> *dest) const
732     {
733         assert (dest != nullptr);
734         // Make sure that the size is the same, reallocate if necessary
735         dest->allocate(width, height);
736 
737         if (dest->width == -1) {
738             printf("ERROR: PlanarRGBData::copyData >>> allocation failed!\n");
739             return;
740         }
741 
742         for (int i = 0; i < height; i++) {
743             memcpy (dest->r(i), r(i), width * sizeof(T));
744             memcpy (dest->g(i), g(i), width * sizeof(T));
745             memcpy (dest->b(i), b(i), width * sizeof(T));
746         }
747     }
748 
rotate(int deg)749     void rotate (int deg) override
750     {
751 
752         if (deg == 90) {
753             PlanarRGBData<T> rotatedImg(height, width);  // New image, rotated
754 
755             for (int ny = 0; ny < rotatedImg.height; ny++) {
756                 int ox = ny;
757                 int oy = height - 1;
758 
759                 for (int nx = 0; nx < rotatedImg.width; nx++) {
760                     rotatedImg.r(ny, nx) = r(oy, ox);
761                     rotatedImg.g(ny, nx) = g(oy, ox);
762                     rotatedImg.b(ny, nx) = b(oy, ox);
763                     --oy;
764                 }
765             }
766 
767             swap(rotatedImg);
768         } else if (deg == 270) {
769             PlanarRGBData<T> rotatedImg(height, width);  // New image, rotated
770 
771             for (int nx = 0; nx < rotatedImg.width; nx++) {
772                 int oy = nx;
773                 int ox = width - 1;
774 
775                 for (int ny = 0; ny < rotatedImg.height; ny++) {
776                     rotatedImg.r(ny, nx) = r(oy, ox);
777                     rotatedImg.g(ny, nx) = g(oy, ox);
778                     rotatedImg.b(ny, nx) = b(oy, ox);
779                     --ox;
780                 }
781             }
782 
783             swap(rotatedImg);
784         } else if (deg == 180) {
785             int height2 = height / 2 + (height & 1);
786 
787 #ifdef _OPENMP
788             // difficult to find a cutoff value where parallelization is counter productive because of processor's data cache collision...
789             bool bigImage = width > 32 && height > 50;
790             #pragma omp parallel for schedule(static) if(bigImage)
791 #endif
792 
793             for (int i = 0; i < height2; i++) {
794                 for (int j = 0; j < width; j++) {
795                     T tmp;
796                     int x = width - 1 - j;
797                     int y = height - 1 - i;
798 
799                     tmp = r(i, j);
800                     r(i, j) = r(y, x);
801                     r(y, x) = tmp;
802 
803                     tmp = g(i, j);
804                     g(i, j) = g(y, x);
805                     g(y, x) = tmp;
806 
807                     tmp = b(i, j);
808                     b(i, j) = b(y, x);
809                     b(y, x) = tmp;
810                 }
811             }
812 #ifdef _OPENMP
813             static_cast<void>(bigImage); // to silence cppcheck warning
814 #endif
815         }
816     }
817 
818     template <class IC>
resizeImgTo(int nw,int nh,TypeInterpolation interp,IC * imgPtr)819     void resizeImgTo (int nw, int nh, TypeInterpolation interp, IC *imgPtr) const
820     {
821         //printf("resizeImgTo: resizing %s image data (%d x %d) to %s (%d x %d)\n", getType(), width, height, imgPtr->getType(), imgPtr->width, imgPtr->height);
822         if (width == nw && height == nh) {
823             // special case where no resizing is necessary, just type conversion....
824             for (int i = 0; i < height; i++) {
825                 for (int j = 0; j < width; j++) {
826                     convertTo(r(i, j), imgPtr->r(i, j));
827                     convertTo(g(i, j), imgPtr->g(i, j));
828                     convertTo(b(i, j), imgPtr->b(i, j));
829                 }
830             }
831         } else if (interp == TI_Nearest) {
832             for (int i = 0; i < nh; i++) {
833                 int ri = i * height / nh;
834 
835                 for (int j = 0; j < nw; j++) {
836                     int ci = j * width / nw;
837                     convertTo(r(ri, ci), imgPtr->r(i, j));
838                     convertTo(g(ri, ci), imgPtr->g(i, j));
839                     convertTo(b(ri, ci), imgPtr->b(i, j));
840                 }
841             }
842         } else if (interp == TI_Bilinear) {
843             float heightByNh = float(height) / float(nh);
844             float widthByNw = float(width) / float(nw);
845             float syf = 0.f;
846 
847             for (int i = 0; i < nh; i++, syf += heightByNh) {
848                 int sy = syf;
849                 float dy = syf - float(sy);
850                 int ny = sy < height - 1 ? sy + 1 : sy;
851 
852                 float sxf = 0.f;
853 
854                 for (int j = 0; j < nw; j++, sxf += widthByNw) {
855                     int sx = sxf;
856                     float dx = sxf - float(sx);
857                     int nx = sx < width - 1 ? sx + 1 : sx;
858 
859                     convertTo(r(sy, sx) * (1.f - dx) * (1.f - dy) + r(sy, nx)*dx * (1.f - dy) + r(ny, sx) * (1.f - dx)*dy + r(ny, nx)*dx * dy, imgPtr->r(i, j));
860                     convertTo(g(sy, sx) * (1.f - dx) * (1.f - dy) + g(sy, nx)*dx * (1.f - dy) + g(ny, sx) * (1.f - dx)*dy + g(ny, nx)*dx * dy, imgPtr->g(i, j));
861                     convertTo(b(sy, sx) * (1.f - dx) * (1.f - dy) + b(sy, nx)*dx * (1.f - dy) + b(ny, sx) * (1.f - dx)*dy + b(ny, nx)*dx * dy, imgPtr->b(i, j));
862                 }
863             }
864         } else {
865             // This case should never occur!
866             for (int i = 0; i < nh; i++) {
867                 for (int j = 0; j < nw; j++) {
868                     imgPtr->r(i, j) = 0;
869                     imgPtr->g(i, j) = 0;
870                     imgPtr->b(i, j) = 0;
871                 }
872             }
873         }
874     }
875 
hflip()876     void hflip () override
877     {
878         int width2 = width / 2;
879 
880 #ifdef _OPENMP
881         // difficult to find a cutoff value where parallelization is counter productive because of processor's data cache collision...
882         bool bigImage = width > 32 && height > 50;
883         #pragma omp parallel for schedule(static) if(bigImage)
884 #endif
885 
886         for (int i = 0; i < height; i++)
887             for (int j = 0; j < width2; j++) {
888                 float temp;
889                 int x = width - 1 - j;
890 
891                 temp = r(i, j);
892                 r(i, j) = r(i, x);
893                 r(i, x) = temp;
894 
895                 temp = g(i, j);
896                 g(i, j) = g(i, x);
897                 g(i, x) = temp;
898 
899                 temp = b(i, j);
900                 b(i, j) = b(i, x);
901                 b(i, x) = temp;
902             }
903 #ifdef _OPENMP
904         static_cast<void>(bigImage); // to silence cppcheck warning
905 #endif
906     }
907 
vflip()908     void vflip () override
909     {
910 
911         int height2 = height / 2;
912 
913 #ifdef _OPENMP
914         // difficult to find a cutoff value where parallelization is counter productive because of processor's data cache collision...
915         bool bigImage = width > 32 && height > 50;
916         #pragma omp parallel for schedule(static) if(bigImage)
917 #endif
918 
919         for (int i = 0; i < height2; i++)
920             for (int j = 0; j < width; j++) {
921                 T tempR, tempG, tempB;
922                 int y = height - 1 - i;
923 
924                 tempR = r(i, j);
925                 r(i, j) = r(y, j);
926                 r(y, j) = tempR;
927 
928                 tempG = g(i, j);
929                 g(i, j) = g(y, j);
930                 g(y, j) = tempG;
931 
932                 tempB = b(i, j);
933                 b(i, j) = b(y, j);
934                 b(y, j) = tempB;
935             }
936 #ifdef _OPENMP
937         static_cast<void>(bigImage); // to silence cppcheck warning
938 #endif
939     }
940 
calcGrayscaleHist(unsigned int * hist16)941     void calcGrayscaleHist(unsigned int *hist16) const
942     {
943         for (int row = 0; row < height; row++)
944             for (int col = 0; col < width; col++) {
945                 unsigned short rIdx, gIdx, bIdx;
946                 convertTo(r(row, col), rIdx);
947                 convertTo(g(row, col), gIdx);
948                 convertTo(b(row, col), bIdx);
949                 hist16[rIdx]++;
950                 hist16[gIdx] += 2; // Bayer 2x green correction
951                 hist16[bIdx]++;
952             }
953     }
954 
computeAutoHistogram(LUTu & histogram,int & histcompr)955     void computeAutoHistogram (LUTu & histogram, int& histcompr) const
956     {
957         histcompr = 3;
958 
959         histogram(65536 >> histcompr);
960         histogram.clear();
961         const LUTf& igammatab = getigammatab();
962 
963 #ifdef _OPENMP
964         #pragma omp parallel
965 #endif
966         {
967             LUTu histThr(histogram.getSize());
968             histThr.clear();
969 #ifdef _OPENMP
970             #pragma omp for schedule(dynamic,16) nowait
971 #endif
972             for (int i = 0; i < height; i++) {
973                 for (int j = 0; j < width; j++) {
974                     float r_, g_, b_;
975                     convertTo<T, float>(r(i, j), r_);
976                     convertTo<T, float>(g(i, j), g_);
977                     convertTo<T, float>(b(i, j), b_);
978                     histThr[static_cast<int>(igammatab[r_]) >> histcompr]++;
979                     histThr[static_cast<int>(igammatab[g_]) >> histcompr]++;
980                     histThr[static_cast<int>(igammatab[b_]) >> histcompr]++;
981                 }
982             }
983 #ifdef _OPENMP
984             #pragma omp critical
985 #endif
986             {
987                 histogram += histThr;
988             }
989         }
990     }
991 
computeHistogramAutoWB(double & avg_r,double & avg_g,double & avg_b,int & n,LUTu & histogram,const int compression)992     void computeHistogramAutoWB (double &avg_r, double &avg_g, double &avg_b, int &n, LUTu &histogram, const int compression) const override
993     {
994         histogram.clear();
995         avg_r = avg_g = avg_b = 0.;
996         n = 0;
997         const LUTf& igammatab = getigammatab();
998         for (unsigned int i = 0; i < (unsigned int)(height); i++)
999             for (unsigned int j = 0; j < (unsigned int)(width); j++) {
1000                 float r_, g_, b_;
1001                 convertTo<T, float>(r(i, j), r_);
1002                 convertTo<T, float>(g(i, j), g_);
1003                 convertTo<T, float>(b(i, j), b_);
1004                 int rtemp = igammatab[r_];
1005                 int gtemp = igammatab[g_];
1006                 int btemp = igammatab[b_];
1007 
1008                 histogram[rtemp >> compression]++;
1009                 histogram[gtemp >> compression] += 2;
1010                 histogram[btemp >> compression]++;
1011 
1012                 // autowb computation
1013                 if (r_ > 64000.f || g_ > 64000.f || b_ > 64000.f) {
1014                     continue;
1015                 }
1016 
1017                 avg_r += double(r_);
1018                 avg_g += double(g_);
1019                 avg_b += double(b_);
1020                 n++;
1021             }
1022     }
1023 
getAutoWBMultipliers(double & rm,double & gm,double & bm)1024     void getAutoWBMultipliers (double &rm, double &gm, double &bm) const override
1025     {
1026 
1027         double avg_r = 0.;
1028         double avg_g = 0.;
1029         double avg_b = 0.;
1030         int n = 0;
1031         //int p = 6;
1032 
1033 #ifdef _OPENMP
1034         #pragma omp parallel for reduction(+:avg_r,avg_g,avg_b,n) schedule(dynamic,16)
1035 #endif
1036         for (unsigned int i = 0; i < (unsigned int)(height); i++)
1037             for (unsigned int j = 0; j < (unsigned int)(width); j++) {
1038                 float r_, g_, b_;
1039                 convertTo<T, float>(r(i, j), r_);
1040                 convertTo<T, float>(g(i, j), g_);
1041                 convertTo<T, float>(b(i, j), b_);
1042 
1043                 if (r_ > 64000.f || g_ > 64000.f || b_ > 64000.f) {
1044                     continue;
1045                 }
1046 
1047                 avg_r += double(r_);
1048                 avg_g += double(g_);
1049                 avg_b += double(b_);
1050                 n++;
1051             }
1052 
1053         rm = avg_r / double(n);
1054         gm = avg_g / double(n);
1055         bm = avg_b / double(n);
1056     }
1057 
transformPixel(int x,int y,int tran,int & tx,int & ty)1058     void transformPixel (int x, int y, int tran, int& tx, int& ty) const
1059     {
1060 
1061         if (!tran) {
1062             tx = x;
1063             ty = y;
1064             return;
1065         }
1066 
1067         int W = width;
1068         int H = height;
1069         int sw = W, sh = H;
1070 
1071         if ((tran & TR_ROT) == TR_R90 || (tran & TR_ROT) == TR_R270) {
1072             sw = H;
1073             sh = W;
1074         }
1075 
1076         int ppx = x, ppy = y;
1077 
1078         if (tran & TR_HFLIP) {
1079             ppx = sw - 1 - x;
1080         }
1081 
1082         if (tran & TR_VFLIP) {
1083             ppy = sh - 1 - y;
1084         }
1085 
1086         tx = ppx;
1087         ty = ppy;
1088 
1089         if ((tran & TR_ROT) == TR_R180) {
1090             tx = W - 1 - ppx;
1091             ty = H - 1 - ppy;
1092         } else if ((tran & TR_ROT) == TR_R90) {
1093             tx = ppy;
1094             ty = H - 1 - ppx;
1095         } else if ((tran & TR_ROT) == TR_R270) {
1096             tx = W - 1 - ppy;
1097             ty = ppx;
1098         }
1099     }
1100 
getSpotWBData(double & reds,double & greens,double & blues,int & rn,int & gn,int & bn,std::vector<Coord2D> & red,std::vector<Coord2D> & green,std::vector<Coord2D> & blue,int tran)1101     void getSpotWBData (double &reds, double &greens, double &blues, int &rn, int &gn, int &bn,
1102                                 std::vector<Coord2D> &red, std::vector<Coord2D> &green, std::vector<Coord2D> &blue,
1103                                 int tran) const override
1104     {
1105         int x;
1106         int y;
1107         reds = 0, greens = 0, blues = 0;
1108         rn = 0, gn = 0, bn = 0;
1109 
1110         for (size_t i = 0; i < red.size(); i++) {
1111             transformPixel (red[i].x, red[i].y, tran, x, y);
1112 
1113             if (x >= 0 && y >= 0 && x < width && y < height) {
1114                 float v;
1115                 convertTo<T, float>(this->r(y, x), v);
1116                 reds += double(v);
1117                 rn++;
1118             }
1119 
1120             transformPixel (green[i].x, green[i].y, tran, x, y);
1121 
1122             if (x >= 0 && y >= 0 && x < width && y < height) {
1123                 float v;
1124                 convertTo<T, float>(this->g(y, x), v);
1125                 greens += double(v);
1126                 gn++;
1127             }
1128 
1129             transformPixel (blue[i].x, blue[i].y, tran, x, y);
1130 
1131             if (x >= 0 && y >= 0 && x < width && y < height) {
1132                 float v;
1133                 convertTo<T, float>(this->b(y, x), v);
1134                 blues += double(v);
1135                 bn++;
1136             }
1137         }
1138     }
1139 
getPipetteData(T & valueR,T & valueG,T & valueB,int posX,int posY,const int squareSize,int tran)1140     void getPipetteData (T &valueR, T &valueG, T &valueB, int posX, int posY, const int squareSize, int tran) const
1141     {
1142         int x;
1143         int y;
1144         float accumulatorR = 0.f;  // using float to avoid range overflow; -> please creates specialization if necessary
1145         float accumulatorG = 0.f;  //    "
1146         float accumulatorB = 0.f;  //    "
1147         unsigned long int n = 0;
1148         int halfSquare = squareSize / 2;
1149         transformPixel (posX, posY, tran, x, y);
1150 
1151         for (int iy = y - halfSquare; iy < y - halfSquare + squareSize; ++iy) {
1152             for (int ix = x - halfSquare; ix < x - halfSquare + squareSize; ++ix) {
1153                 if (ix >= 0 && iy >= 0 && ix < width && iy < height) {
1154                     accumulatorR += float(this->r(iy, ix));
1155                     accumulatorG += float(this->g(iy, ix));
1156                     accumulatorB += float(this->b(iy, ix));
1157                     ++n;
1158                 }
1159             }
1160         }
1161 
1162         valueR = n ? T(accumulatorR / float(n)) : T(0);
1163         valueG = n ? T(accumulatorG / float(n)) : T(0);
1164         valueB = n ? T(accumulatorB / float(n)) : T(0);
1165     }
1166 
readData(FILE * f)1167     void readData (FILE *f)
1168     {
1169         for (int i = 0; i < height; i++) {
1170             if (fread(r(i), sizeof(T), width, f) < static_cast<size_t>(width)) {
1171                 break;
1172             }
1173         }
1174 
1175         for (int i = 0; i < height; i++) {
1176             if (fread(g(i), sizeof(T), width, f) < static_cast<size_t>(width)) {
1177                 break;
1178             }
1179         }
1180 
1181         for (int i = 0; i < height; i++) {
1182             if (fread(b(i), sizeof(T), width, f) < static_cast<size_t>(width)) {
1183                 break;
1184             }
1185         }
1186     }
1187 
writeData(FILE * f)1188     void writeData (FILE *f) const
1189     {
1190         for (int i = 0; i < height; i++) {
1191             fwrite (r(i), sizeof(T), width, f);
1192         }
1193 
1194         for (int i = 0; i < height; i++) {
1195             fwrite (g(i), sizeof(T), width, f);
1196         }
1197 
1198         for (int i = 0; i < height; i++) {
1199             fwrite (b(i), sizeof(T), width, f);
1200         }
1201     }
1202 
1203 };
1204 
1205 // --------------------------------------------------------------------
1206 //                       Chunky order classes
1207 // --------------------------------------------------------------------
1208 
1209 template <class T>
1210 class ChunkyPtr
1211 {
1212 private:
1213     T* ptr;
1214     ssize_t width;
1215 public:
1216 #if CHECK_BOUNDS
1217     size_t width_, height_;
1218 #endif
1219 
1220 #if CHECK_BOUNDS
ChunkyPtr()1221     ChunkyPtr() : ptr (NULL), width(-1), width_(0), height_(0) {}
1222 #else
ChunkyPtr()1223     ChunkyPtr() : ptr (nullptr), width(-1) {}
1224 #endif
1225     void init(T* base, ssize_t w = -1)
1226     {
1227         ptr = base;
1228         width = w;
1229     }
swap(ChunkyPtr<T> & other)1230     void swap (ChunkyPtr<T> &other)
1231     {
1232         T* tmpsPtr = other.ptr;
1233         other.ptr = ptr;
1234         ptr = tmpsPtr;
1235 
1236         ssize_t tmpWidth = other.width;
1237         other.width = width;
1238         width = tmpWidth;
1239 
1240 #if CHECK_BOUNDS
1241         size_t tmp = other.width_;
1242         other.width_ = width_;
1243         width_ = tmp;
1244         tmp = other.height_;
1245         other.height_ = height_;
1246         height_ = tmp;
1247 #endif
1248 
1249     }
1250 
1251     // Will send back the start of a row, starting with a red, green or blue value
operator()1252     T* operator() (size_t row) const
1253     {
1254 #if CHECK_BOUNDS
1255         assert (row < height_);
1256 #endif
1257         return &ptr[3 * (row * width)];
1258     }
1259     // Will send back a value at a given row, col position
operator()1260     T& operator() (size_t row, size_t col)
1261     {
1262 #if CHECK_BOUNDS
1263         assert (row < height_ && col < width_);
1264 #endif
1265         return ptr[3 * (row * width + col)];
1266     }
operator()1267     const T  operator() (size_t row, size_t col) const
1268     {
1269 #if CHECK_BOUNDS
1270         assert (row < height_ && col < width_);
1271 #endif
1272         return ptr[3 * (row * width + col)];
1273     }
1274 };
1275 
1276 template <class T>
1277 class ChunkyRGBData : virtual public ImageDatas
1278 {
1279 
1280 private:
1281     AlignedBuffer<T> abData;
1282 
1283 public:
1284     T* data;
1285     ChunkyPtr<T> r;
1286     ChunkyPtr<T> g;
1287     ChunkyPtr<T> b;
1288 
ChunkyRGBData()1289     ChunkyRGBData() : data (nullptr) {}
ChunkyRGBData(int w,int h)1290     ChunkyRGBData(int w, int h) : data (nullptr)
1291     {
1292         allocate(w, h);
1293     }
1294 
1295     /** Returns the pixel data, in r/g/b order from top left to bottom right continuously.
1296       * @return a pointer to the pixel data */
getData()1297     const T* getData ()
1298     {
1299         return data;
1300     }
1301 
swap(ChunkyRGBData<T> & other)1302     void swap(ChunkyRGBData<T> &other)
1303     {
1304         abData.swap(other.abData);
1305         r.swap(other.r);
1306         g.swap(other.g);
1307         b.swap(other.b);
1308         T* tmpData = other.data;
1309         other.data = data;
1310         data = tmpData;
1311         int tmpWidth = other.width;
1312         other.width = width;
1313         width = tmpWidth;
1314         int tmpHeight = other.height;
1315         other.height = height;
1316         height = tmpHeight;
1317 #if CHECK_BOUNDS
1318         r.width_ = width;
1319         r.height_ = height;
1320         g.width_ = width;
1321         g.height_ = height;
1322         b.width_ = width;
1323         b.height_ = height;
1324 #endif
1325     }
1326 
1327     /*
1328      * If any of the required allocation fails, "width" and "height" are set to -1, and all remaining buffer are freed
1329      * Can be safely used to reallocate an existing image or to free up it's memory with "allocate (0,0);"
1330      */
allocate(int W,int H)1331     void allocate (int W, int H) override
1332     {
1333 
1334         if (W == width && H == height) {
1335             return;
1336         }
1337 
1338         width = W;
1339         height = H;
1340 #if CHECK_BOUNDS
1341         r.width_ = width;
1342         r.height_ = height;
1343         g.width_ = width;
1344         g.height_ = height;
1345         b.width_ = width;
1346         b.height_ = height;
1347 #endif
1348 
1349         abData.resize((size_t)width * (size_t)height * (size_t)3);
1350 
1351         if (!abData.isEmpty()) {
1352             data = abData.data;
1353             r.init(data,   width);
1354             g.init(data + 1, width);
1355             b.init(data + 2, width);
1356         } else {
1357             data = nullptr;
1358             r.init(nullptr);
1359             g.init(nullptr);
1360             b.init(nullptr);
1361             width = height = -1;
1362 #if CHECK_BOUNDS
1363             r.width_ = r.height_ = -1;
1364             g.width_ = g.height_ = -1;
1365             b.width_ = b.height_ = -1;
1366 #endif
1367         }
1368     }
1369 
1370     /** Copy the data to another ChunkyRGBData */
copyData(ChunkyRGBData<T> * dest)1371     void copyData(ChunkyRGBData<T> *dest) const
1372     {
1373         assert (dest != nullptr);
1374         // Make sure that the size is the same, reallocate if necessary
1375         dest->allocate(width, height);
1376 
1377         if (dest->width == -1) {
1378             printf("ERROR: ChunkyRGBData::copyData >>> allocation failed!\n");
1379             return;
1380         }
1381 
1382         memcpy (dest->data, data, 3 * width * height * sizeof(T));
1383     }
1384 
rotate(int deg)1385     void rotate (int deg) override
1386     {
1387 
1388         if (deg == 90) {
1389             ChunkyRGBData<T> rotatedImg(height, width);  // New image, rotated
1390 
1391             for (int ny = 0; ny < rotatedImg.height; ny++) {
1392                 int ox = ny;
1393                 int oy = height - 1;
1394 
1395                 for (int nx = 0; nx < rotatedImg.width; nx++) {
1396                     rotatedImg.r(ny, nx) = r(oy, ox);
1397                     rotatedImg.g(ny, nx) = g(oy, ox);
1398                     rotatedImg.b(ny, nx) = b(oy, ox);
1399                     --oy;
1400                 }
1401             }
1402 
1403             swap(rotatedImg);
1404         } else if (deg == 270) {
1405             ChunkyRGBData<T> rotatedImg(height, width);  // New image, rotated
1406 
1407             for (int nx = 0; nx < rotatedImg.width; nx++) {
1408                 int oy = nx;
1409                 int ox = width - 1;
1410 
1411                 for (int ny = 0; ny < rotatedImg.height; ny++) {
1412                     rotatedImg.r(ny, nx) = r(oy, ox);
1413                     rotatedImg.g(ny, nx) = g(oy, ox);
1414                     rotatedImg.b(ny, nx) = b(oy, ox);
1415                     --ox;
1416                 }
1417             }
1418 
1419             swap(rotatedImg);
1420         } else if (deg == 180) {
1421             int height2 = height / 2 + (height & 1);
1422 
1423             // Maybe not sufficiently optimized, but will do what it has to do
1424             for (int i = 0; i < height2; i++) {
1425                 for (int j = 0; j < width; j++) {
1426                     T tmp;
1427                     int x = width - 1 - j;
1428                     int y = height - 1 - i;
1429 
1430                     tmp = r(i, j);
1431                     r(i, j) = r(y, x);
1432                     r(y, x) = tmp;
1433 
1434                     tmp = g(i, j);
1435                     g(i, j) = g(y, x);
1436                     g(y, x) = tmp;
1437 
1438                     tmp = b(i, j);
1439                     b(i, j) = b(y, x);
1440                     b(y, x) = tmp;
1441                 }
1442             }
1443         }
1444     }
1445 
1446     template <class IC>
resizeImgTo(int nw,int nh,TypeInterpolation interp,IC * imgPtr)1447     void resizeImgTo (int nw, int nh, TypeInterpolation interp, IC *imgPtr) const
1448     {
1449         //printf("resizeImgTo: resizing %s image data (%d x %d) to %s (%d x %d)\n", getType(), width, height, imgPtr->getType(), imgPtr->width, imgPtr->height);
1450         if (width == nw && height == nh) {
1451             // special case where no resizing is necessary, just type conversion....
1452             for (int i = 0; i < height; i++) {
1453                 for (int j = 0; j < width; j++) {
1454                     convertTo(r(i, j), imgPtr->r(i, j));
1455                     convertTo(g(i, j), imgPtr->g(i, j));
1456                     convertTo(b(i, j), imgPtr->b(i, j));
1457                 }
1458             }
1459         } else if (interp == TI_Nearest) {
1460             for (int i = 0; i < nh; i++) {
1461                 int ri = i * height / nh;
1462 
1463                 for (int j = 0; j < nw; j++) {
1464                     int ci = j * width / nw;
1465                     convertTo(r(ri, ci), imgPtr->r(i, j));
1466                     convertTo(g(ri, ci), imgPtr->g(i, j));
1467                     convertTo(b(ri, ci), imgPtr->b(i, j));
1468                 }
1469             }
1470         } else if (interp == TI_Bilinear) {
1471             for (int i = 0; i < nh; i++) {
1472                 int sy = i * height / nh;
1473 
1474                 if (sy >= height) {
1475                     sy = height - 1;
1476                 }
1477 
1478                 float dy = float(i) * float(height) / float(nh) - float(sy);
1479                 int ny = sy + 1;
1480 
1481                 if (ny >= height) {
1482                     ny = sy;
1483                 }
1484 
1485                 for (int j = 0; j < nw; j++) {
1486                     int sx = j * width / nw;
1487 
1488                     if (sx >= width) {
1489                         sx = width;
1490                     }
1491 
1492                     float dx = float(j) * float(width) / float(nw) - float(sx);
1493                     int nx = sx + 1;
1494 
1495                     if (nx >= width) {
1496                         nx = sx;
1497                     }
1498 
1499                     T valR = r(sy, sx) * (1.f - dx) * (1.f - dy) + r(sy, nx) * dx * (1.f - dy) + r(ny, sx) * (1.f - dx) * dy + r(ny, nx) * dx * dy;
1500                     T valG = g(sy, sx) * (1.f - dx) * (1.f - dy) + g(sy, nx) * dx * (1.f - dy) + g(ny, sx) * (1.f - dx) * dy + g(ny, nx) * dx * dy;
1501                     T valB = b(sy, sx) * (1.f - dx) * (1.f - dy) + b(sy, nx) * dx * (1.f - dy) + b(ny, sx) * (1.f - dx) * dy + b(ny, nx) * dx * dy;
1502                     convertTo(valR, imgPtr->r(i, j));
1503                     convertTo(valG, imgPtr->g(i, j));
1504                     convertTo(valB, imgPtr->b(i, j));
1505                 }
1506             }
1507         } else {
1508             // This case should never occur!
1509             for (int i = 0; i < nh; i++) {
1510                 for (int j = 0; j < nw; j++) {
1511                     imgPtr->r(i, j) = 0;
1512                     imgPtr->g(i, j) = 0;
1513                     imgPtr->b(i, j) = 0;
1514                 }
1515             }
1516         }
1517     }
1518 
hflip()1519     void hflip () override
1520     {
1521         int width2 = width / 2;
1522 
1523         for (int i = 0; i < height; i++) {
1524             int offsetBegin = 0;
1525             int offsetEnd = 3 * (width - 1);
1526 
1527             for (int j = 0; j < width2; j++) {
1528                 T temp;
1529 
1530                 temp = data[offsetBegin];
1531                 data[offsetBegin] = data[offsetEnd];
1532                 data[offsetEnd] = temp;
1533 
1534                 ++offsetBegin;
1535                 ++offsetEnd;
1536 
1537                 temp = data[offsetBegin];
1538                 data[offsetBegin] = data[offsetEnd];
1539                 data[offsetEnd] = temp;
1540 
1541                 ++offsetBegin;
1542                 ++offsetEnd;
1543 
1544                 temp = data[offsetBegin];
1545                 data[offsetBegin] = data[offsetEnd];
1546                 data[offsetEnd] = temp;
1547 
1548                 ++offsetBegin;
1549                 offsetEnd -= 5;
1550 
1551             }
1552         }
1553     }
1554 
vflip()1555     void vflip () override
1556     {
1557 
1558         AlignedBuffer<T> lBuffer(3 * width);
1559         T* lineBuffer = lBuffer.data;
1560         size_t size = 3 * width * sizeof(T);
1561 
1562         for (int i = 0; i < height / 2; i++) {
1563             T *lineBegin1 = r(i);
1564             T *lineBegin2 = r(height - 1 - i);
1565             memcpy (lineBuffer, lineBegin1, size);
1566             memcpy (lineBegin1, lineBegin2, size);
1567             memcpy (lineBegin2, lineBuffer, size);
1568         }
1569     }
1570 
calcGrayscaleHist(unsigned int * hist16)1571     void calcGrayscaleHist(unsigned int *hist16) const
1572     {
1573         for (int row = 0; row < height; row++)
1574             for (int col = 0; col < width; col++) {
1575                 unsigned short rIdx, gIdx, bIdx;
1576                 convertTo(r(row, col), rIdx);
1577                 convertTo(g(row, col), gIdx);
1578                 convertTo(b(row, col), bIdx);
1579                 hist16[rIdx]++;
1580                 hist16[gIdx] += 2; // Bayer 2x green correction
1581                 hist16[bIdx]++;
1582             }
1583     }
1584 
computeAutoHistogram(LUTu & histogram,int & histcompr)1585     void computeAutoHistogram (LUTu & histogram, int& histcompr) const
1586     {
1587         histcompr = 3;
1588 
1589         histogram(65536 >> histcompr);
1590         histogram.clear();
1591         const LUTf& igammatab = getigammatab();
1592 
1593 #ifdef _OPENMP
1594         #pragma omp parallel
1595 #endif
1596         {
1597             LUTu histThr(histogram.getSize());
1598             histThr.clear();
1599 #ifdef _OPENMP
1600             #pragma omp for schedule(dynamic,16) nowait
1601 #endif
1602             for (int i = 0; i < height; i++) {
1603                 for (int j = 0; j < width; j++) {
1604                     float r_, g_, b_;
1605                     convertTo<T, float>(r(i, j), r_);
1606                     convertTo<T, float>(g(i, j), g_);
1607                     convertTo<T, float>(b(i, j), b_);
1608                     histThr[static_cast<int>(igammatab[r_]) >> histcompr]++;
1609                     histThr[static_cast<int>(igammatab[g_]) >> histcompr]++;
1610                     histThr[static_cast<int>(igammatab[b_]) >> histcompr]++;
1611                 }
1612             }
1613 #ifdef _OPENMP
1614             #pragma omp critical
1615 #endif
1616             {
1617                 histogram += histThr;
1618             }
1619         }
1620     }
1621 
computeHistogramAutoWB(double & avg_r,double & avg_g,double & avg_b,int & n,LUTu & histogram,const int compression)1622     void computeHistogramAutoWB (double &avg_r, double &avg_g, double &avg_b, int &n, LUTu &histogram, const int compression) const override
1623     {
1624         histogram.clear();
1625         avg_r = avg_g = avg_b = 0.;
1626         n = 0;
1627         const LUTf& igammatab = getigammatab();
1628 
1629         for (unsigned int i = 0; i < (unsigned int)(height); i++)
1630             for (unsigned int j = 0; j < (unsigned int)(width); j++) {
1631                 float r_, g_, b_;
1632                 convertTo<T, float>(r(i, j), r_);
1633                 convertTo<T, float>(g(i, j), g_);
1634                 convertTo<T, float>(b(i, j), b_);
1635                 int rtemp = igammatab[r_];
1636                 int gtemp = igammatab[g_];
1637                 int btemp = igammatab[b_];
1638 
1639                 histogram[rtemp >> compression]++;
1640                 histogram[gtemp >> compression] += 2;
1641                 histogram[btemp >> compression]++;
1642 
1643                 // autowb computation
1644                 if (r_ > 64000.f || g_ > 64000.f || b_ > 64000.f) {
1645                     continue;
1646                 }
1647 
1648                 avg_r += double(r_);
1649                 avg_g += double(g_);
1650                 avg_b += double(b_);
1651                 n++;
1652             }
1653     }
1654 
getAutoWBMultipliers(double & rm,double & gm,double & bm)1655     void getAutoWBMultipliers (double &rm, double &gm, double &bm) const override
1656     {
1657 
1658         double avg_r = 0.;
1659         double avg_g = 0.;
1660         double avg_b = 0.;
1661         int n = 0;
1662         //int p = 6;
1663 
1664 #ifdef _OPENMP
1665         #pragma omp parallel for reduction(+:avg_r,avg_g,avg_b,n) schedule(dynamic,16)
1666 #endif
1667         for (unsigned int i = 0; i < (unsigned int)(height); i++)
1668             for (unsigned int j = 0; j < (unsigned int)(width); j++) {
1669                 float r_, g_, b_;
1670                 convertTo<T, float>(r(i, j), r_);
1671                 convertTo<T, float>(g(i, j), g_);
1672                 convertTo<T, float>(b(i, j), b_);
1673 
1674                 if (r_ > 64000.f || g_ > 64000.f || b_ > 64000.f) {
1675                     continue;
1676                 }
1677 
1678                 avg_r += double(r_);
1679                 avg_g += double(g_);
1680                 avg_b += double(b_);
1681                 n++;
1682             }
1683 
1684         rm = avg_r / double(n);
1685         gm = avg_g / double(n);
1686         bm = avg_b / double(n);
1687     }
1688 
transformPixel(int x,int y,int tran,int & tx,int & ty)1689     void transformPixel (int x, int y, int tran, int& tx, int& ty) const
1690     {
1691 
1692         if (!tran) {
1693             tx = x;
1694             ty = y;
1695             return;
1696         }
1697 
1698         int W = width;
1699         int H = height;
1700         int sw = W, sh = H;
1701 
1702         if ((tran & TR_ROT) == TR_R90 || (tran & TR_ROT) == TR_R270) {
1703             sw = H;
1704             sh = W;
1705         }
1706 
1707         int ppx = x, ppy = y;
1708 
1709         if (tran & TR_HFLIP) {
1710             ppx = sw - 1 - x;
1711         }
1712 
1713         if (tran & TR_VFLIP) {
1714             ppy = sh - 1 - y;
1715         }
1716 
1717         tx = ppx;
1718         ty = ppy;
1719 
1720         if ((tran & TR_ROT) == TR_R180) {
1721             tx = W - 1 - ppx;
1722             ty = H - 1 - ppy;
1723         } else if ((tran & TR_ROT) == TR_R90) {
1724             tx = ppy;
1725             ty = H - 1 - ppx;
1726         } else if ((tran & TR_ROT) == TR_R270) {
1727             tx = W - 1 - ppy;
1728             ty = ppx;
1729         }
1730     }
1731 
getSpotWBData(double & reds,double & greens,double & blues,int & rn,int & gn,int & bn,std::vector<Coord2D> & red,std::vector<Coord2D> & green,std::vector<Coord2D> & blue,int tran)1732     void getSpotWBData (double &reds, double &greens, double &blues, int &rn, int &gn, int &bn,
1733                                 std::vector<Coord2D> &red, std::vector<Coord2D> &green, std::vector<Coord2D> &blue,
1734                                 int tran) const override
1735     {
1736         int x;
1737         int y;
1738         reds = 0, greens = 0, blues = 0;
1739         rn = 0, gn = 0, bn = 0;
1740 
1741         for (size_t i = 0; i < red.size(); i++) {
1742             transformPixel (red[i].x, red[i].y, tran, x, y);
1743 
1744             if (x >= 0 && y >= 0 && x < width && y < height) {
1745                 float v;
1746                 convertTo<T, float>(this->r(y, x), v);
1747                 reds += double(v);
1748                 rn++;
1749             }
1750 
1751             transformPixel (green[i].x, green[i].y, tran, x, y);
1752 
1753             if (x >= 0 && y >= 0 && x < width && y < height) {
1754                 float v;
1755                 convertTo<T, float>(this->g(y, x), v);
1756                 greens += double(v);
1757                 gn++;
1758             }
1759 
1760             transformPixel (blue[i].x, blue[i].y, tran, x, y);
1761 
1762             if (x >= 0 && y >= 0 && x < width && y < height) {
1763                 float v;
1764                 convertTo<T, float>(this->b(y, x), v);
1765                 blues += double(v);
1766                 bn++;
1767             }
1768         }
1769     }
1770 
readData(FILE * f)1771     void readData (FILE *f)
1772     {
1773         for (int i = 0; i < height; i++) {
1774             if (fread(r(i), sizeof(T), 3 * width, f) < 3 * static_cast<size_t>(width)) {
1775                 break;
1776             }
1777         }
1778     }
1779 
writeData(FILE * f)1780     void writeData (FILE *f) const
1781     {
1782         for (int i = 0; i < height; i++) {
1783             fwrite (r(i), sizeof(T), 3 * width, f);
1784         }
1785     }
1786 
1787 };
1788 
1789 // --------------------------------------------------------------------
1790 
1791 
1792 /** @brief This class represents an image (the result of the image processing) */
1793 class IImage : virtual public ImageDimensions
1794 {
1795 public:
1796 
~IImage()1797     virtual ~IImage() {}
1798     /** @brief Returns a mutex that can is useful in many situations. No image operations shuold be performed without locking this mutex.
1799       * @return The mutex */
1800     virtual MyMutex& getMutex () = 0;
1801     virtual cmsHPROFILE getProfile () const = 0;
1802     /** @brief Returns the bits per pixel of the image.
1803       * @return The bits per pixel of the image */
1804     virtual int getBitsPerPixel () const = 0;
1805     /** @brief Saves the image to file. It autodetects the format (jpg, tif, png are supported).
1806       * @param fname is the name of the file
1807         @return the error code, 0 if none */
1808     virtual int saveToFile (const Glib::ustring &fname) const = 0;
1809     /** @brief Saves the image to file in a png format.
1810       * @param fname is the name of the file
1811       * @param compression is the amount of compression (0-6), -1 corresponds to the default
1812       * @param bps can be 8 or 16 depending on the bits per pixels the output file will have
1813         @return the error code, 0 if none */
1814     virtual int saveAsPNG (const Glib::ustring &fname, int bps = -1) const = 0;
1815     /** @brief Saves the image to file in a jpg format.
1816       * @param fname is the name of the file
1817       * @param quality is the quality of the jpeg (0...100), set it to -1 to use default
1818         @return the error code, 0 if none */
1819     virtual int saveAsJPEG (const Glib::ustring &fname, int quality = 100, int subSamp = 3 ) const = 0;
1820     /** @brief Saves the image to file in a tif format.
1821       * @param fname is the name of the file
1822       * @param bps can be 8 or 16 depending on the bits per pixels the output file will have
1823       * @param isFloat is true for saving float images. Will be ignored by file format not supporting float data
1824         @return the error code, 0 if none */
1825     virtual int saveAsTIFF (const Glib::ustring &fname, int bps = -1, bool isFloat = false, bool uncompressed = false) const = 0;
1826     /** @brief Sets the progress listener if you want to follow the progress of the image saving operations (optional).
1827       * @param pl is the pointer to the class implementing the ProgressListener interface */
1828     virtual void setSaveProgressListener (ProgressListener* pl) = 0;
1829     /** @brief Free the image */
1830     virtual void free () = 0;
1831 };
1832 
1833 /** @brief This class represents an image having a float pixel planar representation.
1834     The planes are stored as two dimensional arrays. All the rows have a 128 bits alignment. */
1835 class IImagefloat : public IImage, public PlanarRGBData<float>
1836 {
1837 public:
~IImagefloat()1838     ~IImagefloat() override {}
1839 };
1840 
1841 /** @brief This class represents an image having a classical 8 bits/pixel representation */
1842 class IImage8 : public IImage, public ChunkyRGBData<unsigned char>
1843 {
1844 public:
~IImage8()1845     ~IImage8() override {}
1846 };
1847 
1848 /** @brief This class represents an image having a 16 bits/pixel planar representation.
1849   The planes are stored as two dimensional arrays. All the rows have a 128 bits alignment. */
1850 class IImage16 : public IImage, public PlanarRGBData<unsigned short>
1851 {
1852 public:
~IImage16()1853     ~IImage16() override {}
1854 };
1855 
1856 }
1857