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