1 /*
2 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #include "config.h"
21 #include "PainterOpenVG.h"
22
23 #include "AffineTransform.h"
24 #include "Color.h"
25 #include "DashArray.h"
26 #include "FloatPoint.h"
27 #include "FloatQuad.h"
28 #include "FloatRect.h"
29 #include "IntRect.h"
30 #include "IntSize.h"
31 #include "NotImplemented.h"
32 #include "PlatformPathOpenVG.h"
33 #include "SurfaceOpenVG.h"
34 #include "TiledImageOpenVG.h"
35 #include "VGUtils.h"
36
37 #if PLATFORM(EGL)
38 #include "EGLUtils.h"
39 #endif
40
41 #include <vgu.h>
42
43 #include <wtf/Assertions.h>
44 #include <wtf/MathExtras.h>
45
46 namespace WebCore {
47
isNonRotatedAffineTransformation(const AffineTransform & t)48 static bool isNonRotatedAffineTransformation(const AffineTransform& t)
49 {
50 return t.b() <= FLT_EPSILON && t.c() <= FLT_EPSILON;
51 }
52
toVGCapStyle(LineCap lineCap)53 static VGCapStyle toVGCapStyle(LineCap lineCap)
54 {
55 switch (lineCap) {
56 case RoundCap:
57 return VG_CAP_ROUND;
58 case SquareCap:
59 return VG_CAP_SQUARE;
60 case ButtCap:
61 default:
62 return VG_CAP_BUTT;
63 }
64 }
65
toVGJoinStyle(LineJoin lineJoin)66 static VGJoinStyle toVGJoinStyle(LineJoin lineJoin)
67 {
68 switch (lineJoin) {
69 case RoundJoin:
70 return VG_JOIN_ROUND;
71 case BevelJoin:
72 return VG_JOIN_BEVEL;
73 case MiterJoin:
74 default:
75 return VG_JOIN_MITER;
76 }
77 }
78
toVGFillRule(WindRule fillRule)79 static VGFillRule toVGFillRule(WindRule fillRule)
80 {
81 return fillRule == RULE_EVENODD ? VG_EVEN_ODD : VG_NON_ZERO;
82 }
83
colorToVGColor(const Color & color)84 static VGuint colorToVGColor(const Color& color)
85 {
86 VGuint vgColor = color.red();
87 vgColor = (vgColor << 8) | color.green();
88 vgColor = (vgColor << 8) | color.blue();
89 vgColor = (vgColor << 8) | color.alpha();
90 return vgColor;
91 }
92
setVGSolidColor(VGPaintMode paintMode,const Color & color)93 static void setVGSolidColor(VGPaintMode paintMode, const Color& color)
94 {
95 VGPaint paint = vgCreatePaint();
96 vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
97 vgSetColor(paint, colorToVGColor(color));
98 vgSetPaint(paint, paintMode);
99 vgDestroyPaint(paint);
100 ASSERT_VG_NO_ERROR();
101 }
102
103
104 struct PlatformPainterState {
105 AffineTransform surfaceTransformation;
106 CompositeOperator compositeOperation;
107 float opacity;
108
109 bool scissoringEnabled;
110 FloatRect scissorRect;
111 #ifdef OPENVG_VERSION_1_1
112 bool maskingChangedAndEnabled;
113 VGMaskLayer mask;
114 #endif
115
116 Color fillColor;
117 StrokeStyle strokeStyle;
118 Color strokeColor;
119 float strokeThickness;
120 LineCap strokeLineCap;
121 LineJoin strokeLineJoin;
122 float strokeMiterLimit;
123 DashArray strokeDashArray;
124 float strokeDashOffset;
125
126 TextDrawingModeFlags textDrawingMode;
127 bool antialiasingEnabled;
128
PlatformPainterStateWebCore::PlatformPainterState129 PlatformPainterState()
130 : compositeOperation(CompositeSourceOver)
131 , opacity(1.0)
132 , scissoringEnabled(false)
133 #ifdef OPENVG_VERSION_1_1
134 , maskingChangedAndEnabled(false)
135 , mask(VG_INVALID_HANDLE)
136 #endif
137 , fillColor(Color::black)
138 , strokeStyle(NoStroke)
139 , strokeThickness(0.0)
140 , strokeLineCap(ButtCap)
141 , strokeLineJoin(MiterJoin)
142 , strokeMiterLimit(4.0)
143 , strokeDashOffset(0.0)
144 , textDrawingMode(TextModeFill)
145 , antialiasingEnabled(true)
146 {
147 }
148
~PlatformPainterStateWebCore::PlatformPainterState149 ~PlatformPainterState()
150 {
151 #ifdef OPENVG_VERSION_1_1
152 if (maskingChangedAndEnabled && mask != VG_INVALID_HANDLE) {
153 vgDestroyMaskLayer(mask);
154 ASSERT_VG_NO_ERROR();
155 mask = VG_INVALID_HANDLE;
156 }
157 #endif
158 }
159
PlatformPainterStateWebCore::PlatformPainterState160 PlatformPainterState(const PlatformPainterState& state)
161 {
162 surfaceTransformation = state.surfaceTransformation;
163
164 scissoringEnabled = state.scissoringEnabled;
165 scissorRect = state.scissorRect;
166 #ifdef OPENVG_VERSION_1_1
167 maskingChangedAndEnabled = false;
168 mask = state.mask;
169 #endif
170 copyPaintState(&state);
171 }
172
maskingEnabledWebCore::PlatformPainterState173 inline bool maskingEnabled()
174 {
175 return maskingChangedAndEnabled || mask != VG_INVALID_HANDLE;
176 }
177
copyPaintStateWebCore::PlatformPainterState178 void copyPaintState(const PlatformPainterState* other)
179 {
180 compositeOperation = other->compositeOperation;
181 opacity = other->opacity;
182
183 fillColor = other->fillColor;
184 strokeStyle = other->strokeStyle;
185 strokeColor = other->strokeColor;
186 strokeThickness = other->strokeThickness;
187 strokeLineCap = other->strokeLineCap;
188 strokeLineJoin = other->strokeLineJoin;
189 strokeMiterLimit = other->strokeMiterLimit;
190 strokeDashArray = other->strokeDashArray;
191 strokeDashOffset = other->strokeDashOffset;
192
193 textDrawingMode = other->textDrawingMode;
194 antialiasingEnabled = other->antialiasingEnabled;
195 }
196
applyStateWebCore::PlatformPainterState197 void applyState(PainterOpenVG* painter)
198 {
199 ASSERT(painter);
200
201 setVGSolidColor(VG_FILL_PATH, fillColor);
202 setVGSolidColor(VG_STROKE_PATH, strokeColor);
203
204 vgSetf(VG_STROKE_LINE_WIDTH, strokeThickness);
205 vgSeti(VG_STROKE_CAP_STYLE, toVGCapStyle(strokeLineCap));
206 vgSeti(VG_STROKE_JOIN_STYLE, toVGJoinStyle(strokeLineJoin));
207 vgSetf(VG_STROKE_MITER_LIMIT, strokeMiterLimit);
208
209 if (antialiasingEnabled)
210 vgSeti(VG_RENDERING_QUALITY, VG_RENDERING_QUALITY_FASTER);
211 else
212 vgSeti(VG_RENDERING_QUALITY, VG_RENDERING_QUALITY_NONANTIALIASED);
213
214 applyBlending(painter);
215 applyStrokeStyle();
216
217 applyTransformation(painter);
218 applyScissorRect();
219
220 #ifdef OPENVG_VERSION_1_1
221 if (maskingEnabled()) {
222 vgSeti(VG_MASKING, VG_TRUE);
223 if (mask != VG_INVALID_HANDLE)
224 vgMask(mask, VG_SET_MASK, 0, 0, painter->surface()->width(), painter->surface()->height());
225 } else
226 vgSeti(VG_MASKING, VG_FALSE);
227 #endif
228 ASSERT_VG_NO_ERROR();
229 }
230
applyBlendingWebCore::PlatformPainterState231 void applyBlending(PainterOpenVG* painter)
232 {
233 VGBlendMode blendMode = VG_BLEND_SRC_OVER;
234
235 switch (compositeOperation) {
236 case CompositeClear: {
237 // Clear means "set to fully transparent regardless of SRC".
238 // We implement that by multiplying DST with white color
239 // (= no changes) and an alpha of 1.0 - opacity, so the destination
240 // pixels will be fully transparent when opacity == 1.0 and
241 // unchanged when opacity == 0.0.
242 blendMode = VG_BLEND_DST_IN;
243 const VGfloat values[] = { 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 - opacity };
244 vgSetfv(VG_COLOR_TRANSFORM_VALUES, 8, values);
245 vgSeti(VG_COLOR_TRANSFORM, VG_TRUE);
246 ASSERT_VG_NO_ERROR();
247 break;
248 }
249 case CompositeCopy:
250 blendMode = VG_BLEND_SRC;
251 break;
252 case CompositeSourceOver:
253 blendMode = VG_BLEND_SRC_OVER;
254 break;
255 case CompositeSourceIn:
256 blendMode = VG_BLEND_SRC_IN;
257 break;
258 case CompositeSourceOut:
259 notImplemented();
260 break;
261 case CompositeSourceAtop:
262 notImplemented();
263 break;
264 case CompositeDestinationOver:
265 blendMode = VG_BLEND_DST_OVER;
266 break;
267 case CompositeDestinationIn:
268 blendMode = VG_BLEND_DST_IN;
269 break;
270 case CompositeDestinationOut:
271 notImplemented();
272 break;
273 case CompositeDestinationAtop:
274 notImplemented();
275 break;
276 case CompositeXOR:
277 notImplemented();
278 break;
279 case CompositePlusDarker:
280 blendMode = VG_BLEND_DARKEN;
281 break;
282 case CompositeHighlight:
283 notImplemented();
284 break;
285 case CompositePlusLighter:
286 blendMode = VG_BLEND_LIGHTEN;
287 break;
288 }
289
290 if (compositeOperation != CompositeClear) {
291 if (opacity >= (1.0 - FLT_EPSILON))
292 vgSeti(VG_COLOR_TRANSFORM, VG_FALSE);
293 else if (blendMode == VG_BLEND_SRC) {
294 blendMode = VG_BLEND_SRC_OVER;
295 VGfloat values[] = { 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, opacity };
296 vgSetfv(VG_COLOR_TRANSFORM_VALUES, 8, values);
297 vgSeti(VG_COLOR_TRANSFORM, VG_TRUE);
298 } else {
299 VGfloat values[] = { 1.0, 1.0, 1.0, opacity, 0.0, 0.0, 0.0, 0.0 };
300 vgSetfv(VG_COLOR_TRANSFORM_VALUES, 8, values);
301 vgSeti(VG_COLOR_TRANSFORM, VG_TRUE);
302 }
303 ASSERT_VG_NO_ERROR();
304 }
305
306 vgSeti(VG_BLEND_MODE, blendMode);
307 ASSERT_VG_NO_ERROR();
308 }
309
applyTransformationWebCore::PlatformPainterState310 void applyTransformation(PainterOpenVG* painter)
311 {
312 // There are *five* separate transforms that can be applied to OpenVG as of 1.1
313 // but it is not clear that we need to set them separately. Instead we set them
314 // all right here and let this be a call to essentially set the world transformation!
315 VGMatrix vgMatrix(surfaceTransformation);
316 const VGfloat* vgFloatArray = vgMatrix.toVGfloat();
317
318 vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
319 vgLoadMatrix(vgFloatArray);
320 ASSERT_VG_NO_ERROR();
321
322 vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
323 vgLoadMatrix(vgFloatArray);
324 ASSERT_VG_NO_ERROR();
325
326 #ifdef OPENVG_VERSION_1_1
327 vgSeti(VG_MATRIX_MODE, VG_MATRIX_GLYPH_USER_TO_SURFACE);
328 vgLoadMatrix(vgFloatArray);
329 ASSERT_VG_NO_ERROR();
330 #endif
331 }
332
applyScissorRectWebCore::PlatformPainterState333 void applyScissorRect()
334 {
335 if (scissoringEnabled) {
336 vgSeti(VG_SCISSORING, VG_TRUE);
337 vgSetfv(VG_SCISSOR_RECTS, 4, VGRect(scissorRect).toVGfloat());
338 } else
339 vgSeti(VG_SCISSORING, VG_FALSE);
340
341 ASSERT_VG_NO_ERROR();
342 }
343
applyStrokeStyleWebCore::PlatformPainterState344 void applyStrokeStyle()
345 {
346 if (strokeStyle == DottedStroke) {
347 VGfloat vgFloatArray[2] = { 1.0, 1.0 };
348 vgSetfv(VG_STROKE_DASH_PATTERN, 2, vgFloatArray);
349 vgSetf(VG_STROKE_DASH_PHASE, 0.0);
350 } else if (strokeStyle == DashedStroke) {
351 if (!strokeDashArray.size()) {
352 VGfloat vgFloatArray[2] = { 4.0, 3.0 };
353 vgSetfv(VG_STROKE_DASH_PATTERN, 2, vgFloatArray);
354 } else {
355 Vector<VGfloat> vgFloatArray(strokeDashArray.size());
356 for (int i = 0; i < strokeDashArray.size(); ++i)
357 vgFloatArray[i] = strokeDashArray[i];
358
359 vgSetfv(VG_STROKE_DASH_PATTERN, vgFloatArray.size(), vgFloatArray.data());
360 }
361 vgSetf(VG_STROKE_DASH_PHASE, strokeDashOffset);
362 } else {
363 vgSetfv(VG_STROKE_DASH_PATTERN, 0, 0);
364 vgSetf(VG_STROKE_DASH_PHASE, 0.0);
365 }
366
367 ASSERT_VG_NO_ERROR();
368 }
369
strokeDisabledWebCore::PlatformPainterState370 inline bool strokeDisabled() const
371 {
372 return (compositeOperation == CompositeSourceOver
373 && (strokeStyle == NoStroke || !strokeColor.alpha()));
374 }
375
fillDisabledWebCore::PlatformPainterState376 inline bool fillDisabled() const
377 {
378 return (compositeOperation == CompositeSourceOver && !fillColor.alpha());
379 }
380
saveMaskIfNecessaryWebCore::PlatformPainterState381 void saveMaskIfNecessary(PainterOpenVG* painter)
382 {
383 #ifdef OPENVG_VERSION_1_1
384 if (maskingChangedAndEnabled) {
385 if (mask != VG_INVALID_HANDLE) {
386 vgDestroyMaskLayer(mask);
387 ASSERT_VG_NO_ERROR();
388 }
389 mask = vgCreateMaskLayer(painter->surface()->width(), painter->surface()->height());
390 ASSERT(mask != VG_INVALID_HANDLE);
391 vgCopyMask(mask, 0, 0, 0, 0, painter->surface()->width(), painter->surface()->height());
392 ASSERT_VG_NO_ERROR();
393 }
394 #endif
395 }
396 };
397
398
PainterOpenVG()399 PainterOpenVG::PainterOpenVG()
400 : m_state(0)
401 , m_surface(0)
402 {
403 }
404
PainterOpenVG(SurfaceOpenVG * surface)405 PainterOpenVG::PainterOpenVG(SurfaceOpenVG* surface)
406 : m_state(0)
407 , m_surface(0)
408 {
409 ASSERT(surface);
410 begin(surface);
411 }
412
~PainterOpenVG()413 PainterOpenVG::~PainterOpenVG()
414 {
415 end();
416 }
417
begin(SurfaceOpenVG * surface)418 void PainterOpenVG::begin(SurfaceOpenVG* surface)
419 {
420 if (surface == m_surface)
421 return;
422
423 ASSERT(surface);
424 ASSERT(!m_state);
425
426 m_surface = surface;
427
428 m_stateStack.append(new PlatformPainterState());
429 m_state = m_stateStack.last();
430
431 m_surface->setActivePainter(this);
432 m_surface->makeCurrent();
433 }
434
end()435 void PainterOpenVG::end()
436 {
437 if (!m_surface)
438 return;
439
440 m_surface->setActivePainter(0);
441 m_surface = 0;
442
443 destroyPainterStates();
444 }
445
destroyPainterStates()446 void PainterOpenVG::destroyPainterStates()
447 {
448 PlatformPainterState* state = 0;
449 while (!m_stateStack.isEmpty()) {
450 state = m_stateStack.last();
451 m_stateStack.removeLast();
452 delete state;
453 }
454 m_state = 0;
455 }
456
457 // Called by friend SurfaceOpenVG, private otherwise.
applyState()458 void PainterOpenVG::applyState()
459 {
460 ASSERT(m_state);
461 m_state->applyState(this);
462 }
463
464 /**
465 * Copy the current back buffer image onto the surface.
466 *
467 * Call this method when all painting operations have been completed,
468 * otherwise the surface won't visibly change.
469 */
blitToSurface()470 void PainterOpenVG::blitToSurface()
471 {
472 ASSERT(m_state); // implies m_surface
473 m_surface->flush();
474 }
475
transformation() const476 AffineTransform PainterOpenVG::transformation() const
477 {
478 ASSERT(m_state);
479 return m_state->surfaceTransformation;
480 }
481
concatTransformation(const AffineTransform & transformation)482 void PainterOpenVG::concatTransformation(const AffineTransform& transformation)
483 {
484 ASSERT(m_state);
485 m_surface->makeCurrent();
486
487 // We do the multiplication ourself using WebCore's AffineTransform rather
488 // than offloading this to VG via vgMultMatrix() to keep things simple and
489 // so we can maintain state ourselves.
490 m_state->surfaceTransformation.multLeft(transformation);
491 m_state->applyTransformation(this);
492 }
493
setTransformation(const AffineTransform & transformation)494 void PainterOpenVG::setTransformation(const AffineTransform& transformation)
495 {
496 ASSERT(m_state);
497 m_surface->makeCurrent();
498
499 m_state->surfaceTransformation = transformation;
500 m_state->applyTransformation(this);
501 }
502
transformPath(VGPath dst,VGPath src,const AffineTransform & transformation)503 void PainterOpenVG::transformPath(VGPath dst, VGPath src, const AffineTransform& transformation)
504 {
505 vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
506
507 // Save the transform state
508 VGfloat currentMatrix[9];
509 vgGetMatrix(currentMatrix);
510 ASSERT_VG_NO_ERROR();
511
512 // Load the new transform
513 vgLoadMatrix(VGMatrix(transformation).toVGfloat());
514 ASSERT_VG_NO_ERROR();
515
516 // Apply the new transform
517 vgTransformPath(dst, src);
518 ASSERT_VG_NO_ERROR();
519
520 // Restore the transform state
521 vgLoadMatrix(currentMatrix);
522 ASSERT_VG_NO_ERROR();
523 }
524
compositeOperation() const525 CompositeOperator PainterOpenVG::compositeOperation() const
526 {
527 ASSERT(m_state);
528 return m_state->compositeOperation;
529 }
530
setCompositeOperation(CompositeOperator op)531 void PainterOpenVG::setCompositeOperation(CompositeOperator op)
532 {
533 ASSERT(m_state);
534 m_surface->makeCurrent();
535
536 m_state->compositeOperation = op;
537 m_state->applyBlending(this);
538 }
539
opacity() const540 float PainterOpenVG::opacity() const
541 {
542 ASSERT(m_state);
543 return m_state->opacity;
544 }
545
setOpacity(float opacity)546 void PainterOpenVG::setOpacity(float opacity)
547 {
548 ASSERT(m_state);
549 m_surface->makeCurrent();
550
551 m_state->opacity = opacity;
552 m_state->applyBlending(this);
553 }
554
strokeThickness() const555 float PainterOpenVG::strokeThickness() const
556 {
557 ASSERT(m_state);
558 return m_state->strokeThickness;
559 }
560
setStrokeThickness(float thickness)561 void PainterOpenVG::setStrokeThickness(float thickness)
562 {
563 ASSERT(m_state);
564 m_surface->makeCurrent();
565
566 m_state->strokeThickness = thickness;
567 vgSetf(VG_STROKE_LINE_WIDTH, thickness);
568 ASSERT_VG_NO_ERROR();
569 }
570
strokeStyle() const571 StrokeStyle PainterOpenVG::strokeStyle() const
572 {
573 ASSERT(m_state);
574 return m_state->strokeStyle;
575 }
576
setStrokeStyle(StrokeStyle style)577 void PainterOpenVG::setStrokeStyle(StrokeStyle style)
578 {
579 ASSERT(m_state);
580 m_surface->makeCurrent();
581
582 m_state->strokeStyle = style;
583 m_state->applyStrokeStyle();
584 }
585
setLineDash(const DashArray & dashArray,float dashOffset)586 void PainterOpenVG::setLineDash(const DashArray& dashArray, float dashOffset)
587 {
588 ASSERT(m_state);
589 m_surface->makeCurrent();
590
591 m_state->strokeDashArray = dashArray;
592 m_state->strokeDashOffset = dashOffset;
593 m_state->applyStrokeStyle();
594 }
595
setLineCap(LineCap lineCap)596 void PainterOpenVG::setLineCap(LineCap lineCap)
597 {
598 ASSERT(m_state);
599 m_surface->makeCurrent();
600
601 m_state->strokeLineCap = lineCap;
602 vgSeti(VG_STROKE_CAP_STYLE, toVGCapStyle(lineCap));
603 ASSERT_VG_NO_ERROR();
604 }
605
setLineJoin(LineJoin lineJoin)606 void PainterOpenVG::setLineJoin(LineJoin lineJoin)
607 {
608 ASSERT(m_state);
609 m_surface->makeCurrent();
610
611 m_state->strokeLineJoin = lineJoin;
612 vgSeti(VG_STROKE_JOIN_STYLE, toVGJoinStyle(lineJoin));
613 ASSERT_VG_NO_ERROR();
614 }
615
setMiterLimit(float miterLimit)616 void PainterOpenVG::setMiterLimit(float miterLimit)
617 {
618 ASSERT(m_state);
619 m_surface->makeCurrent();
620
621 m_state->strokeMiterLimit = miterLimit;
622 vgSetf(VG_STROKE_MITER_LIMIT, miterLimit);
623 ASSERT_VG_NO_ERROR();
624 }
625
strokeColor() const626 Color PainterOpenVG::strokeColor() const
627 {
628 ASSERT(m_state);
629 return m_state->strokeColor;
630 }
631
setStrokeColor(const Color & color)632 void PainterOpenVG::setStrokeColor(const Color& color)
633 {
634 ASSERT(m_state);
635 m_surface->makeCurrent();
636
637 m_state->strokeColor = color;
638 setVGSolidColor(VG_STROKE_PATH, color);
639 }
640
fillColor() const641 Color PainterOpenVG::fillColor() const
642 {
643 ASSERT(m_state);
644 return m_state->fillColor;
645 }
646
setFillColor(const Color & color)647 void PainterOpenVG::setFillColor(const Color& color)
648 {
649 ASSERT(m_state);
650 m_surface->makeCurrent();
651
652 m_state->fillColor = color;
653 setVGSolidColor(VG_FILL_PATH, color);
654 }
655
textDrawingMode() const656 TextDrawingModeFlags PainterOpenVG::textDrawingMode() const
657 {
658 ASSERT(m_state);
659 return m_state->textDrawingMode;
660 }
661
setTextDrawingMode(TextDrawingModeFlags mode)662 void PainterOpenVG::setTextDrawingMode(TextDrawingModeFlags mode)
663 {
664 ASSERT(m_state);
665 m_state->textDrawingMode = mode;
666 }
667
antialiasingEnabled() const668 bool PainterOpenVG::antialiasingEnabled() const
669 {
670 ASSERT(m_state);
671 return m_state->antialiasingEnabled;
672 }
673
setAntialiasingEnabled(bool enabled)674 void PainterOpenVG::setAntialiasingEnabled(bool enabled)
675 {
676 ASSERT(m_state);
677 m_surface->makeCurrent();
678
679 m_state->antialiasingEnabled = enabled;
680
681 if (enabled)
682 vgSeti(VG_RENDERING_QUALITY, VG_RENDERING_QUALITY_FASTER);
683 else
684 vgSeti(VG_RENDERING_QUALITY, VG_RENDERING_QUALITY_NONANTIALIASED);
685 }
686
scale(const FloatSize & scaleFactors)687 void PainterOpenVG::scale(const FloatSize& scaleFactors)
688 {
689 ASSERT(m_state);
690 m_surface->makeCurrent();
691
692 AffineTransform transformation = m_state->surfaceTransformation;
693 transformation.scaleNonUniform(scaleFactors.width(), scaleFactors.height());
694 setTransformation(transformation);
695 }
696
rotate(float radians)697 void PainterOpenVG::rotate(float radians)
698 {
699 ASSERT(m_state);
700 m_surface->makeCurrent();
701
702 AffineTransform transformation = m_state->surfaceTransformation;
703 transformation.rotate(rad2deg(radians));
704 setTransformation(transformation);
705 }
706
translate(float dx,float dy)707 void PainterOpenVG::translate(float dx, float dy)
708 {
709 ASSERT(m_state);
710 m_surface->makeCurrent();
711
712 AffineTransform transformation = m_state->surfaceTransformation;
713 transformation.translate(dx, dy);
714 setTransformation(transformation);
715 }
716
drawPath(const Path & path,VGbitfield specifiedPaintModes,WindRule fillRule)717 void PainterOpenVG::drawPath(const Path& path, VGbitfield specifiedPaintModes, WindRule fillRule)
718 {
719 ASSERT(m_state);
720
721 VGbitfield paintModes = 0;
722 if (!m_state->strokeDisabled())
723 paintModes |= VG_STROKE_PATH;
724 if (!m_state->fillDisabled())
725 paintModes |= VG_FILL_PATH;
726
727 paintModes &= specifiedPaintModes;
728
729 if (!paintModes)
730 return;
731
732 m_surface->makeCurrent();
733
734 vgSeti(VG_FILL_RULE, toVGFillRule(fillRule));
735 vgDrawPath(path.platformPath()->vgPath(), paintModes);
736 ASSERT_VG_NO_ERROR();
737 }
738
intersectScissorRect(const FloatRect & rect)739 void PainterOpenVG::intersectScissorRect(const FloatRect& rect)
740 {
741 // Scissor rectangles are defined by float values, but e.g. painting
742 // something red to a float-clipped rectangle and then painting something
743 // white to the same rectangle will leave some red remnants as it is
744 // rendered to full pixels in between. Also, some OpenVG implementations
745 // are likely to clip to integer coordinates anyways because of the above
746 // effect. So considering the above (and confirming through tests) the
747 // visual result is better if we clip to the enclosing integer rectangle
748 // rather than the exact float rectangle for scissoring.
749 if (m_state->scissoringEnabled)
750 m_state->scissorRect.intersect(FloatRect(enclosingIntRect(rect)));
751 else {
752 m_state->scissoringEnabled = true;
753 m_state->scissorRect = FloatRect(enclosingIntRect(rect));
754 }
755
756 m_state->applyScissorRect();
757 }
758
intersectClipRect(const FloatRect & rect)759 void PainterOpenVG::intersectClipRect(const FloatRect& rect)
760 {
761 ASSERT(m_state);
762 m_surface->makeCurrent();
763
764 if (m_state->surfaceTransformation.isIdentity()) {
765 // No transformation required, skip all the complex stuff.
766 intersectScissorRect(rect);
767 return;
768 }
769
770 // Check if the actual destination rectangle is still rectilinear (can be
771 // represented as FloatRect) so we could apply scissoring instead of
772 // (potentially more expensive) path clipping. Note that scissoring is not
773 // subject to transformations, so we need to do the transformation to
774 // surface coordinates by ourselves.
775 FloatQuad effectiveScissorQuad = m_state->surfaceTransformation.mapQuad(FloatQuad(rect));
776
777 if (effectiveScissorQuad.isRectilinear())
778 intersectScissorRect(effectiveScissorQuad.boundingBox());
779 else {
780 // The transformed scissorRect cannot be represented as FloatRect
781 // anymore, so we need to perform masking instead.
782 Path scissorRectPath;
783 scissorRectPath.addRect(rect);
784 clipPath(scissorRectPath, PainterOpenVG::IntersectClip);
785 }
786 }
787
clipPath(const Path & path,PainterOpenVG::ClipOperation maskOp,WindRule clipRule)788 void PainterOpenVG::clipPath(const Path& path, PainterOpenVG::ClipOperation maskOp, WindRule clipRule)
789 {
790 #ifdef OPENVG_VERSION_1_1
791 ASSERT(m_state);
792 m_surface->makeCurrent();
793
794 if (m_state->mask != VG_INVALID_HANDLE && !m_state->maskingChangedAndEnabled) {
795 // The parent's mask has been inherited - dispose the handle so that
796 // it won't be overwritten.
797 m_state->maskingChangedAndEnabled = true;
798 m_state->mask = VG_INVALID_HANDLE;
799 } else if (!m_state->maskingEnabled()) {
800 // None of the parent painter states had a mask enabled yet.
801 m_state->maskingChangedAndEnabled = true;
802 vgSeti(VG_MASKING, VG_TRUE);
803 // Make sure not to inherit previous mask state from previously written
804 // (but disabled) masks. For VG_FILL_MASK the first argument is ignored,
805 // we pass VG_INVALID_HANDLE which is what the OpenVG spec suggests.
806 vgMask(VG_INVALID_HANDLE, VG_FILL_MASK, 0, 0, m_surface->width(), m_surface->height());
807 }
808
809 // Intersect the path from the mask, or subtract it from there.
810 // (In either case we always decrease the visible area, never increase it,
811 // which means masking never has to modify scissor rectangles.)
812 vgSeti(VG_FILL_RULE, toVGFillRule(clipRule));
813 vgRenderToMask(path.platformPath()->vgPath(), VG_FILL_PATH, (VGMaskOperation) maskOp);
814 ASSERT_VG_NO_ERROR();
815 #else
816 notImplemented();
817 #endif
818 }
819
drawRect(const FloatRect & rect,VGbitfield specifiedPaintModes)820 void PainterOpenVG::drawRect(const FloatRect& rect, VGbitfield specifiedPaintModes)
821 {
822 ASSERT(m_state);
823
824 VGbitfield paintModes = 0;
825 if (!m_state->strokeDisabled())
826 paintModes |= VG_STROKE_PATH;
827 if (!m_state->fillDisabled())
828 paintModes |= VG_FILL_PATH;
829
830 paintModes &= specifiedPaintModes;
831
832 if (!paintModes)
833 return;
834
835 m_surface->makeCurrent();
836
837 VGPath path = vgCreatePath(
838 VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
839 1.0 /* scale */, 0.0 /* bias */,
840 5 /* expected number of segments */,
841 5 /* expected number of total coordinates */,
842 VG_PATH_CAPABILITY_APPEND_TO);
843 ASSERT_VG_NO_ERROR();
844
845 if (vguRect(path, rect.x(), rect.y(), rect.width(), rect.height()) == VGU_NO_ERROR) {
846 vgDrawPath(path, paintModes);
847 ASSERT_VG_NO_ERROR();
848 }
849
850 vgDestroyPath(path);
851 ASSERT_VG_NO_ERROR();
852 }
853
drawRoundedRect(const FloatRect & rect,const IntSize & topLeft,const IntSize & topRight,const IntSize & bottomLeft,const IntSize & bottomRight,VGbitfield specifiedPaintModes)854 void PainterOpenVG::drawRoundedRect(const FloatRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, VGbitfield specifiedPaintModes)
855 {
856 ASSERT(m_state);
857
858 VGbitfield paintModes = 0;
859 if (!m_state->strokeDisabled())
860 paintModes |= VG_STROKE_PATH;
861 if (!m_state->fillDisabled())
862 paintModes |= VG_FILL_PATH;
863
864 paintModes &= specifiedPaintModes;
865
866 if (!paintModes)
867 return;
868
869 m_surface->makeCurrent();
870
871 VGPath path = vgCreatePath(
872 VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
873 1.0 /* scale */, 0.0 /* bias */,
874 10 /* expected number of segments */,
875 25 /* expected number of total coordinates */,
876 VG_PATH_CAPABILITY_APPEND_TO);
877 ASSERT_VG_NO_ERROR();
878
879 // clamp corner arc sizes
880 FloatSize clampedTopLeft = FloatSize(topLeft).shrunkTo(rect.size()).expandedTo(FloatSize());
881 FloatSize clampedTopRight = FloatSize(topRight).shrunkTo(rect.size()).expandedTo(FloatSize());
882 FloatSize clampedBottomLeft = FloatSize(bottomLeft).shrunkTo(rect.size()).expandedTo(FloatSize());
883 FloatSize clampedBottomRight = FloatSize(bottomRight).shrunkTo(rect.size()).expandedTo(FloatSize());
884
885 // As OpenVG's coordinate system is flipped in comparison to WebKit's,
886 // we have to specify the opposite value for the "clockwise" value.
887 static const VGubyte pathSegments[] = {
888 VG_MOVE_TO_ABS,
889 VG_HLINE_TO_REL,
890 VG_SCCWARC_TO_REL,
891 VG_VLINE_TO_REL,
892 VG_SCCWARC_TO_REL,
893 VG_HLINE_TO_REL,
894 VG_SCCWARC_TO_REL,
895 VG_VLINE_TO_REL,
896 VG_SCCWARC_TO_REL,
897 VG_CLOSE_PATH
898 };
899 // Also, the rounded rectangle path proceeds from the top to the bottom,
900 // requiring height distances and clamped radius sizes to be flipped.
901 const VGfloat pathData[] = {
902 rect.x() + clampedTopLeft.width(), rect.y(),
903 rect.width() - clampedTopLeft.width() - clampedTopRight.width(),
904 clampedTopRight.width(), clampedTopRight.height(), 0, clampedTopRight.width(), clampedTopRight.height(),
905 rect.height() - clampedTopRight.height() - clampedBottomRight.height(),
906 clampedBottomRight.width(), clampedBottomRight.height(), 0, -clampedBottomRight.width(), clampedBottomRight.height(),
907 -(rect.width() - clampedBottomLeft.width() - clampedBottomRight.width()),
908 clampedBottomLeft.width(), clampedBottomLeft.height(), 0, -clampedBottomLeft.width(), -clampedBottomLeft.height(),
909 -(rect.height() - clampedTopLeft.height() - clampedBottomLeft.height()),
910 clampedTopLeft.width(), clampedTopLeft.height(), 0, clampedTopLeft.width(), -clampedTopLeft.height(),
911 };
912
913 vgAppendPathData(path, 10, pathSegments, pathData);
914 vgDrawPath(path, paintModes);
915 vgDestroyPath(path);
916 ASSERT_VG_NO_ERROR();
917 }
918
drawLine(const IntPoint & from,const IntPoint & to)919 void PainterOpenVG::drawLine(const IntPoint& from, const IntPoint& to)
920 {
921 ASSERT(m_state);
922
923 if (m_state->strokeDisabled())
924 return;
925
926 m_surface->makeCurrent();
927
928 VGPath path = vgCreatePath(
929 VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
930 1.0 /* scale */, 0.0 /* bias */,
931 2 /* expected number of segments */,
932 4 /* expected number of total coordinates */,
933 VG_PATH_CAPABILITY_APPEND_TO);
934 ASSERT_VG_NO_ERROR();
935
936 VGUErrorCode errorCode;
937
938 // Try to align lines to pixels, centering them between pixels for odd thickness values.
939 if (fmod(m_state->strokeThickness + 0.5, 2.0) < 1.0)
940 errorCode = vguLine(path, from.x(), from.y(), to.x(), to.y());
941 else if ((to.y() - from.y()) > (to.x() - from.x())) // more vertical than horizontal
942 errorCode = vguLine(path, from.x() + 0.5, from.y(), to.x() + 0.5, to.y());
943 else
944 errorCode = vguLine(path, from.x(), from.y() + 0.5, to.x(), to.y() + 0.5);
945
946 if (errorCode == VGU_NO_ERROR) {
947 vgDrawPath(path, VG_STROKE_PATH);
948 ASSERT_VG_NO_ERROR();
949 }
950
951 vgDestroyPath(path);
952 ASSERT_VG_NO_ERROR();
953 }
954
drawArc(const IntRect & rect,int startAngle,int angleSpan,VGbitfield specifiedPaintModes)955 void PainterOpenVG::drawArc(const IntRect& rect, int startAngle, int angleSpan, VGbitfield specifiedPaintModes)
956 {
957 ASSERT(m_state);
958
959 VGbitfield paintModes = 0;
960 if (!m_state->strokeDisabled())
961 paintModes |= VG_STROKE_PATH;
962 if (!m_state->fillDisabled())
963 paintModes |= VG_FILL_PATH;
964
965 paintModes &= specifiedPaintModes;
966
967 if (!paintModes)
968 return;
969
970 m_surface->makeCurrent();
971
972 VGPath path = vgCreatePath(
973 VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
974 1.0 /* scale */, 0.0 /* bias */,
975 2 /* expected number of segments */,
976 4 /* expected number of total coordinates */,
977 VG_PATH_CAPABILITY_APPEND_TO);
978 ASSERT_VG_NO_ERROR();
979
980 if (vguArc(path, rect.x() + rect.width() / 2.0, rect.y() + rect.height() / 2.0, rect.width(), rect.height(), -startAngle, -angleSpan, VGU_ARC_OPEN) == VGU_NO_ERROR) {
981 vgDrawPath(path, VG_STROKE_PATH);
982 ASSERT_VG_NO_ERROR();
983 }
984
985 vgDestroyPath(path);
986 ASSERT_VG_NO_ERROR();
987 }
988
drawEllipse(const IntRect & rect,VGbitfield specifiedPaintModes)989 void PainterOpenVG::drawEllipse(const IntRect& rect, VGbitfield specifiedPaintModes)
990 {
991 ASSERT(m_state);
992
993 VGbitfield paintModes = 0;
994 if (!m_state->strokeDisabled())
995 paintModes |= VG_STROKE_PATH;
996 if (!m_state->fillDisabled())
997 paintModes |= VG_FILL_PATH;
998
999 paintModes &= specifiedPaintModes;
1000
1001 if (!paintModes)
1002 return;
1003
1004 m_surface->makeCurrent();
1005
1006 VGPath path = vgCreatePath(
1007 VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
1008 1.0 /* scale */, 0.0 /* bias */,
1009 4 /* expected number of segments */,
1010 12 /* expected number of total coordinates */,
1011 VG_PATH_CAPABILITY_APPEND_TO);
1012 ASSERT_VG_NO_ERROR();
1013
1014 if (vguEllipse(path, rect.x() + rect.width() / 2.0, rect.y() + rect.height() / 2.0, rect.width(), rect.height()) == VGU_NO_ERROR) {
1015 vgDrawPath(path, paintModes);
1016 ASSERT_VG_NO_ERROR();
1017 }
1018
1019 vgDestroyPath(path);
1020 ASSERT_VG_NO_ERROR();
1021 }
1022
drawPolygon(size_t numPoints,const FloatPoint * points,VGbitfield specifiedPaintModes)1023 void PainterOpenVG::drawPolygon(size_t numPoints, const FloatPoint* points, VGbitfield specifiedPaintModes)
1024 {
1025 ASSERT(m_state);
1026
1027 VGbitfield paintModes = 0;
1028 if (!m_state->strokeDisabled())
1029 paintModes |= VG_STROKE_PATH;
1030 if (!m_state->fillDisabled())
1031 paintModes |= VG_FILL_PATH;
1032
1033 paintModes &= specifiedPaintModes;
1034
1035 if (!paintModes)
1036 return;
1037
1038 m_surface->makeCurrent();
1039
1040 // Path segments: all points + "close path".
1041 const VGint numSegments = numPoints + 1;
1042 const VGint numCoordinates = numPoints * 2;
1043
1044 VGPath path = vgCreatePath(
1045 VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
1046 1.0 /* scale */, 0.0 /* bias */,
1047 numSegments /* expected number of segments */,
1048 numCoordinates /* expected number of total coordinates */,
1049 VG_PATH_CAPABILITY_APPEND_TO);
1050 ASSERT_VG_NO_ERROR();
1051
1052 Vector<VGfloat> vgPoints(numCoordinates);
1053 for (int i = 0; i < numPoints; ++i) {
1054 vgPoints[i*2] = points[i].x();
1055 vgPoints[i*2 + 1] = points[i].y();
1056 }
1057
1058 if (vguPolygon(path, vgPoints.data(), numPoints, VG_TRUE /* closed */) == VGU_NO_ERROR) {
1059 vgDrawPath(path, paintModes);
1060 ASSERT_VG_NO_ERROR();
1061 }
1062
1063 vgDestroyPath(path);
1064 ASSERT_VG_NO_ERROR();
1065 }
1066
drawImage(TiledImageOpenVG * tiledImage,const FloatRect & dst,const FloatRect & src)1067 void PainterOpenVG::drawImage(TiledImageOpenVG* tiledImage, const FloatRect& dst, const FloatRect& src)
1068 {
1069 ASSERT(m_state);
1070 m_surface->makeCurrent();
1071
1072 // If buffers can be larger than the maximum OpenVG image sizes,
1073 // we split them into tiles.
1074 IntRect drawnTiles = tiledImage->tilesInRect(src);
1075 AffineTransform srcToDstTransformation = makeMapBetweenRects(
1076 FloatRect(FloatPoint(0.0, 0.0), src.size()), dst);
1077 srcToDstTransformation.translate(-src.x(), -src.y());
1078
1079 for (int yIndex = drawnTiles.y(); yIndex < drawnTiles.bottom(); ++yIndex) {
1080 for (int xIndex = drawnTiles.x(); xIndex < drawnTiles.right(); ++xIndex) {
1081 // The srcTile rectangle is an aligned tile cropped by the src rectangle.
1082 FloatRect tile(tiledImage->tileRect(xIndex, yIndex));
1083 FloatRect srcTile = intersection(src, tile);
1084
1085 save();
1086
1087 // If the image is drawn in full, all we need is the proper transformation
1088 // in order to get it drawn at the right spot on the surface.
1089 concatTransformation(AffineTransform(srcToDstTransformation).translate(tile.x(), tile.y()));
1090
1091 // If only a part of the tile is drawn, we also need to clip the surface.
1092 if (srcTile != tile) {
1093 // Put boundaries relative to tile origin, as we already
1094 // translated to (x, y) with the transformation matrix.
1095 srcTile.move(-tile.x(), -tile.y());
1096 intersectClipRect(srcTile);
1097 }
1098
1099 VGImage image = tiledImage->tile(xIndex, yIndex);
1100 if (image != VG_INVALID_HANDLE) {
1101 vgDrawImage(image);
1102 ASSERT_VG_NO_ERROR();
1103 }
1104
1105 restore();
1106 }
1107 }
1108 }
1109
1110 #ifdef OPENVG_VERSION_1_1
drawText(VGFont vgFont,Vector<VGuint> & characters,VGfloat * adjustmentsX,VGfloat * adjustmentsY,const FloatPoint & point)1111 void PainterOpenVG::drawText(VGFont vgFont, Vector<VGuint>& characters, VGfloat* adjustmentsX, VGfloat* adjustmentsY, const FloatPoint& point)
1112 {
1113 ASSERT(m_state);
1114
1115 VGbitfield paintModes = 0;
1116
1117 if (m_state->textDrawingMode & TextModeClip)
1118 return; // unsupported for every port except CG at the time of writing
1119 if (m_state->textDrawingMode & TextModeFill && !m_state->fillDisabled())
1120 paintModes |= VG_FILL_PATH;
1121 if (m_state->textDrawingMode & TextModeStroke && !m_state->strokeDisabled())
1122 paintModes |= VG_STROKE_PATH;
1123
1124 m_surface->makeCurrent();
1125
1126 FloatPoint effectivePoint = m_state->surfaceTransformation.mapPoint(point);
1127 FloatPoint p = point;
1128 AffineTransform* originalTransformation = 0;
1129
1130 // In case the font isn't drawn at a pixel-exact baseline and we can easily
1131 // fix that (which is the case for non-rotated affine transforms), let's
1132 // align the starting point to the pixel boundary in order to prevent
1133 // font rendering issues such as glyphs that appear off by a pixel.
1134 // This causes us to have inconsistent spacing between baselines in a
1135 // larger paragraph, but that seems to be the least of all evils.
1136 if ((fmod(effectivePoint.x() + 0.01, 1.0) > 0.02 || fmod(effectivePoint.y() + 0.01, 1.0) > 0.02)
1137 && isNonRotatedAffineTransformation(m_state->surfaceTransformation))
1138 {
1139 originalTransformation = new AffineTransform(m_state->surfaceTransformation);
1140 setTransformation(AffineTransform(
1141 m_state->surfaceTransformation.a(), 0,
1142 0, m_state->surfaceTransformation.d(),
1143 roundf(effectivePoint.x()), roundf(effectivePoint.y())));
1144 p = FloatPoint();
1145 }
1146
1147 const VGfloat vgPoint[2] = { p.x(), p.y() };
1148 vgSetfv(VG_GLYPH_ORIGIN, 2, vgPoint);
1149 ASSERT_VG_NO_ERROR();
1150
1151 vgDrawGlyphs(vgFont, characters.size(), characters.data(),
1152 adjustmentsX, adjustmentsY, paintModes, VG_TRUE /* allow autohinting */);
1153 ASSERT_VG_NO_ERROR();
1154
1155 if (originalTransformation) {
1156 setTransformation(*originalTransformation);
1157 delete originalTransformation;
1158 }
1159 }
1160 #endif
1161
asNewNativeImage(const IntRect & src,VGImageFormat format)1162 TiledImageOpenVG* PainterOpenVG::asNewNativeImage(const IntRect& src, VGImageFormat format)
1163 {
1164 ASSERT(m_state);
1165 m_surface->sharedSurface()->makeCurrent();
1166
1167 const IntSize vgMaxImageSize(vgGeti(VG_MAX_IMAGE_WIDTH), vgGeti(VG_MAX_IMAGE_HEIGHT));
1168 ASSERT_VG_NO_ERROR();
1169
1170 const IntRect rect = intersection(src, IntRect(0, 0, m_surface->width(), m_surface->height()));
1171 TiledImageOpenVG* tiledImage = new TiledImageOpenVG(rect.size(), vgMaxImageSize);
1172
1173 const int numColumns = tiledImage->numColumns();
1174 const int numRows = tiledImage->numRows();
1175
1176 // Create the images as resources of the shared surface/context.
1177 for (int yIndex = 0; yIndex < numRows; ++yIndex) {
1178 for (int xIndex = 0; xIndex < numColumns; ++xIndex) {
1179 IntRect tileRect = tiledImage->tileRect(xIndex, yIndex);
1180 VGImage image = vgCreateImage(format, tileRect.width(), tileRect.height(), VG_IMAGE_QUALITY_FASTER);
1181 ASSERT_VG_NO_ERROR();
1182
1183 tiledImage->setTile(xIndex, yIndex, image);
1184 }
1185 }
1186
1187 // Fill the image contents with our own surface/context being current.
1188 m_surface->makeCurrent();
1189
1190 for (int yIndex = 0; yIndex < numRows; ++yIndex) {
1191 for (int xIndex = 0; xIndex < numColumns; ++xIndex) {
1192 IntRect tileRect = tiledImage->tileRect(xIndex, yIndex);
1193
1194 vgGetPixels(tiledImage->tile(xIndex, yIndex), 0, 0,
1195 rect.x() + tileRect.x(), rect.y() + tileRect.y(),
1196 tileRect.width(), tileRect.height());
1197 ASSERT_VG_NO_ERROR();
1198 }
1199 }
1200
1201 return tiledImage;
1202 }
1203
save(PainterOpenVG::SaveMode saveMode)1204 void PainterOpenVG::save(PainterOpenVG::SaveMode saveMode)
1205 {
1206 ASSERT(m_state);
1207
1208 // If the underlying context/surface was switched away by someone without
1209 // telling us, it might not correspond to the one assigned to this painter.
1210 // Switch back so we can save the state properly. (Should happen rarely.)
1211 // Use DontSaveOrApplyPainterState mode in order to avoid recursion.
1212 m_surface->makeCurrent(SurfaceOpenVG::DontSaveOrApplyPainterState);
1213
1214 if (saveMode == PainterOpenVG::CreateNewState) {
1215 m_state->saveMaskIfNecessary(this);
1216 PlatformPainterState* state = new PlatformPainterState(*m_state);
1217 m_stateStack.append(state);
1218 m_state = m_stateStack.last();
1219 } else if (saveMode == PainterOpenVG::CreateNewStateWithPaintStateOnly) {
1220 m_state->saveMaskIfNecessary(this);
1221 PlatformPainterState* state = new PlatformPainterState();
1222 state->copyPaintState(m_state);
1223 m_stateStack.append(state);
1224 m_state = m_stateStack.last();
1225 } else // if (saveMode == PainterOpenVG::KeepCurrentState)
1226 m_state->saveMaskIfNecessary(this);
1227 }
1228
restore()1229 void PainterOpenVG::restore()
1230 {
1231 ASSERT(m_stateStack.size() >= 2);
1232 m_surface->makeCurrent(SurfaceOpenVG::DontApplyPainterState);
1233
1234 PlatformPainterState* state = m_stateStack.last();
1235 m_stateStack.removeLast();
1236 delete state;
1237
1238 m_state = m_stateStack.last();
1239 m_state->applyState(this);
1240 }
1241
1242 }
1243