继承 QPaintEngine 利用 QSvgRenderer 从SVG 图片中提取路径(QPainterPath)的方法
SVG 作为为可缩放矢量图形(Scalable Vector Graphics),易于编辑和维护,基于XML的文本文件存储,在网页设计、图标制作、数据可视化和其他图形相关的领域应用广泛。在应用工程中总会有动态修改路径、绘制颜色等需求,这就需要能够动态的解析 svg 文件,获取对应的路径、颜色等参数。有许多解析 svg 文件的类库,例如:svgHelper。svgHelper 通过 QDomDocument 对 svg 文件进行了解析并提取出了路径和颜色信息,虽然该方法可行,但是具体的解析过程还是相当繁琐,稍有不慎就会出现错误。另外,渐变色、画刷类型、字体、是否显示等属性该库就无法表示。
QT 提供了svg 库用于svg 的解析,但是大部分代码被封装起来,只提供了 QSvgRenderer 类和 QSvgGenerator 类给用户使用。而 QSvgRenderer 类能使用的只有 render() 方法。从 svg 库的文件目录可以看出来,svg 图像对应的节点、样式、字体等信息都有对应的描述类。

QSvgRenderer 加载 svg 文件后,由 QSvgTinyDocument 类负责解析文件。QSvgTinyDocument 通过 QSvgHandler 执行具体的解析工作,QSvgTinyDocument 存储了解析后的 SVG 结构、样式等信息。

QSvgTinyDocument 提供了 size()、 width() 、height() 、viewBox()、QSvgNode *namedNode(const QString &id) const; QSvgPaintStyleProperty *namedStyle(const QString &id) const;等方法,可以获取 SVG 解析后的相关信息。但是这些方法都在私有头文件中隐藏了起来。

如果要使用 QT 的 svg 库只有通过 render() 方法,该方法的具体执行由 QSvgTinyDocument 类的 draw() 方法负责。

