Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Everywhere: Make loading pcx format possible #157

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading