LUMIERA.clone/src/steam/engine/worker/dummy-image-generator.cpp
Ichthyostega 8abeb02397 XV-Display: prototype for flexible control of ouput pixel format
As an example, the `PixbufDisplayer` needs packed RGB888 data,
while the `XvDisplayer` expects YUV (MPEG-style) pixels.

The research setup is not well equipped to handle any kind of content
or format negotiation; yet for the experimentation, the connections can
be wired as !SigC-Signals. After the preceding refactorings,
`DummyImageGenerator` can be configured to perform the conversion to YUV
only when necessary, and to use the working buffer flexibly.

When supplied with packed RGB pixel data, the display in the Gtk::Image
is now correct, and also handles layout and scaling appropriately.
2025-05-12 03:43:09 +02:00

185 lines
5.2 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
DummyImageGenerator - creating test output frames for simulated playback
Copyright (C)
2009, Joel Holdsworth <joel@airwebreathe.org.uk>,
Hermann Vosseler <Ichthyostega@web.de>
  **Lumiera** is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 2 of the License, or (at your
  option) any later version. See the file COPYING for further details.
* *****************************************************************/
/** @file dummy-image-generator.cpp
** Implementation of a generator for dummy video frames for a mockup
** @todo obsolete since 2010, can be removed once we have a real player in the UI
*/
#include "steam/engine/worker/dummy-image-generator.hpp"
#include <algorithm>
namespace steam {
namespace node {
using std::byte;
using std::clamp;
namespace { // implementation details
using Trip = std::array<byte,3>;
/** slightly simplified conversion from RGB components to Y'CbCr with Rec.601 (MPEG style) */
inline Trip
rgb_to_yuv (Trip const& rgb)
{
auto r = int(rgb[0]);
auto g = int(rgb[1]);
auto b = int(rgb[2]);
Trip yuv;
auto& [y,u,v] = yuv;
y = byte(clamp ( 0 + ( 299 * r + 587 * g + 114 * b) / 1000, 16,235)); // Luma clamped to MPEG scan range
u = byte(clamp (128 + (-168736 * r - 331264 * g + 500000 * b) / 1000000, 0, 255)); // Chroma components mapped according to Rec.601
v = byte(clamp (128 + ( 500000 * r - 418688 * g - 81312 * b) / 1000000, 0, 255)); // (but with integer arithmetics and slightly rounded coefficients)
return yuv;
}
void
rgb_buffer_to_yuy2 (byte* in, byte* out, uint cntPix)
{
REQUIRE (cntPix %2 == 0);
for (uint i = 0; i < cntPix; i += 2)
{// convert and interleave 2 pixels in one step
uint ip = i * 3; // Input uses (RGB)-triples
uint op = i * 2; // Output packed in groups with 2 bytes
Trip& rgb0 = reinterpret_cast<Trip&> (in[ip]);
Trip& rgb1 = reinterpret_cast<Trip&> (in[ip+3]);
Trip yuv0 = rgb_to_yuv (rgb0);
Trip yuv1 = rgb_to_yuv (rgb1);
auto& [y0,u0,v0] = yuv0;
auto& [y1,_u,_v] = yuv1; // note: this format discards half of the chroma information
out[op ] = y0;
out[op + 1] = u0;
out[op + 2] = y1;
out[op + 3] = v0;
} }
} // (End) implementation details
DummyImageGenerator::DummyImageGenerator(uint fps)
: fps_{fps}
, useRGB_{false}
, beat_{false}
, frame_{0}
{ }
void
DummyImageGenerator::configure (lumiera::DisplayerInput displayFormat)
{
INFO (steam, "ImageGen: use format %d", displayFormat);
REQUIRE ( displayFormat == lumiera::DISPLAY_NONE
or displayFormat == lumiera::DISPLAY_YUV
or displayFormat == lumiera::DISPLAY_RGB);
useRGB_ = (displayFormat == lumiera::DISPLAY_RGB);
}
void
DummyImageGenerator::generateFrame (DummyFrame buffer)
{
++frame_;
if(frame_ > 2 * fps_)
frame_ = 0;
if(frame_ < 1 * fps_)
generateNoise (buffer);
else
generateBars (buffer);
}
void
DummyImageGenerator::generateNoise (DummyFrame buffer)
{ // create random snow...
for (uint i = 0; i < W*H*3; i+=3)
{
auto value = byte(rand());
buffer[i] = value;
buffer[i+1] = value;
buffer[i+2] = value;
}
}
/** fill the RGB-Buffer with the well-known NTSC colour-bar pattern */
void
DummyImageGenerator::generateBars (DummyFrame buffer)
{
using Row = std::array<Trip, W>;
using Img = std::array<Row, H>;
Img& img = reinterpret_cast<Img&> (*buffer);
Row& row = img[0];
const byte ON = byte(0xC0);
const byte OFF{0};
// classic NTSC colour bars --R---G---B--
std::array<Trip, 7> bars = {{{ ON, ON, ON}
,{ ON, ON,OFF}
,{OFF, ON, ON}
,{OFF, ON,OFF}
,{ ON,OFF, ON}
,{ ON,OFF,OFF}
,{OFF,OFF, ON}
}};
// create a colour strip pattern in the first row...
for (uint x = 0; x < W; ++x)
{ // quantise into 7 columns
uint col = x * 7/W;
row[x] = bars[col];
}
// fill remaining rows of the frame with the same pattern
for (uint y = 1; y < H; ++y)
img[y] = row;
}
DummyFrame
DummyImageGenerator::next()
{
beat_ = not beat_;
DummyFrame outBuff = current();
// next output buffer to return
DummyFrame workspace = useRGB_? outBuff : workBuf_.data();
generateFrame (workspace);
if (not useRGB_)
rgb_buffer_to_yuy2(workBuf_.data(), outBuff, W*H);
return outBuff;
}
DummyFrame
DummyImageGenerator::current()
{
return beat_? outFrame_A_.data() : outFrame_B_.data();
}
}} // namespace steam::node