Adding a framework to generate code for fonts.

- Work in progress to add a framework generating bitmap fonts.
main
Lucky Resistor 2021-06-06 13:29:22 +02:00
rodzic 3c46483bcb
commit 0018e4e9db
40 zmienionych plików z 1962 dodań i 224 usunięć

Wyświetl plik

@ -0,0 +1,67 @@
//
// (c)2021 by Lucky Resistor. https://luckyresistor.me/
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
#include "ApplicationController.hpp"
ApplicationController *ApplicationController::_instance = nullptr;
ApplicationController::ApplicationController(QObject *parent)
: QObject(parent)
{
_instance = this;
// Start as soon the main loop is running.
QMetaObject::invokeMethod(this, &ApplicationController::start, Qt::QueuedConnection);
}
QFont ApplicationController::monospaceFont() const
{
QFont monospaceFont;
#ifdef Q_OS_WIN32
monospaceFont = QFont("Consolas", 12);
#else
#ifdef Q_OS_MAC
monospaceFont = QFont("Menlo", 12);
#else
monospaceFont = QFont("Lucida Console", 12);
#endif
#endif
return monospaceFont;
}
void ApplicationController::start()
{
_mainWindow = new MainWindow();
_mainWindow->show();
}
ApplicationController *ApplicationController::instance()
{
return _instance;
}
ApplicationController* gApp()
{
return ApplicationController::instance();
}

Wyświetl plik

@ -0,0 +1,57 @@
//
// (c)2021 by Lucky Resistor. https://luckyresistor.me/
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
#pragma once
#include "MainWindow.hpp"
#include <QtCore/QObject>
/// The application controller
///
class ApplicationController : public QObject
{
Q_OBJECT
public:
/// ctor
///
explicit ApplicationController(QObject *parent = nullptr);
public:
/// Get the default monospaced font.
///
QFont monospaceFont() const;
public:
/// Get the global instance.
///
static ApplicationController* instance();
private:
/// Start the application.
///
Q_SLOT void start();
private:
static ApplicationController *_instance; ///< The controller instance.
MainWindow *_mainWindow; ///< The main window.
};
ApplicationController* gApp();

Wyświetl plik

@ -0,0 +1,29 @@
//
// (c)2021 by Lucky Resistor. https://luckyresistor.me/
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
#include "BitmapConverter.hpp"
BitmapConverter::BitmapConverter(const QString &displayName)
: Converter(displayName)
{
}
Converter::Mode BitmapConverter::mode() const
{
return Mode::Bitmap;
}

Wyświetl plik

@ -0,0 +1,45 @@
//
// (c)2021 by Lucky Resistor. https://luckyresistor.me/
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
#pragma once
#include "Converter.hpp"
/// The base class for all bitmap converters.
///
class BitmapConverter : public Converter
{
public:
/// ctor
///
BitmapConverter(const QString &displayName);
public: // implement Converter
Mode mode() const override;
public:
/// Generate the code from the given image.
///
/// @param image The image to convert.
/// @param parameter A map with parameters passed to this converter.
/// @return The generated code.
///
virtual QString generateCode(const QImage &image, const QVariantMap &parameter) const = 0;
};

Wyświetl plik

@ -17,16 +17,19 @@
#include "BitmapPanel.hpp"
#include "ApplicationController.hpp"
#include "Converter.hpp"
#include "BitmapPreview.hpp"
#include <QComboBox>
#include <QLabel>
#include <QScrollArea>
#include <QVBoxLayout>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QLabel>
#include <QtWidgets/QScrollArea>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QPushButton>
BitmapPanel::BitmapPanel(QWidget *parent) : QWidget(parent)
BitmapPanel::BitmapPanel(QWidget *parent)
: QWidget(parent), _converter(nullptr)
{
initializeUi();
}
@ -35,16 +38,49 @@ BitmapPanel::BitmapPanel(QWidget *parent) : QWidget(parent)
void BitmapPanel::setImage(const QImage &image)
{
_bitmapPreview->setImage(image);
_bitmapPreview->setFixedSize(_bitmapPreview->sizeHint());
}
void BitmapPanel::setConverter(const Converter *converter)
{
_bitmapPreview->setConverter(converter);
if (_converter != converter) {
_converter = converter;
_bitmapPreview->setConverter(converter);
_bitmapPreview->setFixedSize(_bitmapPreview->sizeHint());
_characterSelector->setVisible(_converter->mode() == Converter::Mode::Font);
}
}
void BitmapPanel::setCharacters(const QString &characters)
{
if (_characters != characters) {
_characters = characters;
_characterSelector->clear();
for (int i = 0; i < characters.size(); ++i) {
const auto c = _characters.at(i);
_characterSelector->addItem(QString("%1: %2 0x%3").arg(i, 3, 10, QChar('0'))
.arg(QString(c)).arg(c.unicode(), 4, 16, QChar('0')), c);
}
_characterSelector->setCurrentIndex(0);
}
}
void BitmapPanel::setParameter(const QVariantMap &parameter)
{
_bitmapPreview->setParameter(parameter);
_bitmapPreview->setFixedSize(_bitmapPreview->sizeHint());
}
QChar BitmapPanel::selectedCharacter() const
{
return _characterSelector->currentData().toChar();
}
void BitmapPanel::initializeUi()
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
@ -72,6 +108,35 @@ void BitmapPanel::initializeUi()
auto previewSettingsLayout = new QHBoxLayout(previewSettingsPanel);
previewSettingsLayout->setContentsMargins(0, 0, 0, 0);
previewSettingsLayout->setSpacing(4);
_fontConversionTools = new QFrame();
auto fontConversionLayout = new QHBoxLayout(_fontConversionTools);
fontConversionLayout->setContentsMargins(0, 0, 0, 0);
fontConversionLayout->setSpacing(2);
previewSettingsLayout->addWidget(_fontConversionTools);
auto previousCharButton = new QPushButton("<");
fontConversionLayout->addWidget(previousCharButton);
_characterSelector = new QComboBox();
_characterSelector->setFont(gApp()->monospaceFont());
fontConversionLayout->addWidget(_characterSelector);
auto nextCharButton = new QPushButton(">");
fontConversionLayout->addWidget(nextCharButton);
connect(_characterSelector, &QComboBox::currentIndexChanged, [=]{
Q_EMIT selectedCharacterChanged(_characterSelector->currentData().toChar());
});
connect(previousCharButton, &QPushButton::clicked, [=]{
auto currentIndex = _characterSelector->currentIndex();
if (currentIndex > 0) {
_characterSelector->setCurrentIndex(currentIndex - 1);
}
});
connect(nextCharButton, &QPushButton::clicked, [=]{
auto currentIndex = _characterSelector->currentIndex();
if (currentIndex < (_characterSelector->count()-1)) {
_characterSelector->setCurrentIndex(currentIndex + 1);
}
});
previewSettingsLayout->addStretch();
previewSettingsLayout->addWidget(new QLabel(tr("Overlay Mode:")));
_overlaySelector = new QComboBox();

Wyświetl plik

@ -23,13 +23,18 @@
class BitmapPreview;
class Converter;
class QComboBox;
class QFrame;
/// A panel with the scrollable bitmap preview.
///
class BitmapPanel : public QWidget
{
Q_OBJECT
public:
/// Create the panel.
///
explicit BitmapPanel(QWidget *parent = nullptr);
public:
@ -41,11 +46,34 @@ public:
///
void setConverter(const Converter *converter);
private:
void initializeUi();
/// Set the current character set.
///
void setCharacters(const QString &characters);
/// Set the current parameter.
///
void setParameter(const QVariantMap &parameter);
/// Get the current selected character
///
QChar selectedCharacter() const;
private:
/// Initialize all UI elements.
///
void initializeUi();
Q_SIGNALS:
/// Emitted if the selected character changes.
///
void selectedCharacterChanged(QChar c);
private:
const Converter *_converter; ///< The current converter.
QString _characters; ///< The list of characters to convert.
BitmapPreview *_bitmapPreview; ///< The bitmap preview;
QFrame *_fontConversionTools; ///< The area with tools for font conversion.
QComboBox *_characterSelector; ///< The selector for a single character.
QComboBox *_overlaySelector; ///< The selector for the overlays.
};

Wyświetl plik

