0
点赞
收藏
分享

微信扫一扫

Qt使用九宫格原理缩放图片


2020.07.21更新

一.QSS

关于九宫格,首先要学习下QSS的border-image,Qt Assistant中关于Border Image有如下介绍:
A border image is an image that is composed of nine parts (top left, top center, top right, center left, center, center right, bottom left, bottom center, and bottom right). When a border of a certain size is required, the corner parts are used as is, and the top, right, bottom, and left parts are stretched or repeated to produce a border with the desired size.
See the CSS3 Draft Specification for details.
Border Image是由九个部分(左上、中上、右上、左中、中、右中、左下、左下、中下和右下)组成的图像。当Border Image应用到指定目标时,四个角的部分原样使用,并拉伸或平铺顶部、右侧、底部和左侧部分,以生成符合目标尺寸的边框。
具体参考CSS3语法
也就是说border-image在贴图时可以指定参数,将图片分成九个部分,分别贴到目标的相应位置,即通常说的九宫格贴图。
9宫格缩放规则如下:
1、将一张图分割成9块
2、四个角(1、3、7、9)在缩放的时候是保持大小不变
3、图块2、8仅当宽度变化时缩放宽度
4、图块4、6仅当高度变化时缩放高度
5、图块5当图片大小发生变化,宽度和高度都进行缩放

Qt使用九宫格原理缩放图片_缩放


普通border-image和九宫格border-image的效果如下

原图   

Qt使用九宫格原理缩放图片_#include_02


效果图

Qt使用九宫格原理缩放图片_图片_03


可以看出普通border-image时,图片会发虚,特别是四个角

普通border-image QSS代码

border-image: url(:/icons/test.png);

九宫格border-image QSS 代码

border-image: url(:/icons/test.png) 8 8 8 8 stretch stretch;
border-width: 8px 8px 8px 8px;

border-width 后面的四个数字按顺序分别为:
border-top 的高 8px
border-right 的宽 8px
border-bottom 的高 8px
border-left 的宽 8px
当然这四个数字也可以分别指定:
border-top-width: 8px
border-right-width: 8px
border-bottom-width: 8px
border-left-width: 8px
border-image后面的参数按顺序分别为:
背景图的路径
四个数字按顺序分别为
背景图中 最上面的 8px 高的图像填充到目标 Widget 的 border-top
背景图中 最右边的 8px 宽的图像填充到目标 Widget 的 border-right
背景图中 最下边的 8px 高的图像填充到目标 Widget 的 border-bottom
背景图中 最左边的 8px 宽的图像填充到目标 Widget 的 border-left
倒数第二个参数 stretch 或 repeat 指定水平方向的缩放或者平铺
倒数第一个参数 stretch 或 repeat 指定垂直方向的缩放或者平铺
需要注意的是border-image后面的数字参数没有单位
那么如何确定上面的边框参数呢?
原则就是尽量确保四个角包含在边框的相交区域里,这样的话边框在缩放的时候才不会变形发虚,我使用的8像素就是这么来的,其实用QQ截图大概估一下就行,如果需要精确点的数据,用图片编辑软件例如Photoshop把背景图按像素比例放大,然后就可以清晰的看到像素数据,边宽也就一目了然了

二.自绘

自绘的核心是将图片先按照上述原理分隔成9个部分,然后在目标widget区域内使用拉伸或平铺绘图
代码可参考​​​http://qtdebug.com/qtbook-paint-nine-patch-painter/​​​ 这里贴一下备忘
QHNinePatchPainter.h

#ifndef QHNINEPATCHPAINTER_H
#define QHNINEPATCHPAINTER_H

#include <QRect>
#include <QMargins>
#include <QPainter>
#include <QPixmap>

