1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2004-12-01
7 * Description : image curves 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 #include "imagecurves.h"
25
26 // C++ includes
27
28 #include <cstdio>
29 #include <cmath>
30 #include <cstring>
31 #include <cstdlib>
32 #include <cerrno>
33
34 // Qt includes
35
36 #include <QFile>
37 #include <QSharedData>
38 #include <QDataStream>
39
40 // Local includes
41
42 #include "digikam_debug.h"
43 #include "digikam_config.h"
44 #include "curvescontainer.h"
45 #include "filteraction.h"
46 #include "digikam_globals.h"
47
48 namespace Digikam
49 {
50
51 class Q_DECL_HIDDEN ImageCurves::Private : public QSharedData
52 {
53
54 public:
55
56 struct Q_DECL_HIDDEN _Curves
57 {
58 /**
59 * Curve types by channels (Smooth or Free).
60 */
61 ImageCurves::CurveType curve_type[ImageCurves::NUM_CHANNELS];
62 /**
63 * Curve main points in Smooth mode ([channel][point id][x,y])
64 */
65 int points[ImageCurves::NUM_CHANNELS][ImageCurves::NUM_POINTS][2];
66 /**
67 * Curve values by channels
68 */
69 unsigned short curve[ImageCurves::NUM_CHANNELS][NUM_SEGMENTS_16BIT];
70 };
71
72 struct Q_DECL_HIDDEN _Lut
73 {
74 unsigned short** luts = nullptr;
75 int nchannels = 0;
76 };
77
78 public:
79
Private()80 explicit Private()
81 : curves (nullptr),
82 lut (nullptr),
83 segmentMax (0),
84 dirty (false)
85 {
86 }
87
~Private()88 ~Private()
89 {
90 if (lut)
91 {
92 freeLutData();
93 delete lut;
94 }
95
96 if (curves)
97 {
98 delete curves;
99 }
100 }
101
init(bool sixteenBit)102 void init(bool sixteenBit)
103 {
104 lut = new Private::_Lut;
105 lut->luts = nullptr;
106 curves = new Private::_Curves;
107 segmentMax = sixteenBit ? MAX_SEGMENT_16BIT : MAX_SEGMENT_8BIT;
108 }
109
isPointEnabled(const QPoint & point) const110 bool isPointEnabled(const QPoint& point) const
111 {
112 return (point.x() > - 1) && (point.y() > -1);
113 }
114
freeLutData()115 void freeLutData()
116 {
117 if (lut->luts)
118 {
119 for (int i = 0 ; i < lut->nchannels ; ++i)
120 {
121 delete [] lut->luts[i];
122 }
123
124 delete [] lut->luts;
125 }
126 }
127
128 /// Curves data.
129 struct _Curves* curves;
130
131 /// Lut data.
132 struct _Lut* lut;
133
134 int segmentMax;
135
136 bool dirty;
137 };
138
139 ImageCurves::CRMatrix CR_basis =
140 {
141 { -0.5, 1.5, -1.5, 0.5 },
142 { 1.0, -2.5, 2.0, -0.5 },
143 { -0.5, 0.0, 0.5, 0.0 },
144 { 0.0, 1.0, 0.0, 0.0 },
145 };
146
ImageCurves(bool sixteenBit)147 ImageCurves::ImageCurves(bool sixteenBit)
148 : d(new Private)
149 {
150 d->init(sixteenBit);
151 curvesReset();
152 }
153
ImageCurves(const CurvesContainer & container)154 ImageCurves::ImageCurves(const CurvesContainer& container)
155 : d(new Private)
156 {
157 d->init(container.sixteenBit);
158 curvesReset();
159 setContainer(container);
160 }
161
ImageCurves(const ImageCurves & other)162 ImageCurves::ImageCurves(const ImageCurves& other)
163 : d(other.d)
164 {
165 }
166
~ImageCurves()167 ImageCurves::~ImageCurves()
168 {
169 }
170
operator =(const ImageCurves & other)171 ImageCurves& ImageCurves::operator=(const ImageCurves& other)
172 {
173 d = other.d;
174 return *this;
175 }
176
fillFromOtherCurves(ImageCurves * const otherCurves)177 void ImageCurves::fillFromOtherCurves(ImageCurves* const otherCurves)
178 {
179 //qCDebug(DIGIKAM_DIMG_LOG) << "Filling this curve from other curve " << otherCurves;
180
181 curvesReset();
182
183 // if the other curves have the same bit depth, simply copy their data
184
185 if (isSixteenBits() == otherCurves->isSixteenBits())
186 {
187 //qCDebug(DIGIKAM_DIMG_LOG) << "Both curves have same type: isSixteenBits = " << isSixteenBits();
188
189 for (int channel = 0 ; channel < NUM_CHANNELS ; ++channel)
190 {
191 if (otherCurves->getCurveType(channel) == CURVE_SMOOTH)
192 {
193 //qCDebug(DIGIKAM_DIMG_LOG) << "Other is CURVE_SMOOTH";
194
195 setCurveType(channel, CURVE_SMOOTH);
196
197 for (int point = 0 ; point < NUM_POINTS ; ++point)
198 {
199 QPoint p = otherCurves->getCurvePoint(channel, point);
200
201 if (d->isPointEnabled(p))
202 {
203 setCurvePoint(channel, point, p);
204 }
205 }
206 }
207 else
208 {
209 //qCDebug(DIGIKAM_DIMG_LOG) << "Other is CURVE_FREE";
210
211 setCurveType(channel, CURVE_FREE);
212
213 for (int i = 0 ; i <= d->segmentMax ; ++i)
214 {
215 setCurveValue(channel, i, otherCurves->getCurveValue(channel, i));
216 }
217 }
218 }
219 }
220
221 // other curve is 8 bit and this curve is 16 bit
222
223 else if (isSixteenBits() && !otherCurves->isSixteenBits())
224 {
225 //qCDebug(DIGIKAM_DIMG_LOG) << "This curve is 16 bit and the other is 8 bit";
226
227 for (int channel = 0 ; channel < NUM_CHANNELS ; ++channel)
228 {
229 if (otherCurves->getCurveType(channel) == CURVE_SMOOTH)
230 {
231 //qCDebug(DIGIKAM_DIMG_LOG) << "Other is CURVE_SMOOTH";
232
233 setCurveType(channel, CURVE_SMOOTH);
234
235 for (int point = 0 ; point < NUM_POINTS ; ++point)
236 {
237 QPoint p = otherCurves->getCurvePoint(channel, point);
238
239 if (d->isPointEnabled(p))
240 {
241 p.setX(p.x() * MULTIPLIER_16BIT);
242 p.setY(p.y() * MULTIPLIER_16BIT);
243 setCurvePoint(channel, point, p);
244 }
245 }
246 }
247 else
248 {
249 //qCDebug(DIGIKAM_DIMG_LOG) << "Other is CURVE_FREE";
250
251 setCurveType(channel, CURVE_FREE);
252
253 for (int i = 0 ; i <= d->segmentMax ; ++i)
254 {
255 setCurveValue(channel, i * MULTIPLIER_16BIT, otherCurves->getCurveValue(channel, i) * MULTIPLIER_16BIT);
256 }
257 }
258 }
259 }
260
261 // other curve is 16 bit and this is 8 bit
262
263 else if (!isSixteenBits() && otherCurves->isSixteenBits())
264 {
265 //qCDebug(DIGIKAM_DIMG_LOG) << "This curve is 8 bit and the other is 16 bit";
266
267 for (int channel = 0 ; channel < NUM_CHANNELS ; ++channel)
268 {
269 if (otherCurves->getCurveType(channel) == CURVE_SMOOTH)
270 {
271 //qCDebug(DIGIKAM_DIMG_LOG) << "Other is CURVE_SMOOTH";
272
273 setCurveType(channel, CURVE_SMOOTH);
274
275 //qCDebug(DIGIKAM_DIMG_LOG) << "Adopting points of channel " << channel;
276
277 for (int point = 0 ; point < NUM_POINTS ; ++point)
278 {
279 QPoint p = otherCurves->getCurvePoint(channel, point);
280
281 //qCDebug(DIGIKAM_DIMG_LOG) << "Point " << point << " in original is " << p;
282
283 if (d->isPointEnabled(p))
284 {
285 p.setX(p.x() / MULTIPLIER_16BIT);
286 p.setY(p.y() / MULTIPLIER_16BIT);
287 setCurvePoint(channel, point, p);
288
289 //qCDebug(DIGIKAM_DIMG_LOG) << "Setting curve point " << point << " to " << getCurvePoint(channel, point);
290 }
291 else
292 {
293 //qCDebug(DIGIKAM_DIMG_LOG) << "ignoring this point";
294 }
295 }
296 }
297 else
298 {
299 //qCDebug(DIGIKAM_DIMG_LOG) << "Other is CURVE_FREE";
300
301 setCurveType(channel, CURVE_FREE);
302
303 for (int i = 0 ; i <= d->segmentMax ; ++i)
304 {
305 setCurveValue(channel, i, otherCurves->getCurveValue(channel, i * MULTIPLIER_16BIT) / MULTIPLIER_16BIT);
306 }
307 }
308 }
309 }
310 else
311 {
312 qCDebug(DIGIKAM_DIMG_LOG) << "Bad logic error, could not fill one curve into another";
313 }
314
315 // invoke calculations once
316
317 curvesCalculateAllCurves();
318 }
319
isDirty() const320 bool ImageCurves::isDirty() const
321 {
322 return d->dirty;
323 }
324
isSixteenBits() const325 bool ImageCurves::isSixteenBits() const
326 {
327 return (d->segmentMax == MAX_SEGMENT_16BIT);
328 }
329
curvesReset()330 void ImageCurves::curvesReset()
331 {
332 memset(d->curves, 0, sizeof(struct Private::_Curves));
333 d->freeLutData();
334 d->lut->luts = nullptr;
335 d->lut->nchannels = 0;
336 d->dirty = false;
337
338 for (int channel = 0 ; channel < NUM_CHANNELS ; ++channel)
339 {
340 setCurveType(channel, CURVE_SMOOTH);
341 curvesChannelReset(channel);
342 }
343 }
344
curvesChannelReset(int channel)345 void ImageCurves::curvesChannelReset(int channel)
346 {
347 int j;
348
349 if (!d->curves)
350 {
351 return;
352 }
353
354 // Construct a linear curve.
355
356 for (j = 0 ; j <= d->segmentMax ; ++j)
357 {
358 d->curves->curve[channel][j] = j;
359 }
360
361 // Init coordinates points to null.
362
363 for (j = 0 ; j < NUM_POINTS ; ++j)
364 {
365 d->curves->points[channel][j][0] = -1;
366 d->curves->points[channel][j][1] = -1;
367 }
368
369 // First and last points init.
370
371 d->curves->points[channel][0][0] = 0;
372 d->curves->points[channel][0][1] = 0;
373 d->curves->points[channel][NUM_POINTS - 1][0] = d->segmentMax;
374 d->curves->points[channel][NUM_POINTS - 1][1] = d->segmentMax;
375 }
376
curvesCalculateCurve(int channel)377 void ImageCurves::curvesCalculateCurve(int channel)
378 {
379 int i;
380 int points[NUM_POINTS];
381 int num_pts;
382 int p1, p2, p3, p4;
383
384 if (!d->curves)
385 {
386 return;
387 }
388
389 switch (d->curves->curve_type[channel])
390 {
391 case CURVE_FREE:
392 {
393 break;
394 }
395
396 case CURVE_SMOOTH:
397 {
398 // Cycle through the curves
399
400 num_pts = 0;
401
402 for (i = 0 ; i < ImageCurves::NUM_POINTS ; ++i)
403 {
404 if (d->curves->points[channel][i][0] != -1)
405 {
406 points[num_pts++] = i;
407 }
408 }
409
410 // Initialize boundary curve points
411
412 if (num_pts != 0)
413 {
414 for (i = 0 ; i < d->curves->points[channel][points[0]][0] ; ++i)
415 {
416 d->curves->curve[channel][i] = d->curves->points[channel][points[0]][1];
417 }
418
419 for (i = d->curves->points[channel][points[num_pts - 1]][0] ; i <= d->segmentMax ; ++i)
420 {
421 d->curves->curve[channel][i] = d->curves->points[channel][points[num_pts - 1]][1];
422 }
423 }
424
425 for (i = 0 ; i < num_pts - 1 ; ++i)
426 {
427 p1 = (i == 0) ? points[i] : points[(i - 1)];
428 p2 = points[i];
429 p3 = points[(i + 1)];
430 p4 = (i == (num_pts - 2)) ? points[(num_pts - 1)] : points[(i + 2)];
431
432 curvesPlotCurve(channel, p1, p2, p3, p4);
433 }
434
435 // Ensure that the control points are used exactly
436 /*
437 for (i = 0 ; i < num_pts ; ++i)
438 {
439 int x, y;
440
441 x = d->curves->points[channel][points[i]][0];
442 y = d->curves->points[channel][points[i]][1];
443 d->curves->curve[channel][x] = y;
444 }
445 */
446 break;
447 }
448 }
449 }
450
curvesLutFunc(int n_channels,int channel,float value)451 float ImageCurves::curvesLutFunc(int n_channels, int channel, float value)
452 {
453 float f;
454 int index;
455 double inten;
456 int j;
457
458 if (!d->curves)
459 {
460 return 0.0;
461 }
462
463 if (n_channels == 1)
464 {
465 j = 0;
466 }
467 else
468 {
469 j = channel + 1;
470 }
471
472 inten = value;
473
474 // For color images this runs through the loop with j = channel +1
475 // the first time and j = 0 the second time.
476
477 // For bw images this runs through the loop with j = 0 the first and
478 // only time.
479
480 for ( ; j >= 0 ; j -= (channel + 1))
481 {
482 // Don't apply the overall curve to the alpha channel.
483
484 if ((j == 0) && ((n_channels == 2) || (n_channels == 4)) && (channel == n_channels - 1))
485 {
486 return inten;
487 }
488
489 if (inten < 0.0)
490 {
491 inten = d->curves->curve[j][0] / (float)d->segmentMax;
492 }
493 else if (inten >= 1.0)
494 {
495 inten = d->curves->curve[j][d->segmentMax] / (float)(d->segmentMax);
496 }
497 else // interpolate the curve.
498 {
499 index = (int)floor(inten * (float)(d->segmentMax));
500 f = inten * (float)(d->segmentMax) - index;
501 inten = ((1.0 - f) * d->curves->curve[j][index ] +
502 (f) * d->curves->curve[j][index + 1]) / (float)(d->segmentMax);
503 }
504 }
505
506 return inten;
507 }
508
curvesPlotCurve(int channel,int p1,int p2,int p3,int p4)509 void ImageCurves::curvesPlotCurve(int channel, int p1, int p2, int p3, int p4)
510 {
511 CRMatrix geometry;
512 CRMatrix tmp1, tmp2;
513 CRMatrix deltas;
514 double x, dx, dx2, dx3;
515 double y, dy, dy2, dy3;
516 double d1, d2, d3;
517 int lastx, lasty;
518 int newx, newy;
519 int i;
520 int loopdiv = d->segmentMax * 3;
521
522 if (!d->curves)
523 {
524 return;
525 }
526
527 // Construct the geometry matrix from the segment.
528
529 for (i = 0 ; i < 4 ; ++i)
530 {
531 geometry[i][2] = 0;
532 geometry[i][3] = 0;
533 }
534
535 for (i = 0 ; i < 2 ; ++i)
536 {
537 geometry[0][i] = d->curves->points[channel][p1][i];
538 geometry[1][i] = d->curves->points[channel][p2][i];
539 geometry[2][i] = d->curves->points[channel][p3][i];
540 geometry[3][i] = d->curves->points[channel][p4][i];
541 }
542
543 // Subdivide the curve 1000 times.
544 // n can be adjusted to give a finer or coarser curve.
545
546 d1 = 1.0 / loopdiv;
547 d2 = d1 * d1;
548 d3 = d1 * d1 * d1;
549
550 // Construct a temporary matrix for determining the forward differencing deltas.
551
552 tmp2[0][0] = 0;
553 tmp2[0][1] = 0;
554 tmp2[0][2] = 0;
555 tmp2[0][3] = 1;
556 tmp2[1][0] = d3;
557 tmp2[1][1] = d2;
558 tmp2[1][2] = d1;
559 tmp2[1][3] = 0;
560 tmp2[2][0] = 6 * d3;
561 tmp2[2][1] = 2 * d2;
562 tmp2[2][2] = 0;
563 tmp2[2][3] = 0;
564 tmp2[3][0] = 6 * d3;
565 tmp2[3][1] = 0;
566 tmp2[3][2] = 0;
567 tmp2[3][3] = 0;
568
569 // Compose the basis and geometry matrices.
570
571 curvesCRCompose(CR_basis, geometry, tmp1);
572
573 // Compose the above results to get the deltas matrix.
574
575 curvesCRCompose(tmp2, tmp1, deltas);
576
577 // Extract the x deltas.
578
579 x = deltas[0][0];
580 dx = deltas[1][0];
581 dx2 = deltas[2][0];
582 dx3 = deltas[3][0];
583
584 // Extract the y deltas.
585
586 y = deltas[0][1];
587 dy = deltas[1][1];
588 dy2 = deltas[2][1];
589 dy3 = deltas[3][1];
590
591 lastx = (int)CLAMP(x, 0.0, (double)d->segmentMax);
592 lasty = (int)CLAMP(y, 0.0, (double)d->segmentMax);
593
594 d->curves->curve[channel][lastx] = lasty;
595
596 // Loop over the curve.
597
598 for (i = 0 ; i < loopdiv ; ++i)
599 {
600 // Increment the x values.
601
602 x += dx;
603 dx += dx2;
604 dx2 += dx3;
605
606 // Increment the y values.
607
608 y += dy;
609 dy += dy2;
610 dy2 += dy3;
611
612 newx = CLAMP((int)lround(x), 0, d->segmentMax);
613 newy = CLAMP((int)lround(y), 0, d->segmentMax);
614
615 // If this point is different than the last one...then draw it.
616
617 if ((lastx != newx) || (lasty != newy))
618 {
619 d->curves->curve[channel][newx] = newy;
620 }
621
622 lastx = newx;
623 lasty = newy;
624 }
625 }
626
curvesCRCompose(CRMatrix a,CRMatrix b,CRMatrix ab)627 void ImageCurves::curvesCRCompose(CRMatrix a, CRMatrix b, CRMatrix ab)
628 {
629 int i, j;
630
631 for (i = 0 ; i < 4 ; ++i)
632 {
633 for (j = 0 ; j < 4 ; ++j)
634 {
635 ab[i][j] = (a[i][0] * b[0][j] +
636 a[i][1] * b[1][j] +
637 a[i][2] * b[2][j] +
638 a[i][3] * b[3][j]);
639 }
640 }
641 }
642
curvesLutSetup(int nchannels)643 void ImageCurves::curvesLutSetup(int nchannels)
644 {
645 int i;
646 uint v;
647 double val;
648
649 curvesCalculateAllCurves();
650
651 d->freeLutData();
652
653 d->lut->nchannels = nchannels;
654 d->lut->luts = new unsigned short*[d->lut->nchannels];
655
656 for (i = 0 ; i < d->lut->nchannels ; ++i)
657 {
658 d->lut->luts[i] = new unsigned short[d->segmentMax + 1];
659
660 for (v = 0 ; v <= (uint)d->segmentMax ; ++v)
661 {
662 // To add gamma correction use func(v ^ g) ^ 1/g instead.
663
664 val = (double)(d->segmentMax) * curvesLutFunc(d->lut->nchannels, i, v / (float)(d->segmentMax)) + 0.5;
665
666 d->lut->luts[i][v] = (unsigned short)CLAMP(val, 0.0, (double)d->segmentMax);
667 }
668 }
669 }
670
curvesLutProcess(uchar * const srcPR,uchar * const destPR,int w,int h)671 void ImageCurves::curvesLutProcess(uchar* const srcPR, uchar* const destPR, int w, int h)
672 {
673 unsigned short* lut0 = nullptr, *lut1 = nullptr, *lut2 = nullptr, *lut3 = nullptr;
674 int i;
675
676 if (d->lut->nchannels > 0)
677 {
678 lut0 = d->lut->luts[0];
679 }
680
681 if (d->lut->nchannels > 1)
682 {
683 lut1 = d->lut->luts[1];
684 }
685
686 if (d->lut->nchannels > 2)
687 {
688 lut2 = d->lut->luts[2];
689 }
690
691 if (d->lut->nchannels > 3)
692 {
693 lut3 = d->lut->luts[3];
694 }
695
696 if (!isSixteenBits()) // 8 bits image.
697 {
698 uchar red, green, blue, alpha;
699 uchar* ptr = srcPR;
700 uchar* dst = destPR;
701
702 for (i = 0 ; i < w * h ; ++i)
703 {
704 blue = ptr[0];
705 green = ptr[1];
706 red = ptr[2];
707 alpha = ptr[3];
708
709 if ((d->lut->nchannels > 0) && lut0)
710 {
711 red = lut0[red];
712 }
713
714 if ((d->lut->nchannels > 1) && lut1)
715 {
716 green = lut1[green];
717 }
718
719 if ((d->lut->nchannels > 2) && lut2)
720 {
721 blue = lut2[blue];
722 }
723
724 if ((d->lut->nchannels > 3) && lut3)
725 {
726 alpha = lut3[alpha];
727 }
728
729 dst[0] = blue;
730 dst[1] = green;
731 dst[2] = red;
732 dst[3] = alpha;
733
734 ptr += 4;
735 dst += 4;
736 }
737 }
738 else // 16 bits image.
739 {
740 unsigned short red, green, blue, alpha;
741 unsigned short* ptr = reinterpret_cast<unsigned short*>(srcPR);
742 unsigned short* dst = reinterpret_cast<unsigned short*>(destPR);
743
744 for (i = 0 ; i < w * h ; ++i)
745 {
746 blue = ptr[0];
747 green = ptr[1];
748 red = ptr[2];
749 alpha = ptr[3];
750
751 if ((d->lut->nchannels > 0) && lut0)
752 {
753 red = lut0[red];
754 }
755
756 if ((d->lut->nchannels > 1) && lut1)
757 {
758 green = lut1[green];
759 }
760
761 if ((d->lut->nchannels > 2) && lut2)
762 {
763 blue = lut2[blue];
764 }
765
766 if ((d->lut->nchannels > 3) && lut3)
767 {
768 alpha = lut3[alpha];
769 }
770
771 dst[0] = blue;
772 dst[1] = green;
773 dst[2] = red;
774 dst[3] = alpha;
775
776 ptr += 4;
777 dst += 4;
778 }
779 }
780 }
781
getDisabledValue()782 QPoint ImageCurves::getDisabledValue()
783 {
784 return QPoint(-1, -1);
785 }
786
isCurvePointEnabled(int channel,int point) const787 bool ImageCurves::isCurvePointEnabled(int channel, int point) const
788 {
789 if (d->curves &&
790 (channel >= 0) &&
791 (channel < NUM_CHANNELS) &&
792 (point >= 0) &&
793 (point < NUM_POINTS) &&
794 (d->curves->points[channel][point][0] >= 0) &&
795 (d->curves->points[channel][point][1] >= 0))
796 {
797 return true;
798 }
799 else
800 {
801 return false;
802 }
803 }
804
getCurvePoint(int channel,int point) const805 QPoint ImageCurves::getCurvePoint(int channel, int point) const
806 {
807 if (d->curves &&
808 (channel >= 0) &&
809 (channel < NUM_CHANNELS) &&
810 (point >= 0) &&
811 (point < NUM_POINTS))
812 {
813 return (QPoint(d->curves->points[channel][point][0],
814 d->curves->points[channel][point][1]));
815 }
816
817 return getDisabledValue();
818 }
819
getCurvePoints(int channel) const820 QPolygon ImageCurves::getCurvePoints(int channel) const
821 {
822 QPolygon array(NUM_POINTS);
823
824 if (d->curves && (channel >= 0) && (channel < NUM_CHANNELS))
825 {
826 for (int j = 0 ; j < NUM_POINTS ; ++j)
827 {
828 array.setPoint(j, getCurvePoint(channel, j));
829 }
830 }
831
832 return array;
833 }
834
getCurveValue(int channel,int bin) const835 int ImageCurves::getCurveValue(int channel, int bin) const
836 {
837 if (d->curves &&
838 (channel >= 0) &&
839 (channel < NUM_CHANNELS) &&
840 (bin >= 0) &&
841 (bin <= d->segmentMax))
842 {
843 return(d->curves->curve[channel][bin]);
844 }
845
846 return 0;
847 }
848
getCurveValues(int channel) const849 QPolygon ImageCurves::getCurveValues(int channel) const
850 {
851 QPolygon array(d->segmentMax + 1);
852
853 if (d->curves && (channel >= 0) && (channel < NUM_CHANNELS))
854 {
855 for (int j = 0 ; j <= d->segmentMax ; ++j)
856 {
857 array.setPoint(j, QPoint(j, getCurveValue(channel, j)));
858 }
859 }
860
861 return array;
862 }
863
getCurvePointX(int channel,int point) const864 int ImageCurves::getCurvePointX(int channel, int point) const
865 {
866 if (d->curves &&
867 (channel >= 0) &&
868 (channel < NUM_CHANNELS) &&
869 (point >= 0) &&
870 (point < NUM_POINTS))
871 {
872 return(d->curves->points[channel][point][0]);
873 }
874
875 return(-1);
876 }
877
getCurvePointY(int channel,int point) const878 int ImageCurves::getCurvePointY(int channel, int point) const
879 {
880 if (d->curves &&
881 (channel >= 0) &&
882 (channel < NUM_CHANNELS) &&
883 (point >= 0) &&
884 (point < NUM_POINTS))
885 {
886 return(d->curves->points[channel][point][1]);
887 }
888
889 return (-1);
890 }
891
getCurveType(int channel) const892 ImageCurves::CurveType ImageCurves::getCurveType(int channel) const
893 {
894 if (d->curves && (channel >= 0) && (channel < NUM_CHANNELS))
895 {
896 return ((ImageCurves::CurveType) d->curves->curve_type[channel]);
897 }
898
899 return CURVE_SMOOTH;
900 }
901
getContainer() const902 CurvesContainer ImageCurves::getContainer() const
903 {
904 CurveType type = CURVE_SMOOTH;
905
906 for (int i = 0 ; i < ColorChannels ; ++i)
907 {
908 if ((type = getCurveType(i)) == CURVE_FREE)
909 {
910 type = CURVE_FREE;
911 break;
912 }
913 }
914
915 CurvesContainer c(type, isSixteenBits());
916 c.initialize();
917
918 if (isLinear())
919 {
920 return c;
921 }
922
923 if (type == CURVE_FREE)
924 {
925 for (int i = 0 ; i < ColorChannels ; ++i)
926 {
927 c.values[i] = getCurveValues(i);
928 }
929 }
930 else
931 {
932 for (int i = 0 ; i < ColorChannels ; ++i)
933 {
934 c.values[i] = getCurvePoints(i);
935 }
936 }
937
938 return c;
939 }
940
getContainer(int channel) const941 CurvesContainer ImageCurves::getContainer(int channel) const
942 {
943 CurveType type = getCurveType(channel);
944 CurvesContainer c(type, isSixteenBits());
945
946 if (isLinear(channel))
947 {
948 return c;
949 }
950
951 if (type == CURVE_FREE)
952 {
953 c.values[channel] = getCurveValues(channel);
954 }
955 else
956 {
957 c.values[channel] = getCurvePoints(channel);
958 }
959
960 return c;
961 }
962
setContainer(const CurvesContainer & container)963 void ImageCurves::setContainer(const CurvesContainer& container)
964 {
965 if (container.curvesType == CURVE_FREE)
966 {
967 for (int i = 0 ; i < ColorChannels ; ++i)
968 {
969 setCurveType(i, CURVE_FREE);
970 setCurveValues(i, container.values[i]);
971 }
972 }
973 else
974 {
975 for (int i = 0 ; i < ColorChannels ; ++i)
976 {
977 setCurveType(i, CURVE_SMOOTH);
978 setCurvePoints(i, container.values[i]);
979 }
980 }
981 }
982
setCurveValue(int channel,int bin,int val)983 void ImageCurves::setCurveValue(int channel, int bin, int val)
984 {
985 if (d->curves &&
986 (channel >= 0) &&
987 (channel < NUM_CHANNELS) &&
988 (bin >= 0) &&
989 (bin <= d->segmentMax))
990 {
991 d->dirty = true;
992 d->curves->curve[channel][bin] = val;
993 }
994 }
995
setCurveValues(int channel,const QPolygon & vals)996 void ImageCurves::setCurveValues(int channel, const QPolygon& vals)
997 {
998 //qCDebug(DIGIKAM_DIMG_LOG) << "vals size: " << vals.size();
999 //qCDebug(DIGIKAM_DIMG_LOG) << "segmentMax: " << d->segmentMax + 1;
1000
1001 if (d->curves && (channel >= 0) && (channel < NUM_CHANNELS))
1002 {
1003 if (vals.isEmpty())
1004 {
1005 //qCDebug(DIGIKAM_DIMG_LOG) << "No curves values to assign: reset";
1006
1007 curvesChannelReset(channel);
1008 }
1009
1010 // Bits depth are different ?
1011
1012 else if (vals.size() != (d->segmentMax + 1))
1013 {
1014 int index;
1015
1016 if (vals.size() == 256)
1017 {
1018 //qCDebug(DIGIKAM_DIMG_LOG) << "8 to 16 bits curves transform";
1019
1020 // 8 to 16 bits.
1021
1022 ImageCurves curve8(false);
1023 ImageCurves curve16(true);
1024
1025 for (int i = 0 ; i <= 16 ; ++i)
1026 {
1027 index = CLAMP(i * 255 / 16, 0, 255);
1028 curve8.setCurvePoint(channel, i, QPoint(index, vals.point(index).y()));
1029 }
1030
1031 curve8.curvesCalculateCurve(channel);
1032 curve16.fillFromOtherCurves(&curve8);
1033
1034 for (int j = 0 ; j <= d->segmentMax ; ++j)
1035 {
1036 setCurveValue(channel, j, curve16.getCurveValue(channel, j));
1037 }
1038 }
1039 else
1040 {
1041 //qCDebug(DIGIKAM_DIMG_LOG) << "16 to 8 bits curves transform";
1042
1043 // 16 to 8 bits.
1044
1045 ImageCurves curve8(false);
1046 ImageCurves curve16(true);
1047
1048 for (int i = 0 ; i <= 16 ; ++i)
1049 {
1050 index = CLAMP(i * 65535 / 16, 0, 65535);
1051 curve16.setCurvePoint(channel, i, QPoint(index, vals.point(index).y()));
1052 }
1053
1054 curve16.curvesCalculateCurve(channel);
1055 curve8.fillFromOtherCurves(&curve16);
1056
1057 for (int j = 0 ; j <= d->segmentMax ; ++j)
1058 {
1059 setCurveValue(channel, j, curve8.getCurveValue(channel, j));
1060 }
1061 }
1062 }
1063 else
1064 {
1065 //qCDebug(DIGIKAM_DIMG_LOG) << "Assign curves values directly";
1066
1067 for (int j = 0 ; j <= d->segmentMax ; ++j)
1068 {
1069 setCurveValue(channel, j, vals.point(j).y());
1070 }
1071 }
1072 }
1073 }
1074
setCurvePoint(int channel,int point,const QPoint & val)1075 void ImageCurves::setCurvePoint(int channel, int point, const QPoint& val)
1076 {
1077 if (d->curves &&
1078 (channel >= 0) &&
1079 (channel < NUM_CHANNELS) &&
1080 (point >= 0) &&
1081 (point < NUM_POINTS) &&
1082 (val.x() >= -1) && (val.x() <= d->segmentMax) && // x can be equal to -1
1083 (val.y() >= 0) && (val.y() <= d->segmentMax)) // if the current point is disable !!!
1084 {
1085 d->dirty = true;
1086 d->curves->points[channel][point][0] = val.x();
1087 d->curves->points[channel][point][1] = val.y();
1088 }
1089 }
1090
unsetCurvePoint(int channel,int point)1091 void ImageCurves::unsetCurvePoint(int channel, int point)
1092 {
1093 if (d->curves &&
1094 (channel >= 0) &&
1095 (channel < NUM_CHANNELS) &&
1096 (point >= 0) &&
1097 (point < NUM_POINTS))
1098 {
1099 d->curves->points[channel][point][0] = -1;
1100 d->curves->points[channel][point][1] = -1;
1101 }
1102 }
1103
setCurvePoints(int channel,const QPolygon & vals)1104 void ImageCurves::setCurvePoints(int channel, const QPolygon& vals)
1105 {
1106 if (d->curves &&
1107 (channel >= 0) &&
1108 (channel < NUM_CHANNELS))
1109 {
1110 if (vals.isEmpty())
1111 {
1112 curvesChannelReset(channel);
1113 }
1114 else if (vals.size() >= NUM_POINTS)
1115 {
1116 for (int j = 0 ; j < NUM_POINTS ; ++j)
1117 {
1118 setCurvePoint(channel, j, vals.point(j));
1119 }
1120 }
1121 else
1122 {
1123 curvesChannelReset(channel);
1124
1125 if (vals.size() == 1)
1126 {
1127 setCurvePoint(channel, NUM_POINTS / 2, vals.first());
1128 }
1129 else
1130 {
1131 for (int j = 0 ; j < vals.size() - 1 ; ++j)
1132 {
1133 setCurvePoint(channel, j, vals.point(j));
1134 }
1135
1136 // set last to last
1137
1138 setCurvePoint(channel, NUM_POINTS - 1, vals.last());
1139 }
1140 }
1141 }
1142 else
1143 {
1144 qCDebug(DIGIKAM_DIMG_LOG) << "Curves points list not applied (nb pts " << vals.size() << " - Channel " << channel << ")";
1145 }
1146 }
1147
setCurvePointX(int channel,int point,int x)1148 void ImageCurves::setCurvePointX(int channel, int point, int x)
1149 {
1150 if (d->curves &&
1151 (channel >= 0) &&
1152 (channel < NUM_CHANNELS) &&
1153 (point >= 0) &&
1154 (point < NUM_POINTS) &&
1155 (x >= -1) &&
1156 (x <= d->segmentMax)) // x can be equal to -1 if the current point is disable !!!
1157 {
1158 d->dirty = true;
1159 d->curves->points[channel][point][0] = x;
1160 }
1161 }
1162
setCurvePointY(int channel,int point,int y)1163 void ImageCurves::setCurvePointY(int channel, int point, int y)
1164 {
1165 if (d->curves &&
1166 (channel >= 0) &&
1167 (channel < NUM_CHANNELS) &&
1168 (point >= 0) &&
1169 (point < NUM_POINTS) &&
1170 (y >= 0) &&
1171 (y <= d->segmentMax))
1172 {
1173 d->dirty = true;
1174 d->curves->points[channel][point][1] = y;
1175 }
1176 }
1177
setCurveType(int channel,CurveType type)1178 void ImageCurves::setCurveType(int channel, CurveType type)
1179 {
1180 if (d->curves &&
1181 (channel >= 0) &&
1182 (channel < NUM_CHANNELS) &&
1183 (type >= CURVE_SMOOTH) &&
1184 (type <= CURVE_FREE))
1185 {
1186 d->curves->curve_type[channel] = type;
1187 }
1188 }
1189
setCurveType(ImageCurves::CurveType type)1190 void ImageCurves::setCurveType(ImageCurves::CurveType type)
1191 {
1192 for (int channel = 0 ; channel < NUM_CHANNELS ; ++channel)
1193 {
1194 setCurveType(channel, type);
1195 }
1196 }
1197
loadCurvesFromGimpCurvesFile(const QUrl & fileUrl)1198 bool ImageCurves::loadCurvesFromGimpCurvesFile(const QUrl& fileUrl)
1199 {
1200 // TODO : port to QFile
1201
1202 FILE* file = nullptr;
1203 int i, j;
1204 int fields;
1205 char buf[50];
1206 int index[NUM_CHANNELS][NUM_POINTS];
1207 int value[NUM_CHANNELS][NUM_POINTS];
1208
1209 #ifdef Q_OS_WIN
1210
1211 file = _wfopen((const wchar_t*)fileUrl.toLocalFile().utf16(), L"r");
1212
1213 #else
1214
1215 file = fopen(fileUrl.toLocalFile().toUtf8().constData(), "r");
1216
1217 #endif
1218
1219 if (!file)
1220 {
1221 return false;
1222 }
1223
1224 if (! fgets(buf, sizeof(buf), file))
1225 {
1226 fclose(file);
1227 return false;
1228 }
1229
1230 if (strcmp(buf, "# GIMP Curves File\n") != 0)
1231 {
1232 fclose(file);
1233 return false;
1234 }
1235
1236 for (i = 0 ; i < NUM_CHANNELS ; ++i)
1237 {
1238 for (j = 0 ; j < NUM_POINTS ; ++j)
1239 {
1240 // FIXME: scanf without field width limits can crash with huge input data
1241
1242 fields = fscanf(file, "%d %d ", &index[i][j], &value[i][j]);
1243
1244 if (fields != 2)
1245 {
1246 qCWarning(DIGIKAM_DIMG_LOG) << "Invalid Gimp curves file!";
1247 fclose(file);
1248 return false;
1249 }
1250 }
1251 }
1252
1253 curvesReset();
1254
1255 for (i = 0 ; i < NUM_CHANNELS ; ++i)
1256 {
1257 d->curves->curve_type[i] = CURVE_SMOOTH;
1258
1259 for (j = 0 ; j < NUM_POINTS ; ++j)
1260 {
1261 d->curves->points[i][j][0] = (isSixteenBits() && (index[i][j] != -1) ?
1262 index[i][j] * MULTIPLIER_16BIT : index[i][j]);
1263 d->curves->points[i][j][1] = (isSixteenBits() && (value[i][j] != -1) ?
1264 value[i][j] * MULTIPLIER_16BIT : value[i][j]);
1265 }
1266 }
1267
1268 curvesCalculateAllCurves();
1269 fclose(file);
1270
1271 return true;
1272 }
1273
curvesCalculateAllCurves()1274 void ImageCurves::curvesCalculateAllCurves()
1275 {
1276 for (int i = 0 ; i < NUM_CHANNELS ; ++i)
1277 {
1278 curvesCalculateCurve(i);
1279 }
1280 }
1281
saveCurvesToGimpCurvesFile(const QUrl & fileUrl) const1282 bool ImageCurves::saveCurvesToGimpCurvesFile(const QUrl& fileUrl) const
1283 {
1284 // TODO : port to QFile
1285
1286 FILE* file = nullptr;
1287 int i, j;
1288 int index;
1289
1290 #ifdef Q_OS_WIN
1291
1292 file = _wfopen((const wchar_t*)fileUrl.toLocalFile().utf16(), L"w");
1293
1294 #else
1295
1296 file = fopen(fileUrl.toLocalFile().toUtf8().constData(), "w");
1297
1298 #endif
1299
1300 if (!file)
1301 {
1302 return false;
1303 }
1304
1305 for (i = 0 ; i < NUM_CHANNELS ; ++i)
1306 {
1307 if (d->curves->curve_type[i] == CURVE_FREE)
1308 {
1309 // Pick representative points from the curve and make them control points.
1310
1311 for (j = 0 ; j <= 8 ; ++j)
1312 {
1313 index = CLAMP(j * 32, 0, d->segmentMax);
1314 d->curves->points[i][j * 2][0] = index;
1315 d->curves->points[i][j * 2][1] = d->curves->curve[i][index];
1316 }
1317 }
1318 }
1319
1320 fprintf(file, "# GIMP Curves File\n");
1321
1322 for (i = 0 ; i < NUM_CHANNELS ; ++i)
1323 {
1324 for (j = 0 ; j < NUM_POINTS ; ++j)
1325 {
1326 fprintf(file, "%d %d ",
1327 (isSixteenBits() && (d->curves->points[i][j][0] != -1) ?
1328 d->curves->points[i][j][0] / MULTIPLIER_16BIT : d->curves->points[i][j][0]),
1329 (isSixteenBits() && (d->curves->points[i][j][1] != -1) ?
1330 d->curves->points[i][j][1] / MULTIPLIER_16BIT : d->curves->points[i][j][1]));
1331
1332 fprintf(file, "\n");
1333 }
1334 }
1335
1336 fflush(file);
1337 fclose(file);
1338
1339 return true;
1340 }
1341
isLinear() const1342 bool ImageCurves::isLinear() const
1343 {
1344 for (int i = 0 ; i < NUM_CHANNELS ; ++i)
1345 {
1346 if (!isLinear(i))
1347 {
1348 return false;
1349 }
1350 }
1351
1352 return true;
1353 }
1354
isLinear(int channel) const1355 bool ImageCurves::isLinear(int channel) const
1356 {
1357 if (!d->curves || (channel < 0) || (channel >= NUM_CHANNELS))
1358 {
1359 return false;
1360 }
1361
1362 if (d->curves->curve_type[channel] == CURVE_FREE)
1363 {
1364 for (int j = 0 ; j < d->segmentMax ; ++j)
1365 {
1366 if (d->curves->curve[channel][j] != j)
1367 {
1368 return false;
1369 }
1370 }
1371
1372 return true;
1373 }
1374 else
1375 {
1376 bool hasFirst = false;
1377 bool hasLast = false;
1378
1379 // find out number of valid points
1380
1381 for (int j = 0 ; j < NUM_POINTS ; ++j)
1382 {
1383 int x = d->curves->points[channel][j][0];
1384 int y = d->curves->points[channel][j][1];
1385
1386 // we allow one first point (0,0), one second and last point(max,max), and the rest must be disabled
1387
1388 if ((x > -1) && (y > -1))
1389 {
1390 if (!hasFirst && !hasLast && (x == 0) && (y == 0))
1391 {
1392 hasFirst = true;
1393 }
1394 else if (hasFirst && !hasLast && (x == d->segmentMax) && (y == d->segmentMax))
1395 {
1396 hasLast = true;
1397 }
1398 else
1399 {
1400 return false;
1401 }
1402 }
1403 }
1404
1405 return true;
1406 }
1407 }
1408
1409 /**
1410 * Binary format:
1411 *
1412 * Version 1 :16
1413 * Type 0,1,2 : 8
1414 * Bytes depth 1,2 : 8
1415 * reserved :32
1416 * count :32
1417 *
1418 * Type 0 (linear curve):
1419 * Type 1 (smooth curve):
1420 * for (0...count)
1421 * point.x :32
1422 * point.y :32
1423 * Type 2 (free curve):
1424 * for (0...count)
1425 * if (Bytes depth == 1)
1426 * value : 8
1427 * else if (Bytes depth == 2)
1428 * value :16
1429 *
1430 * In Big Endian byte order.
1431 * Data then converted to base64.
1432 */
1433
channelToBinary(int channel) const1434 QByteArray ImageCurves::channelToBinary(int channel) const
1435 {
1436 if (!d->curves ||
1437 (channel < 0) ||
1438 (channel >= NUM_CHANNELS))
1439 {
1440 return QByteArray();
1441 }
1442
1443 QByteArray data;
1444 QDataStream s(&data, QIODevice::WriteOnly);
1445
1446 quint8 type;
1447
1448 if (isLinear(channel))
1449 {
1450 type = 0;
1451 }
1452 else if (d->curves->curve_type[channel] == CURVE_SMOOTH)
1453 {
1454 type = 1;
1455 }
1456 else //if (d->curves->curve_type[channel] == CURVE_FREE)
1457 {
1458 type = 2;
1459 }
1460
1461 s << (quint16)1; // version
1462 s << (quint8)type; // type
1463 s << (quint8)(isSixteenBits() ? 2 : 1); // bytes depth
1464 s << (quint32)0; // reserved
1465
1466 if (type == 0)
1467 {
1468 // write only a zero count for linear curve
1469
1470 s << (quint32)0;
1471 }
1472 else if (type == 1)
1473 {
1474 quint32 count = 0;
1475
1476 // find out number of valid points
1477
1478 for (int j = 0 ; j < NUM_POINTS ; ++j)
1479 {
1480 if (d->curves->points[channel][j][0] > -1 && d->curves->points[channel][j][1] > -1)
1481 {
1482 count++;
1483 }
1484 }
1485
1486 s << (quint32)count; // number of stored points,
1487
1488 for (int j = 0 ; j < NUM_POINTS ; ++j)
1489 {
1490 if (d->curves->points[channel][j][0] > -1 && d->curves->points[channel][j][1] > -1)
1491 {
1492 s << (qint32)d->curves->points[channel][j][0];
1493 s << (qint32)d->curves->points[channel][j][1];
1494 }
1495 }
1496 }
1497 else if (type == 2)
1498 {
1499 s << (quint32)d->segmentMax; // number of stored segments
1500
1501 if (isSixteenBits())
1502 {
1503 for (int j = 0 ; j < d->segmentMax ; ++j)
1504 {
1505 s << (quint16)d->curves->curve[channel][j];
1506 }
1507 }
1508 else
1509 {
1510 for (int j = 0 ; j < d->segmentMax ; ++j)
1511 {
1512 s << (quint8)d->curves->curve[channel][j];
1513 }
1514 }
1515 }
1516
1517 return data;
1518 }
1519
setChannelFromBinary(int channel,const QByteArray & data)1520 bool ImageCurves::setChannelFromBinary(int channel, const QByteArray& data)
1521 {
1522 if (!d->curves || (channel < 0) || (channel >= NUM_CHANNELS))
1523 {
1524 return false;
1525 }
1526
1527 if (data.isEmpty())
1528 {
1529 curvesChannelReset(channel);
1530 return false;
1531 }
1532
1533 QDataStream s(data);
1534
1535 quint32 nothing, count;
1536 quint16 version;
1537 quint8 type, depth;
1538
1539 s >> version;
1540
1541 if (version != 1)
1542 {
1543 return false;
1544 }
1545
1546 s >> type;
1547
1548 if (type > 2)
1549 {
1550 return false;
1551 }
1552
1553 s >> depth;
1554
1555 if (
1556 ((depth == 1) && isSixteenBits()) ||
1557 ((depth == 2) && !isSixteenBits()) ||
1558 (depth == 0) ||
1559 (depth > 2)
1560 )
1561 {
1562 return false;
1563 }
1564
1565 s >> nothing;
1566 s >> count;
1567
1568 if (type == 0)
1569 {
1570 // linear
1571
1572 setCurveType(channel, CURVE_SMOOTH);
1573 curvesChannelReset(channel);
1574 }
1575 else if (type == 1)
1576 {
1577 setCurveType(channel, CURVE_SMOOTH);
1578
1579 uint usedCount = qMin((uint)NUM_POINTS, count);
1580 QPolygon p(usedCount);
1581 quint32 x, y;
1582
1583 for (uint j = 0 ; j < usedCount ; ++j)
1584 {
1585 s >> x;
1586 s >> y;
1587 p.setPoint(j, x, y);
1588 }
1589
1590 setCurvePoints(channel, p);
1591 }
1592 else // (type == 2)
1593 {
1594 if ((count != (uint)d->segmentMax) || s.atEnd())
1595 {
1596 return false;
1597 }
1598
1599 setCurveType(channel, CURVE_FREE);
1600
1601 if (isSixteenBits())
1602 {
1603 quint16 data16;
1604
1605 for (int j = 0 ; j < d->segmentMax ; ++j)
1606 {
1607 s >> data16;
1608 d->curves->curve[channel][j] = data16;
1609 }
1610 }
1611 else
1612 {
1613 quint8 data8;
1614
1615 for (int j = 0 ; j < d->segmentMax ; ++j)
1616 {
1617 s >> data8;
1618 d->curves->curve[channel][j] = data8;
1619 }
1620 }
1621
1622 }
1623
1624 return true;
1625 }
1626
1627 } // namespace Digikam
1628