Example #1
0
void ImageBufferData::transformColorSpace(const Vector<int>& lookUpTable)
{
    ASSERT(m_surface);

    VGint width = m_surface->width();
    VGint height = m_surface->height();

    VGubyte* data = new VGubyte[width * height * 4];
    VGubyte* currentPixel = data;

    m_surface->makeCurrent();

    vgReadPixels(data, width * 4, IMAGEBUFFER_VG_EXCHANGE_FORMAT, 0, 0, width, height);
    ASSERT_VG_NO_ERROR();

    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; currentPixel += 4, x++) {
            currentPixel[IMAGEBUFFER_A] = lookUpTable[currentPixel[IMAGEBUFFER_A]];
            currentPixel[IMAGEBUFFER_R] = lookUpTable[currentPixel[IMAGEBUFFER_R]];
            currentPixel[IMAGEBUFFER_G] = lookUpTable[currentPixel[IMAGEBUFFER_G]];
            currentPixel[IMAGEBUFFER_B] = lookUpTable[currentPixel[IMAGEBUFFER_B]];
        }
    }

    vgWritePixels(data, width * 4, IMAGEBUFFER_VG_EXCHANGE_FORMAT, 0, 0, width, height);
    ASSERT_VG_NO_ERROR();

    delete[] data;
}
Example #2
0
void PainterOpenVG::drawEllipse(const IntRect& rect, VGbitfield specifiedPaintModes)
{
    ASSERT(m_state);

    VGbitfield paintModes = 0;
    if (!m_state->strokeDisabled())
        paintModes |= VG_STROKE_PATH;
    if (!m_state->fillDisabled())
        paintModes |= VG_FILL_PATH;

    paintModes &= specifiedPaintModes;

    if (!paintModes)
        return;

    m_surface->makeCurrent();

    VGPath path = vgCreatePath(
        VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
        1.0 /* scale */, 0.0 /* bias */,
        4 /* expected number of segments */,
        12 /* expected number of total coordinates */,
        VG_PATH_CAPABILITY_APPEND_TO);
    ASSERT_VG_NO_ERROR();

    if (vguEllipse(path, rect.x() + rect.width() / 2.0, rect.y() + rect.height() / 2.0, rect.width(), rect.height()) == VGU_NO_ERROR) {
        vgDrawPath(path, paintModes);
        ASSERT_VG_NO_ERROR();
    }

    vgDestroyPath(path);
    ASSERT_VG_NO_ERROR();
}
Example #3
0
void TiledImageOpenVG::detachTiles()
{
    makeSharedContextCurrent(); // because we create new images

    int numTiles = m_tiles.size();
    VGImage newTile, originalTile;

    for (int i = 0; i < numTiles; ++i) {
        originalTile = m_tiles.at(i);

        if (originalTile == VG_INVALID_HANDLE)
            continue;

        VGImageFormat format = (VGImageFormat) vgGetParameteri(originalTile, VG_IMAGE_FORMAT);
        VGint width = vgGetParameteri(originalTile, VG_IMAGE_WIDTH);
        VGint height = vgGetParameteri(originalTile, VG_IMAGE_HEIGHT);
        ASSERT_VG_NO_ERROR();

        newTile = vgCreateImage(format, width, height, VG_IMAGE_QUALITY_FASTER);
        ASSERT_VG_NO_ERROR();

        vgCopyImage(newTile, 0, 0, originalTile, 0, 0, width, height, VG_FALSE /* dither */);
        ASSERT_VG_NO_ERROR();

        m_tiles.at(i) = newTile;
    }
}
Example #4
0
void PainterOpenVG::drawLine(const IntPoint& from, const IntPoint& to)
{
    ASSERT(m_state);

    if (m_state->strokeDisabled())
        return;

    m_surface->makeCurrent();

    VGPath path = vgCreatePath(
        VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
        1.0 /* scale */, 0.0 /* bias */,
        2 /* expected number of segments */,
        4 /* expected number of total coordinates */,
        VG_PATH_CAPABILITY_APPEND_TO);
    ASSERT_VG_NO_ERROR();

    VGUErrorCode errorCode;

    // Try to align lines to pixels, centering them between pixels for odd thickness values.
    if (fmod(m_state->strokeThickness + 0.5, 2.0) < 1.0)
        errorCode = vguLine(path, from.x(), from.y(), to.x(), to.y());
    else if ((to.y() - from.y()) > (to.x() - from.x())) // more vertical than horizontal
        errorCode = vguLine(path, from.x() + 0.5, from.y(), to.x() + 0.5, to.y());
    else
        errorCode = vguLine(path, from.x(), from.y() + 0.5, to.x(), to.y() + 0.5);

    if (errorCode == VGU_NO_ERROR) {
        vgDrawPath(path, VG_STROKE_PATH);
        ASSERT_VG_NO_ERROR();
    }

    vgDestroyPath(path);
    ASSERT_VG_NO_ERROR();
}
Example #5
0
NativeImagePtr RGBA32Buffer::asNewNativeImage() const
{
    if (!m_image) {
        static const VGImageFormat bufferFormat = VG_sARGB_8888_PRE;

        // Save memory by using 16-bit images for fully opaque images.
        const VGImageFormat imageFormat = hasAlpha()
            ? WEBKIT_OPENVG_NATIVE_IMAGE_FORMAT_s_8888
            : VG_sRGB_565;

#if PLATFORM(EGL)
        EGLDisplayOpenVG::current()->sharedPlatformSurface()->makeCurrent();
#endif

        const IntSize vgMaxImageSize(vgGeti(VG_MAX_IMAGE_WIDTH), vgGeti(VG_MAX_IMAGE_HEIGHT));
        ASSERT_VG_NO_ERROR();

        PassRefPtr<TiledImageOpenVG> tiledImage = adoptRef(new TiledImageOpenVG(
            IntSize(width(), height()), vgMaxImageSize));

        const int numColumns = tiledImage->numColumns();
        const int numRows = tiledImage->numRows();

        for (int yIndex = 0; yIndex < numRows; ++yIndex) {
            for (int xIndex = 0; xIndex < numColumns; ++xIndex) {
                IntRect tileRect = tiledImage->tileRect(xIndex, yIndex);
                VGImage tile = vgCreateImage(imageFormat,
                    tileRect.width(), tileRect.height(), VG_IMAGE_QUALITY_FASTER);
                ASSERT_VG_NO_ERROR();

                PixelData* pixelData = m_bytes.data();
                pixelData += (tileRect.y() * width()) + tileRect.x();

                vgImageSubData(tile, reinterpret_cast<unsigned char*>(pixelData),
                    width() * sizeof(PixelData), bufferFormat,
                    0, 0, tileRect.width(), tileRect.height());
                ASSERT_VG_NO_ERROR();

                tiledImage->setTile(xIndex, yIndex, tile);
            }
        }

        // Incomplete images will be returned without storing them in m_image,
        // so the image will be regenerated once loading is complete.
        if (m_status != FrameComplete)
            return tiledImage;

        m_image = tiledImage;
        m_bytes.clear();
    }

    return m_image; // and increase refcount
}
Example #6
0
VGPath SurfaceOpenVG::cachedPath(CachedPathDescriptor which)
{
    Vector<VGPath>& paths = cachedPaths();

    if (paths.isEmpty()) {
        paths.resize(CachedPathCount);
        paths.fill(VG_INVALID_HANDLE);
    }

    if (paths.at(which) == VG_INVALID_HANDLE) {
        sharedSurface()->makeCurrent();
        VGPath path = VG_INVALID_HANDLE;
        VGUErrorCode errorCode;

        switch (which) {
        case CachedLinePath:
            path = vgCreatePath(
                VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
                1.0 /* scale */, 0.0 /* bias */,
                2 /* expected number of segments */,
                4 /* expected number of total coordinates */,
                VG_PATH_CAPABILITY_APPEND_TO);
            ASSERT_VG_NO_ERROR();

            errorCode = vguLine(path, 0, 0, 1, 0);
            ASSERT(errorCode == VGU_NO_ERROR);
            break;

        case CachedRectPath:
            path = vgCreatePath(
                VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
                1.0 /* scale */, 0.0 /* bias */,
                5 /* expected number of segments */,
                5 /* expected number of total coordinates */,
                VG_PATH_CAPABILITY_APPEND_TO);
            ASSERT_VG_NO_ERROR();

            errorCode = vguRect(path, 0, 0, 1, 1);
            ASSERT(errorCode == VGU_NO_ERROR);
            break;

        default:
            ASSERT_NOT_REACHED();
        }

        paths.at(which) = path;
        makeCurrent();
    }

    return paths.at(which);
}
Example #7
0
    void applyStrokeStyle()
    {
        if (strokeStyle == DottedStroke) {
            VGfloat vgFloatArray[2] = { 1.0, 1.0 };
            vgSetfv(VG_STROKE_DASH_PATTERN, 2, vgFloatArray);
            vgSetf(VG_STROKE_DASH_PHASE, 0.0);
        } else if (strokeStyle == DashedStroke) {
            if (!strokeDashArray.size()) {
                VGfloat vgFloatArray[2] = { 4.0, 3.0 };
                vgSetfv(VG_STROKE_DASH_PATTERN, 2, vgFloatArray);
            } else {
                Vector<VGfloat> vgFloatArray(strokeDashArray.size());
                for (int i = 0; i < strokeDashArray.size(); ++i)
                    vgFloatArray[i] = strokeDashArray[i];

                vgSetfv(VG_STROKE_DASH_PATTERN, vgFloatArray.size(), vgFloatArray.data());
            }
            vgSetf(VG_STROKE_DASH_PHASE, strokeDashOffset);
        } else {
            vgSetfv(VG_STROKE_DASH_PATTERN, 0, 0);
            vgSetf(VG_STROKE_DASH_PHASE, 0.0);
        }

        ASSERT_VG_NO_ERROR();
    }
