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