264 lines
10 KiB
C++
264 lines
10 KiB
C++
/******************************************************************************
|
|
* Copyright (c) 2018(-2023) STMicroelectronics.
|
|
* All rights reserved.
|
|
*
|
|
* This file is part of the TouchGFX 4.21.2 distribution.
|
|
*
|
|
* This software is licensed under terms that can be found in the LICENSE file in
|
|
* the root directory of this software component.
|
|
* If no LICENSE file comes with this software, it is provided AS-IS.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#include <math.h>
|
|
#include <touchgfx/hal/VectorRenderer.hpp>
|
|
#include <touchgfx/widgets/SVGImage.hpp>
|
|
|
|
// Declarations, defined in SVGDatabase.cpp
|
|
namespace SVGDatabase
|
|
{
|
|
const touchgfx::VGObject* getInstance();
|
|
uint16_t getInstanceSize();
|
|
} // namespace SVGDatabase
|
|
|
|
namespace touchgfx
|
|
{
|
|
void SVGImage::setSVG(uint16_t id)
|
|
{
|
|
if (id >= SVGDatabase::getInstanceSize())
|
|
{
|
|
svgId = SVG_INVALID;
|
|
return;
|
|
}
|
|
|
|
svgId = id;
|
|
reset();
|
|
|
|
// If the widget is empty, resize it to the size of the SVG
|
|
if (rect.isEmpty())
|
|
{
|
|
resizeToCurrentSVG();
|
|
}
|
|
}
|
|
|
|
void SVGImage::resizeToCurrentSVG()
|
|
{
|
|
if (svgId == SVG_INVALID)
|
|
{
|
|
return;
|
|
}
|
|
const VGObject* const table = SVGDatabase::getInstance();
|
|
const VGObject& image = table[svgId];
|
|
const Matrix3x3 matrix = getTransformationMatrix();
|
|
// Transform bounding box corners
|
|
const Matrix3x3::Point p1 = matrix.affineTransform(0.0f, 0.0f);
|
|
const Matrix3x3::Point p2 = matrix.affineTransform(0.0f, image.imageHeight);
|
|
const Matrix3x3::Point p3 = matrix.affineTransform(image.imageWidth, 0.0f);
|
|
const Matrix3x3::Point p4 = matrix.affineTransform(image.imageWidth, image.imageHeight);
|
|
// Find limits of transformed box
|
|
const float x_max = MAX(MAX(p1.x, p2.x), MAX(p3.x, p4.x));
|
|
const float y_max = MAX(MAX(p1.y, p2.y), MAX(p3.y, p4.y));
|
|
setWidthHeight(static_cast<int16_t>(ceilf(x_max)), static_cast<int16_t>(ceilf(y_max)));
|
|
}
|
|
|
|
void SVGImage::reset()
|
|
{
|
|
scaleX = 1.0f;
|
|
scaleY = 1.0f;
|
|
rotation = 0.0f;
|
|
rotationCenterX = 0.0f;
|
|
rotationCenterY = 0.0f;
|
|
imagePositionX = 0.0f;
|
|
imagePositionY = 0.0f;
|
|
}
|
|
|
|
float SVGImage::getImageWidth() const
|
|
{
|
|
if (svgId == SVG_INVALID)
|
|
{
|
|
return 0.0f;
|
|
}
|
|
const VGObject* const table = SVGDatabase::getInstance();
|
|
const VGObject& image = table[svgId];
|
|
return image.imageWidth;
|
|
}
|
|
|
|
float SVGImage::getImageHeight() const
|
|
{
|
|
if (svgId == SVG_INVALID)
|
|
{
|
|
return 0.0f;
|
|
}
|
|
const VGObject* const table = SVGDatabase::getInstance();
|
|
const VGObject& image = table[svgId];
|
|
return image.imageHeight;
|
|
}
|
|
|
|
void SVGImage::draw(const Rect& invalidatedArea) const
|
|
{
|
|
if (svgId == SVG_INVALID)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const VGObject* const table = SVGDatabase::getInstance();
|
|
const VGObject& image = table[svgId];
|
|
|
|
VectorRenderer* renderer = VectorRenderer::getInstance();
|
|
assert(renderer && "No VectorRenderer instance found!");
|
|
|
|
renderer->setup(*this, invalidatedArea);
|
|
|
|
Matrix3x3 matrix = getTransformationMatrix();
|
|
for (const VGShape* shape = image.shape; shape != 0; shape = shape->next)
|
|
{
|
|
Matrix3x3 shapeMatrix;
|
|
shapeMatrix.setAffineTransformation(shape->transformation);
|
|
shapeMatrix = matrix.multiply(shapeMatrix);
|
|
|
|
// Skip entire shape if bounding box does not cover invalidatedArea
|
|
// Find bounding box including stroke width
|
|
const float strokeWidthHalf = shape->strokeWidth / 2.0f;
|
|
const float bb_xMin = shape->boundingbox[0] - strokeWidthHalf;
|
|
const float bb_yMin = shape->boundingbox[1] - strokeWidthHalf;
|
|
const float bb_xMax = shape->boundingbox[2] + strokeWidthHalf;
|
|
const float bb_yMax = shape->boundingbox[3] + strokeWidthHalf;
|
|
// Transform bounding box corners
|
|
const Matrix3x3::Point p1 = shapeMatrix.affineTransform(bb_xMin, bb_yMin);
|
|
const Matrix3x3::Point p2 = shapeMatrix.affineTransform(bb_xMin, bb_yMax);
|
|
const Matrix3x3::Point p3 = shapeMatrix.affineTransform(bb_xMax, bb_yMin);
|
|
const Matrix3x3::Point p4 = shapeMatrix.affineTransform(bb_xMax, bb_yMax);
|
|
// Find limits of transformed box
|
|
const int16_t x_min = static_cast<int16_t>(floorf(MIN(MIN(p1.x, p2.x), MIN(p3.x, p4.x))));
|
|
const int16_t x_max = static_cast<int16_t>(ceilf(MAX(MAX(p1.x, p2.x), MAX(p3.x, p4.x))));
|
|
const int16_t y_min = static_cast<int16_t>(floorf(MIN(MIN(p1.y, p2.y), MIN(p3.y, p4.y))));
|
|
const int16_t y_max = static_cast<int16_t>(ceilf(MAX(MAX(p1.y, p2.y), MAX(p3.y, p4.y))));
|
|
Rect shapeBox(x_min, y_min, (x_max - x_min) + 1, (y_max - y_min) + 1);
|
|
if (shapeBox.intersect(invalidatedArea))
|
|
{
|
|
renderer->setTransformationMatrix(shapeMatrix);
|
|
|
|
// First fill
|
|
if (shape->fillMode != VG_NONE)
|
|
{
|
|
// Set drawing mode and color for this shape
|
|
renderer->setMode((shape->drawingMode == VG_FILL_EVEN_ODD) ? VectorRenderer::FILL_EVEN_ODD : VectorRenderer::FILL_NON_ZERO);
|
|
renderer->setAlpha(shape->fillAlpha);
|
|
|
|
// Set the fill color
|
|
switch (shape->fillMode)
|
|
{
|
|
case VG_NONE:
|
|
break; // Guarded above
|
|
case VG_FIXED_COLOR:
|
|
{
|
|
const VGFixedColor* color = reinterpret_cast<const VGFixedColor*>(shape->fillPaint);
|
|
renderer->setColor(color->argb);
|
|
break;
|
|
}
|
|
case VG_LINEAR_GRADIENT:
|
|
{
|
|
//Set shape box size on linear (and radial) gradient
|
|
const float width = shape->boundingbox[2] - shape->boundingbox[0];
|
|
const float height = shape->boundingbox[3] - shape->boundingbox[1];
|
|
|
|
const VGLinearGradient* linear = reinterpret_cast<const VGLinearGradient*>(shape->fillPaint);
|
|
renderer->setLinearGradient(linear->x0, linear->y0, linear->x1, linear->y1,
|
|
linear->stops, linear->stopPositions, linear->colors,
|
|
width, height,
|
|
linear->isSolid, linear->palette);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Draw this Shape
|
|
renderer->drawPath(shape->commands, shape->numCommands, shape->points, shape->numPoints, shape->boundingbox);
|
|
}
|
|
|
|
// Then stroke
|
|
if (shape->strokeMode != VG_NONE)
|
|
{
|
|
// Stroking is nonzero
|
|
renderer->setMode(VectorRenderer::STROKE);
|
|
renderer->setStrokeWidth(shape->strokeWidth);
|
|
renderer->setStrokeLineCap(shape->strokeLineCap);
|
|
renderer->setStrokeLineJoin(shape->strokeLineJoin);
|
|
renderer->setStrokeMiterLimit(shape->strokeMiterLimit);
|
|
renderer->setAlpha(shape->strokeAlpha);
|
|
|
|
// Set the fill color
|
|
switch (shape->strokeMode)
|
|
{
|
|
case VG_NONE:
|
|
break; // Guarded above
|
|
case VG_FIXED_COLOR:
|
|
{
|
|
const VGFixedColor* color = reinterpret_cast<const VGFixedColor*>(shape->strokePaint);
|
|
renderer->setColor(color->argb);
|
|
break;
|
|
}
|
|
case VG_LINEAR_GRADIENT:
|
|
{
|
|
//Set shape box size on linear (and radial) gradient
|
|
const float width = shape->boundingbox[2] - shape->boundingbox[0] + shape->strokeWidth;
|
|
const float height = shape->boundingbox[3] - shape->boundingbox[1] + shape->strokeWidth;
|
|
|
|
const VGLinearGradient* linear = reinterpret_cast<const VGLinearGradient*>(shape->strokePaint);
|
|
renderer->setLinearGradient(linear->x0, linear->y0, linear->x1, linear->y1,
|
|
linear->stops, linear->stopPositions, linear->colors,
|
|
width, height,
|
|
linear->isSolid, linear->palette);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Draw this Shape
|
|
const float boundingbox[4] = { shape->boundingbox[0] - strokeWidthHalf, shape->boundingbox[1] - strokeWidthHalf,
|
|
shape->boundingbox[2] + strokeWidthHalf, shape->boundingbox[3] + strokeWidthHalf };
|
|
renderer->drawPath(shape->commands, shape->numCommands, shape->points, shape->numPoints, boundingbox);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Finalize rendering
|
|
renderer->tearDown();
|
|
}
|
|
|
|
void SVGImage::invalidateContent() const
|
|
{
|
|
if (!visible || svgId == SVG_INVALID)
|
|
{
|
|
return;
|
|
}
|
|
const VGObject* const table = SVGDatabase::getInstance();
|
|
const VGObject& image = table[svgId];
|
|
const Matrix3x3 matrix = getTransformationMatrix();
|
|
// Transform bounding box corners
|
|
const Matrix3x3::Point p1 = matrix.affineTransform(image.x, image.y);
|
|
const Matrix3x3::Point p2 = matrix.affineTransform(image.x, image.y + image.height);
|
|
const Matrix3x3::Point p3 = matrix.affineTransform(image.x + image.width, image.y);
|
|
const Matrix3x3::Point p4 = matrix.affineTransform(image.x + image.width, image.y + image.height);
|
|
// Find limits of transformed box
|
|
const int16_t x_min = static_cast<int16_t>(floorf(MIN(MIN(p1.x, p2.x), MIN(p3.x, p4.x))));
|
|
const int16_t x_max = static_cast<int16_t>(ceilf(MAX(MAX(p1.x, p2.x), MAX(p3.x, p4.x))));
|
|
const int16_t y_min = static_cast<int16_t>(floorf(MIN(MIN(p1.y, p2.y), MIN(p3.y, p4.y))));
|
|
const int16_t y_max = static_cast<int16_t>(ceilf(MAX(MAX(p1.y, p2.y), MAX(p3.y, p4.y))));
|
|
Rect r(x_min, y_min, (x_max - x_min) + 1, (y_max - y_min) + 1);
|
|
invalidateRect(r);
|
|
}
|
|
|
|
touchgfx::Matrix3x3 SVGImage::getTransformationMatrix() const
|
|
{
|
|
Matrix3x3 matrix; // Implicit reset to identity matrix
|
|
if (svgId != SVG_INVALID)
|
|
{
|
|
matrix.scale(scaleX, scaleY);
|
|
matrix.translate(imagePositionX - rotationCenterX, imagePositionY - rotationCenterY);
|
|
matrix.rotate(rotation);
|
|
matrix.translate(rotationCenterX, rotationCenterY);
|
|
}
|
|
return matrix;
|
|
}
|
|
|
|
} // namespace touchgfx
|