Example #8
0
void ImageBufferData::putImageData(ImageData*& source, const IntRect& sourceRect, const IntPoint& destPoint, const IntSize& size, VGImageFormat format)
{
    ASSERT(m_surface);

    ASSERT(sourceRect.width() > 0);
    ASSERT(sourceRect.height() > 0);

    // We expect the sourceRect to be a subset of the given source image.
    ASSERT(sourceRect.x() >= 0);
    ASSERT(sourceRect.y() >= 0);
    ASSERT(sourceRect.right() <= source->width());
    ASSERT(sourceRect.bottom() <= source->height());

    // The target origin point is the combined offset of sourceRect.location()
    // and destPoint.
    int destx = destPoint.x() + sourceRect.x();
    int desty = destPoint.y() + sourceRect.y();
    ASSERT(destx >= 0);
    ASSERT(desty >= 0);
    ASSERT(destx + sourceRect.width() <= size.width());
    ASSERT(desty + sourceRect.height() <= size.height());

    unsigned const char* data = source->data()->data()->data();
    int dataOffset = (sourceRect.y() * source->width() * 4) + (sourceRect.x() * 4);

    m_surface->makeCurrent();

    vgWritePixels(data + dataOffset, source->width() * 4, format,
        destx, desty, sourceRect.width(), sourceRect.height());
    ASSERT_VG_NO_ERROR();
}
PlatformPathOpenVG::~PlatformPathOpenVG()
{
    makeCompatibleContextCurrent();

    vgDestroyPath(m_vgPath);
    ASSERT_VG_NO_ERROR();
}
float Path::length()
{
    m_path->makeCompatibleContextCurrent();
    VGfloat length = vgPathLength(m_path->vgPath(), 0, vgGetParameteri(m_path->vgPath(), VG_PATH_NUM_SEGMENTS));
    ASSERT_VG_NO_ERROR();
    return length;
}
Example #11
0
PassRefPtr<ImageData> ImageBufferData::getImageData(const IntRect& rect, const IntSize& size, VGImageFormat format) const
{
    ASSERT(m_surface);

    PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height());
    unsigned char* data = result->data()->data()->data();

    // If this copy operation won't fill all of the pixels in the target image,
    // paint it black in order to avoid random uninitialized pixel garbage.
    if (rect.x() < 0 || rect.y() < 0 || (rect.right()) > size.width() || (rect.bottom()) > size.height())
        memset(data, 0, result->data()->length());

    if (!m_tiledImage)
        m_surface->makeCurrent();

    // OpenVG ignores pixels that are out of bounds, so we can just
    // call vgReadPixels() without any further safety assurances.
    if (m_surface->isCurrent()) {
        vgReadPixels(data, rect.width() * 4, format,
            rect.x(), rect.y(), rect.width(), rect.height());
    } else {
        vgGetImageSubData(m_tiledImage->tile(0, 0), data, rect.width() * 4, format,
            rect.x(), rect.y(), rect.width(), rect.height());
    }
    ASSERT_VG_NO_ERROR();

    return result;
}
void Path::addArcTo(const FloatPoint& point1, const FloatPoint& point2, float radius)
{
    // See http://philip.html5.org/tests/canvas/suite/tests/spec.html#arcto.

    const FloatPoint& point0 = m_path->m_currentPoint;
    if (!radius || point0 == point1 || point1 == point2) {
        addLineTo(point1);
        return;
    }

    FloatSize v01 = point0 - point1;
    FloatSize v21 = point2 - point1;

    // sin(A - B) = sin(A) * cos(B) - sin(B) * cos(A)
    double cross = v01.width() * v21.height() - v01.height() * v21.width();

    if (fabs(cross) < 1E-10) {
        // on one line
        addLineTo(point1);
        return;
    }

    double d01 = hypot(v01.width(), v01.height());
    double d21 = hypot(v21.width(), v21.height());
    double angle = (piDouble - fabs(asin(cross / (d01 * d21)))) * 0.5;
    double span = radius * tan(angle);
    double rate = span / d01;
    FloatPoint startPoint = FloatPoint(point1.x() + v01.width() * rate,
                                       point1.y() + v01.height() * rate);
    rate = span / d21;
    FloatPoint endPoint = FloatPoint(point1.x() + v21.width() * rate,
                                     point1.y() + v21.height() * rate);

    // Fa: large arc flag, makes the difference between SCWARC_TO and LCWARC_TO
    //     respectively SCCWARC_TO and LCCWARC_TO arcs. We always use small
    //     arcs for arcTo(), as the arc is defined as the "shortest arc" of the
    //     circle specified in HTML 5.

    // Fs: sweep flag, specifying whether the arc is drawn in increasing (true)
    //     or decreasing (0) direction.
    const bool anticlockwise = cross < 0;

    // Translate the large arc and sweep flags into an OpenVG segment command.
    const VGubyte segmentCommand = anticlockwise ? VG_SCCWARC_TO_ABS : VG_SCWARC_TO_ABS;

    const VGubyte pathSegments[] = {
        VG_LINE_TO_ABS,
        segmentCommand
    };
    const VGfloat pathData[] = {
        startPoint.x(), startPoint.y(),
        radius, radius, 0, endPoint.x(), endPoint.y()
    };

    m_path->makeCompatibleContextCurrent();
    vgAppendPathData(m_path->vgPath(), 2, pathSegments, pathData);
    ASSERT_VG_NO_ERROR();

    m_path->m_currentPoint = endPoint;
}
Example #13
0
static void setVGSolidColor(VGPaintMode paintMode, const Color& color)
{
    VGPaint paint = vgCreatePaint();
    vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
    vgSetColor(paint, colorToVGColor(color));
    vgSetPaint(paint, paintMode);
    vgDestroyPaint(paint);
    ASSERT_VG_NO_ERROR();
}
Example #14
0
void PainterOpenVG::setStrokeThickness(float thickness)
{
    ASSERT(m_state);
    m_surface->makeCurrent();

    m_state->strokeThickness = thickness;
    vgSetf(VG_STROKE_LINE_WIDTH, thickness);
    ASSERT_VG_NO_ERROR();
}
Example #15
0
void PainterOpenVG::setLineCap(LineCap lineCap)
{
    ASSERT(m_state);
    m_surface->makeCurrent();

    m_state->strokeLineCap = lineCap;
    vgSeti(VG_STROKE_CAP_STYLE, toVGCapStyle(lineCap));
    ASSERT_VG_NO_ERROR();
}
Example #16
0
void PainterOpenVG::setLineJoin(LineJoin lineJoin)
{
    ASSERT(m_state);
    m_surface->makeCurrent();

    m_state->strokeLineJoin = lineJoin;
    vgSeti(VG_STROKE_JOIN_STYLE, toVGJoinStyle(lineJoin));
    ASSERT_VG_NO_ERROR();
}
Example #17
0
void PainterOpenVG::setMiterLimit(float miterLimit)
{
    ASSERT(m_state);
    m_surface->makeCurrent();

    m_state->strokeMiterLimit = miterLimit;
    vgSetf(VG_STROKE_MITER_LIMIT, miterLimit);
    ASSERT_VG_NO_ERROR();
}
Example #18
0
void PainterOpenVG::drawPolygon(size_t numPoints, const FloatPoint* points, VGbitfield specifiedPaintModes)
{
    ASSERT(m_state);

    VGbitfield paintModes = 0;
    if (!m_state->strokeDisabled())
        paintModes |= VG_STROKE_PATH;
    if (!m_state->fillDisabled())
        paintModes |= VG_FILL_PATH;

    paintModes &= specifiedPaintModes;

    if (!paintModes)
        return;

    m_surface->makeCurrent();

    // Path segments: all points + "close path".
    const VGint numSegments = numPoints + 1;
    const VGint numCoordinates = numPoints * 2;

    VGPath path = vgCreatePath(
        VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
        1.0 /* scale */, 0.0 /* bias */,
        numSegments /* expected number of segments */,
        numCoordinates /* expected number of total coordinates */,
        VG_PATH_CAPABILITY_APPEND_TO);
    ASSERT_VG_NO_ERROR();

    Vector<VGfloat> vgPoints(numCoordinates);
    for (int i = 0; i < numPoints; ++i) {
        vgPoints[i*2]     = points[i].x();
        vgPoints[i*2 + 1] = points[i].y();
    }

    if (vguPolygon(path, vgPoints.data(), numPoints, VG_TRUE /* closed */) == VGU_NO_ERROR) {
        vgDrawPath(path, paintModes);
        ASSERT_VG_NO_ERROR();
    }

    vgDestroyPath(path);
    ASSERT_VG_NO_ERROR();
}
Example #19
0
    void applyScissorRect()
    {
        if (scissoringEnabled) {
            vgSeti(VG_SCISSORING, VG_TRUE);
            vgSetfv(VG_SCISSOR_RECTS, 4, VGRect(scissorRect).toVGfloat());
        } else
            vgSeti(VG_SCISSORING, VG_FALSE);

        ASSERT_VG_NO_ERROR();
    }
