Skip to content

Introduce PixelFormat enum #2735

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

Merged
merged 5 commits into from
Aug 6, 2023
Merged
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: 1 addition & 1 deletion include/reelmagic.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ void ReelMagic_RENDER_SetPalette(const uint8_t entry, const uint8_t red,
void ReelMagic_RENDER_SetSize(const uint16_t width, const uint16_t height,
const bool double_width, const bool double_height,
const Fraction& render_pixel_aspect_ratio,
const uint8_t bits_per_pixel,
const PixelFormat pixel_format,
const double frames_per_second,
const VideoMode& video_mode);

Expand Down
41 changes: 34 additions & 7 deletions include/render.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

#include "../src/gui/render_scalers.h"
#include "fraction.h"
#include "vga.h"

struct RenderPal_t {
struct {
Expand All @@ -51,6 +52,32 @@ struct VideoMode {
Fraction pixel_aspect_ratio = {};
};

enum class PixelFormat : uint8_t {
// Up to 256 colours, paletted;
// stored as packed uint8 data
Indexed8 = 8,

// 32K high colour, 5 bits per red/blue/green component;
// stored as packed uint16 data with highest bit unused
BGR555 = 15,
//
// 65K high colour, 5 bits for red/blue, 6 bit for green;
// stored as packed uint16 data
BGR565 = 16,
//
// 16.7M (24-bit) true colour, 8 bits per red/blue/green component;
// stored as packed 24-bit data
BGR888 = 24,
//
// 16.7M (32-bit) true colour; 8 bits per red/blue/green component;
// stored as packed uint32 data with highest 8 bits unused
BGRX8888 = 32
};

const char* to_string(const PixelFormat pf);

uint8_t get_bits_per_pixel(const PixelFormat pf);

struct Render_t {
// Details about the rendered image.
// E.g. for the 320x200 256-colour 13h VGA mode with double-scanning
Expand Down Expand Up @@ -84,8 +111,8 @@ struct Render_t {

uint32_t start = 0;

// Pixel format of the image data (see `bpp` in vga.h for details)
uint8_t bpp = 0;
// Pixel format of the image data
PixelFormat pixel_format = {};

// Frames per second
double fps = 0;
Expand Down Expand Up @@ -175,14 +202,14 @@ struct RenderedImage {
// aspect ratio of the source video mode)
Fraction pixel_aspect_ratio = {};

// Pixel format of the image data (see `bpp` in vga.h for details)
uint8_t bits_per_pixel = 0;
// Pixel format of the image data
PixelFormat pixel_format = {};

// Bytes per row
uint16_t pitch = 0;

// (width * height) number of pixels stored in the pixel format defined
// by bits_per_pixel
// by pixel_format
uint8_t* image_data = nullptr;

// Pointer to a (256 * 4) byte long palette data, stored as 8-bit RGB
Expand All @@ -192,7 +219,7 @@ struct RenderedImage {

inline bool is_paletted() const
{
return (bits_per_pixel == 8);
return (pixel_format == PixelFormat::Indexed8);
}

RenderedImage deep_copy() const
Expand Down Expand Up @@ -236,7 +263,7 @@ std::deque<std::string> RENDER_InventoryShaders();
void RENDER_SetSize(const uint16_t width, const uint16_t height,
const bool double_width, const bool double_height,
const Fraction& render_pixel_aspect_ratio,
const uint8_t bits_per_pixel,
const PixelFormat pixel_format,
const double frames_per_second, const VideoMode& video_mode);

bool RENDER_StartUpdate(void);
Expand Down
2 changes: 1 addition & 1 deletion include/sdlmain.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ struct SDL_Block {
int height = 0;
} requested_window_bounds = {};

uint8_t bpp = 0;
PixelFormat pixel_format = {};
double dpi_scale = 1.0;
bool fullscreen = false;

Expand Down
29 changes: 4 additions & 25 deletions include/vga.h
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@ enum PixelsPerChar : int8_t {
Nine = 9,
};

enum class PixelFormat : uint8_t;

struct VGA_Draw {
bool resizing = false;
uint16_t width = 0;
Expand Down Expand Up @@ -266,30 +268,7 @@ struct VGA_Draw {
double per_line_ms = 0;
} delay = {};

// clang-format off
//
// Non-paletted RGB image data is stored as a continuous stream of BGR
// pixel values in memory.
//
// Valid values are the following:
//
// 8 - Indexed8 Up to 256 colours, paletted;
// stored as packed uint8 data
//
// 15 - BGR555 32K high colour, 5 bits per red/blue/green component;
// stored as packed uint16 data with highest bit unused
//
// 16 - BGR565 65K high colour, 5 bits for red/blue, 6 bit for green;
// stored as packed uint16 data
//
// 24 - BGR888 16.7M (24-bit) true colour, 8 bits per red/blue/green component;
// stored as packed 24-bit data
//
// 32 - BGRX8888 16.7M (32-bit) true colour; 8 bits per red/blue/green component;
// stored as packed uint32 data with highest 8 bits unused
//
// clang-format on
uint8_t bpp = 0;
PixelFormat pixel_format = {};

double host_refresh_hz = RefreshRateHostDefault;
double dos_refresh_hz = RefreshRateDosDefault;
Expand Down Expand Up @@ -999,7 +978,7 @@ void VGA_StartUpdateLFB(void);
void VGA_SetBlinking(uint8_t enabled);
void VGA_SetCGA2Table(uint8_t val0,uint8_t val1);
void VGA_SetCGA4Table(uint8_t val0,uint8_t val1,uint8_t val2,uint8_t val3);
uint8_t VGA_ActivateHardwareCursor();
PixelFormat VGA_ActivateHardwareCursor();
void VGA_KillDrawing(void);

void VGA_SetOverride(bool vga_override);
Expand Down
53 changes: 24 additions & 29 deletions src/capture/capture_video.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ static constexpr auto AviHeaderSize = 500;
static struct {
FILE* handle = nullptr;

uint32_t frames = 0;
VideoCodec* codec = nullptr;
int width = 0;
int height = 0;
uint8_t bits_per_pixel = 0;
float frames_per_second = 0.0f;
uint32_t frames = 0;
VideoCodec* codec = nullptr;
int width = 0;
int height = 0;
PixelFormat pixel_format = {};
float frames_per_second = 0.0f;

uint32_t written = 0;
uint32_t buf_size = 0;
Expand Down Expand Up @@ -286,7 +286,7 @@ void capture_video_add_audio_data(const uint32_t sample_rate,
}

static void create_avi_file(const uint16_t width, const uint16_t height,
const uint8_t bits_per_pixel,
const PixelFormat pixel_format,
const float frames_per_second, ZMBV_FORMAT format)
{
video.handle = CAPTURE_CreateFile(CaptureType::Video);
Expand All @@ -306,7 +306,7 @@ static void create_avi_file(const uint16_t width, const uint16_t height,

video.width = width;
video.height = height;
video.bits_per_pixel = bits_per_pixel;
video.pixel_format = pixel_format;
video.frames_per_second = frames_per_second;

for (auto i = 0; i < AviHeaderSize; ++i) {
Expand All @@ -329,32 +329,32 @@ void capture_video_add_frame(const RenderedImage& image, const float frames_per_

// Disable capturing if any of the test fails
if (video.handle && (video.width != video_width || video.height != video_height ||
video.bits_per_pixel != image.bits_per_pixel ||
video.pixel_format != image.pixel_format ||
video.frames_per_second != frames_per_second)) {
capture_video_finalise();
}

ZMBV_FORMAT format;

switch (image.bits_per_pixel) {
case 8: format = ZMBV_FORMAT::BPP_8; break;
case 15: format = ZMBV_FORMAT::BPP_15; break;
case 16: format = ZMBV_FORMAT::BPP_16; break;
switch (image.pixel_format) {
case PixelFormat::Indexed8: format = ZMBV_FORMAT::BPP_8; break;
case PixelFormat::BGR555: format = ZMBV_FORMAT::BPP_15; break;
case PixelFormat::BGR565: format = ZMBV_FORMAT::BPP_16; break;

// ZMBV is "the DOSBox capture format" supported by external
// tools such as VLC, MPV, and ffmpeg. Because DOSBox originally
// didn't have 24-bit color, the format itself doesn't support
// it. I this case we tell ZMBV the data is 32-bit and let the
// rgb24's int() cast operator up-convert.
case 24: format = ZMBV_FORMAT::BPP_32; break;
case 32: format = ZMBV_FORMAT::BPP_32; break;
default: assertm(false, "Invalid bits_per_pixel value"); return;
case PixelFormat::BGR888: format = ZMBV_FORMAT::BPP_32; break;
case PixelFormat::BGRX8888: format = ZMBV_FORMAT::BPP_32; break;
default: assertm(false, "Invalid pixel_format value"); return;
}

if (!video.handle) {
create_avi_file(video_width,
video_height,
image.bits_per_pixel,
image.pixel_format,
frames_per_second,
format);
}
Expand Down Expand Up @@ -382,20 +382,17 @@ void capture_video_add_frame(const RenderedImage& image, const float frames_per_
// TODO This all assumes little-endian byte order; should be
// made endianness-aware like capture_image.cpp
if (image.double_width) {
switch (image.bits_per_pixel) {
// Indexed8
case 8:
switch (image.pixel_format) {
case PixelFormat::Indexed8:
for (auto x = 0; x < image.width; ++x) {
const auto pixel = src_row[x];
row_buffer[x * 2 + 0] = pixel;
row_buffer[x * 2 + 1] = pixel;
}
break;

// BGR555
case 15:
// BGR565
case 16:
case PixelFormat::BGR555:
case PixelFormat::BGR565:
for (auto x = 0; x < image.width; ++x) {
const auto pixel = ((uint16_t*)src_row)[x];

Expand All @@ -404,8 +401,7 @@ void capture_video_add_frame(const RenderedImage& image, const float frames_per_
}
break;

// BGR888
case 24:
case PixelFormat::BGR888:
for (auto x = 0; x < image.width; ++x) {
const auto pixel = reinterpret_cast<const Rgb888*>(
src_row)[x];
Expand All @@ -417,8 +413,7 @@ void capture_video_add_frame(const RenderedImage& image, const float frames_per_
}
break;

// BGRX8888
case 32:
case PixelFormat::BGRX8888:
for (auto x = 0; x < image.width; ++x) {
const auto pixel = ((uint32_t*)src_row)[x];

Expand All @@ -430,7 +425,7 @@ void capture_video_add_frame(const RenderedImage& image, const float frames_per_
row_pointer = row_buffer;

} else {
if (image.bits_per_pixel == 24) {
if (image.pixel_format == PixelFormat::BGR888) {
for (auto x = 0; x < image.width; ++x) {
const auto pixel = reinterpret_cast<const Rgb888*>(
src_row)[x];
Expand Down
4 changes: 0 additions & 4 deletions src/capture/image/image_decoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,6 @@ void ImageDecoder::Init(const RenderedImage& image, const uint8_t row_skip_count
assert(image.width > 0);
assert(image.height > 0);

assert(image.bits_per_pixel == 8 || image.bits_per_pixel == 15 ||
image.bits_per_pixel == 16 || image.bits_per_pixel == 24 ||
image.bits_per_pixel == 32);

assert(image.pitch >= image.width);
assert(image.pixel_aspect_ratio.ToDouble() >= 0.0);
assert(image.image_data);
Expand Down
35 changes: 14 additions & 21 deletions src/capture/image/image_decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "rgb565.h"
#include "rgb888.h"
#include "support.h"
#include "vga.h"

class ImageDecoder {
public:
Expand Down Expand Up @@ -76,21 +77,13 @@ class ImageDecoder {

inline void IncrementPos()
{
switch (image.bits_per_pixel) {
case 8: // Indexed8
++pos;
break;
case 15: // BGR555
case 16: // BGR565
pos += 2;
break;
case 24: // BGR888
pos += 3;
break;
case 32: // XBGR8888
pos += 4;
break;
default: assertm(false, "Invalid bits_per_pixel value");
switch (image.pixel_format) {
case PixelFormat::Indexed8: ++pos; break;
case PixelFormat::BGR555:
case PixelFormat::BGR565: pos += 2; break;
case PixelFormat::BGR888: pos += 3; break;
case PixelFormat::BGRX8888: pos += 4; break;
default: assertm(false, "Invalid pixel_format value");
}
}

Expand All @@ -111,28 +104,28 @@ class ImageDecoder {
{
Rgb888 pixel = {};

switch (image.bits_per_pixel) {
case 15: { // BGR555
switch (image.pixel_format) {
case PixelFormat::BGR555: {
const auto p = host_to_le(
*reinterpret_cast<const uint16_t*>(pos));
pixel = Rgb555(p).ToRgb888();
} break;

case 16: { // BGR565
case PixelFormat::BGR565: {
const auto p = host_to_le(
*reinterpret_cast<const uint16_t*>(pos));
pixel = Rgb565(p).ToRgb888();
} break;

case 24: // BGR888
case 32: { // XBGR8888
case PixelFormat::BGR888:
case PixelFormat::BGRX8888: {
const auto b = *(pos + 0);
const auto g = *(pos + 1);
const auto r = *(pos + 2);

pixel = {r, g, b};
} break;
default: assertm(false, "Invalid bits_per_pixel value");
default: assertm(false, "Invalid pixel_format value");
}

IncrementPos();
Expand Down
4 changes: 2 additions & 2 deletions src/capture/image/image_saver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ static void write_upscaled_png(FILE* outfile, PngWriter& png_writer,
const uint8_t* palette_data)
{
switch (image_scaler.GetOutputPixelFormat()) {
case PixelFormat::Indexed8:
case OutputPixelFormat::Indexed8:
if (!png_writer.InitIndexed8(outfile,
width,
height,
Expand All @@ -141,7 +141,7 @@ static void write_upscaled_png(FILE* outfile, PngWriter& png_writer,
return;
}
break;
case PixelFormat::Rgb888:
case OutputPixelFormat::Rgb888:
if (!png_writer.InitRgb888(
outfile, width, height, pixel_aspect_ratio, video_mode)) {
return;
Expand Down
Loading