On Thu, Jul 5, 2012 at 11:20 AM, Ian Thomson <ian.thom...@iongeo.com> wrote:
> On 05/07/12 16:12, Josiah Bryan wrote: > > So, my Qt friends, is there a better way to fill a triangle with a color > > specified for each vertex, interpolated across the triangle? Some method > > that takes *less* than 4 milliseconds *per triangle*? Did I just miss > > something in the Qt gradient routines that would do this quite easily? > > Any ideas? > > You might want to draw your triangles on to a QImage, using > QImage::scanLine to manipulate the pixels directly, and then put the > QImage on to the QWidget at the end. I would imagine this would be > faster than QPainter::fillRect. > > Wow - that was faster. 379ms for 1K triangles. Much much improved - so much so that I don't think I'll need to optimize further for now. (Revised code attached incase anyone wants to use it.) Thanks for your tip - I *should* have thought of that myself, don't know why I just made it more complex to begin with. Anyways, thanks! -- Josiah Bryan 765-215-0511 josiahbr...@gmail.com
//#include "MainWindow.h" #include <QtGui> class MainWindow : public QWidget { public: MainWindow(); protected: }; class TRGBFloat { public: float R; float G; float B; }; bool fillTriColor(QImage *img, QVector<QPointF> points, QList<QColor> colors) { if(!img) return false; if(img->format() != QImage::Format_ARGB32_Premultiplied) return false; double imgWidth = img->width(); double imgHeight = img->height(); /// The following routine translated from Delphi, originally found at: // http://www.swissdelphicenter.ch/en/showcode.php?id=1780 double LX, RX, Ldx, Rdx,// : Single; Dif1, Dif2;// : Single; TRGBFloat LRGB, RRGB, RGB, RGBdx, LRGBdy, RRGBdy;// : TRGBFloat; QColor RGBT;// : RGBTriple; //QList<QColor> Scan; // : PRGBTripleArray; double y, x, ScanStart, ScanEnd;// : integer; bool Right; // : boolean; QPointF tmpPoint; QColor tmpColor;// : TPointColor; // sort vertices by Y int Vmax = 0; if (points[1].y() > points[0].y()) Vmax = 1; if (points[2].y() > points[Vmax].y()) Vmax = 2; if(Vmax != 2) { tmpPoint = points[2]; points[2] = points[Vmax]; points[Vmax] = tmpPoint; tmpColor = colors[2]; colors[2] = colors[Vmax]; colors[Vmax] = tmpColor; } if(points[1].y() > points[0].y()) Vmax = 1; else Vmax = 0; if(Vmax == 0) { tmpPoint = points[1]; points[1] = points[0]; points[0] = tmpPoint; tmpColor = colors[1]; colors[1] = colors[0]; colors[0] = tmpColor; } Dif1 = points[2].y() - points[0].y(); if(Dif1 == 0) Dif1 = 0.001; // prevent div by 0 error Dif2 = points[1].y() - points[0].y(); if(Dif2 == 0) Dif2 = 0.001; //work out if middle point is to the left or right of the line // connecting upper and lower points if(points[1].x() > (points[2].x() - points[0].x()) * Dif2 / Dif1 + points[0].x()) Right = true; else Right = false; // calculate increments in x and colour for stepping through the lines if(Right) { Ldx = (points[2].x() - points[0].x()) / Dif1; Rdx = (points[1].x() - points[0].x()) / Dif2; LRGBdy.B = (colors[2].blue() - colors[0].blue()) / Dif1; LRGBdy.G = (colors[2].green() - colors[0].green()) / Dif1; LRGBdy.R = (colors[2].red() - colors[0].red()) / Dif1; RRGBdy.B = (colors[1].blue() - colors[0].blue()) / Dif2; RRGBdy.G = (colors[1].green() - colors[0].green()) / Dif2; RRGBdy.R = (colors[1].red() - colors[0].red()) / Dif2; } else { // Ldx := (V[1].X - V[0].X) / Dif2; // Rdx := (V[2].X - V[0].X) / Dif1; // RRGBdy.B := (V[2].RGB.B - V[0].RGB.B) / Dif1; // RRGBdy.G := (V[2].RGB.G - V[0].RGB.G) / Dif1; // RRGBdy.R := (V[2].RGB.R - V[0].RGB.R) / Dif1; // LRGBdy.B := (V[1].RGB.B - V[0].RGB.B) / Dif2; // LRGBdy.G := (V[1].RGB.G - V[0].RGB.G) / Dif2; // LRGBdy.R := (V[1].RGB.R - V[0].RGB.R) / Dif2; Ldx = (points[1].x() - points[0].x()) / Dif1; Rdx = (points[2].x() - points[0].x()) / Dif2; RRGBdy.B = (colors[2].blue() - colors[0].blue()) / Dif1; RRGBdy.G = (colors[2].green() - colors[0].green()) / Dif1; RRGBdy.R = (colors[2].red() - colors[0].red()) / Dif1; LRGBdy.B = (colors[1].blue() - colors[0].blue()) / Dif2; LRGBdy.G = (colors[1].green() - colors[0].green()) / Dif2; LRGBdy.R = (colors[1].red() - colors[0].red()) / Dif2; } LRGB.R = colors[0].red(); LRGB.G = colors[0].green(); LRGB.B = colors[0].blue(); RRGB = LRGB; LX = points[0].x(); RX = points[0].x(); // fill region 1 for(y = points[0].y(); y <= points[1].y() - 1; y++) { // y clipping if(y > imgHeight - 1) break; if(y < 0) { LX = LX + Ldx; RX = RX + Rdx; LRGB.B = LRGB.B + LRGBdy.B; LRGB.G = LRGB.G + LRGBdy.G; LRGB.R = LRGB.R + LRGBdy.R; RRGB.B = RRGB.B + RRGBdy.B; RRGB.G = RRGB.G + RRGBdy.G; RRGB.R = RRGB.R + RRGBdy.R; continue; } // Scan = ABitmap.ScanLine[y]; // calculate increments in color for stepping through pixels Dif1 = RX - LX + 1; if(Dif1 == 0) Dif1 = 0.001; RGBdx.B = (RRGB.B - LRGB.B) / Dif1; RGBdx.G = (RRGB.G - LRGB.G) / Dif1; RGBdx.R = (RRGB.R - LRGB.R) / Dif1; // x clipping if(LX < 0) { ScanStart = 0; RGB.B = LRGB.B + (RGBdx.B * fabs(LX)); RGB.G = LRGB.G + (RGBdx.G * fabs(LX)); RGB.R = LRGB.R + (RGBdx.R * fabs(LX)); } else { RGB = LRGB; ScanStart = LX; //round(LX); } if(RX - 1 > imgWidth - 1) ScanEnd = imgWidth - 1; else ScanEnd = RX - 1; //round(RX) - 1; // scan the line QRgb* scanline = (QRgb*)img->scanLine((int)y); for(x = ScanStart; x <= ScanEnd; x++) { //RGBT.rgbtBlue := trunc(RGB.B); //RGBT.rgbtGreen := trunc(RGB.G); //RGBT.rgbtRed := trunc(RGB.R); //Scan[x] := RGBT; //p->fillRect(QRectF(x,y,1.,1.),QColor((int)RGB.R, (int)RGB.G, (int)RGB.B)); scanline[(int)x] = qRgb((int)RGB.R, (int)RGB.G, (int)RGB.B); RGB.B = RGB.B + RGBdx.B; RGB.G = RGB.G + RGBdx.G; RGB.R = RGB.R + RGBdx.R; } /// It's faster to fill individual pixel rectangles than to use a liner gradient for the entire line /// Testing on my machine showed 1k tri in 7sec with liner gradient, and 1k tri in 4sec with the above for(x) loop // ScanStart = LX; //round(LX); // ScanEnd = RX - 1; //round(RX) - 1; // QLinearGradient gradient(ScanStart, y, ScanEnd, y); // gradient.setColorAt(0.0, QColor((int)LRGB.R, (int)LRGB.G, (int)LRGB.B)); // gradient.setColorAt(1.0, QColor((int)RRGB.R, (int)RRGB.G, (int)RRGB.B)); // // double length = (ScanEnd - ScanStart); // p->fillRect(QRectF(ScanStart, y, length, 1.), QBrush(gradient)); // increment edge x positions LX = LX + Ldx; RX = RX + Rdx; // increment edge colours by the y colour increments LRGB.B = LRGB.B + LRGBdy.B; LRGB.G = LRGB.G + LRGBdy.G; LRGB.R = LRGB.R + LRGBdy.R; RRGB.B = RRGB.B + RRGBdy.B; RRGB.G = RRGB.G + RRGBdy.G; RRGB.R = RRGB.R + RRGBdy.R; } Dif1 = points[2].y() - points[1].y(); if(Dif1 == 0) Dif1 = 0.001; // calculate new increments for region 2 if(Right) { Rdx = (points[2].x() - points[1].x()) / Dif1; RX = points[1].x(); RRGBdy.B = (colors[2].blue() - colors[1].blue()) / Dif1; RRGBdy.G = (colors[2].green() - colors[1].green()) / Dif1; RRGBdy.R = (colors[2].red() - colors[1].red()) / Dif1; RRGB.R = colors[1].red(); RRGB.G = colors[1].green(); RRGB.B = colors[1].blue(); } else { Ldx = (points[2].x() - points[1].x()) / Dif1; LX = points[1].x(); LRGBdy.B = (colors[2].blue() - colors[1].blue()) / Dif1; LRGBdy.G = (colors[2].green() - colors[1].green()) / Dif1; LRGBdy.R = (colors[2].red() - colors[1].red()) / Dif1; LRGB.R = colors[1].red(); LRGB.G = colors[1].green(); LRGB.B = colors[1].blue(); } // fill region 2 for(y = points[1].y(); y < points[2].y() - 1; y++) { // y clipping if(y > imgHeight - 1) break; if(y < 0) { LX = LX + Ldx; RX = RX + Rdx; LRGB.B = LRGB.B + LRGBdy.B; LRGB.G = LRGB.G + LRGBdy.G; LRGB.R = LRGB.R + LRGBdy.R; RRGB.B = RRGB.B + RRGBdy.B; RRGB.G = RRGB.G + RRGBdy.G; RRGB.R = RRGB.R + RRGBdy.R; continue; } //Scan := ABitmap.ScanLine[y]; Dif1 = RX - LX + 1; if(Dif1 == 0) Dif1 = 0.001; RGBdx.B = (RRGB.B - LRGB.B) / Dif1; RGBdx.G = (RRGB.G - LRGB.G) / Dif1; RGBdx.R = (RRGB.R - LRGB.R) / Dif1; // x clipping if(LX < 0) { ScanStart = 0; RGB.B = LRGB.B + (RGBdx.B * fabs(LX)); RGB.G = LRGB.G + (RGBdx.G * fabs(LX)); RGB.R = LRGB.R + (RGBdx.R * fabs(LX)); } else { RGB = LRGB; ScanStart = LX; //round(LX); } if(RX - 1 > imgWidth - 1) ScanEnd = imgWidth - 1; else ScanEnd = RX - 1; //round(RX) - 1; // scan the line QRgb* scanline = (QRgb*)img->scanLine((int)y); for(x = ScanStart; x < ScanEnd; x++) { //RGBT.rgbtBlue := trunc(RGB.B); //RGBT.rgbtGreen := trunc(RGB.G); //RGBT.rgbtRed := trunc(RGB.R); //Scan[x] := RGBT; //p->fillRect(QRectF(x,y,1.,1.),QColor((int)RGB.R, (int)RGB.G, (int)RGB.B)); scanline[(int)x] = qRgb((int)RGB.R, (int)RGB.G, (int)RGB.B); RGB.B = RGB.B + RGBdx.B; RGB.G = RGB.G + RGBdx.G; RGB.R = RGB.R + RGBdx.R; } // ScanStart = LX; //round(LX); // ScanEnd = RX - 1; //round(RX) - 1; // QLinearGradient gradient(ScanStart, y, ScanEnd, y); // gradient.setColorAt(0.0, QColor((int)LRGB.R, (int)LRGB.G, (int)LRGB.B)); // gradient.setColorAt(1.0, QColor((int)RRGB.R, (int)RRGB.G, (int)RRGB.B)); // // double length = (ScanEnd - ScanStart); // p->fillRect(QRectF(ScanStart, y, length, 1.), QBrush(gradient)); // LX = LX + Ldx; RX = RX + Rdx; LRGB.B = LRGB.B + LRGBdy.B; LRGB.G = LRGB.G + LRGBdy.G; LRGB.R = LRGB.R + LRGBdy.R; RRGB.B = RRGB.B + RRGBdy.B; RRGB.G = RRGB.G + RRGBdy.G; RRGB.R = RRGB.R + RRGBdy.R; } return true; } MainWindow::MainWindow() : QWidget() { // Setup the layout of the window QVBoxLayout *vbox = new QVBoxLayout(this); vbox->setContentsMargins(0,0,0,0); QImage img(640,480,QImage::Format_ARGB32_Premultiplied); QPainter p(&img); p.fillRect(img.rect(), Qt::black); QList<QColor> colors = QList<QColor>() << Qt::red << Qt::green << Qt::blue; double imgWidth = img.width(); double imgHeight = img.height(); QVector<QPointF> points = QVector<QPointF>() << QPointF(10, 10) << QPointF(imgWidth - 10, imgHeight / 2) << QPointF(imgWidth / 2, imgHeight - 10); // p.setBrush(colors[0]); // //p.setPen(colors[1]); // p.drawConvexPolygon(points); fillTriColor(&img, points, colors); #define randRange(range) ( range * ((float) rand () / RAND_MAX) ) QTime t; t.start(); int max = 1000; for(int i=0; i<max; i++) { QVector<QPointF> points = QVector<QPointF>() << QPointF(randRange(imgWidth), randRange(imgHeight)) << QPointF(randRange(imgWidth), randRange(imgHeight)) << QPointF(randRange(imgWidth), randRange(imgHeight)); fillTriColor(&img, points, colors); } int time = t.elapsed(); double msPerTri = (double)time / (double)max; qDebug() << "ms per tri: "<<msPerTri<<", total time elapsed for"<<max<<"triangles:"<<time; img.save("sample.jpg"); QLabel *label = new QLabel(); label->setPixmap(QPixmap::fromImage(img)); vbox->addWidget(label); // Adjust window for a 4:3 aspect ratio resize(640, 480); }
_______________________________________________ Interest mailing list Interest@qt-project.org http://lists.qt-project.org/mailman/listinfo/interest