2 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
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.
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.
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.
21 #include "PainterOpenVG.h"
23 #include "AffineTransform.h"
25 #include "DashArray.h"
26 #include "FloatPoint.h"
27 #include "FloatQuad.h"
28 #include "FloatRect.h"
31 #include "NotImplemented.h"
32 #include "PlatformPathOpenVG.h"
33 #include "SurfaceOpenVG.h"
34 #include "TiledImageOpenVG.h"
43 #include <wtf/Assertions.h>
44 #include <wtf/MathExtras.h>
48 static bool isNonRotatedAffineTransformation(const AffineTransform& t)
50 return t.b() <= FLT_EPSILON && t.c() <= FLT_EPSILON;
53 static VGCapStyle toVGCapStyle(LineCap lineCap)
66 static VGJoinStyle toVGJoinStyle(LineJoin lineJoin)
79 static VGFillRule toVGFillRule(WindRule fillRule)
81 return fillRule == RULE_EVENODD ? VG_EVEN_ODD : VG_NON_ZERO;
84 static VGuint colorToVGColor(const Color& color)
86 VGuint vgColor = color.red();
87 vgColor = (vgColor << 8) | color.green();
88 vgColor = (vgColor << 8) | color.blue();
89 vgColor = (vgColor << 8) | color.alpha();
93 static void setVGSolidColor(VGPaintMode paintMode, const Color& color)
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();
104 struct PlatformPainterState {
105 AffineTransform surfaceTransformation;
106 CompositeOperator compositeOperation;
109 bool scissoringEnabled;
110 FloatRect scissorRect;
111 #ifdef OPENVG_VERSION_1_1
112 bool maskingChangedAndEnabled;
117 StrokeStyle strokeStyle;
119 float strokeThickness;
120 LineCap strokeLineCap;
121 LineJoin strokeLineJoin;
122 float strokeMiterLimit;
123 DashArray strokeDashArray;
124 float strokeDashOffset;
126 TextDrawingModeFlags textDrawingMode;
127 bool antialiasingEnabled;
129 PlatformPainterState()
130 : compositeOperation(CompositeSourceOver)
132 , scissoringEnabled(false)
133 #ifdef OPENVG_VERSION_1_1
134 , maskingChangedAndEnabled(false)
135 , mask(VG_INVALID_HANDLE)
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)
149 ~PlatformPainterState()
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;
160 PlatformPainterState(const PlatformPainterState& state)
162 surfaceTransformation = state.surfaceTransformation;
164 scissoringEnabled = state.scissoringEnabled;
165 scissorRect = state.scissorRect;
166 #ifdef OPENVG_VERSION_1_1
167 maskingChangedAndEnabled = false;
170 copyPaintState(&state);
173 inline bool maskingEnabled()
175 return maskingChangedAndEnabled || mask != VG_INVALID_HANDLE;
178 void copyPaintState(const PlatformPainterState* other)
180 compositeOperation = other->compositeOperation;
181 opacity = other->opacity;
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;
193 textDrawingMode = other->textDrawingMode;
194 antialiasingEnabled = other->antialiasingEnabled;
197 void applyState(PainterOpenVG* painter)
201 setVGSolidColor(VG_FILL_PATH, fillColor);
202 setVGSolidColor(VG_STROKE_PATH, strokeColor);
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);
209 if (antialiasingEnabled)
210 vgSeti(VG_RENDERING_QUALITY, VG_RENDERING_QUALITY_FASTER);
212 vgSeti(VG_RENDERING_QUALITY, VG_RENDERING_QUALITY_NONANTIALIASED);
214 applyBlending(painter);
217 applyTransformation(painter);
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());
226 vgSeti(VG_MASKING, VG_FALSE);
228 ASSERT_VG_NO_ERROR();
231 void applyBlending(PainterOpenVG* painter)
233 VGBlendMode blendMode = VG_BLEND_SRC_OVER;
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();
250 blendMode = VG_BLEND_SRC;
252 case CompositeSourceOver:
253 blendMode = VG_BLEND_SRC_OVER;
255 case CompositeSourceIn:
256 blendMode = VG_BLEND_SRC_IN;
258 case CompositeSourceOut:
261 case CompositeSourceAtop:
264 case CompositeDestinationOver:
265 blendMode = VG_BLEND_DST_OVER;
267 case CompositeDestinationIn:
268 blendMode = VG_BLEND_DST_IN;
270 case CompositeDestinationOut:
273 case CompositeDestinationAtop:
279 case CompositePlusDarker:
280 blendMode = VG_BLEND_DARKEN;
282 case CompositePlusLighter:
283 blendMode = VG_BLEND_LIGHTEN;
287 if (compositeOperation != CompositeClear) {
288 if (opacity >= (1.0 - FLT_EPSILON))
289 vgSeti(VG_COLOR_TRANSFORM, VG_FALSE);
290 else if (blendMode == VG_BLEND_SRC) {
291 blendMode = VG_BLEND_SRC_OVER;
292 VGfloat values[] = { 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, opacity };
293 vgSetfv(VG_COLOR_TRANSFORM_VALUES, 8, values);
294 vgSeti(VG_COLOR_TRANSFORM, VG_TRUE);
296 VGfloat values[] = { 1.0, 1.0, 1.0, opacity, 0.0, 0.0, 0.0, 0.0 };
297 vgSetfv(VG_COLOR_TRANSFORM_VALUES, 8, values);
298 vgSeti(VG_COLOR_TRANSFORM, VG_TRUE);
300 ASSERT_VG_NO_ERROR();
303 vgSeti(VG_BLEND_MODE, blendMode);
304 ASSERT_VG_NO_ERROR();
307 void applyTransformation(PainterOpenVG* painter)
309 // There are *five* separate transforms that can be applied to OpenVG as of 1.1
310 // but it is not clear that we need to set them separately. Instead we set them
311 // all right here and let this be a call to essentially set the world transformation!
312 VGMatrix vgMatrix(surfaceTransformation);
313 const VGfloat* vgFloatArray = vgMatrix.toVGfloat();
315 vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
316 vgLoadMatrix(vgFloatArray);
317 ASSERT_VG_NO_ERROR();
319 vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
320 vgLoadMatrix(vgFloatArray);
321 ASSERT_VG_NO_ERROR();
323 #ifdef OPENVG_VERSION_1_1
324 vgSeti(VG_MATRIX_MODE, VG_MATRIX_GLYPH_USER_TO_SURFACE);
325 vgLoadMatrix(vgFloatArray);
326 ASSERT_VG_NO_ERROR();
330 void applyScissorRect()
332 if (scissoringEnabled) {
333 vgSeti(VG_SCISSORING, VG_TRUE);
334 vgSetfv(VG_SCISSOR_RECTS, 4, VGRect(scissorRect).toVGfloat());
336 vgSeti(VG_SCISSORING, VG_FALSE);
338 ASSERT_VG_NO_ERROR();
341 void applyStrokeStyle()
343 if (strokeStyle == DottedStroke) {
344 VGfloat vgFloatArray[2] = { 1.0, 1.0 };
345 vgSetfv(VG_STROKE_DASH_PATTERN, 2, vgFloatArray);
346 vgSetf(VG_STROKE_DASH_PHASE, 0.0);
347 } else if (strokeStyle == DashedStroke) {
348 if (!strokeDashArray.size()) {
349 VGfloat vgFloatArray[2] = { 4.0, 3.0 };
350 vgSetfv(VG_STROKE_DASH_PATTERN, 2, vgFloatArray);
352 Vector<VGfloat> vgFloatArray(strokeDashArray.size());
353 for (int i = 0; i < strokeDashArray.size(); ++i)
354 vgFloatArray[i] = strokeDashArray[i];
356 vgSetfv(VG_STROKE_DASH_PATTERN, vgFloatArray.size(), vgFloatArray.data());
358 vgSetf(VG_STROKE_DASH_PHASE, strokeDashOffset);
360 vgSetfv(VG_STROKE_DASH_PATTERN, 0, 0);
361 vgSetf(VG_STROKE_DASH_PHASE, 0.0);
364 ASSERT_VG_NO_ERROR();
367 inline bool strokeDisabled() const
369 return (compositeOperation == CompositeSourceOver
370 && (strokeStyle == NoStroke || !strokeColor.alpha()));
373 inline bool fillDisabled() const
375 return (compositeOperation == CompositeSourceOver && !fillColor.alpha());
378 void saveMaskIfNecessary(PainterOpenVG* painter)
380 #ifdef OPENVG_VERSION_1_1
381 if (maskingChangedAndEnabled) {
382 if (mask != VG_INVALID_HANDLE) {
383 vgDestroyMaskLayer(mask);
384 ASSERT_VG_NO_ERROR();
386 mask = vgCreateMaskLayer(painter->surface()->width(), painter->surface()->height());
387 ASSERT(mask != VG_INVALID_HANDLE);
388 vgCopyMask(mask, 0, 0, 0, 0, painter->surface()->width(), painter->surface()->height());
389 ASSERT_VG_NO_ERROR();
396 PainterOpenVG::PainterOpenVG()
402 PainterOpenVG::PainterOpenVG(SurfaceOpenVG* surface)
410 PainterOpenVG::~PainterOpenVG()
415 void PainterOpenVG::begin(SurfaceOpenVG* surface)
417 if (surface == m_surface)
425 m_stateStack.append(new PlatformPainterState());
426 m_state = m_stateStack.last();
428 m_surface->setActivePainter(this);
429 m_surface->makeCurrent();
432 void PainterOpenVG::end()
437 m_surface->setActivePainter(0);
440 destroyPainterStates();
443 void PainterOpenVG::destroyPainterStates()
445 PlatformPainterState* state = 0;
446 while (!m_stateStack.isEmpty()) {
447 state = m_stateStack.last();
448 m_stateStack.removeLast();
454 // Called by friend SurfaceOpenVG, private otherwise.
455 void PainterOpenVG::applyState()
458 m_state->applyState(this);
462 * Copy the current back buffer image onto the surface.
464 * Call this method when all painting operations have been completed,
465 * otherwise the surface won't visibly change.
467 void PainterOpenVG::blitToSurface()
469 ASSERT(m_state); // implies m_surface
473 AffineTransform PainterOpenVG::transformation() const
476 return m_state->surfaceTransformation;
479 void PainterOpenVG::concatTransformation(const AffineTransform& transformation)
482 m_surface->makeCurrent();
484 // We do the multiplication ourself using WebCore's AffineTransform rather
485 // than offloading this to VG via vgMultMatrix() to keep things simple and
486 // so we can maintain state ourselves.
487 m_state->surfaceTransformation.multLeft(transformation);
488 m_state->applyTransformation(this);
491 void PainterOpenVG::setTransformation(const AffineTransform& transformation)
494 m_surface->makeCurrent();
496 m_state->surfaceTransformation = transformation;
497 m_state->applyTransformation(this);
500 void PainterOpenVG::transformPath(VGPath dst, VGPath src, const AffineTransform& transformation)
502 vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
504 // Save the transform state
505 VGfloat currentMatrix[9];
506 vgGetMatrix(currentMatrix);
507 ASSERT_VG_NO_ERROR();
509 // Load the new transform
510 vgLoadMatrix(VGMatrix(transformation).toVGfloat());
511 ASSERT_VG_NO_ERROR();
513 // Apply the new transform
514 vgTransformPath(dst, src);
515 ASSERT_VG_NO_ERROR();
517 // Restore the transform state
518 vgLoadMatrix(currentMatrix);
519 ASSERT_VG_NO_ERROR();
522 CompositeOperator PainterOpenVG::compositeOperation() const
525 return m_state->compositeOperation;
528 void PainterOpenVG::setCompositeOperation(CompositeOperator op)
531 m_surface->makeCurrent();
533 m_state->compositeOperation = op;
534 m_state->applyBlending(this);
537 float PainterOpenVG::opacity() const
540 return m_state->opacity;
543 void PainterOpenVG::setOpacity(float opacity)
546 m_surface->makeCurrent();
548 m_state->opacity = opacity;
549 m_state->applyBlending(this);
552 float PainterOpenVG::strokeThickness() const
555 return m_state->strokeThickness;
558 void PainterOpenVG::setStrokeThickness(float thickness)
561 m_surface->makeCurrent();
563 m_state->strokeThickness = thickness;
564 vgSetf(VG_STROKE_LINE_WIDTH, thickness);
565 ASSERT_VG_NO_ERROR();
568 StrokeStyle PainterOpenVG::strokeStyle() const
571 return m_state->strokeStyle;
574 void PainterOpenVG::setStrokeStyle(StrokeStyle style)
577 m_surface->makeCurrent();
579 m_state->strokeStyle = style;
580 m_state->applyStrokeStyle();
583 void PainterOpenVG::setLineDash(const DashArray& dashArray, float dashOffset)
586 m_surface->makeCurrent();
588 m_state->strokeDashArray = dashArray;
589 m_state->strokeDashOffset = dashOffset;
590 m_state->applyStrokeStyle();
593 void PainterOpenVG::setLineCap(LineCap lineCap)
596 m_surface->makeCurrent();
598 m_state->strokeLineCap = lineCap;
599 vgSeti(VG_STROKE_CAP_STYLE, toVGCapStyle(lineCap));
600 ASSERT_VG_NO_ERROR();
603 void PainterOpenVG::setLineJoin(LineJoin lineJoin)
606 m_surface->makeCurrent();
608 m_state->strokeLineJoin = lineJoin;
609 vgSeti(VG_STROKE_JOIN_STYLE, toVGJoinStyle(lineJoin));
610 ASSERT_VG_NO_ERROR();
613 void PainterOpenVG::setMiterLimit(float miterLimit)
616 m_surface->makeCurrent();
618 m_state->strokeMiterLimit = miterLimit;
619 vgSetf(VG_STROKE_MITER_LIMIT, miterLimit);
620 ASSERT_VG_NO_ERROR();
623 Color PainterOpenVG::strokeColor() const
626 return m_state->strokeColor;
629 void PainterOpenVG::setStrokeColor(const Color& color)
632 m_surface->makeCurrent();
634 m_state->strokeColor = color;
635 setVGSolidColor(VG_STROKE_PATH, color);
638 Color PainterOpenVG::fillColor() const
641 return m_state->fillColor;
644 void PainterOpenVG::setFillColor(const Color& color)
647 m_surface->makeCurrent();
649 m_state->fillColor = color;
650 setVGSolidColor(VG_FILL_PATH, color);
653 TextDrawingModeFlags PainterOpenVG::textDrawingMode() const
656 return m_state->textDrawingMode;
659 void PainterOpenVG::setTextDrawingMode(TextDrawingModeFlags mode)
662 m_state->textDrawingMode = mode;
665 bool PainterOpenVG::antialiasingEnabled() const
668 return m_state->antialiasingEnabled;
671 void PainterOpenVG::setAntialiasingEnabled(bool enabled)
674 m_surface->makeCurrent();
676 m_state->antialiasingEnabled = enabled;
679 vgSeti(VG_RENDERING_QUALITY, VG_RENDERING_QUALITY_FASTER);
681 vgSeti(VG_RENDERING_QUALITY, VG_RENDERING_QUALITY_NONANTIALIASED);
684 void PainterOpenVG::scale(const FloatSize& scaleFactors)
687 m_surface->makeCurrent();
689 AffineTransform transformation = m_state->surfaceTransformation;
690 transformation.scaleNonUniform(scaleFactors.width(), scaleFactors.height());
691 setTransformation(transformation);
694 void PainterOpenVG::rotate(float radians)
697 m_surface->makeCurrent();
699 AffineTransform transformation = m_state->surfaceTransformation;
700 transformation.rotate(rad2deg(radians));
701 setTransformation(transformation);
704 void PainterOpenVG::translate(float dx, float dy)
707 m_surface->makeCurrent();
709 AffineTransform transformation = m_state->surfaceTransformation;
710 transformation.translate(dx, dy);
711 setTransformation(transformation);
714 void PainterOpenVG::drawPath(const Path& path, VGbitfield specifiedPaintModes, WindRule fillRule)
718 VGbitfield paintModes = 0;
719 if (!m_state->strokeDisabled())
720 paintModes |= VG_STROKE_PATH;
721 if (!m_state->fillDisabled())
722 paintModes |= VG_FILL_PATH;
724 paintModes &= specifiedPaintModes;
729 m_surface->makeCurrent();
731 vgSeti(VG_FILL_RULE, toVGFillRule(fillRule));
732 vgDrawPath(path.platformPath()->vgPath(), paintModes);
733 ASSERT_VG_NO_ERROR();
736 void PainterOpenVG::intersectScissorRect(const FloatRect& rect)
738 // Scissor rectangles are defined by float values, but e.g. painting
739 // something red to a float-clipped rectangle and then painting something
740 // white to the same rectangle will leave some red remnants as it is
741 // rendered to full pixels in between. Also, some OpenVG implementations
742 // are likely to clip to integer coordinates anyways because of the above
743 // effect. So considering the above (and confirming through tests) the
744 // visual result is better if we clip to the enclosing integer rectangle
745 // rather than the exact float rectangle for scissoring.
746 if (m_state->scissoringEnabled)
747 m_state->scissorRect.intersect(FloatRect(enclosingIntRect(rect)));
749 m_state->scissoringEnabled = true;
750 m_state->scissorRect = FloatRect(enclosingIntRect(rect));
753 m_state->applyScissorRect();
756 void PainterOpenVG::intersectClipRect(const FloatRect& rect)
759 m_surface->makeCurrent();
761 if (m_state->surfaceTransformation.isIdentity()) {
762 // No transformation required, skip all the complex stuff.
763 intersectScissorRect(rect);
767 // Check if the actual destination rectangle is still rectilinear (can be
768 // represented as FloatRect) so we could apply scissoring instead of
769 // (potentially more expensive) path clipping. Note that scissoring is not
770 // subject to transformations, so we need to do the transformation to
771 // surface coordinates by ourselves.
772 FloatQuad effectiveScissorQuad = m_state->surfaceTransformation.mapQuad(FloatQuad(rect));
774 if (effectiveScissorQuad.isRectilinear())
775 intersectScissorRect(effectiveScissorQuad.boundingBox());
777 // The transformed scissorRect cannot be represented as FloatRect
778 // anymore, so we need to perform masking instead.
779 Path scissorRectPath;
780 scissorRectPath.addRect(rect);
781 clipPath(scissorRectPath, PainterOpenVG::IntersectClip);
785 void PainterOpenVG::clipPath(const Path& path, PainterOpenVG::ClipOperation maskOp, WindRule clipRule)
787 #ifdef OPENVG_VERSION_1_1
789 m_surface->makeCurrent();
791 if (m_state->mask != VG_INVALID_HANDLE && !m_state->maskingChangedAndEnabled) {
792 // The parent's mask has been inherited - dispose the handle so that
793 // it won't be overwritten.
794 m_state->maskingChangedAndEnabled = true;
795 m_state->mask = VG_INVALID_HANDLE;
796 } else if (!m_state->maskingEnabled()) {
797 // None of the parent painter states had a mask enabled yet.
798 m_state->maskingChangedAndEnabled = true;
799 vgSeti(VG_MASKING, VG_TRUE);
800 // Make sure not to inherit previous mask state from previously written
801 // (but disabled) masks. For VG_FILL_MASK the first argument is ignored,
802 // we pass VG_INVALID_HANDLE which is what the OpenVG spec suggests.
803 vgMask(VG_INVALID_HANDLE, VG_FILL_MASK, 0, 0, m_surface->width(), m_surface->height());
806 // Intersect the path from the mask, or subtract it from there.
807 // (In either case we always decrease the visible area, never increase it,
808 // which means masking never has to modify scissor rectangles.)
809 vgSeti(VG_FILL_RULE, toVGFillRule(clipRule));
810 vgRenderToMask(path.platformPath()->vgPath(), VG_FILL_PATH, (VGMaskOperation) maskOp);
811 ASSERT_VG_NO_ERROR();
817 void PainterOpenVG::drawRect(const FloatRect& rect, VGbitfield specifiedPaintModes)
821 VGbitfield paintModes = 0;
822 if (!m_state->strokeDisabled())
823 paintModes |= VG_STROKE_PATH;
824 if (!m_state->fillDisabled())
825 paintModes |= VG_FILL_PATH;
827 paintModes &= specifiedPaintModes;
832 m_surface->makeCurrent();
834 VGPath path = vgCreatePath(
835 VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
836 1.0 /* scale */, 0.0 /* bias */,
837 5 /* expected number of segments */,
838 5 /* expected number of total coordinates */,
839 VG_PATH_CAPABILITY_APPEND_TO);
840 ASSERT_VG_NO_ERROR();
842 if (vguRect(path, rect.x(), rect.y(), rect.width(), rect.height()) == VGU_NO_ERROR) {
843 vgDrawPath(path, paintModes);
844 ASSERT_VG_NO_ERROR();
848 ASSERT_VG_NO_ERROR();
851 void PainterOpenVG::drawRoundedRect(const FloatRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, VGbitfield specifiedPaintModes)
855 VGbitfield paintModes = 0;
856 if (!m_state->strokeDisabled())
857 paintModes |= VG_STROKE_PATH;
858 if (!m_state->fillDisabled())
859 paintModes |= VG_FILL_PATH;
861 paintModes &= specifiedPaintModes;
866 m_surface->makeCurrent();
868 VGPath path = vgCreatePath(
869 VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
870 1.0 /* scale */, 0.0 /* bias */,
871 10 /* expected number of segments */,
872 25 /* expected number of total coordinates */,
873 VG_PATH_CAPABILITY_APPEND_TO);
874 ASSERT_VG_NO_ERROR();
876 // clamp corner arc sizes
877 FloatSize clampedTopLeft = FloatSize(topLeft).shrunkTo(rect.size()).expandedTo(FloatSize());
878 FloatSize clampedTopRight = FloatSize(topRight).shrunkTo(rect.size()).expandedTo(FloatSize());
879 FloatSize clampedBottomLeft = FloatSize(bottomLeft).shrunkTo(rect.size()).expandedTo(FloatSize());
880 FloatSize clampedBottomRight = FloatSize(bottomRight).shrunkTo(rect.size()).expandedTo(FloatSize());
882 // As OpenVG's coordinate system is flipped in comparison to WebKit's,
883 // we have to specify the opposite value for the "clockwise" value.
884 static const VGubyte pathSegments[] = {
896 // Also, the rounded rectangle path proceeds from the top to the bottom,
897 // requiring height distances and clamped radius sizes to be flipped.
898 const VGfloat pathData[] = {
899 rect.x() + clampedTopLeft.width(), rect.y(),
900 rect.width() - clampedTopLeft.width() - clampedTopRight.width(),
901 clampedTopRight.width(), clampedTopRight.height(), 0, clampedTopRight.width(), clampedTopRight.height(),
902 rect.height() - clampedTopRight.height() - clampedBottomRight.height(),
903 clampedBottomRight.width(), clampedBottomRight.height(), 0, -clampedBottomRight.width(), clampedBottomRight.height(),
904 -(rect.width() - clampedBottomLeft.width() - clampedBottomRight.width()),
905 clampedBottomLeft.width(), clampedBottomLeft.height(), 0, -clampedBottomLeft.width(), -clampedBottomLeft.height(),
906 -(rect.height() - clampedTopLeft.height() - clampedBottomLeft.height()),
907 clampedTopLeft.width(), clampedTopLeft.height(), 0, clampedTopLeft.width(), -clampedTopLeft.height(),
910 vgAppendPathData(path, 10, pathSegments, pathData);
911 vgDrawPath(path, paintModes);
913 ASSERT_VG_NO_ERROR();
916 void PainterOpenVG::drawLine(const IntPoint& from, const IntPoint& to)
920 if (m_state->strokeDisabled())
923 m_surface->makeCurrent();
925 VGPath path = vgCreatePath(
926 VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
927 1.0 /* scale */, 0.0 /* bias */,
928 2 /* expected number of segments */,
929 4 /* expected number of total coordinates */,
930 VG_PATH_CAPABILITY_APPEND_TO);
931 ASSERT_VG_NO_ERROR();
933 VGUErrorCode errorCode;
935 // Try to align lines to pixels, centering them between pixels for odd thickness values.
936 if (fmod(m_state->strokeThickness + 0.5, 2.0) < 1.0)
937 errorCode = vguLine(path, from.x(), from.y(), to.x(), to.y());
938 else if ((to.y() - from.y()) > (to.x() - from.x())) // more vertical than horizontal
939 errorCode = vguLine(path, from.x() + 0.5, from.y(), to.x() + 0.5, to.y());
941 errorCode = vguLine(path, from.x(), from.y() + 0.5, to.x(), to.y() + 0.5);
943 if (errorCode == VGU_NO_ERROR) {
944 vgDrawPath(path, VG_STROKE_PATH);
945 ASSERT_VG_NO_ERROR();
949 ASSERT_VG_NO_ERROR();
952 void PainterOpenVG::drawArc(const IntRect& rect, int startAngle, int angleSpan, VGbitfield specifiedPaintModes)
956 VGbitfield paintModes = 0;
957 if (!m_state->strokeDisabled())
958 paintModes |= VG_STROKE_PATH;
959 if (!m_state->fillDisabled())
960 paintModes |= VG_FILL_PATH;
962 paintModes &= specifiedPaintModes;
967 m_surface->makeCurrent();
969 VGPath path = vgCreatePath(
970 VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
971 1.0 /* scale */, 0.0 /* bias */,
972 2 /* expected number of segments */,
973 4 /* expected number of total coordinates */,
974 VG_PATH_CAPABILITY_APPEND_TO);
975 ASSERT_VG_NO_ERROR();
977 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) {
978 vgDrawPath(path, VG_STROKE_PATH);
979 ASSERT_VG_NO_ERROR();
983 ASSERT_VG_NO_ERROR();
986 void PainterOpenVG::drawEllipse(const IntRect& rect, VGbitfield specifiedPaintModes)
990 VGbitfield paintModes = 0;
991 if (!m_state->strokeDisabled())
992 paintModes |= VG_STROKE_PATH;
993 if (!m_state->fillDisabled())
994 paintModes |= VG_FILL_PATH;
996 paintModes &= specifiedPaintModes;
1001 m_surface->makeCurrent();
1003 VGPath path = vgCreatePath(
1004 VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
1005 1.0 /* scale */, 0.0 /* bias */,
1006 4 /* expected number of segments */,
1007 12 /* expected number of total coordinates */,
1008 VG_PATH_CAPABILITY_APPEND_TO);
1009 ASSERT_VG_NO_ERROR();
1011 if (vguEllipse(path, rect.x() + rect.width() / 2.0, rect.y() + rect.height() / 2.0, rect.width(), rect.height()) == VGU_NO_ERROR) {
1012 vgDrawPath(path, paintModes);
1013 ASSERT_VG_NO_ERROR();
1016 vgDestroyPath(path);
1017 ASSERT_VG_NO_ERROR();
1020 void PainterOpenVG::drawPolygon(size_t numPoints, const FloatPoint* points, VGbitfield specifiedPaintModes)
1024 VGbitfield paintModes = 0;
1025 if (!m_state->strokeDisabled())
1026 paintModes |= VG_STROKE_PATH;
1027 if (!m_state->fillDisabled())
1028 paintModes |= VG_FILL_PATH;
1030 paintModes &= specifiedPaintModes;
1035 m_surface->makeCurrent();
1037 // Path segments: all points + "close path".
1038 const VGint numSegments = numPoints + 1;
1039 const VGint numCoordinates = numPoints * 2;
1041 VGPath path = vgCreatePath(
1042 VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
1043 1.0 /* scale */, 0.0 /* bias */,
1044 numSegments /* expected number of segments */,
1045 numCoordinates /* expected number of total coordinates */,
1046 VG_PATH_CAPABILITY_APPEND_TO);
1047 ASSERT_VG_NO_ERROR();
1049 Vector<VGfloat> vgPoints(numCoordinates);
1050 for (int i = 0; i < numPoints; ++i) {
1051 vgPoints[i*2] = points[i].x();
1052 vgPoints[i*2 + 1] = points[i].y();
1055 if (vguPolygon(path, vgPoints.data(), numPoints, VG_TRUE /* closed */) == VGU_NO_ERROR) {
1056 vgDrawPath(path, paintModes);
1057 ASSERT_VG_NO_ERROR();
1060 vgDestroyPath(path);
1061 ASSERT_VG_NO_ERROR();
1064 void PainterOpenVG::drawImage(TiledImageOpenVG* tiledImage, const FloatRect& dst, const FloatRect& src)
1067 m_surface->makeCurrent();
1069 // If buffers can be larger than the maximum OpenVG image sizes,
1070 // we split them into tiles.
1071 IntRect drawnTiles = tiledImage->tilesInRect(src);
1072 AffineTransform srcToDstTransformation = makeMapBetweenRects(
1073 FloatRect(FloatPoint(0.0, 0.0), src.size()), dst);
1074 srcToDstTransformation.translate(-src.x(), -src.y());
1076 for (int yIndex = drawnTiles.y(); yIndex < drawnTiles.bottom(); ++yIndex) {
1077 for (int xIndex = drawnTiles.x(); xIndex < drawnTiles.right(); ++xIndex) {
1078 // The srcTile rectangle is an aligned tile cropped by the src rectangle.
1079 FloatRect tile(tiledImage->tileRect(xIndex, yIndex));
1080 FloatRect srcTile = intersection(src, tile);
1084 // If the image is drawn in full, all we need is the proper transformation
1085 // in order to get it drawn at the right spot on the surface.
1086 concatTransformation(AffineTransform(srcToDstTransformation).translate(tile.x(), tile.y()));
1088 // If only a part of the tile is drawn, we also need to clip the surface.
1089 if (srcTile != tile) {
1090 // Put boundaries relative to tile origin, as we already
1091 // translated to (x, y) with the transformation matrix.
1092 srcTile.move(-tile.x(), -tile.y());
1093 intersectClipRect(srcTile);
1096 VGImage image = tiledImage->tile(xIndex, yIndex);
1097 if (image != VG_INVALID_HANDLE) {
1099 ASSERT_VG_NO_ERROR();
1107 #ifdef OPENVG_VERSION_1_1
1108 void PainterOpenVG::drawText(VGFont vgFont, Vector<VGuint>& characters, VGfloat* adjustmentsX, VGfloat* adjustmentsY, const FloatPoint& point)
1112 VGbitfield paintModes = 0;
1114 if (m_state->textDrawingMode & TextModeClip)
1115 return; // unsupported for every port except CG at the time of writing
1116 if (m_state->textDrawingMode & TextModeFill && !m_state->fillDisabled())
1117 paintModes |= VG_FILL_PATH;
1118 if (m_state->textDrawingMode & TextModeStroke && !m_state->strokeDisabled())
1119 paintModes |= VG_STROKE_PATH;
1121 m_surface->makeCurrent();
1123 FloatPoint effectivePoint = m_state->surfaceTransformation.mapPoint(point);
1124 FloatPoint p = point;
1125 AffineTransform* originalTransformation = 0;
1127 // In case the font isn't drawn at a pixel-exact baseline and we can easily
1128 // fix that (which is the case for non-rotated affine transforms), let's
1129 // align the starting point to the pixel boundary in order to prevent
1130 // font rendering issues such as glyphs that appear off by a pixel.
1131 // This causes us to have inconsistent spacing between baselines in a
1132 // larger paragraph, but that seems to be the least of all evils.
1133 if ((fmod(effectivePoint.x() + 0.01, 1.0) > 0.02 || fmod(effectivePoint.y() + 0.01, 1.0) > 0.02)
1134 && isNonRotatedAffineTransformation(m_state->surfaceTransformation))
1136 originalTransformation = new AffineTransform(m_state->surfaceTransformation);
1137 setTransformation(AffineTransform(
1138 m_state->surfaceTransformation.a(), 0,
1139 0, m_state->surfaceTransformation.d(),
1140 roundf(effectivePoint.x()), roundf(effectivePoint.y())));
1144 const VGfloat vgPoint[2] = { p.x(), p.y() };
1145 vgSetfv(VG_GLYPH_ORIGIN, 2, vgPoint);
1146 ASSERT_VG_NO_ERROR();
1148 vgDrawGlyphs(vgFont, characters.size(), characters.data(),
1149 adjustmentsX, adjustmentsY, paintModes, VG_TRUE /* allow autohinting */);
1150 ASSERT_VG_NO_ERROR();
1152 if (originalTransformation) {
1153 setTransformation(*originalTransformation);
1154 delete originalTransformation;
1159 TiledImageOpenVG* PainterOpenVG::asNewNativeImage(const IntRect& src, VGImageFormat format)
1162 m_surface->sharedSurface()->makeCurrent();
1164 const IntSize vgMaxImageSize(vgGeti(VG_MAX_IMAGE_WIDTH), vgGeti(VG_MAX_IMAGE_HEIGHT));
1165 ASSERT_VG_NO_ERROR();
1167 const IntRect rect = intersection(src, IntRect(0, 0, m_surface->width(), m_surface->height()));
1168 TiledImageOpenVG* tiledImage = new TiledImageOpenVG(rect.size(), vgMaxImageSize);
1170 const int numColumns = tiledImage->numColumns();
1171 const int numRows = tiledImage->numRows();
1173 // Create the images as resources of the shared surface/context.
1174 for (int yIndex = 0; yIndex < numRows; ++yIndex) {
1175 for (int xIndex = 0; xIndex < numColumns; ++xIndex) {
1176 IntRect tileRect = tiledImage->tileRect(xIndex, yIndex);
1177 VGImage image = vgCreateImage(format, tileRect.width(), tileRect.height(), VG_IMAGE_QUALITY_FASTER);
1178 ASSERT_VG_NO_ERROR();
1180 tiledImage->setTile(xIndex, yIndex, image);
1184 // Fill the image contents with our own surface/context being current.
1185 m_surface->makeCurrent();
1187 for (int yIndex = 0; yIndex < numRows; ++yIndex) {
1188 for (int xIndex = 0; xIndex < numColumns; ++xIndex) {
1189 IntRect tileRect = tiledImage->tileRect(xIndex, yIndex);
1191 vgGetPixels(tiledImage->tile(xIndex, yIndex), 0, 0,
1192 rect.x() + tileRect.x(), rect.y() + tileRect.y(),
1193 tileRect.width(), tileRect.height());
1194 ASSERT_VG_NO_ERROR();
1201 void PainterOpenVG::save(PainterOpenVG::SaveMode saveMode)
1205 // If the underlying context/surface was switched away by someone without
1206 // telling us, it might not correspond to the one assigned to this painter.
1207 // Switch back so we can save the state properly. (Should happen rarely.)
1208 // Use DontSaveOrApplyPainterState mode in order to avoid recursion.
1209 m_surface->makeCurrent(SurfaceOpenVG::DontSaveOrApplyPainterState);
1211 if (saveMode == PainterOpenVG::CreateNewState) {
1212 m_state->saveMaskIfNecessary(this);
1213 PlatformPainterState* state = new PlatformPainterState(*m_state);
1214 m_stateStack.append(state);
1215 m_state = m_stateStack.last();
1216 } else if (saveMode == PainterOpenVG::CreateNewStateWithPaintStateOnly) {
1217 m_state->saveMaskIfNecessary(this);
1218 PlatformPainterState* state = new PlatformPainterState();
1219 state->copyPaintState(m_state);
1220 m_stateStack.append(state);
1221 m_state = m_stateStack.last();
1222 } else // if (saveMode == PainterOpenVG::KeepCurrentState)
1223 m_state->saveMaskIfNecessary(this);
1226 void PainterOpenVG::restore()
1228 ASSERT(m_stateStack.size() >= 2);
1229 m_surface->makeCurrent(SurfaceOpenVG::DontApplyPainterState);
1231 PlatformPainterState* state = m_stateStack.last();
1232 m_stateStack.removeLast();
1235 m_state = m_stateStack.last();
1236 m_state->applyState(this);