diff --git a/mapeditor/BitmapHandler.cpp b/mapeditor/BitmapHandler.cpp new file mode 100644 index 000000000..7eedc8582 --- /dev/null +++ b/mapeditor/BitmapHandler.cpp @@ -0,0 +1,173 @@ +// +// BitmapHandler.cpp +// vcmieditor +// +// Created by nordsoft on 29.08.2022. +// + +#include "BitmapHandler.h" + +#include "StdInc.h" + +#include "../lib/filesystem/Filesystem.h" +#include "BitmapHandler.h" +#include "../lib/vcmi_endian.h" + +#include +#include +#include + +namespace BitmapHandler +{ + QImage loadH3PCX(ui8 * data, size_t size); + + QImage loadBitmapFromDir(std::string path, std::string fname, bool setKey=true); +} + +bool isPCX(const ui8 *header)//check whether file can be PCX according to header +{ + ui32 fSize = read_le_u32(header + 0); + ui32 width = read_le_u32(header + 4); + ui32 height = read_le_u32(header + 8); + return fSize == width*height || fSize == width*height*3; +} + +enum Epcxformat +{ + PCX8B, + PCX24B +}; + +QImage BitmapHandler::loadH3PCX(ui8 * pcx, size_t size) +{ + //SDL_Surface * ret; + + Epcxformat format; + int it=0; + + ui32 fSize = read_le_u32(pcx + it); it+=4; + ui32 width = read_le_u32(pcx + it); it+=4; + ui32 height = read_le_u32(pcx + it); it+=4; + + if (fSize==width*height*3) + format=PCX24B; + else if (fSize==width*height) + format=PCX8B; + else + return QImage(); + + QSize qsize(width, height); + + if (format==PCX8B) + { + it = 0xC; + //auto bitmap = QBitmap::fromData(qsize, pcx + it); + QImage image(pcx + it, width, height, QImage::Format_MonoLSB); + + //palette - last 256*3 bytes + QVector colorTable; + it = (int)size-256*3; + for (int i=0;i<256;i++) + { + char bytes[3]; + bytes[0] = pcx[it++]; + bytes[1] = pcx[it++]; + bytes[2] = pcx[it++]; + colorTable.append(qRgb(bytes[0], bytes[1], bytes[2])); + } + image.setColorTable(colorTable); + return image; + } + else + { + QImage image(pcx + it, width, height, QImage::Format_RGB32); + return image; + } +} + +QImage BitmapHandler::loadBitmapFromDir(std::string path, std::string fname, bool setKey) +{ + if(!fname.size()) + { + logGlobal->warn("Call to loadBitmap with void fname!"); + return QImage(); + } + if (!CResourceHandler::get()->existsResource(ResourceID(path + fname, EResType::IMAGE))) + { + return QImage(); + } + + auto fullpath = CResourceHandler::get()->getResourceName(ResourceID(path + fname, EResType::IMAGE)); + auto readFile = CResourceHandler::get()->load(ResourceID(path + fname, EResType::IMAGE))->readAll(); + + if (isPCX(readFile.first.get())) + {//H3-style PCX + auto image = loadH3PCX(readFile.first.get(), readFile.second); + if(!image.isNull()) + { + if(image.bitPlaneCount() == 1 && setKey) + { + QVector colorTable = image.colorTable(); + colorTable[0] = qRgba(255, 255, 255, 0); + image.setColorTable(colorTable); + } + } + else + { + logGlobal->error("Failed to open %s as H3 PCX!", fname); + } + return image; + } + else + { //loading via SDL_Image + QImage image(QString::fromStdString(fullpath->native())); + if(!image.isNull()) + { + if(image.bitPlaneCount() == 1) + { + //set correct value for alpha\unused channel + QVector colorTable = image.colorTable(); + for(auto & c : colorTable) + c = qRgb(qRed(c), qGreen(c), qBlue(c)); + image.setColorTable(colorTable); + } + } + else + { + logGlobal->error("Failed to open %s via QImage", fname); + return image; + } + } + return QImage(); + // When modifying anything here please check two use cases: + // 1) Vampire mansion in Necropolis (not 1st color is transparent) + // 2) Battle background when fighting on grass/dirt, topmost sky part (NO transparent color) + // 3) New objects that may use 24-bit images for icons (e.g. witchking arts) + /*if (ret->format->palette) + { + CSDL_Ext::setDefaultColorKeyPresize(ret); + } + else if (ret->format->Amask) + { + SDL_SetSurfaceBlendMode(ret, SDL_BLENDMODE_BLEND); + } + else // always set + { + CSDL_Ext::setDefaultColorKey(ret); + } + return ret;*/ +} + +QImage BitmapHandler::loadBitmap(std::string fname, bool setKey) +{ + QImage image = loadBitmapFromDir("DATA/", fname, setKey); + if(image.isNull()) + { + image = loadBitmapFromDir("SPRITES/", fname, setKey); + if(image.isNull()) + { + logGlobal->error("Error: Failed to find file %s", fname); + } + } + return image; +} diff --git a/mapeditor/BitmapHandler.h b/mapeditor/BitmapHandler.h new file mode 100644 index 000000000..9b68789c1 --- /dev/null +++ b/mapeditor/BitmapHandler.h @@ -0,0 +1,15 @@ +// +// BitmapHandler.hpp +// vcmieditor +// +// Created by nordsoft on 29.08.2022. +// + +#pragma once + +namespace BitmapHandler +{ + //Load file from /DATA or /SPRITES + QImage loadBitmap(std::string fname, bool setKey=true); +} + diff --git a/mapeditor/mainwindow.cpp b/mapeditor/mainwindow.cpp index 57e7d9e15..f9d4a710b 100644 --- a/mapeditor/mainwindow.cpp +++ b/mapeditor/mainwindow.cpp @@ -3,6 +3,9 @@ #include #include +#include +#include +#include #include "../lib/VCMIDirs.h" #include "../lib/VCMI_Lib.h" @@ -10,6 +13,8 @@ #include "../lib/CConfigHandler.h" #include "../lib/filesystem/Filesystem.h" #include "../lib/GameConstants.h" +#include "../lib/mapping/CMapService.h" +#include "../lib/mapping/CMap.h" #include "CGameInfo.h" @@ -89,7 +94,30 @@ MainWindow::~MainWindow() void MainWindow::on_actionOpen_triggered() { - auto fileName = QFileDialog::getOpenFileName(this, tr("Open Image"), QString::fromStdString(VCMIDirs::get().userCachePath().native()), tr("Homm3 Files (*.vmap *.h3m)")); - + auto filename = QFileDialog::getOpenFileName(this, tr("Open Image"), QString::fromStdString(VCMIDirs::get().userCachePath().native()), tr("Homm3 Files (*.vmap *.h3m)")); + + if(filename.isNull()) + return; + + QFileInfo fi(filename); + std::string fname = fi.fileName().toStdString(); + std::string fdir = fi.dir().path().toStdString(); + ResourceID resId("MAPS/" + fname, EResType::MAP); + + if(!CResourceHandler::get()->existsResource(resId)) + { + QMessageBox::information(this, "Failed to open map", "Only map folder is supported"); + return; + } + + CMapService mapService; + try + { + auto cmap = mapService.loadMap(resId); + } + catch(const std::exception & e) + { + QMessageBox::critical(this, "Failed to open map", e.what()); + } }