1 #include <wrap/qt/outline2_rasterizer.h>
2 #include <wrap/qt/col_qt_convert.h>
3 #include <vcg/space/color4.h>
4 #include <wrap/qt/col_qt_convert.h>
5 
6 #include <fstream>
7 
8 using namespace vcg;
9 using namespace std;
10 
rasterize(RasterizedOutline2 & poly,float scale,int rast_i,int rotationNum,int gutterWidth)11 void QtOutline2Rasterizer::rasterize(RasterizedOutline2 &poly,
12                                  float scale,
13                                  int rast_i,
14                                  int rotationNum,
15                                  int gutterWidth)
16 {
17 
18     gutterWidth *= 2; // since the brush is centered on the outline multiply the given value by 2
19 
20     float rotRad = M_PI*2.0f*float(rast_i) / float(rotationNum);
21 
22     //get polygon's BB, rotated according to the input parameter
23     Box2f bb;
24     vector<Point2f> pointvec = poly.getPoints();
25     for(size_t i=0;i<pointvec.size();++i) {
26         Point2f pp=pointvec[i];
27         pp.Rotate(rotRad);
28         bb.Add(pp);
29     }
30 
31     //create the polygon to print it
32     QVector<QPointF> points;
33     vector<Point2f> newpoints = poly.getPoints();
34     for (size_t i = 0; i < newpoints.size(); i++) {
35         points.push_back(QPointF(newpoints[i].X(), newpoints[i].Y()));
36     }
37 
38     // Compute the raster space size by rounding up the scaled bounding box size
39     // and adding the gutter width.
40     int sizeX = (int)ceil(bb.DimX()*scale);
41     int sizeY = (int)ceil(bb.DimY()*scale);
42     int safetyBuffer = 2;
43     sizeX += (gutterWidth + safetyBuffer);
44     sizeY += (gutterWidth + safetyBuffer);
45 
46     QImage img(sizeX,sizeY,QImage::Format_RGB32);
47     QColor backgroundColor(Qt::transparent);
48     img.fill(backgroundColor);
49 
50     ///SETUP OF DRAWING PROCEDURE
51     QPainter painter;
52     painter.begin(&img);
53     {
54         QBrush br;
55         br.setStyle(Qt::SolidPattern);
56         br.setColor(Qt::yellow);
57 
58         QPen qp;
59         qp.setWidthF(0);
60         qp.setWidth(gutterWidth);
61         qp.setCosmetic(true);
62         qp.setColor(Qt::yellow);
63         qp.setJoinStyle(Qt::MiterJoin);
64         qp.setMiterLimit(0);
65 
66         painter.setBrush(br);
67         painter.setPen(qp);
68 
69         painter.resetTransform();
70         painter.translate(QPointF(-(bb.min.X()*scale) + (gutterWidth + safetyBuffer)/2.0f, -(bb.min.Y()*scale) + (gutterWidth + safetyBuffer)/2.0f));
71         painter.rotate(math::ToDeg(rotRad));
72         painter.scale(scale,scale);
73 
74         painter.drawPolygon(QPolygonF(points));
75     }
76     painter.end();
77 
78     // workaround/hack to avoid ``disappearing'' primitives: use a cosmetic pen to
79     // draw the poly boundary.
80     // The proper way to do this would be to use conservative reasterization, which
81     // Qt doesn't seem to support
82     std::vector<QPointF> lines;
83     for (int i = 1; i < points.size(); ++i) {
84         lines.push_back(points[i-1]);
85         lines.push_back(points[i]);
86     }
87     lines.push_back(points.back());
88     lines.push_back(points.front());
89 
90     painter.begin(&img);
91     {
92         QBrush br;
93         br.setStyle(Qt::SolidPattern);
94         br.setColor(Qt::yellow);
95 
96         QPen qp;
97         qp.setWidthF(0);
98         qp.setWidth(std::max(1, gutterWidth));
99         qp.setCosmetic(true);
100         qp.setColor(Qt::yellow);
101 
102         painter.setBrush(br);
103         painter.setPen(qp);
104 
105         painter.resetTransform();
106         painter.translate(QPointF(-(bb.min.X()*scale) + (gutterWidth + safetyBuffer)/2.0f, -(bb.min.Y()*scale) + (gutterWidth + safetyBuffer)/2.0f));
107         painter.rotate(math::ToDeg(rotRad));
108         painter.scale(scale,scale);
109 
110         //painter.drawPoints(QPolygonF(points));
111         painter.drawLines(lines.data(), lines.size()/2);
112     }
113     painter.end();
114 
115     // Cropping
116 
117     /*
118     // Slower version
119     int minX = img.width();
120     int minY = img.height();
121     int maxX = -1;
122     int maxY = -1;
123 
124     for (int i = 0; i < img.height(); ++i) {
125         const QRgb *line = reinterpret_cast<const QRgb*>(img.scanLine(i));
126         for (int j = 0; j < img.width(); ++j) {
127             if (line[j] != backgroundColor.rgb()) {
128                 if (j < minX) minX = j;
129                 if (j > maxX) maxX = j;
130                 if (i < minY) minY = i;
131                 if (i > maxY) maxY = i;
132             }
133         }
134     }
135     */
136 
137     int minX = img.width();
138     int minY = img.height();
139     int maxX = 0;
140     int maxY = 0;
141 
142     for (int i = 0; i < img.height(); ++i) {
143         const QRgb *line = reinterpret_cast<const QRgb*>(img.scanLine(i));
144         for (int j = 0; j < img.width(); ++j) {
145             if (line[j] != backgroundColor.rgb()) {
146                 minY = i;
147                 break;
148             }
149         }
150         if (minY < img.height()) break;
151     }
152 
153     for (int i = img.height() - 1; i >= 0; --i) {
154         const QRgb *line = reinterpret_cast<const QRgb*>(img.scanLine(i));
155         for (int j = 0; j < img.width(); ++j) {
156             if (line[j] != backgroundColor.rgb()) {
157                 maxY = i;
158                 break;
159             }
160         }
161         if (maxY > 0) break;
162     }
163 
164     for (int i = minY; i <= maxY; ++i) {
165         const QRgb *line = reinterpret_cast<const QRgb*>(img.scanLine(i));
166         for (int j = 0; j < minX; ++j)
167             if (line[j] != backgroundColor.rgb() && j < minX) {
168                 minX = j;
169                 break;
170             }
171         for (int j = img.width() - 1; j >= maxX; --j)
172             if (line[j] != backgroundColor.rgb() && j > maxX) {
173                 maxX = j;
174                 break;
175             }
176     }
177 
178     assert (minX <= maxX && minY <= maxY);
179 
180     int imgW = (maxX - minX) + 1;
181     int imgH = (maxY - minY) + 1;
182 
183     {
184         QImage imgcp = img.copy(0, 0, img.width(), img.height());
185         img = imgcp.copy(minX, minY, imgW, imgH);
186     }
187 
188     //create the first grid, which will then be rotated 3 times.
189     //we will reuse this grid to create the rasterizations corresponding to this one rotated by 90/180/270°
190     vector<vector<int> > tetrisGrid;
191     QRgb yellow = QColor(Qt::yellow).rgb();
192     tetrisGrid.resize(img.height());
193     for (int k = 0; k < img.height(); k++) {
194         tetrisGrid[k].resize(img.width(), 0);
195     }
196     for (int y = 0; y < img.height(); y++) {
197         const uchar* line = img.scanLine(y);
198         for(int x = 0; x < img.width(); ++x) {
199             if (((QRgb*)line)[x] == yellow) {
200                 tetrisGrid[y][x] = 1;
201             }
202         }
203     }
204 
205     //create the 4 rasterizations (one every 90°) using the discrete representation grid we've just created
206     int rotationOffset = rotationNum/4;
207     for (int j = 0; j < 4; j++) {
208         if (j != 0)  {
209             tetrisGrid = rotateGridCWise(tetrisGrid);
210         }
211         //add the grid to the poly's vector of grids
212         poly.getGrids(rast_i + rotationOffset*j) = tetrisGrid;
213 
214         //initializes bottom/left/deltaX/deltaY vectors of the poly, for the current rasterization
215         poly.initFromGrid(rast_i + rotationOffset*j);
216     }
217 }
218 
219 // rotates the grid 90 degree clockwise (by simple swap)
220 // used to lower the cost of rasterization.
rotateGridCWise(vector<vector<int>> & inGrid)221 vector<vector<int> > QtOutline2Rasterizer::rotateGridCWise(vector< vector<int> >& inGrid) {
222     vector<vector<int> > outGrid(inGrid[0].size());
223     for (size_t i = 0; i < inGrid[0].size(); i++) {
224         outGrid[i].reserve(inGrid.size());
225         for (size_t j = 0; j < inGrid.size(); j++) {
226             outGrid[i].push_back(inGrid[inGrid.size() - j - 1][i]);
227         }
228     }
229     return outGrid;
230 }
231