@ -48,9 +48,7 @@ void BitmapPreview::setImage(const QImage &image)
if (_image != image) {
_image = image;
_pixmap = QPixmap::fromImage(image);
if (_converter != nullptr) {
setGeneratedSize(_converter->generatedSize(_image.size()));
}
updateGeneratedSize();
recalculate();
update();
}
@ -80,9 +78,18 @@ void BitmapPreview::setConverter(const Converter *converter)
{
if (_converter != converter) {
_converter = converter;
if (_converter != nullptr) {
setGeneratedSize(_converter->generatedSize(_image.size()));
}
updateGeneratedSize();
update();
}
}
void BitmapPreview::setParameter(const QVariantMap &parameter)
{
if (_parameter != parameter) {
_parameter = parameter;
updateGeneratedSize();
recalculate();
update();
}
}
@ -118,7 +125,7 @@ void BitmapPreview::paintEvent(QPaintEvent *pe)
p.setOpacity(1.0);
p.translate(x, y);
OverlayPainter op(&p, pe->rect(), _displayFactor, _pixmap.size(), _generatedSize);
_converter->paintOverlay(op, _overlayMode, _image);
_converter->paintOverlay(op, _overlayMode, _image, _parameter);
}
} else {
p.setPen(Qt::white);
@ -134,6 +141,16 @@ void BitmapPreview::resizeEvent(QResizeEvent *e)
}
void BitmapPreview::updateGeneratedSize()
{
if (_converter != nullptr && _converter->mode() == Converter::Mode::Bitmap) {
setGeneratedSize(_converter->generatedSize(_image.size(), _parameter));
} else {
setGeneratedSize(_image.size());
}
}
void BitmapPreview::recalculate()
{
if (_pixmap.isNull()) {

Wyświetl plik

@ -22,6 +22,7 @@
#include <QtWidgets/QWidget>
#include <QtGui/QImage>
#include <QtGui/QPixmap>
#include <QtCore/QVariantMap>
class Converter;
@ -49,6 +50,10 @@ public:
///
void setConverter(const Converter *converter);
/// Set the current parameter.
///
void setParameter(const QVariantMap &parameter);
public:
QSize sizeHint() const override;
QSize minimumSizeHint() const override;
@ -58,6 +63,10 @@ protected: // QWidget interface
void resizeEvent(QResizeEvent *event) override;
private:
/// update the generated size
///
void updateGeneratedSize();
/// Recalculate all sizes
///
void recalculate();
@ -74,5 +83,6 @@ private:
QSize _minimumSize; ///< The preferred size of this widget.
OverlayMode _overlayMode; ///< The overlay mode.
const Converter *_converter; ///< The current converter.
QVariantMap _parameter; ///< The current set of parameter.
};

Wyświetl plik

@ -29,12 +29,18 @@ QString Converter::displayName() const
}
QSize Converter::generatedSize(const QSize &imageSize) const
QSize Converter::generatedSize(const QSize &imageSize, const QVariantMap&) const
{
return imageSize;
}
ParameterDefinitionPtr Converter::createParameterDefinition() const
{
return ParameterDefinition::create();
}
LegendDataPtr Converter::legendData(OverlayMode) const
{
auto ld = LegendData::create();
@ -43,7 +49,7 @@ LegendDataPtr Converter::legendData(OverlayMode) const
}
void Converter::paintOverlay(OverlayPainter &op, OverlayMode, const QImage&) const
void Converter::paintOverlay(OverlayPainter &op, OverlayMode, const QImage&, const QVariantMap&) const
{
op.drawPixelOutline(op.imageRect(), colorBitmapSizeOriginal, 1, 2);
if (op.generatedSize().isValid() && !op.generatedSize().isEmpty()) {

Wyświetl plik

@ -20,6 +20,7 @@
#include "LegendData.hpp"
#include "OverlayPainter.hpp"
#include "OverlayMode.hpp"
#include "ParameterDefinition.hpp"
#include <QtGui/QImage>
#include <QtCore/QObject>
@ -30,10 +31,17 @@
///
class Converter
{
public:
enum class Mode {
Bitmap,
Font
};
public:
/// Create a new converter.
///
/// @param displayName The name displayed in the UI for this coonverter.
/// @param mode The converter mode.
///
Converter(const QString &displayName);
@ -46,21 +54,21 @@ public:
///
QString displayName() const;
/// Get the mode for this converter.
///
virtual Mode mode() const = 0;
public:
/// Return the size of the generated bitmap data.
///
/// @param imageSize The size of the image.
/// @return The size of the generated bitmap data.
///
virtual QSize generatedSize(const QSize &imageSize) const;
virtual QSize generatedSize(const QSize &imageSize, const QVariantMap &parameter) const;
/// Generate the code from the given image.
/// Create the parameter definition for the converter.
///
/// @param image The image to convert.
/// @param parameter A map with parameters passed to this converter.
/// @return The generated code.
///
virtual QString generateCode(const QImage &image, const QVariantMap &parameter) const = 0;
virtual ParameterDefinitionPtr createParameterDefinition() const;
/// Return a legend for the bitmap preview.
///
@ -71,8 +79,9 @@ public:
/// @param p The painter.
/// @param mode The overlay mode (never `None`).
/// @param image The image.
/// @param parameter The current set of parameter.
///
virtual void paintOverlay(OverlayPainter &p, OverlayMode mode, const QImage &image) const;
virtual void paintOverlay(OverlayPainter &p, OverlayMode mode, const QImage &image, const QVariantMap &parameter) const;
protected:
/// The color for the image frame.

Wyświetl plik

@ -21,7 +21,7 @@
ConverterFramebuf::ConverterFramebuf(const QString &displayName)
: Converter(displayName)
: BitmapConverter(displayName)
{
}

Wyświetl plik

@ -17,12 +17,12 @@
#pragma once
#include "Converter.hpp"
#include "BitmapConverter.hpp"
/// Base class of framebuf converters.
///
class ConverterFramebuf : public Converter
class ConverterFramebuf : public BitmapConverter
{
public:
ConverterFramebuf(const QString &displayName);

Wyświetl plik

@ -25,124 +25,29 @@ ConverterFramebufMono::ConverterFramebufMono(
int unitSize)
:
ConverterFramebuf(displayName),
_unitOrientation(unitOrientation),
_bitDirection(bitDirection),
_unitSize(unitSize)
MonoTools(unitOrientation, bitDirection, unitSize)
{
}
QSize ConverterFramebufMono::generatedSize(const QSize &imageSize) const
QSize ConverterFramebufMono::generatedSize(const QSize &imageSize, const QVariantMap &parameter) const
{
if (_unitOrientation == UnitOrientation::Vertical) {
if ((imageSize.height() % _unitSize) != 0) {
return QSize(imageSize.width(), ((imageSize.height()/_unitSize)+1)*_unitSize);
} else {
return imageSize;
}
} else {
if ((imageSize.width() % _unitSize) != 0) {
return QSize(((imageSize.width()/_unitSize)+1)*_unitSize, imageSize.height());
} else {
return imageSize;
}
}
return monoGeneratedSize(imageSize, parameter);
}
LegendDataPtr ConverterFramebufMono::legendData(OverlayMode mode) const
{
auto data = ConverterFramebuf::legendData(mode);
if (mode == OverlayMode::BitAssigments) {
data->addEntry(colorBitAssignment1, colorBitAssignment2, QObject::tr("Bit Assignment"));
} else {
data->addEntry(colorPixelInterpretation, QObject::tr("Pixel Interpretation"));
}
addMonoLegendData(data, mode);
return data;
}
void ConverterFramebufMono::paintOverlay(OverlayPainter &p, OverlayMode mode, const QImage &image) const
void ConverterFramebufMono::paintOverlay(OverlayPainter &p, OverlayMode mode, const QImage &image, const QVariantMap &parameter) const
{
ConverterFramebuf::paintOverlay(p, mode, image);
if (mode == OverlayMode::BitAssigments) {
const auto bitL = (_bitDirection == BitDirection::LSB ? "0" : QString::number(_unitSize-1));
const auto bitH = (_bitDirection == BitDirection::LSB ? QString::number(_unitSize-1) : "0");
bool colorFlag = false;
if (_unitOrientation == UnitOrientation::Vertical) {
for (int y = 0; y < p.imageSize().height(); y += _unitSize) {
for (int x = 0; x < p.imageSize().width(); ++x) {
QRect rect(x, y, 1, _unitSize);
if (p.arePixelUpdated(rect)) {
const auto color = colorFlag ? colorBitAssignment1 : colorBitAssignment2;
p.drawPixelOutline(rect, color, 1);
p.drawPixelText(QRect(x, y, 1, 1), color, bitL);
p.drawPixelText(QRect(x, y+_unitSize-1, 1, 1), color, bitH);
}
colorFlag = !colorFlag;
}
if (p.imageSize().width() % 2 == 0) {
colorFlag = !colorFlag;
}
}
} else {
for (int y = 0; y < p.imageSize().height(); ++y) {
for (int x = 0; x < p.imageSize().width(); x += _unitSize) {
QRect rect(x, y, _unitSize, 1);
if (p.arePixelUpdated(rect)) {
const auto color = colorFlag ? colorBitAssignment1 : colorBitAssignment2;
p.drawPixelOutline(rect, color, 1);
p.drawPixelText(QRect(x, y, 1, 1), color, bitH);
p.drawPixelText(QRect(x+_unitSize-1, y, 1, 1), color, bitL);
}
colorFlag = !colorFlag;
}
if ((p.imageSize().width()/_unitSize) % 2 == 0) {
colorFlag = !colorFlag;
}
}
}
} else if (mode == OverlayMode::PixelInterpretation) {
for (int y = 0; y < p.generatedSize().height(); ++y) {
for (int x = 0; x < p.generatedSize().width(); ++x) {
auto text = (getPixel(x, y, image) ? "1" : "0");
const QRect rect(x, y, 1, 1);
if (p.arePixelUpdated(rect)) {
p.drawPixelText(rect, colorPixelInterpretation, text);
}
}
}
}
}
bool ConverterFramebufMono::getPixel(int x, int y, const QImage &image)
{
if (x < 0 || y < 0 || x >= image.width() || y >= image.height()) {
return false;
}
auto color = image.pixelColor(x, y);
float h, s, l, a;
color.getHslF(&h, &s, &l, &a);
if (a < 0.5) {
return false;
}
return l < 0.5;
}
uint32_t ConverterFramebufMono::readUnit(int x, int y, int dx, int dy, int count, const QImage &image)
{
uint32_t result = 0;
for (int i = 0; i < count; ++i) {
result <<= 1;
if (getPixel(x, y, image)) {
result |= 0b1;
}
x += dx;
y += dy;
}
return result;
ConverterFramebuf::paintOverlay(p, mode, image, parameter);
monoPaintOverlay(p, mode, image, parameter);
}

Wyświetl plik

@ -17,24 +17,14 @@
#pragma once
#include "MonoTools.hpp"
#include "ConverterFramebuf.hpp"
/// The base class of all mono converters
///
class ConverterFramebufMono : public ConverterFramebuf
class ConverterFramebufMono : public ConverterFramebuf, public MonoTools
{
protected:
enum class UnitOrientation {
Horizontal,
Vertical
};
enum class BitDirection {
LSB,
MSB
};
public:
/// Create a new mono converter.
///
@ -49,30 +39,8 @@ public:
int unitSize);
public: // Converter interface
QSize generatedSize(const QSize &imageSize) const override;
QSize generatedSize(const QSize &imageSize, const QVariantMap &parameter) const override;
LegendDataPtr legendData(OverlayMode mode) const override;
void paintOverlay(OverlayPainter &p, OverlayMode mode, const QImage &image) const override;
protected:
/// Interpret a single pixel.
///
/// Returns `false` if the pixel is out of bounds.
///
static bool getPixel(int x, int y, const QImage &image);
/// Read a single unit.
///
static uint32_t readUnit(int x, int y, int dx, int dy, int count, const QImage &image);
/// The colors for the bit assignments
///
static constexpr QColor colorBitAssignment1 = QColor(240, 40, 40);
static constexpr QColor colorBitAssignment2 = QColor(240, 80, 80);
static constexpr QColor colorPixelInterpretation = QColor(240, 240, 40);
protected:
UnitOrientation _unitOrientation; ///< The unit orientatioon.
BitDirection _bitDirection; ///< The bit direction.
int _unitSize; ///< The number of bits per unit.
void paintOverlay(OverlayPainter &p, OverlayMode mode, const QImage &image, const QVariantMap &parameter) const override;
};

Wyświetl plik

@ -26,7 +26,7 @@ ConverterFramebufMonoHLSB::ConverterFramebufMonoHLSB()
}
QString ConverterFramebufMonoHLSB::generateCode(const QImage &image, const QVariantMap&) const
QString ConverterFramebufMonoHLSB::generateCode(const QImage &image, const QVariantMap &parameter) const
{
QByteArray data;
for (int y = 0; y < image.height(); ++y) {
@ -34,5 +34,5 @@ QString ConverterFramebufMonoHLSB::generateCode(const QImage &image, const QVari
data.append(static_cast<uint8_t>(readUnit(x, y, 1, 0, 8, image)));
}
}
return createCode(data, generatedSize(image.size()), "MONO_HLSB");
return createCode(data, generatedSize(image.size(), parameter), "MONO_HLSB");
}

Wyświetl plik

@ -23,7 +23,7 @@ ConverterFramebufMonoHMSB::ConverterFramebufMonoHMSB()
}
QString ConverterFramebufMonoHMSB::generateCode(const QImage &image, const QVariantMap&) const
QString ConverterFramebufMonoHMSB::generateCode(const QImage &image, const QVariantMap &parameter) const
{
QByteArray data;
for (int y = 0; y < image.height(); ++y) {
@ -31,6 +31,6 @@ QString ConverterFramebufMonoHMSB::generateCode(const QImage &image, const QVari
data.append(static_cast<uint8_t>(readUnit(x+7, y, -1, 0, 8, image)));
}
}
return createCode(data, generatedSize(image.size()), "MONO_HMSB");
return createCode(data, generatedSize(image.size(), parameter), "MONO_HMSB");
}

Wyświetl plik

@ -23,7 +23,7 @@ ConverterFramebufMonoVLSB::ConverterFramebufMonoVLSB()
}
QString ConverterFramebufMonoVLSB::generateCode(const QImage &image, const QVariantMap&) const
QString ConverterFramebufMonoVLSB::generateCode(const QImage &image, const QVariantMap &parameter) const
{
QByteArray data;
for (int y = 0; y < image.height(); y += 8) {
@ -31,6 +31,6 @@ QString ConverterFramebufMonoVLSB::generateCode(const QImage &image, const QVari
data.append(static_cast<uint8_t>(readUnit(x, y+7, 0, -1, 8, image)));
}
}
return createCode(data, generatedSize(image.size()), "MONO_VLSB");
return createCode(data, generatedSize(image.size(), parameter), "MONO_VLSB");
}

