chalk

kis_gradient.cpp
1/*
2 * kis_gradient.cpp - part of Krayon
3 *
4 * Copyright (c) 2000 Matthias Elter <elter@kde.org>
5 * 2001 John Califf
6 * 2004 Boudewijn Rempt <boud@valdyas.org>
7 * 2004 Adrian Page <adrian@pagenet.plus.com>
8 * 2004 Sven Langkamp <longamp@reallygood.de>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 */
24
25#include <cfloat>
26#include <cmath>
27
28#include <tqimage.h>
29#include <tqtextstream.h>
30#include <tqfile.h>
31
32#include <koColor.h>
33#include <kogradientmanager.h>
34
35#include <kdebug.h>
36#include <tdelocale.h>
37
38#include "kis_gradient.h"
39
40#define PREVIEW_WIDTH 64
41#define PREVIEW_HEIGHT 64
42
43KisGradientSegment::RGBColorInterpolationStrategy *KisGradientSegment::RGBColorInterpolationStrategy::m_instance = 0;
44KisGradientSegment::HSVCWColorInterpolationStrategy *KisGradientSegment::HSVCWColorInterpolationStrategy::m_instance = 0;
45KisGradientSegment::HSVCCWColorInterpolationStrategy *KisGradientSegment::HSVCCWColorInterpolationStrategy::m_instance = 0;
46
47KisGradientSegment::LinearInterpolationStrategy *KisGradientSegment::LinearInterpolationStrategy::m_instance = 0;
48KisGradientSegment::CurvedInterpolationStrategy *KisGradientSegment::CurvedInterpolationStrategy::m_instance = 0;
49KisGradientSegment::SineInterpolationStrategy *KisGradientSegment::SineInterpolationStrategy::m_instance = 0;
50KisGradientSegment::SphereIncreasingInterpolationStrategy *KisGradientSegment::SphereIncreasingInterpolationStrategy::m_instance = 0;
51KisGradientSegment::SphereDecreasingInterpolationStrategy *KisGradientSegment::SphereDecreasingInterpolationStrategy::m_instance = 0;
52
53KisGradient::KisGradient(const TQString& file) : super(file)
54{
55}
56
57KisGradient::~KisGradient()
58{
59 for (uint i = 0; i < m_segments.count(); i++) {
60 delete m_segments[i];
61 m_segments[i] = 0;
62 }
63}
64
65bool KisGradient::load()
66{
67 return init();
68}
69
70bool KisGradient::save()
71{
72 return false;
73}
74
75TQImage KisGradient::img()
76{
77 return m_img;
78}
79
80bool KisGradient::init()
81{
82 KoGradientManager gradLoader;
83 KoGradient* grad = gradLoader.loadGradient(filename());
84
85 if( !grad )
86 return false;
87
88 m_segments.clear();
89
90 if( grad->colorStops.count() > 1 ) {
91 KoColorStop *colstop;
92 for(colstop = grad->colorStops.first(); colstop; colstop = grad->colorStops.next()) {
93 KoColorStop *colstopNext = grad->colorStops.next();
94
95 if(colstopNext) {
96 KoColor leftRgb((int)(colstop->color1 * 255 + 0.5), (int)(colstop->color2 * 255 + 0.5), (int)(colstop->color3 * 255 + 0.5));
97 KoColor rightRgb((int)(colstopNext->color1 * 255 + 0.5), (int)(colstopNext->color2 * 255 + 0.5), (int)(colstopNext->color3 * 255 + 0.5));
98
99 double midp = colstop->midpoint;
100 midp = colstop->offset + ((colstopNext->offset - colstop->offset) * midp);
101
102 Color leftColor(leftRgb.color(), colstop->opacity);
103 Color rightColor(rightRgb.color(), colstopNext->opacity);
104
105 KisGradientSegment *segment = new KisGradientSegment(colstop->interpolation, colstop->colorType, colstop->offset, midp, colstopNext->offset, leftColor, rightColor);
106 TQ_CHECK_PTR(segment);
107
108 if ( !segment->isValid() ) {
109 delete segment;
110 return false;
111 }
112
113 m_segments.push_back(segment);
114 grad->colorStops.prev();
115 }
116 else {
117 grad->colorStops.prev();
118 break;
119 }
120 }
121 }
122 else
123 return false;
124
125 if (!m_segments.isEmpty()) {
126 m_img = generatePreview(PREVIEW_WIDTH, PREVIEW_HEIGHT);
127 setValid(true);
128 return true;
129 }
130 else {
131 return false;
132 }
133}
134
135void KisGradient::setImage(const TQImage& img)
136{
137 m_img = img;
138 m_img.detach();
139
140 setValid(true);
141}
142
143KisGradientSegment *KisGradient::segmentAt(double t) const
144{
145 Q_ASSERT(t >= 0 || t <= 1);
146 Q_ASSERT(!m_segments.empty());
147
148 for(TQValueVector<KisGradientSegment *>::const_iterator it = m_segments.begin(); it!= m_segments.end(); ++it)
149 {
150 if (t > (*it)->startOffset() - DBL_EPSILON && t < (*it)->endOffset() + DBL_EPSILON) {
151 return *it;
152 }
153 }
154
155 return 0;
156}
157
158void KisGradient::colorAt(double t, TQColor *color, TQ_UINT8 *opacity) const
159{
160 const KisGradientSegment *segment = segmentAt(t);
161 Q_ASSERT(segment != 0);
162
163 if (segment) {
164 Color col = segment->colorAt(t);
165 *color = col.color();
166 *opacity = static_cast<TQ_UINT8>(col.alpha() * OPACITY_OPAQUE + 0.5);
167 }
168}
169
170TQImage KisGradient::generatePreview(int width, int height) const
171{
172 TQImage img(width, height, 32);
173
174 for (int y = 0; y < img.height(); y++) {
175 for (int x = 0; x < img.width(); x++) {
176
177 int backgroundRed = 128 + 63 * ((x / 4 + y / 4) % 2);
178 int backgroundGreen = backgroundRed;
179 int backgroundBlue = backgroundRed;
180
181 TQColor color;
182 TQ_UINT8 opacity;
183 double t = static_cast<double>(x) / (img.width() - 1);
184
185 colorAt(t, &color, &opacity);
186
187 double alpha = static_cast<double>(opacity) / OPACITY_OPAQUE;
188
189 int red = static_cast<int>((1 - alpha) * backgroundRed + alpha * color.red() + 0.5);
190 int green = static_cast<int>((1 - alpha) * backgroundGreen + alpha * color.green() + 0.5);
191 int blue = static_cast<int>((1 - alpha) * backgroundBlue + alpha * color.blue() + 0.5);
192
193 img.setPixel(x, y, tqRgb(red, green, blue));
194 }
195 }
196
197 return img;
198}
199
200KisGradientSegment::KisGradientSegment(int interpolationType, int colorInterpolationType, double startOffset, double middleOffset, double endOffset, const Color& startColor, const Color& endColor)
201{
202 m_interpolator = 0;
203
204 switch (interpolationType) {
205 case INTERP_LINEAR:
206 m_interpolator = LinearInterpolationStrategy::instance();
207 break;
208 case INTERP_CURVED:
209 m_interpolator = CurvedInterpolationStrategy::instance();
210 break;
211 case INTERP_SINE:
212 m_interpolator = SineInterpolationStrategy::instance();
213 break;
214 case INTERP_SPHERE_INCREASING:
215 m_interpolator = SphereIncreasingInterpolationStrategy::instance();
216 break;
217 case INTERP_SPHERE_DECREASING:
218 m_interpolator = SphereDecreasingInterpolationStrategy::instance();
219 break;
220 }
221
222 m_colorInterpolator = 0;
223
224 switch (colorInterpolationType) {
225 case COLOR_INTERP_RGB:
226 m_colorInterpolator = RGBColorInterpolationStrategy::instance();
227 break;
228 case COLOR_INTERP_HSV_CCW:
229 m_colorInterpolator = HSVCCWColorInterpolationStrategy::instance();
230 break;
231 case COLOR_INTERP_HSV_CW:
232 m_colorInterpolator = HSVCWColorInterpolationStrategy::instance();
233 break;
234 }
235
236 if (startOffset < DBL_EPSILON) {
237 m_startOffset = 0;
238 }
239 else
240 if (startOffset > 1 - DBL_EPSILON) {
241 m_startOffset = 1;
242 }
243 else {
244 m_startOffset = startOffset;
245 }
246
247 if (middleOffset < m_startOffset + DBL_EPSILON) {
248 m_middleOffset = m_startOffset;
249 }
250 else
251 if (middleOffset > 1 - DBL_EPSILON) {
252 m_middleOffset = 1;
253 }
254 else {
255 m_middleOffset = middleOffset;
256 }
257
258 if (endOffset < m_middleOffset + DBL_EPSILON) {
259 m_endOffset = m_middleOffset;
260 }
261 else
262 if (endOffset > 1 - DBL_EPSILON) {
263 m_endOffset = 1;
264 }
265 else {
266 m_endOffset = endOffset;
267 }
268
269 m_length = m_endOffset - m_startOffset;
270
271 if (m_length < DBL_EPSILON) {
272 m_middleT = 0.5;
273 }
274 else {
275 m_middleT = (m_middleOffset - m_startOffset) / m_length;
276 }
277
278 m_startColor = startColor;
279 m_endColor = endColor;
280}
281
282const Color& KisGradientSegment::startColor() const
283{
284 return m_startColor;
285}
286
287const Color& KisGradientSegment::endColor() const
288{
289 return m_endColor;
290}
291
292double KisGradientSegment::startOffset() const
293{
294 return m_startOffset;
295}
296
297double KisGradientSegment::middleOffset() const
298{
299 return m_middleOffset;
300}
301
302double KisGradientSegment::endOffset() const
303{
304 return m_endOffset;
305}
306
307void KisGradientSegment::setStartOffset(double t)
308{
309 m_startOffset = t;
310 m_length = m_endOffset - m_startOffset;
311
312 if (m_length < DBL_EPSILON) {
313 m_middleT = 0.5;
314 }
315 else {
316 m_middleT = (m_middleOffset - m_startOffset) / m_length;
317 }
318}
319void KisGradientSegment::setMiddleOffset(double t)
320{
321 m_middleOffset = t;
322
323 if (m_length < DBL_EPSILON) {
324 m_middleT = 0.5;
325 }
326 else {
327 m_middleT = (m_middleOffset - m_startOffset) / m_length;
328 }
329}
330
331void KisGradientSegment::setEndOffset(double t)
332{
333 m_endOffset = t;
334 m_length = m_endOffset - m_startOffset;
335
336 if (m_length < DBL_EPSILON) {
337 m_middleT = 0.5;
338 }
339 else {
340 m_middleT = (m_middleOffset - m_startOffset) / m_length;
341 }
342}
343
344int KisGradientSegment::interpolation() const
345{
346 return m_interpolator->type();
347}
348
349void KisGradientSegment::setInterpolation(int interpolationType)
350{
351 switch (interpolationType) {
352 case INTERP_LINEAR:
353 m_interpolator = LinearInterpolationStrategy::instance();
354 break;
355 case INTERP_CURVED:
356 m_interpolator = CurvedInterpolationStrategy::instance();
357 break;
358 case INTERP_SINE:
359 m_interpolator = SineInterpolationStrategy::instance();
360 break;
361 case INTERP_SPHERE_INCREASING:
362 m_interpolator = SphereIncreasingInterpolationStrategy::instance();
363 break;
364 case INTERP_SPHERE_DECREASING:
365 m_interpolator = SphereDecreasingInterpolationStrategy::instance();
366 break;
367 }
368}
369
370int KisGradientSegment::colorInterpolation() const
371{
372 return m_colorInterpolator->type();
373}
374
375void KisGradientSegment::setColorInterpolation(int colorInterpolationType)
376{
377 switch (colorInterpolationType) {
378 case COLOR_INTERP_RGB:
379 m_colorInterpolator = RGBColorInterpolationStrategy::instance();
380 break;
381 case COLOR_INTERP_HSV_CCW:
382 m_colorInterpolator = HSVCCWColorInterpolationStrategy::instance();
383 break;
384 case COLOR_INTERP_HSV_CW:
385 m_colorInterpolator = HSVCWColorInterpolationStrategy::instance();
386 break;
387 }
388}
389
390Color KisGradientSegment::colorAt(double t) const
391{
392 Q_ASSERT(t > m_startOffset - DBL_EPSILON && t < m_endOffset + DBL_EPSILON);
393
394 double segmentT;
395
396 if (m_length < DBL_EPSILON) {
397 segmentT = 0.5;
398 }
399 else {
400 segmentT = (t - m_startOffset) / m_length;
401 }
402
403 double colorT = m_interpolator->valueAt(segmentT, m_middleT);
404
405 Color color = m_colorInterpolator->colorAt(colorT, m_startColor, m_endColor);
406
407 return color;
408}
409
410bool KisGradientSegment::isValid() const
411{
412 if (m_interpolator == 0 || m_colorInterpolator ==0)
413 return false;
414 return true;
415}
416
417KisGradientSegment::RGBColorInterpolationStrategy *KisGradientSegment::RGBColorInterpolationStrategy::instance()
418{
419 if (m_instance == 0) {
420 m_instance = new RGBColorInterpolationStrategy();
421 TQ_CHECK_PTR(m_instance);
422 }
423
424 return m_instance;
425}
426
427Color KisGradientSegment::RGBColorInterpolationStrategy::colorAt(double t, Color start, Color end) const
428{
429 int startRed = start.color().red();
430 int startGreen = start.color().green();
431 int startBlue = start.color().blue();
432 double startAlpha = start.alpha();
433 int red = static_cast<int>(startRed + t * (end.color().red() - startRed) + 0.5);
434 int green = static_cast<int>(startGreen + t * (end.color().green() - startGreen) + 0.5);
435 int blue = static_cast<int>(startBlue + t * (end.color().blue() - startBlue) + 0.5);
436 double alpha = startAlpha + t * (end.alpha() - startAlpha);
437
438 return Color(TQColor(red, green, blue), alpha);
439}
440
441KisGradientSegment::HSVCWColorInterpolationStrategy *KisGradientSegment::HSVCWColorInterpolationStrategy::instance()
442{
443 if (m_instance == 0) {
444 m_instance = new HSVCWColorInterpolationStrategy();
445 TQ_CHECK_PTR(m_instance);
446 }
447
448 return m_instance;
449}
450
451Color KisGradientSegment::HSVCWColorInterpolationStrategy::colorAt(double t, Color start, Color end) const
452{
453 KoColor sc = KoColor(start.color());
454 KoColor ec = KoColor(end.color());
455
456 int s = static_cast<int>(sc.S() + t * (ec.S() - sc.S()) + 0.5);
457 int v = static_cast<int>(sc.V() + t * (ec.V() - sc.V()) + 0.5);
458 int h;
459
460 if (ec.H() < sc.H()) {
461 h = static_cast<int>(ec.H() + (1 - t) * (sc.H() - ec.H()) + 0.5);
462 }
463 else {
464 h = static_cast<int>(ec.H() + (1 - t) * (360 - ec.H() + sc.H()) + 0.5);
465
466 if (h > 359) {
467 h -= 360;
468 }
469 }
470
471 double alpha = start.alpha() + t * (end.alpha() - start.alpha());
472
473 return Color(KoColor(h, s, v, KoColor::csHSV).color(), alpha);
474}
475
476KisGradientSegment::HSVCCWColorInterpolationStrategy *KisGradientSegment::HSVCCWColorInterpolationStrategy::instance()
477{
478 if (m_instance == 0) {
479 m_instance = new HSVCCWColorInterpolationStrategy();
480 TQ_CHECK_PTR(m_instance);
481 }
482
483 return m_instance;
484}
485
486Color KisGradientSegment::HSVCCWColorInterpolationStrategy::colorAt(double t, Color start, Color end) const
487{
488 KoColor sc = KoColor(start.color());
489 KoColor se = KoColor(end.color());
490
491 int s = static_cast<int>(sc.S() + t * (se.S() - sc.S()) + 0.5);
492 int v = static_cast<int>(sc.V() + t * (se.V() - sc.V()) + 0.5);
493 int h;
494
495 if (sc.H() < se.H()) {
496 h = static_cast<int>(sc.H() + t * (se.H() - sc.H()) + 0.5);
497 }
498 else {
499 h = static_cast<int>(sc.H() + t * (360 - sc.H() + se.H()) + 0.5);
500
501 if (h > 359) {
502 h -= 360;
503 }
504 }
505
506 double alpha = start.alpha() + t * (end.alpha() - start.alpha());
507
508 return Color(KoColor(h, s, v, KoColor::csHSV).color(), alpha);
509}
510
511KisGradientSegment::LinearInterpolationStrategy *KisGradientSegment::LinearInterpolationStrategy::instance()
512{
513 if (m_instance == 0) {
514 m_instance = new LinearInterpolationStrategy();
515 TQ_CHECK_PTR(m_instance);
516 }
517
518 return m_instance;
519}
520
521double KisGradientSegment::LinearInterpolationStrategy::calcValueAt(double t, double middle)
522{
523 Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON);
524 Q_ASSERT(middle > -DBL_EPSILON && middle < 1 + DBL_EPSILON);
525
526 double value = 0;
527
528 if (t <= middle) {
529 if (middle < DBL_EPSILON) {
530 value = 0;
531 }
532 else {
533 value = (t / middle) * 0.5;
534 }
535 }
536 else {
537 if (middle > 1 - DBL_EPSILON) {
538 value = 1;
539 }
540 else {
541 value = ((t - middle) / (1 - middle)) * 0.5 + 0.5;
542 }
543 }
544
545 return value;
546}
547
548double KisGradientSegment::LinearInterpolationStrategy::valueAt(double t, double middle) const
549{
550 return calcValueAt(t, middle);
551}
552
553KisGradientSegment::CurvedInterpolationStrategy::CurvedInterpolationStrategy()
554{
555 m_logHalf = log(0.5);
556}
557
558KisGradientSegment::CurvedInterpolationStrategy *KisGradientSegment::CurvedInterpolationStrategy::instance()
559{
560 if (m_instance == 0) {
561 m_instance = new CurvedInterpolationStrategy();
562 TQ_CHECK_PTR(m_instance);
563 }
564
565 return m_instance;
566}
567
568double KisGradientSegment::CurvedInterpolationStrategy::valueAt(double t, double middle) const
569{
570 Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON);
571 Q_ASSERT(middle > -DBL_EPSILON && middle < 1 + DBL_EPSILON);
572
573 double value = 0;
574
575 if (middle < DBL_EPSILON) {
576 middle = DBL_EPSILON;
577 }
578
579 value = pow(t, m_logHalf / log(middle));
580
581 return value;
582}
583
584KisGradientSegment::SineInterpolationStrategy *KisGradientSegment::SineInterpolationStrategy::instance()
585{
586 if (m_instance == 0) {
587 m_instance = new SineInterpolationStrategy();
588 TQ_CHECK_PTR(m_instance);
589 }
590
591 return m_instance;
592}
593
594double KisGradientSegment::SineInterpolationStrategy::valueAt(double t, double middle) const
595{
596 double lt = LinearInterpolationStrategy::calcValueAt(t, middle);
597 double value = (sin(-M_PI_2 + M_PI * lt) + 1.0) / 2.0;
598
599 return value;
600}
601
602KisGradientSegment::SphereIncreasingInterpolationStrategy *KisGradientSegment::SphereIncreasingInterpolationStrategy::instance()
603{
604 if (m_instance == 0) {
605 m_instance = new SphereIncreasingInterpolationStrategy();
606 TQ_CHECK_PTR(m_instance);
607 }
608
609 return m_instance;
610}
611
612double KisGradientSegment::SphereIncreasingInterpolationStrategy::valueAt(double t, double middle) const
613{
614 double lt = LinearInterpolationStrategy::calcValueAt(t, middle) - 1;
615 double value = sqrt(1 - lt * lt);
616
617 return value;
618}
619
620KisGradientSegment::SphereDecreasingInterpolationStrategy *KisGradientSegment::SphereDecreasingInterpolationStrategy::instance()
621{
622 if (m_instance == 0) {
623 m_instance = new SphereDecreasingInterpolationStrategy();
624 TQ_CHECK_PTR(m_instance);
625 }
626
627 return m_instance;
628}
629
630double KisGradientSegment::SphereDecreasingInterpolationStrategy::valueAt(double t, double middle) const
631{
632 double lt = LinearInterpolationStrategy::calcValueAt(t, middle);
633 double value = 1 - sqrt(1 - lt * lt);
634
635 return value;
636}
637
638#include "kis_gradient.moc"
639