2018年4月8日日曜日

SVG 色名と X11 色名

SVG 色名と X11 色名の一覧を表示して比較するプログラムです。
x11rgbtext.h:

#pragma once

#include <QRegularExpression>
#include <QStringList>

class X11RGBText
{
private:
    X11RGBText();
    virtual ~X11RGBText() = default;

    void init();

public:
    X11RGBText(const X11RGBText&) = delete;
    X11RGBText& operator=(const X11RGBText&) = delete;
    X11RGBText(X11RGBText&&) = delete;
    X11RGBText& operator=(X11RGBText&&) = delete;

    static X11RGBText& instance();

    QStringList names() const;
    QColor color(const QString& name) const;

private:
    QStringList colorNames;
    QHash<QString, QColor> x11rgbTextHash;
};
names() は X11 色名のリストを返し、color() は指定した色名の QColor を返します。
x11rgbtext.cpp:

#include "x11rgbtext.h"
#include <QTextStream>
#include <QScrollArea>
#include <QRegularExpression>
#include <QFile>

X11RGBText::X11RGBText()
{
    init();
}

X11RGBText& X11RGBText::instance() {
    static X11RGBText x11rgbText;
    return x11rgbText;
}

QStringList X11RGBText::names() const {
    return colorNames;
}

QColor X11RGBText::color(const QString& name) const
{
    return *x11rgbTextHash.find(name);
}

void X11RGBText::init()
{
    const QRegularExpression leadingWhiteSpacesRegularExpression = QRegularExpression("^\\s+");
    const QRegularExpression startWithDigitRegularExpression = QRegularExpression("^[0-9]");

    for (const QString& rgbTextFileName : { "/etc/X11/rgb.txt",
                                            "/usr/lib/X11/rgb.txt",
                                            "/usr/share/X11/rgb.txt",
                                            "/opt/X11/share/X11/rgb.txt",
                                            "/usr/share/emacs/22.1/etc/rgb.txt",
                                            "/opt/local/share/netpbm/rgb.txt" }) {
        QFile rgbTextFile(rgbTextFileName);
        if (rgbTextFile.open(QIODevice::ReadOnly)) {
            QTextStream rgbTextInputStream(&rgbTextFile);
            while (!rgbTextInputStream.atEnd()) {
                QString line = rgbTextInputStream.readLine();
                line = line.replace(leadingWhiteSpacesRegularExpression, "");
                if (!line.contains(startWithDigitRegularExpression)) {
                    continue;
                }
                QTextStream lineStream(&line);
                int r, g, b;
                QString colorName;
                lineStream >> r >> g >> b;
                colorName = lineStream.readAll().trimmed();
                colorNames.append(colorName);
                x11rgbTextHash.insert(colorName, QColor(r, g, b));
            }
            break;
        }
    }
}
rgb.txt ファイルは以降のような形式で記述されています。init() では色を定義している行を探して、色名を QStringList 型の colorNames に入れ、 色名をキーにして色を求めるために QHash<QString, QColor> 型の x11rgbTextHash に登録しています。
/etc/X11/rgb.txt:
! $Xorg: rgb.txt,v 1.3 2000/08/17 19:54:00 cpqbld Exp $
255 250 250             snow
248 248 255             ghost white
248 248 255             GhostWhite
245 245 245             white smoke
245 245 245             WhiteSmoke
...
/opt/local/share/netpbm/rgb.txt:
# The colors in this color dictionary are from the following sources, in
# order.  Some color names are defined multiple times, and sometimes
# with different colors.  Many colors have multiple names.
...
  0   0   0 Black
255 255 255 White
255   0   0 Red
  0 255   0 Green
  0   0 255 Blue
  0 255 255 Cyan
...
colorstack.h:

#pragma once

#include <QWidget>

class ColorStack : public QWidget
{
    Q_OBJECT

public:
    explicit ColorStack(const QStringList& colors, QWidget* parent = 0);
    bool eventFilter(QObject* watched, QEvent* event) override;
};
色名の一覧を表示するウィジェットです。
colorstack.cpp:

