1095 | | VERBOSE(VB_IMPORTANT, "Dynamic Line Art: DrawLinel not implemented"); |
| 1098 | // Get the arguments so that the lower x comes first and the |
| 1099 | // absolute gradient is less than one. |
| 1100 | if (abs(y2-y1) > abs(x2-x1)) |
| 1101 | { |
| 1102 | if (y2 > y1) |
| 1103 | DrawLineSub(y1, x1, y2, x2, true); |
| 1104 | else |
| 1105 | DrawLineSub(y2, x2, y1, x1, true); |
| 1106 | } |
| 1107 | else |
| 1108 | { |
| 1109 | if (x2 > x1) |
| 1110 | DrawLineSub(x1, y1, x2, y2, false); |
| 1111 | else |
| 1112 | DrawLineSub(x2, y2, x1, y1, false); |
| 1113 | } |
| 1116 | // Based on the Bresenham line drawing algorithm but extended to draw |
| 1117 | // thick lines. |
| 1118 | void MHIDLA::DrawLineSub(int x1, int y1, int x2, int y2, bool swapped) |
| 1119 | { |
| 1120 | QRgb colour = qRgba(m_lineColour.red(), m_lineColour.green(), |
| 1121 | m_lineColour.blue(), m_lineColour.alpha()); |
| 1122 | int dx = x2-x1, dy = abs(y2-y1); |
| 1123 | int yStep = y2 >= y1 ? 1 : -1; |
| 1124 | // Adjust the starting positions to take account of the |
| 1125 | // line width. |
| 1126 | int error2 = dx/2; |
| 1127 | for (int k = 0; k < m_lineWidth/2; k++) |
| 1128 | { |
| 1129 | y1--; |
| 1130 | y2--; |
| 1131 | error2 += dy; |
| 1132 | if (error2*2 > dx) |
| 1133 | { |
| 1134 | error2 -= dx; |
| 1135 | x1 += yStep; |
| 1136 | x2 += yStep; |
| 1137 | } |
| 1138 | } |
| 1139 | // Main loop |
| 1140 | int y = y1; |
| 1141 | int error = dx/2; |
| 1142 | for (int x = x1; x <= x2; x++) // Include both endpoints |
| 1143 | { |
| 1144 | error2 = dx/2; |
| 1145 | int j = 0; |
| 1146 | // Inner loop also uses the Bresenham algorithm to draw lines |
| 1147 | // perpendicular to the principal direction. |
| 1148 | for (int i = 0; i < m_lineWidth; i++) |
| 1149 | { |
| 1150 | if (swapped) |
| 1151 | { |
| 1152 | if (x+j >= 0 && y+i >= 0 && y+i < m_width && x+j < m_height) |
| 1153 | m_image.setPixel(y+i, x+j, colour); |
| 1154 | } |
| 1155 | else |
| 1156 | { |
| 1157 | if (x+j >= 0 && y+i >= 0 && x+j < m_width && y+i < m_height) |
| 1158 | m_image.setPixel(x+j, y+i, colour); |
| 1159 | } |
| 1160 | error2 += dy; |
| 1161 | if (error2*2 > dx) |
| 1162 | { |
| 1163 | error2 -= dx; |
| 1164 | j -= yStep; |
| 1165 | if (i < m_lineWidth-1) |
| 1166 | { |
| 1167 | // Add another pixel in this case. |
| 1168 | if (swapped) |
| 1169 | { |
| 1170 | if (x+j >= 0 && y+i >= 0 && y+i < m_width && x+j < m_height) |
| 1171 | m_image.setPixel(y+i, x+j, colour); |
| 1172 | } |
| 1173 | else |
| 1174 | { |
| 1175 | if (x+j >= 0 && y+i >= 0 && x+j < m_width && y+i < m_height) |
| 1176 | m_image.setPixel(x+j, y+i, colour); |
| 1177 | } |
| 1178 | } |
| 1179 | } |
| 1180 | } |
| 1181 | error += dy; |
| 1182 | if (error*2 > dx) |
| 1183 | { |
| 1184 | error -= dx; |
| 1185 | y += yStep; |
| 1186 | } |
| 1187 | } |
| 1188 | } |
| 1189 | |
1147 | | VERBOSE(VB_IMPORTANT, "Dynamic Line Art: DrawPoly not implemented"); |
| 1257 | int nPoints = points.size(); |
| 1258 | if (nPoints < 2) |
| 1259 | return; |
| 1260 | |
| 1261 | if (isFilled) |
| 1262 | { |
| 1263 | // Polygon filling is done by sketching the outline of |
| 1264 | // the polygon in a separate bitmap and then raster scanning |
| 1265 | // across this to generate the fill. There are some special |
| 1266 | // cases that have to be considered when doing this. Maximum |
| 1267 | // and minimum points have to be removed otherwise they will |
| 1268 | // turn the scan on but not off again. Horizontal lines are |
| 1269 | // suppressed and their ends handled specially. |
| 1270 | QRect bounds = points.boundingRect(); |
| 1271 | int width = bounds.width()+1, height = bounds.height()+1; |
| 1272 | QBitArray boundsMap(width*height); |
| 1273 | boundsMap.fill(0); |
| 1274 | // Draw the boundaries in the bounds map. This is |
| 1275 | // the Bresenham algorithm if the absolute gradient is |
| 1276 | // greater than 1 but puts only the centre of each line |
| 1277 | // (so there is only one point for each y value) if less. |
| 1278 | QPoint last = points[nPoints-1]; // Last point |
| 1279 | for (int i = 0; i < nPoints; i++) |
| 1280 | { |
| 1281 | QPoint thisPoint = points[i]; |
| 1282 | int x1 = last.x() - bounds.x(); |
| 1283 | int y1 = last.y() - bounds.y(); |
| 1284 | int x2 = thisPoint.x() - bounds.x(); |
| 1285 | int y2 = thisPoint.y() - bounds.y(); |
| 1286 | int x, xEnd, y, yEnd; |
| 1287 | if (y2 > y1) |
| 1288 | { |
| 1289 | x = x1; |
| 1290 | y = y1; |
| 1291 | xEnd = x2; |
| 1292 | yEnd = y2; |
| 1293 | } |
| 1294 | else |
| 1295 | { |
| 1296 | x = x2; |
| 1297 | y = y2; |
| 1298 | xEnd = x1; |
| 1299 | yEnd = y1; |
| 1300 | } |
| 1301 | int dx = abs(xEnd-x), dy = yEnd-y; |
| 1302 | int xStep = xEnd >= x ? 1 : -1; |
| 1303 | if (abs(y2-y1) > abs(x2-x1)) |
| 1304 | { |
| 1305 | int error = dy/2; |
| 1306 | y++; |
| 1307 | for (; y < yEnd; y++) // Exclude endpoints |
| 1308 | { |
| 1309 | boundsMap.toggleBit(x+y*width); |
| 1310 | error += dx; |
| 1311 | if (error*2 > dy) |
| 1312 | { |
| 1313 | error -= dy; |
| 1314 | x += xStep; |
| 1315 | } |
| 1316 | } |
| 1317 | } |
| 1318 | else |
| 1319 | { |
| 1320 | int error = 0; |
| 1321 | y++; |
| 1322 | for (; y < yEnd; y++) |
| 1323 | { |
| 1324 | boundsMap.toggleBit(x+y*width); |
| 1325 | error += dx; |
| 1326 | while (error > dy) |
| 1327 | { |
| 1328 | x += xStep; |
| 1329 | error -= dy; |
| 1330 | } |
| 1331 | } |
| 1332 | } |
| 1333 | QPoint nextPoint = points[(i+1) % nPoints]; |
| 1334 | int nextY = nextPoint.y() - bounds.y(); |
| 1335 | int turn = (y2 - y1) * (nextY - y2); |
| 1336 | if (turn > 0) // Not a max or min |
| 1337 | boundsMap.toggleBit(x2+y2*width); |
| 1338 | else if (turn == 0) // Previous or next line is horizontal |
| 1339 | { |
| 1340 | // We only draw a point at the beginning or end of a horizontal |
| 1341 | // line if it turns clockwise. This means that the fill |
| 1342 | // will be different depending on the direction the polygon was |
| 1343 | // drawn but that will be tidied up when we draw the lines round. |
| 1344 | if (y1 == y2) |
| 1345 | { |
| 1346 | if ((x2-x1) * (nextY - y2) > 0) |
| 1347 | boundsMap.toggleBit(x2+y2*width); |
| 1348 | } |
| 1349 | else if ((nextPoint.x() - bounds.x() - x2) * (y2 - y1) < 0) |
| 1350 | // Next line is horizontal - draw point if turn is clockwise. |
| 1351 | boundsMap.toggleBit(x2+y2*width); |
| 1352 | } |
| 1353 | last = thisPoint; |
| 1354 | } |
| 1355 | QRgb fillColour = qRgba(m_fillColour.red(), m_fillColour.green(), |
| 1356 | m_fillColour.blue(), m_fillColour.alpha()); |
| 1357 | // Now scan the bounds map and use this to fill the polygon. |
| 1358 | for (int j = 0; j < bounds.height(); j++) |
| 1359 | { |
| 1360 | bool penDown = false; |
| 1361 | for (int k = 0; k < bounds.width(); k++) |
| 1362 | { |
| 1363 | if (boundsMap.testBit(k+j*width)) |
| 1364 | penDown = ! penDown; |
| 1365 | else if (penDown && k+bounds.x() >= 0 && j+bounds.y() >= 0 && |
| 1366 | k+bounds.x() < m_width && j+bounds.y() < m_height) |
| 1367 | m_image.setPixel(k+bounds.x(), j+bounds.y(), fillColour); |
| 1368 | } |
| 1369 | } |
| 1370 | |
| 1371 | // Draw the boundary |
| 1372 | last = points[nPoints-1]; // Last point |
| 1373 | for (int i = 0; i < nPoints; i++) |
| 1374 | { |
| 1375 | DrawLine(points[i].x(), points[i].y(), last.x(), last.y()); |
| 1376 | last = points[i]; |
| 1377 | } |
| 1378 | } |
| 1379 | else // PolyLine - draw lines between the points but don't close it. |
| 1380 | { |
| 1381 | for (int i = 1; i < nPoints; i++) |
| 1382 | { |
| 1383 | DrawLine(points[i].x(), points[i].y(), points[i-1].x(), points[i-1].y()); |
| 1384 | } |
| 1385 | } |