Home | History | Annotate | Download | only in openvg
      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 
     48 static bool isNonRotatedAffineTransformation(const AffineTransform& t)
     49 {
     50     return t.b() <= FLT_EPSILON && t.c() <= FLT_EPSILON;
     51 }
     52 
     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 
     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 
     79 static VGFillRule toVGFillRule(WindRule fillRule)
     80 {
     81     return fillRule == RULE_EVENODD ? VG_EVEN_ODD : VG_NON_ZERO;
     82 }
     83 
     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 
     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 
    129     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 
    149     ~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 
    160     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 
    173     inline bool maskingEnabled()
    174     {
    175         return maskingChangedAndEnabled || mask != VG_INVALID_HANDLE;
    176     }
    177 
    178     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 
    197     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 
    231     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 
    310     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 
    333     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 
    344     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 
    370     inline bool strokeDisabled() const
    371     {
    372         return (compositeOperation == CompositeSourceOver
    373             && (strokeStyle == NoStroke || !strokeColor.alpha()));
    374     }
    375 
    376     inline bool fillDisabled() const
    377     {
    378         return (compositeOperation == CompositeSourceOver && !fillColor.alpha());
    379     }
    380 
    381     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 
    399 PainterOpenVG::PainterOpenVG()
    400     : m_state(0)
    401     , m_surface(0)
    402 {
    403 }
    404 
    405 PainterOpenVG::PainterOpenVG(SurfaceOpenVG* surface)
    406     : m_state(0)
    407     , m_surface(0)
    408 {
    409     ASSERT(surface);
    410     begin(surface);
    411 }
    412 
    413 PainterOpenVG::~PainterOpenVG()
    414 {
    415     end();
    416 }
    417 
    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 
    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 
    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.
    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  */
    470 void PainterOpenVG::blitToSurface()
    471 {
    472     ASSERT(m_state); // implies m_surface
    473     m_surface->flush();
    474 }
    475 
    476 AffineTransform PainterOpenVG::transformation() const
    477 {
    478     ASSERT(m_state);
    479     return m_state->surfaceTransformation;
    480 }
    481 
    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 
    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 
    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 
    525 CompositeOperator PainterOpenVG::compositeOperation() const
    526 {
    527     ASSERT(m_state);
    528     return m_state->compositeOperation;
    529 }
    530 
    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 
    540 float PainterOpenVG::opacity() const
    541 {
    542     ASSERT(m_state);
    543     return m_state->opacity;
    544 }
    545 
    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 
    555 float PainterOpenVG::strokeThickness() const
    556 {
    557     ASSERT(m_state);
    558     return m_state->strokeThickness;
    559 }
    560 
    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 
    571 StrokeStyle PainterOpenVG::strokeStyle() const
    572 {
    573     ASSERT(m_state);
    574     return m_state->strokeStyle;
    575 }
    576 
    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 
    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 
    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 
    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 
    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 
    626 Color PainterOpenVG::strokeColor() const
    627 {
    628     ASSERT(m_state);
    629     return m_state->strokeColor;
    630 }
    631 
    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 
    641 Color PainterOpenVG::fillColor() const
    642 {
    643     ASSERT(m_state);
    644     return m_state->fillColor;
    645 }
    646 
    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 
    656 TextDrawingModeFlags PainterOpenVG::textDrawingMode() const
    657 {
    658     ASSERT(m_state);
    659     return m_state->textDrawingMode;
    660 }
    661 
    662 void PainterOpenVG::setTextDrawingMode(TextDrawingModeFlags mode)
    663 {
    664     ASSERT(m_state);
    665     m_state->textDrawingMode = mode;
    666 }
    667 
    668 bool PainterOpenVG::antialiasingEnabled() const
    669 {
    670     ASSERT(m_state);
    671     return m_state->antialiasingEnabled;
    672 }
    673 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
   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 
   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
   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 
   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 
   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 
   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