class QHNinePatchPainter {
public:
// background:背景图 上下左右四个边宽 水平和垂直是否使用拉伸缩放.
QHNinePatchPainter(const QPixmap &background,
int left, int top, int right, int bottom,
bool horizontalStretch = true, bool verticalStretch = true);

~QHNinePatchPainter();

void draw(QPainter *painter, const QRect &rect) const;

// 根据九宫格 4 边的宽度把 rect 按九宫格分割为 9 个 rect: 左、左上、上、右上、右、右下、下、左下、中间.
QList<QRect> calculateNinePatchRects(const QRect &rect) const;

// 对图片进行缩放.
QPixmap scalePixmap(const QPixmap &pixmap, const QSize &size) const;

public:
int m_left;
int m_top;
int m_right;
int m_bottom;
bool m_horizontalStretch;
bool m_verticalStretch;

QPixmap m_leftPixmap;
QPixmap m_topLeftPixmap;
QPixmap m_topPixmap;
QPixmap m_topRightPixmap;
QPixmap m_rightPixmap;
QPixmap m_bottomLeftPixmap;
QPixmap m_bottomPixmap;
QPixmap m_bottomRightPixmap;
QPixmap m_centerPixmap;
};

#endif // QHNINEPATCHPAINTER_H

QHNinePatchPainter.cpp

#include "QHNinePatchPainter.h"

#include <QDebug>

QHNinePatchPainter::QHNinePatchPainter(const QPixmap &background,
int left, int top, int right, int bottom,
bool horizontalStretch, bool verticalStretch)
: m_left(left)
, m_top(top)
, m_right(right)
, m_bottom(bottom)
, m_horizontalStretch(horizontalStretch)
, m_verticalStretch(verticalStretch)
{
// 把 background 分割成 9 个子图.
QRect pixmapRect(0, 0, background.width(), background.height());
QList<QRect> rects = calculateNinePatchRects(pixmapRect);

m_leftPixmap = background.copy(rects.at(0));
m_topLeftPixmap = background.copy(rects.at(1));
m_topPixmap = background.copy(rects.at(2));
m_topRightPixmap = background.copy(rects.at(3));
m_rightPixmap = background.copy(rects.at(4));
m_bottomRightPixmap = background.copy(rects.at(5));
m_bottomPixmap = background.copy(rects.at(6));
m_bottomLeftPixmap = background.copy(rects.at(7));
m_centerPixmap = background.copy(rects.at(8));
}

QHNinePatchPainter::~QHNinePatchPainter()
{

}

void QHNinePatchPainter::draw(QPainter *painter, const QRect &rect) const
{
// 把要绘制的 Rect 分割成 9 个部分,上,右,下,左 4 边的宽和背景图的一样.
QList<QRect> rects = calculateNinePatchRects(rect);

QRect leftRect = rects.at(0);
QRect topLeftRect = rects.at(1);
QRect topRect = rects.at(2);
QRect topRightRect = rects.at(3);
QRect rightRect = rects.at(4);
QRect bottomRightRect = rects.at(5);
QRect bottomRect = rects.at(6);
QRect bottomLeftRect = rects.at(7);
QRect centerRect = rects.at(8);

// 绘制 4 个角.
painter->drawPixmap(topLeftRect, m_topLeftPixmap);
painter->drawPixmap(topRightRect, m_topRightPixmap);
painter->drawPixmap(bottomRightRect, m_bottomRightPixmap);
painter->drawPixmap(bottomLeftRect, m_bottomLeftPixmap);

// 绘制左、右边.
if (m_horizontalStretch)
{
// 拉伸.
painter->drawPixmap(leftRect, scalePixmap(m_leftPixmap, leftRect.size()));
painter->drawPixmap(rightRect, scalePixmap(m_rightPixmap, rightRect.size()));
} else
{
// 平铺.
painter->drawTiledPixmap(leftRect, m_leftPixmap);
painter->drawTiledPixmap(rightRect, m_rightPixmap);
}

// 绘制上、下边.
if (m_verticalStretch)
{
// 拉伸.
painter->drawPixmap(topRect, scalePixmap(m_topPixmap, topRect.size()));
painter->drawPixmap(bottomRect, scalePixmap(m_bottomPixmap, bottomRect.size()));
}
else
{
// 平铺.
painter->drawTiledPixmap(topRect, m_topPixmap);
painter->drawTiledPixmap(bottomRect, m_bottomPixmap);
}

int pmw = m_centerPixmap.width();
int pmh = m_centerPixmap.height();
int crw = centerRect.width();
int crh = centerRect.height();

// 绘制中间部分(最简单办法就是中间部分都进行拉伸).
if (m_horizontalStretch && m_verticalStretch)
{
// 水平和垂直都拉伸.
painter->drawPixmap(centerRect, scalePixmap(m_centerPixmap, centerRect.size()));
}
else if (m_horizontalStretch && !m_verticalStretch)
{
// 水平拉伸,垂直平铺.
QSize size(crw, pmh);
QPixmap centerPixmap = scalePixmap(m_centerPixmap, size);
painter->drawTiledPixmap(centerRect, centerPixmap);
}
else if (!m_horizontalStretch && m_verticalStretch)
{
// 水平平铺,垂直拉伸.
QSize size(pmw, crh);
QPixmap centerPixmap = scalePixmap(m_centerPixmap, size);
painter->drawTiledPixmap(centerRect, centerPixmap);
}
else
{
// 水平和垂直都平铺.
painter->drawTiledPixmap(centerRect, m_centerPixmap);
}
}

