Files
hfut-bishe/client/gui/editor/EntityCutoutUtils.cpp
2026-04-08 14:37:01 +08:00

111 lines
3.1 KiB
C++

#include "editor/EntityCutoutUtils.h"
#include <algorithm>
#include <cmath>
#include <QPainter>
#include <QPolygonF>
#include <QTransform>
namespace entity_cutout {
QPainterPath pathFromWorldPolygon(const QVector<QPointF>& poly) {
QPainterPath path;
if (poly.size() < 3) {
return path;
}
path.addPolygon(QPolygonF(poly));
path.closeSubpath();
return path;
}
QPointF polygonCentroid(const QVector<QPointF>& poly) {
if (poly.size() < 3) {
return {};
}
double a2 = 0.0;
double cx6a = 0.0;
double cy6a = 0.0;
for (int i = 0; i < poly.size(); ++i) {
const QPointF p0 = poly[i];
const QPointF p1 = poly[(i + 1) % poly.size()];
const double cross = static_cast<double>(p0.x()) * static_cast<double>(p1.y()) -
static_cast<double>(p1.x()) * static_cast<double>(p0.y());
a2 += cross;
cx6a += (static_cast<double>(p0.x()) + static_cast<double>(p1.x())) * cross;
cy6a += (static_cast<double>(p0.y()) + static_cast<double>(p1.y())) * cross;
}
if (std::abs(a2) < 1e-6) {
const QRectF bb = pathFromWorldPolygon(poly).boundingRect();
return bb.center();
}
const double inv6a = 1.0 / (3.0 * a2);
return QPointF(cx6a * inv6a, cy6a * inv6a);
}
QRect clampRectToImage(const QRect& r, const QSize& size) {
QRect out = r.normalized();
if (out.isNull()) {
return {};
}
out.setLeft(std::max(0, out.left()));
out.setTop(std::max(0, out.top()));
out.setRight(std::min(size.width() - 1, out.right()));
out.setBottom(std::min(size.height() - 1, out.bottom()));
if (out.width() <= 0 || out.height() <= 0) {
return {};
}
return out;
}
QImage extractEntityImage(const QImage& bg, const QVector<QPointF>& polyWorld, QPointF& outTopLeftWorld) {
if (bg.isNull() || polyWorld.size() < 3) {
outTopLeftWorld = {};
return {};
}
const QPainterPath path = pathFromWorldPolygon(polyWorld);
if (path.isEmpty()) {
outTopLeftWorld = {};
return {};
}
const QRect bbox = clampRectToImage(path.boundingRect().toAlignedRect(), bg.size());
if (bbox.isNull()) {
outTopLeftWorld = {};
return {};
}
outTopLeftWorld = bbox.topLeft();
QImage out(bbox.size(), QImage::Format_ARGB32_Premultiplied);
out.fill(Qt::transparent);
QPainter p(&out);
p.setRenderHint(QPainter::Antialiasing, true);
QTransform tr;
tr.translate(-bbox.left(), -bbox.top());
const QPainterPath localPath = tr.map(path);
p.setClipPath(localPath);
p.drawImage(QPoint(0, 0), bg, bbox);
p.end();
return out;
}
void applyBlackFillToBackground(QImage& bgCutout, const QVector<QPointF>& polyWorld) {
if (bgCutout.isNull() || polyWorld.size() < 3) {
return;
}
QPainterPath path = pathFromWorldPolygon(polyWorld);
if (path.isEmpty()) {
return;
}
QPainter p(&bgCutout);
p.setRenderHint(QPainter::Antialiasing, true);
p.setPen(Qt::NoPen);
p.setBrush(QColor(0, 0, 0, 255));
p.drawPath(path);
p.end();
}
} // namespace entity_cutout