#include "colorstack.h"
#include "x11rgbtext.h"
#include <QLayout>
#include <QLabel>
#include <QToolTip>
#include <QHelpEvent>

ColorStack::ColorStack(const QStringList& colorNames, QWidget* parent)
    : QWidget(parent)
{
    const auto topLayout = new QVBoxLayout(this);
    topLayout->setSpacing(0);
    topLayout->setMargin(0);

    for (const QString& colorName : qAsConst(colorNames)) {
        QColor color(colorName);

        auto colorLabel = new QLabel(colorName);
        colorLabel->setAutoFillBackground(true);
        QPalette colorLabelPalette = colorLabel->palette();
        if (color.isValid()) {
            colorLabelPalette.setColor(QPalette::Window, color);
        } else {
            color = X11RGBText::instance().color(colorName);
            colorLabelPalette.setColor(QPalette::Window, color);
        }

        // Choose a proper text color for the background color.
        int gray = 0;
        if (color.isValid()) {
            if (colorName.compare("transparent", Qt::CaseInsensitive) == 0) {  // Qt extension to SVG color names
                gray = qGray(colorLabel->palette().color(QPalette::Window).rgb());
            } else {
                gray = qGray(color.rgb());
            }
        }
        if (double(gray) > (255.0 / 2.0)) {
            colorLabelPalette.setColor(QPalette::WindowText, "Black");
        } else {
            colorLabelPalette.setColor(QPalette::WindowText, "White");
        }

        colorLabel->setPalette(colorLabelPalette);
        colorLabel->installEventFilter(this);
        topLayout->addWidget(colorLabel);
    }
}

bool ColorStack::eventFilter(QObject* watched, QEvent* event)
{
    if (event->type() == QEvent::ToolTip) {
        const QHelpEvent* const helpEvent = static_cast<QHelpEvent*>(event);
        QLabel* const label = qobject_cast<QLabel*>(watched);
        if (label) {
            const QColor color = label->palette().color(QPalette::Window);
            QToolTip::showText(helpEvent->globalPos(), color.name());
        }
    }
    return false;
}
イベントフィルターでは表示されている色名のラベルの上にマウスカーソルが来たときに 16 進数で色名を表示しています。
harness.h:

#pragma once

#include "colorstack.h"
#include "x11rgbtext.h"
#include <QWidget>

class Harness : public QWidget
{
    Q_OBJECT

public:
    Harness();
};
SVG 色名の一覧と X11 色名の一覧を横に並べて表示するウィジェットです。
harness.cpp:

#include "harness.h"
#include "x11rgbtext.h"
#include <QLayout>
#include <QLabel>
#include <QScrollArea>

Harness::Harness()
    : QWidget(0)
{
    auto topLayout = new QHBoxLayout(this);

    auto addColorStack = [&](const QString& title, ColorStack* const colorStack) {
        auto colorStackLayout = new QVBoxLayout;

        colorStackLayout->addWidget(new QLabel(title));

        const auto scrollArea = new QScrollArea;
        scrollArea->setWidgetResizable(true);
        scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
        scrollArea->setWidget(colorStack);
        colorStackLayout->addWidget(scrollArea);

        topLayout->addLayout(colorStackLayout);
    };

    addColorStack("SVG name", new ColorStack(QColor::colorNames()));
    addColorStack("rgb.txt", new ColorStack(X11RGBText::instance().names()));
}

main.cpp:

#include "harness.h"
#include <QApplication>

int main(int argc, char** argv)
{
    QApplication app(argc, argv);

    Harness harness;
    harness.show();

    return app.exec();
}

macOS と Ubuntu で実行した結果です。

macOS での実行結果
Ubuntu での実行結果

参考情報

QColor Class
ウェブカラー

0 件のコメント:

コメントを投稿

注: コメントを投稿できるのは、このブログのメンバーだけです。