QList<QRect> QHNinePatchPainter::calculateNinePatchRects(const QRect &rect) const
{
int x = rect.x();
int y = rect.y();
int cw = rect.width() - m_left - m_right; // 中间部分的宽.
int ch = rect.height() - m_top - m_bottom; // 中间部分的高.

// 根据把 rect 分割成 9 个部分: 左、左上、上、右上、右、右下、下、左下、中间.
QRect leftRect(x, y + m_top, m_left, ch);
QRect topLeftRect(x, y, m_left, m_top);
QRect topRect(x + m_left, y, cw, m_top);
QRect topRightRect(x + m_left + cw, y, m_right, m_top);
QRect rightRect(x + m_left + cw, y + m_top, m_right, ch);
QRect bottomRightRect(x + m_left + cw, y + m_top + ch, m_right, m_bottom);
QRect bottomRect(x + m_left, y + m_top + ch, cw, m_bottom);
QRect bottomLeftRect(x, y + m_top + ch, m_left, m_bottom);
QRect centerRect(x + m_left, y + m_top, cw, ch);

return QList<QRect>() << leftRect << topLeftRect
<< topRect << topRightRect << rightRect
<< bottomRightRect << bottomRect << bottomLeftRect
<< centerRect;
}

QPixmap QHNinePatchPainter::scalePixmap(const QPixmap &pixmap, const QSize &size) const
{
return pixmap.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}

使用方法:

m_painter = new QHNinePatchPainter(QPixmap(":/icons/test.png"),8,8,8,8);
QPainter p(this);
m_painter->draw(&p,QRect(220,150,200,100));

效果图

Qt使用九宫格原理缩放图片_缩放_04


完整测试代码如下:

QHWidget.h

#ifndef QHWIDGET_H
#define QHWIDGET_H

#include <QWidget>

#include "QHNinePatchPainter.h"

namespace Ui {
class QHWidget;
}

class QHWidget : public QWidget
{
Q_OBJECT

public:
explicit QHWidget(QWidget *parent = nullptr);
~QHWidget();

protected:
void paintEvent(QPaintEvent *event) override;

private:
Ui::QHWidget *ui;

QHNinePatchPainter *m_painter;
};

#endif // QHWIDGET_H

QHWidget.cpp

#include "QHWidget.h"
#include "ui_QHWidget.h"

QHWidget::QHWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::QHWidget)
{
ui->setupUi(this);

m_painter = new QHNinePatchPainter(QPixmap(":/icons/test.png"),8,8,8,8);

ui->pushButton->setStyleSheet("border-image: url(:/icons/test.png); "
"color: white");
ui->pushButton_2->setStyleSheet("border-image: url(:/icons/test.png) 8 8 8 8 stretch stretch;"
"border-width: 8px 8px 8px 8px;"
"color: white;");
}

QHWidget::~QHWidget()
{
delete ui;
}

void QHWidget::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)

QPainter p(this);

p.drawPixmap(QRect(10,150,200,100),QPixmap(":/icons/test.png"));
p.setPen(Qt::white);
p.drawText(QRect(10,150,200,100),Qt::AlignCenter,"normal draw");

m_painter->draw(&p,QRect(220,150,200,100));
p.drawText(QRect(220,150,200,100),Qt::AlignCenter,"nine-patch draw");
}

参考链接:​​http://qtdebug.com/qtbook-paint-nine-patch-painter/​​

举报

相关推荐

0 条评论