kogradientmanager.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2005 Tim Beaulen <tbscope@gmail.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20#include "kogradientmanager.h"
21
22#include "svgnamedcolors.h"
23
24#include <tqtextstream.h>
25#include <tqcstring.h>
26
27#include <kdebug.h>
28
29KoGradientManager::KoGradientManager()
30{
31}
32
33KoGradientManager::~KoGradientManager()
34{
35 // XXX: Should we delete the gradients here?
36}
37
38KoGradient* KoGradientManager::loadGradient(const TQString& filename)
39{
40 TQString strExt;
41 const int result=filename.findRev('.');
42 if (result>=0)
43 {
44 strExt=filename.mid(result).lower();
45 }
46
47 TQFile f(filename);
48
49 if(f.open(IO_ReadOnly))
50 {
51 if(strExt == ".ggr")
52 {
53 return loadChalkGradient(&f);
54 }
55 else if(strExt==".kgr")
56 {
57 return loadKarbonGradient(&f);
58 }
59 else if(strExt==".svg")
60 {
61 return loadSvgGradient(&f);
62 }
63 }
64
65 return 0;
66}
67
68KoGradient* KoGradientManager::loadKarbonGradient(TQFile* file)
69{
70 TQDomDocument doc;
71
72 if(!(doc.setContent(file)))
73 file->close();
74 else
75 {
76 TQDomElement e;
77 TQDomNode n = doc.documentElement().firstChild();
78
79 if(!n.isNull())
80 {
81 e = n.toElement();
82
83 if(!e.isNull())
84 if( e.tagName() == "GRADIENT" )
85 return parseKarbonGradient(e);
86 }
87 }
88
89 return 0;
90}
91
92KoGradient* KoGradientManager::loadChalkGradient(TQFile* file)
93{
94 KoGradient* grad = new KoGradient();
95
96 TQByteArray m_data = file->readAll();
97 file->close();
98
99 TQTextIStream fileContent(m_data);
100 fileContent.setEncoding(TQTextStream::UnicodeUTF8);
101
102 TQString header = fileContent.readLine();
103
104 if (header != "GIMP Gradient")
105 {
106 delete grad;
107 return 0;
108 }
109
110 TQString nameDefinition = fileContent.readLine();
111 TQString numSegmentsText;
112
113 if (nameDefinition.startsWith("Name: "))
114 {
115 TQString nameText = nameDefinition.right(nameDefinition.length() - 6);
116 numSegmentsText = fileContent.readLine();
117 }
118 else
119 {
120 // Older format without name.
121
122 numSegmentsText = nameDefinition;
123 }
124
125 int numSegments;
126 bool ok;
127
128 numSegments = numSegmentsText.toInt(&ok);
129
130 if (!ok || numSegments < 1)
131 {
132 return 0;
133 }
134
135 for (int i = 0; i < numSegments; i++)
136 {
137 KoColorStop *stop = new KoColorStop();
138
139 TQString segmentText = fileContent.readLine();
140 TQTextIStream segmentFields(&segmentText);
141
142 double leftOffset;
143 double middleOffset;
144 double rightOffset;
145
146 segmentFields >> leftOffset >> middleOffset >> rightOffset;
147
148 double leftRed;
149 double leftGreen;
150 double leftBlue;
151 double leftAlpha;
152
153 segmentFields >> leftRed >> leftGreen >> leftBlue >> leftAlpha;
154
155 double rightRed;
156 double rightGreen;
157 double rightBlue;
158 double rightAlpha;
159
160 segmentFields >> rightRed >> rightGreen >> rightBlue >> rightAlpha;
161
162 int interpolationType;
163 int colorInterpolationType;
164
165 segmentFields >> interpolationType >> colorInterpolationType;
166
167 middleOffset = (middleOffset - leftOffset) / (rightOffset - leftOffset);
168
169 stop->opacity = leftAlpha;
170 stop->midpoint = middleOffset;
171 stop->offset = leftOffset;
172
173 stop->color1 = leftRed;
174 stop->color2 = leftGreen;
175 stop->color3 = leftBlue;
176 stop->color4 = 0.0;
177 stop->colorType = colorInterpolationType;
178 stop->interpolation = interpolationType;
179
180 grad->colorStops.append(stop);
181
182 if(rightOffset == 1.0)
183 {
184 KoColorStop *lastStop = new KoColorStop();
185 lastStop->opacity = rightAlpha;
186 lastStop->midpoint = middleOffset;
187 lastStop->offset = rightOffset;
188 lastStop->color1 = rightRed;
189 lastStop->color2 = rightGreen;
190 lastStop->color3 = rightBlue;
191 lastStop->color4 = 0.0;
192 lastStop->colorType = colorInterpolationType;
193 lastStop->interpolation = interpolationType;
194 grad->colorStops.append(lastStop);
195 }
196 }
197
198 if (!grad->colorStops.isEmpty())
199 {
200 grad->originX = 0.0;
201 grad->originY = 1.0;
202 grad->vectorX = 0.0;
203 grad->vectorY = 0.0;
204 grad->focalpointX = 0.0;
205 grad->focalpointY = 0.0;
206 grad->gradientType = gradient_type_linear;
207 grad->gradientRepeatMethod = repeat_method_none;
208
209 return grad;
210 }
211 else
212 {
213 delete grad;
214 return 0;
215 }
216}
217
218KoGradient* KoGradientManager::loadSvgGradient(TQFile* file)
219{
220 TQDomDocument doc;
221
222 if(!(doc.setContent(file)))
223 file->close();
224 else
225 {
226 for( TQDomNode n = doc.documentElement().firstChild(); !n.isNull(); n = n.nextSibling() )
227 {
228 TQDomElement e = n.toElement();
229 if( e.isNull() ) continue;
230
231 if( e.tagName() == "linearGradient" || e.tagName() == "radialGradient" )
232 return parseSvgGradient(e);
233 }
234 }
235
236 return 0;
237}
238
239KoGradient* KoGradientManager::parseKarbonGradient(const TQDomElement& element)
240{
241 KoGradient* grad = new KoGradient();
242
243 grad->originX = element.attribute("originX", "0.0").toDouble();
244 grad->originY = element.attribute("originY", "0.0").toDouble();
245 grad->focalpointX = element.attribute("focalX", "0.0").toDouble();
246 grad->focalpointY = element.attribute("focalY", "0.0").toDouble();
247 grad->vectorX = element.attribute("vectorX", "0.0").toDouble();
248 grad->vectorY = element.attribute("vectorY", "0.0").toDouble();
249 grad->gradientType = (KoGradientType)element.attribute("type", 0).toInt();
250 grad->gradientRepeatMethod = (KoGradientRepeatMethod)element.attribute("repeatMethod", 0).toInt();
251
252 grad->colorStops.clear();
253
254 // load stops
255 TQDomNodeList list = element.childNodes();
256 for( uint i = 0; i < list.count(); ++i )
257 {
258 if( list.item( i ).isElement() )
259 {
260 TQDomElement colorstop = list.item( i ).toElement();
261
262 if( colorstop.tagName() == "COLORSTOP" )
263 {
264 KoColorStop *stop = new KoColorStop();
265
266 TQDomElement e = colorstop.firstChild().toElement();
267
268 switch(e.attribute("colorSpace").toUShort())
269 {
270 case 1: // cmyk
271 stop->color1 = e.attribute( "v1", "0.0" ).toFloat();
272 stop->color2 = e.attribute( "v2", "0.0" ).toFloat();
273 stop->color3 = e.attribute( "v3", "0.0" ).toFloat();
274 stop->color4 = e.attribute( "v4", "0.0" ).toFloat();
275 stop->colorType = color_type_cmyk;
276 stop->interpolation = interpolation_linear;
277 break;
278 case 2: // hsv
279 stop->color1 = e.attribute( "v1", "0.0" ).toFloat();
280 stop->color2 = e.attribute( "v2", "0.0" ).toFloat();
281 stop->color3 = e.attribute( "v3", "0.0" ).toFloat();
282 stop->color4 = 0.0;
283 stop->colorType = color_type_hsv_cw;
284 stop->interpolation = interpolation_linear;
285 break;
286 case 3: // gray
287 stop->color1 = e.attribute( "v1", "0.0" ).toFloat();
288 stop->color2 = 0.0;
289 stop->color3 = 0.0;
290 stop->color4 = 0.0;
291 stop->colorType = color_type_gray;
292 stop->interpolation = interpolation_linear;
293 break;
294 default: // rgb
295 stop->color1 = e.attribute( "v1", "0.0" ).toFloat();
296 stop->color2 = e.attribute( "v2", "0.0" ).toFloat();
297 stop->color3 = e.attribute( "v3", "0.0" ).toFloat();
298 stop->color4 = 0.0;
299 stop->colorType = color_type_rgb;
300 stop->interpolation = interpolation_linear;
301 }
302
303 stop->opacity = e.attribute("opacity", "1.0").toFloat();
304
305 stop->offset = colorstop.attribute("ramppoint", "0.0").toFloat();
306 stop->midpoint = colorstop.attribute("midpoint", "0.5").toFloat();
307
308 grad->colorStops.append(stop);
309 }
310 }
311 }
312
313 return grad;
314}
315
316KoGradient* KoGradientManager::parseSvgGradient(const TQDomElement& element)
317{
318 KoGradient* grad = new KoGradient;
319
320 grad->colorStops.clear();
321 grad->gradientRepeatMethod = repeat_method_none;
322
323 /*TQString href = e.attribute( "xlink:href" ).mid( 1 );
324 if( !href.isEmpty() )
325 {
326 }*/
327
328 bool bbox = element.attribute( "gradientUnits" ) != "userSpaceOnUse";
329
330 if( element.tagName() == "linearGradient" )
331 {
332 if( bbox )
333 {
334 TQString s;
335
336 s = element.attribute( "x1", "0%" );
337 double xOrigin;
338 if( s.endsWith( "%" ) )
339 xOrigin = s.remove( '%' ).toDouble();
340 else
341 xOrigin = s.toDouble() * 100.0;
342
343 s = element.attribute( "y1", "0%" );
344 double yOrigin;
345 if( s.endsWith( "%" ) )
346 yOrigin = s.remove( '%' ).toDouble();
347 else
348 yOrigin = s.toDouble() * 100.0;
349
350 s = element.attribute( "x2", "100%" );
351 double xVector;
352 if( s.endsWith( "%" ) )
353 xVector = s.remove( '%' ).toDouble();
354 else
355 xVector = s.toDouble() * 100.0;
356
357 s = element.attribute( "y2", "0%" );
358 double yVector;
359 if( s.endsWith( "%" ) )
360 yVector = s.remove( '%' ).toDouble();
361 else
362 yVector = s.toDouble() * 100.0;
363
364 grad->originX = xOrigin;
365 grad->originY = yOrigin;
366 grad->vectorX = xVector;
367 grad->vectorY = yVector;
368 }
369 else
370 {
371 grad->originX = element.attribute( "x1" ).toDouble();
372 grad->originY = element.attribute( "y1" ).toDouble();
373 grad->vectorX = element.attribute( "x2" ).toDouble();
374 grad->vectorY = element.attribute( "y2" ).toDouble();
375 }
376 grad->gradientType = gradient_type_linear;
377 }
378 else
379 {
380 if( bbox )
381 {
382 TQString s;
383
384 s = element.attribute( "cx", "50%" );
385 double xOrigin;
386 if( s.endsWith( "%" ) )
387 xOrigin = s.remove( '%' ).toDouble();
388 else
389 xOrigin = s.toDouble() * 100.0;
390
391 s = element.attribute( "cy", "50%" );
392 double yOrigin;
393 if( s.endsWith( "%" ) )
394 yOrigin = s.remove( '%' ).toDouble();
395 else
396 yOrigin = s.toDouble() * 100.0;
397
398 s = element.attribute( "cx", "50%" );
399 double xVector;
400 if( s.endsWith( "%" ) )
401 xVector = s.remove( '%' ).toDouble();
402 else
403 xVector = s.toDouble() * 100.0;
404
405 s = element.attribute( "r", "50%" );
406 if( s.endsWith( "%" ) )
407 xVector += s.remove( '%' ).toDouble();
408 else
409 xVector += s.toDouble() * 100.0;
410
411 s = element.attribute( "cy", "50%" );
412 double yVector;
413 if( s.endsWith( "%" ) )
414 yVector = s.remove( '%' ).toDouble();
415 else
416 yVector = s.toDouble() * 100.0;
417
418 s = element.attribute( "fx", "50%" );
419 double xFocal;
420 if( s.endsWith( "%" ) )
421 xFocal = s.remove( '%' ).toDouble();
422 else
423 xFocal = s.toDouble() * 100.0;
424
425 s = element.attribute( "fy", "50%" );
426 double yFocal;
427 if( s.endsWith( "%" ) )
428 yFocal = s.remove( '%' ).toDouble();
429 else
430 yFocal = s.toDouble() * 100.0;
431
432 grad->originX = xOrigin;
433 grad->originY = yOrigin;
434 grad->vectorX = xVector;
435 grad->vectorY = yVector;
436 grad->focalpointX = xFocal;
437 grad->focalpointY = yFocal;
438 }
439 else
440 {
441 grad->originX = element.attribute( "cx" ).toDouble();
442 grad->originY = element.attribute( "cy" ).toDouble();
443 grad->vectorX = element.attribute( "cx" ).toDouble() + element.attribute( "r" ).toDouble();
444 grad->vectorY = element.attribute( "cy" ).toDouble();
445 grad->focalpointX = element.attribute( "fx" ).toDouble();
446 grad->focalpointY = element.attribute( "fy" ).toDouble();
447 }
448 grad->gradientType = gradient_type_radial;
449 }
450 // handle spread method
451 TQString spreadMethod = element.attribute( "spreadMethod" );
452 if( !spreadMethod.isEmpty() )
453 {
454 if( spreadMethod == "reflect" )
455 grad->gradientRepeatMethod = repeat_method_reflect;
456 else if( spreadMethod == "repeat" )
457 grad->gradientRepeatMethod = repeat_method_repeat;
458 }
459
460 for( TQDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() )
461 {
462 TQDomElement colorstop = n.toElement();
463 if( colorstop.tagName() == "stop" )
464 {
465 KoColorStop *stop = new KoColorStop();
466 TQColor c;
467 float off;
468 TQString temp = colorstop.attribute( "offset" );
469 if( temp.contains( '%' ) )
470 {
471 temp = temp.left( temp.length() - 1 );
472 off = temp.toFloat() / 100.0;
473 }
474 else
475 off = temp.toFloat();
476
477 if( !colorstop.attribute( "stop-color" ).isEmpty() )
478 parseSvgColor( c, colorstop.attribute( "stop-color" ) );
479 else
480 {
481 // try style attr
482 TQString style = colorstop.attribute( "style" ).simplifyWhiteSpace();
483 TQStringList substyles = TQStringList::split( ';', style );
484 for( TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it )
485 {
486 TQStringList substyle = TQStringList::split( ':', (*it) );
487 TQString command = substyle[0].stripWhiteSpace();
488 TQString params = substyle[1].stripWhiteSpace();
489 if( command == "stop-color" )
490 parseSvgColor( c, params );
491 if( command == "stop-opacity" )
492 stop->opacity = params.toDouble();
493 }
494
495 }
496 if( !colorstop.attribute( "stop-opacity" ).isEmpty() )
497 stop->opacity = colorstop.attribute( "stop-opacity" ).toDouble();
498
499 stop->offset = off;
500 stop->midpoint = 0.5;
501 stop->color1 = c.red() / 255.0;
502 stop->color2 = c.green() / 255.0;
503 stop->color3 = c.blue() / 255.0;
504 stop->color4 = 0.0;
505 stop->colorType = color_type_rgb;
506 stop->interpolation = interpolation_linear;
507 grad->colorStops.append(stop);
508 }
509 }
510
511 return grad;
512}
513
514void KoGradientManager::parseSvgColor(TQColor &color, const TQString &s)
515{
516 if( s.startsWith( "rgb(" ) )
517 {
518 TQString parse = s.stripWhiteSpace();
519 TQStringList colors = TQStringList::split( ',', parse );
520 TQString r = colors[0].right( ( colors[0].length() - 4 ) );
521 TQString g = colors[1];
522 TQString b = colors[2].left( ( colors[2].length() - 1 ) );
523
524 if( r.contains( "%" ) )
525 {
526 r = r.left( r.length() - 1 );
527 r = TQString::number( int( ( double( 255 * r.toDouble() ) / 100.0 ) ) );
528 }
529
530 if( g.contains( "%" ) )
531 {
532 g = g.left( g.length() - 1 );
533 g = TQString::number( int( ( double( 255 * g.toDouble() ) / 100.0 ) ) );
534 }
535
536 if( b.contains( "%" ) )
537 {
538 b = b.left( b.length() - 1 );
539 b = TQString::number( int( ( double( 255 * b.toDouble() ) / 100.0 ) ) );
540 }
541
542 color = TQColor( r.toInt(), g.toInt(), b.toInt() );
543 }
544 else
545 {
546 TQString rgbColor = s.stripWhiteSpace();
547 TQColor c;
548 if( rgbColor.startsWith( "#" ) )
549 c.setNamedColor( rgbColor );
550 else
551 {
552 int r, g, b;
553 svgNamedColorToRGB( rgbColor, r, g, b );
554 c = TQColor( r, g, b );
555 }
556 color = c;
557 }
558}
559