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