1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2004-07-29
7 * Description : image levels manipulation methods.
8 *
9 * Copyright (C) 2004-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10 *
11 * This program is free software; you can redistribute it
12 * and/or modify it under the terms of the GNU General
13 * Public License as published by the Free Software Foundation;
14 * either version 2, or (at your option)
15 * any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * ============================================================ */
23
24 /**
25 * Map RGB to intensity
26 */
27 #define LEVELS_RGB_INTENSITY_RED 0.30
28 #define LEVELS_RGB_INTENSITY_GREEN 0.59
29 #define LEVELS_RGB_INTENSITY_BLUE 0.11
30 #define LEVELS_RGB_INTENSITY(r,g,b) ((r) * LEVELS_RGB_INTENSITY_RED + \
31 (g) * LEVELS_RGB_INTENSITY_GREEN + \
32 (b) * LEVELS_RGB_INTENSITY_BLUE)
33
34 #include "imagelevels.h"
35
36 // Qt includes
37
38 #include <QFile>
39
40 // C++ includes
41
42 #include <cstdio>
43 #include <cmath>
44 #include <cstring>
45 #include <cstdlib>
46 #include <cerrno>
47
48 // Local includes
49
50 #include "digikam_debug.h"
51 #include "digikam_config.h"
52 #include "imagehistogram.h"
53 #include "digikam_globals.h"
54
55 namespace Digikam
56 {
57
58 class Q_DECL_HIDDEN ImageLevels::Private
59 {
60
61 public:
62
63 enum PixelType
64 {
65 RedPixel = 0,
66 GreenPixel,
67 BluePixel,
68 AlphaPixel
69 };
70
71 struct Q_DECL_HIDDEN _Levels
72 {
73 double gamma[5] = { 0.0 };
74
75 int low_input[5] = { 0 };
76 int high_input[5] = { 0 };
77
78 int low_output[5] = { 0 };
79 int high_output[5] = { 0 };
80 };
81
82 struct Q_DECL_HIDDEN _Lut
83 {
84 unsigned short** luts = nullptr;
85 int nchannels = 0;
86 };
87
88 public:
89
Private()90 explicit Private()
91 : levels (nullptr),
92 lut (nullptr),
93 sixteenBit (false),
94 dirty (false)
95 {
96 }
97
98 /// Levels data.
99 _Levels* levels;
100
101 /// Lut data.
102 _Lut* lut;
103
104 bool sixteenBit;
105 bool dirty;
106 };
107
ImageLevels(bool sixteenBit)108 ImageLevels::ImageLevels(bool sixteenBit)
109 : d(new Private)
110 {
111 d->lut = new Private::_Lut;
112 d->levels = new Private::_Levels;
113 d->sixteenBit = sixteenBit;
114 d->lut->luts = nullptr;
115 d->lut->nchannels = 0;
116
117 reset();
118 }
119
~ImageLevels()120 ImageLevels::~ImageLevels()
121 {
122 if (d->lut)
123 {
124 if (d->lut->luts)
125 {
126 for (int i = 0 ; i < d->lut->nchannels ; ++i)
127 {
128 delete [] d->lut->luts[i];
129 }
130
131 delete [] d->lut->luts;
132 }
133
134 delete d->lut;
135 }
136
137 if (d->levels)
138 {
139 delete d->levels;
140 }
141
142 delete d;
143 }
144
isDirty()145 bool ImageLevels::isDirty()
146 {
147 return d->dirty;
148 }
149
isSixteenBits()150 bool ImageLevels::isSixteenBits()
151 {
152 return d->sixteenBit;
153 }
154
reset()155 void ImageLevels::reset()
156 {
157 for (int channel = 0 ; channel < 5 ; ++channel)
158 {
159 levelsChannelReset(channel);
160 }
161 }
162
levelsChannelReset(int channel)163 void ImageLevels::levelsChannelReset(int channel)
164 {
165 if (!d->levels)
166 {
167 return;
168 }
169
170 d->levels->gamma[channel] = 1.0;
171 d->levels->low_input[channel] = 0;
172 d->levels->high_input[channel] = d->sixteenBit ? 65535 : 255;
173 d->levels->low_output[channel] = 0;
174 d->levels->high_output[channel] = d->sixteenBit ? 65535 : 255;
175 d->dirty = false;
176 }
177
levelsAuto(ImageHistogram * const hist)178 void ImageLevels::levelsAuto(ImageHistogram* const hist)
179 {
180 if (!d->levels || !hist)
181 {
182 return;
183 }
184
185 levelsChannelReset(LuminosityChannel);
186
187 for (int channel = RedChannel ; channel <= BlueChannel ; ++channel)
188 {
189 levelsChannelAuto(hist, channel);
190 }
191
192 d->dirty = true;
193 }
194
levelsChannelAuto(ImageHistogram * const hist,int channel)195 void ImageLevels::levelsChannelAuto(ImageHistogram* const hist, int channel)
196 {
197 if (!d->levels || !hist)
198 {
199 return;
200 }
201
202 d->levels->gamma[channel] = 1.0;
203 d->levels->low_output[channel] = 0;
204 d->levels->high_output[channel] = d->sixteenBit ? 65535 : 255;
205 double count = hist->getCount(channel, 0, d->sixteenBit ? 65535 : 255);
206
207 if (count == 0.0)
208 {
209 d->levels->low_input[channel] = 0;
210 d->levels->high_input[channel] = 0;
211 }
212 else
213 {
214 // Set the low input
215
216 double new_count = 0.0;
217 double percentage = 0.0;
218 double next_percentage = 0.0;
219 int i;
220
221 for (i = 0 ; i < (d->sixteenBit ? 65535 : 255) ; ++i)
222 {
223 new_count += hist->getValue(channel, i);
224 percentage = new_count / count;
225 next_percentage = (new_count + hist->getValue(channel, i + 1)) / count;
226
227 if (fabs(percentage - 0.006) < fabs(next_percentage - 0.006))
228 {
229 d->levels->low_input[channel] = i + 1;
230 break;
231 }
232 }
233
234 // Set the high input
235
236 new_count = 0.0;
237
238 for (i = (d->sixteenBit ? 65535 : 255) ; i > 0 ; --i)
239 {
240 new_count += hist->getValue(channel, i);
241 percentage = new_count / count;
242 next_percentage = (new_count + hist->getValue(channel, i - 1)) / count;
243
244 if (fabs(percentage - 0.006) < fabs(next_percentage - 0.006))
245 {
246 d->levels->high_input[channel] = i - 1;
247 break;
248 }
249 }
250 }
251
252 d->dirty = true;
253 }
254
levelsInputFromColor(int channel,const DColor & color)255 int ImageLevels::levelsInputFromColor(int channel, const DColor& color)
256 {
257 switch (channel)
258 {
259 case LuminosityChannel:
260 return qMax(qMax(color.red(), color.green()), color.blue());
261
262 case RedChannel:
263 return color.red();
264
265 case GreenChannel:
266 return color.green();
267
268 case BlueChannel:
269 return color.blue();
270 }
271
272 return 0; // just to please the compiler.
273 }
274
levelsBlackToneAdjustByColors(int channel,const DColor & color)275 void ImageLevels::levelsBlackToneAdjustByColors(int channel, const DColor& color)
276 {
277 if (!d->levels)
278 {
279 return;
280 }
281
282 d->levels->low_input[channel] = levelsInputFromColor(channel, color);
283 d->dirty = true;
284 }
285
levelsWhiteToneAdjustByColors(int channel,const DColor & color)286 void ImageLevels::levelsWhiteToneAdjustByColors(int channel, const DColor& color)
287 {
288 if (!d->levels)
289 {
290 return;
291 }
292
293 d->levels->high_input[channel] = levelsInputFromColor(channel, color);
294 d->dirty = true;
295 }
296
levelsGrayToneAdjustByColors(int channel,const DColor & color)297 void ImageLevels::levelsGrayToneAdjustByColors(int channel, const DColor& color)
298 {
299 if (!d->levels)
300 {
301 return;
302 }
303
304 int input;
305 int range;
306 double inten;
307 double out_light;
308 unsigned short lightness;
309
310 // Calculate lightness value.
311
312 lightness = (unsigned short)LEVELS_RGB_INTENSITY(color.red(), color.green(), color.blue());
313 input = levelsInputFromColor(channel, color);
314 range = d->levels->high_input[channel] - d->levels->low_input[channel];
315
316 if (range <= 0)
317 {
318 return;
319 }
320
321 input -= d->levels->low_input[channel];
322
323 if (input < 0)
324 {
325 return;
326 }
327
328 // Normalize input and lightness.
329
330 inten = (double) input / (double) range;
331 out_light = (double) lightness / (double) range;
332
333 if (out_light <= 0)
334 {
335 return;
336 }
337
338 // Map selected color to corresponding lightness.
339
340 d->levels->gamma[channel] = log(inten) / log(out_light);
341 d->dirty = true;
342 }
343
levelsCalculateTransfers()344 void ImageLevels::levelsCalculateTransfers()
345 {
346 double inten;
347 int i, j;
348
349 if (!d->levels)
350 {
351 return;
352 }
353
354 // Recalculate the levels arrays.
355
356 for (j = 0 ; j < 5 ; ++j)
357 {
358 for (i = 0 ; i <= (d->sixteenBit ? 65535 : 255) ; ++i)
359 {
360 // determine input intensity.
361
362 if (d->levels->high_input[j] != d->levels->low_input[j])
363 {
364 inten = ((double)(i - d->levels->low_input[j]) /
365 (double)(d->levels->high_input[j] - d->levels->low_input[j]));
366 }
367 else
368 {
369 inten = (double)(i - d->levels->low_input[j]);
370 }
371
372 inten = CLAMP(inten, 0.0, 1.0);
373
374 if (d->levels->gamma[j] != 0.0)
375 {
376 inten = pow(inten, (1.0 / d->levels->gamma[j]));
377 (void)inten; // Remove clang warnings.
378 }
379 }
380 }
381 }
382
levelsLutFunc(int n_channels,int channel,float value)383 float ImageLevels::levelsLutFunc(int n_channels, int channel, float value)
384 {
385 double inten;
386 int j;
387
388 if (!d->levels)
389 {
390 return 0.0;
391 }
392
393 if (n_channels == 1)
394 {
395 j = 0;
396 }
397 else
398 {
399 j = channel + 1;
400 }
401
402 inten = value;
403
404 // For color images this runs through the loop with j = channel +1
405 // the first time and j = 0 the second time.
406 //
407 // For bw images this runs through the loop with j = 0 the first and
408 // only time.
409
410 for ( ; j >= 0 ; j -= (channel + 1))
411 {
412 // Don't apply the overall curve to the alpha channel.
413
414 if ((j == 0) && ((n_channels == 2) || (n_channels == 4)) && (channel == n_channels - 1))
415 {
416 return inten;
417 }
418
419 // Determine input intensity.
420
421 if (d->levels->high_input[j] != d->levels->low_input[j])
422 {
423 inten = ((double)((float)(d->sixteenBit ? 65535 : 255) * inten - d->levels->low_input[j]) /
424 (double)(d->levels->high_input[j] - d->levels->low_input[j]));
425 }
426 else
427 {
428 inten = (double)((float)(d->sixteenBit ? 65535 : 255) * inten - d->levels->low_input[j]);
429 }
430
431 if (d->levels->gamma[j] != 0.0)
432 {
433 if (inten >= 0.0)
434 {
435 inten = pow(inten, (1.0 / d->levels->gamma[j]));
436 }
437 else
438 {
439 inten = -pow(-inten, (1.0 / d->levels->gamma[j]));
440 }
441 }
442
443 // determine the output intensity.
444
445 if (d->levels->high_output[j] >= d->levels->low_output[j])
446 {
447 inten = (double)(inten * (d->levels->high_output[j] -
448 d->levels->low_output[j]) + d->levels->low_output[j]);
449 }
450 else
451 {
452 inten = (double)(d->levels->low_output[j] - inten *
453 (d->levels->low_output[j] - d->levels->high_output[j]));
454 }
455
456 inten /= (float)(d->sixteenBit ? 65535 : 255);
457 }
458
459 return inten;
460 }
461
levelsLutSetup(int nchannels)462 void ImageLevels::levelsLutSetup(int nchannels)
463 {
464 int i;
465 uint v;
466 double val;
467
468 if (d->lut->luts)
469 {
470 for (i = 0 ; i < d->lut->nchannels ; ++i)
471 {
472 delete [] d->lut->luts[i];
473 }
474
475 delete [] d->lut->luts;
476 }
477
478 d->lut->nchannels = nchannels;
479 d->lut->luts = new unsigned short*[d->lut->nchannels];
480
481 for (i = 0 ; i < d->lut->nchannels ; ++i)
482 {
483 d->lut->luts[i] = new unsigned short[(d->sixteenBit ? 65535 : 255) + 1];
484
485 for (v = 0 ; v <= (uint)(d->sixteenBit ? 65535 : 255) ; ++v)
486 {
487 // to add gamma correction use func(v ^ g) ^ 1/g instead.
488
489 val = (float)(d->sixteenBit ? 65535 : 255) *
490 levelsLutFunc(d->lut->nchannels, i, v / (float)(d->sixteenBit ? 65535 : 255)) + 0.5;
491
492 d->lut->luts[i][v] = (unsigned short)CLAMP(val, 0.0, (d->sixteenBit ? 65535.0 : 255.0));
493 }
494 }
495 }
496
levelsLutProcess(uchar * const srcPR,uchar * const destPR,int w,int h)497 void ImageLevels::levelsLutProcess(uchar* const srcPR, uchar* const destPR, int w, int h)
498 {
499 unsigned short* lut0 = nullptr, *lut1 = nullptr, *lut2 = nullptr, *lut3 = nullptr;
500
501 int i;
502
503 if (d->lut->nchannels > 0)
504 {
505 lut0 = d->lut->luts[0];
506 }
507
508 if (d->lut->nchannels > 1)
509 {
510 lut1 = d->lut->luts[1];
511 }
512
513 if (d->lut->nchannels > 2)
514 {
515 lut2 = d->lut->luts[2];
516 }
517
518 if (d->lut->nchannels > 3)
519 {
520 lut3 = d->lut->luts[3];
521 }
522
523 if (!d->sixteenBit) // 8 bits image.
524 {
525 uchar red, green, blue, alpha;
526 uchar* ptr = srcPR;
527 uchar* dst = destPR;
528
529 for (i = 0 ; i < w * h ; ++i)
530 {
531 blue = ptr[0];
532 green = ptr[1];
533 red = ptr[2];
534 alpha = ptr[3];
535
536 if (d->lut->nchannels > 0)
537 {
538 red = lut0[red];
539 }
540
541 if (d->lut->nchannels > 1)
542 {
543 green = lut1[green];
544 }
545
546 if (d->lut->nchannels > 2)
547 {
548 blue = lut2[blue];
549 }
550
551 if (d->lut->nchannels > 3)
552 {
553 alpha = lut3[alpha];
554 }
555
556 dst[0] = blue;
557 dst[1] = green;
558 dst[2] = red;
559 dst[3] = alpha;
560
561 ptr += 4;
562 dst += 4;
563 }
564 }
565 else // 16 bits image.
566 {
567 unsigned short red, green, blue, alpha;
568 unsigned short* ptr = reinterpret_cast<unsigned short*>(srcPR);
569 unsigned short* dst = reinterpret_cast<unsigned short*>(destPR);
570
571 for (i = 0 ; i < w * h ; ++i)
572 {
573 blue = ptr[0];
574 green = ptr[1];
575 red = ptr[2];
576 alpha = ptr[3];
577
578 if (d->lut->nchannels > 0)
579 {
580 red = lut0[red];
581 }
582
583 if (d->lut->nchannels > 1)
584 {
585 green = lut1[green];
586 }
587
588 if (d->lut->nchannels > 2)
589 {
590 blue = lut2[blue];
591 }
592
593 if (d->lut->nchannels > 3)
594 {
595 alpha = lut3[alpha];
596 }
597
598 dst[0] = blue;
599 dst[1] = green;
600 dst[2] = red;
601 dst[3] = alpha;
602
603 ptr += 4;
604 dst += 4;
605 }
606 }
607 }
608
setLevelGammaValue(int channel,double val)609 void ImageLevels::setLevelGammaValue(int channel, double val)
610 {
611 if (d->levels && (channel >= 0) && (channel < 5))
612 {
613 d->levels->gamma[channel] = val;
614 d->dirty = true;
615 }
616 }
617
setLevelLowInputValue(int channel,int val)618 void ImageLevels::setLevelLowInputValue(int channel, int val)
619 {
620 if (d->levels && (channel >= 0) && (channel < 5))
621 {
622 d->levels->low_input[channel] = val;
623 d->dirty = true;
624 }
625 }
626
setLevelHighInputValue(int channel,int val)627 void ImageLevels::setLevelHighInputValue(int channel, int val)
628 {
629 if (d->levels && (channel >= 0) && (channel < 5))
630 {
631 d->levels->high_input[channel] = val;
632 d->dirty = true;
633 }
634 }
635
setLevelLowOutputValue(int channel,int val)636 void ImageLevels::setLevelLowOutputValue(int channel, int val)
637 {
638 if (d->levels && (channel >= 0) && (channel < 5))
639 {
640 d->levels->low_output[channel] = val;
641 d->dirty = true;
642 }
643 }
644
setLevelHighOutputValue(int channel,int val)645 void ImageLevels::setLevelHighOutputValue(int channel, int val)
646 {
647 if (d->levels && (channel >= 0) && (channel < 5))
648 {
649 d->levels->high_output[channel] = val;
650 d->dirty = true;
651 }
652 }
653
getLevelGammaValue(int channel)654 double ImageLevels::getLevelGammaValue(int channel)
655 {
656 if (d->levels && (channel >= 0) && (channel < 5))
657 {
658 return (d->levels->gamma[channel]);
659 }
660
661 return 0.0;
662 }
663
getLevelLowInputValue(int channel)664 int ImageLevels::getLevelLowInputValue(int channel)
665 {
666 if (d->levels && (channel >= 0) && (channel < 5))
667 {
668 return (d->levels->low_input[channel]);
669 }
670
671 return 0;
672 }
673
getLevelHighInputValue(int channel)674 int ImageLevels::getLevelHighInputValue(int channel)
675 {
676 if (d->levels && (channel >= 0) && (channel < 5))
677 {
678 return (d->levels->high_input[channel]);
679 }
680
681 return 0;
682 }
683
getLevelLowOutputValue(int channel)684 int ImageLevels::getLevelLowOutputValue(int channel)
685 {
686 if (d->levels && (channel >= 0) && (channel < 5))
687 {
688 return (d->levels->low_output[channel]);
689 }
690
691 return 0;
692 }
693
getLevelHighOutputValue(int channel)694 int ImageLevels::getLevelHighOutputValue(int channel)
695 {
696 if (d->levels && (channel >= 0) && (channel < 5))
697 {
698 return (d->levels->high_output[channel]);
699 }
700
701 return 0;
702 }
703
loadLevelsFromGimpLevelsFile(const QUrl & fileUrl)704 bool ImageLevels::loadLevelsFromGimpLevelsFile(const QUrl& fileUrl)
705 {
706 // TODO : support QUrl !
707
708 FILE* file = nullptr;
709 int low_input[5];
710 int high_input[5];
711 int low_output[5];
712 int high_output[5];
713 double gamma[5];
714 int i, fields;
715 char buf[50];
716 char* nptr = nullptr;
717
718 #ifdef Q_OS_WIN
719
720 file = _wfopen((const wchar_t*)fileUrl.toLocalFile().utf16(), L"r");
721
722 #else
723
724 file = fopen(fileUrl.toLocalFile().toUtf8().constData(), "r");
725
726 #endif
727
728 if (!file)
729 {
730 return false;
731 }
732
733 if (! fgets(buf, sizeof(buf), file))
734 {
735 fclose(file);
736 return false;
737 }
738
739 if (strcmp(buf, "# GIMP Levels File\n") != 0)
740 {
741 fclose(file);
742 return false;
743 }
744
745 for (i = 0 ; i < 5 ; ++i)
746 {
747 // FIXME: scanf without field width limits can crash with huge input data
748
749 fields = fscanf(file, "%d %d %d %d ",
750 &low_input[i],
751 &high_input[i],
752 &low_output[i],
753 &high_output[i]);
754
755 if (fields != 4)
756 {
757 qCWarning(DIGIKAM_DIMG_LOG) << "Invalid Gimp levels file!";
758 fclose(file);
759 return false;
760 }
761
762 if (!fgets(buf, 50, file))
763 {
764 qCWarning(DIGIKAM_DIMG_LOG) << "Invalid Gimp levels file!";
765 fclose(file);
766 return false;
767 }
768
769 gamma[i] = strtod(buf, &nptr);
770
771 if (buf == nptr || errno == ERANGE)
772 {
773 qCWarning(DIGIKAM_DIMG_LOG) << "Invalid Gimp levels file!";
774 fclose(file);
775 return false;
776 }
777 }
778
779 for (i = 0 ; i < 5 ; ++i)
780 {
781 setLevelGammaValue(i, gamma[i]);
782 setLevelLowInputValue(i, d->sixteenBit ? low_input[i] * 255 : low_input[i]);
783 setLevelHighInputValue(i, d->sixteenBit ? high_input[i] * 255 : high_input[i]);
784 setLevelLowOutputValue(i, d->sixteenBit ? low_output[i] * 255 : low_output[i]);
785 setLevelHighOutputValue(i, d->sixteenBit ? high_output[i] * 255 : high_output[i]);
786 }
787
788 fclose(file);
789 return true;
790 }
791
saveLevelsToGimpLevelsFile(const QUrl & fileUrl)792 bool ImageLevels::saveLevelsToGimpLevelsFile(const QUrl& fileUrl)
793 {
794 // TODO : support QUrl !
795
796 FILE* file = nullptr;
797 int i;
798
799 #ifdef Q_OS_WIN
800
801 file = _wfopen((const wchar_t*)fileUrl.toLocalFile().utf16(), L"w");
802
803 #else
804
805 file = fopen(fileUrl.toLocalFile().toUtf8().constData(), "w");
806
807 #endif
808
809 if (!file)
810 {
811 return false;
812 }
813
814 fprintf(file, "# GIMP Levels File\n");
815
816 for (i = 0 ; i < 5 ; ++i)
817 {
818 char buf[256];
819 sprintf(buf, "%f", getLevelGammaValue(i));
820
821 fprintf(file, "%d %d %d %d %s\n",
822 d->sixteenBit ? getLevelLowInputValue(i) / 255 : getLevelLowInputValue(i),
823 d->sixteenBit ? getLevelHighInputValue(i) / 255 : getLevelHighInputValue(i),
824 d->sixteenBit ? getLevelLowOutputValue(i) / 255 : getLevelLowOutputValue(i),
825 d->sixteenBit ? getLevelHighInputValue(i) / 255 : getLevelHighInputValue(i),
826 buf);
827 }
828
829 fflush(file);
830 fclose(file);
831
832 return true;
833 }
834
835 } // namespace Digikam
836