#include "widget.h" #include #include #include // Exponential blur, Jani Huhtanen, 2006 // template static inline void blurinner(unsigned char *bptr, int &zR, int &zG, int &zB, int &zA, int alpha); template static inline void blurrow( QImage & im, int line, int alpha); template static inline void blurcol( QImage & im, int col, int alpha); /* * expblur(QImage &img, int radius) * * In-place blur of image 'img' with kernel * of approximate radius 'radius'. * * Blurs with two sided exponential impulse * response. * * aprec = precision of alpha parameter * in fixed-point format 0.aprec * * zprec = precision of state parameters * zR,zG,zB and zA in fp format 8.zprec */ template void expblur( QImage &img, int radius ) { if(radius<1) return; /* Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity) */ int alpha = (int)((1<(img,row,alpha); } for(int col=0;col(img,col,alpha); } return; } template static inline void blurinner(unsigned char *bptr, int &zR, int &zG, int &zB, int &zA, int alpha) { int R,G,B,A; R = *bptr; G = *(bptr+1); B = *(bptr+2); A = *(bptr+3); zR += (alpha * ((R<>aprec; zG += (alpha * ((G<>aprec; zB += (alpha * ((B<>aprec; zA += (alpha * ((A<>aprec; *bptr = zR>>zprec; *(bptr+1) = zG>>zprec; *(bptr+2) = zB>>zprec; *(bptr+3) = zA>>zprec; } template static inline void blurrow( QImage & im, int line, int alpha) { int zR,zG,zB,zA; QRgb *ptr = (QRgb *)im.scanLine(line); zR = *((unsigned char *)ptr )<((unsigned char *)&ptr[index],zR,zG,zB,zA,alpha); } for(int index=im.width()-2; index>=0; index--) { blurinner((unsigned char *)&ptr[index],zR,zG,zB,zA,alpha); } } template static inline void blurcol( QImage & im, int col, int alpha) { int zR,zG,zB,zA; QRgb *ptr = (QRgb *)im.bits(); ptr+=col; zR = *((unsigned char *)ptr )<((unsigned char *)&ptr[index],zR,zG,zB,zA,alpha); } for(int index=(im.height()-2)*im.width(); index>=0; index-=im.width()) { blurinner((unsigned char *)&ptr[index],zR,zG,zB,zA,alpha); } } // Stack Blur Algorithm by Mario Klingemann void fastbluralpha(QImage &img, int radius) { if (radius < 1) { return; } QRgb *pix = (QRgb*)img.bits(); int w = img.width(); int h = img.height(); int wm = w-1; int hm = h-1; int wh = w*h; int div = radius+radius+1; int *r = new int[wh]; int *g = new int[wh]; int *b = new int[wh]; int *a = new int[wh]; int rsum, gsum, bsum, asum, x, y, i, yp, yi, yw; QRgb p; int *vmin = new int[qMax(w,h)]; int divsum = (div+1)>>1; divsum *= divsum; int *dv = new int[256*divsum]; for (i=0; i < 256*divsum; ++i) { dv[i] = (i/divsum); } yw = yi = 0; int **stack = new int*[div]; for(int i = 0; i < div; ++i) { stack[i] = new int[4]; } int stackpointer; int stackstart; int *sir; int rbs; int r1 = radius+1; int routsum, goutsum, boutsum, aoutsum; int rinsum, ginsum, binsum, ainsum; for (y = 0; y < h; ++y){ rinsum = ginsum = binsum = ainsum = routsum = goutsum = boutsum = aoutsum = rsum = gsum = bsum = asum = 0; for(i =- radius; i <= radius; ++i) { p = pix[yi+qMin(wm,qMax(i,0))]; sir = stack[i+radius]; sir[0] = qRed(p); sir[1] = qGreen(p); sir[2] = qBlue(p); sir[3] = qAlpha(p); rbs = r1-abs(i); rsum += sir[0]*rbs; gsum += sir[1]*rbs; bsum += sir[2]*rbs; asum += sir[3]*rbs; if (i > 0){ rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; ainsum += sir[3]; } else { routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; aoutsum += sir[3]; } } stackpointer = radius; for (x=0; x < w; ++x) { r[yi] = dv[rsum]; g[yi] = dv[gsum]; b[yi] = dv[bsum]; a[yi] = dv[asum]; rsum -= routsum; gsum -= goutsum; bsum -= boutsum; asum -= aoutsum; stackstart = stackpointer-radius+div; sir = stack[stackstart%div]; routsum -= sir[0]; goutsum -= sir[1]; boutsum -= sir[2]; aoutsum -= sir[3]; if (y == 0) { vmin[x] = qMin(x+radius+1,wm); } p = pix[yw+vmin[x]]; sir[0] = qRed(p); sir[1] = qGreen(p); sir[2] = qBlue(p); sir[3] = qAlpha(p); rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; ainsum += sir[3]; rsum += rinsum; gsum += ginsum; bsum += binsum; asum += ainsum; stackpointer = (stackpointer+1)%div; sir = stack[(stackpointer)%div]; routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; aoutsum += sir[3]; rinsum -= sir[0]; ginsum -= sir[1]; binsum -= sir[2]; ainsum -= sir[3]; ++yi; } yw += w; } for (x=0; x < w; ++x){ rinsum = ginsum = binsum = ainsum = routsum = goutsum = boutsum = aoutsum = rsum = gsum = bsum = asum = 0; yp =- radius * w; for(i=-radius; i <= radius; ++i) { yi=qMax(0,yp)+x; sir = stack[i+radius]; sir[0] = r[yi]; sir[1] = g[yi]; sir[2] = b[yi]; sir[3] = a[yi]; rbs = r1-abs(i); rsum += r[yi]*rbs; gsum += g[yi]*rbs; bsum += b[yi]*rbs; asum += a[yi]*rbs; if (i > 0) { rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; ainsum += sir[3]; } else { routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; aoutsum += sir[3]; } if (i < hm){ yp += w; } } yi = x; stackpointer = radius; for (y=0; y < h; ++y){ pix[yi] = qRgba(dv[rsum], dv[gsum], dv[bsum], dv[asum]); rsum -= routsum; gsum -= goutsum; bsum -= boutsum; asum -= aoutsum; stackstart = stackpointer-radius+div; sir = stack[stackstart%div]; routsum -= sir[0]; goutsum -= sir[1]; boutsum -= sir[2]; aoutsum -= sir[3]; if (x==0){ vmin[y] = qMin(y+r1,hm)*w; } p = x+vmin[y]; sir[0] = r[p]; sir[1] = g[p]; sir[2] = b[p]; sir[3] = a[p]; rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; ainsum += sir[3]; rsum += rinsum; gsum += ginsum; bsum += binsum; asum += ainsum; stackpointer = (stackpointer+1)%div; sir = stack[stackpointer]; routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; aoutsum += sir[3]; rinsum -= sir[0]; ginsum -= sir[1]; binsum -= sir[2]; ainsum -= sir[3]; yi += w; } } delete [] r; delete [] g; delete [] b; delete [] a; delete [] vmin; delete [] dv; } Widget::Widget() : QWidget(0), m_radius(5) { m_images.append(QImage("robe1.png")); m_images.append(QImage("test.jpg")); m_current = 0; m_mouseIn = true; m_mouseDown = false; m_tile = QPixmap(100, 100); m_tile.fill(Qt::white); QPainter pt(&m_tile); QColor color(240, 240, 240); pt.fillRect(0, 0, 50, 50, color); pt.fillRect(50, 50, 50, 50, color); pt.end(); generateLens(QRectF(0, 0, 80, 80)); } template inline const T& qClamp(const T &x, const T &low, const T &high) { if (x < low) return low; else if (x > high) return high; else return x; } void Widget::paintEvent(QPaintEvent *e) { QPainter p(this); p.setClipRect(e->rect()); p.drawTiledPixmap(rect(), m_tile); m_blurred = m_images[m_current]; int x = (size().width() - m_blurred.size().width())/2; int y = (size().height() - m_blurred.size().height())/2; QRectF circle(m_pos.x()-40, m_pos.y()-40, 80, 80); if (m_mouseDown) { QPainterPath path; path.addRect(rect()); path.addEllipse(circle); path.setFillRule(Qt::OddEvenFill); p.save(); p.setClipPath(path); } p.drawImage(qClamp(x, 0, x), qClamp(y, 0, y), m_images[m_current]); if (m_mouseDown) { p.restore(); } if (m_mouseDown) { //fastbluralpha(m_blurred, m_radius); //ExpBlur with 0.16 fp for alpha and //8.7 fp for state parameters zR,zG,zB and zA expblur<16,7>(m_blurred, m_radius); #if 0 p.drawImage(qClamp(x, 0, x), qClamp(y, 0, y), m_blurred); #else QPainterPath path; path.addEllipse(circle); p.save(); p.setRenderHint(QPainter::Antialiasing); p.setClipPath(path); p.fillRect(circle, Qt::transparent); p.drawImage(qClamp(x, 0, x), qClamp(y, 0, y), m_blurred); p.restore(); p.drawImage(circle.topLeft(), m_lens); #endif } if (m_mouseIn) drawHelp(&p); drawRadius(&p); p.end(); } void Widget::mousePressEvent(QMouseEvent *e) { m_mouseDown = true; if (e->button() == Qt::RightButton) { ++m_current; if (m_current >= m_images.size()) m_current = 0; } else { m_pos = e->pos(); m_mouseDown = true; } update(); } void Widget::mouseMoveEvent(QMouseEvent *e) { m_pos = e->pos(); //m_radius = int(50 * (pos.y()/rect().width())); update(); } void Widget::mouseReleaseEvent(QMouseEvent *) { m_mouseDown = false; update(); } QSize Widget::sizeHint() const { return QSize(485, 405); } void Widget::enterEvent(QEvent *) { m_mouseIn = true; update(); } void Widget::leaveEvent(QEvent *) { m_mouseIn = false; update(); } void Widget::drawHelp(QPainter *p) { p->save(); p->setWindow(0, 0, 800, 600); p->setRenderHint(QPainter::Antialiasing); p->setPen(QPen(Qt::black)); p->setBrush(QBrush(QColor(193, 193, 193, 127))); p->drawRoundRect(10, 500, 300, 90, 5, 20); QFont font("ComicSans", 14); font.setUnderline(true); p->setFont(font); p->drawText(100, 520, "Help"); font.setUnderline(false); font.setPointSize(12); p->setFont(font); p->drawText(30, 545, QString("Left click, hold and drag to blur.")); p->drawText(30, 570, QString("Scroll wheel to change radius.")); p->restore(); } void Widget::generateLens(const QRectF &bounds) { QPainter painter; m_lens = QImage(bounds.size().toSize(), QImage::Format_ARGB32_Premultiplied); m_lens.fill(0); painter.begin(&m_lens); const qreal rad = 40; QRadialGradient gr(rad, rad, rad, 3 * rad / 5, 3 * rad / 5); gr.setColorAt(0.0, QColor(255, 255, 255, 191)); gr.setColorAt(0.2, QColor(255, 255, 127, 191)); gr.setColorAt(0.9, QColor(150, 150, 200, 63)); gr.setColorAt(0.95, QColor(0, 0, 0, 127)); gr.setColorAt(1, QColor(0, 0, 0, 0)); painter.setRenderHint(QPainter::Antialiasing); painter.setBrush(gr); painter.setPen(Qt::NoPen); painter.drawEllipse(bounds); } void Widget::wheelEvent(QWheelEvent *e) { if (e->delta() > 0) ++m_radius; else --m_radius; m_radius = qClamp(m_radius, 0, 15); update(); } void Widget::drawRadius(QPainter *p) { p->save(); p->setWindow(0, 0, 800, 600); p->setRenderHint(QPainter::Antialiasing); p->setPen(QPen(Qt::black)); p->setBrush(QBrush(QColor(193, 193, 193, 127))); p->drawRoundRect(650, 540, 130, 50, 10, 20); QFont font("ComicSans", 14); font.setUnderline(true); p->setFont(font); p->drawText(660, 560, "Radius"); font.setUnderline(false); font.setPointSize(12); p->setFont(font); p->drawText(670, 580, QString("Radius is: %1").arg(m_radius)); p->restore(); }