From 4e556e72149e1ee55f52942e126a32c8f9c32c98 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Wed, 23 Jun 2021 10:11:58 +0200 Subject: [PATCH] add support of TGA image format, fix #766 --- src/meshlabplugins/io_base/baseio.cpp | 170 ++++++++++++++++++++++---- src/meshlabplugins/io_base/baseio.h | 3 + 2 files changed, 151 insertions(+), 22 deletions(-) diff --git a/src/meshlabplugins/io_base/baseio.cpp b/src/meshlabplugins/io_base/baseio.cpp index 4de06447c..6214d76c1 100644 --- a/src/meshlabplugins/io_base/baseio.cpp +++ b/src/meshlabplugins/io_base/baseio.cpp @@ -75,7 +75,8 @@ const static std::list importImageFormatList = { FileFormat("Joint Photographic Experts Group", "JPEG"), FileFormat("Portable Network Graphics", "PNG"), FileFormat("X11 Bitmap", "XBM"), - FileFormat("X11 Bitmap", "XPM") + FileFormat("X11 Bitmap", "XPM"), + FileFormat("Truevision Graphics Adapter", "TGA") }; const static std::list exportImageFormatList = { @@ -514,31 +515,37 @@ QImage BaseMeshIOPlugin::openImage( vcg::CallBackPos*) { QImage loadedImage; - bool supportedFormat = false; - for (const FileFormat& f : importImageFormatList){ - if (f.extensions.first().toUpper() == format.toUpper()) - supportedFormat = true; + //format not natively supported by QImage + if (format.toUpper() == "TGA") { + loadedImage = loadTga(fileName.toStdString().c_str()); } - - if (supportedFormat) { - QFileInfo fi(fileName); - - if(!fi.exists()) { - QString errorMsgFormat = "Unable to open file:\n\"%1\"\n\nError details: file %1 does not exist."; - throw MLException(errorMsgFormat.arg(fileName)); - } - if(!fi.isReadable()) { - QString errorMsgFormat = "Unable to open file:\n\"%1\"\n\nError details: file %1 is not readable."; - throw MLException(errorMsgFormat.arg(fileName)); + else { //check if it is a format supported natively by QImage + bool supportedFormat = false; + for (const FileFormat& f : importImageFormatList){ + if (f.extensions.first().toUpper() == format.toUpper()) + supportedFormat = true; } - loadedImage.load(fileName); - if (loadedImage.isNull()){ - throw MLException("Failed to load the image " + fileName); + if (supportedFormat) { + QFileInfo fi(fileName); + + if(!fi.exists()) { + QString errorMsgFormat = "Unable to open file:\n\"%1\"\n\nError details: file %1 does not exist."; + throw MLException(errorMsgFormat.arg(fileName)); + } + if(!fi.isReadable()) { + QString errorMsgFormat = "Unable to open file:\n\"%1\"\n\nError details: file %1 is not readable."; + throw MLException(errorMsgFormat.arg(fileName)); + } + + loadedImage.load(fileName); + if (loadedImage.isNull()){ + throw MLException("Failed to load the image " + fileName); + } + } + else { + wrongOpenFormat(format); } - } - else { - wrongOpenFormat(format); } return loadedImage; } @@ -713,4 +720,123 @@ RichParameterList BaseMeshIOPlugin::initSaveParameter(const QString &format, con return par; } +/** + * @brief BaseMeshIOPlugin::loadTga + * source: https://forum.qt.io/topic/74712/qimage-from-tga-with-alpha/11 + */ +QImage BaseMeshIOPlugin::loadTga(const char* filePath) +{ + QImage img; + if (!img.load(filePath)) { + + // open the file + std::fstream fsPicture(filePath, std::ios::in | std::ios::binary); + + if (!fsPicture.is_open()) { + throw MLException("Impossible to open the file"); + } + + // some variables + std::vector* vui8Pixels; + std::uint32_t ui32BpP; + std::uint32_t ui32Width; + std::uint32_t ui32Height; + + // read in the header + std::uint8_t ui8x18Header[19] = { 0 }; + fsPicture.read(reinterpret_cast(&ui8x18Header), sizeof(ui8x18Header) - 1); + + //get variables + vui8Pixels = new std::vector; + bool bCompressed; + std::uint32_t ui32IDLength; + std::uint32_t ui32PicType; + std::uint32_t ui32PaletteLength; + std::uint32_t ui32Size; + + // extract all information from header + ui32IDLength = ui8x18Header[0]; + ui32PicType = ui8x18Header[2]; + ui32PaletteLength = ui8x18Header[6] * 0x100 + ui8x18Header[5]; + ui32Width = ui8x18Header[13] * 0x100 + ui8x18Header[12]; + ui32Height = ui8x18Header[15] * 0x100 + ui8x18Header[14]; + ui32BpP = ui8x18Header[16]; + + // calculate some more information + ui32Size = ui32Width * ui32Height * ui32BpP / 8; + bCompressed = ui32PicType == 9 || ui32PicType == 10; + vui8Pixels->resize(ui32Size); + + // jump to the data block + fsPicture.seekg(ui32IDLength + ui32PaletteLength, std::ios_base::cur); + + if (ui32PicType == 2 && (ui32BpP == 24 || ui32BpP == 32)) { + fsPicture.read(reinterpret_cast(vui8Pixels->data()), ui32Size); + } + // else if compressed 24 or 32 bit + else if (ui32PicType == 10 && (ui32BpP == 24 || ui32BpP == 32)) { // compressed + std::uint8_t tempChunkHeader; + std::uint8_t tempData[5]; + unsigned int tempByteIndex = 0; + + do { + fsPicture.read(reinterpret_cast(&tempChunkHeader), sizeof(tempChunkHeader)); + + if (tempChunkHeader >> 7) { // repeat count + // just use the first 7 bits + tempChunkHeader = (uint8_t(tempChunkHeader << 1) >> 1); + + fsPicture.read(reinterpret_cast(&tempData), ui32BpP / 8); + + for (int i = 0; i <= tempChunkHeader; i++) { + vui8Pixels->at(tempByteIndex++) = tempData[0]; + vui8Pixels->at(tempByteIndex++) = tempData[1]; + vui8Pixels->at(tempByteIndex++) = tempData[2]; + if (ui32BpP == 32) vui8Pixels->at(tempByteIndex++) = tempData[3]; + } + } + else { // data count + // just use the first 7 bits + tempChunkHeader = (uint8_t(tempChunkHeader << 1) >> 1); + + for (int i = 0; i <= tempChunkHeader; i++) { + fsPicture.read(reinterpret_cast(&tempData), ui32BpP / 8); + + vui8Pixels->at(tempByteIndex++) = tempData[0]; + vui8Pixels->at(tempByteIndex++) = tempData[1]; + vui8Pixels->at(tempByteIndex++) = tempData[2]; + if (ui32BpP == 32) vui8Pixels->at(tempByteIndex++) = tempData[3]; + } + } + } while (tempByteIndex < ui32Size); + } + // not useable format + else { + fsPicture.close(); + throw MLException("Not useable TGA format"); + } + + fsPicture.close(); + + img = QImage(ui32Width, ui32Height, QImage::Format_RGB888); + + int pixelSize = ui32BpP == 32 ? 4 : 3; + //TODO: write direct into img + for (unsigned int x = 0; x < ui32Width; x++) { + for (unsigned int y = 0; y < ui32Height; y++) { + int valr = vui8Pixels->at(y * ui32Width * pixelSize + x * pixelSize + 2); + int valg = vui8Pixels->at(y * ui32Width * pixelSize + x * pixelSize + 1); + int valb = vui8Pixels->at(y * ui32Width * pixelSize + x * pixelSize); + + QColor value(valr, valg, valb); + img.setPixelColor(x, y, value); + } + } + + img = img.mirrored(); + + } + return img; +} + MESHLAB_PLUGIN_NAME_EXPORTER(BaseMeshIOPlugin) diff --git a/src/meshlabplugins/io_base/baseio.h b/src/meshlabplugins/io_base/baseio.h index b73132323..2f3ebba2c 100644 --- a/src/meshlabplugins/io_base/baseio.h +++ b/src/meshlabplugins/io_base/baseio.h @@ -99,6 +99,9 @@ public: RichParameterList initPreOpenParameter(const QString &formatName) const; RichParameterList initSaveParameter(const QString &format, const MeshModel &/*m*/) const; + +private: + QImage loadTga(const char* filePath); }; #endif