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 <http://www.gnu.org/licenses/>.
18  */
19 #include "rtengine.h"
20 #include "rtthumbnail.h"
21 #include "../rtgui/options.h"
22 #include "image8.h"
23 #include <lcms2.h>
24 #include "curves.h"
25 #include <glibmm.h>
26 #include "improcfun.h"
27 #include "colortemp.h"
28 #include "mytime.h"
29 #include "utils.h"
30 #include "iccstore.h"
31 #include "iccmatrices.h"
32 #include "rawimagesource.h"
33 #include "stdimagesource.h"
34 #include <glib/gstdio.h>
35 #include "rawimage.h"
36 #include "rtjpeg.h"
37 #include "../rtgui/ppversion.h"
38 #include "improccoordinator.h"
39 #include "settings.h"
40 #include <locale.h>
41 #include "median.h"
42 #define BENCHMARK
43 #include "StopWatch.h"
44 
45 namespace {
46 
checkRawImageThumb(const rtengine::RawImage & raw_image)47 bool checkRawImageThumb (const rtengine::RawImage& raw_image)
48 {
49     return raw_image.checkThumbOk();
50 }
51 
52 
scale_colors(rtengine::RawImage * ri,float scale_mul[4],float cblack[4],bool multiThread)53 void scale_colors (rtengine::RawImage *ri, float scale_mul[4], float cblack[4], bool multiThread)
54 {
55     rtengine::RawImage::ImageType image = ri->get_image();
56     const int height = ri->get_iheight();
57     const int width = ri->get_iwidth();
58     const int top_margin = ri->get_topmargin();
59     const int left_margin = ri->get_leftmargin();
60     const int raw_width = ri->get_rawwidth();
61     const bool isFloat = ri->isFloat();
62     const float * const float_raw_image = ri->get_FloatRawImage();
63 
64     if (ri->isBayer()) {
65 #ifdef _OPENMP
66         #pragma omp parallel for if(multiThread)
67 #endif
68         for (int row = 0; row < height; ++row) {
69             unsigned c0 = ri->FC (row, 0);
70             unsigned c1 = ri->FC (row, 1);
71             int col = 0;
72 
73             for (; col < width - 1; col += 2) {
74                 float val0;
75                 float val1;
76                 if (isFloat) {
77                     val0 = float_raw_image[(row + top_margin) * raw_width + col + left_margin];
78                     val1 = float_raw_image[(row + top_margin) * raw_width + col + left_margin + 1];
79                 } else {
80                     val0 = image[row * width + col][c0];
81                     val1 = image[row * width + col + 1][c1];
82                 }
83                 val0 -= cblack[c0];
84                 val1 -= cblack[c1];
85                 val0 *= scale_mul[c0];
86                 val1 *= scale_mul[c1];
87                 image[row * width + col][c0] = rtengine::CLIP (val0);
88                 image[row * width + col + 1][c1] = rtengine::CLIP (val1);
89             }
90 
91             if (col < width) { // in case width is odd
92                 float val0;
93                 if (isFloat) {
94                     val0 = float_raw_image[(row + top_margin) * raw_width + col + left_margin];
95                 } else {
96                     val0 = image[row * width + col][c0];
97                 }
98                 val0 -= cblack[c0];
99                 val0 *= scale_mul[c0];
100                 image[row * width + col][c0] = rtengine::CLIP (val0);
101             }
102         }
103     } else if (ri->isXtrans()) {
104 #ifdef _OPENMP
105         #pragma omp parallel for if(multiThread)
106 #endif
107         for (int row = 0; row < height; ++row) {
108             unsigned c[6];
109             for (int i = 0; i < 6; ++i) {
110                 c[i] = ri->XTRANSFC (row, i);
111             }
112 
113             int col = 0;
114 
115             for (; col < width - 5; col += 6) {
116                 for (int i = 0; i < 6; ++i) {
117                     const unsigned ccol = c[i];
118                     float val;
119                     if (isFloat) {
120                         val = float_raw_image[(row + top_margin) * raw_width + col + i + left_margin];
121                     } else {
122                         val = image[row * width + col + i][ccol];
123                     }
124                     val -= cblack[ccol];
125                     val *= scale_mul[ccol];
126                     image[row * width + col + i][ccol] = rtengine::CLIP (val);
127                 }
128             }
129 
130             for (; col < width; ++col) { // remaining columns
131                 const unsigned ccol = ri->XTRANSFC (row, col);
132                 float val;
133                 if (isFloat) {
134                     val = float_raw_image[(row + top_margin) * raw_width + col + left_margin];
135                 } else {
136                     val = image[row * width + col][ccol];
137                 }
138                 val -= cblack[ccol];
139                 val *= scale_mul[ccol];
140                 image[row * width + col][ccol] = rtengine::CLIP (val);
141             }
142         }
143     } else if (isFloat) {
144 #ifdef _OPENMP
145         #pragma omp parallel for if(multiThread)
146 #endif
147         for (int row = 0; row < height; ++row) {
148             for (int col = 0; col < width; ++col) {
149                 for (int i = 0; i < ri->get_colors(); ++i) {
150                     float val = float_raw_image[(row + top_margin) * raw_width + col + left_margin + i];
151                     val -= cblack[i];
152                     val *= scale_mul[i];
153                     image[row * width + col][i] = val;
154                 }
155             }
156         }
157     } else {
158         const int size = ri->get_iheight() * ri->get_iwidth();
159 
160 #ifdef _OPENMP
161         #pragma omp parallel for if(multiThread)
162 #endif
163         for (int i = 0; i < size; ++i) {
164             for (int j = 0; j < 4; ++j) {
165                 float val = image[i][j];
166                 val -= cblack[j];
167                 val *= scale_mul[j];
168                 image[i][j] = rtengine::CLIP (val);
169             }
170         }
171     }
172 }
173 
174 } // namespace
175 
176 extern Options options;
177 
178 namespace rtengine {
179 
180 extern const Settings *settings;
181 
182 using namespace procparams;
183 
loadFromImage(const Glib::ustring & fname,int & w,int & h,int fixwh,double wbEq)184 Thumbnail* Thumbnail::loadFromImage (const Glib::ustring& fname, int &w, int &h, int fixwh, double wbEq)
185 {
186 
187     StdImageSource imgSrc;
188 
189     if (imgSrc.load(fname, std::max(w, 0), std::max(h, 0))) {
190         return nullptr;
191     }
192 
193     ImageIO* img = imgSrc.getImageIO();
194 
195     Thumbnail* tpp = new Thumbnail ();
196 
197     unsigned char* data;
198     img->getEmbeddedProfileData (tpp->embProfileLength, data);
199 
200     if (data && tpp->embProfileLength) {
201         tpp->embProfileData = new unsigned char [tpp->embProfileLength];
202         memcpy (tpp->embProfileData, data, tpp->embProfileLength);
203     }
204 
205     tpp->scaleForSave = 8192;
206     tpp->defGain = 1.0;
207     tpp->gammaCorrected = false;
208     tpp->isRaw = 0;
209     tpp->sensorType = ST_NONE;
210     memset (tpp->colorMatrix, 0, sizeof (tpp->colorMatrix));
211     tpp->colorMatrix[0][0] = 1.0;
212     tpp->colorMatrix[1][1] = 1.0;
213     tpp->colorMatrix[2][2] = 1.0;
214 
215     if (fixwh < 0 && w > 0 && h > 0) {
216         int ww = h * img->getWidth() / img->getHeight();
217         int hh = w * img->getHeight() / img->getWidth();
218         if (ww <= w) {
219             w = ww;
220             tpp->scale = double(img->getHeight()) / h;
221         } else {
222             h = hh;
223             tpp->scale = double(img->getWidth()) / w;
224         }
225     } else if (fixwh == 1) {
226         w = h * img->getWidth() / img->getHeight();
227         tpp->scale = (double)img->getHeight() / h;
228     } else {
229         h = w * img->getHeight() / img->getWidth();
230         tpp->scale = (double)img->getWidth() / w;
231     }
232 
233     h = std::max(h, 1);
234     w = std::max(w, 1);
235 
236     // bilinear interpolation
237     if (tpp->thumbImg) {
238         delete tpp->thumbImg;
239         tpp->thumbImg = nullptr;
240     }
241 
242     // we want the same image type than the source file
243     tpp->thumbImg = resizeToSameType (w, h, TI_Bilinear, img);
244 
245     // histogram computation
246     tpp->aeHistCompression = 3;
247     tpp->aeHistogram (65536 >> tpp->aeHistCompression);
248 
249     double avg_r = 0;
250     double avg_g = 0;
251     double avg_b = 0;
252     int n = 0;
253 
254     if (img->getType() == rtengine::sImage8) {
255         Image8 *image = static_cast<Image8*> (img);
256         image->computeHistogramAutoWB (avg_r, avg_g, avg_b, n, tpp->aeHistogram, tpp->aeHistCompression);
257     } else if (img->getType() == sImage16) {
258         Image16 *image = static_cast<Image16*> (img);
259         image->computeHistogramAutoWB (avg_r, avg_g, avg_b, n, tpp->aeHistogram, tpp->aeHistCompression);
260     } else if (img->getType() == sImagefloat) {
261         Imagefloat *image = static_cast<Imagefloat*> (img);
262         image->computeHistogramAutoWB (avg_r, avg_g, avg_b, n, tpp->aeHistogram, tpp->aeHistCompression);
263     } else {
264         printf ("loadFromImage: Unsupported image type \"%s\"!\n", img->getType());
265     }
266 
267     // ProcParams paramsForAutoExp; // Dummy for constructor
268     // ImProcFunctions ipf (&paramsForAutoExp, false);
269     // ipf.getAutoExp (tpp->aeHistogram, tpp->aeHistCompression, 0.02, tpp->aeExposureCompensation, tpp->aeLightness, tpp->aeContrast, tpp->aeBlack, tpp->aeHighlightCompression, tpp->aeHighlightCompressionThreshold);
270     // tpp->aeValid = true;
271 
272     if (n > 0) {
273         ColorTemp cTemp;
274 
275         tpp->redAWBMul   = avg_r / double (n);
276         tpp->greenAWBMul = avg_g / double (n);
277         tpp->blueAWBMul  = avg_b / double (n);
278         tpp->wbEqual = wbEq;
279 
280         cTemp.mul2temp (tpp->redAWBMul, tpp->greenAWBMul, tpp->blueAWBMul, tpp->wbEqual, tpp->autoWBTemp, tpp->autoWBGreen);
281     }
282 
283     tpp->init ();
284 
285     return tpp;
286 }
287 
288 
loadQuickFromRaw(const Glib::ustring & fname,eSensorType & sensorType,int & w,int & h,int fixwh,bool rotate,bool forHistogramMatching)289 Thumbnail* Thumbnail::loadQuickFromRaw (const Glib::ustring& fname, eSensorType &sensorType, int &w, int &h, int fixwh, bool rotate, bool forHistogramMatching)
290 {
291     Thumbnail* tpp = new Thumbnail ();
292     tpp->isRaw = 1;
293     tpp->sensorType = sensorType;
294     memset (tpp->colorMatrix, 0, sizeof (tpp->colorMatrix));
295     tpp->colorMatrix[0][0] = 1.0;
296     tpp->colorMatrix[1][1] = 1.0;
297     tpp->colorMatrix[2][2] = 1.0;
298 
299     RawImage *ri = new RawImage (fname);
300     unsigned int imageNum = 0;
301     int r = ri->loadRaw (false, imageNum, false);
302 
303     if ( r ) {
304         delete tpp;
305         delete ri;
306         sensorType = ST_NONE;
307         return nullptr;
308     }
309 
310     sensorType = ri->getSensorType();
311 
312     Image8* img = new Image8 ();
313     // No sample format detection occurred earlier, so we set them here,
314     // as they are mandatory for the setScanline method
315     img->setSampleFormat (IIOSF_UNSIGNED_CHAR);
316     img->setSampleArrangement (IIOSA_CHUNKY);
317 
318     int err = 1;
319 
320     // See if it is something we support
321     if (checkRawImageThumb (*ri)) {
322         const char* data ((const char*)fdata (ri->get_thumbOffset(), ri->get_file()));
323 
324         if ( (unsigned char)data[1] == 0xd8 ) {
325             err = img->loadJPEGFromMemory (data, ri->get_thumbLength());
326         } else if (ri->is_ppmThumb()) {
327             err = img->loadPPMFromMemory (data, ri->get_thumbWidth(), ri->get_thumbHeight(), ri->get_thumbSwap(), ri->get_thumbBPS());
328         }
329     }
330 
331     // did we succeed?
332     if ( err ) {
333         if (options.rtSettings.verbose) {
334             std::cout << "Could not extract thumb from " << fname.c_str() << std::endl;
335         }
336         delete tpp;
337         delete img;
338         delete ri;
339         return nullptr;
340     }
341 
342     if (forHistogramMatching) {
343         // Special case, meaning that we want a full sized thumbnail image
344         w = img->getWidth();
345         h = img->getHeight();
346         tpp->scale = 1.;
347     } else {
348         if (fixwh == 1) {
349             w = h * img->getWidth() / img->getHeight();
350             tpp->scale = (double)img->getHeight() / h;
351         } else {
352             h = w * img->getHeight() / img->getWidth();
353             tpp->scale = (double)img->getWidth() / w;
354         }
355     }
356 
357     if (tpp->thumbImg) {
358         delete tpp->thumbImg;
359         tpp->thumbImg = nullptr;
360     }
361 
362     if (forHistogramMatching) {
363         tpp->thumbImg = img;
364     } else {
365         tpp->thumbImg = resizeTo<Image8> (w, h, TI_Nearest, img);
366         delete img;
367     }
368 
369     if (rotate && ri->get_rotateDegree() > 0 && ri->thumbNeedsRotation()) {
370         tpp->thumbImg->rotate(ri->get_rotateDegree());
371         // width/height may have changed after rotating
372         w = tpp->thumbImg->getWidth();
373         h = tpp->thumbImg->getHeight();
374     }
375 
376     if (!forHistogramMatching) {
377         tpp->init ();
378     }
379 
380     delete ri;
381 
382     return tpp;
383 }
384 
385 #define FISRED(filter,row,col) \
386     ((filter >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3)==0 || !filter)
387 #define FISGREEN(filter,row,col) \
388     ((filter >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3)==1 || !filter)
389 #define FISBLUE(filter,row,col) \
390     ((filter >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3)==2 || !filter)
391 
loadFromRaw(const Glib::ustring & fname,eSensorType & sensorType,int & w,int & h,int fixwh,double wbEq,bool rotate,bool forHistogramMatching)392 Thumbnail* Thumbnail::loadFromRaw (const Glib::ustring& fname, eSensorType &sensorType, int &w, int &h, int fixwh, double wbEq, bool rotate, bool forHistogramMatching)
393 {
394     RawImage *ri = new RawImage (fname);
395     unsigned int tempImageNum = 0;
396 
397     int r = ri->loadRaw(true, tempImageNum, false, nullptr, 1.0, false);
398 
399     if ( r ) {
400         delete ri;
401         sensorType = ST_NONE;
402         return nullptr;
403     }
404 
405     if (ri->getFrameCount() == 7) {
406         // special case for Hasselblad H6D-100cMS pixelshift files
407         // first frame is not bayer, load second frame
408         int r = ri->loadRaw(true, 1, false, nullptr, 1.0, false);
409 
410         if ( r ) {
411             delete ri;
412             sensorType = ST_NONE;
413             return nullptr;
414         }
415     }
416     sensorType = ri->getSensorType();
417 
418     int width = ri->get_width();
419     int height = ri->get_height();
420     rtengine::Thumbnail* tpp = new rtengine::Thumbnail;
421 
422     tpp->isRaw = true;
423     tpp->sensorType = sensorType;
424     tpp->embProfile = nullptr;
425     tpp->embProfileData = nullptr;
426     tpp->embProfileLength = ri->get_profileLen();
427 
428     if (ri->get_profileLen())
429         tpp->embProfile = cmsOpenProfileFromMem (ri->get_profile(),
430                           ri->get_profileLen()); //\ TODO check if mutex is needed
431 
432     tpp->redMultiplier = ri->get_pre_mul (0);
433     tpp->greenMultiplier = ri->get_pre_mul (1);
434     tpp->blueMultiplier = ri->get_pre_mul (2);
435 
436     float pre_mul[4], scale_mul[4], cblack[4];
437     ri->get_colorsCoeff (pre_mul, scale_mul, cblack, false);
438     scale_colors (ri, scale_mul, cblack, forHistogramMatching); // enable multithreading when forHistogramMatching is true
439 
440     ri->pre_interpolate();
441 
442     tpp->camwbRed = tpp->redMultiplier / pre_mul[0]; //ri->get_pre_mul(0);
443     tpp->camwbGreen = tpp->greenMultiplier / pre_mul[1]; //ri->get_pre_mul(1);
444     tpp->camwbBlue = tpp->blueMultiplier / pre_mul[2]; //ri->get_pre_mul(2);
445     //tpp->defGain = 1.0 / min(ri->get_pre_mul(0), ri->get_pre_mul(1), ri->get_pre_mul(2));
446     tpp->defGain = max (scale_mul[0], scale_mul[1], scale_mul[2], scale_mul[3]) / min (scale_mul[0], scale_mul[1], scale_mul[2], scale_mul[3]);
447     tpp->defGain *= std::pow(2, ri->getBaselineExposure());
448     tpp->scaleGain = scale_mul[0] / pre_mul[0]; // used to reconstruct scale_mul from filmnegativethumb.cc
449 
450     tpp->gammaCorrected = true;
451 
452     unsigned filter = ri->get_filters();
453     int firstgreen = 1;
454 
455     // locate first green location in the first row
456     if (ri->getSensorType() == ST_BAYER)
457         while (!FISGREEN (filter, 1, firstgreen) && firstgreen < 3) {
458             firstgreen++;
459         }
460 
461     int skip = 1;
462 
463     if (ri->get_FujiWidth() != 0) {
464         if (fixwh == 1) { // fix height, scale width
465             skip = ((ri->get_height() - ri->get_FujiWidth()) / sqrt (0.5) - firstgreen - 1) / h;
466         } else {
467             skip = (ri->get_FujiWidth() / sqrt (0.5) - firstgreen - 1) / w;
468         }
469     } else {
470         if (fixwh == 1) { // fix height, scale width
471             skip = (ri->get_height() - firstgreen - 1) / h;
472         } else {
473             skip = (ri->get_width() - firstgreen - 1) / w;
474         }
475     }
476 
477     if (skip % 2) {
478         skip--;
479     }
480 
481     if (skip < 2) {
482         skip = 2;
483     }
484 
485     int hskip = skip, vskip = skip;
486 
487     if (!ri->get_model().compare ("D1X")) {
488         hskip *= 2;
489     }
490 
491     int rofs = 0;
492     int tmpw = (width - 2) / hskip;
493     int tmph = (height - 2) / vskip;
494 
495     rtengine::RawImage::ImageType image = ri->get_image();
496 
497     Imagefloat* tmpImg = new Imagefloat (tmpw, tmph);
498 
499     if (ri->getSensorType() == ST_BAYER) {
500         // demosaicing! (sort of)
501         for (int row = 1, y = 0; row < height - 1 && y < tmph; row += vskip, y++) {
502             rofs = row * width;
503 
504             for (int col = firstgreen, x = 0; col < width - 1 && x < tmpw; col += hskip, x++) {
505                 int ofs = rofs + col;
506                 int g = image[ofs][1];
507                 int r, b;
508 
509                 if (FISRED (filter, row, col + 1)) {
510                     r = (image[ofs + 1    ][0] + image[ofs - 1    ][0]) >> 1;
511                     b = (image[ofs + width][2] + image[ofs - width][2]) >> 1;
512                 } else {
513                     b = (image[ofs + 1    ][2] + image[ofs - 1    ][2]) >> 1;
514                     r = (image[ofs + width][0] + image[ofs - width][0]) >> 1;
515                 }
516 
517                 tmpImg->r (y, x) = r;
518                 tmpImg->g (y, x) = g;
519                 tmpImg->b (y, x) = b;
520             }
521         }
522     } else if (ri->get_colors() == 1) {
523         for (int row = 1, y = 0; row < height - 1 && y < tmph; row += vskip, y++) {
524             rofs = row * width;
525 
526             for (int col = firstgreen, x = 0; col < width - 1 && x < tmpw; col
527                     += hskip, x++) {
528                 int ofs = rofs + col;
529                 tmpImg->r (y, x) = tmpImg->g (y, x) = tmpImg->b (y, x) = image[ofs][0];
530             }
531         }
532     } else {
533         if (ri->getSensorType() == ST_FUJI_XTRANS) {
534             for ( int row = 1, y = 0; row < height - 1 && y < tmph; row += vskip, y++) {
535                 rofs = row * width;
536 
537                 for ( int col = 1, x = 0; col < width - 1 && x < tmpw; col += hskip, x++ ) {
538                     int ofs = rofs + col;
539                     float sum[3] = {};
540                     int c;
541 
542                     for (int v = -1; v <= 1; v++) {
543                         for (int h = -1; h <= 1; h++) {
544                             c = ri->XTRANSFC (row + v, col + h);
545                             sum[c] += image[ofs + v * width + h][c];
546                         }
547                     }
548 
549                     c = ri->XTRANSFC (row, col);
550 
551                     switch (c) {
552                         case 0:
553                             tmpImg->r (y, x) = image[ofs][0];
554                             tmpImg->g (y, x) = sum[1] / 5.f;
555                             tmpImg->b (y, x) = sum[2] / 3.f;
556                             break;
557 
558                         case 1:
559                             tmpImg->r (y, x) = sum[0] / 2.f;
560                             tmpImg->g (y, x) = image[ofs][1];
561                             tmpImg->b (y, x) = sum[2] / 2.f;
562                             break;
563 
564                         case 2:
565                             tmpImg->r (y, x) = sum[0] / 3.f;
566                             tmpImg->g (y, x) = sum[1] / 5.f;
567                             tmpImg->b (y, x) = image[ofs][2];
568                             break;
569                     }
570                 }
571             }
572         } else {
573             int iwidth = ri->get_iwidth();
574             int iheight = ri->get_iheight();
575             int left_margin = ri->get_leftmargin();
576             firstgreen += left_margin;
577             int top_margin = ri->get_topmargin();
578             int wmax = tmpw;
579             int hmax = tmph;
580 
581             if ((ri->get_maker() == "Sigma" || ri->get_maker() == "Pentax" || ri->get_maker() == "Sony") && ri->DNGVERSION()) { // Hack to prevent sigma dng files from crashing
582                 wmax = (width - 2 - left_margin) / hskip;
583                 hmax = (height - 2 - top_margin) / vskip;
584             }
585 
586             int y = 0;
587 
588             for (int row = 1 + top_margin; row < iheight + top_margin  - 1 && y < hmax; row += vskip, y++) {
589                 rofs = row * iwidth;
590 
591                 int x = 0;
592 
593                 for (int col = firstgreen; col < iwidth + left_margin - 1 && x < wmax; col += hskip, x++) {
594                     int ofs = rofs + col;
595                     tmpImg->r (y, x) = image[ofs][0];
596                     tmpImg->g (y, x) = image[ofs][1];
597                     tmpImg->b (y, x) = image[ofs][2];
598                 }
599 
600                 for (; x < tmpw; ++x) {
601                     tmpImg->r (y, x) = tmpImg->g (y, x) = tmpImg->b (y, x) = 0;
602                 }
603             }
604 
605             for (; y < tmph; ++y) {
606                 for (int x = 0; x < tmpw; ++x) {
607                     tmpImg->r (y, x) = tmpImg->g (y, x) = tmpImg->b (y, x) = 0;
608                 }
609             }
610         }
611     }
612 
613     if (ri->get_FujiWidth() != 0) {
614         int fw = ri->get_FujiWidth() / hskip;
615         double step = sqrt (0.5);
616         int wide = fw / step;
617         int high = (tmph - fw) / step;
618         Imagefloat* fImg = new Imagefloat (wide, high);
619         float r, c;
620 
621         for (int row = 0; row < high; row++)
622             for (int col = 0; col < wide; col++) {
623                 int ur = r = fw + (row - col) * step;
624                 int uc = c = (row + col) * step;
625 
626                 if (ur > tmph - 2 || uc > tmpw - 2) {
627                     continue;
628                 }
629 
630                 double fr = r - ur;
631                 double fc = c - uc;
632                 fImg->r (row, col) = (tmpImg->r (ur, uc) * (1 - fc) + tmpImg->r (ur, uc + 1) * fc) * (1 - fr)   +   (tmpImg->r (ur + 1, uc) * (1 - fc) + tmpImg->r (ur + 1, uc + 1) * fc) * fr;
633                 fImg->g (row, col) = (tmpImg->g (ur, uc) * (1 - fc) + tmpImg->g (ur, uc + 1) * fc) * (1 - fr)   +   (tmpImg->g (ur + 1, uc) * (1 - fc) + tmpImg->g (ur + 1, uc + 1) * fc) * fr;
634                 fImg->b (row, col) = (tmpImg->b (ur, uc) * (1 - fc) + tmpImg->b (ur, uc + 1) * fc) * (1 - fr)   +   (tmpImg->b (ur + 1, uc) * (1 - fc) + tmpImg->b (ur + 1, uc + 1) * fc) * fr;
635             }
636 
637         delete tmpImg;
638         tmpImg = fImg;
639         tmpw = wide;
640         tmph = high;
641     }
642 
643     const bool rotate_90 =
644         rotate
645         && (
646             ri->get_rotateDegree() == 90
647             || ri->get_rotateDegree() == 270
648         );
649 
650     if (rotate_90) {
651         std::swap (tmpw, tmph);
652     }
653 
654     if (fixwh == 1) { // fix height, scale width
655         w = tmpw * h / tmph;
656     } else {
657         h = tmph * w / tmpw;
658     }
659 
660     if (tpp->thumbImg) {
661         delete tpp->thumbImg;
662     }
663 
664     if (rotate_90) {
665         tpp->thumbImg = resizeTo<Image16> (h, w, TI_Bilinear, tmpImg);
666     } else {
667         tpp->thumbImg = resizeTo<Image16> (w, h, TI_Bilinear, tmpImg);
668     }
669 
670     delete tmpImg;
671 
672 
673     if (ri->get_FujiWidth() != 0) {
674         tpp->scale = (double) (height - ri->get_FujiWidth()) * 2.0 / (rotate_90 ? w : h);
675     } else {
676         tpp->scale = (double) height / (rotate_90 ? w : h);
677     }
678     if(!forHistogramMatching) { // we don't need this for histogram matching
679 
680         // generate histogram for auto exposure, also calculate autoWB
681         tpp->aeHistCompression = 3;
682         tpp->aeHistogram(65536 >> tpp->aeHistCompression);
683         tpp->aeHistogram.clear();
684 
685         const unsigned int add = filter ? 1 : 4 / ri->get_colors();
686 
687         double pixSum[3] = {0.0};
688         unsigned int n[3] = {0};
689         const double compression = pow(2.0, tpp->aeHistCompression);
690         const double camWb[3] = {tpp->camwbRed / compression, tpp->camwbGreen / compression, tpp->camwbBlue / compression};
691         const double clipval = 64000.0 / tpp->defGain;
692 
693         for (int i = 32; i < height - 32; i++) {
694             int start, end;
695 
696             if (ri->get_FujiWidth() != 0) {
697                 int fw = ri->get_FujiWidth();
698                 start = ABS (fw - i) + 32;
699                 end = min (height + width - fw - i, fw + i) - 32;
700             } else {
701                 start = 32;
702                 end = width - 32;
703             }
704 
705             if (ri->get_colors() == 1) {
706                 for (int j = start; j < end; j++) {
707                     tpp->aeHistogram[image[i * width + j][0] >> tpp->aeHistCompression]++;
708                 }
709             } else if (ri->getSensorType() == ST_BAYER) {
710                 int c0 = ri->FC(i, start);
711                 int c1 = ri->FC(i, start + 1);
712                 int j = start;
713                 int n0 = 0;
714                 int n1 = 0;
715                 double pixSum0 = 0.0;
716                 double pixSum1 = 0.0;
717                 for (; j < end - 1; j+=2) {
718                     double v0 = image[i * width + j][c0];
719                     tpp->aeHistogram[(int)(camWb[c0] * v0)]++;
720                     if (v0 <= clipval) {
721                         pixSum0 += v0;
722                         n0++;
723                     }
724                     double v1 = image[i * width + j + 1][c1];
725                     tpp->aeHistogram[(int)(camWb[c1] * v1)]++;
726                     if (v1 <= clipval) {
727                         pixSum1 += v1;
728                         n1++;
729                     }
730                 }
731                 if (j < end) {
732                     double v0 = image[i * width + j][c0];
733                     tpp->aeHistogram[(int)(camWb[c0] * v0)]++;
734                     if (v0 <= clipval) {
735                         pixSum0 += v0;
736                         n0++;
737                     }
738                 }
739                 n[c0] += n0;
740                 n[c1] += n1;
741                 pixSum[c0] += pixSum0;
742                 pixSum[c1] += pixSum1;
743             } else if (ri->getSensorType() == ST_FUJI_XTRANS) {
744                 int c[6];
745                 for(int cc = 0; cc < 6; ++cc) {
746                     c[cc] = ri->XTRANSFC(i, start + cc);
747                 }
748                 int j = start;
749                 for (; j < end - 5; j += 6) {
750                     for(int cc = 0; cc < 6; ++cc) {
751                         double d = image[i * width + j + cc][c[cc]];
752                         tpp->aeHistogram[(int)(camWb[c[cc]] * d)]++;
753                         if (d <= clipval) {
754                             pixSum[c[cc]] += d;
755                             n[c[cc]]++;
756                         }
757                     }
758                 }
759                 for (; j < end; j++) {
760                     if (ri->ISXTRANSGREEN (i, j)) {
761                         double d = image[i * width + j][1];
762                         tpp->aeHistogram[(int)(camWb[1] * d)]++;
763                         if (d <= clipval) {
764                             pixSum[1] += d;
765                             n[1]++;
766                         }
767                     } else if (ri->ISXTRANSRED (i, j)) {
768                         double d = image[i * width + j][0];
769                         tpp->aeHistogram[(int)(camWb[0] * d)]++;
770                         if (d <= clipval) {
771                             pixSum[0] += d;
772                             n[0]++;
773                         }
774                     } else if (ri->ISXTRANSBLUE (i, j)) {
775                         double d = image[i * width + j][2];
776                         tpp->aeHistogram[(int)(camWb[2] * d)]++;
777                         if (d <= clipval) {
778                             pixSum[2] += d;
779                             n[2]++;
780                         }
781                     }
782                 }
783             } else { /* if(ri->getSensorType()==ST_FOVEON) */
784                 for (int j = start; j < end; j++) {
785                     double r = image[i * width + j][0];
786                     if (r <= clipval) {
787                         pixSum[0] += r;
788                         n[0]++;
789                     }
790                     double g = image[i * width + j][1];
791                     if (g <= clipval) {
792                         pixSum[1] += g;
793                         n[1]++;
794                     }
795                     tpp->aeHistogram[((int)g) >> tpp->aeHistCompression] += add;
796                     double b = image[i * width + j][2];
797                     if (b <= clipval) {
798                         pixSum[2] += b;
799                         n[2]++;
800                     }
801                     tpp->aeHistogram[((int) (b * 0.5f)) >> tpp->aeHistCompression] += add;
802                 }
803             }
804         }
805         // ProcParams paramsForAutoExp; // Dummy for constructor
806         // ImProcFunctions ipf (&paramsForAutoExp, false);
807         // ipf.getAutoExp (tpp->aeHistogram, tpp->aeHistCompression, 0.02, tpp->aeExposureCompensation, tpp->aeLightness, tpp->aeContrast, tpp->aeBlack, tpp->aeHighlightCompression, tpp->aeHighlightCompressionThreshold);
808         // tpp->aeValid = true;
809 
810         if (ri->get_colors() == 1) {
811             pixSum[0] = pixSum[1] = pixSum[2] = 1.;
812             n[0] = n[1] = n[2] = 1;
813         }
814         pixSum[0] *= tpp->defGain;
815         pixSum[1] *= tpp->defGain;
816         pixSum[2] *= tpp->defGain;
817 
818         double reds = pixSum[0] / std::max(n[0], 1u) * tpp->camwbRed;
819         double greens = pixSum[1] / std::max(n[1], 1u) * tpp->camwbGreen;
820         double blues = pixSum[2] / std::max(n[2], 1u) * tpp->camwbBlue;
821 
822         tpp->redAWBMul   = ri->get_rgb_cam (0, 0) * reds + ri->get_rgb_cam (0, 1) * greens + ri->get_rgb_cam (0, 2) * blues;
823         tpp->greenAWBMul = ri->get_rgb_cam (1, 0) * reds + ri->get_rgb_cam (1, 1) * greens + ri->get_rgb_cam (1, 2) * blues;
824         tpp->blueAWBMul  = ri->get_rgb_cam (2, 0) * reds + ri->get_rgb_cam (2, 1) * greens + ri->get_rgb_cam (2, 2) * blues;
825         tpp->wbEqual = wbEq;
826 
827         ColorTemp cTemp;
828         cTemp.mul2temp (tpp->redAWBMul, tpp->greenAWBMul, tpp->blueAWBMul, tpp->wbEqual, tpp->autoWBTemp, tpp->autoWBGreen);
829     }
830 
831     if (rotate && ri->get_rotateDegree() > 0) {
832         tpp->thumbImg->rotate (ri->get_rotateDegree());
833     }
834 
835     for (int a = 0; a < 3; a++)
836         for (int b = 0; b < 3; b++) {
837             tpp->colorMatrix[a][b] = ri->get_rgb_cam (a, b);
838         }
839 
840     tpp->init();
841 
842     RawImageSource::computeFullSize(ri, TR_NONE, tpp->full_width, tpp->full_height);
843 
844     delete ri;
845     return tpp;
846 }
847 #undef FISRED
848 #undef FISGREEN
849 #undef FISBLUE
850 
init()851 void Thumbnail::init ()
852 {
853     RawImageSource::inverse33 (colorMatrix, iColorMatrix);
854     //colorMatrix is rgb_cam
855     memset (cam2xyz, 0, sizeof (cam2xyz));
856 
857     for (int i = 0; i < 3; i++)
858         for (int j = 0; j < 3; j++)
859             for (int k = 0; k < 3; k++) {
860                 cam2xyz[i][j] += xyz_sRGB[i][k] * colorMatrix[k][j];
861             }
862 
863     camProfile = ICCStore::getInstance()->createFromMatrix (cam2xyz, false, "Camera");
864 }
865 
Thumbnail()866 Thumbnail::Thumbnail () :
867     camProfile (nullptr),
868     iColorMatrix{},
869     cam2xyz{},
870     thumbImg (nullptr),
871     camwbRed (1.0),
872     camwbGreen (1.0),
873     camwbBlue (1.0),
874     redAWBMul (-1.0),
875     greenAWBMul (-1.0),
876     blueAWBMul (-1.0),
877     autoWBTemp (2700),
878     autoWBGreen (1.0),
879     wbEqual (-1.0),
880     aeHistCompression (3),
881     aeValid(false),
882     aeExposureCompensation(0.0),
883     aeLightness(0),
884     aeContrast(0),
885     aeBlack(0),
886     aeHighlightCompression(0),
887     aeHighlightCompressionThreshold(0),
888     embProfileLength (0),
889     embProfileData (nullptr),
890     embProfile (nullptr),
891     redMultiplier (1.0),
892     greenMultiplier (1.0),
893     blueMultiplier (1.0),
894     scale (1.0),
895     defGain (1.0),
896     scaleForSave (8192),
897     gammaCorrected (false),
898     colorMatrix{},
899     scaleGain(1.0),
900     sensorType(ST_NONE),
901     isRaw(false),
902     full_width(-1),
903     full_height(-1)
904 {
905 }
906 
~Thumbnail()907 Thumbnail::~Thumbnail ()
908 {
909 
910     delete thumbImg;
911     //delete [] aeHistogram;
912     delete [] embProfileData;
913 
914     if (embProfile) {
915         cmsCloseProfile (embProfile);
916     }
917 
918     if (camProfile) {
919         cmsCloseProfile (camProfile);
920     }
921 }
922 
923 // Simple processing of RAW internal JPGs
quickProcessImage(const procparams::ProcParams & params,int rheight,rtengine::TypeInterpolation interp)924 IImage8* Thumbnail::quickProcessImage (const procparams::ProcParams& params, int rheight, rtengine::TypeInterpolation interp)
925 {
926 
927     int rwidth;
928 
929     if (params.coarse.rotate == 90 || params.coarse.rotate == 270) {
930         rwidth = rheight;
931         rheight = thumbImg->getHeight() * rwidth / thumbImg->getWidth();
932     } else {
933         rwidth = thumbImg->getWidth() * rheight / thumbImg->getHeight();
934     }
935 
936     Image8* baseImg = resizeTo<Image8> (rwidth, rheight, interp, thumbImg);
937 
938     if (params.coarse.rotate) {
939         baseImg->rotate (params.coarse.rotate);
940     }
941 
942     if (params.coarse.hflip) {
943         baseImg->hflip ();
944     }
945 
946     if (params.coarse.vflip) {
947         baseImg->vflip ();
948     }
949 
950     return baseImg;
951 }
952 
953 // Full thumbnail processing, second stage if complete profile exists
processImage(const procparams::ProcParams & params,eSensorType sensorType,int rheight,TypeInterpolation interp,const FramesMetaData * metadata,double & myscale,bool forMonitor,bool forHistogramMatching)954 IImage8* Thumbnail::processImage (const procparams::ProcParams& params, eSensorType sensorType, int rheight, TypeInterpolation interp, const FramesMetaData *metadata, double& myscale, bool forMonitor, bool forHistogramMatching)
955 {
956     std::string camName = metadata->getCamera();
957 
958     // check if the WB's equalizer value has changed
959     if (wbEqual < (params.wb.equal - 5e-4) || wbEqual > (params.wb.equal + 5e-4)) {
960         wbEqual = params.wb.equal;
961         // recompute the autoWB
962         ColorTemp cTemp;
963         cTemp.mul2temp (redAWBMul, greenAWBMul, blueAWBMul, wbEqual, autoWBTemp, autoWBGreen);
964     }
965 
966     // compute WB multipliers
967     ColorTemp currWB;
968 
969     if (!params.wb.enabled) {
970         currWB = ColorTemp();
971     } else if (params.wb.method == WBParams::CAMERA) {
972         //recall colorMatrix is rgb_cam
973         double cam_r = colorMatrix[0][0] * camwbRed + colorMatrix[0][1] * camwbGreen + colorMatrix[0][2] * camwbBlue;
974         double cam_g = colorMatrix[1][0] * camwbRed + colorMatrix[1][1] * camwbGreen + colorMatrix[1][2] * camwbBlue;
975         double cam_b = colorMatrix[2][0] * camwbRed + colorMatrix[2][1] * camwbGreen + colorMatrix[2][2] * camwbBlue;
976         currWB = ColorTemp (cam_r, cam_g, cam_b, params.wb.equal);
977     } else if (params.wb.method == WBParams::AUTO) {
978         currWB = ColorTemp (autoWBTemp, autoWBGreen, wbEqual, "Custom");
979     } else if (params.wb.method == WBParams::CUSTOM_TEMP) {
980         currWB = ColorTemp(params.wb.temperature, params.wb.green, params.wb.equal, "Custom");
981     } else if (params.wb.method == WBParams::CUSTOM_MULT) {
982         currWB = ColorTemp(params.wb.mult[0], params.wb.mult[1], params.wb.mult[2], 1.0);
983     }
984     double rm, gm, bm;
985     if (currWB.getTemp() < 0) {
986         rm = redMultiplier;
987         gm = greenMultiplier;
988         bm = blueMultiplier;
989     } else {
990         double r, g, b;
991         currWB.getMultipliers (r, g, b);
992         //iColorMatrix is cam_rgb
993         rm = iColorMatrix[0][0] * r + iColorMatrix[0][1] * g + iColorMatrix[0][2] * b;
994         gm = iColorMatrix[1][0] * r + iColorMatrix[1][1] * g + iColorMatrix[1][2] * b;
995         bm = iColorMatrix[2][0] * r + iColorMatrix[2][1] * g + iColorMatrix[2][2] * b;
996     }
997     rm = camwbRed / rm;
998     gm = camwbGreen / gm;
999     bm = camwbBlue / bm;
1000     double mul_lum = 0.299 * rm + 0.587 * gm + 0.114 * bm;
1001     float rmi, gmi, bmi;
1002 
1003     rmi = rm * defGain / mul_lum;
1004     gmi = gm * defGain / mul_lum;
1005     bmi = bm * defGain / mul_lum;
1006 
1007     // The RAW exposure is not reflected since it's done in preprocessing. If we only have e.g. the cached thumb,
1008     // that is already preprocessed. So we simulate the effect here roughly my modifying the exposure accordingly
1009     if (isRaw) {
1010         rmi *= params.raw.expos;
1011         gmi *= params.raw.expos;
1012         bmi *= params.raw.expos;
1013     }
1014 
1015     // resize to requested width and perform coarse transformation
1016     int rwidth;
1017 
1018     if (params.coarse.rotate == 90 || params.coarse.rotate == 270) {
1019         rwidth = rheight;
1020         rheight = int (size_t (thumbImg->getHeight()) * size_t (rwidth) / size_t (thumbImg->getWidth()));
1021     } else {
1022         rwidth = int (size_t (thumbImg->getWidth()) * size_t (rheight) / size_t (thumbImg->getHeight()));
1023     }
1024 
1025     rwidth = std::max(rwidth, 1);
1026     rheight = std::max(rheight, 1);
1027 
1028     Imagefloat* baseImg = resizeTo<Imagefloat> (rwidth, rheight, interp, thumbImg);
1029     processFilmNegative(params, baseImg, rwidth, rheight);
1030     baseImg->assignColorSpace(params.icm.workingProfile);
1031 
1032     if (params.coarse.rotate) {
1033         baseImg->rotate (params.coarse.rotate);
1034         rwidth = baseImg->getWidth();
1035         rheight = baseImg->getHeight();
1036     }
1037 
1038     if (params.coarse.hflip) {
1039         baseImg->hflip ();
1040     }
1041 
1042     if (params.coarse.vflip) {
1043         baseImg->vflip ();
1044     }
1045 
1046     const bool hrenabled = params.exposure.enabled && params.exposure.hrmode != procparams::ExposureParams::HR_OFF;
1047 
1048     // apply white balance and raw white point (simulated)
1049     for (int i = 0; i < rheight; i++) {
1050 #ifdef _OPENMP
1051         #pragma omp simd
1052 #endif
1053         for (int j = 0; j < rwidth; j++) {
1054             float red = baseImg->r (i, j) * rmi;
1055             float green = baseImg->g (i, j) * gmi;
1056             float blue = baseImg->b (i, j) * bmi;
1057 
1058             // avoid magenta highlights if highlight recovery is enabled
1059             if (hrenabled && red > MAXVALF && blue > MAXVALF) {
1060                 baseImg->r(i, j) = baseImg->g(i, j) = baseImg->b(i, j) = CLIP((red + green + blue) / 3.f);
1061             } else {
1062                 baseImg->r(i, j) = CLIP(red);
1063                 baseImg->g(i, j) = CLIP(green);
1064                 baseImg->b(i, j) = CLIP(blue);
1065             }
1066         }
1067     }
1068 
1069     // if luma denoise has to be done for thumbnails, it should be right here
1070 
1071     // perform color space transformation
1072 
1073     if (isRaw) {
1074         double pre_mul[3] = { redMultiplier, greenMultiplier, blueMultiplier };
1075         RawImageSource::colorSpaceConversion (baseImg, params.icm, currWB, pre_mul, embProfile, camProfile, cam2xyz, camName, metadata->getFileName());
1076     } else {
1077         StdImageSource::colorSpaceConversion (baseImg, params.icm, embProfile, thumbImg->getSampleFormat());
1078     }
1079 
1080     int fw = baseImg->getWidth();
1081     int fh = baseImg->getHeight();
1082 
1083     ImProcFunctions ipf (&params, forHistogramMatching); // enable multithreading when forHistogramMatching is true
1084     int origFW;
1085     int origFH;
1086     double tscale = 0.0;
1087     getDimensions (origFW, origFH, tscale);
1088     ipf.setScale((origFW * tscale) / rwidth);
1089     //ipf.updateColorProfiles (ICCStore::getInstance()->getDefaultMonitorProfileName(), options.rtSettings.monitorIntent, false, false);
1090     ipf.setMonitorTransform(ICCStore::getInstance()->getThumbnailMonitorTransform());
1091 
1092     LUTu hist16 (65536);
1093 
1094     ipf.firstAnalysis (baseImg, params, hist16);
1095 
1096     bool stop = ipf.process(ImProcFunctions::Pipeline::THUMBNAIL, ImProcFunctions::Stage::STAGE_0, baseImg);
1097 
1098     // perform transform
1099     if (ipf.needsTransform()) {
1100         Imagefloat* trImg = new Imagefloat(fw, fh, baseImg);
1101         ipf.transform (baseImg, trImg, 0, 0, 0, 0, fw, fh, origFW * tscale + 0.5, origFH * tscale + 0.5, metadata, 0, false); // Raw rotate degree not detectable here
1102         delete baseImg;
1103         baseImg = trImg;
1104     }
1105 
1106     DCPProfile *dcpProf = nullptr;
1107     DCPProfile::ApplyState as;
1108 
1109     if (isRaw) {
1110         cmsHPROFILE dummy;
1111         RawImageSource::findInputProfile (params.icm.inputProfile, nullptr, camName, metadata->getFileName(), &dcpProf, dummy);
1112 
1113         if (dcpProf) {
1114             dcpProf->setStep2ApplyState (params.icm.workingProfile, params.icm.toneCurve, params.icm.applyLookTable, params.icm.applyBaselineExposureOffset, as);
1115         }
1116     }
1117     ipf.setDCPProfile(dcpProf, as);
1118 
1119     stop = stop || ipf.process(ImProcFunctions::Pipeline::THUMBNAIL, ImProcFunctions::Stage::STAGE_1, baseImg);
1120     stop = stop || ipf.process(ImProcFunctions::Pipeline::THUMBNAIL, ImProcFunctions::Stage::STAGE_2, baseImg);
1121     stop = stop || ipf.process(ImProcFunctions::Pipeline::THUMBNAIL, ImProcFunctions::Stage::STAGE_3, baseImg);
1122 
1123     // obtain final image
1124     Image8 *readyImg = nullptr;
1125     if (forMonitor) {
1126         readyImg = new Image8 (fw, fh);
1127         ipf.rgb2monitor(baseImg, readyImg);
1128     } else {
1129         readyImg = ipf.rgb2out(baseImg, 0, 0, fw, fh, params.icm, false);
1130     }
1131     delete baseImg;
1132 
1133     // calculate scale
1134     if (params.coarse.rotate == 90 || params.coarse.rotate == 270) {
1135         myscale = scale * thumbImg->getWidth() / fh;
1136     } else {
1137         myscale = scale * thumbImg->getHeight() / fh;
1138     }
1139 
1140     myscale = 1.0 / myscale;
1141 
1142     ipf.setMonitorTransform(nullptr);
1143 
1144     return readyImg;
1145 }
1146 
getImageWidth(const procparams::ProcParams & params,int rheight,float & ratio)1147 int Thumbnail::getImageWidth (const procparams::ProcParams& params, int rheight, float &ratio)
1148 {
1149     if (!thumbImg) {
1150         return 0;    // Can happen if thumb is just building and GUI comes in with resize wishes
1151     }
1152 
1153     int rwidth;
1154 
1155     if (params.coarse.rotate == 90 || params.coarse.rotate == 270) {
1156         ratio = (float) (thumbImg->getHeight()) / (float) (thumbImg->getWidth());
1157     } else {
1158         ratio = (float) (thumbImg->getWidth()) / (float) (thumbImg->getHeight());
1159     }
1160 
1161     rwidth = (int) (ratio * (float)rheight);
1162 
1163     return rwidth;
1164 }
1165 
getDimensions(int & w,int & h,double & scaleFac)1166 void Thumbnail::getDimensions (int& w, int& h, double& scaleFac)
1167 {
1168     if (thumbImg) {
1169         w = thumbImg->getWidth();
1170         h = thumbImg->getHeight();
1171         scaleFac = scale;
1172     } else {
1173         w = 0;
1174         h = 0;
1175         scale = 1;
1176     }
1177 }
1178 
getCamWB(double & temp,double & green)1179 void Thumbnail::getCamWB (double& temp, double& green)
1180 {
1181 
1182     double cam_r = colorMatrix[0][0] * camwbRed + colorMatrix[0][1] * camwbGreen + colorMatrix[0][2] * camwbBlue;
1183     double cam_g = colorMatrix[1][0] * camwbRed + colorMatrix[1][1] * camwbGreen + colorMatrix[1][2] * camwbBlue;
1184     double cam_b = colorMatrix[2][0] * camwbRed + colorMatrix[2][1] * camwbGreen + colorMatrix[2][2] * camwbBlue;
1185     ColorTemp currWB = ColorTemp (cam_r, cam_g, cam_b, 1.0);  // we do not take the equalizer into account here, because we want camera's WB
1186     temp = currWB.getTemp ();
1187     green = currWB.getGreen ();
1188 }
1189 
getAutoWB(double & temp,double & green,double equal)1190 void Thumbnail::getAutoWB (double& temp, double& green, double equal)
1191 {
1192 
1193     if (equal != wbEqual) {
1194         // compute the values depending on equal
1195         ColorTemp cTemp;
1196         wbEqual = equal;
1197         // compute autoWBTemp and autoWBGreen
1198         cTemp.mul2temp (redAWBMul, greenAWBMul, blueAWBMul, wbEqual, autoWBTemp, autoWBGreen);
1199     }
1200 
1201     temp = autoWBTemp;
1202     green = autoWBGreen;
1203 }
1204 
getAutoWBMultipliers(double & rm,double & gm,double & bm)1205 void Thumbnail::getAutoWBMultipliers (double& rm, double& gm, double& bm)
1206 {
1207     rm = redAWBMul;
1208     gm = greenAWBMul;
1209     bm = blueAWBMul;
1210 }
1211 
1212 
getSpotWB(const procparams::ProcParams & params,int xp,int yp,int rect,double & rtemp,double & rgreen)1213 void Thumbnail::getSpotWB (const procparams::ProcParams& params, int xp, int yp, int rect, double& rtemp, double& rgreen)
1214 {
1215 
1216     std::vector<Coord2D> points, red, green, blue;
1217 
1218     for (int i = yp - rect; i <= yp + rect; i++)
1219         for (int j = xp - rect; j <= xp + rect; j++) {
1220             points.push_back (Coord2D (j, i));
1221         }
1222 
1223     int fw = thumbImg->getWidth(), fh = thumbImg->getHeight();
1224 
1225     if (params.coarse.rotate == 90 || params.coarse.rotate == 270) {
1226         fw = thumbImg->getHeight();
1227         fh = thumbImg->getWidth();
1228     }
1229 
1230     ImProcFunctions ipf (&params, false);
1231     ipf.transCoord (fw, fh, points, red, green, blue);
1232     int tr = getCoarseBitMask (params.coarse);
1233     // calculate spot wb (copy & pasted from stdimagesource)
1234     double reds = 0, greens = 0, blues = 0;
1235     int rn = 0, gn = 0, bn = 0;
1236     thumbImg->getSpotWBData (reds, greens, blues, rn, gn, bn, red, green, blue, tr);
1237     reds = reds / rn * camwbRed;
1238     greens = greens / gn * camwbGreen;
1239     blues = blues / bn * camwbBlue;
1240 
1241     double rm = colorMatrix[0][0] * reds + colorMatrix[0][1] * greens + colorMatrix[0][2] * blues;
1242     double gm = colorMatrix[1][0] * reds + colorMatrix[1][1] * greens + colorMatrix[1][2] * blues;
1243     double bm = colorMatrix[2][0] * reds + colorMatrix[2][1] * greens + colorMatrix[2][2] * blues;
1244 
1245     ColorTemp ct (rm, gm, bm, params.wb.equal);
1246     rtemp = ct.getTemp ();
1247     rgreen = ct.getGreen ();
1248 }
1249 
1250 
transformPixel(int x,int y,int tran,int & tx,int & ty)1251 void Thumbnail::transformPixel (int x, int y, int tran, int& tx, int& ty)
1252 {
1253 
1254     int W = thumbImg->getWidth();
1255     int H = thumbImg->getHeight();
1256     int sw = W, sh = H;
1257 
1258     if ((tran & TR_ROT) == TR_R90 || (tran & TR_ROT) == TR_R270) {
1259         sw = H;
1260         sh = W;
1261     }
1262 
1263     int ppx = x, ppy = y;
1264 
1265     if (tran & TR_HFLIP) {
1266         ppx = sw - 1 - x ;
1267     }
1268 
1269     if (tran & TR_VFLIP) {
1270         ppy = sh - 1 - y;
1271     }
1272 
1273     tx = ppx;
1274     ty = ppy;
1275 
1276     if ((tran & TR_ROT) == TR_R180) {
1277         tx = W - 1 - ppx;
1278         ty = H - 1 - ppy;
1279     } else if ((tran & TR_ROT) == TR_R90) {
1280         tx = ppy;
1281         ty = H - 1 - ppx;
1282     } else if ((tran & TR_ROT) == TR_R270) {
1283         tx = W - 1 - ppy;
1284         ty = ppx;
1285     }
1286 
1287     tx /= scale;
1288     ty /= scale;
1289 }
1290 
getGrayscaleHistEQ(int trim_width)1291 unsigned char* Thumbnail::getGrayscaleHistEQ (int trim_width)
1292 {
1293     if (!thumbImg) {
1294         return nullptr;
1295     }
1296 
1297     if (thumbImg->getWidth() < trim_width) {
1298         return nullptr;
1299     }
1300 
1301     // to utilize the 8 bit color range of the thumbnail we brighten it and apply gamma correction
1302     unsigned char* tmpdata = new unsigned char[thumbImg->getHeight() * trim_width];
1303     int ix = 0;
1304 
1305     if (gammaCorrected) {
1306         // if it's gamma correct (usually a RAW), we have the problem that there is a lot noise etc. that makes the maximum way too high.
1307         // Strategy is limit a certain percent of pixels so the overall picture quality when scaling to 8 bit is way better
1308         const double BurnOffPct = 0.03; // *100 = percent pixels that may be clipped
1309 
1310         // Calc the histogram
1311         unsigned int* hist16 = new unsigned int [65536];
1312         memset (hist16, 0, sizeof (int) * 65536);
1313 
1314         if (thumbImg->getType() == sImage8) {
1315             Image8 *image = static_cast<Image8*> (thumbImg);
1316             image->calcGrayscaleHist (hist16);
1317         } else if (thumbImg->getType() == sImage16) {
1318             Image16 *image = static_cast<Image16*> (thumbImg);
1319             image->calcGrayscaleHist (hist16);
1320         } else if (thumbImg->getType() == sImagefloat) {
1321             Imagefloat *image = static_cast<Imagefloat*> (thumbImg);
1322             image->calcGrayscaleHist (hist16);
1323         } else {
1324             printf ("getGrayscaleHistEQ #1: Unsupported image type \"%s\"!\n", thumbImg->getType());
1325         }
1326 
1327         // Go down till we cut off that many pixels
1328         unsigned long cutoff = thumbImg->getHeight() * thumbImg->getHeight() * 4 * BurnOffPct;
1329 
1330         int max_;
1331         unsigned long sum = 0;
1332 
1333         for (max_ = 65535; max_ > 16384 && sum < cutoff; max_--) {
1334             sum += hist16[max_];
1335         }
1336 
1337         delete[] hist16;
1338 
1339         scaleForSave = 65535 * 8192 / max_;
1340 
1341         // Correction and gamma to 8 Bit
1342         if (thumbImg->getType() == sImage8) {
1343             Image8 *image = static_cast<Image8*> (thumbImg);
1344 
1345             for (int i = 0; i < thumbImg->getHeight(); i++)
1346                 for (int j = (thumbImg->getWidth() - trim_width) / 2; j < trim_width + (thumbImg->getWidth() - trim_width) / 2; j++) {
1347                     unsigned short r_, g_, b_;
1348                     image->convertTo (image->r (i, j), r_);
1349                     image->convertTo (image->g (i, j), g_);
1350                     image->convertTo (image->b (i, j), b_);
1351                     int r = Color::gammatabThumb[min (r_, static_cast<unsigned short> (max_)) * scaleForSave >> 13];
1352                     int g = Color::gammatabThumb[min (g_, static_cast<unsigned short> (max_)) * scaleForSave >> 13];
1353                     int b = Color::gammatabThumb[min (b_, static_cast<unsigned short> (max_)) * scaleForSave >> 13];
1354                     tmpdata[ix++] = (r * 19595 + g * 38469 + b * 7472) >> 16;
1355                 }
1356         } else if (thumbImg->getType() == sImage16) {
1357             Image16 *image = static_cast<Image16*> (thumbImg);
1358 
1359             for (int i = 0; i < thumbImg->getHeight(); i++)
1360                 for (int j = (thumbImg->getWidth() - trim_width) / 2; j < trim_width + (thumbImg->getWidth() - trim_width) / 2; j++) {
1361                     unsigned short r_, g_, b_;
1362                     image->convertTo (image->r (i, j), r_);
1363                     image->convertTo (image->g (i, j), g_);
1364                     image->convertTo (image->b (i, j), b_);
1365                     int r = Color::gammatabThumb[min (r_, static_cast<unsigned short> (max_)) * scaleForSave >> 13];
1366                     int g = Color::gammatabThumb[min (g_, static_cast<unsigned short> (max_)) * scaleForSave >> 13];
1367                     int b = Color::gammatabThumb[min (b_, static_cast<unsigned short> (max_)) * scaleForSave >> 13];
1368                     tmpdata[ix++] = (r * 19595 + g * 38469 + b * 7472) >> 16;
1369                 }
1370         } else if (thumbImg->getType() == sImagefloat) {
1371             Imagefloat *image = static_cast<Imagefloat*> (thumbImg);
1372 
1373             for (int i = 0; i < thumbImg->getHeight(); i++)
1374                 for (int j = (thumbImg->getWidth() - trim_width) / 2; j < trim_width + (thumbImg->getWidth() - trim_width) / 2; j++) {
1375                     unsigned short r_, g_, b_;
1376                     image->convertTo (image->r (i, j), r_);
1377                     image->convertTo (image->g (i, j), g_);
1378                     image->convertTo (image->b (i, j), b_);
1379                     int r = Color::gammatabThumb[min (r_, static_cast<unsigned short> (max_)) * scaleForSave >> 13];
1380                     int g = Color::gammatabThumb[min (g_, static_cast<unsigned short> (max_)) * scaleForSave >> 13];
1381                     int b = Color::gammatabThumb[min (b_, static_cast<unsigned short> (max_)) * scaleForSave >> 13];
1382                     tmpdata[ix++] = (r * 19595 + g * 38469 + b * 7472) >> 16;
1383                 }
1384         }
1385     } else {
1386         // If it's not gamma corrected (usually a JPG) we take the normal maximum
1387         int max = 0;
1388 
1389         if (thumbImg->getType() == sImage8) {
1390             Image8 *image = static_cast<Image8*> (thumbImg);
1391             unsigned char max_ = 0;
1392 
1393             for (int row = 0; row < image->getHeight(); row++)
1394                 for (int col = 0; col < image->getWidth(); col++) {
1395                     if (image->r (row, col) > max_) {
1396                         max_ = image->r (row, col);
1397                     }
1398 
1399                     if (image->g (row, col) > max_) {
1400                         max_ = image->g (row, col);
1401                     }
1402 
1403                     if (image->b (row, col) > max_) {
1404                         max_ = image->b (row, col);
1405                     }
1406                 }
1407 
1408             image->convertTo (max_, max);
1409 
1410             if (max < 16384) {
1411                 max = 16384;
1412             }
1413 
1414             scaleForSave = 65535 * 8192 / max;
1415 
1416             // Correction and gamma to 8 Bit
1417             for (int i = 0; i < image->getHeight(); i++)
1418                 for (int j = (image->getWidth() - trim_width) / 2; j < trim_width + (image->getWidth() - trim_width) / 2; j++) {
1419                     unsigned short rtmp, gtmp, btmp;
1420                     image->convertTo (image->r (i, j), rtmp);
1421                     image->convertTo (image->g (i, j), gtmp);
1422                     image->convertTo (image->b (i, j), btmp);
1423                     int r = rtmp * scaleForSave >> 21;
1424                     int g = gtmp * scaleForSave >> 21;
1425                     int b = btmp * scaleForSave >> 21;
1426                     tmpdata[ix++] = (r * 19595 + g * 38469 + b * 7472) >> 16;
1427                 }
1428         } else if (thumbImg->getType() == sImage16) {
1429             Image16 *image = static_cast<Image16*> (thumbImg);
1430             unsigned short max_ = 0;
1431 
1432             for (int row = 0; row < image->getHeight(); row++)
1433                 for (int col = 0; col < image->getWidth(); col++) {
1434                     if (image->r (row, col) > max_) {
1435                         max_ = image->r (row, col);
1436                     }
1437 
1438                     if (image->g (row, col) > max_) {
1439                         max_ = image->g (row, col);
1440                     }
1441 
1442                     if (image->b (row, col) > max_) {
1443                         max_ = image->b (row, col);
1444                     }
1445                 }
1446 
1447             image->convertTo (max_, max);
1448 
1449             if (max < 16384) {
1450                 max = 16384;
1451             }
1452 
1453             scaleForSave = 65535 * 8192 / max;
1454 
1455             // Correction and gamma to 8 Bit
1456             for (int i = 0; i < image->getHeight(); i++)
1457                 for (int j = (image->getWidth() - trim_width) / 2; j < trim_width + (image->getWidth() - trim_width) / 2; j++) {
1458                     unsigned short rtmp, gtmp, btmp;
1459                     image->convertTo (image->r (i, j), rtmp);
1460                     image->convertTo (image->g (i, j), gtmp);
1461                     image->convertTo (image->b (i, j), btmp);
1462                     int r = rtmp * scaleForSave >> 21;
1463                     int g = gtmp * scaleForSave >> 21;
1464                     int b = btmp * scaleForSave >> 21;
1465                     tmpdata[ix++] = (r * 19595 + g * 38469 + b * 7472) >> 16;
1466                 }
1467         } else if (thumbImg->getType() == sImagefloat) {
1468             Imagefloat *image = static_cast<Imagefloat*> (thumbImg);
1469             float max_ = 0.f;
1470 
1471             for (int row = 0; row < image->getHeight(); row++)
1472                 for (int col = 0; col < image->getWidth(); col++) {
1473                     if (image->r (row, col) > max_) {
1474                         max_ = image->r (row, col);
1475                     }
1476 
1477                     if (image->g (row, col) > max_) {
1478                         max_ = image->g (row, col);
1479                     }
1480 
1481                     if (image->b (row, col) > max_) {
1482                         max_ = image->b (row, col);
1483                     }
1484                 }
1485 
1486             image->convertTo (max_, max);
1487 
1488             if (max < 16384) {
1489                 max = 16384;
1490             }
1491 
1492             scaleForSave = 65535 * 8192 / max;
1493 
1494             // Correction and gamma to 8 Bit
1495             for (int i = 0; i < image->getHeight(); i++)
1496                 for (int j = (image->getWidth() - trim_width) / 2; j < trim_width + (image->getWidth() - trim_width) / 2; j++) {
1497                     unsigned short rtmp, gtmp, btmp;
1498                     image->convertTo (image->r (i, j), rtmp);
1499                     image->convertTo (image->g (i, j), gtmp);
1500                     image->convertTo (image->b (i, j), btmp);
1501                     int r = rtmp * scaleForSave >> 21;
1502                     int g = gtmp * scaleForSave >> 21;
1503                     int b = btmp * scaleForSave >> 21;
1504                     tmpdata[ix++] = (r * 19595 + g * 38469 + b * 7472) >> 16;
1505                 }
1506         } else {
1507             printf ("getGrayscaleHistEQ #2: Unsupported image type \"%s\"!\n", thumbImg->getType());
1508         }
1509     }
1510 
1511     // histogram equalization
1512     unsigned int hist[256] = {0};
1513 
1514     for (int i = 0; i < ix; i++) {
1515         hist[tmpdata[i]]++;
1516     }
1517 
1518     int cdf = 0, cdf_min = -1;
1519 
1520     for (int i = 0; i < 256; i++) {
1521         cdf += hist[i];
1522 
1523         if (cdf > 0 && cdf_min == -1) {
1524             cdf_min = cdf;
1525         }
1526 
1527         if (cdf_min != -1) {
1528             hist[i] = (cdf - cdf_min) * 255 / ((thumbImg->getHeight() * trim_width) - cdf_min);
1529         }
1530     }
1531 
1532     for (int i = 0; i < ix; i++) {
1533         tmpdata[i] = hist[tmpdata[i]];
1534     }
1535 
1536     return tmpdata;
1537 }
1538 
writeImage(const Glib::ustring & fname)1539 bool Thumbnail::writeImage (const Glib::ustring& fname)
1540 {
1541 
1542     if (!thumbImg) {
1543         return false;
1544     }
1545 
1546     Glib::ustring fullFName = fname + ".rtti";
1547 
1548     FILE* f = g_fopen (fullFName.c_str (), "wb");
1549 
1550     if (!f) {
1551         return false;
1552     }
1553 
1554     fwrite (thumbImg->getType(), sizeof (char), strlen (thumbImg->getType()), f);
1555     fputc ('\n', f);
1556     guint32 w = guint32 (thumbImg->getWidth());
1557     guint32 h = guint32 (thumbImg->getHeight());
1558     fwrite (&w, sizeof (guint32), 1, f);
1559     fwrite (&h, sizeof (guint32), 1, f);
1560 
1561     if (thumbImg->getType() == sImage8) {
1562         Image8 *image = static_cast<Image8*> (thumbImg);
1563         image->writeData (f);
1564     } else if (thumbImg->getType() == sImage16) {
1565         Image16 *image = static_cast<Image16*> (thumbImg);
1566         image->writeData (f);
1567     } else if (thumbImg->getType() == sImagefloat) {
1568         Imagefloat *image = static_cast<Imagefloat*> (thumbImg);
1569         image->writeData (f);
1570     }
1571 
1572     //thumbImg->writeData(f);
1573     fclose (f);
1574     return true;
1575 }
1576 
readImage(const Glib::ustring & fname)1577 bool Thumbnail::readImage (const Glib::ustring& fname)
1578 {
1579 
1580     if (thumbImg) {
1581         delete thumbImg;
1582         thumbImg = nullptr;
1583     }
1584 
1585     Glib::ustring fullFName = fname + ".rtti";
1586 
1587     if (!Glib::file_test(fullFName, Glib::FILE_TEST_EXISTS)) {
1588         return false;
1589     }
1590 
1591     FILE* f = g_fopen(fullFName.c_str (), "rb");
1592 
1593     if (!f) {
1594         return false;
1595     }
1596 
1597     char imgType[31];  // 30 -> arbitrary size, but should be enough for all image type's name
1598     fgets(imgType, 30, f);
1599     imgType[strlen(imgType) - 1] = '\0'; // imgType has a \n trailing character, so we overwrite it by the \0 char
1600 
1601     guint32 width, height;
1602 
1603     if (fread(&width, 1, sizeof(guint32), f) < sizeof(guint32)) {
1604         width = 0;
1605     }
1606 
1607     if (fread(&height, 1, sizeof(guint32), f) < sizeof(guint32)) {
1608         height = 0;
1609     }
1610 
1611     bool success = false;
1612 
1613     if (std::min(width , height) > 0) {
1614         if (!strcmp(imgType, sImage8)) {
1615             Image8 *image = new Image8(width, height);
1616             image->readData(f);
1617             thumbImg = image;
1618             success = true;
1619         } else if (!strcmp(imgType, sImage16)) {
1620             Image16 *image = new Image16(width, height);
1621             image->readData(f);
1622             thumbImg = image;
1623             success = true;
1624         } else if (!strcmp(imgType, sImagefloat)) {
1625             Imagefloat *image = new Imagefloat(width, height);
1626             image->readData(f);
1627             thumbImg = image;
1628             success = true;
1629         } else {
1630             printf ("readImage: Unsupported image type \"%s\"!\n", imgType);
1631         }
1632     }
1633     fclose(f);
1634     return success;
1635 }
1636 
readData(const Glib::ustring & fname)1637 bool Thumbnail::readData  (const Glib::ustring& fname)
1638 {
1639     setlocale (LC_NUMERIC, "C"); // to set decimal point to "."
1640     Glib::KeyFile keyFile;
1641 
1642     try {
1643         MyMutex::MyLock thmbLock (thumbMutex);
1644 
1645         try {
1646             keyFile.load_from_file (fname);
1647         } catch (Glib::Error&) {
1648             return false;
1649         }
1650 
1651         if (keyFile.has_group ("LiveThumbData")) {
1652             if (keyFile.has_key ("LiveThumbData", "CamWBRed")) {
1653                 camwbRed            = keyFile.get_double ("LiveThumbData", "CamWBRed");
1654             }
1655 
1656             if (keyFile.has_key ("LiveThumbData", "CamWBGreen")) {
1657                 camwbGreen          = keyFile.get_double ("LiveThumbData", "CamWBGreen");
1658             }
1659 
1660             if (keyFile.has_key ("LiveThumbData", "CamWBBlue")) {
1661                 camwbBlue           = keyFile.get_double ("LiveThumbData", "CamWBBlue");
1662             }
1663 
1664             if (keyFile.has_key ("LiveThumbData", "RedAWBMul")) {
1665                 redAWBMul           = keyFile.get_double ("LiveThumbData", "RedAWBMul");
1666             }
1667 
1668             if (keyFile.has_key ("LiveThumbData", "GreenAWBMul")) {
1669                 greenAWBMul         = keyFile.get_double ("LiveThumbData", "GreenAWBMul");
1670             }
1671 
1672             if (keyFile.has_key ("LiveThumbData", "BlueAWBMul")) {
1673                 blueAWBMul          = keyFile.get_double ("LiveThumbData", "BlueAWBMul");
1674             }
1675 
1676             if (keyFile.has_key ("LiveThumbData", "AEHistCompression")) {
1677                 aeHistCompression   = keyFile.get_integer ("LiveThumbData", "AEHistCompression");
1678             }
1679 
1680             aeValid = true;
1681             if (keyFile.has_key ("LiveThumbData", "AEExposureCompensation")) {
1682                 aeExposureCompensation = keyFile.get_double ("LiveThumbData", "AEExposureCompensation");
1683             } else {
1684                 aeValid = false;
1685             }
1686             if (keyFile.has_key ("LiveThumbData", "AELightness")) {
1687                 aeLightness   = keyFile.get_integer ("LiveThumbData", "AELightness");
1688             } else {
1689                 aeValid = false;
1690             }
1691             if (keyFile.has_key ("LiveThumbData", "AEContrast")) {
1692                 aeContrast   = keyFile.get_integer ("LiveThumbData", "AEContrast");
1693             } else {
1694                 aeValid = false;
1695             }
1696             if (keyFile.has_key ("LiveThumbData", "AEBlack")) {
1697                 aeBlack   = keyFile.get_integer ("LiveThumbData", "AEBlack");
1698             } else {
1699                 aeValid = false;
1700             }
1701             if (keyFile.has_key ("LiveThumbData", "AEHighlightCompression")) {
1702                 aeHighlightCompression   = keyFile.get_integer ("LiveThumbData", "AEHighlightCompression");
1703             } else {
1704                 aeValid = false;
1705             }
1706             if (keyFile.has_key ("LiveThumbData", "AEHighlightCompressionThreshold")) {
1707                 aeHighlightCompressionThreshold   = keyFile.get_integer ("LiveThumbData", "AEHighlightCompressionThreshold");
1708             } else {
1709                 aeValid = false;
1710             }
1711 
1712             if (keyFile.has_key ("LiveThumbData", "RedMultiplier")) {
1713                 redMultiplier       = keyFile.get_double ("LiveThumbData", "RedMultiplier");
1714             }
1715 
1716             if (keyFile.has_key ("LiveThumbData", "GreenMultiplier")) {
1717                 greenMultiplier     = keyFile.get_double ("LiveThumbData", "GreenMultiplier");
1718             }
1719 
1720             if (keyFile.has_key ("LiveThumbData", "BlueMultiplier")) {
1721                 blueMultiplier      = keyFile.get_double ("LiveThumbData", "BlueMultiplier");
1722             }
1723 
1724             if (keyFile.has_key ("LiveThumbData", "Scale")) {
1725                 scale               = keyFile.get_double ("LiveThumbData", "Scale");
1726             }
1727 
1728             if (keyFile.has_key ("LiveThumbData", "DefaultGain")) {
1729                 defGain             = keyFile.get_double ("LiveThumbData", "DefaultGain");
1730             }
1731 
1732             if (keyFile.has_key ("LiveThumbData", "ScaleForSave")) {
1733                 scaleForSave        = keyFile.get_integer ("LiveThumbData", "ScaleForSave");
1734             }
1735 
1736             if (keyFile.has_key ("LiveThumbData", "GammaCorrected")) {
1737                 gammaCorrected      = keyFile.get_boolean ("LiveThumbData", "GammaCorrected");
1738             }
1739 
1740             if (keyFile.has_key ("LiveThumbData", "ColorMatrix")) {
1741                 std::vector<double> cm = keyFile.get_double_list ("LiveThumbData", "ColorMatrix");
1742                 int ix = 0;
1743 
1744                 for (int i = 0; i < 3; i++)
1745                     for (int j = 0; j < 3; j++) {
1746                         colorMatrix[i][j] = cm[ix++];
1747                     }
1748             }
1749 
1750             if (keyFile.has_key("LiveThumbData", "ScaleGain")) {
1751                 scaleGain = keyFile.get_double("LiveThumbData", "ScaleGain");
1752             }
1753         }
1754 
1755         return true;
1756     } catch (Glib::Error &err) {
1757         if (options.rtSettings.verbose) {
1758             printf ("Thumbnail::readData / Error code %d while reading values from \"%s\":\n%s\n", err.code(), fname.c_str(), err.what().c_str());
1759         }
1760     } catch (...) {
1761         if (options.rtSettings.verbose) {
1762             printf ("Thumbnail::readData / Unknown exception while trying to load \"%s\"!\n", fname.c_str());
1763         }
1764     }
1765 
1766     return false;
1767 }
1768 
writeData(const Glib::ustring & fname)1769 bool Thumbnail::writeData  (const Glib::ustring& fname)
1770 {
1771     MyMutex::MyLock thmbLock (thumbMutex);
1772 
1773     Glib::ustring keyData;
1774 
1775     try {
1776 
1777         Glib::KeyFile keyFile;
1778 
1779         try {
1780             keyFile.load_from_file (fname);
1781         } catch (Glib::Error&) {}
1782 
1783         keyFile.set_double  ("LiveThumbData", "CamWBRed", camwbRed);
1784         keyFile.set_double  ("LiveThumbData", "CamWBGreen", camwbGreen);
1785         keyFile.set_double  ("LiveThumbData", "CamWBBlue", camwbBlue);
1786         keyFile.set_double  ("LiveThumbData", "RedAWBMul", redAWBMul);
1787         keyFile.set_double  ("LiveThumbData", "GreenAWBMul", greenAWBMul);
1788         keyFile.set_double  ("LiveThumbData", "BlueAWBMul", blueAWBMul);
1789         keyFile.set_double  ("LiveThumbData", "AEExposureCompensation", aeExposureCompensation);
1790         keyFile.set_integer ("LiveThumbData", "AELightness", aeLightness);
1791         keyFile.set_integer ("LiveThumbData", "AEContrast", aeContrast);
1792         keyFile.set_integer ("LiveThumbData", "AEBlack", aeBlack);
1793         keyFile.set_integer ("LiveThumbData", "AEHighlightCompression", aeHighlightCompression);
1794         keyFile.set_integer ("LiveThumbData", "AEHighlightCompressionThreshold", aeHighlightCompressionThreshold);
1795         keyFile.set_double  ("LiveThumbData", "RedMultiplier", redMultiplier);
1796         keyFile.set_double  ("LiveThumbData", "GreenMultiplier", greenMultiplier);
1797         keyFile.set_double  ("LiveThumbData", "BlueMultiplier", blueMultiplier);
1798         keyFile.set_double  ("LiveThumbData", "Scale", scale);
1799         keyFile.set_double  ("LiveThumbData", "DefaultGain", defGain);
1800         keyFile.set_integer ("LiveThumbData", "ScaleForSave", scaleForSave);
1801         keyFile.set_boolean ("LiveThumbData", "GammaCorrected", gammaCorrected);
1802         Glib::ArrayHandle<double> cm ((double*)colorMatrix, 9, Glib::OWNERSHIP_NONE);
1803         keyFile.set_double_list("LiveThumbData", "ColorMatrix", cm);
1804         keyFile.set_double("LiveThumbData", "ScaleGain", scaleGain);
1805 
1806         keyData = keyFile.to_data ();
1807 
1808     } catch (Glib::Error& err) {
1809         if (options.rtSettings.verbose) {
1810             printf ("Thumbnail::writeData / Error code %d while reading values from \"%s\":\n%s\n", err.code(), fname.c_str(), err.what().c_str());
1811         }
1812     } catch (...) {
1813         if (options.rtSettings.verbose) {
1814             printf ("Thumbnail::writeData / Unknown exception while trying to save \"%s\"!\n", fname.c_str());
1815         }
1816     }
1817 
1818     if (keyData.empty ()) {
1819         return false;
1820     }
1821 
1822     FILE *f = g_fopen (fname.c_str (), "wt");
1823 
1824     if (!f) {
1825         if (options.rtSettings.verbose) {
1826             printf ("Thumbnail::writeData / Error: unable to open file \"%s\" with write access!\n", fname.c_str());
1827         }
1828 
1829         return false;
1830     } else {
1831         fprintf (f, "%s", keyData.c_str ());
1832         fclose (f);
1833     }
1834 
1835     return true;
1836 }
1837 
readEmbProfile(const Glib::ustring & fname)1838 bool Thumbnail::readEmbProfile  (const Glib::ustring& fname)
1839 {
1840 
1841     embProfileData = nullptr;
1842     embProfile = nullptr;
1843     embProfileLength = 0;
1844 
1845     FILE* f = g_fopen (fname.c_str (), "rb");
1846 
1847     if (f) {
1848         if (!fseek (f, 0, SEEK_END)) {
1849             int profileLength = ftell (f);
1850 
1851             if (profileLength > 0) {
1852                 embProfileLength = profileLength;
1853 
1854                 if (!fseek (f, 0, SEEK_SET)) {
1855                     embProfileData = new unsigned char[embProfileLength];
1856                     embProfileLength = fread (embProfileData, 1, embProfileLength, f);
1857                     embProfile = cmsOpenProfileFromMem (embProfileData, embProfileLength);
1858                 }
1859             }
1860         }
1861 
1862         fclose (f);
1863         return embProfile != nullptr;
1864     }
1865 
1866     return false;
1867 }
1868 
writeEmbProfile(const Glib::ustring & fname)1869 bool Thumbnail::writeEmbProfile (const Glib::ustring& fname)
1870 {
1871 
1872     if (embProfileData) {
1873         FILE* f = g_fopen (fname.c_str (), "wb");
1874 
1875         if (f) {
1876             fwrite (embProfileData, 1, embProfileLength, f);
1877             fclose (f);
1878             return true;
1879         }
1880     }
1881 
1882     return false;
1883 }
1884 
1885 
getImage8Data()1886 unsigned char* Thumbnail::getImage8Data()
1887 {
1888     if (thumbImg && thumbImg->getType() == rtengine::sImage8) {
1889         Image8* img8 = static_cast<Image8*> (thumbImg);
1890         return img8->data;
1891     }
1892 
1893     return nullptr;
1894 }
1895 
1896 
loadInfoFromRaw(const Glib::ustring & fname,eSensorType & sensorType,int & w,int & h,int fixwh)1897 Thumbnail* Thumbnail::loadInfoFromRaw(const Glib::ustring &fname, eSensorType &sensorType, int &w, int &h, int fixwh)
1898 {
1899     RawImage *ri = new RawImage(fname);
1900     unsigned int tempImageNum = 0;
1901 
1902     int r = ri->loadRaw(false, tempImageNum, false, nullptr, 1.0, false);
1903 
1904     if (r) {
1905         delete ri;
1906         sensorType = ST_NONE;
1907         return nullptr;
1908     }
1909 
1910     if (ri->getFrameCount() == 7) {
1911         // special case for Hasselblad H6D-100cMS pixelshift files
1912         // first frame is not bayer, load second frame
1913         int r = ri->loadRaw(false, 1, false, nullptr, 1.0, false);
1914 
1915         if (r) {
1916             delete ri;
1917             sensorType = ST_NONE;
1918             return nullptr;
1919         }
1920     }
1921     sensorType = ri->getSensorType();
1922 
1923     int width = ri->get_width();
1924     int height = ri->get_height();
1925     rtengine::Thumbnail *tpp = new rtengine::Thumbnail;
1926 
1927     tpp->isRaw = true;
1928     tpp->sensorType = sensorType;
1929     tpp->embProfile = nullptr;
1930     tpp->embProfileData = nullptr;
1931     tpp->embProfileLength = 0;
1932 
1933     tpp->redMultiplier = ri->get_pre_mul (0);
1934     tpp->greenMultiplier = ri->get_pre_mul (1);
1935     tpp->blueMultiplier = ri->get_pre_mul (2);
1936 
1937     tpp->camwbRed = 1.0;
1938     tpp->camwbGreen = 1.0;
1939     tpp->camwbBlue = 1.0;
1940     tpp->defGain = 1.0;
1941 
1942     float pre_mul[4], scale_mul[4], cblack[4];
1943     ri->get_colorsCoeff (pre_mul, scale_mul, cblack, false);
1944     tpp->defGain = max(scale_mul[0], scale_mul[1], scale_mul[2], scale_mul[3]) / min (scale_mul[0], scale_mul[1], scale_mul[2], scale_mul[3]);
1945     tpp->defGain *= std::pow(2, ri->getBaselineExposure());
1946 
1947     int skip = 1;
1948     int firstgreen = 1;
1949 
1950     if (ri->get_FujiWidth() != 0) {
1951         if (fixwh == 1) { // fix height, scale width
1952             skip = ((ri->get_height() - ri->get_FujiWidth()) / sqrt (0.5) - firstgreen - 1) / h;
1953         } else {
1954             skip = (ri->get_FujiWidth() / sqrt (0.5) - firstgreen - 1) / w;
1955         }
1956     } else {
1957         if (fixwh == 1) { // fix height, scale width
1958             skip = (ri->get_height() - firstgreen - 1) / h;
1959         } else {
1960             skip = (ri->get_width() - firstgreen - 1) / w;
1961         }
1962     }
1963 
1964     if (skip % 2) {
1965         skip--;
1966     }
1967 
1968     if (skip < 2) {
1969         skip = 2;
1970     }
1971 
1972     int hskip = skip, vskip = skip;
1973 
1974     if (!ri->get_model().compare ("D1X")) {
1975         hskip *= 2;
1976     }
1977 
1978     int tmpw = (width - 2) / hskip;
1979     int tmph = (height - 2) / vskip;
1980 
1981 
1982     if (ri->get_FujiWidth() != 0) {
1983         int fw = ri->get_FujiWidth() / hskip;
1984         double step = sqrt (0.5);
1985         int wide = fw / step;
1986         int high = (tmph - fw) / step;
1987         tmpw = wide;
1988         tmph = high;
1989     }
1990 
1991     const bool rotate_90 =
1992         true
1993         && (
1994             ri->get_rotateDegree() == 90
1995             || ri->get_rotateDegree() == 270
1996         );
1997 
1998     if (rotate_90) {
1999         std::swap (tmpw, tmph);
2000     }
2001 
2002     if (fixwh == 1) { // fix height, scale width
2003         w = tmpw * h / tmph;
2004     } else {
2005         h = tmph * w / tmpw;
2006     }
2007 
2008 
2009     if (ri->get_FujiWidth() != 0) {
2010         tpp->scale = (double) (height - ri->get_FujiWidth()) * 2.0 / (rotate_90 ? w : h);
2011     } else {
2012         tpp->scale = (double) height / (rotate_90 ? w : h);
2013     }
2014 
2015     RawImageSource::computeFullSize(ri, TR_NONE, tpp->full_width, tpp->full_height);
2016 
2017     delete ri;
2018     return tpp;
2019 }
2020 
2021 
2022 } // namespace rtengine
2023