149
FontConverter.cpp 100644
Wyświetl plik

@ -0,0 +1,149 @@
//
// (c)2021 by Lucky Resistor. https://luckyresistor.me/
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
#include "FontConverter.hpp"
FontConverter::FontConverter(const QString &displayName)
: Converter(displayName)
{
}
Converter::Mode FontConverter::mode() const
{
return Mode::Font;
}
ParameterDefinitionPtr FontConverter::createParameterDefinition() const
{
auto pd = Converter::createParameterDefinition();
pd->addIntegerSize("maximumSize", QObject::tr("Maximum Size"),
QSize(12, 16), QSize(8, 8), QSize(256, 256));
pd->addIntegerPosition("charOffset", QObject::tr("Char Offset"),
QPoint(0, 0), QPoint(-256, -256), QPoint(256, 256));
pd->addCheckbox("convertMono", QObject::tr("Convert to Mono"), false);
pd->addInteger("convertMonoThreshold", QObject::tr("Conversion Threshold"), 128, 0, 255);
pd->addCheckbox("preferBitmapFont", QObject::tr("Prefer Bitmap Font"), false);
pd->addCheckbox("noAntialiasFont", QObject::tr("No Antialias Font"), false);
pd->addCheckbox("trimLeft", QObject::tr("Trim Left Side"), true);
pd->addCheckbox("trimRight", QObject::tr("Trim Right Side"), true);
pd->addCheckbox("invertFont", QObject::tr("Invert the Font"), false);
return pd;
}
QImage FontConverter::generateImage(const QFont &font, QChar c, const QVariantMap &parameter) const
{
auto maximumSize = parameter["maximumSize"].toSize();
if (!maximumSize.isValid() || maximumSize.isNull()) {
maximumSize = QSize(8, 8);
}
const auto charOffset = parameter["charOffset"].toPoint();
QImage result(maximumSize, QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(1.0);
QPainter p;
p.begin(&result);
p.fillRect(result.rect(), Qt::white);
p.setRenderHint(QPainter::TextAntialiasing, false);
auto fontForDrawing = font;
int fontStyleStrategy = 0;
if (parameter["preferBitmapFont"].toBool()) {
fontStyleStrategy |= QFont::PreferBitmap;
}
if (parameter["noAntialiasFont"].toBool()) {
fontStyleStrategy |= QFont::NoAntialias;
}
fontStyleStrategy |= QFont::NoSubpixelAntialias;
fontForDrawing.setStyleStrategy(static_cast<QFont::StyleStrategy>(fontStyleStrategy));
QFontMetrics fontMetrics(fontForDrawing);
auto charBoundingRect = fontMetrics.boundingRect(c);
// Make sure the first pixel is in the bitmap, draw baseline at ascent.
QPoint targetPoint(-charBoundingRect.left(), fontMetrics.ascent());
targetPoint += charOffset;
p.setFont(fontForDrawing);
p.drawText(targetPoint, QString(c));
p.end();
// Postprocess the font.
const auto threshold = static_cast<unsigned int>(parameter["convertMonoThreshold"].toInt());
if (parameter["convertMono"].toBool()) {
for (int x = 0; x < result.width(); ++x) {
for (int y = 0; y < result.height(); ++y) {
const auto pixel = result.pixel(x, y);
if ((pixel & 0xff) <= threshold) {
result.setPixel(x, y, qRgb(0, 0, 0));
} else {
result.setPixel(x, y, qRgb(255, 255, 255));
}
}
}
}
// Trim the font.
const auto trimLeft = parameter["trimLeft"].toBool();
const auto trimRight = parameter["trimRight"].toBool();
if (trimLeft || trimRight) {
auto resultRect = result.rect();
int leftTrim = 0;
int rightTrim = 0;
if (trimLeft) {
for (int x = 0; x < result.width(); ++x) {
bool hasPixelSet = false;
for (int y = 0; y < result.height(); ++y) {
const auto pixel = result.pixel(x, y);
if ((pixel & 0xff) <= threshold) {
hasPixelSet = true;
break;
}
}
if (hasPixelSet) {
break;
}
leftTrim += 1;
}
}
if (trimRight) {
for (int x = result.width()-1; x >= 0; --x) {
bool hasPixelSet = false;
for (int y = 0; y < result.width(); ++y) {
const auto pixel = result.pixel(x, y);
if ((pixel & 0xff) <= threshold) {
hasPixelSet = true;
break;
}
}
if (hasPixelSet) {
break;
}
rightTrim += 1;
}
}
if ((resultRect.width() - leftTrim - rightTrim) <= 0) {
// The font image is empty, use one pixel line.
resultRect.setWidth(1);
} else {
resultRect = resultRect.marginsRemoved(QMargins(leftTrim, 0, rightTrim, 0));
}
if (resultRect != result.rect()) {
result = result.copy(resultRect);
}
}
// Invert the font.
if (parameter["invertFont"].toBool()) {
result.invertPixels();
}
return result;
}

58
FontConverter.hpp 100644
Wyświetl plik

@ -0,0 +1,58 @@
//
// (c)2021 by Lucky Resistor. https://luckyresistor.me/
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
#pragma once
#include "Converter.hpp"
/// The base class for all font converters.
///
class FontConverter : public Converter
{
public:
/// ctor
///
FontConverter(const QString &displayName);
public: // implement Converter
Mode mode() const override;
ParameterDefinitionPtr createParameterDefinition() const override;
public:
/// Create a bitmap for a given character.
///
/// This method should return the given character in exact the size and format it will
/// be converted as bitmap.
///
/// @param font The font for the character.
/// @param c The character to convert.
/// @param parameter The current map of parameters.
///
virtual QImage generateImage(const QFont &font, QChar c, const QVariantMap &parameter) const;
/// Generate the code from the given font.
///
/// The string with all characters to convert is passed as parameter 'characters'.
///
/// @param font The font to convert.
/// @param parameter A map with parameters passed to this converter.
/// @return The generated code.
///
virtual QString generateCode(const QFont &font, const QVariantMap &parameter) const = 0;
};

Wyświetl plik

@ -0,0 +1,52 @@
//
// (c)2021 by Lucky Resistor. https://luckyresistor.me/
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
#include "FontConverterFramebufMono.hpp"
FontConverterFramebufMono::FontConverterFramebufMono()
:
FontConverter("MicroPython Font Mono VLSB"),
MonoTools(UnitOrientation::Vertical, BitDirection::LSB, 8)
{
}
QSize FontConverterFramebufMono::generatedSize(const QSize &imageSize, const QVariantMap &parameter) const
{
return monoGeneratedSize(imageSize, parameter);
}
QString FontConverterFramebufMono::generateCode(const QFont&, const QVariantMap&) const
{
return "The generated format is not decided yet.";
}
LegendDataPtr FontConverterFramebufMono::legendData(OverlayMode mode) const
{
auto data = FontConverter::legendData(mode);
addMonoLegendData(data, mode);
return data;
}
void FontConverterFramebufMono::paintOverlay(OverlayPainter &p, OverlayMode mode, const QImage &image, const QVariantMap &parameter) const
{
FontConverter::paintOverlay(p, mode, image, parameter);
monoPaintOverlay(p, mode, image, parameter);
}

Wyświetl plik

@ -0,0 +1,39 @@
//
// (c)2021 by Lucky Resistor. https://luckyresistor.me/
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
#pragma once
#include "FontConverter.hpp"
#include "MonoTools.hpp"
/// A simple converter to create mono bitmap fonts.
///
class FontConverterFramebufMono : public FontConverter, public MonoTools
{
public:
/// ctor
///
FontConverterFramebufMono();
public: // implement FontConverter
QSize generatedSize(const QSize &imageSize, const QVariantMap &parameter) const override;
QString generateCode(const QFont &font, const QVariantMap &parameter) const override;
LegendDataPtr legendData(OverlayMode mode) const override;
void paintOverlay(OverlayPainter &p, OverlayMode mode, const QImage &image, const QVariantMap &parameter) const override;
};

Wyświetl plik

@ -17,10 +17,12 @@
#include "MainWindow.hpp"
#include "ApplicationController.hpp"
#include "BitmapPanel.hpp"
#include "ConverterFramebufMonoVLSB.hpp"
#include "ConverterFramebufMonoHLSB.hpp"
#include "ConverterFramebufMonoHMSB.hpp"
#include "FontConverterFramebufMono.hpp"
#include <QtCore/QSettings>
#include <QtGui/QAction>
@ -37,6 +39,11 @@
#include <QtWidgets/QPlainTextEdit>
#include <QtWidgets/QScrollArea>
#include <QtWidgets/QSplitter>
#include <QtWidgets/QFontComboBox>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QFormLayout>
#include <QtWidgets/QSpinBox>
#include <QtWidgets/QHBoxLayout>
#include <QtGui/QDesktopServices>
@ -47,6 +54,9 @@ MainWindow::MainWindow(QWidget *parent)
initializeUi();
initializeMenu();
loadSettings();
onFormatChanged();
onFontChanged();
onCharactersChanged();
}
@ -60,13 +70,16 @@ void MainWindow::initializeConverterList()
_converterList.append(new ConverterFramebufMonoVLSB());
_converterList.append(new ConverterFramebufMonoHMSB());
_converterList.append(new ConverterFramebufMonoHLSB());
_converterList.append(new FontConverterFramebufMono());
}
void MainWindow::initializeUi()
{
const int cFixedWidth = 400;
setMinimumSize(800, 600);
setWindowTitle(tr("Micropython Bitmap Tool - V%1 - Lucky Resistor").arg(qApp->applicationVersion()));
setWindowTitle(tr("MicroPython Bitmap Tool - V%1 - Lucky Resistor").arg(qApp->applicationVersion()));
auto centralWidget = new QWidget();
centralWidget->setObjectName("CentralWidget");
@ -83,15 +96,16 @@ void MainWindow::initializeUi()
settingsLayout->setSpacing(4);
auto logo = new QLabel();
logo->setFixedSize(300, 250);
logo->setFixedSize(cFixedWidth, 100);
logo->setPixmap(QPixmap(":/images/AppLogo.png"));
logo->setAlignment(Qt::AlignCenter);
settingsLayout->addWidget(logo);
auto versionLabel = new QLabel(tr("<b>Version %1</b>").arg(qApp->applicationVersion()));
versionLabel->setAlignment(Qt::AlignCenter);
settingsLayout->addWidget(versionLabel);
settingsLayout->addSpacing(16);
settingsLayout->addWidget(new QLabel(tr("Generated Format:")));
settingsLayout->addWidget(new QLabel(tr("<b>Generated Format:</b>")));
_formatSelector = new QComboBox();
for (auto converter : _converterList) {
_formatSelector->addItem(converter->displayName());
@ -99,20 +113,96 @@ void MainWindow::initializeUi()
_formatSelector->setCurrentIndex(0);
settingsLayout->addWidget(_formatSelector);
settingsLayout->addStretch();
settingsLayout->addWidget(new QLabel(tr("<b>Parameters:</b>")));
settingsLayout->addWidget(new QLabel(tr("<b>Loaded Bitmap Info:</b>")));
auto parameterScroll = new QScrollArea();
parameterScroll->setObjectName("ParameterScrollArea");
_parameterFrame = new QFrame();
_parameterFrame->setObjectName("ParameterFrame");
_parameterFrame->setFixedWidth(cFixedWidth);
_parameterLayout = new QFormLayout(_parameterFrame);
_parameterLayout->setContentsMargins(8, 8, 8, 8);
parameterScroll->setWidget(_parameterFrame);
parameterScroll->setWidgetResizable(true);
parameterScroll->setFixedWidth(cFixedWidth);
parameterScroll->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
//parameterScroll->setBackgroundRole(QPalette::Window);
settingsLayout->addWidget(parameterScroll);
_bitmapConverterFrame = new QFrame();
auto bitmapConverterLayout = new QVBoxLayout(_bitmapConverterFrame);
bitmapConverterLayout->setContentsMargins(0, 0, 0, 0);
bitmapConverterLayout->setSpacing(4);
settingsLayout->addWidget(_bitmapConverterFrame);
bitmapConverterLayout->addWidget(new QLabel(tr("<b>Loaded Bitmap Info:</b>")));
_bitmapInfo = new QLabel(tr("No Bitmap Loaded"));
_bitmapInfo->setObjectName("BitmapInfo");
_bitmapInfo->setMinimumHeight(200);
_bitmapInfo->setMinimumHeight(100);
_bitmapInfo->setAlignment(Qt::AlignTop|Qt::AlignLeft);
settingsLayout->addWidget(_bitmapInfo);
bitmapConverterLayout->addWidget(_bitmapInfo);
auto loadButton = new QPushButton();
loadButton->setText(tr("Load Bitmap"));
settingsLayout->addWidget(loadButton);
bitmapConverterLayout->addWidget(loadButton);
connect(loadButton, &QPushButton::clicked, this, &MainWindow::onLoadBitmap);
_fontConverterFrame = new QFrame();
auto fontConverterLayout = new QVBoxLayout(_fontConverterFrame);
fontConverterLayout->setContentsMargins(0, 0, 0, 0);
fontConverterLayout->setSpacing(4);
settingsLayout->addWidget(_fontConverterFrame);
fontConverterLayout->addWidget(new QLabel(tr("<b>Selected Font Info:</b>")));
_fontInfo = new QLabel(tr("No Font Seleccted"));
_fontInfo->setObjectName("FontInfo");
_fontInfo->setMinimumHeight(100);
_fontInfo->setAlignment(Qt::AlignTop|Qt::AlignLeft);
fontConverterLayout->addWidget(_fontInfo);
fontConverterLayout->addWidget(new QLabel(tr("<b>Selected Font:</b>")));
_fontSelector = new QFontComboBox();
fontConverterLayout->addWidget(_fontSelector);
auto fontDetailsLayout = new QHBoxLayout();
fontDetailsLayout->setContentsMargins(0, 0, 0, 0);
fontDetailsLayout->setSpacing(4);
fontConverterLayout->addLayout(fontDetailsLayout);
_fontWeightSelector = new QComboBox();
_fontWeightSelector->addItem(tr("Thin"), static_cast<int>(QFont::Thin));
_fontWeightSelector->addItem(tr("Extra Light"), static_cast<int>(QFont::ExtraLight));
_fontWeightSelector->addItem(tr("Light"), static_cast<int>(QFont::Light));
_fontWeightSelector->addItem(tr("Normal"), static_cast<int>(QFont::Normal));
_fontWeightSelector->addItem(tr("Medium"), static_cast<int>(QFont::Medium));
_fontWeightSelector->addItem(tr("Demi Bold"), static_cast<int>(QFont::DemiBold));
_fontWeightSelector->addItem(tr("Bold"), static_cast<int>(QFont::Bold));
_fontWeightSelector->addItem(tr("Extra Bold"), static_cast<int>(QFont::ExtraBold));
_fontWeightSelector->addItem(tr("Black"), static_cast<int>(QFont::Black));
_fontWeightSelector->setCurrentIndex(3);
_fontSizeSelector = new QSpinBox();
_fontSizeSelector->setRange(4, 200);
_fontSizeSelector->setValue(12);
_fontHinting = new QComboBox();
_fontHinting->addItem(tr("Default"), static_cast<int>(QFont::PreferDefaultHinting));
_fontHinting->addItem(tr("None"), static_cast<int>(QFont::PreferNoHinting));
_fontHinting->addItem(tr("Vertical"), static_cast<int>(QFont::PreferVerticalHinting));
_fontHinting->addItem(tr("Full"), static_cast<int>(QFont::PreferFullHinting));
_fontHinting->setCurrentIndex(0);
fontDetailsLayout->addWidget(new QLabel(tr("Size:")));
fontDetailsLayout->addWidget(_fontSizeSelector);
fontDetailsLayout->addWidget(new QLabel(tr("Weight:")));
fontDetailsLayout->addWidget(_fontWeightSelector);
fontDetailsLayout->addWidget(new QLabel(tr("Hinting:")));
fontDetailsLayout->addWidget(_fontHinting);
fontConverterLayout->addWidget(new QLabel(tr("Characters to convert:")));
_fontCharacters = new QPlainTextEdit();
_fontCharacters->setFixedHeight(100);
_fontCharacters->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
_fontCharacters->setWordWrapMode(QTextOption::WrapAnywhere);
_fontCharacters->setPlainText("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,:;!?()+-=\"'<>");
_fontCharacters->setReadOnly(true);
fontConverterLayout->addWidget(_fontCharacters);
connect(_fontCharacters, &QPlainTextEdit::textChanged, this, &MainWindow::onCharactersChanged);
auto previewSplittter = new QSplitter(Qt::Vertical);
previewSplittter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
previewSplittter->setChildrenCollapsible(false);
@ -133,15 +223,9 @@ void MainWindow::initializeUi()
_codePreview->setWordWrapMode(QTextOption::NoWrap);
codeLayout->addWidget(_codePreview);
#ifdef Q_OS_WIN32
_codePreview->setFont(QFont("Consolas", 12));
#else
#ifdef Q_OS_MAC
_codePreview->setFont(QFont("Menlo", 12));
#else
_codePreview->setFont(QFont("Lucida Console", 12));
#endif
#endif
const auto monospaceFont = gApp()->monospaceFont();
_codePreview->setFont(monospaceFont);
_fontCharacters->setFont(monospaceFont);
auto codeActions = new QFrame();
auto codeActionsLayout = new QHBoxLayout(codeActions);
@ -161,11 +245,12 @@ void MainWindow::initializeUi()
previewSplittter->setStretchFactor(0, 4);
previewSplittter->setStretchFactor(1, 1);
connect(_formatSelector, &QComboBox::currentIndexChanged, [=]{
_bitmapPanel->setConverter(selectedConverter());
updateBitmapInfo();
updateCode();
});
connect(_formatSelector, &QComboBox::currentIndexChanged, this, &MainWindow::onFormatChanged);
connect(_fontSelector, &QFontComboBox::currentFontChanged, this, &MainWindow::onFontChanged);
connect(_fontSizeSelector, &QSpinBox::valueChanged, this, &MainWindow::onFontChanged);
connect(_fontWeightSelector, &QComboBox::currentIndexChanged, this, &MainWindow::onFontChanged);
connect(_fontHinting, &QComboBox::currentIndexChanged, this, &MainWindow::onFontChanged);
connect(_bitmapPanel, &BitmapPanel::selectedCharacterChanged, this, &MainWindow::onSelectedCharacterChanged);
}
@ -229,6 +314,18 @@ void MainWindow::loadSettings()
restoreGeometry(settings.value("mainWindow.geometry").toByteArray());
restoreState(settings.value("mainWindow.state").toByteArray());
_formatSelector->setCurrentIndex(settings.value("format.index").toInt(0));
_currentFont = QFont(settings.value("font.family", "Arial").toString());
_currentFont.setPixelSize(settings.value("font.size", 12).toInt());
_currentFont.setWeight(static_cast<QFont::Weight>(settings.value("font.weight", 500).toInt()));
_fontSelector->setCurrentFont(_currentFont);
_fontSizeSelector->setValue(_currentFont.pixelSize());
const int index = _fontWeightSelector->findData(static_cast<int>(_currentFont.weight()));
if (index >= 0) {
_fontWeightSelector->setCurrentIndex(index);
} else {
_fontWeightSelector->setCurrentIndex(3); // normal
}
_fontHinting->setCurrentIndex(settings.value("font.hinting", 0).toInt());
}
@ -238,6 +335,10 @@ void MainWindow::saveSettings()
settings.setValue("mainWindow.geometry", saveGeometry());
settings.setValue("mainWindow.state", saveState());
settings.setValue("format.index", _formatSelector->currentIndex());
settings.setValue("font.family", _currentFont.family());
settings.setValue("font.size", _currentFont.pixelSize());
settings.setValue("font.weight", static_cast<int>(_currentFont.weight()));
settings.setValue("font.hinting", _fontHinting->currentIndex());
}
@ -251,42 +352,120 @@ Converter *MainWindow::selectedConverter() const
}
void MainWindow::updateBitmapInfo()
void MainWindow::updateInfoBox()
{
QString text;
QTextStream ts(&text);
ts << "<p>";
ts << "Bitmap Size: " << _currentImage.width() << "x" << _currentImage.height() << "<br>";
ts << "Bit Depth: " << _currentImage.depth() << "<br>";
ts << "Color Format: ";
switch (_currentImage.pixelFormat().colorModel()) {
case QPixelFormat::RGB: ts << "RGB"; break;
case QPixelFormat::BGR: ts << "BGR"; break;
case QPixelFormat::Indexed: ts << "Indexed"; break;
case QPixelFormat::Grayscale: ts << "Grayscale"; break;
case QPixelFormat::CMYK: ts << "CMYK"; break;
case QPixelFormat::HSL: ts << "HSL"; break;
case QPixelFormat::HSV: ts << "HSV"; break;
case QPixelFormat::YUV: ts << "YUV"; break;
case QPixelFormat::Alpha: ts << "Alpha"; break;
if (selectedConverter()->mode() == Converter::Mode::Bitmap) {
ts << "<p>";
ts << "Bitmap Size: " << _currentImage.width() << "x" << _currentImage.height() << "<br>";
ts << "Bit Depth: " << _currentImage.depth() << "<br>";
ts << "Color Format: ";
switch (_currentImage.pixelFormat().colorModel()) {
case QPixelFormat::RGB: ts << "RGB"; break;
case QPixelFormat::BGR: ts << "BGR"; break;
case QPixelFormat::Indexed: ts << "Indexed"; break;
case QPixelFormat::Grayscale: ts << "Grayscale"; break;
case QPixelFormat::CMYK: ts << "CMYK"; break;
case QPixelFormat::HSL: ts << "HSL"; break;
case QPixelFormat::HSV: ts << "HSV"; break;
case QPixelFormat::YUV: ts << "YUV"; break;
case QPixelFormat::Alpha: ts << "Alpha"; break;
}
ts << "<br>";
const auto gs = selectedConverter()->generatedSize(_currentImage.size(), createParameterMap());
ts << "Generated Size: " << gs.width() << "x" << gs.height() << "</p>";
_bitmapInfo->setText(text);
} else {
ts << "<p>";
ts << "Family: " << _currentFont.family() << "<br>";
ts << "Weight: ";
switch (_currentFont.weight()) {
case QFont::Thin: ts << tr("Thin"); break;
case QFont::ExtraLight: ts << tr("Extra Light"); break;
case QFont::Light: ts << tr("Light"); break;
case QFont::Normal: ts << tr("Normal"); break;
case QFont::Medium: ts << tr("Medium"); break;
case QFont::DemiBold: ts << tr("Demi Bold"); break;
case QFont::Bold: ts << tr("Bold"); break;
case QFont::ExtraBold: ts << tr("Extra Bold"); break;
case QFont::Black: ts << tr("Black"); break;
}
ts << "<br>";
ts << "Pixel Size: " << _currentFont.pixelSize() << "<br>";
ts << "</p>";
_fontInfo->setText(text);
}
ts << "<br>";
const auto gs = selectedConverter()->generatedSize(_currentImage.size());
ts << "Generated Size: " << gs.width() << "x" << gs.height() << "</p>";
_bitmapInfo->setText(text);
}
void MainWindow::updateCode()
{
QString code;
if (!_currentImage.isNull()) {
code = selectedConverter()->generateCode(_currentImage, QVariantMap());
if (selectedConverter()->mode() == Converter::Mode::Bitmap) {
auto bitmapConverter = static_cast<BitmapConverter*>(selectedConverter());
if (!_currentImage.isNull()) {
code = bitmapConverter->generateCode(_currentImage, QVariantMap());
}
} else {
auto fontConverter = static_cast<FontConverter*>(selectedConverter());
code = fontConverter->generateCode(_currentFont, createParameterMap());
}
_codePreview->setPlainText(code);
}
void MainWindow::updateParameters()
{
auto converter = selectedConverter();
if (converter == nullptr) {
return;
}
// Replace the current parameter definition.
_displayedParameters = converter->createParameterDefinition();
// Remove the previous UI
while (_parameterLayout->count() > 0) {
auto item = _parameterLayout->takeAt(0);
delete item;
}
for (auto w : _parameterFrame->findChildren<QWidget*>()) {
w->deleteLater();
}
// Build the new parameter UI.
if (_displayedParameters->parameterList().isEmpty()) {
auto label = new QLabel();
label->setText(tr("No Parameters"));
_parameterLayout->addWidget(label);
} else {
for (const auto &parameter : _displayedParameters->parameterList()) {
auto pw = parameterFactory.createWidget(parameter);
pw->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
pw->setObjectName(parameter->identifier());
_parameterLayout->addRow(parameter->label() + ":", pw);
connect(pw, &ParameterWidget::valueChanged, this, &MainWindow::onParameterChanged);
}
}
auto stretchItem = new QWidget();
stretchItem->setFixedWidth(10);
stretchItem->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
_parameterLayout->addRow(stretchItem);
}
QVariantMap MainWindow::createParameterMap() const
{
QVariantMap result;
result["characters"] = _fontCharacters->toPlainText();
result["font"] = _currentFont;
result["char"] = _bitmapPanel->selectedCharacter();
for (auto pw : _parameterFrame->findChildren<ParameterWidget*>()) {
result[pw->objectName()] = pw->value();
}
return result;
}
void MainWindow::onLoadBitmap()
{
QSettings settings;
@ -320,11 +499,78 @@ void MainWindow::onLoadBitmap()
_currentImage = newImage;
_bitmapPanel->setImage(newImage);
_bitmapPanel->setConverter(selectedConverter());
updateBitmapInfo();
updateInfoBox();
updateCode();
}
void MainWindow::onFormatChanged()
{
const auto converter = selectedConverter();
_bitmapPanel->setConverter(converter);
if (converter->mode() == Converter::Mode::Bitmap) {
_bitmapConverterFrame->setVisible(true);
_fontConverterFrame->setVisible(false);
} else {
_bitmapConverterFrame->setVisible(false);
_fontConverterFrame->setVisible(true);
}
updateParameters();
updateInfoBox();
updateCode();
_bitmapPanel->setParameter(createParameterMap());
onSelectedCharacterChanged(_bitmapPanel->selectedCharacter());
}
void MainWindow::onFontChanged()
{
_currentFont = _fontSelector->currentFont();
_currentFont.setWeight(static_cast<QFont::Weight>(_fontWeightSelector->currentData().toInt()));
_currentFont.setPixelSize(_fontSizeSelector->value());
const auto hintingPreferences = static_cast<QFont::HintingPreference>(_fontHinting->currentData().toInt());
_currentFont.setHintingPreference(hintingPreferences);
updateInfoBox();
updateCode();
_bitmapPanel->setParameter(createParameterMap());
onSelectedCharacterChanged(_bitmapPanel->selectedCharacter());
}
void MainWindow::onCharactersChanged()
{
auto characters = _fontCharacters->toPlainText();
_bitmapPanel->setCharacters(characters);
_bitmapPanel->setConverter(selectedConverter());
_bitmapPanel->setParameter(createParameterMap());
onSelectedCharacterChanged(_bitmapPanel->selectedCharacter());
}
void MainWindow::onSelectedCharacterChanged(QChar c)
{
auto converter = selectedConverter();
if (converter == nullptr) {
return;
}
if (converter->mode() == Converter::Mode::Font) {
auto fontConverter = static_cast<const FontConverter*>(converter);
auto newImage = fontConverter->generateImage(_currentFont, c, createParameterMap());
_currentImage = newImage;
_bitmapPanel->setImage(newImage);
_bitmapPanel->setConverter(selectedConverter());
_bitmapPanel->setParameter(createParameterMap());
}
}
void MainWindow::onParameterChanged()
{
_bitmapPanel->setParameter(createParameterMap());
onSelectedCharacterChanged(_bitmapPanel->selectedCharacter());
}
void MainWindow::closeEvent(QCloseEvent *event)
{
saveSettings();

Wyświetl plik

@ -17,6 +17,10 @@
#pragma once
#include "ParameterDefinition.hpp"
#include "ParameterFactory.hpp"
#include <QtCore/QVariantMap>
#include <QtWidgets/QMainWindow>
@ -25,6 +29,11 @@ class QPlainTextEdit;
class QComboBox;
class QLabel;
class BitmapPanel;
class QFrame;
class QFontComboBox;
class QLineEdit;
class QSpinBox;
class QFormLayout;
/// The main widnow
@ -64,26 +73,73 @@ private:
/// Update the bitmap info.
///
void updateBitmapInfo();
void updateInfoBox();
/// Update the code.
///
void updateCode();
/// Update the parameter frame for the current converter.
///
void updateParameters();
/// Get the current parameters
///
QVariantMap createParameterMap() const;
private Q_SLOTS:
/// Load a bitmap.
///
void onLoadBitmap();
/// After a new converter is selected.
///
void onFormatChanged();
/// After a new font is selected.
///
void onFontChanged();
/// After the character list has changed
///
void onCharactersChanged();
/// If the selected character changed
///
void onSelectedCharacterChanged(QChar c);
/// After a parameter has changed.
///
void onParameterChanged();
protected: // Implement QWidget
void closeEvent(QCloseEvent *event) override;
private:
ParameterFactory parameterFactory; ///< The parameter factory.
QComboBox *_formatSelector; ///< The format selector.
QPlainTextEdit *_codePreview; ///< The generated code.
BitmapPanel *_bitmapPanel; ///< The bitmap panel.
QImage _currentImage; ///< The current loaded image
QFrame *_parameterFrame; ///< The frame with the parameters.
QFormLayout *_parameterLayout; ///< The parameter layout.
ParameterDefinitionPtr _displayedParameters; ///< The currently displayed parameters.
QImage _currentImage; ///< The current loaded image for bitmap converters.
QFont _currentFont; ///< The currently selected font for font converters.
QFrame *_bitmapConverterFrame; ///< The frame for bitmap converters.
QLabel *_bitmapInfo; ///< The label with the image info.
QFrame *_fontConverterFrame; ///< The frame for font converters.
QLabel *_fontInfo; ///< The label with the font info.
QFontComboBox *_fontSelector; ///< The combo box to select a new font.
QComboBox *_fontWeightSelector; ///< The selector for the font weight.
QSpinBox *_fontSizeSelector; ///< The selector for the used font size.
QComboBox *_fontHinting; ///< The hinting settings for the font.
QPlainTextEdit *_fontCharacters; ///< The characters of the font to be converted.
QList<Converter*> _converterList; ///< A list of available converters.
};

Wyświetl plik

@ -3,6 +3,7 @@ QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17
CONFIG += sdk_no_version_check
TARGET = "MicroPythonBitmapTool"
@ -11,6 +12,8 @@ TARGET = "MicroPythonBitmapTool"
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
ApplicationController.cpp \
BitmapConverter.cpp \
BitmapPanel.cpp \
BitmapPreview.cpp \
Converter.cpp \
@ -19,13 +22,22 @@ SOURCES += \
ConverterFramebufMonoHLSB.cpp \
ConverterFramebufMonoHMSB.cpp \
ConverterFramebufMonoVLSB.cpp \
FontConverter.cpp \
FontConverterFramebufMono.cpp \
LegendData.cpp \
LegendEntry.cpp \
MonoTools.cpp \
OverlayPainter.cpp \
ParameterDefinition.cpp \
ParameterEntry.cpp \
ParameterFactory.cpp \
ParameterWidget.cpp \
main.cpp \
MainWindow.cpp
HEADERS += \
ApplicationController.hpp \
BitmapConverter.hpp \
BitmapPanel.hpp \
BitmapPreview.hpp \
Converter.hpp \
@ -34,11 +46,19 @@ HEADERS += \
ConverterFramebufMonoHLSB.hpp \
ConverterFramebufMonoHMSB.hpp \
ConverterFramebufMonoVLSB.hpp \
FontConverter.hpp \
FontConverterFramebufMono.hpp \
LegendData.hpp \
LegendEntry.hpp \
MainWindow.hpp \
MonoTools.hpp \
OverlayMode.hpp \
OverlayPainter.hpp
OverlayPainter.hpp \
ParameterDefinition.hpp \
ParameterEntry.hpp \
ParameterFactory.hpp \
ParameterType.hpp \
ParameterWidget.hpp
RESOURCES += \
data/data.qrc

134
MonoTools.cpp 100644
Wyświetl plik

@ -0,0 +1,134 @@
//
// (c)2021 by Lucky Resistor. https://luckyresistor.me/
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
#include "MonoTools.hpp"
MonoTools::MonoTools(MonoTools::UnitOrientation unitOrientation, MonoTools::BitDirection bitDirection, int unitSize)
: _unitOrientation(unitOrientation), _bitDirection(bitDirection), _unitSize(unitSize)
{
}
void MonoTools::addMonoLegendData(LegendDataPtr data, OverlayMode mode) const
{
if (mode == OverlayMode::BitAssigments) {
data->addEntry(colorBitAssignment1, colorBitAssignment2, QObject::tr("Bit Assignment"));
} else {
data->addEntry(colorPixelInterpretation, QObject::tr("Pixel Interpretation"));
}
}
QSize MonoTools::monoGeneratedSize(const QSize &imageSize, const QVariantMap&) const
{
if (_unitOrientation == UnitOrientation::Vertical) {
if ((imageSize.height() % _unitSize) != 0) {
return QSize(imageSize.width(), ((imageSize.height()/_unitSize)+1)*_unitSize);
} else {
return imageSize;
}
} else {
if ((imageSize.width() % _unitSize) != 0) {
return QSize(((imageSize.width()/_unitSize)+1)*_unitSize, imageSize.height());
} else {
return imageSize;
}
}
}
void MonoTools::monoPaintOverlay(OverlayPainter &p, OverlayMode mode, const QImage &image, const QVariantMap&) const
{
if (mode == OverlayMode::BitAssigments) {
const auto bitL = (_bitDirection == BitDirection::LSB ? "0" : QString::number(_unitSize-1));
const auto bitH = (_bitDirection == BitDirection::LSB ? QString::number(_unitSize-1) : "0");
bool colorFlag = false;
if (_unitOrientation == UnitOrientation::Vertical) {
for (int y = 0; y < p.imageSize().height(); y += _unitSize) {
for (int x = 0; x < p.imageSize().width(); ++x) {
QRect rect(x, y, 1, _unitSize);
if (p.arePixelUpdated(rect)) {
const auto color = colorFlag ? colorBitAssignment1 : colorBitAssignment2;
p.drawPixelOutline(rect, color, 1);
p.drawPixelText(QRect(x, y, 1, 1), color, bitL);
p.drawPixelText(QRect(x, y+_unitSize-1, 1, 1), color, bitH);
}
colorFlag = !colorFlag;
}
if (p.imageSize().width() % 2 == 0) {
colorFlag = !colorFlag;
}
}
} else {
for (int y = 0; y < p.imageSize().height(); ++y) {
for (int x = 0; x < p.imageSize().width(); x += _unitSize) {
QRect rect(x, y, _unitSize, 1);
if (p.arePixelUpdated(rect)) {
const auto color = colorFlag ? colorBitAssignment1 : colorBitAssignment2;
p.drawPixelOutline(rect, color, 1);
p.drawPixelText(QRect(x, y, 1, 1), color, bitH);
p.drawPixelText(QRect(x+_unitSize-1, y, 1, 1), color, bitL);
}
colorFlag = !colorFlag;
}
if ((p.imageSize().width()/_unitSize) % 2 == 0) {
colorFlag = !colorFlag;
}
}
}
} else if (mode == OverlayMode::PixelInterpretation) {
for (int y = 0; y < p.generatedSize().height(); ++y) {
for (int x = 0; x < p.generatedSize().width(); ++x) {
auto text = (getPixel(x, y, image) ? "1" : "0");
const QRect rect(x, y, 1, 1);
if (p.arePixelUpdated(rect)) {
p.drawPixelText(rect, colorPixelInterpretation, text);
}
}
}
}
}
bool MonoTools::getPixel(int x, int y, const QImage &image)
{
if (x < 0 || y < 0 || x >= image.width() || y >= image.height()) {
return false;
}
auto color = image.pixelColor(x, y);
float h, s, l, a;
color.getHslF(&h, &s, &l, &a);
if (a < 0.5) {
return false;
}
return l < 0.5;
}
uint32_t MonoTools::readUnit(int x, int y, int dx, int dy, int count, const QImage &image)
{
uint32_t result = 0;
for (int i = 0; i < count; ++i) {
result <<= 1;
if (getPixel(x, y, image)) {
result |= 0b1;
}
x += dx;
y += dy;
}
return result;
}

83
MonoTools.hpp 100644
Wyświetl plik

@ -0,0 +1,83 @@
//
// (c)2021 by Lucky Resistor. https://luckyresistor.me/
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
#pragma once
#include "LegendData.hpp"
#include "OverlayPainter.hpp"
#include "OverlayMode.hpp"
#include "ParameterDefinition.hpp"
#include <QtGui/QColor>
#include <QtGui/QImage>
class MonoTools
{
public:
enum class UnitOrientation {
Horizontal,
Vertical
};
enum class BitDirection {
LSB,
MSB
};
public:
/// ctor
///
MonoTools(UnitOrientation unitOrientation,
BitDirection bitDirection,
int unitSize);
/// Legend data for mono conversion
///
void addMonoLegendData(LegendDataPtr data, OverlayMode mode) const;
/// The generated size for mono conversion.
///
QSize monoGeneratedSize(const QSize &imageSize, const QVariantMap &parameter) const;
/// Paint overlay for mono data.
///
void monoPaintOverlay(OverlayPainter &p, OverlayMode mode, const QImage &image, const QVariantMap &parameter) const;
/// Interpret a single pixel.
///
/// Returns `false` if the pixel is out of bounds.
///
static bool getPixel(int x, int y, const QImage &image);
/// Read a single unit.
///
static uint32_t readUnit(int x, int y, int dx, int dy, int count, const QImage &image);
public:
/// The colors for the bit assignments
///
static constexpr QColor colorBitAssignment1 = QColor(240, 40, 40);
static constexpr QColor colorBitAssignment2 = QColor(240, 80, 80);
static constexpr QColor colorPixelInterpretation = QColor(240, 240, 40);
protected:
UnitOrientation _unitOrientation; ///< The unit orientatioon.
BitDirection _bitDirection; ///< The bit direction.
int _unitSize; ///< The number of bits per unit.
};

Wyświetl plik

@ -0,0 +1,81 @@
//
// (c)2021 by Lucky Resistor. https://luckyresistor.me/
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
#include "ParameterDefinition.hpp"
ParameterDefinitionPtr ParameterDefinition::create()
{
return ParameterDefinitionPtr(new ParameterDefinition());
}
void ParameterDefinition::addCheckbox(
const QString &identifier,
const QString &label,
bool defaultValue)
{
QVariantMap settings;
settings["default"] = defaultValue;
_parameterList.append(ParameterEntry::create(
ParameterType::Checkbox, identifier, label, settings));
}
void ParameterDefinition::addInteger(
const QString &identifier,
const QString &label,
int defaultValue,
int minimum,
int maximum)
{
QVariantMap settings;
settings["default"] = defaultValue;
settings["minimum"] = minimum;
settings["maximum"] = maximum;
_parameterList.append(ParameterEntry::create(ParameterType::IntegerValue, identifier, label, settings));
}
void ParameterDefinition::addIntegerSize(const QString &identifier, const QString &label, QSize defaultValue, QSize minimum, QSize maximum)
{
QVariantMap settings;
settings["default"] = defaultValue;
settings["minimum"] = minimum;
settings["maximum"] = maximum;
_parameterList.append(ParameterEntry::create(ParameterType::IntegerSize, identifier, label, settings));
}
void ParameterDefinition::addIntegerPosition(const QString &identifier, const QString &label, QPoint defaultValue, QPoint minimum, QPoint maximum)
{
QVariantMap settings;
settings["default"] = defaultValue;
settings["minimum"] = minimum;
settings["maximum"] = maximum;
_parameterList.append(ParameterEntry::create(ParameterType::IntegerPosition, identifier, label, settings));
}
const ParameterDefinition::ParameterList &ParameterDefinition::parameterList() const
{
return _parameterList;
}
ParameterDefinition::ParameterDefinition()
{
}

Wyświetl plik

@ -0,0 +1,73 @@
//
// (c)2021 by Lucky Resistor. https://luckyresistor.me/
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
#pragma once
#include "ParameterEntry.hpp"
#include <QtCore/QSharedPointer>
#include <QtCore/QSize>
#include <QtCore/QPoint>
class ParameterDefinition;
using ParameterDefinitionPtr = QSharedPointer<ParameterDefinition>;
using ParameterDefinitionPtrConst = QSharedPointer<const ParameterDefinition>;
/// The definition of parameters, returned by a converter.
///
class ParameterDefinition
{
public:
/// A parameter list.
///
using ParameterList = QList<ParameterEntryPtr>;
public:
/// Create a new parameter definition.
///
static ParameterDefinitionPtr create();
public:
/// Add a checkbox parameter.
///
void addCheckbox(const QString &identifier, const QString &label, bool defaultValue);
/// Add an integer parameter.
///
void addInteger(const QString &identifier, const QString &label, int defaultValue, int minimum, int maximum);
/// Add an integer size parameter.
///
void addIntegerSize(const QString &identifier, const QString &label, QSize defaultValue, QSize minimum, QSize maximum);
/// Add an integer position parameter.
///
void addIntegerPosition(const QString &identifier, const QString &label, QPoint defaultValue, QPoint minimum, QPoint maximum);
/// Get the parameter list.
///
const ParameterList& parameterList() const;
private:
ParameterDefinition();
private:
ParameterList _parameterList; ///< The list with all parameters.
};

65
ParameterEntry.cpp 100644
Wyświetl plik

@ -0,0 +1,65 @@
//
// (c)2021 by Lucky Resistor. https://luckyresistor.me/
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
#include "ParameterEntry.hpp"
ParameterEntryPtr ParameterEntry::create(
ParameterType type,
const QString &identifier,
const QString &label,
const QVariantMap &settings)
{
return ParameterEntryPtr(new ParameterEntry(type, identifier, label, settings));
}
ParameterType ParameterEntry::type() const
{
return _type;
}
QString ParameterEntry::identifier() const
{
return _identifier;
}
QString ParameterEntry::label() const
{
return _label;
}
QVariantMap ParameterEntry::settings() const
{
return _settings;
}
ParameterEntry::ParameterEntry(
ParameterType type,
const QString &identifier,
const QString &label,
const QVariantMap &settings)
:
_type(type),
_identifier(identifier),
_label(label),
_settings(settings)
{
}

74
ParameterEntry.hpp 100644
Wyświetl plik

@ -0,0 +1,74 @@
//
// (c)2021 by Lucky Resistor. https://luckyresistor.me/
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
#pragma once
#include "ParameterType.hpp"
#include <QtCore/QString>
#include <QtCore/QVariantMap>
#include <QtCore/QSharedPointer>
class ParameterEntry;
using ParameterEntryPtr = QSharedPointer<ParameterEntry>;
/// One entry in the parameter definition.
///
class ParameterEntry
{
public:
static ParameterEntryPtr create(
ParameterType type,
const QString &identifier,
const QString &label,
const QVariantMap &settings);
public:
/// The parameter type.
///
ParameterType type() const;
/// The identifier of the parameter.
///
QString identifier() const;
/// The label for the parameter.
///
QString label() const;
/// Settings for the parameter.
///
QVariantMap settings() const;
private:
/// Create a new raw paremeter entry.
///
ParameterEntry(
ParameterType type,
const QString &identifier,
const QString &label,
const QVariantMap &settings);
private:
ParameterType _type;
QString _identifier;
QString _label;
QVariantMap _settings;
};

Wyświetl plik

@ -0,0 +1,40 @@
//
// (c)2021 by Lucky Resistor. https://luckyresistor.me/
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
#include "ParameterFactory.hpp"
ParameterFactory::ParameterFactory()
{
}
ParameterWidget *ParameterFactory::createWidget(const ParameterEntryPtr &parameter) const
{
switch (parameter->type()) {
case ParameterType::Checkbox:
return new CheckBoxParameter(parameter);
case ParameterType::IntegerValue:
return new IntegerParameter(parameter);
case ParameterType::IntegerSize:
return new IntegerSizeParameter(parameter);
case ParameterType::IntegerPosition:
return new IntegerPositionParameter(parameter);
default:
break;
}
return nullptr;
}

Wyświetl plik

@ -0,0 +1,37 @@
//
// (c)2021 by Lucky Resistor. https://luckyresistor.me/
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
#pragma once
#include "ParameterWidget.hpp"
#include <QtWidgets/QWidget>
/// A factory to handle the generic parameters.
///
class ParameterFactory
{
public:
ParameterFactory();
public:
/// Create the widget for to edit a parameter.
///
ParameterWidget *createWidget(const ParameterEntryPtr &parameter) const;
};

28
ParameterType.hpp 100644
Wyświetl plik

@ -0,0 +1,28 @@
//
// (c)2021 by Lucky Resistor. https://luckyresistor.me/
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
#pragma once
/// The parameter type.
///
enum class ParameterType {
Checkbox, ///< A boolean parameter
IntegerValue, ///< An integer value.
IntegerSize, ///< A size value
IntegerPosition, ///< A position value.
};

173
ParameterWidget.cpp 100644
Wyświetl plik

@ -0,0 +1,173 @@
//
// (c)2021 by Lucky Resistor. https://luckyresistor.me/
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
#include "ParameterWidget.hpp"
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QSpinBox>
#include <QtWidgets/QCheckBox>
#include <QtWidgets/QLabel>
ParameterWidget::ParameterWidget(const ParameterEntryPtr &parameter, QWidget *parent)
: QWidget(parent), _currentValue(parameter->settings()["default"])
{
}
CheckBoxParameter::CheckBoxParameter(const ParameterEntryPtr &parameter, QWidget *parent)
: ParameterWidget(parameter, parent)
{
auto mainLayout = new QHBoxLayout(this);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(2);
_value = new QCheckBox();
mainLayout->addWidget(_value);
_value->setChecked(_currentValue.toBool());
connect(_value, &QCheckBox::toggled, this, &CheckBoxParameter::valueChanged);
}
QVariant CheckBoxParameter::value() const
{
return static_cast<bool>(_value->isChecked());
}
void CheckBoxParameter::setValue(const QVariant &value)
{
if (value != _currentValue) {
_currentValue = value;
_value->setChecked(value.toBool());
}
}
IntegerParameter::IntegerParameter(const ParameterEntryPtr &parameter, QWidget *parent)
: ParameterWidget(parameter, parent)
{
auto mainLayout = new QHBoxLayout(this);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(2);
_value = new QSpinBox();
_value->setRange(
parameter->settings()["minimum"].toInt(),
parameter->settings()["maximum"].toInt()
);
mainLayout->addWidget(_value);
_value->setValue(_currentValue.toInt());
connect(_value, &QSpinBox::valueChanged, this, &CheckBoxParameter::valueChanged);
}
QVariant IntegerParameter::value() const
{
return _value->value();
}
void IntegerParameter::setValue(const QVariant &value)
{
if (value != _currentValue) {
_value->setValue(value.toInt());
}
}
IntegerSizeParameter::IntegerSizeParameter(const ParameterEntryPtr &parameter, QWidget *parent)
: ParameterWidget(parameter, parent)
{
auto mainLayout = new QHBoxLayout(this);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(2);
mainLayout->addWidget(new QLabel(tr("W:")));
_a = new QSpinBox();
_a->setRange(
parameter->settings()["minimum"].toSize().width(),
parameter->settings()["maximum"].toSize().width()
);
mainLayout->addWidget(_a);
_a->setValue(_currentValue.toSize().width());
connect(_a, &QSpinBox::valueChanged, this, &CheckBoxParameter::valueChanged);
mainLayout->addWidget(new QLabel(tr("H:")));
_b = new QSpinBox();
_b->setRange(
parameter->settings()["minimum"].toSize().height(),
parameter->settings()["maximum"].toSize().height()
);
mainLayout->addWidget(_b);
_b->setValue(_currentValue.toSize().height());
connect(_b, &QSpinBox::valueChanged, this, &CheckBoxParameter::valueChanged);
}
QVariant IntegerSizeParameter::value() const
{
return QSize(_a->value(), _b->value());
}
void IntegerSizeParameter::setValue(const QVariant &value)
{
if (_currentValue != value) {
_a->setValue(value.toSize().width());
_b->setValue(value.toSize().height());
}
}
IntegerPositionParameter::IntegerPositionParameter(const ParameterEntryPtr &parameter, QWidget *parent)
: ParameterWidget(parameter, parent)
{
auto mainLayout = new QHBoxLayout(this);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(2);
mainLayout->addWidget(new QLabel(tr("X:")));
_a = new QSpinBox();
_a->setRange(
parameter->settings()["minimum"].toPoint().x(),
parameter->settings()["maximum"].toPoint().x()
);
mainLayout->addWidget(_a);
_a->setValue(_currentValue.toPoint().x());
connect(_a, &QSpinBox::valueChanged, this, &CheckBoxParameter::valueChanged);
mainLayout->addWidget(new QLabel(tr("Y:")));
_b = new QSpinBox();
_b->setRange(
parameter->settings()["minimum"].toPoint().y(),
parameter->settings()["maximum"].toPoint().y()
);
mainLayout->addWidget(_b);
_b->setValue(_currentValue.toPoint().y());
connect(_b, &QSpinBox::valueChanged, this, &CheckBoxParameter::valueChanged);
}
QVariant IntegerPositionParameter::value() const
{
return QPoint(_a->value(), _b->value());
}
void IntegerPositionParameter::setValue(const QVariant &value)
{
if (_currentValue != value) {
_a->setValue(value.toPoint().x());
_b->setValue(value.toPoint().y());
}
}

114
ParameterWidget.hpp 100644
Wyświetl plik

@ -0,0 +1,114 @@
//
// (c)2021 by Lucky Resistor. https://luckyresistor.me/
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
#pragma once
#include "ParameterEntry.hpp"
#include <QtWidgets/QWidget>
class QCheckBox;
class QSpinBox;
/// The base class of all parameter widgets.
///
class ParameterWidget : public QWidget
{
Q_OBJECT
public:
/// Create the widget.
///
ParameterWidget(const ParameterEntryPtr &parameter, QWidget *parent = nullptr);
/// Get the current value for the widget.
///
virtual QVariant value() const = 0;
/// Set the value for this widget.
///
virtual void setValue(const QVariant &value) = 0;
signals:
/// Emitted if the value for this parameter changes.
///
void valueChanged();
protected:
QVariant _currentValue; ///< Buffer to limit signals.
};
class CheckBoxParameter : public ParameterWidget
{
Q_OBJECT
public:
CheckBoxParameter(const ParameterEntryPtr &parameter, QWidget *parent = nullptr);
QVariant value() const override;
void setValue(const QVariant &value) override;
private:
QCheckBox *_value;
};
class IntegerParameter : public ParameterWidget
{
Q_OBJECT
public:
IntegerParameter(const ParameterEntryPtr &parameter, QWidget *parent = nullptr);
QVariant value() const override;
void setValue(const QVariant &value) override;
private:
QSpinBox *_value;
};
class IntegerSizeParameter : public ParameterWidget
{
Q_OBJECT
public:
IntegerSizeParameter(const ParameterEntryPtr &parameter, QWidget *parent = nullptr);
QVariant value() const override;
void setValue(const QVariant &value) override;
private:
QSpinBox *_a;
QSpinBox *_b;
};
class IntegerPositionParameter : public ParameterWidget
{
Q_OBJECT
public:
IntegerPositionParameter(const ParameterEntryPtr &parameter, QWidget *parent = nullptr);
QVariant value() const override;
void setValue(const QVariant &value) override;
private:
QSpinBox *_a;
QSpinBox *_b;
};

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 8.6 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 4.8 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 19 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 9.5 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 30 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 14 KiB

Wyświetl plik

@ -14,8 +14,16 @@
color: #f8f8f8;
}
#BitmapInfo {
#BitmapInfo, #FontInfo {
border: 1px solid rgba(0,0,0,0.25);
padding: 8px;
margin: 0px 0px 16px 0px;
}
#ParameterScrollArea {
border: none;
}
#ParameterFrame {
background-color: #616875;
}

Wyświetl plik

@ -14,16 +14,19 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
#include "MainWindow.hpp"
#include "ApplicationController.hpp"
#include <QtWidgets/QApplication>
#include <QtCore/QFile>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setApplicationName("MicroPython Bitmap Tool");
a.setApplicationVersion("1.0.1");
a.setApplicationVersion("1.2.0");
a.setApplicationDisplayName("MicroPython Bitmap Tool");
a.setOrganizationDomain("luckyresistor.me");
a.setOrganizationName("Lucky Resistoor");
@ -32,7 +35,6 @@ int main(int argc, char *argv[])
a.setStyleSheet(QString::fromUtf8(styleSheet.readAll()));
styleSheet.close();
MainWindow w;
w.show();
ApplicationController app;
return a.exec();
}