Skip to content

Commit a96ab10

Browse files
committed
Fix raw video capture for 160x200 16-colour Tandy/PCjr modes
Before this change, raw video captures of the 160x200 16-colour Tandy/PCjr video modes were written width-doubled as 320x200.
1 parent 61f3fb6 commit a96ab10

File tree

2 files changed

+36
-34
lines changed

2 files changed

+36
-34
lines changed

src/capture/capture_video.cpp

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -364,14 +364,10 @@ static void create_avi_file(const uint16_t width, const uint16_t height,
364364
// We always write non-double-scanned and non-pixel-doubled frames in raw
365365
// video capture mode :
366366
//
367-
// - Pixel-doubling is not a problem as that's always performed as a
368-
// post-processing step; it's never "baked-into" the rendered image.
369-
// We just need to completely ignore the `double_width` flag.
370-
//
371-
// - Double scanning can be either "baked-in" or performed in post-processing.
372-
// Ignoring the `double_height` flag takes care of the post-processing
373-
// variety, but for "baked-in" double scanning, we need to skip every second
374-
// row.
367+
// Double scanning and pixel doubling can be either "baked-in" or performed in
368+
// post-processing. Ignoring the `double_height` and `double_width` flags
369+
// takes care of the post-processing variety, but for the "baked-in" variants
370+
// we need to skip every second row or pixel.
375371
//
376372
// So, for example, the 320x200 13h VGA mode is always written as 320x200 in
377373
// raw capture mode regardless of the state of the width & height doubling
@@ -388,15 +384,16 @@ static void compress_raw_frame(const RenderedImage& image)
388384
const auto& src = image.params;
389385
auto src_row = image.image_data;
390386

391-
// We always write non-double-scanned frames in raw video capture mode.
392-
// Therefore, to reconstruct the raw image, we must skip every second
393-
// row if we're dealing with "baked in" double scanning.
387+
// To reconstruct the raw image, we must skip every second row
388+
// when dealing with "baked-in" double scanning.
389+
const auto raw_height = (src.height / (src.rendered_double_scan ? 2 : 1));
390+
const auto src_pitch = (image.pitch * (src.rendered_double_scan ? 2 : 1));
394391

395-
const auto raw_height = (src.height /
396-
(image.params.rendered_double_scan ? 2 : 1));
392+
// To reconstruct the raw image, we must skip every second pixel
393+
// when dealing with "baked-in" pixel doubling.
394+
const auto raw_width = (src.width / (src.rendered_pixel_doubling ? 2 : 1));
397395

398-
const auto src_pitch = (image.pitch *
399-
(image.params.rendered_double_scan ? 2 : 1));
396+
const auto pixel_skip_count = (src.rendered_pixel_doubling ? 1 : 0);
400397

401398
auto compress_row = [&](const uint8_t* row_buffer) {
402399
video.codec->CompressLines(1, &row_buffer);
@@ -409,7 +406,8 @@ static void compress_raw_frame(const RenderedImage& image)
409406
// that this is a shortcut scenario; hard-code it to false to exercise
410407
// the rote version below.
411408

412-
const auto can_use_src_directly = (src_bpp == dest_bpp);
409+
const auto can_use_src_directly = (src_bpp == dest_bpp &&
410+
pixel_skip_count == 0);
413411
if (can_use_src_directly) {
414412
for (auto i = 0; i < raw_height; ++i, src_row += src_pitch) {
415413
compress_row(src_row);
@@ -427,10 +425,13 @@ static void compress_raw_frame(const RenderedImage& image)
427425
dest_row.resize(dest_row_bytes, 0);
428426
}
429427

428+
const auto src_advance = src_bpp * (pixel_skip_count + 1);
429+
430430
for (auto i = 0; i < raw_height; ++i, src_row += src_pitch) {
431431
auto src_pixel = src_row;
432432
auto dest_pixel = dest_row.data();
433-
for (auto j = 0; j < src.width; ++j, src_pixel += src_bpp) {
433+
434+
for (auto j = 0; j < raw_width; ++j, src_pixel += src_advance) {
434435
std::memcpy(dest_pixel, src_pixel, src_bpp);
435436
dest_pixel += dest_bpp;
436437
}
@@ -443,30 +444,31 @@ void capture_video_add_frame(const RenderedImage& image, const float frames_per_
443444
const auto& src = image.params;
444445
assert(src.width <= SCALER_MAXWIDTH);
445446

446-
// Pixel-doubling is never "baked-in", so we always write the frames
447-
// non-pixel-doubled. The only exception is composite modes; these are
448-
// rendered at 2x width and we want to write them as such (we need enough
449-
// horizontal resolution to properly represent the composite artifacts).
450-
const auto video_width = src.width;
447+
// To reconstruct the raw image, we must skip every second row when
448+
// dealing with "baked-in" double scanning.
449+
const auto raw_width = check_cast<uint16_t>(
450+
src.width / (src.rendered_pixel_doubling ? 2 : 1));
451451

452-
// We always write non-double-scanned frames in raw video capture mode
453-
const auto video_height = src.video_mode.height;
452+
// To reconstruct the raw image, we must skip every second pixel
453+
// when dealing with "baked-in" pixel doubling.
454+
const auto raw_height = check_cast<uint16_t>(
455+
src.height / (src.rendered_double_scan ? 2 : 1));
454456

455457
// Disable capturing if any of the test fails
456-
if (video.handle && (video.width != video_width || video.height != video_height ||
458+
if (video.handle && (video.width != raw_width || video.height != raw_height ||
457459
video.pixel_format != src.pixel_format ||
458460
video.frames_per_second != frames_per_second)) {
459461
capture_video_finalise();
460462
}
461463

462-
const auto format = to_zmbv_format(src.pixel_format);
464+
const auto zmbv_format = to_zmbv_format(src.pixel_format);
463465

464466
if (!video.handle) {
465-
create_avi_file(video_width,
466-
video_height,
467+
create_avi_file(raw_width,
468+
raw_height,
467469
src.pixel_format,
468470
frames_per_second,
469-
format);
471+
zmbv_format);
470472
}
471473
if (!video.handle) {
472474
return;
@@ -475,7 +477,7 @@ void capture_video_add_frame(const RenderedImage& image, const float frames_per_
475477
const auto codec_flags = (video.frames % 300 == 0) ? 1 : 0;
476478

477479
if (!video.codec->PrepareCompressFrame(codec_flags,
478-
format,
480+
zmbv_format,
479481
image.palette_data,
480482
video.buf.data(),
481483
video.buf_size)) {

src/capture/image/image_saver.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,15 +154,15 @@ void ImageSaver::SaveRawImage(const RenderedImage& image)
154154
{
155155
PngWriter png_writer = {};
156156

157+
const auto& src = image.params;
158+
157159
// To reconstruct the raw image, we must skip every second row when
158160
// dealing with "baked-in" double scanning.
159-
const uint8_t row_skip_count = (image.params.rendered_double_scan ? 1 : 0);
161+
const uint8_t row_skip_count = (src.rendered_double_scan ? 1 : 0);
160162

161163
// To reconstruct the raw image, we must skip every second pixel when
162164
// dealing with "baked-in" pixel doubling.
163-
const uint8_t pixel_skip_count = (image.params.rendered_pixel_doubling ? 1 : 0);
164-
165-
const auto& src = image.params;
165+
const uint8_t pixel_skip_count = (src.rendered_pixel_doubling ? 1 : 0);
166166

167167
const auto output_width = check_cast<uint16_t>(src.width /
168168
(pixel_skip_count + 1));

0 commit comments

Comments
 (0)