dxx-rebirth/similar/2d/pcx.cpp
Kp 6e4b63e098 Use C++17 std::size instead of custom lengthof
lengthof was added to provide the size of C arrays.  C++17 std::size is
now available, and can serve the same purpose.  Remove the custom
lengthof.

The generated code is the same in both cases, other than the change in
line numbers caused by removal of the #include directives.
2020-07-05 23:34:32 +00:00

389 lines
10 KiB
C++

/*
* Portions of this file are copyright Rebirth contributors and licensed as
* described in COPYING.txt.
* Portions of this file are copyright Parallax Software and licensed
* according to the Parallax license below.
* See COPYING.txt for license details.
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
*/
/*
*
* Routines to read/write pcx images.
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "gr.h"
#include "grdef.h"
#include "u_mem.h"
#include "pcx.h"
#include "physfsx.h"
#include "palette.h"
#include "dxxsconf.h"
#include "dsx-ns.h"
#include "compiler-poison.h"
#include "compiler-range_for.h"
#include "d_range.h"
#include "partial_range.h"
#include "console.h"
#if DXX_USE_SDLIMAGE
#include "physfsrwops.h"
#include <SDL_image.h>
#endif
namespace dcx {
namespace {
#if DXX_USE_SDLIMAGE
struct RAII_SDL_Surface
{
struct deleter
{
void operator()(SDL_Surface *s) const
{
SDL_FreeSurface(s);
}
};
std::unique_ptr<SDL_Surface, deleter> surface;
explicit RAII_SDL_Surface(SDL_Surface *const s) :
surface(s)
{
}
};
#endif
}
#if !DXX_USE_OGL && DXX_USE_SCREENSHOT_FORMAT_LEGACY
static int pcx_encode_byte(ubyte byt, ubyte cnt, PHYSFS_File *fid);
static int pcx_encode_line(const uint8_t *inBuff, uint_fast32_t inLen, PHYSFS_File *fp);
/* PCX Header data type */
struct PCXHeader
{
ubyte Manufacturer;
ubyte Version;
ubyte Encoding;
ubyte BitsPerPixel;
short Xmin;
short Ymin;
short Xmax;
short Ymax;
short Hdpi;
short Vdpi;
ubyte ColorMap[16][3];
ubyte Reserved;
ubyte Nplanes;
short BytesPerLine;
ubyte filler[60];
} __pack__;
#endif
#define PCXHEADER_SIZE 128
#if DXX_USE_SDLIMAGE
static pcx_result pcx_read_bitmap(const char *const filename, grs_main_bitmap &bmp, palette_array_t &palette, RWops_ptr rw)
{
RAII_SDL_Surface surface(IMG_LoadPCX_RW(rw.get()));
if (!surface.surface)
{
con_printf(CON_NORMAL, "%s:%u: failed to create surface from \"%s\"", __FILE__, __LINE__, filename);
return pcx_result::ERROR_OPENING;
}
const auto &s = *surface.surface.get();
const auto fmt = s.format;
if (!fmt || fmt->BitsPerPixel != 8)
return pcx_result::ERROR_WRONG_VERSION;
const auto fpal = fmt->palette;
if (!fpal || fpal->ncolors != palette.size())
return pcx_result::ERROR_NO_PALETTE;
const unsigned xsize = s.w;
const unsigned ysize = s.h;
if (xsize > 3840)
return pcx_result::ERROR_MEMORY;
if (ysize > 2400)
return pcx_result::ERROR_MEMORY;
DXX_CHECK_MEM_IS_DEFINED(s.pixels, xsize * ysize);
gr_init_bitmap_alloc(bmp, bm_mode::linear, 0, 0, xsize, ysize, xsize);
std::copy_n(reinterpret_cast<const uint8_t *>(s.pixels), xsize * ysize, &bmp.get_bitmap_data()[0]);
{
const auto a = [](const SDL_Color &c) {
return rgb_t{
static_cast<uint8_t>(c.r >> 2),
static_cast<uint8_t>(c.g >> 2),
static_cast<uint8_t>(c.b >> 2)
};
};
std::transform(fpal->colors, fpal->colors + palette.size(), palette.begin(), a);
}
return pcx_result::SUCCESS;
}
#else
static pcx_result pcx_read_blank(const char *const filename, grs_main_bitmap &bmp, palette_array_t &palette)
{
con_printf(CON_NORMAL, "%s:%u: PCX support disabled at compile time; cannot read file \"%s\"", __FILE__, __LINE__, filename);
constexpr unsigned xsize = 640;
constexpr unsigned ysize = 480;
gr_init_bitmap_alloc(bmp, bm_mode::linear, 0, 0, xsize, ysize, xsize);
auto &bitmap_data = *reinterpret_cast<uint8_t (*)[ysize][xsize]>(bmp.get_bitmap_data());
constexpr uint8_t border = 1;
constexpr uint8_t body = 0;
std::fill(&bitmap_data[0][0], &bitmap_data[2][0], border);
std::fill(&bitmap_data[2][0], &bitmap_data[ysize - 2][0], body);
std::fill(&bitmap_data[ysize - 2][0], &bitmap_data[ysize][0], border);
for (auto &&y : xrange(2u, ysize - 2))
{
bitmap_data[y][0] = border;
bitmap_data[y][1] = border;
bitmap_data[y][xsize - 2] = border;
bitmap_data[y][xsize - 1] = border;
}
palette = {};
palette[border] = {63, 63, 63};
return pcx_result::SUCCESS;
}
#endif
}
#if defined(DXX_BUILD_DESCENT_I)
namespace dsx {
#if DXX_USE_SDLIMAGE
static std::pair<std::unique_ptr<uint8_t[]>, std::size_t> load_physfs_blob(const char *const filename)
{
RAIIPHYSFS_File file(PHYSFSX_openReadBuffered(filename));
if (!file)
{
con_printf(CON_VERBOSE, "%s:%u: failed to open \"%s\"", __FILE__, __LINE__, filename);
return {};
}
const std::size_t fsize = PHYSFS_fileLength(file);
if (fsize > 0x100000)
{
con_printf(CON_VERBOSE, "%s:%u: file too large \"%s\"", __FILE__, __LINE__, filename);
return {};
}
auto encoded_buffer = std::make_unique<uint8_t[]>(fsize);
if (PHYSFS_read(file, encoded_buffer.get(), 1, fsize) < fsize)
{
con_printf(CON_VERBOSE, "%s:%u: failed to read \"%s\"", __FILE__, __LINE__, filename);
return {};
}
return {std::move(encoded_buffer), fsize};
}
static std::pair<std::unique_ptr<uint8_t[]>, std::size_t> load_decoded_physfs_blob(const char *const filename)
{
auto encoded_blob = load_physfs_blob(filename);
auto &&[encoded_buffer, fsize] = encoded_blob;
if (!encoded_buffer)
return encoded_blob;
const std::size_t data_size = fsize - 1;
auto &&transform_predicate = [xor_value = encoded_buffer[data_size]](uint8_t c) mutable {
return c ^ --xor_value;
};
auto &&decoded_buffer = std::make_unique<uint8_t[]>(data_size);
const auto b = encoded_buffer.get();
std::transform(std::make_reverse_iterator(std::next(b, data_size)), std::make_reverse_iterator(b), decoded_buffer.get(), transform_predicate);
return {std::move(decoded_buffer), data_size};
}
#endif
pcx_result bald_guy_load(const char *const filename, grs_main_bitmap &bmp, palette_array_t &palette)
{
#if DXX_USE_SDLIMAGE
const auto &&[bguy_data, data_size] = load_decoded_physfs_blob(filename);
if (!bguy_data)
return pcx_result::ERROR_OPENING;
RWops_ptr rw(SDL_RWFromConstMem(bguy_data.get(), data_size));
return pcx_read_bitmap(filename, bmp, palette, std::move(rw));
#else
return pcx_read_blank(filename, bmp, palette);
#endif
}
}
#endif
namespace dcx {
pcx_result pcx_read_bitmap(const char *const filename, grs_main_bitmap &bmp, palette_array_t &palette)
{
#if DXX_USE_SDLIMAGE
auto rw = PHYSFSRWOPS_openRead(filename);
if (!rw)
{
con_printf(CON_NORMAL, "%s:%u: failed to open \"%s\"", __FILE__, __LINE__, filename);
return pcx_result::ERROR_OPENING;
}
return pcx_read_bitmap(filename, bmp, palette, std::move(rw));
#else
return pcx_read_blank(filename, bmp, palette);
#endif
}
#if !DXX_USE_OGL && DXX_USE_SCREENSHOT_FORMAT_LEGACY
pcx_result pcx_write_bitmap(PHYSFS_File *const PCXfile, const grs_bitmap *const bmp, palette_array_t &palette)
{
int retval;
ubyte data;
PCXHeader header{};
header.Manufacturer = 10;
header.Encoding = 1;
header.Nplanes = 1;
header.BitsPerPixel = 8;
header.Version = 5;
header.Xmax = bmp->bm_w-1;
header.Ymax = bmp->bm_h-1;
header.BytesPerLine = bmp->bm_w;
if (PHYSFS_write(PCXfile, &header, PCXHEADER_SIZE, 1) != 1)
{
return pcx_result::ERROR_WRITING;
}
{
const uint_fast32_t bm_w = bmp->bm_w;
const uint_fast32_t bm_rowsize = bmp->bm_rowsize;
const auto bm_data = bmp->get_bitmap_data();
const auto e = &bm_data[bm_rowsize * bmp->bm_h];
for (auto i = &bm_data[0]; i != e; i += bm_rowsize)
{
if (!pcx_encode_line(i, bm_w, PCXfile))
{
return pcx_result::ERROR_WRITING;
}
}
}
// Mark an extended palette
data = 12;
if (PHYSFS_write(PCXfile, &data, 1, 1) != 1)
{
return pcx_result::ERROR_WRITING;
}
retval = PHYSFS_write(PCXfile, &palette[0], sizeof(palette), 1);
if (retval !=1) {
return pcx_result::ERROR_WRITING;
}
return pcx_result::SUCCESS;
}
// returns number of bytes written into outBuff, 0 if failed
int pcx_encode_line(const uint8_t *inBuff, uint_fast32_t inLen, PHYSFS_File *fp)
{
ubyte last;
int i;
int total;
ubyte runCount; // max single runlength is 63
total = 0;
last = *(inBuff);
runCount = 1;
range_for (const auto ub, unchecked_partial_range(inBuff, 1u, inLen))
{
if (ub == last) {
runCount++; // it encodes
if (runCount == 63) {
if (!(i=pcx_encode_byte(last, runCount, fp)))
return(0);
total += i;
runCount = 0;
}
} else { // this != last
if (runCount) {
if (!(i=pcx_encode_byte(last, runCount, fp)))
return(0);
total += i;
}
last = ub;
runCount = 1;
}
}
if (runCount) { // finish up
if (!(i=pcx_encode_byte(last, runCount, fp)))
return 0;
return total + i;
}
return total;
}
static inline int PHYSFSX_putc(PHYSFS_File *file, uint8_t ch)
{
if (PHYSFS_write(file, &ch, 1, 1) < 1)
return -1;
else
return ch;
}
// subroutine for writing an encoded byte pair
// returns count of bytes written, 0 if error
int pcx_encode_byte(ubyte byt, ubyte cnt, PHYSFS_File *fid)
{
if (cnt) {
if ( (cnt==1) && (0xc0 != (0xc0 & byt)) ) {
if(EOF == PHYSFSX_putc(fid, static_cast<int>(byt)))
return 0; // disk write error (probably full)
return 1;
} else {
if(EOF == PHYSFSX_putc(fid, 0xC0 | cnt))
return 0; // disk write error
if(EOF == PHYSFSX_putc(fid, static_cast<int>(byt)))
return 0; // disk write error
return 2;
}
}
return 0;
}
#endif
//text for error messges
constexpr char pcx_error_messages[] = {
"No error.\0"
"Error opening file.\0"
"Could not read PCX header.\0"
"Unsupported PCX version.\0"
"Error reading data.\0"
"Could not find palette information.\0"
"Error writing data.\0"
};
//function to return pointer to error message
const char *pcx_errormsg(const pcx_result r)
{
const char *p = pcx_error_messages;
unsigned error_number = static_cast<unsigned>(r);
while (error_number--) {
if (p == std::end(pcx_error_messages))
return nullptr;
p += strlen(p)+1;
}
return p;
}
}