QSvgTinyDocument 类的 draw() 方法调用 QSvgNode 类的 draw() 方法绘制具体的 node。qsvggraphics_p.h 定义了 SVG 支持的各种图形、路径、动画等的描述类,这些类都继承自 QSvgNode 并实现了虚函数 drawCommand(QPainter *p, QSvgExtraStates &states) 定义了对应图像如何在 QPainter 上绘制。
void QSvgTinyDocument::draw(QPainter *p, const QString &id,
const QRectF &bounds)
{
QSvgNode *node = scopeNode(id);
if (!node) {
qCDebug(lcSvgHandler, "Couldn't find node %s. Skipping rendering.", qPrintable(id));
return;
}
if (m_time == 0)
m_time = QDateTime::currentMSecsSinceEpoch();
if (node->displayMode() == QSvgNode::NoneMode)
return;
p->save();
const QRectF elementBounds = node->transformedBounds();
mapSourceToTarget(p, bounds, elementBounds);
QTransform originalTransform = p->worldTransform();
//XXX set default style on the painter
QPen pen(Qt::NoBrush, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
pen.setMiterLimit(4);
p->setPen(pen);
p->setBrush(Qt::black);
p->setRenderHint(QPainter::Antialiasing);
p->setRenderHint(QPainter::SmoothPixmapTransform);
QStack<QSvgNode*> parentApplyStack;
QSvgNode *parent = node->parent();
while (parent) {
parentApplyStack.push(parent);
parent = parent->parent();
}
for (int i = parentApplyStack.size() - 1; i >= 0; --i)
parentApplyStack[i]->applyStyle(p, m_states);
// Reset the world transform so that our parents don't affect
// the position
QTransform currentTransform = p->worldTransform();
p->setWorldTransform(originalTransform);
node->draw(p, m_states);
p->setWorldTransform(currentTransform);
for (int i = 0; i < parentApplyStack.size(); ++i)
parentApplyStack[i]->revertStyle(p, m_states);
//p->fillRect(bounds.adjusted(-5, -5, 5, 5), QColor(0, 0, 255, 100));
p->restore();
}
drawCommand 方法通过 QPainter 提供的绘制函数进行图形绘制。QPainter 的绘制动作由 QPaintDevice 通过 QPaintEngine 实现。

如果实现一个 SVG 的绘制引擎,那么所有的绘制动作都可以被该引擎截获并重新解释。GitHub项目 Compelling Data Designer 通过继承 QPaintEngine 实现了 SVG 的绘制引擎,通过 QSvgRenderer 的 render()方法重新获取了 QPainterPath 及其填充、线条等属性。
// svgpathdevice.h
class SvgEngine : public QPaintEngine
{
public:
SvgEngine();
QList<PainterPathEx> getSvgPath();
// QPaintEngine interface
bool begin(QPaintDevice *pdev) override;
bool end() override;
void updateState(const QPaintEngineState &state) override;
void drawPath(const QPainterPath &path) override;
void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) override;
void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) override;
void drawTextItem(const QPointF &p, const QTextItem &textItem) override;
QPaintEngine::Type type() const override;
private:
QList<PainterPathEx> pathList;
};
class SvgPathDevice : public QPaintDevice
{
public:
SvgPathDevice(int w, int h);
SvgPathDevice(const QSize &size);
~SvgPathDevice();
QList<PainterPathEx> getSvgPath();
qreal devicePixelRatio() const;
void setDevicePixelRatio(qreal scaleFactor);
// QPaintDevice interface
QPaintEngine *paintEngine() const override;
int devType() const override;
protected:
int metric(PaintDeviceMetric metric) const override;
private:
SvgEngine *engine;
qreal pixelRatio{1};
int width;
int height;
int qt_defaultDpiX() const;
int qt_defaultDpiY() const;
};
// endtypefactory.cpp
QList<PainterPathEx> EndTypeFactory::extractPath(QSvgRenderer *render, QString id)
{
if (!render->elementExists(id)) {
return QList<PainterPathEx>();
}
render->setAspectRatioMode(Qt::KeepAspectRatio);
auto size = render->defaultSize();
SvgPathDevice svgPath(size);
QPainter p(&svgPath);
render->render(&p, id, QRectF{0, 0, size.width()*1.0, size.height()*1.0});
p.end();
return svgPath.getSvgPath();
}
该项目中线条的终端形状全部使用 SVG 文件进行定义,使用 EndTypeFactory 类作为终端形状的工厂类管理所有 svg 图形。EndTypeFactory 类加载 svg 图像时,通过 SvgPathDevice 类获取了图像的 PainterPath,保证了后续绘制过程中使用矢量图进行绘制。具体代码可以查看 plugins/lineplugin 目录下的 svgpathdevice 和 endtypefactory 类文件。

