Skip to content

Commit

Permalink
Everywhere: Make loading pcx format possible
Browse files Browse the repository at this point in the history
  • Loading branch information
tetektoza committed Jan 7, 2025
1 parent 6c57fd8 commit dcf7e61
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
include_directories(source/)

set(PROJECT_SOURCES
source/d1formats/pcx.cpp
source/d1formats/pcx.h
source/views/celview.cpp
source/config/config.cpp
source/d1formats/d1amp.cpp
Expand Down
2 changes: 2 additions & 0 deletions source/d1formats/d1gfx.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class D1GfxPixel {

class D1GfxFrame {
friend class D1Cel;
friend class Pcx;
friend class D1CelFrame;
friend class D1Cl2;
friend class D1Cl2Frame;
Expand Down Expand Up @@ -67,6 +68,7 @@ class D1Gfx : public QObject {

friend class D1Cel;
friend class D1Cl2;
friend class Pcx;
friend class D1CelTileset;

public:
Expand Down
91 changes: 91 additions & 0 deletions source/d1formats/pcx.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#include "pcx.h"
#include "d1gfx.h"
#include <QFile>
#include <QBuffer>
#include <QByteArray>
#include <QDataStream>

bool Pcx::load(D1Gfx &gfx, QString filePath, const OpenAsParam &params)
{
if (!QFile::exists(filePath))
return false;

QFile file = QFile(filePath);

if (!file.open(QIODevice::ReadOnly))
return false;

QByteArray fileData = file.readAll();
QBuffer fileBuffer(&fileData);

if (!fileBuffer.open(QIODevice::ReadOnly))
return false;

QDataStream in(&fileBuffer);
in.setByteOrder(QDataStream::BigEndian);

PCXHeader header;

char* headerBuf = reinterpret_cast<char*>(&header);

D1GfxFrame frame;

in.readRawData(headerBuf, PcxHeaderSize);

frame.width = header.Xmax;
frame.height = header.Ymax;

gfx.gfxFilePath = filePath;

constexpr uint16_t PaletteSize = 768;
const uint32_t imgDataSize = fileData.size() - PcxHeaderSize - PaletteSize - 1;
unsigned char imgData[imgDataSize];
in.readRawData(reinterpret_cast<char *>(imgData), imgDataSize);

QList<D1GfxPixel> pixelLine;
for (int i = 0; i < imgDataSize; i++) {

if (pixelLine.size() == header.BytesPerLine) {
frame.pixels.append(pixelLine);
pixelLine.clear();
}

constexpr uint8_t PcxMaxSinglePixel = 0xBF;

const uint8_t byte = imgData[i];
if (byte <= PcxMaxSinglePixel) {
pixelLine.append(D1GfxPixel::colorPixel(byte));
continue;
}

constexpr uint8_t PcxRunLengthMask = 0x3F;
const uint8_t runLength = (byte & PcxRunLengthMask);
for (unsigned int repeatedByte = 0; repeatedByte < runLength; repeatedByte++)
{
if (pixelLine.size() == header.BytesPerLine) {
frame.pixels.append(pixelLine);
pixelLine.clear();
}
pixelLine.append(D1GfxPixel::colorPixel(imgData[i+1]));
}
i++;
}

gfx.frames.append(frame);

// TBD how to actually handle loading palette, since it is being
// from the image directly

// unsigned char paletteBuf[768] = { 0 };
// in.skipRawData(1);
// in.readRawData(reinterpret_cast<char *>(paletteBuf), 768);
// QFile pcxFile("pcxtest.pal");
// if (!pcxFile.open(QIODevice::WriteOnly)) {
// }
//
// QByteArray byteArray(reinterpret_cast<const char*>(paletteBuf), 768);
// pcxFile.write(byteArray);
// pcxFile.close();

return true;
}
35 changes: 35 additions & 0 deletions source/d1formats/pcx.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once

#include <cstddef>
#include <cstdint>
#include <QString>
#include "d1gfx.h"

struct PCXHeader {
uint8_t Manufacturer;
uint8_t Version;
uint8_t Encoding;
uint8_t BitsPerPixel;
uint16_t Xmin;
uint16_t Ymin;
uint16_t Xmax;
uint16_t Ymax;
uint16_t HDpi;
uint16_t VDpi;
uint8_t Colormap[48];
uint8_t Reserved;
uint8_t NPlanes;
uint16_t BytesPerLine;
uint16_t PaletteInfo;
uint16_t HscreenSize;
uint16_t VscreenSize;
uint8_t Filler[54];
};

class Pcx {
public:
static constexpr size_t PcxHeaderSize = 128;

static bool load(D1Gfx &gfx, QString filePath, const OpenAsParam &params);
};

13 changes: 11 additions & 2 deletions source/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#include "d1formats/d1celtileset.h"
#include "d1formats/d1cl2.h"
#include "ui_mainwindow.h"
#include "widgets/palettewidget.h"
#include "d1formats/pcx.h"

MainWindow::MainWindow()
: QMainWindow(nullptr)
Expand Down Expand Up @@ -424,7 +426,7 @@ void MainWindow::on_actionRegroupFrames_triggered()

void MainWindow::on_actionOpen_triggered()
{
QString openFilePath = this->fileDialog(FILE_DIALOG_MODE::OPEN, "Open Graphics", "CEL/CL2/CLX Files (*.cel *.CEL *.cl2 *.CL2 *.clx *.CLX)");
QString openFilePath = this->fileDialog(FILE_DIALOG_MODE::OPEN, "Open Graphics", "CEL/CL2/CLX/PCX Files (*.cel *.CEL *.cl2 *.CL2 *.clx *.CLX *.pcx *.PCX)");

if (!openFilePath.isEmpty()) {
OpenAsParam params;
Expand Down Expand Up @@ -481,7 +483,8 @@ void MainWindow::openFile(const OpenAsParam &params)
if (!openFilePath.isEmpty()
&& !openFilePath.toLower().endsWith(".cel")
&& !openFilePath.toLower().endsWith(".cl2")
&& !openFilePath.endsWith(".clx")) {
&& !openFilePath.endsWith(".clx")
&& !openFilePath.endsWith(".pcx")) {
return;
}

Expand Down Expand Up @@ -591,6 +594,12 @@ void MainWindow::openFile(const OpenAsParam &params)
return;
}
}
else if (openFilePath.toLower().endsWith(".pcx")) {
if (!Pcx::load(*this->gfx, openFilePath, params)) {
QMessageBox::critical(this, "Error", "Failed loading PCX file: " + openFilePath);
return;
}
}

// Add palette widgets for PAL and TRNs
this->palWidget = new PaletteWidget(this->undoStack, "Palette");
Expand Down

0 comments on commit dcf7e61

Please sign in to comment.