From 0469df5f1dcec4dd0184a6e403619d1c66946761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C4=8Dma?= <58054719+0xMartin@users.noreply.github.com> Date: Mon, 12 Feb 2024 20:47:59 +0100 Subject: [PATCH] BMP strider calculater low range bug fixed, improved bmp image load/save, import image function --- BMPEditor/Base/bmpio.cpp | 30 +++++++++++------- BMPEditor/Base/bmpio.h | 2 +- BMPEditor/Base/formatter.cpp | 4 +-- BMPEditor/Base/image.cpp | 43 +++++++++++++++++++++++++ BMPEditor/Base/image.h | 7 +++++ BMPEditor/Editor/imageinfopanel.cpp | 5 +-- BMPEditor/Image/bmpimage.cpp | 39 +++++++++++++++++++++++ BMPEditor/Image/bmpimage.h | 9 ++++++ BMPEditor/Struct/bmpstruct.cpp | 2 +- BMPEditor/about.ui | 2 +- BMPEditor/mainwindow.cpp | 49 ++++++++++++++++++++++++++--- BMPEditor/mainwindow.h | 5 +-- BMPEditor/mainwindow.ui | 15 +++++++++ 13 files changed, 186 insertions(+), 26 deletions(-) diff --git a/BMPEditor/Base/bmpio.cpp b/BMPEditor/Base/bmpio.cpp index 58ad9ae..ed81f4f 100644 --- a/BMPEditor/Base/bmpio.cpp +++ b/BMPEditor/Base/bmpio.cpp @@ -1,7 +1,6 @@ #include "bmpio.h" -#include -#include +#include #include #include "../Base/error.h" @@ -20,8 +19,9 @@ int BMP_IO_loadBMPImage(const QString &path, } // soubor ze ktereho bude nacitan BMP obrazke (binarni cteni) - std::ifstream file(path.toStdString(), std::ios::binary | std::ios::in); - if (!file.is_open()) { + //std::ifstream file(path.toStdString(), std::ios::binary); + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) { return ERR_FILE_OPEN; } qDebug() << "BMP LOAD - file opened"; @@ -70,11 +70,11 @@ int BMP_IO_loadBMPImage(const QString &path, qDebug() << "BMP LOAD - image pixel array allocated (" << pixArrSiz << ")"; // vypocet stride (musi byt dorovnany na 32b, posledni bity jsou 0) - uint16_t stride = BMP_IO_calculateStride(infoHeader.bitCount, infoHeader.width); + uint32_t stride = BMP_IO_calculateStride(infoHeader.bitCount, infoHeader.width); qDebug() << "BMP LOAD - stride size: " << stride; // nacteni vsech pixelu obrazku z data bufferu - file.seekg(fileHeader.offset); + file.seek(fileHeader.offset); uint32_t index = 0; uint32_t offset = 0; RGBQUAD_t color; @@ -138,8 +138,9 @@ int BMP_IO_saveBMPImage(const QString &path, qDebug() << "BMP SAVE - header validated"; // ovevre soubor pro zapis dat - std::ofstream file(path.toStdString(), std::ios::binary); - if (!file.is_open()) { + //std::ofstream file(path.toStdString(), std::ios::binary); + QFile file(path); + if (!file.open(QIODevice::WriteOnly)) { return ERR_FILE_OPEN; } qDebug() << "BMP SAVE - file opened"; @@ -159,7 +160,7 @@ int BMP_IO_saveBMPImage(const QString &path, } // vypocet stride - uint16_t stride = BMP_IO_calculateStride(infoHeader.bitCount, infoHeader.width); + uint32_t stride = BMP_IO_calculateStride(infoHeader.bitCount, infoHeader.width); qDebug() << "BMP SAVE - stride size: " << stride; // zapis dat obrazku (inverzni zpusob jak u cteni) @@ -220,8 +221,13 @@ int BMP_IO_saveBMPImage(const QString &path, delete[] buff; qDebug() << "BMP image data writing done (" << infoHeader.width << "; " << infoHeader.height << ")"; + // flush + file.flush(); + // overeni zda data byli spravne zapsany - if (!file.good()) { + if (file.error() != QFile::NoError) { + qDebug() << "BMP write to file error:" << file.errorString(); + file.close(); return ERR_FILE_WRITE; } @@ -230,9 +236,9 @@ int BMP_IO_saveBMPImage(const QString &path, return STATUS_OK; } -uint16_t BMP_IO_calculateStride(uint8_t bitCount, uint16_t width) +uint32_t BMP_IO_calculateStride(uint8_t bitCount, uint16_t width) { - uint16_t stride = bitCount * width; + uint32_t stride = bitCount * width; if (stride % 32 != 0) stride = (stride | 31) + 1; stride /= 8; diff --git a/BMPEditor/Base/bmpio.h b/BMPEditor/Base/bmpio.h index b82a5b9..91f0b13 100644 --- a/BMPEditor/Base/bmpio.h +++ b/BMPEditor/Base/bmpio.h @@ -41,6 +41,6 @@ extern int BMP_IO_saveBMPImage(const QString & path, * @param width - Sirka obrazku v pixelech * @return Velikost stride */ -extern uint16_t BMP_IO_calculateStride(uint8_t bitCount, uint16_t width); +extern uint32_t BMP_IO_calculateStride(uint8_t bitCount, uint16_t width); #endif // BMPIO_H diff --git a/BMPEditor/Base/formatter.cpp b/BMPEditor/Base/formatter.cpp index 9e0b804..05cc6a4 100644 --- a/BMPEditor/Base/formatter.cpp +++ b/BMPEditor/Base/formatter.cpp @@ -61,7 +61,7 @@ static int FORMATTER_formatToBMPWithPalette(Image *src, Image **dst, uint8_t bit bmpOut->bitDepth = bitCount; // vypocet stride - uint16_t stride = BMP_IO_calculateStride(bmpOut->bitDepth, bmpOut->width); + uint32_t stride = BMP_IO_calculateStride(bmpOut->bitDepth, bmpOut->width); // number of colors uint16_t paletteSize = 1 << bitCount; @@ -150,7 +150,7 @@ int FORMATTER_formatToBMP24(Image *src, Image **dst) bmp24->bitDepth = 24; // vypocet stride - uint16_t stride = BMP_IO_calculateStride(bmp24->bitDepth, bmp24->width); + uint32_t stride = BMP_IO_calculateStride(bmp24->bitDepth, bmp24->width); // incializace BMP file header bmp24->bmpFileHeader.type = 0x4D42; // 'BM' diff --git a/BMPEditor/Base/image.cpp b/BMPEditor/Base/image.cpp index cb47587..cd73ffa 100644 --- a/BMPEditor/Base/image.cpp +++ b/BMPEditor/Base/image.cpp @@ -3,6 +3,8 @@ #include "error.h" #include #include +#include +#include Image::Image() { this->type = IMG_NONE; @@ -41,6 +43,47 @@ QString Image::getName() const { return QString::fromStdString(fileName); } +int Image::importImage(const QString &filePath) +{ + // overeni existence souboru + if (!QFile::exists(filePath)) { + qDebug() << "File not exists: " << filePath; + return ERR_FILE_OPEN; + } + + // image reader + QImageReader reader(filePath); + + // nacteni informaci o obrazku + QSize imageSize = reader.size(); + + // nastaveni vlastnosti obrazku + this->imgPath = filePath; + this->width = imageSize.width(); + this->height = imageSize.height(); + this->bitDepth = 24; + + // nacteni obrazku do pameti + QImage image = reader.read(); + if (image.isNull()) { + qDebug() << "IMAGE read error: " << reader.errorString(); + return ERR_FILE_READ; + } + + // konverze formatu na RGB (pokud tomu tak jeste neni) + if (image.format() != QImage::Format_RGB888) { + image = image.convertToFormat(QImage::Format_RGB888); + } + + // kopirovani dat z QImage do Image + image = image.mirrored(false, true); + this->dataLen = this->width * this->height * 3; + this->pixels = new unsigned char[this->dataLen]; + memcpy(this->pixels, image.constBits(), this->dataLen); + + return STATUS_OK; +} + int Image::buildImagePreview() { if(this->imagePreview != NULL) { diff --git a/BMPEditor/Base/image.h b/BMPEditor/Base/image.h index 1886bbc..4823361 100644 --- a/BMPEditor/Base/image.h +++ b/BMPEditor/Base/image.h @@ -98,6 +98,13 @@ class Image */ QString getName() const; + /** + * @brief Univerzalni metoda pro import libovolneho typu obrazku + * @param filePath - Cesta k obrazku + * @return Error code + */ + int importImage(const QString &filePath); + }; #endif // IMAGE_H diff --git a/BMPEditor/Editor/imageinfopanel.cpp b/BMPEditor/Editor/imageinfopanel.cpp index a6b907e..49b21df 100644 --- a/BMPEditor/Editor/imageinfopanel.cpp +++ b/BMPEditor/Editor/imageinfopanel.cpp @@ -104,6 +104,7 @@ void ImageInfoPanel::setImage(Image *img) { // paleta barev this->colorPaletteFrame->getLabel()->setText("Color Palette"); + int colorsPerRow = std::floor((this->width() - 50) / 32); if(bmp->bmpInfoHeader.bitCount <= 8 && bmp->bmpInfoHeader.bitCount > 0) { int row = 0; int col = 0; @@ -113,12 +114,12 @@ void ImageInfoPanel::setImage(Image *img) { colorFrame->setFrameShape(QFrame::Box); colorFrame->setFrameShadow(QFrame::Plain); colorFrame->setLineWidth(1); - colorFrame->setFixedSize(22, 22); + colorFrame->setFixedSize(24, 24); colorFrame->setToolTip(QString("RGB(%1, %2, %3)").arg(color.red).arg(color.green).arg(color.blue)); colorFrame->setStyleSheet(QString("background-color: rgb(%1, %2, %3);").arg(color.red).arg(color.green).arg(color.blue)); colorPalette->addWidget(colorFrame, row, col); col++; - if (col >= 7) { + if (col >= colorsPerRow) { col = 0; row++; } diff --git a/BMPEditor/Image/bmpimage.cpp b/BMPEditor/Image/bmpimage.cpp index 66f5f40..c3e2ef1 100644 --- a/BMPEditor/Image/bmpimage.cpp +++ b/BMPEditor/Image/bmpimage.cpp @@ -155,3 +155,42 @@ int BMPImage::isBMPImage(Image *img, int bitDepth) { } return STATUS_OK; } + +int BMPImage::importAsBMP24(const QString &path) +{ + // nacteni obrazovych dat do rodicovske tridy + int errCode = Image::importImage(path); + if(errCode != STATUS_OK) { + return errCode; + } + + // vypocet stride ve finalnim BMP obrazku + uint32_t stride = BMP_IO_calculateStride(24, this->width); + + // nastaveni typu obrazku + this->type = IMG_BMP; + + // file header + this->bmpFileHeader.type = 0x4D42; + this->bmpFileHeader.size = sizeof(BitMapFileHeader_t) + sizeof(BitMapInfoHeader_t) + (stride * this->height); + this->bmpFileHeader.reserved1 = this->bmpFileHeader.reserved2 = 0; + this->bmpFileHeader.offset = sizeof(BitMapFileHeader_t) + sizeof(BitMapInfoHeader_t); + + // info header + this->bmpInfoHeader.size = sizeof(BitMapInfoHeader_t); + this->bmpInfoHeader.width = this->width; + this->bmpInfoHeader.height = this->height; + this->bmpInfoHeader.planes = 1; + this->bmpInfoHeader.bitCount = 24; + this->bmpInfoHeader.compression = 0x0; + this->bmpInfoHeader.imageSize = (stride * this->height); + this->bmpInfoHeader.xPixelsPerMeter = 0x0; + this->bmpInfoHeader.yPixelsPerMeter = 0x0; + this->bmpInfoHeader.colorsUsed = 0; + this->bmpInfoHeader.colorsImportant = 0; + + // sestaveni nahledu + this->buildImagePreview(); + + return STATUS_OK; +} diff --git a/BMPEditor/Image/bmpimage.h b/BMPEditor/Image/bmpimage.h index af90bb2..6ca7858 100644 --- a/BMPEditor/Image/bmpimage.h +++ b/BMPEditor/Image/bmpimage.h @@ -29,6 +29,15 @@ class BMPImage : public Image // overeni formatu abstraktniho Image static int isBMPImage(Image *img, int bitDepth); + + /** + * @brief Obrazek libovolneho formatu importuje do aplikace jak BMP24. Pro nacteni + * obrazku z jeneho formatu nez BMP je pouzita knihovna. Dale uz se pouziva vlastni + * implementace pro praci z BMP + * @param path - Cesta k obrazku + * @return Error code + */ + int importAsBMP24(const QString &path); }; #endif // BMPIMAGE_H diff --git a/BMPEditor/Struct/bmpstruct.cpp b/BMPEditor/Struct/bmpstruct.cpp index b2f9ddc..69e1385 100644 --- a/BMPEditor/Struct/bmpstruct.cpp +++ b/BMPEditor/Struct/bmpstruct.cpp @@ -33,7 +33,7 @@ int BMP_STRUCT_validate(const BitMapFileHeader_t& fileHeader, const BitMapInfoHe return ERR_INVALID_FILE_OFFSET; } - // overeni platnost hlavicky BMP obrazku + // overeni platnost info hlavicky BMP obrazku if (infoHeader.size != 40) { return ERR_INVALID_BMP_HEADER_SIZ; } diff --git a/BMPEditor/about.ui b/BMPEditor/about.ui index a15197c..e1aff5e 100644 --- a/BMPEditor/about.ui +++ b/BMPEditor/about.ui @@ -57,7 +57,7 @@ - <h1>BMP Editor</h1><b>Author:</b> Martin Krčma<br><b>Last Update:</b> 9. 2. 2024<br><b>Version:</b> 0.1.0 + <html><head/><body><h1 style=" margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:xx-large; font-weight:700;">BMP Editor</span></h1><p><span style=" font-weight:700;">Author:</span> Martin Krčma<br/><span style=" font-weight:700;">Last Update:</span> 12. 2. 2024<br/><span style=" font-weight:700;">Version:</span> 0.2.0</p></body></html> diff --git a/BMPEditor/mainwindow.cpp b/BMPEditor/mainwindow.cpp index 3533cb8..e3fe99f 100644 --- a/BMPEditor/mainwindow.cpp +++ b/BMPEditor/mainwindow.cpp @@ -219,6 +219,7 @@ void MainWindow::appActionActivation() this->ui->actionSharpen->setEnabled(enabled); this->ui->actionEdge_Detection->setEnabled(enabled); this->ui->actionEmboss->setEnabled(enabled); + this->ui->actionApply_Custom_Kernel->setEnabled(enabled); // transformace this->ui->actionRotate_90_plus->setEnabled(enabled); this->ui->actionRotate_90_minus->setEnabled(enabled); @@ -239,6 +240,16 @@ void MainWindow::appActionActivation() this->ui->actionRead_message->setEnabled(enabled); } +void MainWindow::checkForHiddenMessage() +{ + // overi zda se v nactenem obrazku nenachazi zprava + QString msg = ""; + int errCode = STEGANOGRAPHY_readMessage(this->image->pixels, this->image->width, this->image->height, msg); + if(errCode == STATUS_OK) { + QMessageBox::information(this, tr("Hidden message"), tr("The image contains a hidden message!\n\nMessage: %1").arg(msg)); + } +} + void MainWindow::imageChanged(const QString &message) { // obrazek byl zmenen nejaky zpusobem @@ -298,11 +309,7 @@ void MainWindow::on_actionOpen_triggered() QMessageBox::information(this, tr("Open Image"), tr("Image opened successfully!")); // overi zda se v nactenem obrazku nenachazi zprava - QString msg = ""; - int errCode = STEGANOGRAPHY_readMessage(this->image->pixels, this->image->width, this->image->height, msg); - if(errCode == STATUS_OK) { - QMessageBox::information(this, tr("Hidden message"), tr("The image contains a hidden message!\n\nMessage: %1").arg(msg)); - } + this->checkForHiddenMessage(); } } @@ -653,3 +660,35 @@ void MainWindow::on_actionApply_Custom_Kernel_triggered() } } + +void MainWindow::on_actionImport_as_BMP_24_triggered() +{ + // app: import image + QString fileName = QFileDialog::getOpenFileName(this, tr("Import Image As BMP 24b"), QDir::homePath(), tr("Images (*.bmp *.jpg *.jpeg *.png *.gif *.tiff)")); + if (!fileName.isEmpty()) { + + qDebug() << "Import file: " << fileName; + BMPImage *bmp = new BMPImage(); + this->statusLabel->setText(tr("Status: Image importing ...")); + + int errCode = bmp->importAsBMP24(fileName); + if(errCode != STATUS_OK) { + + // nastala chyba pri importu souboru + QString errorStr; + getErrorCodeInfo(errCode, errorStr); + QMessageBox::critical(this, tr("Import Error"), tr("Failed to import BMP image. Error: %1").arg(errorStr)); + this->statusLabel->setText(tr("Status: Failed to import image")); + + } else { + // obrazek uspesne nacten => zobrazeni v editoru + this->setImage(bmp); + QMessageBox::information(this, tr("Import Image"), tr("Image imported successfully!")); + + // overi zda se v nactenem obrazku nenachazi zprava + this->checkForHiddenMessage(); + } + + } +} + diff --git a/BMPEditor/mainwindow.h b/BMPEditor/mainwindow.h index 602b64c..4141309 100644 --- a/BMPEditor/mainwindow.h +++ b/BMPEditor/mainwindow.h @@ -60,10 +60,9 @@ class MainWindow : public QMainWindow KernelInputDialog kernelInputDialog; void closeEvent(QCloseEvent *event); - void formatBMP(int bitCount); - void appActionActivation(); + void checkForHiddenMessage(); private slots: void imageChanged(const QString &message); @@ -133,6 +132,8 @@ private slots: void on_actionApply_Custom_Kernel_triggered(); + void on_actionImport_as_BMP_24_triggered(); + private: Ui::MainWindow *ui; QSplitter * splitter_horizontal; diff --git a/BMPEditor/mainwindow.ui b/BMPEditor/mainwindow.ui index 24d6616..b1572c8 100644 --- a/BMPEditor/mainwindow.ui +++ b/BMPEditor/mainwindow.ui @@ -37,6 +37,7 @@ + @@ -154,6 +155,9 @@ Rotate +90° + + Ctrl+R + @@ -164,6 +168,9 @@ Rotate -90° + + Shift+R + @@ -340,6 +347,14 @@ Flip vertically Apply Custom Kernel + + + Import as BMP 24 + + + Ctrl+I + +