继承 QPaintEngine 利用 QSvgRenderer 从SVG 图片中提取路径(QPainterPath)的方法的更多相关文章
- 利用sfntly的sfnttool.jar提取中文字体
雨忆博客中提到了sfntly(具体介绍可以看:https://code.google.com/p/sfntly/),利用其中sfnttool.jar就可以提取只包含指定字符的字体,如果想在页面中通过@ ...
- 利用Readability解决网页正文提取问题
分享: 利用Readability解决网页正文提取问题 做数据抓取和分析的各位亲们, 有没有遇到下面的难题呢? - 如何从各式各样的网页中提取正文!? 虽然可以用SS为各种网站写脚本做解析, 但是 ...
- 利用ArcGIS水文分析工具提取河网
转自原文 利用ArcGIS水文分析工具提取河网(转) DEM包含有多种信息,ArcToolBox提供了利用DEM提取河网的方法,但是操作比较烦琐(帮助可参看Hydrologic analysis sa ...
- 利用matlab自带函数快速提取二值图像的图像边缘 bwperim函数
clear all;close all;clc; I = imread('rice.png'); I = im2bw(I); J = bwperim(I); % 提取二值图像图像边缘 figure ...
- HelloServlet类继承HttpServlet利用HttpServletResponse对象
HelloServlet类继承HttpServlet利用HttpServletResponse对象 HelloServlet类的doGet()方法先得到username请求参数,对其进行中文字符编码转 ...
- 等效介质理论模型---利用S参数反演法提取超材料结构的等效参数
等效介质理论模型---利用S参数反演法提取超材料结构的等效参数 S参数反演法,即利用等效模型的传输矩阵和S参数求解超材料结构的等效折射率n和等效阻抗Z的过程.本文对等效介质理论模型进行了详细介绍,并提 ...
- Java -- JDBC_利用反射及 JDBC 元数据编写通用的查询方法
先利用 SQL 进行查询,得到结果集: 利用反射创建实体类的对象:创建对象: 获取结果集的列的别名: 再获取结果集的每一列的值, 结合 3 得到一个 Map,键:列的别名,值:列的值: 再利用反射为 ...
- Jmeter正则表达式提取器的使用方法(转)
下面简单介绍一下Jmeter正则表达式提取器的使用方法. 1.添加Jmeter正则表达式提取器:在具体的Request下添加Jmeter正则表达式提取器(Jmeter正则表达式在“后置处理器”下面) ...
- JDBC学习笔记(5)——利用反射及JDBC元数据编写通用的查询方法
JDBC元数据 1)DatabaseMetaData /** * 了解即可:DatabaseMetaData是描述数据库的元数据对象 * 可以由Connection得到 */ 具体的应用代码: @Te ...
- javascript 正则匹配 提取所有 preg_match_all matchAll方法
javascript 提取全部的的方法.javascript中没有matchAll这种方法. 用while来实现类似 PHP 中的preg_match_all() :(by default7#zbph ...
随机推荐
- UML的9种常用图与建模工具详解
UML即Unified Model Language,是一种建模语言,也是标准建模语言.在软件开发中,当系统规模比较复杂时,需要用图形抽象地来表达复杂的概念,让整个软件设计更具有可读性,可理解性,以便 ...
- Spring Boot前后端分离直接访问静态页+ajax实现动态网页
Spring Boot前后端分离直接访问静态页+ajax实现动态网页. 一般java里面Spring Boot项目的静态资源resources/下面有两个文件夹和一个配置文件,分别是static/目录 ...
- idea+maven打包.jar发布项目
开发完项目后,idea+maven环境打包成.jar包,才能发布项目.下面记录常用的几种打包方式. 一,通过mvn命令打包 比较专业的用法是通过mvn命令打包: mvn clean package - ...
- OV7670寄存器说明
I2C读出来的内容的地址,比datasheet给出的小9? add- 0x0 = 0x0add- 0x1 = 0x76add- 0x2 = 0x73add- 0x3 = 0x4add- 0x4 = 0 ...
- dart子类的继承
1 Dart中类的继承 1.子类使用extends关键字来继承父类 2.子类会继承父类里面可见的属性和方法,但是不会继承构造函数 3. 子类能够复写父类的方法 getter和setter方法 2Dar ...
- 注册全局组件(H5) 任意页面使用
在view下创建components文件夹. 在components下创建文件夹base. base文件夹是用来存放 基础组件的. 比如说页面中很多处都在使用的公共组件 如你需要自定义的按钮 在com ...
- 生成对抗网络GAN简介
本文分享自天翼云开发者社区<生成对抗网络GAN简介>,作者:王****青 生成对抗网络(Generative Adversarial Networks,GAN)是一种深度敏感词模型,用于生 ...
- JUC并发-4.wait和notify以及Atomic原理
大纲 1.wait()与notify()实现一个简易的内存队列 2.wait()与notify()的底层原理 3.分布式存储系统NameNode机制介绍 4.分布式存储系统的edits log机制介绍 ...
- MOS管的寄生电容
我们经常看到,在电源电路中,功率MOS管的G极经常会串联一个小电阻,几欧姆到几十欧姆不等,那么这个电阻用什么作用呢? 这个电阻的作用有2个作用:限制G极电流,抑制振荡. 限制G极电流MOS管是由电压驱 ...
- 机器学习 | 强化学习(5) | 价值函数拟合(Value Function Approximation)
价值函数拟合(Value Function Approximation) 导论(Introduction) 目前的价值函数都是基于打表法(lookup table)进行穷举 对于所有状态\(s\)都有 ...