You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
230 lines
7.1 KiB
230 lines
7.1 KiB
#include <wrap/qt/outline2_rasterizer.h> |
|
#include <wrap/qt/col_qt_convert.h> |
|
#include <vcg/space/color4.h> |
|
#include <wrap/qt/col_qt_convert.h> |
|
|
|
#include <fstream> |
|
|
|
using namespace vcg; |
|
using namespace std; |
|
|
|
void QtOutline2Rasterizer::rasterize(RasterizedOutline2 &poly, |
|
float scale, |
|
int rast_i, |
|
int rotationNum, |
|
int gutterWidth) |
|
{ |
|
|
|
gutterWidth *= 2; // since the brush is centered on the outline multiply the given value by 2 |
|
|
|
float rotRad = M_PI*2.0f*float(rast_i) / float(rotationNum); |
|
|
|
//get polygon's BB, rotated according to the input parameter |
|
Box2f bb; |
|
vector<Point2f> pointvec = poly.getPoints(); |
|
for(size_t i=0;i<pointvec.size();++i) { |
|
Point2f pp=pointvec[i]; |
|
pp.Rotate(rotRad); |
|
bb.Add(pp); |
|
} |
|
|
|
//create the polygon to print it |
|
QVector<QPointF> points; |
|
vector<Point2f> newpoints = poly.getPoints(); |
|
for (size_t i = 0; i < newpoints.size(); i++) { |
|
points.push_back(QPointF(newpoints[i].X(), newpoints[i].Y())); |
|
} |
|
|
|
// Compute the raster space size by rounding up the scaled bounding box size |
|
// and adding the gutter width. |
|
int sizeX = (int)ceil(bb.DimX()*scale); |
|
int sizeY = (int)ceil(bb.DimY()*scale); |
|
int safetyBuffer = 2; |
|
sizeX += (gutterWidth + safetyBuffer); |
|
sizeY += (gutterWidth + safetyBuffer); |
|
|
|
QImage img(sizeX,sizeY,QImage::Format_RGB32); |
|
QColor backgroundColor(Qt::transparent); |
|
img.fill(backgroundColor); |
|
|
|
///SETUP OF DRAWING PROCEDURE |
|
QPainter painter; |
|
painter.begin(&img); |
|
{ |
|
QBrush br; |
|
br.setStyle(Qt::SolidPattern); |
|
br.setColor(Qt::yellow); |
|
|
|
QPen qp; |
|
qp.setWidthF(0); |
|
qp.setWidth(gutterWidth); |
|
qp.setCosmetic(true); |
|
qp.setColor(Qt::yellow); |
|
qp.setJoinStyle(Qt::MiterJoin); |
|
qp.setMiterLimit(0); |
|
|
|
painter.setBrush(br); |
|
painter.setPen(qp); |
|
|
|
painter.resetTransform(); |
|
painter.translate(QPointF(-(bb.min.X()*scale) + (gutterWidth + safetyBuffer)/2.0f, -(bb.min.Y()*scale) + (gutterWidth + safetyBuffer)/2.0f)); |
|
painter.rotate(math::ToDeg(rotRad)); |
|
painter.scale(scale,scale); |
|
|
|
painter.drawPolygon(QPolygonF(points)); |
|
} |
|
painter.end(); |
|
|
|
// workaround/hack to avoid ``disappearing'' primitives: use a cosmetic pen to |
|
// draw the poly boundary. |
|
// The proper way to do this would be to use conservative reasterization, which |
|
// Qt doesn't seem to support |
|
std::vector<QPointF> lines; |
|
for (int i = 1; i < points.size(); ++i) { |
|
lines.push_back(points[i-1]); |
|
lines.push_back(points[i]); |
|
} |
|
lines.push_back(points.back()); |
|
lines.push_back(points.front()); |
|
|
|
painter.begin(&img); |
|
{ |
|
QBrush br; |
|
br.setStyle(Qt::SolidPattern); |
|
br.setColor(Qt::yellow); |
|
|
|
QPen qp; |
|
qp.setWidthF(0); |
|
qp.setWidth(std::max(1, gutterWidth)); |
|
qp.setCosmetic(true); |
|
qp.setColor(Qt::yellow); |
|
|
|
painter.setBrush(br); |
|
painter.setPen(qp); |
|
|
|
painter.resetTransform(); |
|
painter.translate(QPointF(-(bb.min.X()*scale) + (gutterWidth + safetyBuffer)/2.0f, -(bb.min.Y()*scale) + (gutterWidth + safetyBuffer)/2.0f)); |
|
painter.rotate(math::ToDeg(rotRad)); |
|
painter.scale(scale,scale); |
|
|
|
//painter.drawPoints(QPolygonF(points)); |
|
painter.drawLines(lines.data(), lines.size()/2); |
|
} |
|
painter.end(); |
|
|
|
// Cropping |
|
|
|
/* |
|
// Slower version |
|
int minX = img.width(); |
|
int minY = img.height(); |
|
int maxX = -1; |
|
int maxY = -1; |
|
|
|
for (int i = 0; i < img.height(); ++i) { |
|
const QRgb *line = reinterpret_cast<const QRgb*>(img.scanLine(i)); |
|
for (int j = 0; j < img.width(); ++j) { |
|
if (line[j] != backgroundColor.rgb()) { |
|
if (j < minX) minX = j; |
|
if (j > maxX) maxX = j; |
|
if (i < minY) minY = i; |
|
if (i > maxY) maxY = i; |
|
} |
|
} |
|
} |
|
*/ |
|
|
|
int minX = img.width(); |
|
int minY = img.height(); |
|
int maxX = 0; |
|
int maxY = 0; |
|
|
|
for (int i = 0; i < img.height(); ++i) { |
|
const QRgb *line = reinterpret_cast<const QRgb*>(img.scanLine(i)); |
|
for (int j = 0; j < img.width(); ++j) { |
|
if (line[j] != backgroundColor.rgb()) { |
|
minY = i; |
|
break; |
|
} |
|
} |
|
if (minY < img.height()) break; |
|
} |
|
|
|
for (int i = img.height() - 1; i >= 0; --i) { |
|
const QRgb *line = reinterpret_cast<const QRgb*>(img.scanLine(i)); |
|
for (int j = 0; j < img.width(); ++j) { |
|
if (line[j] != backgroundColor.rgb()) { |
|
maxY = i; |
|
break; |
|
} |
|
} |
|
if (maxY > 0) break; |
|
} |
|
|
|
for (int i = minY; i <= maxY; ++i) { |
|
const QRgb *line = reinterpret_cast<const QRgb*>(img.scanLine(i)); |
|
for (int j = 0; j < minX; ++j) |
|
if (line[j] != backgroundColor.rgb() && j < minX) { |
|
minX = j; |
|
break; |
|
} |
|
for (int j = img.width() - 1; j >= maxX; --j) |
|
if (line[j] != backgroundColor.rgb() && j > maxX) { |
|
maxX = j; |
|
break; |
|
} |
|
} |
|
|
|
assert (minX <= maxX && minY <= maxY); |
|
|
|
int imgW = (maxX - minX) + 1; |
|
int imgH = (maxY - minY) + 1; |
|
|
|
{ |
|
QImage imgcp = img.copy(0, 0, img.width(), img.height()); |
|
img = imgcp.copy(minX, minY, imgW, imgH); |
|
} |
|
|
|
//create the first grid, which will then be rotated 3 times. |
|
//we will reuse this grid to create the rasterizations corresponding to this one rotated by 90/180/270° |
|
vector<vector<int> > tetrisGrid; |
|
QRgb yellow = QColor(Qt::yellow).rgb(); |
|
tetrisGrid.resize(img.height()); |
|
for (int k = 0; k < img.height(); k++) { |
|
tetrisGrid[k].resize(img.width(), 0); |
|
} |
|
for (int y = 0; y < img.height(); y++) { |
|
const uchar* line = img.scanLine(y); |
|
for(int x = 0; x < img.width(); ++x) { |
|
if (((QRgb*)line)[x] == yellow) { |
|
tetrisGrid[y][x] = 1; |
|
} |
|
} |
|
} |
|
|
|
//create the 4 rasterizations (one every 90°) using the discrete representation grid we've just created |
|
int rotationOffset = rotationNum/4; |
|
for (int j = 0; j < 4; j++) { |
|
if (j != 0) { |
|
tetrisGrid = rotateGridCWise(tetrisGrid); |
|
} |
|
//add the grid to the poly's vector of grids |
|
poly.getGrids(rast_i + rotationOffset*j) = tetrisGrid; |
|
|
|
//initializes bottom/left/deltaX/deltaY vectors of the poly, for the current rasterization |
|
poly.initFromGrid(rast_i + rotationOffset*j); |
|
} |
|
} |
|
|
|
// rotates the grid 90 degree clockwise (by simple swap) |
|
// used to lower the cost of rasterization. |
|
vector<vector<int> > QtOutline2Rasterizer::rotateGridCWise(vector< vector<int> >& inGrid) { |
|
vector<vector<int> > outGrid(inGrid[0].size()); |
|
for (size_t i = 0; i < inGrid[0].size(); i++) { |
|
outGrid[i].reserve(inGrid.size()); |
|
for (size_t j = 0; j < inGrid.size(); j++) { |
|
outGrid[i].push_back(inGrid[inGrid.size() - j - 1][i]); |
|
} |
|
} |
|
return outGrid; |
|
}
|
|
|