Files
mxl-multiviewer/NoSignalFeed.cpp
T
2026-05-18 00:03:06 +03:00

221 lines
4.9 KiB
C++

#include "NoSignalFeed.hpp"
#include "PixelFormat.hpp"
namespace
{
constexpr uint32_t TileBorderPixels = 0;
uint32_t glyphRows(char c, uint32_t row)
{
switch (c)
{
case 'A':
{
constexpr uint32_t rows[] =
{0x0E, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11};
return rows[row];
}
case 'G':
{
constexpr uint32_t rows[] =
{0x0E, 0x11, 0x10, 0x17, 0x11, 0x11, 0x0E};
return rows[row];
}
case 'I':
{
constexpr uint32_t rows[] =
{0x1F, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1F};
return rows[row];
}
case 'L':
{
constexpr uint32_t rows[] =
{0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1F};
return rows[row];
}
case 'N':
{
constexpr uint32_t rows[] =
{0x11, 0x19, 0x15, 0x13, 0x11, 0x11, 0x11};
return rows[row];
}
case 'O':
{
constexpr uint32_t rows[] =
{0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E};
return rows[row];
}
case 'S':
{
constexpr uint32_t rows[] =
{0x0F, 0x10, 0x10, 0x0E, 0x01, 0x01, 0x1E};
return rows[row];
}
default:
return 0;
}
}
void drawText(
VideoFrame& frame,
const char* text,
uint32_t originX,
uint32_t originY,
uint32_t scale,
RgbColor color)
{
for (uint32_t charIndex = 0; text[charIndex] != '\0'; ++charIndex)
{
const char c = text[charIndex];
if (c == ' ')
{
continue;
}
const uint32_t charX =
originX + charIndex * 6 * scale;
for (uint32_t gy = 0; gy < 7; ++gy)
{
const uint32_t bits = glyphRows(c, gy);
for (uint32_t gx = 0; gx < 5; ++gx)
{
if ((bits & (1u << (4 - gx))) == 0)
{
continue;
}
for (uint32_t sy = 0; sy < scale; ++sy)
{
for (uint32_t sx = 0; sx < scale; ++sx)
{
const uint32_t x =
charX + gx * scale + sx;
const uint32_t y =
originY + gy * scale + sy;
if (x < frame.width && y < frame.height)
{
frame.pixels[y * frame.width + x] =
packRgba8(color);
}
}
}
}
}
}
}
void fillNoSignalFrame(VideoFrame& frame)
{
frame.pixels.resize(frame.width * frame.height);
for (uint32_t y = 0; y < frame.height; ++y)
{
for (uint32_t x = 0; x < frame.width; ++x)
{
const bool isBorder =
x < TileBorderPixels ||
y < TileBorderPixels ||
x >= frame.width - TileBorderPixels ||
y >= frame.height - TileBorderPixels;
const bool hatch =
((x + y) / 18) % 2 == 0;
const RgbColor color =
isBorder
? RgbColor{95, 100, 110}
: (hatch
? RgbColor{18, 20, 24}
: RgbColor{12, 14, 18});
frame.pixels[y * frame.width + x] =
packRgba8(color);
}
}
const uint32_t scale =
frame.width >= 512 ? 5 : 3;
constexpr uint32_t textChars = 9;
const uint32_t textWidth =
(textChars * 6 - 1) * scale;
const uint32_t textHeight = 7 * scale;
const uint32_t textX =
frame.width > textWidth
? (frame.width - textWidth) / 2
: 0;
const uint32_t textY =
frame.height > textHeight
? (frame.height - textHeight) / 2
: 0;
drawText(
frame,
"NO SIGNAL",
textX,
textY,
scale,
{210, 215, 225}
);
}
}
NoSignalFeed::NoSignalFeed(
uint32_t feedIndex,
uint32_t frameWidth,
uint32_t frameHeight)
{
configure(
feedIndex,
frameWidth,
frameHeight
);
}
void NoSignalFeed::configure(
uint32_t feedIndex,
uint32_t frameWidth,
uint32_t frameHeight)
{
(void)feedIndex;
generated = false;
version = 0;
frame.width = frameWidth;
frame.height = frameHeight;
frame.pixels.resize(frame.width * frame.height);
}
const VideoFrame& NoSignalFeed::getFrame(uint32_t frameCounter)
{
(void)frameCounter;
if (!generated)
{
fillNoSignalFrame(frame);
generated = true;
++version;
}
return frame;
}
FeedRuntimeStatus NoSignalFeed::status() const
{
return FeedRuntimeStatus::NoSignal;
}
std::string NoSignalFeed::displayName() const
{
return "NO SIGNAL";
}