Files
create/src/Gui/BitmapFactory.cpp
Markus Reitböck a72a0d6405 Gui: use CMake to generate precompiled headers on all platforms
"Professional CMake" book suggest the following:

"Targets should build successfully with or without compiler support for precompiled headers. It
should be considered an optimization, not a requirement. In particular, do not explicitly include a
precompile header (e.g. stdafx.h) in the source code, let CMake force-include an automatically
generated precompile header on the compiler command line instead. This is more portable across
the major compilers and is likely to be easier to maintain. It will also avoid warnings being
generated from certain code checking tools like iwyu (include what you use)."

Therefore, removed the "#include <PreCompiled.h>" from sources, also
there is no need for the "#ifdef _PreComp_" anymore
2025-09-14 09:47:03 +02:00

695 lines
21 KiB
C++

/***************************************************************************
* Copyright (c) 2004 Jürgen Riegel <juergen.riegel@web.de> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
# include <QApplication>
# include <QBitmap>
# include <QDir>
# include <QFile>
# include <QFileInfo>
# include <QMap>
# include <QImageReader>
# include <QPainter>
# include <QPalette>
# include <QScreen>
# include <QString>
# include <QSvgRenderer>
# include <QStyleOption>
#include <string>
#include <Inventor/fields/SoSFImage.h>
#include <App/Application.h>
#include <Base/Console.h>
#include <Base/ConsoleObserver.h>
#include "BitmapFactory.h"
using namespace Gui;
namespace Gui {
class BitmapFactoryInstP
{
public:
QMap<std::string, QPixmap> xpmCache;
bool useIconTheme;
};
}
BitmapFactoryInst* BitmapFactoryInst::_pcSingleton = nullptr;
BitmapFactoryInst& BitmapFactoryInst::instance()
{
if (!_pcSingleton)
{
_pcSingleton = new BitmapFactoryInst;
std::map<std::string,std::string>::const_iterator it;
it = App::GetApplication().Config().find("ProgramIcons");
if (it != App::GetApplication().Config().end()) {
QString home = QString::fromStdString(App::Application::getHomePath());
QString path = QString::fromUtf8(it->second.c_str());
if (QDir(path).isRelative()) {
path = QFileInfo(QDir(home), path).absoluteFilePath();
}
_pcSingleton->addPath(path);
}
_pcSingleton->addPath(QStringLiteral("%1/icons").arg(QString::fromStdString(App::Application::getHomePath())));
_pcSingleton->addPath(QStringLiteral("%1/icons").arg(QString::fromStdString(App::Application::getUserAppDataDir())));
_pcSingleton->addPath(QLatin1String(":/icons/"));
_pcSingleton->addPath(QLatin1String(":/Icons/"));
}
return *_pcSingleton;
}
void BitmapFactoryInst::destruct ()
{
if (_pcSingleton)
delete _pcSingleton;
_pcSingleton = nullptr;
}
BitmapFactoryInst::BitmapFactoryInst()
{
d = new BitmapFactoryInstP;
restoreCustomPaths();
configureUseIconTheme();
}
BitmapFactoryInst::~BitmapFactoryInst()
{
delete d;
}
void BitmapFactoryInst::restoreCustomPaths()
{
Base::Reference<ParameterGrp> group = App::GetApplication().GetParameterGroupByPath
("User parameter:BaseApp/Preferences/Bitmaps");
std::vector<std::string> paths = group->GetASCIIs("CustomPath");
for (auto & path : paths) {
addPath(QString::fromUtf8(path.c_str()));
}
}
void Gui::BitmapFactoryInst::configureUseIconTheme()
{
Base::Reference<ParameterGrp> group = App::GetApplication().GetParameterGroupByPath
("User parameter:BaseApp/Preferences/Bitmaps/Theme");
d->useIconTheme = group->GetBool("UseIconTheme", group->GetBool("ThemeSearchPaths", false));
}
void BitmapFactoryInst::addPath(const QString& path)
{
QDir::addSearchPath(QStringLiteral("icons"), path);
}
void BitmapFactoryInst::removePath(const QString& path)
{
QStringList iconPaths = QDir::searchPaths(QStringLiteral("icons"));
int pos = iconPaths.indexOf(path);
if (pos != -1) {
iconPaths.removeAt(pos);
QDir::setSearchPaths(QStringLiteral("icons"), iconPaths);
}
}
QStringList BitmapFactoryInst::getPaths() const
{
return QDir::searchPaths(QStringLiteral("icons"));
}
QStringList BitmapFactoryInst::findIconFiles() const
{
QStringList files, filters;
QList<QByteArray> formats = QImageReader::supportedImageFormats();
for (QList<QByteArray>::iterator it = formats.begin(); it != formats.end(); ++it)
filters << QStringLiteral("*.%1").arg(QString::fromLatin1(*it).toLower());
QStringList paths = QDir::searchPaths(QStringLiteral("icons"));
paths.removeDuplicates();
for (QStringList::Iterator pt = paths.begin(); pt != paths.end(); ++pt) {
QDir d(*pt);
d.setNameFilters(filters);
QFileInfoList fi = d.entryInfoList();
for (QFileInfoList::iterator it = fi.begin(); it != fi.end(); ++it)
files << it->absoluteFilePath();
}
files.removeDuplicates();
return files;
}
void BitmapFactoryInst::addPixmapToCache(const char* name, const QPixmap& icon)
{
d->xpmCache[name] = icon;
}
bool BitmapFactoryInst::findPixmapInCache(const char* name, QPixmap& px) const
{
QMap<std::string, QPixmap>::Iterator it = d->xpmCache.find(name);
if (it != d->xpmCache.end()) {
px = it.value();
return true;
}
return false;
}
QIcon BitmapFactoryInst::iconFromTheme(const char* name, const QIcon& fallback)
{
if (!d->useIconTheme) {
return iconFromDefaultTheme(name, fallback);
}
QString iconName = QString::fromUtf8(name);
QIcon icon = QIcon::fromTheme(iconName, fallback);
if (icon.isNull()) {
QPixmap px = pixmap(name);
if (!px.isNull())
icon.addPixmap(px);
}
return icon;
}
bool BitmapFactoryInst::loadPixmap(const QString& filename, QPixmap& icon) const
{
QFileInfo fi(filename);
if (fi.exists()) {
// first check if it's an SVG because Qt's qsvg4 module shouldn't be used therefore
if (fi.suffix().toLower() == QLatin1String("svg")) {
QFile svgFile(fi.filePath());
if (svgFile.open(QFile::ReadOnly | QFile::Text)) {
QByteArray content = svgFile.readAll();
icon = pixmapFromSvg(content, QSize(64,64));
}
}
else {
// try with Qt plugins
icon.load(fi.filePath());
}
}
return !icon.isNull();
}
QIcon Gui::BitmapFactoryInst::iconFromDefaultTheme(const char* name, const QIcon& fallback)
{
QIcon icon;
QPixmap px = pixmap(name);
if (!px.isNull()) {
icon.addPixmap(px);
return icon;
} else {
return fallback;
}
return icon;
}
QPixmap BitmapFactoryInst::pixmap(const char* name) const
{
if (!name || *name == '\0')
return {};
// as very first test check whether the pixmap is in the cache
QMap<std::string, QPixmap>::Iterator it = d->xpmCache.find(name);
if (it != d->xpmCache.end())
return it.value();
QPixmap icon;
// Try whether an absolute path is given
QString fn = QString::fromUtf8(name);
loadPixmap(fn, icon);
// try to find it in the 'icons' search paths
if (icon.isNull()) {
QList<QByteArray> formats = QImageReader::supportedImageFormats();
formats.prepend("SVG"); // check first for SVG to use special import mechanism
QString fileName = QStringLiteral("icons:") + fn;
if (!loadPixmap(fileName, icon)) {
// Go through supported file formats
for (QList<QByteArray>::iterator fm = formats.begin(); fm != formats.end(); ++fm) {
QString path = QStringLiteral("%1.%2").arg(fileName,
QString::fromLatin1((*fm).toLower().constData()));
if (loadPixmap(path, icon)) {
break;
}
}
}
}
if (!icon.isNull()) {
d->xpmCache[name] = icon;
return icon;
}
Base::Console().warning("Cannot find icon: %s\n", name);
return QPixmap(Gui::BitmapFactory().pixmapFromSvg("help-browser", QSize(16, 16)));
}
QPixmap BitmapFactoryInst::pixmapFromSvg(const char* name, const QSizeF& size,
const ColorMap& colorMapping) const
{
static qreal dpr = getMaximumDPR();
// If an absolute path is given
QPixmap icon;
QString iconPath;
QString fn = QString::fromUtf8(name);
if (QFile(fn).exists())
iconPath = fn;
// try to find it in the 'icons' search paths
if (iconPath.isEmpty()) {
QString fileName = QStringLiteral("icons:") + fn;
QFileInfo fi(fileName);
if (fi.exists()) {
iconPath = fi.filePath();
}
else {
fileName += QLatin1String(".svg");
fi.setFile(fileName);
if (fi.exists()) {
iconPath = fi.filePath();
}
}
}
if (!iconPath.isEmpty()) {
QFile file(iconPath);
if (file.open(QFile::ReadOnly | QFile::Text)) {
QByteArray content = file.readAll();
icon = pixmapFromSvg(content, size * dpr, colorMapping);
}
}
if (!icon.isNull()) {
icon.setDevicePixelRatio(dpr);
}
return icon;
}
QPixmap BitmapFactoryInst::pixmapFromSvg(const QByteArray& originalContents, const QSizeF& size,
const ColorMap& colorMapping) const
{
QString stringContents = QString::fromUtf8(originalContents);
for ( const auto &colorToColor : colorMapping ) {
ulong fromColor = colorToColor.first;
ulong toColor = colorToColor.second;
QString fromColorString = QStringLiteral("#%1").arg(fromColor, 6, 16, QChar::fromLatin1('0'));
QString toColorString = QStringLiteral("#%1").arg(toColor, 6, 16, QChar::fromLatin1('0'));
stringContents = stringContents.replace(fromColorString, toColorString);
}
QByteArray contents = stringContents.toUtf8();
QImage image(size.toSize(), QImage::Format_ARGB32_Premultiplied);
image.fill(0x00000000);
QPainter p(&image);
QSvgRenderer svg;
{
// tmp. disable the report window to suppress some bothering warnings
const Base::ILoggerBlocker blocker("ReportOutput", Base::ConsoleSingleton::MsgType_Wrn);
svg.load(contents);
}
svg.render(&p);
p.end();
return QPixmap::fromImage(image);
}
QStringList BitmapFactoryInst::pixmapNames() const
{
QStringList names;
for (QMap<std::string, QPixmap>::Iterator It = d->xpmCache.begin(); It != d->xpmCache.end(); ++It) {
QString item = QString::fromUtf8(It.key().c_str());
if (!names.contains(item))
names << item;
}
return names;
}
QPixmap BitmapFactoryInst::resize(int w, int h, const QPixmap& p, Qt::BGMode bgmode) const
{
if (bgmode == Qt::TransparentMode) {
if (p.width() == 0 || p.height() == 0)
w = 1;
QPixmap pix = p;
int x = pix.width () > w ? 0 : (w - pix.width ())/2;
int y = pix.height() > h ? 0 : (h - pix.height())/2;
if (x == 0 && y == 0)
return pix;
QPixmap pm (w,h);
QBitmap mask (w,h);
mask.fill(Qt::color0);
QBitmap bm = pix.mask();
if (!bm.isNull())
{
QPainter painter(&mask);
painter.drawPixmap(QPoint(x, y), bm, QRect(0, 0, pix.width(), pix.height()));
pm.setMask(mask);
}
else
{
pm.setMask(mask);
pm = fillRect(x, y, pix.width(), pix.height(), pm, Qt::OpaqueMode);
}
QPainter pt;
pt.begin( &pm );
pt.drawPixmap(x, y, pix);
pt.end();
return pm;
} else { // Qt::OpaqueMode
QPixmap pix = p;
if (pix.width() == 0 || pix.height() == 0)
return pix; // do not resize a null pixmap
QPalette pal = qApp->palette();
QColor dl = pal.color(QPalette::Disabled, QPalette::Light);
QColor dt = pal.color(QPalette::Disabled, QPalette::Text);
QPixmap pm(w,h);
pm.fill(dl);
QPainter pt;
pt.begin( &pm );
pt.setPen( dl );
pt.drawPixmap(1, 1, pix);
pt.setPen( dt );
pt.drawPixmap(0, 0, pix);
pt.end();
return pm;
}
}
QPixmap BitmapFactoryInst::fillRect(int x, int y, int w, int h, const QPixmap& p, Qt::BGMode bgmode) const
{
QBitmap b = p.mask();
if (b.isNull())
return p; // sorry, but cannot do anything
QPixmap pix = p;
// modify the mask
QPainter pt;
pt.begin(&b);
if (bgmode == Qt::OpaqueMode)
pt.fillRect(x, y, w, h, Qt::color1); // make opaque
else // Qt::TransparentMode
pt.fillRect(x, y, w, h, Qt::color0); // make transparent
pt.end();
pix.setMask(b);
return pix;
}
QPixmap BitmapFactoryInst::merge(const QPixmap& p1, const QPixmap& p2, bool vertical) const
{
int width = 0;
int height = 0;
int x = 0;
int y = 0;
// get the size for the new pixmap
if (vertical) {
y = p1.height();
width = qMax( p1.width(), p2.width() );
height = p1.height() + p2.height();
} else {
x = p1.width();
width = p1.width() + p2.width();
height = qMax( p1.height(), p2.height() );
}
QPixmap res( width, height );
QBitmap mask( width, height );
QBitmap mask1 = p1.mask();
QBitmap mask2 = p2.mask();
mask.fill( Qt::color0 );
auto* pt1 = new QPainter(&res);
pt1->drawPixmap(0, 0, p1);
pt1->drawPixmap(x, y, p2);
delete pt1;
auto* pt2 = new QPainter(&mask);
pt2->drawPixmap(0, 0, mask1);
pt2->drawPixmap(x, y, mask2);
delete pt2;
res.setMask(mask);
return res;
}
QPixmap BitmapFactoryInst::merge(const QPixmap& p1, const QPixmap& p2, Position pos) const
{
// does the similar as the method above except that this method does not resize the resulting pixmap
int x = 0, y = 0;
qreal dpr1 = p1.devicePixelRatio();
qreal dpr2 = p2.devicePixelRatio();
switch (pos)
{
case TopLeft:
break;
case TopRight:
x = p1.width ()/dpr1 - p2.width ()/dpr2;
break;
case BottomLeft:
y = p1.height()/dpr1 - p2.height()/dpr2;
break;
case BottomRight:
x = p1.width ()/dpr1 - p2.width ()/dpr2;
y = p1.height()/dpr1 - p2.height()/dpr2;
break;
}
QPixmap p = p1;
p = fillRect(x, y, p2.width(), p2.height(), p, Qt::OpaqueMode);
QPainter pt;
pt.begin( &p );
pt.setPen(Qt::NoPen);
pt.drawRect(x, y, p2.width(), p2.height());
pt.drawPixmap(x, y, p2);
pt.end();
return p;
}
QPixmap BitmapFactoryInst::disabled(const QPixmap& p) const
{
QStyleOption opt;
opt.palette = QApplication::palette();
return QApplication::style()->generatedIconPixmap(QIcon::Disabled, p, &opt);
}
QPixmap BitmapFactoryInst::empty(QSize size) const
{
qreal dpr = getMaximumDPR();
QPixmap res(size * dpr);
res.fill(Qt::transparent);
res.setDevicePixelRatio(dpr);
return res;
}
void BitmapFactoryInst::convert(const QImage& p, SoSFImage& img) const
{
SbVec2s size;
size[0] = p.width();
size[1] = p.height();
int buffersize = static_cast<int>(p.sizeInBytes());
int numcomponents = 0;
QVector<QRgb> table = p.colorTable();
if (!table.isEmpty()) {
if (p.hasAlphaChannel()) {
if (p.allGray()) {
numcomponents = 2;
}
else {
numcomponents = 4;
}
}
else {
if (p.allGray()) {
numcomponents = 1;
}
else {
numcomponents = 3;
}
}
}
else {
numcomponents = buffersize / (size[0] * size[1]);
}
int depth = numcomponents;
// Coin3D only supports up to 32-bit images
if (numcomponents == 8) {
numcomponents = 4;
}
// allocate image data
img.setValue(size, numcomponents, nullptr);
unsigned char* bytes = img.startEditing(size, numcomponents);
int width = (int)size[0];
int height = (int)size[1];
for (int y = 0; y < height; y++) {
unsigned char* line = &bytes[width * numcomponents * (height - (y + 1))];
for (int x = 0; x < width; x++) {
QColor col = p.pixelColor(x,y);
switch (depth) {
default:
break;
case 1:
{
QRgb rgb = col.rgb();
line[0] = qGray(rgb);
} break;
case 2:
{
QRgb rgb = col.rgba();
line[0] = qGray(rgb);
line[1] = qAlpha(rgb);
} break;
case 3:
{
QRgb rgb = col.rgb();
line[0] = qRed(rgb);
line[1] = qGreen(rgb);
line[2] = qBlue(rgb);
} break;
case 4:
{
QRgb rgb = col.rgba();
line[0] = qRed(rgb);
line[1] = qGreen(rgb);
line[2] = qBlue(rgb);
line[3] = qAlpha(rgb);
} break;
case 8:
{
QRgba64 rgb = col.rgba64();
line[0] = qRed(rgb);
line[1] = qGreen(rgb);
line[2] = qBlue(rgb);
line[3] = qAlpha(rgb);
} break;
}
line += numcomponents;
}
}
img.finishEditing();
}
void BitmapFactoryInst::convert(const SoSFImage& p, QImage& img) const
{
SbVec2s size;
int numcomponents;
const unsigned char * bytes = p.getValue(size, numcomponents);
if (!bytes)
return;
int width = (int)size[0];
int height = (int)size[1];
img = QImage(width, height, QImage::Format_RGB32);
QRgb * bits = (QRgb*) img.bits();
for (int y = 0; y < height; y++)
{
const unsigned char * line = &bytes[width*numcomponents*(height-(y+1))];
for (int x = 0; x < width; x++)
{
switch (numcomponents)
{
default:
case 1:
*bits++ = qRgb(line[0], line[0], line[0]);
break;
case 2:
*bits++ = qRgba(line[0], line[0], line[0], line[1]);
break;
case 3:
*bits++ = qRgb(line[0], line[1], line[2]);
break;
case 4:
*bits++ = qRgba(line[0], line[1], line[2], line[3]);
break;
}
line += numcomponents;
}
}
}
QIcon BitmapFactoryInst::mergePixmap (const QIcon &base, const QPixmap &px, Gui::BitmapFactoryInst::Position position)
{
QIcon overlayedIcon;
int w = QApplication::style()->pixelMetric(QStyle::PM_ListViewIconSize);
overlayedIcon.addPixmap(Gui::BitmapFactory().merge(base.pixmap(w, w, QIcon::Normal, QIcon::Off),
px,position), QIcon::Normal, QIcon::Off);
overlayedIcon.addPixmap(Gui::BitmapFactory().merge(base.pixmap(w, w, QIcon::Normal, QIcon::On ),
px,position), QIcon::Normal, QIcon::Off);
return overlayedIcon;
}
qreal BitmapFactoryInst::getMaximumDPR()
{
qreal dpr = 1.0F;
for (QScreen* screen: QGuiApplication::screens()) {
dpr = std::max(screen->devicePixelRatio(), dpr);
}
return dpr;
}