Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QIcon Recoloring: Best Approach?
Forum Updated to NodeBB v4.3 + New Features

QIcon Recoloring: Best Approach?

Scheduled Pinned Locked Moved Unsolved General and Desktop
3 Posts 2 Posters 1.4k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • E Offline
    E Offline
    Ewan Green
    wrote on last edited by Ewan Green
    #1

    I'm in a situation where I want to dynamically recolor all of the QIcons used according to QPalette::ButtonText.
    Not only does this include my custom icons, but also the icons applied to widgets/buttons by default, QStandardIcon, etc...

    Firstly, what is the best approach to recoloring existing icons? QGraphicsColorizeEffect may not do what I need, since black will stay black.
    Secondly, what is the best way to "intercept" these icons so I can do the above? Would I have to do some QProxyStyle trickery, or is there a better way?

    Edit: I have found a way to solve the first problem, and I have reimplemented QPixmapColorizeFilter's draw(). Second question still remains.

    If you're curious (Googlers):

    #include <QPainter>
    //maybe also include QPixmap and stuff
    
    static void grayscale(const QImage &image, QImage &dest,
                          const QRect &rect = QRect()) {
      QRect destRect = rect;
      QRect srcRect = rect;
      if (rect.isNull()) {
        srcRect = dest.rect();
        destRect = dest.rect();
      }
      if (&image != &dest) {
        destRect.moveTo(QPoint(0, 0));
      }
      const unsigned int *data = (const unsigned int *)image.bits();
      unsigned int *outData = (unsigned int *)dest.bits();
      if (dest.size() == image.size() && image.rect() == srcRect) {
        // a bit faster loop for grayscaling everything
        int pixels = dest.width() * dest.height();
        for (int i = 0; i < pixels; ++i) {
          int val = qGray(data[i]);
          outData[i] = qRgba(val, val, val, qAlpha(data[i]));
        }
      } else {
        int yd = destRect.top();
        for (int y = srcRect.top(); y <= srcRect.bottom() && y < image.height();
             y++) {
          data = (const unsigned int *)image.scanLine(y);
          outData = (unsigned int *)dest.scanLine(yd++);
          int xd = destRect.left();
          for (int x = srcRect.left(); x <= srcRect.right() && x < image.width();
               x++) {
            int val = qGray(data[x]);
            outData[xd++] = qRgba(val, val, val, qAlpha(data[x]));
          }
        }
      }
    }
    // This is reimplemented qpixmapfilter.cpp behavior.
    // https://code.woboq.org/qt5/qtbase/src/widgets/effects/qpixmapfilter.cpp.html#946
    
    void recolor(QPainter *painter, const QPixmap &src, QColor color,
                 unsigned int strength) { // (Use this one)
      if (src.isNull())
        return;
      // raster implementation
      QImage srcImage;
      QImage destImage;
      QRectF srcRect(0, 0, src.width(), src.height());
      if (srcRect.isNull()) {
        srcImage = src.toImage();
        const auto format = srcImage.hasAlphaChannel()
                                ? QImage::Format_ARGB32_Premultiplied
                                : QImage::Format_RGB32;
        srcImage = std::move(srcImage).convertToFormat(format);
        destImage = QImage(srcImage.size(), srcImage.format());
      } else {
        QRect rect = srcRect.toAlignedRect().intersected(src.rect());
        srcImage = src.copy(rect).toImage();
        const auto format = srcImage.hasAlphaChannel()
                                ? QImage::Format_ARGB32_Premultiplied
                                : QImage::Format_RGB32;
        srcImage = std::move(srcImage).convertToFormat(format);
        destImage = QImage(rect.size(), srcImage.format());
      }
      destImage.setDevicePixelRatio(src.devicePixelRatioF());
      // do colorizing
      QPainter destPainter(&destImage);
      grayscale(srcImage, destImage, srcImage.rect());
      destPainter.setCompositionMode(QPainter::CompositionMode_Screen);
      destPainter.fillRect(srcImage.rect(), color);
      destPainter.end();
      // alpha blending srcImage and destImage
      QImage buffer = srcImage;
      QPainter bufPainter(&buffer);
      bufPainter.setOpacity(strength);
      bufPainter.drawImage(0, 0, destImage);
      bufPainter.end();
      destImage = std::move(buffer);
      //  if (srcImage.hasAlphaChannel())
      destImage.setAlphaChannel(srcImage.alphaChannel());
      painter->drawImage(QPoint(0, 0), destImage);
    }
    // This is reimplemented  QPixmapColorizeFilter behavior.
    // https://code.woboq.org/qt5/qtbase/src/widgets/effects/qpixmapfilter.cpp.html#1089
    

    Ewan Green

    1 Reply Last reply
    0
    • AxelViennaA Offline
      AxelViennaA Offline
      AxelVienna
      wrote on last edited by
      #2

      I don't quite understand what you mean with "intercept".
      Below is a small method to re-size and re-color an icon, taken from its path.
      It may not be the most elegant way but it works.
      Curious to see optimization proposals ;-)

      QImage shape_icon(const QString &icon, const QColor &color, const qreal &strength=1.0, const int &w=40, const int &h=40)
      {
          // Resize icon and put it into QImage
          QImage src = QIcon(icon).pixmap(QSize(w,h)).toImage();
          if(src.isNull()) return QImage();
          
          // prepare graphics scene and pixmap
          QGraphicsScene scene;
          QGraphicsPixmapItem item;
          item.setPixmap(QPixmap::fromImage(src));
          
          // create an effect with color and strength
          QGraphicsColorizeEffect effect;
          effect.setColor(color);
          effect.setStrength(strength);
          item.setGraphicsEffect(&effect);
          scene.addItem(&item);
          QImage res = src;
          QPainter ptr(&res);
          scene.render(&ptr, QRectF(), src.rect() );
          return res;
      }
      
      

      C++ and Python walk into a bar. C++ reuses the first glass.

      1 Reply Last reply
      1
      • E Offline
        E Offline
        Ewan Green
        wrote on last edited by Ewan Green
        #3

        Depending on the style, icons will be set on buttons (QDialogButtonBox, QMessageBox::StandardButtons, etc.). These icons are never guaranteed to match the "theme" that you have in place, and I'm wondering if there's a virtual method somewhere that would allow me to process the QIcon and return the processed. I've already come up with functionality to resize & reshape an icon, but I appreciate the suggestion.
        My methodology takes code from a private class used by QGraphicsColorizeEffect called QPixmapColorizeFilter. I simply make a new pixmap from the icon at a certain size & run it through the recolor function, and as long as the accompanying QPainter works as it should then my pixmap will be replaced with the colorized one.

        Usage looks like this.

        QIcon function() {
          QPixmap px = QIcon(/* whatever icon */).pixmap(/* whatever size */);
          QPainter p(&px);
          recolor(&p, px, QColor(/* whatever color */), 1 /* whatever strength */);
          return QIcon(px);
        }
        

        Ewan Green

        1 Reply Last reply
        0

        • Login

        • Login or register to search.
        • First post
          Last post
        0
        • Categories
        • Recent
        • Tags
        • Popular
        • Users
        • Groups
        • Search
        • Get Qt Extensions
        • Unsolved