1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7 
8 #include "scpainter.h"
9 #include "scpattern.h"
10 #include "util_color.h"
11 #include "util.h"
12 #include "util_math.h"
13 
14 #include <cairo.h>
15 #if CAIRO_HAS_FC_FONT
16 #include <cairo-ft.h>
17 #endif
18 #include "text/storytext.h"
19 #include "text/textshaper.h"
20 #include "text/glyphcluster.h"
21 
22 #include <cmath>
23 #include <QDebug>
24 
ScPainter(QImage * target,int w,int h,double transparency,int blendmode)25 ScPainter::ScPainter(QImage *target, int w, int h, double transparency, int blendmode)
26 {
27 	Q_ASSERT(w >= 0);
28 	Q_ASSERT(h >= 0);
29 	m_maskPattern = nullptr;
30 	m_pattern = nullptr;
31 	m_imageMask = nullptr;
32 	m_image = target;
33 	m_layerTransparency = transparency;
34 	m_blendMode = blendmode;
35 	m_blendModeFill = 0;
36 	m_blendModeStroke = 0;
37 	m_width = w;
38 	m_height= h;
39 	m_fontSize = 0.0;
40 	mf_underline = false;
41 	mf_strikeout = false;
42 	mf_shadow = false;
43 	mf_outlined = false;
44 	m_fill = QColor(0,0,0);
45 	m_fill_trans = 1.0;
46 	m_fillRule = true;
47 	m_fillMode = 1;
48 	m_patternScaleX = 0.0;
49 	m_patternScaleX = 0.0;
50 	m_patternScaleY = 0.0;
51 	m_patternOffsetX = 0.0;
52 	m_patternOffsetY = 0.0;
53 	m_patternRotation = 0.0;
54 	m_patternSkewX = 0.0;
55 	m_patternSkewY = 0.0;
56 	m_patternMirrorX = false;
57 	m_patternMirrorY = false;
58 	m_gradientScale = 0.0;
59 	m_gradientSkew = 0.0;
60 	setHatchParameters(0, 2, 0, false, QColor(), QColor(), 0.0, 0.0);
61 	m_stroke = QColor(0,0,0);
62 	m_stroke_trans = 1.0;
63 	m_LineWidth = 1.0;
64 	m_strokeMode = 0;
65 	m_maskMode = 0;
66 	m_mask_patternScaleX = 0.0;
67 	m_mask_patternScaleY = 0.0;
68 	m_mask_patternOffsetX = 0.0;
69 	m_mask_patternOffsetY = 0.0;
70 	m_mask_patternRotation = 0.0;
71 	m_mask_patternSkewX = 0.0;
72 	m_mask_patternSkewY = 0.0;
73 	m_mask_patternMirrorX = false;
74 	m_mask_patternMirrorY = false;
75 	m_mask_gradientScale = 0.0;
76 	m_mask_gradientSkew = 0.0;
77 	PLineEnd = Qt::FlatCap;
78 	PLineJoin = Qt::MiterJoin;
79 	m_offset = 0;
80 	m_zoomFactor = 1;
81 	m_layeredMode = true;
82 	m_imageMode = true;
83 	m_svgMode = false;
84 
85 	fill_gradient = VGradient(VGradient::linear);
86 	stroke_gradient = VGradient(VGradient::linear);
87 
88 	m_matrix = QTransform();
89 	m_zoomStack.clear();
90 	cairo_surface_t *img = cairo_image_surface_create_for_data(m_image->bits(), CAIRO_FORMAT_ARGB32, w, h, w * 4);
91 	cairo_surface_set_device_scale(img, m_image->devicePixelRatio(), m_image->devicePixelRatio());
92 	m_cr = cairo_create(img);
93 	cairo_save(m_cr);
94 	cairo_set_fill_rule (m_cr, CAIRO_FILL_RULE_EVEN_ODD);
95 	cairo_set_operator(m_cr, CAIRO_OPERATOR_OVER);
96 	cairo_set_tolerance(m_cr, 0.5);
97 }
98 
~ScPainter()99 ScPainter::~ScPainter()
100 {
101 	cairo_surface_destroy(cairo_get_target(m_cr));
102 	cairo_destroy(m_cr);
103 }
104 
beginLayer(double transparency,int blendmode,FPointArray * clipArray)105 void ScPainter::beginLayer(double transparency, int blendmode, FPointArray *clipArray)
106 {
107 	layerProp la;
108 	la.blendmode = m_blendMode;
109 	la.tranparency = m_layerTransparency;
110 	m_layerTransparency = transparency;
111 	m_blendMode = blendmode;
112 	la.pushed = false;
113 	la.groupClip.resize(0);
114 	la.maskMode = m_maskMode;
115 	la.mask_patternScaleX = m_mask_patternScaleX;
116 	la.mask_patternScaleY = m_mask_patternScaleY;
117 	la.mask_patternOffsetX = m_mask_patternOffsetX;
118 	la.mask_patternOffsetY = m_mask_patternOffsetY;
119 	la.mask_patternRotation = m_mask_patternRotation;
120 	la.mask_patternSkewX = m_mask_patternSkewX;
121 	la.mask_patternSkewY = m_mask_patternSkewY;
122 	la.mask_patternMirrorX = m_mask_patternMirrorX;
123 	la.mask_patternMirrorY = m_mask_patternMirrorY;
124 	la.mask_gradientScale = m_mask_gradientScale;
125 	la.mask_gradientSkew = m_mask_gradientSkew;
126 	la.mask_gradient = mask_gradient;
127 	la.maskPattern = m_maskPattern;
128 	if (clipArray != nullptr)
129 		la.groupClip = *clipArray;
130 	la.data = cairo_get_group_target(m_cr);
131 	la.fillRule = m_fillRule;
132 	cairo_push_group(m_cr);
133 	la.pushed = true;
134 	m_Layers.push(la);
135 }
136 
endLayer()137 void ScPainter::endLayer()
138 {
139 	layerProp la;
140 	if (m_Layers.count() == 0)
141 		return;
142 	la = m_Layers.pop();
143 	m_maskMode = la.maskMode;
144 	m_mask_patternScaleX = la.mask_patternScaleX;
145 	m_mask_patternScaleY = la.mask_patternScaleY;
146 	m_mask_patternOffsetX = la.mask_patternOffsetX;
147 	m_mask_patternOffsetY = la.mask_patternOffsetY;
148 	m_mask_patternRotation = la.mask_patternRotation;
149 	m_mask_patternSkewX = la.mask_patternSkewX;
150 	m_mask_patternSkewY = la.mask_patternSkewY;
151 	m_mask_patternMirrorX = la.mask_patternMirrorX;
152 	m_mask_patternMirrorY = la.mask_patternMirrorY;
153 	m_mask_gradientScale = la.mask_gradientScale;
154 	m_mask_gradientSkew = la.mask_gradientSkew;
155 	mask_gradient = la.mask_gradient;
156 	m_maskPattern = la.maskPattern;
157 	m_fillRule = la.fillRule;
158 	if (la.pushed)
159 	{
160 		cairo_pop_group_to_source (m_cr);
161 		if (!la.groupClip.empty())
162 		{
163 			if (m_fillRule)
164 				cairo_set_fill_rule (m_cr, CAIRO_FILL_RULE_EVEN_ODD);
165 			else
166 				cairo_set_fill_rule (m_cr, CAIRO_FILL_RULE_WINDING);
167 			setupPolygon(&la.groupClip);
168 			setClipPath();
169 		}
170 		cairo_set_operator(m_cr, CAIRO_OPERATOR_OVER);
171 		if (m_maskMode > 0)
172 		{
173 			cairo_pattern_t *patM = getMaskPattern();
174 			setRasterOp(m_blendMode);
175 			cairo_mask(m_cr, patM);
176 			if ((m_maskMode == 2) || (m_maskMode == 4) || (m_maskMode == 5) || (m_maskMode == 6))
177 				cairo_surface_destroy(m_imageMask);
178 			cairo_pattern_destroy(patM);
179 		}
180 		else
181 		{
182 			setRasterOp(m_blendMode);
183 			cairo_paint_with_alpha (m_cr, m_layerTransparency);
184 		}
185 		cairo_set_operator(m_cr, CAIRO_OPERATOR_OVER);
186 	}
187 	m_layerTransparency = la.tranparency;
188 	m_blendMode = la.blendmode;
189 	m_maskMode = 0;
190 }
191 
begin()192 void ScPainter::begin()
193 {
194 }
195 
end()196 void ScPainter::end()
197 {
198 	if (m_svgMode)
199 		cairo_show_page (m_cr);
200 	if (m_layeredMode)
201 	{
202 		cairo_surface_flush(cairo_get_target(m_cr));
203 		cairo_restore(m_cr);
204 		return;
205 	}
206 }
207 
clear()208 void ScPainter::clear()
209 {
210 	if (m_imageMode)
211 		m_image->fill(qRgba(255, 255, 255, 255));
212 }
213 
clear(const QColor & c)214 void ScPainter::clear(const QColor &c)
215 {
216 	QRgb cs = c.rgb();
217 	if (m_imageMode)
218 		m_image->fill(qRgba(qRed(cs), qGreen(cs), qBlue(cs), qAlpha(cs)));
219 }
220 
context()221 cairo_t *ScPainter::context()
222 {
223 	return m_cr;
224 }
225 
worldMatrix()226 const QTransform ScPainter::worldMatrix()
227 {
228 	cairo_matrix_t matrix;
229 	cairo_get_matrix(m_cr, &matrix);
230 	QTransform mat = QTransform(matrix.xx, matrix.yx, matrix.xy, matrix.yy, matrix.x0, matrix.y0);
231 	return mat;
232 }
233 
setWorldMatrix(const QTransform & mat)234 void ScPainter::setWorldMatrix(const QTransform &mat)
235 {
236 	cairo_matrix_t matrix;
237 	cairo_matrix_init(&matrix, mat.m11(), mat.m12(), mat.m21(), mat.m22(), mat.dx(), mat.dy());
238 	cairo_set_matrix(m_cr, &matrix);
239 }
240 
setAntialiasing(bool enable)241 void ScPainter::setAntialiasing(bool enable)
242 {
243 	if (enable)
244 		cairo_set_antialias(m_cr, CAIRO_ANTIALIAS_DEFAULT);
245 	else
246 		cairo_set_antialias(m_cr, CAIRO_ANTIALIAS_NONE);
247 }
248 
setZoomFactor(double zoomFactor)249 void ScPainter::setZoomFactor(double zoomFactor)
250 {
251 	m_zoomFactor = zoomFactor;
252 	cairo_scale (m_cr, m_zoomFactor, m_zoomFactor);
253 }
254 
translate(double x,double y)255 void ScPainter::translate(double x, double y)
256 {
257 	cairo_translate (m_cr, x, y);
258 }
259 
translate(const QPointF & offset)260 void ScPainter::translate(const QPointF& offset)
261 {
262 	cairo_translate (m_cr, offset.x(), offset.y());
263 }
264 
rotate(double r)265 void ScPainter::rotate(double r)
266 {
267 	cairo_rotate (m_cr, r * 3.1415927 / 180.0);
268 }
269 
scale(double x,double y)270 void ScPainter::scale(double x, double y)
271 {
272 	cairo_scale (m_cr, x, y);
273 	m_zoomFactor *= qMax(x, y);
274 }
275 
moveTo(const double & x,const double & y)276 void ScPainter::moveTo(const double &x, const double &y)
277 {
278 	cairo_move_to(m_cr, x, y);
279 }
280 
281 void
lineTo(const double & x,const double & y)282 ScPainter::lineTo(const double &x, const double &y)
283 {
284 	cairo_line_to(m_cr, x, y);
285 }
286 
curveTo(const FPoint & p1,const FPoint & p2,const FPoint & p3)287 void ScPainter::curveTo(const FPoint& p1, const FPoint& p2, const FPoint& p3)
288 {
289 	cairo_curve_to(m_cr, p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y());
290 }
291 
newPath()292 void ScPainter::newPath()
293 {
294 	cairo_new_path(m_cr);
295 }
296 
closePath()297 void ScPainter::closePath()
298 {
299 	cairo_close_path(m_cr);
300 }
301 
setFillRule(bool fillRule)302 void ScPainter::setFillRule(bool fillRule)
303 {
304 	m_fillRule = fillRule;
305 }
306 
setFillMode(int fill)307 void ScPainter::setFillMode(int fill)
308 {
309 	m_fillMode = fill;
310 }
311 
setStrokeMode(int stroke)312 void ScPainter::setStrokeMode(int stroke)
313 {
314 	m_strokeMode = stroke;
315 }
316 
setGradient(VGradient::VGradientType mode,const FPoint & orig,const FPoint & vec,const FPoint & foc,double scale,double skew)317 void ScPainter::setGradient(VGradient::VGradientType mode, const FPoint& orig, const FPoint& vec, const FPoint& foc, double scale, double skew)
318 {
319 	fill_gradient.setType(mode);
320 	fill_gradient.setOrigin(orig);
321 	fill_gradient.setVector(vec);
322 	fill_gradient.setFocalPoint(foc);
323 	stroke_gradient.setType(mode);
324 	stroke_gradient.setOrigin(orig);
325 	stroke_gradient.setVector(vec);
326 	stroke_gradient.setFocalPoint(foc);
327 	m_gradientScale = scale;
328 	if (skew == 90.0)
329 		m_gradientSkew = 1;
330 	else if (skew == 180.0)
331 		m_gradientSkew = 0;
332 	else if (skew == 270.0)
333 		m_gradientSkew = -1;
334 	else if (skew == 360.0)
335 		m_gradientSkew = 0;
336 	else
337 		m_gradientSkew = tan(M_PI / 180.0 * skew);
338 }
339 
setMaskMode(int mask)340 void ScPainter::setMaskMode(int mask)
341 {
342 	m_maskMode = mask;
343 }
344 
setGradientMask(VGradient::VGradientType mode,const FPoint & orig,const FPoint & vec,const FPoint & foc,double scale,double skew)345 void ScPainter::setGradientMask(VGradient::VGradientType mode, const FPoint& orig, const FPoint& vec, const FPoint& foc, double scale, double skew)
346 {
347 	mask_gradient.setType(mode);
348 	mask_gradient.setOrigin(orig);
349 	mask_gradient.setVector(vec);
350 	mask_gradient.setFocalPoint(foc);
351 	m_mask_gradientScale = scale;
352 	if (skew == 90.0)
353 		m_mask_gradientSkew = 1;
354 	else if (skew == 180.0)
355 		m_mask_gradientSkew = 0;
356 	else if (skew == 270.0)
357 		m_mask_gradientSkew = -1;
358 	else if (skew == 360.0)
359 		m_mask_gradientSkew = 0;
360 	else
361 		m_mask_gradientSkew = tan(M_PI / 180.0 * skew);
362 }
363 
setPatternMask(ScPattern * pattern,double scaleX,double scaleY,double offsetX,double offsetY,double rotation,double skewX,double skewY,bool mirrorX,bool mirrorY)364 void ScPainter::setPatternMask(ScPattern *pattern, double scaleX, double scaleY, double offsetX, double offsetY, double rotation, double skewX, double skewY, bool mirrorX, bool mirrorY)
365 {
366 	m_maskPattern = pattern;
367 	m_mask_patternScaleX = scaleX / 100.0;
368 	m_mask_patternScaleY = scaleY / 100.0;
369 	m_mask_patternOffsetX = offsetX;
370 	m_mask_patternOffsetY = offsetY;
371 	m_mask_patternRotation = rotation;
372 	m_mask_patternSkewX = skewX;
373 	m_mask_patternSkewY = skewY;
374 	m_mask_patternMirrorX = mirrorX;
375 	m_mask_patternMirrorY = mirrorY;
376 }
377 
set4ColorGeometry(const FPoint & p1,const FPoint & p2,const FPoint & p3,const FPoint & p4,const FPoint & c1,const FPoint & c2,const FPoint & c3,const FPoint & c4)378 void ScPainter::set4ColorGeometry(const FPoint& p1, const FPoint& p2, const FPoint& p3, const FPoint& p4, const FPoint& c1, const FPoint& c2, const FPoint& c3, const FPoint& c4)
379 {
380 	fill_gradient.setType(VGradient::fourcolor);
381 	gradPatchP1 = p1;
382 	gradPatchP2 = p2;
383 	gradPatchP3 = p3;
384 	gradPatchP4 = p4;
385 	gradControlP1 = c1;
386 	gradControlP2 = c2;
387 	gradControlP3 = c3;
388 	gradControlP4 = c4;
389 }
390 
set4ColorColors(const QColor & col1,const QColor & col2,const QColor & col3,const QColor & col4)391 void ScPainter::set4ColorColors(const QColor& col1, const QColor& col2, const QColor& col3, const QColor& col4)
392 {
393 	gradPatchColor1 = col1;
394 	gradPatchColor2 = col2;
395 	gradPatchColor3 = col3;
396 	gradPatchColor4 = col4;
397 }
398 
setDiamondGeometry(const FPoint & p1,const FPoint & p2,const FPoint & p3,const FPoint & p4,const FPoint & c1,const FPoint & c2,const FPoint & c3,const FPoint & c4,const FPoint & c5)399 void ScPainter::setDiamondGeometry(const FPoint& p1, const FPoint& p2, const FPoint& p3, const FPoint& p4, const FPoint& c1, const FPoint& c2, const FPoint& c3, const FPoint& c4, const FPoint& c5)
400 {
401 	fill_gradient.setType(VGradient::diamond);
402 	gradPatchP1 = p1;
403 	gradPatchP2 = p2;
404 	gradPatchP3 = p3;
405 	gradPatchP4 = p4;
406 	gradControlP1 = c1;
407 	gradControlP2 = c2;
408 	gradControlP3 = c3;
409 	gradControlP4 = c4;
410 	gradControlP5 = c5;
411 }
412 
setMeshGradient(const FPoint & p1,const FPoint & p2,const FPoint & p3,const FPoint & p4,const QList<QList<MeshPoint>> & meshArray)413 void ScPainter::setMeshGradient(const FPoint& p1, const FPoint& p2, const FPoint& p3, const FPoint& p4, const QList<QList<MeshPoint>>& meshArray)
414 {
415 	fill_gradient.setType(VGradient::mesh);
416 	meshGradientArray = meshArray;
417 	gradPatchP1 = p1;
418 	gradPatchP2 = p2;
419 	gradPatchP3 = p3;
420 	gradPatchP4 = p4;
421 }
422 
setMeshGradient(const FPoint & p1,const FPoint & p2,const FPoint & p3,const FPoint & p4,const QList<meshGradientPatch> & meshPatches)423 void ScPainter::setMeshGradient(const FPoint& p1, const FPoint& p2, const FPoint& p3, const FPoint& p4, const QList<meshGradientPatch>& meshPatches)
424 {
425 	fill_gradient.setType(VGradient::freemesh);
426 	meshGradientPatches = meshPatches;
427 	gradPatchP1 = p1;
428 	gradPatchP2 = p2;
429 	gradPatchP3 = p3;
430 	gradPatchP4 = p4;
431 }
432 
setHatchParameters(int mode,double distance,double angle,bool useBackground,const QColor & background,const QColor & foreground,double width,double height)433 void ScPainter::setHatchParameters(int mode, double distance, double angle, bool useBackground, const QColor& background, const QColor& foreground, double width, double height)
434 {
435 	m_hatchType = mode;
436 	m_hatchDistance = distance;
437 	m_hatchAngle = angle;
438 	m_hatchUseBackground = useBackground;
439 	m_hatchBackground = background;
440 	m_hatchForeground = foreground;
441 	m_hatchWidth = width;
442 	m_hatchHeight = height;
443 }
444 
fillPath()445 void ScPainter::fillPath()
446 {
447 	if (m_fillMode != 0)
448 		fillPathHelper();
449 }
450 
strokePath()451 void ScPainter::strokePath()
452 {
453 //	if (LineWidth == 0)
454 //		return;
455 	if (m_strokeMode != 0)
456 		strokePathHelper();
457 }
458 
pen()459 QColor ScPainter::pen()
460 {
461 	return m_stroke;
462 }
463 
brush()464 QColor ScPainter::brush()
465 {
466 	return m_fill;
467 }
468 
brushOpacity()469 double ScPainter::brushOpacity()
470 {
471 	return m_fill_trans;
472 }
473 
setPen(const QColor & c)474 void ScPainter::setPen(const QColor &c)
475 {
476 	m_stroke = c;
477 }
478 
setPen(const QColor & c,double w,Qt::PenStyle st,Qt::PenCapStyle ca,Qt::PenJoinStyle jo)479 void ScPainter::setPen(const QColor &c, double w, Qt::PenStyle st, Qt::PenCapStyle ca, Qt::PenJoinStyle jo)
480 {
481 	m_stroke = c;
482 	m_LineWidth = w;
483 	PLineEnd = ca;
484 	PLineJoin = jo;
485 	m_offset = 0;
486 	getDashArray(st, w, m_array);
487 }
488 
setLineWidth(double w)489 void ScPainter::setLineWidth(double w)
490 {
491 	m_LineWidth = w;
492 }
493 
setPenOpacity(double op)494 void ScPainter::setPenOpacity(double op)
495 {
496 	m_stroke_trans = op;
497 }
498 
499 
setDash(const QVector<double> & array,double ofs)500 void ScPainter::setDash(const QVector<double>& array, double ofs)
501 {
502 	m_array = array;
503 	m_offset = ofs;
504 }
505 
setBrush(const QColor & c)506 void ScPainter::setBrush(const QColor &c)
507 {
508 	m_fill = c;
509 }
510 
setBrushOpacity(double op)511 void ScPainter::setBrushOpacity(double op)
512 {
513 	m_fill_trans = op;
514 }
515 
setOpacity(double op)516 void ScPainter::setOpacity(double op)
517 {
518 	m_fill_trans = op;
519 	m_stroke_trans = op;
520 }
521 
setFont(const ScFace & f,double s)522 void ScPainter::setFont(const ScFace &f, double s)
523 {
524 	m_font = f;
525 	m_fontSize = s;
526 }
527 
save()528 void ScPainter::save()
529 {
530 	cairo_save(m_cr);
531 	m_zoomStack.push(m_zoomFactor);
532 }
533 
restore()534 void ScPainter::restore()
535 {
536 	cairo_restore(m_cr);
537 	if (!m_zoomStack.isEmpty())
538 		m_zoomFactor = m_zoomStack.pop();
539 }
540 
blendModeFill()541 int ScPainter::blendModeFill()
542 {
543 	return m_blendModeFill;
544 }
545 
setRasterOp(int blendMode)546 void ScPainter::setRasterOp(int blendMode)
547 {
548 	if (blendMode == 0)
549 		cairo_set_operator(m_cr, CAIRO_OPERATOR_OVER);
550 	else if (blendMode == 1)
551 		cairo_set_operator(m_cr, CAIRO_OPERATOR_DARKEN);
552 	else if (blendMode == 2)
553 		cairo_set_operator(m_cr, CAIRO_OPERATOR_LIGHTEN);
554 	else if (blendMode == 3)
555 		cairo_set_operator(m_cr, CAIRO_OPERATOR_MULTIPLY);
556 	else if (blendMode == 4)
557 		cairo_set_operator(m_cr, CAIRO_OPERATOR_SCREEN);
558 	else if (blendMode == 5)
559 		cairo_set_operator(m_cr, CAIRO_OPERATOR_OVERLAY);
560 	else if (blendMode == 6)
561 		cairo_set_operator(m_cr, CAIRO_OPERATOR_HARD_LIGHT);
562 	else if (blendMode == 7)
563 		cairo_set_operator(m_cr, CAIRO_OPERATOR_SOFT_LIGHT);
564 	else if (blendMode == 8)
565 		cairo_set_operator(m_cr, CAIRO_OPERATOR_DIFFERENCE);
566 	else if (blendMode == 9)
567 		cairo_set_operator(m_cr, CAIRO_OPERATOR_EXCLUSION);
568 	else if (blendMode == 10)
569 		cairo_set_operator(m_cr, CAIRO_OPERATOR_COLOR_DODGE);
570 	else if (blendMode == 11)
571 		cairo_set_operator(m_cr, CAIRO_OPERATOR_COLOR_BURN);
572 	else if (blendMode == 12)
573 		cairo_set_operator(m_cr, CAIRO_OPERATOR_HSL_HUE);
574 	else if (blendMode == 13)
575 		cairo_set_operator(m_cr, CAIRO_OPERATOR_HSL_SATURATION);
576 	else if (blendMode == 14)
577 		cairo_set_operator(m_cr, CAIRO_OPERATOR_HSL_COLOR);
578 	else if (blendMode == 15)
579 		cairo_set_operator(m_cr, CAIRO_OPERATOR_HSL_LUMINOSITY);
580 	else if (blendMode == 16)
581 		cairo_set_operator(m_cr, CAIRO_OPERATOR_ADD);
582 	else if (blendMode == 17)
583 		cairo_set_operator(m_cr, CAIRO_OPERATOR_DEST_IN);
584 	else if (blendMode == 18)
585 		cairo_set_operator(m_cr, CAIRO_OPERATOR_DEST_OUT);
586 	else if (blendMode == 19)
587 		cairo_set_operator(m_cr, CAIRO_OPERATOR_CLEAR);
588 	else
589 		cairo_set_operator(m_cr, CAIRO_OPERATOR_OVER);
590 }
591 
setBlendModeFill(int blendMode)592 void ScPainter::setBlendModeFill(int blendMode)
593 {
594 	m_blendModeFill = blendMode;
595 }
596 
setBlendModeStroke(int blendMode)597 void ScPainter::setBlendModeStroke(int blendMode)
598 {
599 	m_blendModeStroke = blendMode;
600 }
601 
setPattern(ScPattern * pattern,double scaleX,double scaleY,double offsetX,double offsetY,double rotation,double skewX,double skewY,bool mirrorX,bool mirrorY)602 void ScPainter::setPattern(ScPattern *pattern, double scaleX, double scaleY, double offsetX, double offsetY, double rotation, double skewX, double skewY, bool mirrorX, bool mirrorY)
603 {
604 	m_pattern = pattern;
605 	m_patternScaleX = scaleX / 100.0;
606 	m_patternScaleY = scaleY / 100.0;
607 	m_patternOffsetX = offsetX;
608 	m_patternOffsetY = offsetY;
609 	m_patternRotation = rotation;
610 	m_patternSkewX = skewX;
611 	m_patternSkewY = skewY;
612 	m_patternMirrorX = mirrorX;
613 	m_patternMirrorY = mirrorY;
614 }
615 
getMaskPattern()616 cairo_pattern_t * ScPainter::getMaskPattern()
617 {
618 	cairo_save(m_cr);
619 	cairo_pattern_t *pat;
620 	if ((m_maskMode == 1) || (m_maskMode == 3))
621 	{
622 		double x1 = mask_gradient.origin().x();
623 		double y1 = mask_gradient.origin().y();
624 		double x2 = mask_gradient.vector().x();
625 		double y2 = mask_gradient.vector().y();
626 		double fx = mask_gradient.focalPoint().x();
627 		double fy = mask_gradient.focalPoint().y();
628 		if (mask_gradient.type() == VGradient::linear)
629 			pat = cairo_pattern_create_linear (x1, y1,  x2, y2);
630 		else
631 			pat = cairo_pattern_create_radial (fx, fy, 0, x1, y1, sqrt(pow(x2 - x1, 2) + pow(y2 - y1,2)));
632 		QList<VColorStop*> colorStops = mask_gradient.colorStops();
633 		QColor qStopColor;
634 		for (int offset = 0 ; offset < colorStops.count() ; offset++)
635 		{
636 			qStopColor = colorStops[ offset ]->color;
637 			double a = colorStops[offset]->opacity;
638 			double r, g, b;
639 			qStopColor.getRgbF(&r, &g, &b);
640 			if (m_maskMode == 3)
641 				a = /* 1.0 - */(0.3 * r + 0.59 * g + 0.11 * b);
642 			cairo_pattern_add_color_stop_rgba (pat, colorStops[ offset ]->rampPoint, r, g, b, a);
643 		}
644 		cairo_matrix_t matrix;
645 		QTransform qmatrix;
646 		if (mask_gradient.type() == VGradient::radial)
647 		{
648 			double rotEnd = xy2Deg(x2 - x1, y2 - y1);
649 			qmatrix.translate(x1, y1);
650 			qmatrix.rotate(rotEnd);
651 			qmatrix.shear(m_mask_gradientSkew, 0);
652 			qmatrix.translate(0, y1 * (1.0 - m_mask_gradientScale));
653 			qmatrix.translate(-x1, -y1);
654 			qmatrix.scale(1, m_mask_gradientScale);
655 		}
656 		else
657 		{
658 			qmatrix.translate(x1, y1);
659 			qmatrix.shear(-m_mask_gradientSkew, 0);
660 			qmatrix.translate(-x1, -y1);
661 		}
662 		cairo_matrix_init(&matrix, qmatrix.m11(), qmatrix.m12(), qmatrix.m21(), qmatrix.m22(), qmatrix.dx(), qmatrix.dy());
663 		cairo_matrix_invert(&matrix);
664 		cairo_pattern_set_matrix (pat, &matrix);
665 	}
666 	else
667 	{
668 		if ((m_maskMode == 4) || (m_maskMode == 5))
669 		{
670 			m_imageQ = m_maskPattern->pattern.copy();
671 			if (m_maskMode == 5)
672 				m_imageQ.invertPixels();
673 			int h = m_imageQ.height();
674 			int w = m_imageQ.width();
675 			int k;
676 			QRgb *s;
677 			QRgb r;
678 			for (int yi = 0; yi < h; ++yi)
679 			{
680 				s = (QRgb*)(m_imageQ.scanLine(yi));
681 				for (int xi = 0; xi < w; ++xi)
682 				{
683 					r = *s;
684 					if (qAlpha(r) == 0)
685 						k = 0; // 255;
686 					else
687 						k = qMin(qRound(0.3 * qRed(r) + 0.59 * qGreen(r) + 0.11 * qBlue(r)), 255);
688 					*s = qRgba(qRed(r), qGreen(r), qBlue(r), /* 255 - */ k);
689 					s++;
690 				}
691 			}
692 			m_imageMask = cairo_image_surface_create_for_data ((uchar*)m_imageQ.bits(), CAIRO_FORMAT_ARGB32, w, h, w * 4);
693 		}
694 		else
695 		{
696 			m_imageQ = m_maskPattern->pattern.copy();
697 			if (m_maskMode == 6)
698 				m_imageQ.invertPixels(QImage::InvertRgba);
699 			m_imageMask = cairo_image_surface_create_for_data ((uchar*)m_imageQ.bits(), CAIRO_FORMAT_ARGB32, m_maskPattern->getPattern()->width(), m_maskPattern->getPattern()->height(), m_maskPattern->getPattern()->width() * 4);
700 		}
701 		pat = cairo_pattern_create_for_surface(m_imageMask);
702 		cairo_pattern_set_extend(pat, CAIRO_EXTEND_REPEAT);
703 		cairo_pattern_set_filter(pat, CAIRO_FILTER_GOOD);
704 		cairo_matrix_t matrix;
705 		QTransform qmatrix;
706 		qmatrix.translate(m_mask_patternOffsetX, m_mask_patternOffsetY);
707 		qmatrix.rotate(m_mask_patternRotation);
708 		qmatrix.shear(-m_mask_patternSkewX, m_mask_patternSkewY);
709 		qmatrix.scale(m_mask_patternScaleX, m_mask_patternScaleY);
710 		qmatrix.scale(m_maskPattern->width / static_cast<double>(m_maskPattern->getPattern()->width()), m_maskPattern->height / static_cast<double>(m_maskPattern->getPattern()->height()));
711 		if (m_mask_patternMirrorX)
712 			qmatrix.scale(-1, 1);
713 		if (m_mask_patternMirrorY)
714 			qmatrix.scale(1, -1);
715 		cairo_matrix_init(&matrix, qmatrix.m11(), qmatrix.m12(), qmatrix.m21(), qmatrix.m22(), qmatrix.dx(), qmatrix.dy());
716 		cairo_matrix_invert(&matrix);
717 		cairo_pattern_set_matrix (pat, &matrix);
718 	}
719 	cairo_restore(m_cr);
720 	return pat;
721 }
722 
fillPathHelper()723 void ScPainter::fillPathHelper()
724 {
725 	cairo_save(m_cr);
726 	cairo_set_operator(m_cr, CAIRO_OPERATOR_OVER);
727 	if (m_fillRule)
728 		cairo_set_fill_rule (m_cr, CAIRO_FILL_RULE_EVEN_ODD);
729 	else
730 		cairo_set_fill_rule (m_cr, CAIRO_FILL_RULE_WINDING);
731 	if (m_fillMode == 1)
732 	{
733 		double r, g, b;
734 		m_fill.getRgbF(&r, &g, &b);
735 		if (m_maskMode > 0)
736 		{
737 			cairo_pattern_t *pat = getMaskPattern();
738 			cairo_set_source_rgb(m_cr, r, g, b);
739 			setRasterOp(m_blendModeFill);
740 			cairo_clip_preserve(m_cr);
741 			cairo_mask(m_cr, pat);
742 			if ((m_maskMode == 2) || (m_maskMode == 4) || (m_maskMode == 5) || (m_maskMode == 6))
743 				cairo_surface_destroy(m_imageMask);
744 			cairo_pattern_destroy(pat);
745 		}
746 		else
747 		{
748 			cairo_set_source_rgba(m_cr, r, g, b, m_fill_trans);
749 			setRasterOp(m_blendModeFill);
750 			cairo_fill_preserve(m_cr);
751 		}
752 	}
753 	else if (m_fillMode == 2)
754 	{
755 		cairo_pattern_t *pat = nullptr;
756 		cairo_surface_t *img = nullptr;
757 		cairo_t *cr = nullptr;
758 		cairo_pattern_t *mpat = nullptr;
759 		if (fill_gradient.type() == VGradient::fourcolor)
760 		{
761 			double p1x = gradPatchP1.x();
762 			double p1y = gradPatchP1.y();
763 			double p2x = gradPatchP2.x();
764 			double p2y = gradPatchP2.y();
765 			double p3x = gradPatchP3.x();
766 			double p3y = gradPatchP3.y();
767 			double p4x = gradPatchP4.x();
768 			double p4y = gradPatchP4.y();
769 			img = cairo_surface_create_similar(cairo_get_target(m_cr), CAIRO_CONTENT_COLOR_ALPHA, p3x, p3y);
770 			cr = cairo_create(img);
771 			cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
772 			cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
773 			cairo_set_tolerance(cr, 0.5);
774 			double r, g, b, a;
775 			mpat = cairo_pattern_create_mesh();
776 			cairo_mesh_pattern_begin_patch(mpat);
777 			cairo_mesh_pattern_move_to(mpat, p1x, p1y);
778 			cairo_mesh_pattern_line_to(mpat, p2x, p2y);
779 			cairo_mesh_pattern_line_to(mpat, p3x, p3y);
780 			cairo_mesh_pattern_line_to(mpat, p4x, p4y);
781 			cairo_mesh_pattern_line_to(mpat, p1x, p1y);
782 			cairo_mesh_pattern_set_control_point(mpat, 0, gradControlP1.x(),  gradControlP1.y());
783 			cairo_mesh_pattern_set_control_point(mpat, 1, gradControlP2.x(),  gradControlP2.y());
784 			cairo_mesh_pattern_set_control_point(mpat, 2, gradControlP3.x(),  gradControlP3.y());
785 			cairo_mesh_pattern_set_control_point(mpat, 3, gradControlP4.x(),  gradControlP4.y());
786 			gradPatchColor1.getRgbF(&r, &g, &b, &a);
787 			cairo_mesh_pattern_set_corner_color_rgba(mpat, 0, r, g, b, a);
788 			gradPatchColor2.getRgbF(&r, &g, &b, &a);
789 			cairo_mesh_pattern_set_corner_color_rgba(mpat, 1, r, g, b, a);
790 			gradPatchColor3.getRgbF(&r, &g, &b, &a);
791 			cairo_mesh_pattern_set_corner_color_rgba(mpat, 2, r, g, b, a);
792 			gradPatchColor4.getRgbF(&r, &g, &b, &a);
793 			cairo_mesh_pattern_set_corner_color_rgba(mpat, 3, r, g, b, a);
794 			cairo_mesh_pattern_end_patch(mpat);
795 			cairo_pattern_set_filter(mpat, CAIRO_FILTER_GOOD);
796 			cairo_set_source(cr, mpat);
797 			cairo_paint_with_alpha(cr, 1.0);
798 			pat = cairo_pattern_create_for_surface(img);
799 			cairo_pattern_set_extend(pat, CAIRO_EXTEND_NONE);
800 			cairo_pattern_set_filter(pat, CAIRO_FILTER_GOOD);
801 		}
802 		else if (fill_gradient.type() == VGradient::diamond)
803 		{
804 			double p1x = gradControlP1.x();
805 			double p1y = gradControlP1.y();
806 			double p2x = gradControlP2.x();
807 			double p2y = gradControlP2.y();
808 			double p3x = gradControlP3.x();
809 			double p3y = gradControlP3.y();
810 			double p4x = gradControlP4.x();
811 			double p4y = gradControlP4.y();
812 			img = cairo_surface_create_similar(cairo_get_target(m_cr), CAIRO_CONTENT_COLOR_ALPHA, gradPatchP3.x(), gradPatchP3.y());
813 			cr = cairo_create(img);
814 			cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
815 			cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
816 			cairo_set_tolerance(cr, 0.5);
817 			double r, g, b, a;
818 			QList<VColorStop*> colorStops = fill_gradient.colorStops();
819 			QList<QColor> qStopColors;
820 			QList<double> qStopRampPoints;
821 			QColor qStopColor;
822 			for (int offset = 0 ; offset < colorStops.count() ; offset++)
823 			{
824 				qStopColor = colorStops[ offset ]->color;
825 				qStopColor.setAlphaF(colorStops[offset]->opacity);
826 				if (offset == 0)
827 				{
828 					if (colorStops[offset]->rampPoint > 0)
829 					{
830 						qStopRampPoints.append(0);
831 						qStopColors.append(qStopColor);
832 					}
833 				}
834 				qStopColors.append(qStopColor);
835 				qStopRampPoints.append(colorStops[offset]->rampPoint);
836 			}
837 			qStopColors.last().getRgbF(&r, &g, &b, &a);
838 			QPointF centerP = QPointF(gradControlP5.x(), gradControlP5.y());
839 			QLineF edge1 = QLineF(centerP, QPointF(p1x, p1y));
840 			QLineF edge2 = QLineF(centerP, QPointF(p2x, p2y));
841 			QLineF edge3 = QLineF(centerP, QPointF(p3x, p3y));
842 			QLineF edge4 = QLineF(centerP, QPointF(p4x, p4y));
843 			QPointF p1 = edge1.pointAt(colorStops.last()->rampPoint);
844 			QPointF p2 = edge2.pointAt(colorStops.last()->rampPoint);
845 			QPointF p3 = edge3.pointAt(colorStops.last()->rampPoint);
846 			QPointF p4 = edge4.pointAt(colorStops.last()->rampPoint);
847 			cairo_set_source_rgba(cr, r, g, b, a);
848 			cairo_paint_with_alpha(cr, 1.0);
849 			cairo_set_operator(cr, CAIRO_OPERATOR_DEST_OUT);
850 			cairo_new_path(cr);
851 			cairo_move_to(cr, p1.x(), p1.y());
852 			cairo_line_to(cr, p2.x(), p2.y());
853 			cairo_line_to(cr, p3.x(), p3.y());
854 			cairo_line_to(cr, p4.x(), p4.y());
855 			cairo_close_path(cr);
856 			cairo_set_source_rgba(cr, 0, 0, 0, 1);
857 			cairo_fill(cr);
858 			cairo_set_operator(cr, CAIRO_OPERATOR_ADD);
859 			mpat = cairo_pattern_create_mesh();
860 			for (int offset = 1 ; offset < qStopRampPoints.count() ; offset++)
861 			{
862 				QLineF e1 = edge1;
863 				QLineF e1s = edge1;
864 				QLineF e2 = edge2;
865 				QLineF e2s = edge2;
866 				QLineF e3 = edge3;
867 				QLineF e3s = edge3;
868 				QLineF e4 = edge4;
869 				QLineF e4s = edge4;
870 				e1.setLength(edge1.length() * qStopRampPoints[ offset ]);
871 				e2.setLength(edge2.length() * qStopRampPoints[ offset ]);
872 				e3.setLength(edge3.length() * qStopRampPoints[ offset ]);
873 				e4.setLength(edge4.length() * qStopRampPoints[ offset ]);
874 				e1s.setLength(edge1.length() * qStopRampPoints[ offset - 1 ]);
875 				e2s.setLength(edge2.length() * qStopRampPoints[ offset - 1 ]);
876 				e3s.setLength(edge3.length() * qStopRampPoints[ offset - 1 ]);
877 				e4s.setLength(edge4.length() * qStopRampPoints[ offset - 1 ]);
878 				if (offset == 1)
879 				{
880 					cairo_mesh_pattern_begin_patch(mpat);
881 					cairo_mesh_pattern_move_to(mpat, centerP.x(), centerP.y());
882 					cairo_mesh_pattern_line_to(mpat, e1.x2(), e1.y2());
883 					cairo_mesh_pattern_line_to(mpat, e2.x2(), e2.y2());
884 					qStopColors[0].getRgbF(&r, &g, &b, &a);
885 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 0, r, g, b, a);
886 					qStopColors[1].getRgbF(&r, &g, &b, &a);
887 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 1, r, g, b, a);
888 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 2, r, g, b, a);
889 					cairo_mesh_pattern_end_patch(mpat);
890 					cairo_mesh_pattern_begin_patch(mpat);
891 					cairo_mesh_pattern_move_to(mpat, centerP.x(), centerP.y());
892 					cairo_mesh_pattern_line_to(mpat, e2.x2(), e2.y2());
893 					cairo_mesh_pattern_line_to(mpat, e3.x2(), e3.y2());
894 					qStopColors[0].getRgbF(&r, &g, &b, &a);
895 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 0, r, g, b, a);
896 					qStopColors[1].getRgbF(&r, &g, &b, &a);
897 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 1, r, g, b, a);
898 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 2, r, g, b, a);
899 					cairo_mesh_pattern_end_patch(mpat);
900 					cairo_mesh_pattern_begin_patch(mpat);
901 					cairo_mesh_pattern_move_to(mpat, centerP.x(), centerP.y());
902 					cairo_mesh_pattern_line_to(mpat, e3.x2(), e3.y2());
903 					cairo_mesh_pattern_line_to(mpat, e4.x2(), e4.y2());
904 					qStopColors[0].getRgbF(&r, &g, &b, &a);
905 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 0, r, g, b, a);
906 					qStopColors[1].getRgbF(&r, &g, &b, &a);
907 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 1, r, g, b, a);
908 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 2, r, g, b, a);
909 					cairo_mesh_pattern_end_patch(mpat);
910 					cairo_mesh_pattern_begin_patch(mpat);
911 					cairo_mesh_pattern_move_to(mpat, centerP.x(), centerP.y());
912 					cairo_mesh_pattern_line_to(mpat, e4.x2(), e4.y2());
913 					cairo_mesh_pattern_line_to(mpat, e1.x2(), e1.y2());
914 					qStopColors[0].getRgbF(&r, &g, &b, &a);
915 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 0, r, g, b, a);
916 					qStopColors[1].getRgbF(&r, &g, &b, &a);
917 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 1, r, g, b, a);
918 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 2, r, g, b, a);
919 					cairo_mesh_pattern_end_patch(mpat);
920 				}
921 				else
922 				{
923 					cairo_mesh_pattern_begin_patch(mpat);
924 					cairo_mesh_pattern_move_to(mpat, e1s.x2(), e1s.y2());
925 					cairo_mesh_pattern_line_to(mpat, e1.x2(), e1.y2());
926 					cairo_mesh_pattern_line_to(mpat, e2.x2(), e2.y2());
927 					cairo_mesh_pattern_line_to(mpat, e2s.x2(), e2s.y2());
928 					qStopColors[offset-1].getRgbF(&r, &g, &b, &a);
929 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 0, r, g, b, a);
930 					qStopColors[offset].getRgbF(&r, &g, &b, &a);
931 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 1, r, g, b, a);
932 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 2, r, g, b, a);
933 					qStopColors[offset-1].getRgbF(&r, &g, &b, &a);
934 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 3, r, g, b, a);
935 					cairo_mesh_pattern_end_patch(mpat);
936 					cairo_mesh_pattern_begin_patch(mpat);
937 					cairo_mesh_pattern_move_to(mpat, e2s.x2(), e2s.y2());
938 					cairo_mesh_pattern_line_to(mpat, e2.x2(), e2.y2());
939 					cairo_mesh_pattern_line_to(mpat, e3.x2(), e3.y2());
940 					cairo_mesh_pattern_line_to(mpat, e3s.x2(), e3s.y2());
941 					qStopColors[offset-1].getRgbF(&r, &g, &b, &a);
942 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 0, r, g, b, a);
943 					qStopColors[offset].getRgbF(&r, &g, &b, &a);
944 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 1, r, g, b, a);
945 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 2, r, g, b, a);
946 					qStopColors[offset-1].getRgbF(&r, &g, &b, &a);
947 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 3, r, g, b, a);
948 					cairo_mesh_pattern_end_patch(mpat);
949 					cairo_mesh_pattern_begin_patch(mpat);
950 					cairo_mesh_pattern_move_to(mpat, e3s.x2(), e3s.y2());
951 					cairo_mesh_pattern_line_to(mpat, e3.x2(), e3.y2());
952 					cairo_mesh_pattern_line_to(mpat, e4.x2(), e4.y2());
953 					cairo_mesh_pattern_line_to(mpat, e4s.x2(), e4s.y2());
954 					qStopColors[offset-1].getRgbF(&r, &g, &b, &a);
955 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 0, r, g, b, a);
956 					qStopColors[offset].getRgbF(&r, &g, &b, &a);
957 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 1, r, g, b, a);
958 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 2, r, g, b, a);
959 					qStopColors[offset-1].getRgbF(&r, &g, &b, &a);
960 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 3, r, g, b, a);
961 					cairo_mesh_pattern_end_patch(mpat);
962 					cairo_mesh_pattern_begin_patch(mpat);
963 					cairo_mesh_pattern_move_to(mpat, e4s.x2(), e4s.y2());
964 					cairo_mesh_pattern_line_to(mpat, e4.x2(), e4.y2());
965 					cairo_mesh_pattern_line_to(mpat, e1.x2(), e1.y2());
966 					cairo_mesh_pattern_line_to(mpat, e1s.x2(), e1s.y2());
967 					qStopColors[offset-1].getRgbF(&r, &g, &b, &a);
968 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 0, r, g, b, a);
969 					qStopColors[offset].getRgbF(&r, &g, &b, &a);
970 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 1, r, g, b, a);
971 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 2, r, g, b, a);
972 					qStopColors[offset-1].getRgbF(&r, &g, &b, &a);
973 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 3, r, g, b, a);
974 					cairo_mesh_pattern_end_patch(mpat);
975 				}
976 			}
977 			cairo_pattern_set_filter(mpat, CAIRO_FILTER_GOOD);
978 			cairo_set_source(cr, mpat);
979 			cairo_paint_with_alpha(cr, 1.0);
980 			pat = cairo_pattern_create_for_surface(img);
981 			cairo_pattern_set_extend(pat, CAIRO_EXTEND_PAD);
982 			cairo_pattern_set_filter(pat, CAIRO_FILTER_GOOD);
983 		}
984 		else if (fill_gradient.type() == VGradient::mesh)
985 		{
986 			double p3x = gradPatchP3.x();
987 			double p3y = gradPatchP3.y();
988 			img = cairo_surface_create_similar(cairo_get_target(m_cr), CAIRO_CONTENT_COLOR_ALPHA, p3x, p3y);
989 			cr = cairo_create(img);
990 			cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
991 			cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
992 			cairo_set_tolerance(cr, 0.5);
993 			double r, g, b, a;
994 			mpat = cairo_pattern_create_mesh();
995 			for (int grow = 0; grow < meshGradientArray.count()-1; grow++)
996 			{
997 				for (int gcol = 0; gcol < meshGradientArray[grow].count()-1; gcol++)
998 				{
999 					MeshPoint mp1 = meshGradientArray[grow][gcol];
1000 					MeshPoint mp2 = meshGradientArray[grow][gcol+1];
1001 					MeshPoint mp3 = meshGradientArray[grow+1][gcol+1];
1002 					MeshPoint mp4 = meshGradientArray[grow+1][gcol];
1003 					cairo_mesh_pattern_begin_patch(mpat);
1004 					cairo_mesh_pattern_move_to(mpat, mp1.gridPoint.x(), mp1.gridPoint.y());
1005 					cairo_mesh_pattern_curve_to(mpat, mp1.controlRight.x(), mp1.controlRight.y(), mp2.controlLeft.x(), mp2.controlLeft.y(), mp2.gridPoint.x(), mp2.gridPoint.y());
1006 					cairo_mesh_pattern_curve_to(mpat, mp2.controlBottom.x(), mp2.controlBottom.y(), mp3.controlTop.x(), mp3.controlTop.y(), mp3.gridPoint.x(), mp3.gridPoint.y());
1007 					cairo_mesh_pattern_curve_to(mpat, mp3.controlLeft.x(), mp3.controlLeft.y(), mp4.controlRight.x(), mp4.controlRight.y(), mp4.gridPoint.x(), mp4.gridPoint.y());
1008 					cairo_mesh_pattern_curve_to(mpat, mp4.controlTop.x(), mp4.controlTop.y(), mp1.controlBottom.x(), mp1.controlBottom.y(), mp1.gridPoint.x(), mp1.gridPoint.y());
1009 					cairo_mesh_pattern_set_control_point(mpat, 0, mp1.controlColor.x(),  mp1.controlColor.y());
1010 					cairo_mesh_pattern_set_control_point(mpat, 1, mp2.controlColor.x(),  mp2.controlColor.y());
1011 					cairo_mesh_pattern_set_control_point(mpat, 2, mp3.controlColor.x(),  mp3.controlColor.y());
1012 					cairo_mesh_pattern_set_control_point(mpat, 3, mp4.controlColor.x(),  mp4.controlColor.y());
1013 					mp1.color.getRgbF(&r, &g, &b, &a);
1014 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 0, r, g, b, a);
1015 					mp2.color.getRgbF(&r, &g, &b, &a);
1016 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 1, r, g, b, a);
1017 					mp3.color.getRgbF(&r, &g, &b, &a);
1018 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 2, r, g, b, a);
1019 					mp4.color.getRgbF(&r, &g, &b, &a);
1020 					cairo_mesh_pattern_set_corner_color_rgba(mpat, 3, r, g, b, a);
1021 					cairo_mesh_pattern_end_patch(mpat);
1022 				}
1023 			}
1024 			cairo_pattern_set_filter(mpat, CAIRO_FILTER_BEST);
1025 			cairo_set_source(cr, mpat);
1026 			cairo_paint_with_alpha(cr, 1.0);
1027 			pat = cairo_pattern_create_for_surface(img);
1028 			cairo_pattern_set_extend(pat, CAIRO_EXTEND_NONE);
1029 			cairo_pattern_set_filter(pat, CAIRO_FILTER_BEST);
1030 		}
1031 		else if (fill_gradient.type() == VGradient::freemesh)
1032 		{
1033 			double p3x = gradPatchP3.x();
1034 			double p3y = gradPatchP3.y();
1035 			img = cairo_surface_create_similar(cairo_get_target(m_cr), CAIRO_CONTENT_COLOR_ALPHA, p3x, p3y);
1036 			cr = cairo_create(img);
1037 			cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
1038 			cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1039 			cairo_set_tolerance(cr, 0.5);
1040 			double r, g, b, a;
1041 			mpat = cairo_pattern_create_mesh();
1042 			for (int col = 0; col < meshGradientPatches.count(); col++)
1043 			{
1044 				meshGradientPatch patch = meshGradientPatches[col];
1045 				MeshPoint mp1 = patch.TL;
1046 				MeshPoint mp2 = patch.TR;
1047 				MeshPoint mp3 = patch.BR;
1048 				MeshPoint mp4 = patch.BL;
1049 				cairo_mesh_pattern_begin_patch(mpat);
1050 				cairo_mesh_pattern_move_to(mpat, mp1.gridPoint.x(), mp1.gridPoint.y());
1051 				cairo_mesh_pattern_curve_to(mpat, mp1.controlRight.x(), mp1.controlRight.y(), mp2.controlLeft.x(), mp2.controlLeft.y(), mp2.gridPoint.x(), mp2.gridPoint.y());
1052 				cairo_mesh_pattern_curve_to(mpat, mp2.controlBottom.x(), mp2.controlBottom.y(), mp3.controlTop.x(), mp3.controlTop.y(), mp3.gridPoint.x(), mp3.gridPoint.y());
1053 				cairo_mesh_pattern_curve_to(mpat, mp3.controlLeft.x(), mp3.controlLeft.y(), mp4.controlRight.x(), mp4.controlRight.y(), mp4.gridPoint.x(), mp4.gridPoint.y());
1054 				cairo_mesh_pattern_curve_to(mpat, mp4.controlTop.x(), mp4.controlTop.y(), mp1.controlBottom.x(), mp1.controlBottom.y(), mp1.gridPoint.x(), mp1.gridPoint.y());
1055 				cairo_mesh_pattern_set_control_point(mpat, 0, mp1.controlColor.x(),  mp1.controlColor.y());
1056 				cairo_mesh_pattern_set_control_point(mpat, 1, mp2.controlColor.x(),  mp2.controlColor.y());
1057 				cairo_mesh_pattern_set_control_point(mpat, 2, mp3.controlColor.x(),  mp3.controlColor.y());
1058 				cairo_mesh_pattern_set_control_point(mpat, 3, mp4.controlColor.x(),  mp4.controlColor.y());
1059 				mp1.color.getRgbF(&r, &g, &b, &a);
1060 				cairo_mesh_pattern_set_corner_color_rgba(mpat, 0, r, g, b, a);
1061 				mp2.color.getRgbF(&r, &g, &b, &a);
1062 				cairo_mesh_pattern_set_corner_color_rgba(mpat, 1, r, g, b, a);
1063 				mp3.color.getRgbF(&r, &g, &b, &a);
1064 				cairo_mesh_pattern_set_corner_color_rgba(mpat, 2, r, g, b, a);
1065 				mp4.color.getRgbF(&r, &g, &b, &a);
1066 				cairo_mesh_pattern_set_corner_color_rgba(mpat, 3, r, g, b, a);
1067 				cairo_mesh_pattern_end_patch(mpat);
1068 			}
1069 			cairo_pattern_set_filter(mpat, CAIRO_FILTER_BEST);
1070 			cairo_set_source(cr, mpat);
1071 			cairo_paint_with_alpha(cr, 1.0);
1072 			pat = cairo_pattern_create_for_surface(img);
1073 			cairo_pattern_set_extend(pat, CAIRO_EXTEND_NONE);
1074 			cairo_pattern_set_filter(pat, CAIRO_FILTER_BEST);
1075 		}
1076 		else
1077 		{
1078 			bool   isFirst = true;
1079 			double rampPoint, lastPoint = 0.0;
1080 			double x1 = fill_gradient.origin().x();
1081 			double y1 = fill_gradient.origin().y();
1082 			double x2 = fill_gradient.vector().x();
1083 			double y2 = fill_gradient.vector().y();
1084 			double fx = fill_gradient.focalPoint().x();
1085 			double fy = fill_gradient.focalPoint().y();
1086 			if (fill_gradient.type() == VGradient::linear)
1087 				pat = cairo_pattern_create_linear (x1, y1,  x2, y2);
1088 			else
1089 				pat = cairo_pattern_create_radial (fx, fy, 0, x1, y1, sqrt(pow(x2 - x1, 2) + pow(y2 - y1,2)));
1090 			if (fill_gradient.repeatMethod() == VGradient::none)
1091 				cairo_pattern_set_extend(pat, CAIRO_EXTEND_NONE);
1092 			else
1093 				cairo_pattern_set_extend(pat, CAIRO_EXTEND_PAD);
1094 			cairo_pattern_set_filter(pat, CAIRO_FILTER_GOOD);
1095 			QList<VColorStop*> colorStops = fill_gradient.colorStops();
1096 			for (int offset = 0 ; offset < colorStops.count() ; offset++)
1097 			{
1098 				rampPoint  = colorStops[ offset ]->rampPoint;
1099 				if ((lastPoint == rampPoint) && (!isFirst))
1100 					continue;
1101 				isFirst = false;
1102 				double a = colorStops[offset]->opacity;
1103 				double r, g, b;
1104 				colorStops[ offset ]->color.getRgbF(&r, &g, &b);
1105 				cairo_pattern_add_color_stop_rgba (pat, colorStops[ offset ]->rampPoint, r, g, b, a);
1106 				lastPoint = rampPoint;
1107 			}
1108 			cairo_matrix_t matrix;
1109 			QTransform qmatrix;
1110 			if (fill_gradient.type() == VGradient::radial)
1111 			{
1112 				double rotEnd = xy2Deg(x2 - x1, y2 - y1);
1113 				qmatrix.translate(x1, y1);
1114 				qmatrix.rotate(rotEnd);
1115 				qmatrix.shear(m_gradientSkew, 0);
1116 				qmatrix.translate(0, y1 * (1.0 - m_gradientScale));
1117 				qmatrix.translate(-x1, -y1);
1118 				qmatrix.scale(1, m_gradientScale);
1119 			}
1120 			else
1121 			{
1122 				qmatrix.translate(x1, y1);
1123 				qmatrix.shear(-m_gradientSkew, 0);
1124 				qmatrix.translate(-x1, -y1);
1125 			}
1126 			cairo_matrix_init(&matrix, qmatrix.m11(), qmatrix.m12(), qmatrix.m21(), qmatrix.m22(), qmatrix.dx(), qmatrix.dy());
1127 			cairo_matrix_invert(&matrix);
1128 			cairo_pattern_set_matrix (pat, &matrix);
1129 		}
1130 		cairo_set_source (m_cr, pat);
1131 		cairo_clip_preserve (m_cr);
1132 		if (m_maskMode > 0)
1133 		{
1134 			cairo_pattern_t *patM = getMaskPattern();
1135 			setRasterOp(m_blendModeFill);
1136 			cairo_mask(m_cr, patM);
1137 			if ((m_maskMode == 2) || (m_maskMode == 4) || (m_maskMode == 5) || (m_maskMode == 6))
1138 				cairo_surface_destroy(m_imageMask);
1139 			cairo_pattern_destroy (patM);
1140 		}
1141 		else
1142 		{
1143 			setRasterOp(m_blendModeFill);
1144 			cairo_paint_with_alpha (m_cr, m_fill_trans);
1145 		}
1146 		cairo_pattern_destroy (pat);
1147 		if ((fill_gradient.type() == VGradient::fourcolor) || (fill_gradient.type() == VGradient::diamond) || (fill_gradient.type() == VGradient::mesh) || (fill_gradient.type() == VGradient::freemesh))
1148 		{
1149 			cairo_surface_destroy(img);
1150 			cairo_pattern_destroy(mpat);
1151 			cairo_destroy(cr);
1152 		}
1153 	}
1154 	else if (m_fillMode == 3)
1155 	{
1156 		cairo_set_antialias(m_cr, CAIRO_ANTIALIAS_NONE);
1157 		cairo_surface_t *image2 = cairo_image_surface_create_for_data ((uchar*)m_pattern->getPattern()->bits(), CAIRO_FORMAT_ARGB32, m_pattern->getPattern()->width(), m_pattern->getPattern()->height(), m_pattern->getPattern()->width() * 4);
1158 		cairo_pattern_t *m_pat = cairo_pattern_create_for_surface(image2);
1159 		cairo_pattern_set_extend(m_pat, CAIRO_EXTEND_REPEAT);
1160 		cairo_pattern_set_filter(m_pat, CAIRO_FILTER_GOOD);
1161 		cairo_matrix_t matrix;
1162 		QTransform qmatrix;
1163 		qmatrix.translate(m_patternOffsetX, m_patternOffsetY);
1164 		qmatrix.rotate(m_patternRotation);
1165 		qmatrix.shear(-m_patternSkewX, m_patternSkewY);
1166 		qmatrix.scale(m_patternScaleX, m_patternScaleY);
1167 		qmatrix.scale(m_pattern->width / static_cast<double>(m_pattern->getPattern()->width()), m_pattern->height / static_cast<double>(m_pattern->getPattern()->height()));
1168 		if (m_patternMirrorX)
1169 			qmatrix.scale(-1, 1);
1170 		if (m_patternMirrorY)
1171 			qmatrix.scale(1, -1);
1172 		cairo_matrix_init(&matrix, qmatrix.m11(), qmatrix.m12(), qmatrix.m21(), qmatrix.m22(), qmatrix.dx(), qmatrix.dy());
1173 		cairo_matrix_invert(&matrix);
1174 		cairo_pattern_set_matrix (m_pat, &matrix);
1175 		cairo_set_source (m_cr, m_pat);
1176 		cairo_clip_preserve (m_cr);
1177 		if (m_maskMode > 0)
1178 		{
1179 			cairo_pattern_t *patM = getMaskPattern();
1180 			setRasterOp(m_blendModeFill);
1181 			cairo_mask(m_cr, patM);
1182 			if ((m_maskMode == 2) || (m_maskMode == 4) || (m_maskMode == 5) || (m_maskMode == 6))
1183 				cairo_surface_destroy(m_imageMask);
1184 			cairo_pattern_destroy (patM);
1185 		}
1186 		else
1187 		{
1188 			setRasterOp(m_blendModeFill);
1189 			cairo_paint_with_alpha (m_cr, m_fill_trans);
1190 		}
1191 		cairo_pattern_destroy (m_pat);
1192 		cairo_surface_destroy (image2);
1193 		cairo_set_antialias(m_cr, CAIRO_ANTIALIAS_DEFAULT);
1194 	}
1195 	else if (m_fillMode == 4)
1196 	{
1197 		cairo_path_t *path;
1198 		path = cairo_copy_path(m_cr);
1199 		cairo_push_group(m_cr);
1200 		if (m_hatchUseBackground && m_hatchBackground.isValid())
1201 		{
1202 			double r2, g2, b2;
1203 			m_hatchBackground.getRgbF(&r2, &g2, &b2);
1204 			cairo_set_source_rgb(m_cr, r2, g2, b2);
1205 			cairo_fill_preserve(m_cr);
1206 		}
1207 		double r, g, b;
1208 		m_hatchForeground.getRgbF(&r, &g, &b);
1209 		cairo_clip_preserve (m_cr);
1210 		cairo_set_line_width(m_cr, 1);
1211 		cairo_set_source_rgb(m_cr, r, g, b);
1212 		translate(m_hatchWidth / 2.0, m_hatchHeight / 2.0);
1213 		double lineLen = sqrt((m_hatchWidth / 2.0) * (m_hatchWidth / 2.0) + (m_hatchHeight / 2.0) * (m_hatchHeight / 2.0));
1214 		double dist = 0.0;
1215 		while (dist < lineLen)
1216 		{
1217 			cairo_save(m_cr);
1218 			rotate(-m_hatchAngle);
1219 			newPath();
1220 			moveTo(-lineLen, dist);
1221 			lineTo(lineLen, dist);
1222 			cairo_stroke(m_cr);
1223 			if (dist > 0)
1224 			{
1225 				newPath();
1226 				moveTo(-lineLen, -dist);
1227 				lineTo(lineLen, -dist);
1228 				cairo_stroke(m_cr);
1229 			}
1230 			cairo_restore(m_cr);
1231 			if ((m_hatchType == 1) || (m_hatchType == 2))
1232 			{
1233 				cairo_save(m_cr);
1234 				rotate(-m_hatchAngle + 90);
1235 				newPath();
1236 				moveTo(-lineLen, dist);
1237 				lineTo(lineLen, dist);
1238 				cairo_stroke(m_cr);
1239 				if (dist > 0)
1240 				{
1241 					newPath();
1242 					moveTo(-lineLen, -dist);
1243 					lineTo(lineLen, -dist);
1244 					cairo_stroke(m_cr);
1245 				}
1246 				cairo_restore(m_cr);
1247 			}
1248 			if (m_hatchType == 2)
1249 			{
1250 				cairo_save(m_cr);
1251 				rotate(-m_hatchAngle + 45);
1252 				double dDist = dist * sqrt(2.0);
1253 				newPath();
1254 				moveTo(-lineLen, dDist);
1255 				lineTo(lineLen, dDist);
1256 				cairo_stroke(m_cr);
1257 				if (dist > 0)
1258 				{
1259 					newPath();
1260 					moveTo(-lineLen, -dDist);
1261 					lineTo(lineLen, -dDist);
1262 					cairo_stroke(m_cr);
1263 				}
1264 				cairo_restore(m_cr);
1265 			}
1266 			dist += m_hatchDistance;
1267 		}
1268 		cairo_pop_group_to_source (m_cr);
1269 		setRasterOp(m_blendModeFill);
1270 		if (m_maskMode > 0)
1271 		{
1272 			cairo_pattern_t *patM = getMaskPattern();
1273 			cairo_pattern_set_filter(patM, CAIRO_FILTER_FAST);
1274 			cairo_mask(m_cr, patM);
1275 			if ((m_maskMode == 2) || (m_maskMode == 4) || (m_maskMode == 5) || (m_maskMode == 6))
1276 				cairo_surface_destroy(m_imageMask);
1277 			cairo_pattern_destroy(patM);
1278 		}
1279 		else
1280 			cairo_paint_with_alpha (m_cr, m_fill_trans);
1281 		newPath();
1282 		cairo_append_path(m_cr, path);
1283 		cairo_path_destroy(path);
1284 	}
1285 	cairo_restore(m_cr);
1286 	cairo_set_operator(m_cr, CAIRO_OPERATOR_OVER);
1287 }
1288 
strokePathHelper()1289 void ScPainter::strokePathHelper()
1290 {
1291 	cairo_save(m_cr);
1292 	cairo_set_operator(m_cr, CAIRO_OPERATOR_OVER);
1293 	if (m_LineWidth == 0)
1294 		cairo_set_line_width(m_cr, 1.0 / m_zoomFactor);
1295 	else
1296 		cairo_set_line_width(m_cr, m_LineWidth);
1297 	if (m_array.count() > 0)
1298 		cairo_set_dash(m_cr, m_array.data(), m_array.count(), m_offset);
1299 	else
1300 		cairo_set_dash(m_cr, nullptr, 0, 0);
1301 	cairo_set_operator(m_cr, CAIRO_OPERATOR_OVER);
1302 	if (PLineEnd == Qt::RoundCap)
1303 		cairo_set_line_cap (m_cr, CAIRO_LINE_CAP_ROUND);
1304 	else if (PLineEnd == Qt::SquareCap)
1305 		cairo_set_line_cap (m_cr, CAIRO_LINE_CAP_SQUARE);
1306 	else if (PLineEnd == Qt::FlatCap)
1307 		cairo_set_line_cap (m_cr, CAIRO_LINE_CAP_BUTT);
1308 	if (PLineJoin == Qt::RoundJoin)
1309 		cairo_set_line_join(m_cr, CAIRO_LINE_JOIN_ROUND);
1310 	else if (PLineJoin == Qt::BevelJoin)
1311 		cairo_set_line_join(m_cr, CAIRO_LINE_JOIN_BEVEL);
1312 	else if (PLineJoin == Qt::MiterJoin)
1313 		cairo_set_line_join(m_cr, CAIRO_LINE_JOIN_MITER);
1314 	if (m_strokeMode == 3)
1315 	{
1316 		cairo_push_group(m_cr);
1317 		cairo_set_antialias(m_cr, CAIRO_ANTIALIAS_NONE);
1318 		cairo_surface_t *image2 = cairo_image_surface_create_for_data ((uchar*)m_pattern->getPattern()->bits(), CAIRO_FORMAT_ARGB32, m_pattern->getPattern()->width(), m_pattern->getPattern()->height(), m_pattern->getPattern()->width() * 4);
1319 		cairo_pattern_t *m_pat = cairo_pattern_create_for_surface(image2);
1320 		cairo_pattern_set_extend(m_pat, CAIRO_EXTEND_REPEAT);
1321 		cairo_pattern_set_filter(m_pat, CAIRO_FILTER_GOOD);
1322 		cairo_matrix_t matrix;
1323 		QTransform qmatrix;
1324 		qmatrix.translate(-m_LineWidth / 2.0, -m_LineWidth / 2.0);
1325 		qmatrix.translate(m_patternOffsetX, m_patternOffsetY);
1326 		qmatrix.rotate(m_patternRotation);
1327 		qmatrix.shear(-m_patternSkewX, m_patternSkewY);
1328 		qmatrix.scale(m_patternScaleX, m_patternScaleY);
1329 		qmatrix.scale(m_pattern->width / static_cast<double>(m_pattern->getPattern()->width()), m_pattern->height / static_cast<double>(m_pattern->getPattern()->height()));
1330 		if (m_patternMirrorX)
1331 			qmatrix.scale(-1, 1);
1332 		if (m_patternMirrorY)
1333 			qmatrix.scale(1, -1);
1334 		cairo_matrix_init(&matrix, qmatrix.m11(), qmatrix.m12(), qmatrix.m21(), qmatrix.m22(), qmatrix.dx(), qmatrix.dy());
1335 		cairo_matrix_invert(&matrix);
1336 		cairo_pattern_set_matrix (m_pat, &matrix);
1337 		cairo_set_source (m_cr, m_pat);
1338 		cairo_stroke_preserve(m_cr);
1339 		cairo_pattern_destroy (m_pat);
1340 		cairo_surface_destroy (image2);
1341 		cairo_set_antialias(m_cr, CAIRO_ANTIALIAS_DEFAULT);
1342 		cairo_pop_group_to_source (m_cr);
1343 		setRasterOp(m_blendModeStroke);
1344 		cairo_paint_with_alpha (m_cr, m_stroke_trans);
1345 	}
1346 	else if (m_strokeMode == 2)
1347 	{
1348 		cairo_push_group(m_cr);
1349 		cairo_pattern_t *pat;
1350 		bool   isFirst = true;
1351 		double rampPoint, lastPoint = 0.0;
1352 		double x1 = stroke_gradient.origin().x();
1353 		double y1 = stroke_gradient.origin().y();
1354 		double x2 = stroke_gradient.vector().x();
1355 		double y2 = stroke_gradient.vector().y();
1356 		double fx = stroke_gradient.focalPoint().x();
1357 		double fy = stroke_gradient.focalPoint().y();
1358 		if (stroke_gradient.type() == VGradient::linear)
1359 			pat = cairo_pattern_create_linear (x1, y1,  x2, y2);
1360 		else
1361 			pat = cairo_pattern_create_radial (fx, fy, 0, x1, y1, sqrt(pow(x2 - x1, 2) + pow(y2 - y1,2)));
1362 		if (stroke_gradient.repeatMethod() == VGradient::none)
1363 			cairo_pattern_set_extend(pat, CAIRO_EXTEND_NONE);
1364 		else
1365 			cairo_pattern_set_extend(pat, CAIRO_EXTEND_PAD);
1366 		cairo_pattern_set_filter(pat, CAIRO_FILTER_GOOD);
1367 		QList<VColorStop*> colorStops = stroke_gradient.colorStops();
1368 		for (int offset = 0 ; offset < colorStops.count() ; offset++)
1369 		{
1370 			rampPoint  = colorStops[ offset ]->rampPoint;
1371 			if ((lastPoint == rampPoint) && (!isFirst))
1372 				continue;
1373 			isFirst = false;
1374 			double a = colorStops[offset]->opacity;
1375 			double r, g, b;
1376 			colorStops[ offset ]->color.getRgbF(&r, &g, &b);
1377 			cairo_pattern_add_color_stop_rgba (pat, rampPoint, r, g, b, a);
1378 			lastPoint = rampPoint;
1379 		}
1380 		cairo_matrix_t matrix;
1381 		QTransform qmatrix;
1382 		if (stroke_gradient.type() == VGradient::radial)
1383 		{
1384 			double rotEnd = xy2Deg(x2 - x1, y2 - y1);
1385 			qmatrix.translate(x1, y1);
1386 			qmatrix.rotate(rotEnd);
1387 			qmatrix.shear(m_gradientSkew, 0);
1388 			qmatrix.translate(0, y1 * (1.0 - m_gradientScale));
1389 			qmatrix.translate(-x1, -y1);
1390 			qmatrix.scale(1, m_gradientScale);
1391 		}
1392 		else
1393 		{
1394 			qmatrix.translate(x1, y1);
1395 			qmatrix.shear(-m_gradientSkew, 0);
1396 			qmatrix.translate(-x1, -y1);
1397 		}
1398 		cairo_matrix_init(&matrix, qmatrix.m11(), qmatrix.m12(), qmatrix.m21(), qmatrix.m22(), qmatrix.dx(), qmatrix.dy());
1399 		cairo_matrix_invert(&matrix);
1400 		cairo_pattern_set_matrix (pat, &matrix);
1401 		cairo_set_source (m_cr, pat);
1402 		cairo_stroke_preserve(m_cr);
1403 		cairo_pattern_destroy (pat);
1404 		cairo_pop_group_to_source (m_cr);
1405 		setRasterOp(m_blendModeStroke);
1406 		cairo_paint_with_alpha (m_cr, m_stroke_trans);
1407 	}
1408 	else
1409 	{
1410 		double r, g, b;
1411 		m_stroke.getRgbF(&r, &g, &b);
1412 		cairo_set_source_rgba(m_cr, r, g, b, m_stroke_trans);
1413 		setRasterOp(m_blendModeStroke);
1414 		cairo_stroke_preserve(m_cr);
1415 	}
1416 	cairo_restore(m_cr);
1417 	cairo_set_operator(m_cr, CAIRO_OPERATOR_OVER);
1418 }
1419 
setClipPath()1420 void ScPainter::setClipPath()
1421 {
1422 	cairo_clip (m_cr);
1423 }
1424 
drawImage(QImage * image)1425 void ScPainter::drawImage(QImage *image)
1426 {
1427 	cairo_set_antialias(m_cr, CAIRO_ANTIALIAS_NONE);
1428 /*
1429 	cairo_surface_t *image3 = cairo_image_surface_create_for_data ((uchar*)image->bits(), CAIRO_FORMAT_ARGB32, image->width(), image->height(), image->width() * 4);
1430 	cairo_set_source_surface (m_cr, image3, 0, 0);
1431 	cairo_paint_with_alpha (m_cr, fill_trans);
1432 	cairo_surface_destroy (image3);
1433 */
1434 /* Code with Layers, crashes on cairo_push_group */
1435 //	cairo_scale(m_cr, m_zoomFactor, m_zoomFactor);
1436 	cairo_push_group(m_cr);
1437 	cairo_set_operator(m_cr, CAIRO_OPERATOR_OVER);
1438 	cairo_set_fill_rule(m_cr, cairo_get_fill_rule(m_cr));
1439 	cairo_surface_t *image2  = cairo_image_surface_create_for_data ((uchar*)image->bits(), CAIRO_FORMAT_RGB24, image->width(), image->height(), image->width() * 4);
1440 	cairo_surface_t *image3 = cairo_image_surface_create_for_data ((uchar*)image->bits(), CAIRO_FORMAT_ARGB32, image->width(), image->height(), image->width() * 4);
1441 	cairo_set_source_surface (m_cr, image2, 0, 0);
1442 	cairo_pattern_set_filter(cairo_get_source(m_cr), CAIRO_FILTER_GOOD);
1443 	cairo_mask_surface (m_cr, image3, 0, 0);
1444 	cairo_surface_destroy (image2);
1445 	cairo_surface_destroy (image3);
1446 	cairo_pop_group_to_source (m_cr);
1447 	cairo_pattern_set_filter(cairo_get_source(m_cr), CAIRO_FILTER_GOOD);
1448 	setRasterOp(m_blendModeFill);
1449 	if (m_maskMode > 0)
1450 	{
1451 		cairo_pattern_t *patM = getMaskPattern();
1452 		cairo_pattern_set_filter(patM, CAIRO_FILTER_GOOD);
1453 		cairo_mask(m_cr, patM);
1454 		if ((m_maskMode == 2) || (m_maskMode == 4) || (m_maskMode == 5) || (m_maskMode == 6))
1455 			cairo_surface_destroy(m_imageMask);
1456 		cairo_pattern_destroy(patM);
1457 	}
1458 	else
1459 		cairo_paint_with_alpha (m_cr, m_fill_trans);
1460 	cairo_set_operator(m_cr, CAIRO_OPERATOR_OVER);
1461 	cairo_set_antialias(m_cr, CAIRO_ANTIALIAS_DEFAULT);
1462 }
1463 
setupPolygon(const FPointArray * points,bool closed)1464 void ScPainter::setupPolygon(const FPointArray *points, bool closed)
1465 {
1466 	bool nPath = true;
1467 	bool first = true;
1468 	FPoint np, np1, np2, np3, np4, firstP;
1469 
1470 	if (points->size() <= 3)
1471 		return;
1472 
1473 	newPath();
1474 	for (int poi = 0; poi < points->size() - 3; poi += 4)
1475 	{
1476 		if (points->isMarker(poi))
1477 		{
1478 			nPath = true;
1479 			continue;
1480 		}
1481 		if (nPath)
1482 		{
1483 			np = points->point(poi);
1484 			if ((!first) && (closed) && (np4 == firstP))
1485 				cairo_close_path(m_cr);
1486 			cairo_move_to(m_cr, np.x(), np.y());
1487 			first = nPath = false;
1488 			firstP = np4 = np;
1489 		}
1490 		np  = points->point(poi);
1491 		np1 = points->point(poi + 1);
1492 		np2 = points->point(poi + 3);
1493 		np3 = points->point(poi + 2);
1494 		if (np4 == np3)
1495 			continue;
1496 		if ((np == np1) && (np2 == np3))
1497 			cairo_line_to(m_cr, np3.x(), np3.y());
1498 		else
1499 			cairo_curve_to(m_cr, np1.x(), np1.y(), np2.x(), np2.y(), np3.x(), np3.y());
1500 		np4 = np3;
1501 	}
1502 	if (closed)
1503 		cairo_close_path(m_cr);
1504 }
1505 
setupSharpPolygon(const FPointArray * points,bool closed)1506 void ScPainter::setupSharpPolygon(const FPointArray *points, bool closed)
1507 {
1508 	bool nPath = true;
1509 	bool first = true;
1510 	FPoint np, np1, np2, np3, np4, firstP;
1511 
1512 	if (points->size() <= 3)
1513 		return;
1514 
1515 	newPath();
1516 	for (int poi = 0; poi < points->size() - 3; poi += 4)
1517 	{
1518 		if (points->isMarker(poi))
1519 		{
1520 			nPath = true;
1521 			continue;
1522 		}
1523 		if (nPath)
1524 		{
1525 			np = points->point(poi);
1526 			if ((!first) && (closed) && (np4 == firstP))
1527 				cairo_close_path(m_cr);
1528 			sharpLineHelper(np);
1529 			cairo_move_to(m_cr, np.x(), np.y());
1530 			first = nPath = false;
1531 			firstP = np4 = np;
1532 		}
1533 		np  = points->point(poi);
1534 		np1 = points->point(poi + 1);
1535 		np2 = points->point(poi + 3);
1536 		np3 = points->point(poi + 2);
1537 		if (np4 == np3)
1538 			continue;
1539 		if ((np == np1) && (np2 == np3))
1540 		{
1541 			sharpLineHelper(np3);
1542 			cairo_line_to(m_cr, np3.x(), np3.y());
1543 		}
1544 		else
1545 			cairo_curve_to(m_cr, np1.x(), np1.y(), np2.x(), np2.y(), np3.x(), np3.y());
1546 		np4 = np3;
1547 	}
1548 	if (closed)
1549 		cairo_close_path(m_cr);
1550 }
1551 
sharpLineHelper(FPoint & pp)1552 void ScPainter::sharpLineHelper(FPoint &pp)
1553 {
1554 	double x1 = pp.x();
1555 	double y1 = pp.y();
1556 	cairo_user_to_device (m_cr, &x1, &y1);
1557 	x1 = floor(x1) + 0.5;
1558 	y1 = floor(y1) + 0.5;
1559 	cairo_device_to_user (m_cr, &x1, &y1);
1560 	pp.setXY(x1, y1);
1561 }
1562 
sharpLineHelper(QPointF & pp)1563 void ScPainter::sharpLineHelper(QPointF &pp)
1564 {
1565 	double x1 = pp.x();
1566 	double y1 = pp.y();
1567 	cairo_user_to_device (m_cr, &x1, &y1);
1568 	x1 = floor(x1) + 0.5;
1569 	y1 = floor(y1) + 0.5;
1570 	cairo_device_to_user (m_cr, &x1, &y1);
1571 	pp.setX(x1);
1572 	pp.setY(y1);
1573 }
1574 
drawPolygon()1575 void ScPainter::drawPolygon()
1576 {
1577 	fillPath();
1578 }
1579 
drawPolyLine()1580 void ScPainter::drawPolyLine()
1581 {
1582 	strokePath();
1583 }
1584 
drawLine(const FPoint & start,const FPoint & end)1585 void ScPainter::drawLine(const FPoint& start, const FPoint& end)
1586 {
1587 	newPath();
1588 	moveTo(start.x(), start.y());
1589 	lineTo(end.x(), end.y());
1590 	strokePath();
1591 }
1592 
drawLine(const QPointF & start,const QPointF & end)1593 void ScPainter::drawLine(const QPointF& start, const QPointF& end)
1594 {
1595 	newPath();
1596 	moveTo(start.x(), start.y());
1597 	lineTo(end.x(), end.y());
1598 	strokePath();
1599 }
1600 
drawSharpLine(FPoint start,FPoint end)1601 void ScPainter::drawSharpLine(FPoint start, FPoint end)
1602 {
1603 	newPath();
1604 	sharpLineHelper(start);
1605 	sharpLineHelper(end);
1606 	moveTo(start.x(), start.y());
1607 	lineTo(end.x(), end.y());
1608 	strokePath();
1609 }
1610 
drawSharpLine(QPointF start,QPointF end)1611 void ScPainter::drawSharpLine(QPointF start, QPointF end)
1612 {
1613 	newPath();
1614 	sharpLineHelper(start);
1615 	sharpLineHelper(end);
1616 	moveTo(start.x(), start.y());
1617 	lineTo(end.x(), end.y());
1618 	strokePath();
1619 }
1620 
drawRect(double x,double y,double w,double h)1621 void ScPainter::drawRect(double x, double y, double w, double h)
1622 {
1623 	newPath();
1624 	cairo_rectangle(m_cr, x, y, w, h);
1625 	fillPath();
1626 	strokePath();
1627 }
1628 
drawSharpRect(double x,double y,double w,double h)1629 void ScPainter::drawSharpRect(double x, double y, double w, double h)
1630 {
1631 	newPath();
1632 	double x1 = x;
1633 	double y1 = y;
1634 	double w1 = w;
1635 	double h1 = h;
1636 	// see http://www.cairographics.org/FAQ/#sharp_lines
1637 	cairo_user_to_device (m_cr, &x1, &y1);
1638 	cairo_user_to_device (m_cr, &w1, &h1);
1639 	x1 = floor(x1) + 0.5;
1640 	y1 = floor(y1) + 0.5;
1641 	w1 = floor(w1) + 0.5;
1642 	h1 = floor(h1) + 0.5;
1643 	cairo_device_to_user (m_cr, &x1, &y1);
1644 	cairo_device_to_user (m_cr, &w1, &h1);
1645 	cairo_rectangle(m_cr, x1, y1, w1, h1);
1646 	fillPath();
1647 	strokePath();
1648 }
1649 
drawText(QRectF area,const QString & text,bool filled,int align)1650 void ScPainter::drawText(QRectF area, const QString& text, bool filled, int align)
1651 {
1652 	cairo_text_extents_t extents;
1653 	cairo_font_extents_t extentsF;
1654 	double x;
1655 	if (align == 0)
1656 		x = area.center().x();
1657 	else
1658 		x = area.x();
1659 	double y = area.y();
1660 	double ww = 0;
1661 	double hh = 0;
1662 	double r, g, b;
1663 
1664 	assert(!m_font.isNone());
1665 
1666 #if CAIRO_HAS_FC_FONT
1667 	FcPattern *pattern = FcPatternBuild(nullptr,
1668 										FC_FILE, FcTypeString, QFile::encodeName(m_font.fontFilePath()).data(),
1669 										FC_INDEX, FcTypeInteger, m_font.faceIndex(),
1670 										nullptr);
1671 	cairo_font_face_t *cairo_face = cairo_ft_font_face_create_for_pattern(pattern);
1672 	FcPatternDestroy(pattern);
1673 
1674 	cairo_set_font_face (m_cr, cairo_face);
1675 	cairo_set_font_size(m_cr, m_fontSize);
1676 
1677 	cairo_font_options_t* options = cairo_font_options_create();
1678 	cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_SLIGHT);
1679 	cairo_set_font_options(m_cr, options);
1680 	cairo_font_options_destroy(options);
1681 
1682 	cairo_font_extents (m_cr, &extentsF);
1683 	QStringList textList = text.split("\n");
1684 	for (int a = 0; a < textList.count(); ++a)
1685 	{
1686 		cairo_text_extents (m_cr, textList[a].toUtf8(), &extents);
1687 		if (align == 0)
1688 			x = qMin(area.center().x() - (extents.width / 2.0 + extents.x_bearing), x);
1689 		ww = qMax(ww, extents.width);
1690 	}
1691 	hh = extentsF.height * textList.count();
1692 	if ((align == 0) || (align == 1))
1693 		y = area.center().y() - ((extentsF.height * textList.count()) / 2.0);
1694 	if (filled)
1695 	{
1696 		m_fill.getRgbF(&r, &g, &b);
1697 		cairo_set_source_rgba(m_cr, r, g, b, m_fill_trans);
1698 		cairo_new_path(m_cr);
1699 		cairo_rectangle(m_cr, x, y, ww, hh);
1700 		cairo_fill(m_cr);
1701 	}
1702 	cairo_new_path(m_cr);
1703 	y += extentsF.ascent;
1704 	cairo_move_to (m_cr, x, y);
1705 	m_stroke.getRgbF(&r, &g, &b);
1706 	cairo_set_source_rgba(m_cr, r, g, b, m_stroke_trans);
1707 
1708 	QVector<cairo_glyph_t> cairoGlyphs;
1709 
1710 	for (int a = 0; a < textList.count(); ++a)
1711 	{
1712 		CharStyle style(m_font, m_fontSize * 10);
1713 		StoryText story;
1714 		story.insertChars(textList[a]);
1715 		story.setCharStyle(0, textList[a].count(), style);
1716 
1717 		TextShaper textShaper(story, 0);
1718 		const QList<GlyphCluster> glyphRuns = textShaper.shape(0, story.length()).glyphs();
1719 
1720 		double tmpx = x;
1721 		for (const GlyphCluster &run : glyphRuns)
1722 		{
1723 			for (const GlyphLayout &gl : run.glyphs())
1724 			{
1725 				cairo_glyph_t glyph;
1726 				glyph.index = gl.glyph;
1727 				glyph.x = tmpx + gl.xoffset;
1728 				glyph.y = y - gl.yoffset;
1729 				tmpx += gl.xadvance;
1730 				cairoGlyphs.append(glyph);
1731 			}
1732 		}
1733 		y += extentsF.height;
1734 	}
1735 
1736 	cairo_show_glyphs(m_cr, cairoGlyphs.data(), cairoGlyphs.count());
1737 #else
1738 	cairo_select_font_face(m_cr, m_font.family().toLatin1(), m_font.isItalic() ? CAIRO_FONT_SLANT_ITALIC : CAIRO_FONT_SLANT_NORMAL, m_font.isBold() ? CAIRO_FONT_WEIGHT_BOLD : CAIRO_FONT_WEIGHT_NORMAL);
1739 	cairo_set_font_size(m_cr, m_fontSize);
1740 	cairo_font_extents (m_cr, &extentsF);
1741 	QStringList textList = text.split("\n");
1742 	for (int a = 0; a < textList.count(); ++a)
1743 	{
1744 		cairo_text_extents (m_cr, textList[a].toUtf8(), &extents);
1745 		if (align == 0)
1746 			x = qMin(area.center().x() - (extents.width / 2.0 + extents.x_bearing), x);
1747 		ww = qMax(ww, extents.width);
1748 	}
1749 	hh = extentsF.height * textList.count();
1750 	if ((align == 0) || (align == 1))
1751 		y = area.center().y() - ((extentsF.height * textList.count()) / 2.0);
1752 	if (filled)
1753 	{
1754 		m_fill.getRgbF(&r, &g, &b);
1755 		cairo_set_source_rgba(m_cr, r, g, b, m_fill_trans);
1756 		cairo_new_path(m_cr);
1757 		cairo_rectangle(m_cr, x, y, ww, hh);
1758 		cairo_fill(m_cr);
1759 	}
1760 	cairo_new_path(m_cr);
1761 	y += extentsF.ascent;
1762 	cairo_move_to (m_cr, x, y);
1763 	m_stroke.getRgbF(&r, &g, &b);
1764 	cairo_set_source_rgba(m_cr, r, g, b, m_stroke_trans);
1765 	for (int a = 0; a < textList.count(); ++a)
1766 	{
1767 		cairo_show_text (m_cr, textList[a].toUtf8());
1768 		y += extentsF.height;
1769 		cairo_move_to (m_cr, x, y);
1770 	}
1771 #endif
1772 }
1773 
drawShadeCircle(const QRectF & re,const QColor & color,bool sunken,int lineWidth)1774 void ScPainter::drawShadeCircle(const QRectF &re, const QColor& color, bool sunken, int lineWidth)
1775 {
1776 	setStrokeMode(ScPainter::Solid);
1777 	double bezierCircle = 0.55228475;
1778 	double dx = re.width();
1779 	double dy = re.height();
1780 	double cx = dx / 2.0;
1781 	double cy = dy / 2.0;
1782 	double rb = 0.5 * (dx < dy ? dx : dy);
1783 	double r = rb - 0.25 * lineWidth;
1784 	QColor shade;
1785 	shade.setRgbF(color.redF() * 0.5, color.greenF() * 0.5, color.blueF() * 0.5);
1786 	QColor light;
1787 	light.setRgbF(color.redF() * 0.5 + 0.5, color.greenF() * 0.5 + 0.5, color.blueF() * 0.5 + 0.5);
1788 	cairo_save(m_cr);
1789 	translate(0, dy);
1790 	scale(1, -1);
1791 	setPen(color, lineWidth * 0.5, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
1792 	newPath();
1793 	moveTo(cx + r, cy);
1794 	cairo_curve_to(m_cr, cx + r, cy + bezierCircle * r, cx + bezierCircle * r, cy + r, cx, cy + r);
1795 	cairo_curve_to(m_cr, cx - bezierCircle * r, cy + r, cx - r, cy + bezierCircle * r, cx - r, cy);
1796 	cairo_curve_to(m_cr, cx - r, cy - bezierCircle * r, cx - bezierCircle * r, cy - r, cx, cy - r);
1797 	cairo_curve_to(m_cr, cx + bezierCircle * r, cy - r, cx + r, cy - bezierCircle * r, cx + r, cy);
1798 	strokePath();
1799 	r = rb - 0.73 * lineWidth;
1800 	double r2 = r / 1.414213562;
1801 	if (sunken)
1802 		setPen(shade, lineWidth * 0.5, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
1803 	else
1804 		setPen(light, lineWidth * 0.5, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
1805 	newPath();
1806 	moveTo(cx + r2, cy + r2);
1807 	cairo_curve_to(m_cr, cx + (1 - bezierCircle) * r2, cy + (1 + bezierCircle) * r2, cx - (1 - bezierCircle) * r2, cy + (1 + bezierCircle) * r2, cx - r2, cy + r2);
1808 	cairo_curve_to(m_cr, cx - (1 + bezierCircle) * r2, cy + (1 - bezierCircle) * r2, cx - (1 + bezierCircle) * r2, cy - (1 - bezierCircle) * r2, cx - r2, cy - r2);
1809 	strokePath();
1810 	if (sunken)
1811 		setPen(light, lineWidth * 0.5, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
1812 	else
1813 		setPen(shade, lineWidth * 0.5, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
1814 	newPath();
1815 	moveTo(cx - r2, cy - r2);
1816 	cairo_curve_to(m_cr, cx - (1 - bezierCircle) * r2, cy - (1 + bezierCircle) * r2, cx + (1 - bezierCircle) * r2, cy - (1 + bezierCircle) * r2, cx + r2, cy - r2);
1817 	cairo_curve_to(m_cr, cx + (1 + bezierCircle) * r2, cy - (1 - bezierCircle) * r2, cx + (1 + bezierCircle) * r2, cy + (1 - bezierCircle) * r2, cx + r2, cy + r2);
1818 	strokePath();
1819 	cairo_restore(m_cr);
1820 }
1821 
drawShadePanel(const QRectF & r,const QColor & color,bool sunken,int lineWidth)1822 void ScPainter::drawShadePanel(const QRectF &r, const QColor& color, bool sunken, int lineWidth)
1823 {
1824 	QColor shade;
1825 	QColor light;
1826 	if (sunken)
1827 	{
1828 		shade.setRgbF(color.redF() * 0.5, color.greenF() * 0.5, color.blueF() * 0.5);
1829 		light.setRgbF(color.redF() * 0.5 + 0.5, color.greenF() * 0.5 + 0.5, color.blueF() * 0.5 + 0.5);
1830 	}
1831 	else
1832 	{
1833 		light.setRgbF(color.redF() * 0.5, color.greenF() * 0.5, color.blueF() * 0.5);
1834 		shade.setRgbF(color.redF() * 0.5 + 0.5, color.greenF() * 0.5 + 0.5, color.blueF() * 0.5 + 0.5);
1835 	}
1836 	double x1, y1, y2, x3;
1837 	x1 = r.x();
1838 	x3 = r.x() + r.width();
1839 	y1 = r.y() + r.height();
1840 	y2 = r.y();
1841 	setFillMode(ScPainter::Solid);
1842 	newPath();
1843 	moveTo(x1, y1);
1844 	lineTo(x1, y2);
1845 	lineTo(x3, y2);
1846 	lineTo(x3 - lineWidth, lineWidth);
1847 	lineTo(lineWidth, lineWidth);
1848 	lineTo(lineWidth, y1 - lineWidth);
1849 	closePath();
1850 	setBrush(shade);
1851 	fillPath();
1852 	newPath();
1853 	moveTo(x1, y1);
1854 	lineTo(x3, y1);
1855 	lineTo(x3, y2);
1856 	lineTo(x3 - lineWidth, lineWidth);
1857 	lineTo(x3 - lineWidth, y1 - lineWidth);
1858 	lineTo(lineWidth, y1 - lineWidth);
1859 	closePath();
1860 	setBrush(light);
1861 	fillPath();
1862 }
1863 
drawUnderlinedRect(const QRectF & r,const QColor & color,int lineWidth)1864 void ScPainter::drawUnderlinedRect(const QRectF &r, const QColor& color, int lineWidth)
1865 {
1866 	setPen(color, lineWidth, Qt::DashLine, Qt::FlatCap, Qt::MiterJoin);
1867 	setStrokeMode(ScPainter::Solid);
1868 	drawLine(r.bottomLeft(), r.topLeft());
1869 	drawLine(r.topLeft(), r.topRight());
1870 	drawLine(r.bottomRight(), r.topRight());
1871 
1872 	setPen(color, lineWidth, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
1873 	drawLine(r.bottomLeft(), r.bottomRight());
1874 }
1875 
colorizeAlpha(const QColor & color)1876 void ScPainter::colorizeAlpha(const QColor& color)
1877 {
1878 	cairo_surface_t *data = cairo_get_group_target(m_cr);
1879 	cairo_surface_flush(data);
1880 	int w   = cairo_image_surface_get_width(data);
1881 	int h   = cairo_image_surface_get_height(data);
1882 	int stride = cairo_image_surface_get_stride(data);
1883 	unsigned char *d = cairo_image_surface_get_data(data);
1884 	int cr = color.red();
1885 	int cg = color.green();
1886 	int cb = color.blue();
1887 	for (int y = 0; y < h; ++y)
1888 	{
1889 		QRgb *dst = (QRgb*)d;
1890 		for (int x = 0; x < w; ++x)
1891 		{
1892 			if (qAlpha(*dst) > 0)
1893 				*dst = qRgba(cr, cg, cb, qAlpha(*dst));
1894 			dst++;
1895 		}
1896 		d += stride;
1897 	}
1898 	cairo_surface_mark_dirty(data);
1899 }
1900 
colorize(const QColor & color)1901 void ScPainter::colorize(const QColor& color)
1902 {
1903 	cairo_surface_t *data = cairo_get_group_target(m_cr);
1904 	cairo_surface_flush(data);
1905 	int w   = cairo_image_surface_get_width(data);
1906 	int h   = cairo_image_surface_get_height(data);
1907 	int stride = cairo_image_surface_get_stride(data);
1908 	unsigned char *d = cairo_image_surface_get_data(data);
1909 	int cr = color.red();
1910 	int cg = color.green();
1911 	int cb = color.blue();
1912 	int hu, sa, v;
1913 	int cc2, cm2, cy2, k2;
1914 	QColor tmpR;
1915 	for (int y = 0; y < h; ++y)
1916 	{
1917 		QRgb *dst = (QRgb*)d;
1918 		for (int x = 0; x < w; ++x)
1919 		{
1920 			if (qAlpha(*dst) > 0)
1921 			{
1922 				k2 = 255 - qMin(qRound(0.3 * qRed(*dst) + 0.59 * qGreen(*dst) + 0.11 * qBlue(*dst)), 255);
1923 				tmpR.setRgb(cr, cg, cb);
1924 				tmpR.getHsv(&hu, &sa, &v);
1925 				tmpR.setHsv(hu, sa * k2 / 255, 255 - ((255 - v) * k2 / 255));
1926 				tmpR.getRgb(&cc2, &cm2, &cy2);
1927 				*dst = qRgba(cc2, cm2, cy2, qAlpha(*dst));
1928 			}
1929 			dst++;
1930 		}
1931 		d += stride;
1932 	}
1933 	cairo_surface_mark_dirty(data);
1934 }
1935 
blurAlpha(int radius)1936 void ScPainter::blurAlpha(int radius)
1937 {
1938 	if (radius < 1)
1939 		return;
1940 	cairo_surface_t *data = cairo_get_group_target(m_cr);
1941 	QRgb *pix = (QRgb*)cairo_image_surface_get_data(data);
1942 	int w   = cairo_image_surface_get_width(data);
1943 	int h   = cairo_image_surface_get_height(data);
1944 	int wm  = w-1;
1945 	int hm  = h-1;
1946 	int wh  = w*h;
1947 	int div = radius+radius+1;
1948 	int *a = new int[wh];
1949 	int asum, x, y, i, yp, yi, yw;
1950 	QRgb p;
1951 	int *vmin = new int[qMax(w,h)];
1952 	int divsum = (div+1)>>1;
1953 	divsum *= divsum;
1954 	int *dv = new int[256*divsum];
1955 	for (i = 0; i < 256 * divsum; ++i)
1956 	{
1957 		dv[i] = (i / divsum);
1958 	}
1959 	yw = yi = 0;
1960 	int **stack = new int*[div];
1961 	for (int i = 0; i < div; ++i)
1962 	{
1963 		stack[i] = new int[1];
1964 	}
1965 	int stackpointer;
1966 	int stackstart;
1967 	int *sir;
1968 	int rbs;
1969 	int r1 = radius+1;
1970 	int aoutsum;
1971 	int ainsum;
1972 	for (y = 0; y < h; ++y)
1973 	{
1974 		ainsum = aoutsum = asum = 0;
1975 		for (i = -radius; i <= radius; ++i)
1976 		{
1977 			p = pix[yi+qMin(wm,qMax(i,0))];
1978 			sir = stack[i+radius];
1979 			sir[0] = qAlpha(p);
1980 			rbs = r1-abs(i);
1981 			asum += sir[0]*rbs;
1982 			if (i > 0)
1983 				ainsum += sir[0];
1984 			else
1985 				aoutsum += sir[0];
1986 		}
1987 		stackpointer = radius;
1988 		for (x = 0; x < w; ++x)
1989 		{
1990 			a[yi] = dv[asum];
1991 			asum -= aoutsum;
1992 			stackstart = stackpointer-radius+div;
1993 			sir = stack[stackstart % div];
1994 			aoutsum -= sir[0];
1995 			if (y == 0)
1996 				vmin[x] = qMin(x + radius + 1, wm);
1997 			p = pix[yw + vmin[x]];
1998 			sir[0] = qAlpha(p);
1999 			ainsum += sir[0];
2000 			asum += ainsum;
2001 			stackpointer = (stackpointer + 1) % div;
2002 			sir = stack[(stackpointer) % div];
2003 			aoutsum += sir[0];
2004 			ainsum -= sir[0];
2005 			++yi;
2006 		}
2007 		yw += w;
2008 	}
2009 	for (x = 0; x < w; ++x)
2010 	{
2011 		ainsum = aoutsum = asum = 0;
2012 		yp = -radius * w;
2013 		for (i = -radius; i <= radius; ++i)
2014 		{
2015 			yi = qMax(0, yp) + x;
2016 			sir = stack[i + radius];
2017 			sir[0] = a[yi];
2018 			rbs = r1 - abs(i);
2019 			asum += a[yi] * rbs;
2020 			if (i > 0)
2021 				ainsum += sir[0];
2022 			else
2023 				aoutsum += sir[0];
2024 			if (i < hm)
2025 			{
2026 				yp += w;
2027 			}
2028 		}
2029 		yi = x;
2030 		stackpointer = radius;
2031 		for (y = 0; y < h; ++y)
2032 		{
2033 			pix[yi] = qRgba(qRed(pix[yi]), qGreen(pix[yi]), qBlue(pix[yi]), dv[asum]);
2034 			asum -= aoutsum;
2035 			stackstart = stackpointer - radius + div;
2036 			sir = stack[stackstart%div];
2037 			aoutsum -= sir[0];
2038 			if (x == 0)
2039 			{
2040 				vmin[y] = qMin(y + r1, hm) * w;
2041 			}
2042 			p = x + vmin[y];
2043 			sir[0] = a[p];
2044 			ainsum += sir[0];
2045 			asum += ainsum;
2046 			stackpointer = (stackpointer+1)%div;
2047 			sir = stack[stackpointer];
2048 			aoutsum += sir[0];
2049 			ainsum -= sir[0];
2050 			yi += w;
2051 		}
2052 	}
2053 	delete [] a;
2054 	delete [] vmin;
2055 	delete [] dv;
2056 	for (int i = 0; i < div; ++i)
2057 	{
2058 		delete [] stack[i];
2059 	}
2060 	delete [] stack;
2061 	cairo_surface_mark_dirty(data);
2062 }
2063 
blur(int radius)2064 void ScPainter::blur(int radius)
2065 {
2066 	if (radius < 1)
2067 		return;
2068 	cairo_surface_t *data = cairo_get_group_target(m_cr);
2069 	QRgb *pix = (QRgb*)cairo_image_surface_get_data(data);
2070 	int w   = cairo_image_surface_get_width(data);
2071 	int h   = cairo_image_surface_get_height(data);
2072 	int wm  = w-1;
2073 	int hm  = h-1;
2074 	int wh  = w*h;
2075 	int div = radius+radius+1;
2076 	int *r = new int[wh];
2077 	int *g = new int[wh];
2078 	int *b = new int[wh];
2079 	int *a = new int[wh];
2080 	int rsum, gsum, bsum, asum, x, y, i, yp, yi, yw;
2081 	QRgb p;
2082 	int *vmin = new int[qMax(w,h)];
2083 	int divsum = (div+1)>>1;
2084 	divsum *= divsum;
2085 	int *dv = new int[256*divsum];
2086 	for (i = 0; i < 256 * divsum; ++i)
2087 	{
2088 		dv[i] = (i / divsum);
2089 	}
2090 	yw = yi = 0;
2091 	int **stack = new int*[div];
2092 	for (int i = 0; i < div; ++i)
2093 	{
2094 		stack[i] = new int[4];
2095 	}
2096 	int stackpointer;
2097 	int stackstart;
2098 	int *sir;
2099 	int rbs;
2100 	int r1 = radius+1;
2101 	int routsum, goutsum, boutsum, aoutsum;
2102 	int rinsum, ginsum, binsum, ainsum;
2103 	for (y = 0; y < h; ++y)
2104 	{
2105 		rinsum = ginsum = binsum = ainsum = routsum = goutsum = boutsum = aoutsum = rsum = gsum = bsum = asum = 0;
2106 		for (i = -radius; i <= radius; ++i)
2107 		{
2108 			p = pix[yi + qMin(wm, qMax(i, 0))];
2109 			sir = stack[i + radius];
2110 			sir[0] = qRed(p);
2111 			sir[1] = qGreen(p);
2112 			sir[2] = qBlue(p);
2113 			sir[3] = qAlpha(p);
2114 			rbs = r1 - abs(i);
2115 			rsum += sir[0] * rbs;
2116 			gsum += sir[1] * rbs;
2117 			bsum += sir[2] * rbs;
2118 			asum += sir[3] * rbs;
2119 			if (i > 0)
2120 			{
2121 				rinsum += sir[0];
2122 				ginsum += sir[1];
2123 				binsum += sir[2];
2124 				ainsum += sir[3];
2125 			}
2126 			else
2127 			{
2128 				routsum += sir[0];
2129 				goutsum += sir[1];
2130 				boutsum += sir[2];
2131 				aoutsum += sir[3];
2132 			}
2133 		}
2134 		stackpointer = radius;
2135 		for (x = 0; x < w; ++x)
2136 		{
2137 			r[yi] = dv[rsum];
2138 			g[yi] = dv[gsum];
2139 			b[yi] = dv[bsum];
2140 			a[yi] = dv[asum];
2141 			rsum -= routsum;
2142 			gsum -= goutsum;
2143 			bsum -= boutsum;
2144 			asum -= aoutsum;
2145 			stackstart = stackpointer - radius + div;
2146 			sir = stack[stackstart % div];
2147 			routsum -= sir[0];
2148 			goutsum -= sir[1];
2149 			boutsum -= sir[2];
2150 			aoutsum -= sir[3];
2151 			if (y == 0)
2152 			{
2153 				vmin[x] = qMin(x + radius + 1,wm);
2154 			}
2155 			p = pix[yw + vmin[x]];
2156 			sir[0] = qRed(p);
2157 			sir[1] = qGreen(p);
2158 			sir[2] = qBlue(p);
2159 			sir[3] = qAlpha(p);
2160 			rinsum += sir[0];
2161 			ginsum += sir[1];
2162 			binsum += sir[2];
2163 			ainsum += sir[3];
2164 			rsum += rinsum;
2165 			gsum += ginsum;
2166 			bsum += binsum;
2167 			asum += ainsum;
2168 			stackpointer = (stackpointer+1)%div;
2169 			sir = stack[(stackpointer)%div];
2170 			routsum += sir[0];
2171 			goutsum += sir[1];
2172 			boutsum += sir[2];
2173 			aoutsum += sir[3];
2174 			rinsum -= sir[0];
2175 			ginsum -= sir[1];
2176 			binsum -= sir[2];
2177 			ainsum -= sir[3];
2178 			++yi;
2179 		}
2180 		yw += w;
2181 	}
2182 	for (x = 0; x < w; ++x)
2183 	{
2184 		rinsum = ginsum = binsum = ainsum = routsum = goutsum = boutsum = aoutsum = rsum = gsum = bsum = asum = 0;
2185 		yp =- radius * w;
2186 		for (i = -radius; i <= radius; ++i)
2187 		{
2188 			yi = qMax(0, yp) + x;
2189 			sir = stack[i + radius];
2190 			sir[0] = r[yi];
2191 			sir[1] = g[yi];
2192 			sir[2] = b[yi];
2193 			sir[3] = a[yi];
2194 			rbs = r1-abs(i);
2195 			rsum += r[yi] * rbs;
2196 			gsum += g[yi] * rbs;
2197 			bsum += b[yi] * rbs;
2198 			asum += a[yi] * rbs;
2199 			if (i > 0)
2200 			{
2201 				rinsum += sir[0];
2202 				ginsum += sir[1];
2203 				binsum += sir[2];
2204 				ainsum += sir[3];
2205 			}
2206 			else
2207 			{
2208 				routsum += sir[0];
2209 				goutsum += sir[1];
2210 				boutsum += sir[2];
2211 				aoutsum += sir[3];
2212 			}
2213 			if (i < hm)
2214 			{
2215 				yp += w;
2216 			}
2217 		}
2218 		yi = x;
2219 		stackpointer = radius;
2220 		for (y = 0; y < h; ++y)
2221 		{
2222 			pix[yi] = qRgba(dv[rsum], dv[gsum], dv[bsum], dv[asum]);
2223 			rsum -= routsum;
2224 			gsum -= goutsum;
2225 			bsum -= boutsum;
2226 			asum -= aoutsum;
2227 			stackstart = stackpointer - radius + div;
2228 			sir = stack[stackstart % div];
2229 			routsum -= sir[0];
2230 			goutsum -= sir[1];
2231 			boutsum -= sir[2];
2232 			aoutsum -= sir[3];
2233 			if (x == 0)
2234 			{
2235 				vmin[y] = qMin(y + r1, hm) * w;
2236 			}
2237 			p = x + vmin[y];
2238 			sir[0] = r[p];
2239 			sir[1] = g[p];
2240 			sir[2] = b[p];
2241 			sir[3] = a[p];
2242 			rinsum += sir[0];
2243 			ginsum += sir[1];
2244 			binsum += sir[2];
2245 			ainsum += sir[3];
2246 			rsum += rinsum;
2247 			gsum += ginsum;
2248 			bsum += binsum;
2249 			asum += ainsum;
2250 			stackpointer = (stackpointer + 1) % div;
2251 			sir = stack[stackpointer];
2252 			routsum += sir[0];
2253 			goutsum += sir[1];
2254 			boutsum += sir[2];
2255 			aoutsum += sir[3];
2256 			rinsum -= sir[0];
2257 			ginsum -= sir[1];
2258 			binsum -= sir[2];
2259 			ainsum -= sir[3];
2260 			yi += w;
2261 		}
2262 	}
2263 	delete [] r;
2264 	delete [] g;
2265 	delete [] b;
2266 	delete [] a;
2267 	delete [] vmin;
2268 	delete [] dv;
2269 	for (int i = 0; i < div; ++i)
2270 	{
2271 		delete [] stack[i];
2272 	}
2273 	delete [] stack;
2274 	cairo_surface_mark_dirty(data);
2275 }
2276