Draw QImage on top of another with QPainter?
-
Hi, I'm trying to make an image drawing program with layer support and I ran into an issue where at runtime, creating a second layer glitches the way how the image is displayed and saved. The attached image was made by opening the program, creating a new layer and saving. The image should then just be a white square.
Here is my code (the QImage pointer is called pixmap because that's what it originally was):
main.cpp
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }
extern.h
#pragma once #include <QColor> #include <vector> extern QColor color; extern QColor canvasColor; extern bool shouldCreateNewImage; extern int w; extern int h; extern bool shouldCreateNewLayer; extern std::vector<int> layerAlpha; extern unsigned long layerIdx;
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QMouseEvent> #include <cstdint> QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); static inline QImage* pixmap; static inline std::vector<QImage*> layers; protected: void mousePressEvent(QMouseEvent* event); void mouseMoveEvent(QMouseEvent* event); void paintEvent(QPaintEvent* event); private slots: void on_actionBrush_size_triggered(); void on_actionBrush_size_down_triggered(); void on_actionColor_triggered(); void on_actionNew_triggered(); void on_actionOpen_triggered(); void on_actionSave_triggered(); void on_actionNew_Layer_triggered(); void on_actionLayer_up_triggered(); void on_actionLayer_down_triggered(); void on_actionSet_current_layer_opacity_triggered(); void on_actionToggle_current_layer_visibility_triggered(); private: Ui::MainWindow *ui; uint8_t brushSize; QPixmap p; }; #endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" #include "colorpicker.h" #include "newimagewindow.h" #include "newlayerdialog.h" #include <iostream> #include <QScrollBar> #include <QColor> #include <QFileDialog> #include <cstdlib> #include <ctime> #include <string> #include <QPainter> #include <QThread> #include "opacitychangedialog.h" QColor color; QColor canvasColor; bool shouldCreateNewImage; bool shouldOpenNewImage; int w; int h; unsigned long layerIdx; bool shouldCreateNewLayer; std::vector<int> layerAlpha; std::vector<bool> layerIsVisible; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { layerAlpha.push_back(255); layerIsVisible.push_back(true); shouldCreateNewLayer = false; shouldOpenNewImage = false; layerIdx = 0; setMouseTracking(false); pixmap = new QImage(":/templates/blank.png"); p = QPixmap(256, 256); p.fill(); layers.push_back(pixmap); ui->setupUi(this); srand(time(0)); ui->scrollAreaWidgetContents->setStyleSheet("background-color: rgb(127, 127, 127);"); color = QColor(0, 0, 0, 255); canvasColor = QColor(255, 255, 255, 255); brushSize = 1; shouldCreateNewImage = false; w = 256; h = 256; } MainWindow::~MainWindow() { delete ui; } void MainWindow::mousePressEvent(QMouseEvent* event) { QImage* img = layers[layerIdx]; QImage alpha = img->alphaChannel(); if (event->x() >= ui->label->width() / 2 - img->width() / 2 + 18 + ui->horizontalSpacer->geometry().width() && event->x() < ui->label->width() / 2 - img->width() / 2 + 18 + img->width() + ui->horizontalSpacer->geometry().width() - ui->scrollArea->horizontalScrollBar()->value() && event->y() >= ui->label->height() / 2 - img->height() / 2 + 38 + ui->verticalSpacer->geometry().height() && event->y() < ui->label->height() / 2 - img->height() / 2 + 38 + ui->verticalSpacer->geometry().height() + img->height() - ui->scrollArea->verticalScrollBar()->value()) { if (brushSize != 1) { for (int i = brushSize / 2 * -1; i < brushSize / 2; i++) { for (int j = brushSize / 2 * -1; j < brushSize / 2; j++) { if (event->x() - (ui->label->width() / 2 - img->width() / 2 + 18 + ui->scrollArea->horizontalScrollBar()->value() + i >= 0 && event->x() - (ui->label->width() / 2 - img->width() / 2 + 18 + ui->scrollArea->horizontalScrollBar()->value() + i < img->width())) && event->y() - (ui->label->height() / 2 - img->height() / 2 + 38 + ui->verticalSpacer->geometry().height() + ui->scrollArea->verticalScrollBar()->value() + j >= 0 && event->y() - (ui->label->height() / 2 - img->height() / 2 + 38 + ui->verticalSpacer->geometry().height() + ui->scrollArea->verticalScrollBar()->value() + j < img->height()))) { img->setPixelColor(event->x() - (ui->label->width() / 2 - img->width() / 2 + 18 + (ui->scrollArea->horizontalScrollBar()->value() * -1) + i + ui->horizontalSpacer->geometry().width()), event->y() - (ui->label->height() / 2 - img->height() / 2 + 38 + (ui->scrollArea->verticalScrollBar()->value() * -1) + j + ui->verticalSpacer->geometry().height()), color); img->setPixelColor(event->x() - (ui->label->width() / 2 - img->width() / 2 + 18 + (ui->scrollArea->horizontalScrollBar()->value() * -1) + i + ui->horizontalSpacer->geometry().width()), event->y() - (ui->label->height() / 2 - img->height() / 2 + 38 + (ui->scrollArea->verticalScrollBar()->value() * -1) + j + ui->verticalSpacer->geometry().height()), color); alpha.setPixel(event->x() - (ui->label->width() / 2 - img->width() / 2 + 18 + (ui->scrollArea->horizontalScrollBar()->value() * -1) + i + ui->horizontalSpacer->geometry().width()), event->y() - (ui->label->height() / 2 - img->height() / 2 + 38 + (ui->scrollArea->verticalScrollBar()->value() * -1) + j + ui->verticalSpacer->geometry().height()), color.alpha()); alpha.setPixel(event->x() - (ui->label->width() / 2 - img->width() / 2 + 18 + (ui->scrollArea->horizontalScrollBar()->value() * -1) + i + ui->horizontalSpacer->geometry().width()), event->y() - (ui->label->height() / 2 - img->height() / 2 + 38 + (ui->scrollArea->verticalScrollBar()->value() * -1) + j + ui->verticalSpacer->geometry().height()), color.alpha()); } } } } else { img->setPixelColor(event->x() - (ui->label->width() / 2 - img->width() / 2 + 18 + (ui->scrollArea->horizontalScrollBar()->value() * -1) + ui->horizontalSpacer->geometry().width()), event->y() - (ui->label->height() / 2 - img->height() / 2 + 38 + (ui->scrollArea->verticalScrollBar()->value() * -1) + ui->verticalSpacer->geometry().height()), color); alpha.setPixel(event->x() - (ui->label->width() / 2 - img->width() / 2 + 18 + (ui->scrollArea->horizontalScrollBar()->value() * -1) + ui->horizontalSpacer->geometry().width()), event->y() - (ui->label->height() / 2 - img->height() / 2 + 38 + (ui->scrollArea->verticalScrollBar()->value() * -1) + ui->verticalSpacer->geometry().height()), color.alpha()); } img->setAlphaChannel(alpha); layers[layerIdx] = new QImage(*img); } QWidget::mousePressEvent(event); } void MainWindow::mouseMoveEvent(QMouseEvent* event) { QImage* img = layers[layerIdx]; QImage alpha = img->alphaChannel(); if (event->x() >= ui->label->width() / 2 - img->width() / 2 + 18 + ui->horizontalSpacer->geometry().width() && event->x() < ui->label->width() / 2 - img->width() / 2 + 18 + img->width() - ui->scrollArea->horizontalScrollBar()->value() + ui->horizontalSpacer->geometry().width() && event->y() >= ui->label->height() / 2 - img->height() / 2 + 38 + ui->verticalSpacer->geometry().height() && event->y() < ui->label->height() / 2 - img->height() / 2 + 38 + ui->verticalSpacer->geometry().height() + img->height() - ui->scrollArea->verticalScrollBar()->value()) { if (brushSize != 1) { for (int i = brushSize / 2 * -1; i < brushSize / 2; i++) { for (int j = brushSize / 2 * -1; j < brushSize / 2; j++) { if (event->x() - (ui->label->width() / 2 - img->width() / 2 + 18 + ui->scrollArea->horizontalScrollBar()->value() + i >= 0 && event->x() - (ui->label->width() / 2 - img->width() / 2 + 18 + ui->scrollArea->horizontalScrollBar()->value() + i < img->width())) && event->y() - (ui->label->height() / 2 - img->height() / 2 + 38 + ui->verticalSpacer->geometry().height() + ui->scrollArea->verticalScrollBar()->value() + j >= 0 && event->y() - (ui->label->height() / 2 - img->height() / 2 + 38 + ui->verticalSpacer->geometry().height() + ui->scrollArea->verticalScrollBar()->value() + j < img->height()))) { img->setPixelColor(event->x() - (ui->label->width() / 2 - img->width() / 2 + 18 + (ui->scrollArea->horizontalScrollBar()->value() * -1) + i + ui->horizontalSpacer->geometry().width()), event->y() - (ui->label->height() / 2 - img->height() / 2 + 38 + (ui->scrollArea->verticalScrollBar()->value() * -1) + j + ui->verticalSpacer->geometry().height()), color); img->setPixelColor(event->x() - (ui->label->width() / 2 - img->width() / 2 + 18 + (ui->scrollArea->horizontalScrollBar()->value() * -1) + i + ui->horizontalSpacer->geometry().width()), event->y() - (ui->label->height() / 2 - img->height() / 2 + 38 + (ui->scrollArea->verticalScrollBar()->value() * -1) + j + ui->verticalSpacer->geometry().height()), color); alpha.setPixel(event->x() - (ui->label->width() / 2 - img->width() / 2 + 18 + (ui->scrollArea->horizontalScrollBar()->value() * -1) + i + ui->horizontalSpacer->geometry().width()), event->y() - (ui->label->height() / 2 - img->height() / 2 + 38 + (ui->scrollArea->verticalScrollBar()->value() * -1) + j + ui->verticalSpacer->geometry().height()), color.alpha()); alpha.setPixel(event->x() - (ui->label->width() / 2 - img->width() / 2 + 18 + (ui->scrollArea->horizontalScrollBar()->value() * -1) + i + ui->horizontalSpacer->geometry().width()), event->y() - (ui->label->height() / 2 - img->height() / 2 + 38 + (ui->scrollArea->verticalScrollBar()->value() * -1) + j + ui->verticalSpacer->geometry().height()), color.alpha()); } } } } else { img->setPixelColor(event->x() - (ui->label->width() / 2 - img->width() / 2 + 18 + (ui->scrollArea->horizontalScrollBar()->value() * -1) + ui->horizontalSpacer->geometry().width()), event->y() - (ui->label->height() / 2 - img->height() / 2 + 38 + (ui->scrollArea->verticalScrollBar()->value() * -1) + ui->verticalSpacer->geometry().height()), color); alpha.setPixel(event->x() - (ui->label->width() / 2 - img->width() / 2 + 18 + (ui->scrollArea->horizontalScrollBar()->value() * -1) + ui->horizontalSpacer->geometry().width()), event->y() - (ui->label->height() / 2 - img->height() / 2 + 38 + (ui->scrollArea->verticalScrollBar()->value() * -1) + ui->verticalSpacer->geometry().height()), color.alpha()); } layers[layerIdx] = new QImage(*img); } QWidget::mouseMoveEvent(event); } void MainWindow::on_actionBrush_size_triggered() { if (brushSize != 255) { brushSize++; } } void MainWindow::on_actionBrush_size_down_triggered() { if (brushSize != 1) { brushSize--; } } void MainWindow::on_actionColor_triggered() { ColorPicker* win = new ColorPicker; win->show(); } void MainWindow::on_actionNew_triggered() { NewImageWindow* win = new NewImageWindow; win->show(); } void MainWindow::paintEvent(QPaintEvent* event) { if (shouldCreateNewImage) { pixmap->fill(canvasColor); p.fill(canvasColor); p = p.scaled(w, h); QImage temp = pixmap->scaled(w, h); pixmap = new QImage(temp); layers.clear(); layers.push_back(pixmap); layerIsVisible.clear(); layerIsVisible.push_back(true); layerAlpha.clear(); layerAlpha.push_back(255); shouldCreateNewImage = false; pixmap = layers[0]; } if (shouldCreateNewLayer) { QImage* addedLayer = new QImage(w, h, layers[0]->format()); layers.insert(layers.begin() + layerIdx + 1, addedLayer); layerIsVisible.insert(layerIsVisible.begin() + layerIdx + 1, true); } if (shouldOpenNewImage) { p = QPixmap(layers[0]->size()); p.fill(); shouldOpenNewImage = false; } QPainter painter(&p); painter.setClipping(true); for (unsigned int i = 0; i < layers.size(); i++) { if (layerIsVisible[i]) { painter.setOpacity((float)(layerAlpha[i]) / 255.0f); painter.drawImage(layers[i]->rect(), *(layers[i])); } } QWidget::paintEvent(event); ui->label->setPixmap(p); update(); } void MainWindow::on_actionOpen_triggered() { QFileDialog* d = new QFileDialog(this, "Open an image", "", tr("Images (*.jpg *.png *.bmp)")); d->setOptions(QFileDialog::DontUseNativeDialog); d->setAcceptMode(QFileDialog::AcceptOpen); if (d->exec()) { QImage img(d->selectedFiles()[0]); layers.clear(); layers.push_back(&img); layerAlpha.clear(); layerAlpha.push_back(255); layerIsVisible.clear(); layerIsVisible.push_back(true); shouldOpenNewImage = true; } } void MainWindow::on_actionSave_triggered() { QFileDialog* d = new QFileDialog(this, "Open an image", "", tr("Images (*.jpg *.png *.bmp)")); d->setOptions(QFileDialog::DontUseNativeDialog); d->setAcceptMode(QFileDialog::AcceptSave); if (d->exec()) { p.save(d->selectedFiles()[0]); } } void MainWindow::on_actionNew_Layer_triggered() { NewLayerDialog* win = new NewLayerDialog; win->show(); } void MainWindow::on_actionLayer_up_triggered() { if (layerIdx < layers.size() - 1) { layerIdx++; } } void MainWindow::on_actionLayer_down_triggered() { if (layerIdx > 0) { layerIdx--; } } void MainWindow::on_actionSet_current_layer_opacity_triggered() { OpacityChangeDialog* win = new OpacityChangeDialog; win->show(); } void MainWindow::on_actionToggle_current_layer_visibility_triggered() { layerIsVisible[layerIdx] = !layerIsVisible[layerIdx]; }
colorpicker.h
#ifndef COLORPICKER_H #define COLORPICKER_H #include <QDialog> #include <QAbstractButton> namespace Ui { class ColorPicker; } class ColorPicker : public QDialog { Q_OBJECT public: explicit ColorPicker(QWidget *parent = nullptr); ~ColorPicker(); protected: void paintEvent(QPaintEvent* event); private slots: void on_buttonBox_clicked(QAbstractButton *button); private: Ui::ColorPicker *ui; }; #endif // COLORPICKER_H
colorpicker.cpp
#include "colorpicker.h" #include "ui_colorpicker.h" #include "extern.h" ColorPicker::ColorPicker(QWidget *parent) : QDialog(parent), ui(new Ui::ColorPicker) { ui->setupUi(this); } ColorPicker::~ColorPicker() { delete ui; } void ColorPicker::paintEvent(QPaintEvent* event) { ui->label_5->setStyleSheet("background-color: rgba(" + QString::fromStdString(std::to_string(ui->spinBox->value())) + ", " + QString::fromStdString(std::to_string(ui->spinBox_2->value())) + ", " + QString::fromStdString(std::to_string(ui->spinBox_3->value())) + ", 255);"); QWidget::paintEvent(event); update(); } void ColorPicker::on_buttonBox_clicked(QAbstractButton *button) { switch (ui->buttonBox->standardButton(button)) { case QDialogButtonBox::Ok: color = QColor(ui->spinBox->value(), ui->spinBox_2->value(), ui->spinBox_3->value(), 255); close(); break; case QDialogButtonBox::Cancel: close(); break; } }
newimagewindow.h
#ifndef NEWIMAGEWINDOW_H #define NEWIMAGEWINDOW_H #include <QDialog> #include <QAbstractButton> namespace Ui { class NewImageWindow; } class NewImageWindow : public QDialog { Q_OBJECT public: explicit NewImageWindow(QWidget *parent = nullptr); ~NewImageWindow(); private slots: void on_buttonBox_clicked(QAbstractButton *button); private: Ui::NewImageWindow *ui; }; #endif // NEWIMAGEWINDOW_H
newimagewindow.cpp
#include "newimagewindow.h" #include "ui_newimagewindow.h" #include "mainwindow.h" #include "extern.h" NewImageWindow::NewImageWindow(QWidget *parent) : QDialog(parent), ui(new Ui::NewImageWindow) { ui->setupUi(this); } NewImageWindow::~NewImageWindow() { delete ui; } void NewImageWindow::on_buttonBox_clicked(QAbstractButton *button) { switch (ui->buttonBox->standardButton(button)) { case QDialogButtonBox::Ok: MainWindow::pixmap = new QImage(":/templates/blank.png"); shouldCreateNewImage = true; w = ui->spinBox->value(); h = ui->spinBox_2->value(); canvasColor = QColor(ui->spinBox_3->value(), ui->spinBox_4->value(), ui->spinBox_5->value(), 255); close(); break; case QDialogButtonBox::Cancel: close(); break; } }
newlayerdialog.h
#ifndef NEWLAYERDIALOG_H #define NEWLAYERDIALOG_H #include <QDialog> #include <QAbstractButton> namespace Ui { class NewLayerDialog; } class NewLayerDialog : public QDialog { Q_OBJECT public: explicit NewLayerDialog(QWidget *parent = nullptr); ~NewLayerDialog(); private slots: void on_horizontalSlider_valueChanged(int value); void on_buttonBox_clicked(QAbstractButton *button); private: Ui::NewLayerDialog *ui; }; #endif // NEWLAYERDIALOG_H
newlayerdialog.cpp
#include "newlayerdialog.h" #include "ui_newlayerdialog.h" #include "extern.h" NewLayerDialog::NewLayerDialog(QWidget *parent) : QDialog(parent), ui(new Ui::NewLayerDialog) { ui->setupUi(this); } NewLayerDialog::~NewLayerDialog() { delete ui; } void NewLayerDialog::on_horizontalSlider_valueChanged(int value) { ui->label_2->setText(QString::fromStdString(std::to_string(value))); } void NewLayerDialog::on_buttonBox_clicked(QAbstractButton *button) { switch (ui->buttonBox->standardButton(button)) { case QDialogButtonBox::Ok: layerAlpha.insert(layerAlpha.begin() + layerIdx + 1, ui->horizontalSlider->value()); shouldCreateNewLayer = true; close(); break; case QDialogButtonBox::Cancel: close(); break; } }
opacitychangedialog.h
#ifndef OPACITYCHANGEDIALOG_H #define OPACITYCHANGEDIALOG_H #include <QDialog> #include <QAbstractButton> namespace Ui { class OpacityChangeDialog; } class OpacityChangeDialog : public QDialog { Q_OBJECT public: explicit OpacityChangeDialog(QWidget *parent = nullptr); ~OpacityChangeDialog(); private slots: void on_horizontalSlider_valueChanged(int value); void on_buttonBox_clicked(QAbstractButton *button); private: Ui::OpacityChangeDialog *ui; }; #endif // OPACITYCHANGEDIALOG_H
opacitychangedialog.cpp
#include "opacitychangedialog.h" #include "ui_opacitychangedialog.h" #include "extern.h" OpacityChangeDialog::OpacityChangeDialog(QWidget *parent) : QDialog(parent), ui(new Ui::OpacityChangeDialog) { ui->setupUi(this); ui->label_2->setText(QString::fromStdString(std::to_string(layerAlpha[layerIdx]))); ui->horizontalSlider->setValue(layerAlpha[layerIdx]); } OpacityChangeDialog::~OpacityChangeDialog() { delete ui; } void OpacityChangeDialog::on_horizontalSlider_valueChanged(int value) { ui->label_2->setText(QString::fromStdString(std::to_string(value))); } void OpacityChangeDialog::on_buttonBox_clicked(QAbstractButton *button) { switch (ui->buttonBox->standardButton(button)) { case QDialogButtonBox::Ok: layerAlpha[layerIdx] = ui->horizontalSlider->value(); close(); break; case QDialogButtonBox::Cancel: close(); break; } }
-
@ChrisW67 said in Draw QImage on top of another with QPainter?:
The best I can tell from the tangled code, when you create a new layer QImage you do not fill it. It therefore contains whatever rubbish was in memory and you paint with that.
Thanks! Problem solved!