PlatformPathOpenVG& PlatformPathOpenVG::operator=(const PlatformPathOpenVG& other)
{
    if (&other != this) {
        clear();
        // makeCompatibleContextCurrent() is called by clear(), so not necessary here.
        vgAppendPath(m_vgPath, other.m_vgPath);
        ASSERT_VG_NO_ERROR();
    }
    return *this;
}
PlatformPathOpenVG::PlatformPathOpenVG(const PlatformPathOpenVG& other)
    : SharedResourceOpenVG()
    , m_currentPoint(other.m_currentPoint)
    , m_subpathStartPoint(other.m_subpathStartPoint)
{
    createPath();
    // makeCompatibleContextCurrent() is called by createPath(), so not necessary here.
    vgAppendPath(m_vgPath, other.m_vgPath);
    ASSERT_VG_NO_ERROR();
}
void PlatformPathOpenVG::clear()
{
    makeCompatibleContextCurrent();

    vgClearPath(m_vgPath, WEBKIT_VG_PATH_CAPABILITIES);
    ASSERT_VG_NO_ERROR();

    m_subpathStartPoint.setX(0);
    m_subpathStartPoint.setY(0);
    m_currentPoint = m_subpathStartPoint;
}
void Path::addBezierCurveTo(const FloatPoint& controlPoint1, const FloatPoint& controlPoint2, const FloatPoint& endPoint)
{
    static const VGubyte pathSegments[] = { VG_CUBIC_TO_ABS };
    const VGfloat pathData[] = { controlPoint1.x(), controlPoint1.y(), controlPoint2.x(), controlPoint2.y(), endPoint.x(), endPoint.y() };

    m_path->makeCompatibleContextCurrent();
    vgAppendPathData(m_path->vgPath(), 1, pathSegments, pathData);
    ASSERT_VG_NO_ERROR();

    m_path->m_currentPoint = endPoint;
}
void Path::addLineTo(const FloatPoint& point)
{
    static const VGubyte pathSegments[] = { VG_LINE_TO_ABS };
    const VGfloat pathData[] = { point.x(), point.y() };

    m_path->makeCompatibleContextCurrent();
    vgAppendPathData(m_path->vgPath(), 1, pathSegments, pathData);
    ASSERT_VG_NO_ERROR();

    m_path->m_currentPoint = point;
}
void PlatformPathOpenVG::createPath()
{
    makeResourceCreationContextCurrent();

    m_vgPath = vgCreatePath(
        VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
        1.0 /* scale */, 0.0 /* bias */,
        0 /* expected number of segments */,
        0 /* expected number of total coordinates */,
        WEBKIT_VG_PATH_CAPABILITIES);
    ASSERT_VG_NO_ERROR();
}
Example #26
0
NativeImagePtr RGBA32Buffer::asNewNativeImage() const
{
    static const VGImageFormat bufferFormat = VG_sARGB_8888_PRE;
    // Save memory by using 16-bit images for fully opaque images.
    const VGImageFormat imageFormat = hasAlpha() ? bufferFormat : VG_sRGB_565;

#if PLATFORM(EGL)
    EGLDisplayOpenVG::current()->sharedPlatformSurface()->makeCurrent();
#endif

    const IntSize vgMaxImageSize(vgGeti(VG_MAX_IMAGE_WIDTH), vgGeti(VG_MAX_IMAGE_HEIGHT));
    ASSERT_VG_NO_ERROR();

    TiledImageOpenVG* tiledImage = new TiledImageOpenVG(IntSize(width(), height()), vgMaxImageSize);

    const int numColumns = tiledImage->numColumns();
    const int numRows = tiledImage->numRows();

    for (int yIndex = 0; yIndex < numRows; ++yIndex) {
        for (int xIndex = 0; xIndex < numColumns; ++xIndex) {
            IntRect tileRect = tiledImage->tileRect(xIndex, yIndex);
            VGImage image = vgCreateImage(imageFormat,
                tileRect.width(), tileRect.height(), VG_IMAGE_QUALITY_FASTER);
            ASSERT_VG_NO_ERROR();

            PixelData* pixelData = const_cast<PixelData*>(m_bytes);
            pixelData += (tileRect.y() * width()) + tileRect.x();

            vgImageSubData(image, reinterpret_cast<unsigned char*>(pixelData),
                width() * sizeof(PixelData), bufferFormat,
                0, 0, tileRect.width(), tileRect.height());
            ASSERT_VG_NO_ERROR();

            tiledImage->setTile(xIndex, yIndex, image);
        }
    }

    return tiledImage;
}
FloatRect Path::boundingRect() const
{
    VGfloat minX;
    VGfloat minY;
    VGfloat width;
    VGfloat height;

    m_path->makeCompatibleContextCurrent();
    vgPathBounds(m_path->vgPath(), &minX, &minY, &width, &height);
    ASSERT_VG_NO_ERROR();

    return FloatRect(FloatPoint(minX, minY), FloatSize(width, height));
}
Example #28
0
    void applyTransformationMatrix(PainterOpenVG* painter)
    {
        // There are *five* separate transforms that can be applied to OpenVG as of 1.1
        // but it is not clear that we need to set them separately.  Instead we set them
        // all right here and let this be a call to essentially set the world transformation!
        VGMatrix vgMatrix(surfaceTransformationMatrix);
        const VGfloat* vgFloatArray = vgMatrix.toVGfloat();

        vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
        vgLoadMatrix(vgFloatArray);
        ASSERT_VG_NO_ERROR();

        vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
        vgLoadMatrix(vgFloatArray);
        ASSERT_VG_NO_ERROR();

#ifdef OPENVG_VERSION_1_1
        vgSeti(VG_MATRIX_MODE, VG_MATRIX_GLYPH_USER_TO_SURFACE);
        vgLoadMatrix(vgFloatArray);
        ASSERT_VG_NO_ERROR();
#endif
    }
FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& point, int height, int from, int to, int width) const
{
    const FontPlatformData& fontPlatformData = primaryFont()->platformData();
    const float scaleFactor = fontPlatformData.scaleFactor();
    Olympia::Platform::Text::Font* font = fontPlatformData.font();
    Olympia::Platform::Text::DrawParam drawParam;

    String sanitized = setupTextDrawing(this, run, &drawParam);
    adjustOffsetsForTextDrawing(run, from, to);

#if PLATFORM(EGL) // FIXME: remove after Text API fixes shared context handling
    if (eglGetCurrentContext() == EGL_NO_CONTEXT)
        EGLDisplayOpenVG::current()->sharedPlatformSurface()->makeCurrent();
#endif
    double fromX;
    FontPlatformData::engine()->textPosToX(from, fromX, *font,
        sanitized.characters(), sanitized.length(), drawParam);
    ASSERT_VG_NO_ERROR();
    fromX *= scaleFactor;

    double toX;
    FontPlatformData::engine()->textPosToX(to, toX, *font,
        sanitized.characters(), sanitized.length(), drawParam);
    ASSERT_VG_NO_ERROR();
    toX *= scaleFactor;

    if (width) {
        double bound = width * scaleFactor;
        if (from == run.length())
            fromX = bound;
        if (to == run.length())
            toX = bound;
    }

    return (toX > fromX)
        ? FloatRect(point.x() + fromX, point.y(), toX - fromX, height)
        : FloatRect(point.x() + toX, point.y(), fromX - toX, height);
}
void Path::closeSubpath()
{
    static const VGubyte pathSegments[] = { VG_CLOSE_PATH };
    // pathData must not be 0, but certain compilers also don't create
    // zero-size arrays. So let's use a random aligned value (sizeof(VGfloat)),
    // it won't be accessed anyways as VG_CLOSE_PATH doesn't take coordinates.
    static const VGfloat* pathData = reinterpret_cast<VGfloat*>(sizeof(VGfloat));

    m_path->makeCompatibleContextCurrent();
    vgAppendPathData(m_path->vgPath(), 1, pathSegments, pathData);
    ASSERT_VG_NO_ERROR();

    m_path->m_currentPoint = m_path->m_subpathStartPoint;
}