Skip to content

Commit bec6d47

Browse files
committed
Add other experimental U32 renderer
1 parent c12c1bd commit bec6d47

File tree

7 files changed

+741
-1
lines changed

7 files changed

+741
-1
lines changed

src/CDI/Video/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ target_sources(CeDImu
77
# RendererSIMD.hpp
88
RendererSoftware.cpp
99
RendererSoftware.hpp
10+
# RendererSoftwareU32.cpp
11+
# RendererSoftwareU32.hpp
1012
)

src/CDI/Video/RendererSoftwareU32.cpp

+265
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
#include "RendererSoftwareU32.hpp"
2+
3+
#include "../common/panic.hpp"
4+
#include "../common/utils.hpp"
5+
6+
#include <cstring>
7+
8+
namespace Video
9+
{
10+
11+
/** \brief Converts the 4-bits backdrop color to ARGB.
12+
* \param color The 4 bit color code.
13+
* \returns The ARGB color.
14+
*/
15+
static constexpr uint32_t backdropCursorColorToARGB(const uint8_t color) noexcept
16+
{
17+
// Background plane has no transparency (Green book V.5.13).
18+
const uint32_t c = (bit<3>(color)) ? Renderer::PIXEL_FULL_INTENSITY : Renderer::PIXEL_HALF_INTENSITY;
19+
uint32_t argb = 0xFF'00'00'00; // Set transparency for cursor plane.
20+
if(bit<2>(color)) argb |= c << 16; // Red.
21+
if(bit<1>(color)) argb |= c << 8; // Green.
22+
if(bit<0>(color)) argb |= c; // Blue.
23+
return argb;
24+
}
25+
26+
/** \brief Draws the next line to draw.
27+
* \param lineA Line A data.
28+
* \param lineB Line B data.
29+
* \return The number of bytes read from memory for each plane `<plane A, plane B>`.
30+
*/
31+
std::pair<uint16_t, uint16_t> RendererSoftwareU32::DrawLine(const uint8_t* lineA, const uint8_t* lineB) noexcept
32+
{
33+
ResetMatte();
34+
35+
const uint16_t bytesA = DrawLinePlane<A>(lineA, nullptr); // nullptr because plane A can't decode RGB555.
36+
const uint16_t bytesB = DrawLinePlane<B>(lineB, lineA);
37+
38+
DrawLineBackdrop();
39+
40+
if(m_mix)
41+
if(m_planeOrder)
42+
OverlayMix<true, true>();
43+
else
44+
OverlayMix<true, false>();
45+
else
46+
if(m_planeOrder)
47+
OverlayMix<false, true>();
48+
else
49+
OverlayMix<false, false>();
50+
51+
m_lineNumber++;
52+
return std::make_pair(bytesA, bytesB);
53+
}
54+
55+
/** \brief To be called when the whole frame is drawn.
56+
* \return The final screen.
57+
*
58+
* This function renders the cursor and pastes it on the screen.
59+
* It also resets some members to prepare for the next frame.
60+
*/
61+
const Plane& RendererSoftwareU32::RenderFrame() noexcept
62+
{
63+
// Should this be inside DrawLine() ?
64+
if(m_cursorEnabled)
65+
{
66+
DrawCursor();
67+
Video::paste(m_screen.data(), m_screen.m_width, m_screen.m_height, m_cursorPlane.data(), m_cursorPlane.m_width, m_cursorPlane.m_height, m_cursorX, m_cursorY);
68+
}
69+
70+
// TODO: this should be on the GUI side.
71+
Plane::iterator dstit = m_screen.begin();
72+
PlaneU32::const_iterator it = m_screenARGB.cbegin();
73+
for(size_t i = 0; i < m_screenARGB.PixelCount(); ++i, ++it, dstit += 3)
74+
{
75+
const Pixel pixel = *it;
76+
dstit[0] = pixel >> 16;
77+
dstit[1] = pixel >> 8;
78+
dstit[2] = pixel;
79+
}
80+
81+
m_lineNumber = 0;
82+
return m_screen;
83+
}
84+
85+
/** \brief Draws the line of the given plane.
86+
* \param lineMain Line that will be decoded.
87+
* \param lineA Line A data if RGB555.
88+
* \return The number of bytes read from memory.
89+
*
90+
* lineA is only used when the decoding method is RGB555.
91+
*/
92+
template<Renderer::ImagePlane PLANE>
93+
uint16_t RendererSoftwareU32::DrawLinePlane(const uint8_t* lineMain, const uint8_t* lineA) noexcept
94+
{
95+
if(m_codingMethod[PLANE] == ImageCodingMethod::OFF)
96+
{
97+
const size_t length = m_plane[PLANE].m_width * m_plane[PLANE].m_bpp;
98+
memset(m_planeLine[PLANE].data(), 0, length);
99+
return 0;
100+
}
101+
102+
const bool is4BPP = false; // TODO.
103+
104+
const uint32_t* clut;
105+
if constexpr(PLANE == A)
106+
clut = m_codingMethod[A] == ICM(CLUT77) && m_clutSelectHigh ? &m_clut[128] : m_clut.data();
107+
else
108+
clut = &m_clut[128];
109+
110+
switch(m_imageType[PLANE])
111+
{
112+
case ImageType::Normal:
113+
return decodeBitmapLineU32(m_planeLine[PLANE].data(), lineA, lineMain, m_plane[PLANE].m_width, clut, m_dyuvInitialValue[PLANE], m_codingMethod[PLANE]);
114+
115+
case ImageType::RunLength:
116+
return decodeRunLengthLineU32(m_planeLine[PLANE].data(), lineMain, m_plane[PLANE].m_width, clut, is4BPP);
117+
118+
case ImageType::Mosaic:
119+
panic("Unsupported type Mosaic");
120+
return 0;
121+
}
122+
123+
std::unreachable();
124+
}
125+
126+
void RendererSoftwareU32::DrawLineBackdrop() noexcept
127+
{
128+
// The pixels of a line are all the same, so backdrop plane only contains the color of each line.
129+
m_backdropColorARGB = backdropCursorColorToARGB(m_backdropColor);
130+
}
131+
132+
void RendererSoftwareU32::DrawCursor() noexcept
133+
{
134+
// Technically speaking the cursor is drawn when the drawing line number is the cursor's one (because video
135+
// is outputted continuously line by line).
136+
// But for here maybe we don't care.
137+
const Pixel color = backdropCursorColorToARGB(m_cursorColor);
138+
const Pixel black{0};
139+
140+
int pattern = 0;
141+
for(PlaneU32::iterator it = m_cursorPlaneARGB.begin(); it < m_cursorPlaneARGB.end();)
142+
{
143+
for(int x = 0; x < m_cursorPlaneARGB.m_width; ++x)
144+
{
145+
const uint16_t mask = (1 << x);
146+
if(m_cursorPatterns[pattern] & mask)
147+
*it = color;
148+
else
149+
*it = black;
150+
++it;
151+
}
152+
153+
++pattern;
154+
}
155+
}
156+
157+
static constexpr uint8_t intByteMult(uint32_t color1, uint32_t color2) noexcept {
158+
return static_cast<uint8_t>(((color1 * (color2 | color2 << 8)) + 0x8080) >> 16);
159+
}
160+
161+
/** \brief Apply the given Image Contribution Factor to the given color component (V.5.9). */
162+
static constexpr uint32_t applyICFComponent(const int color, const int icf) noexcept
163+
{
164+
return static_cast<uint8_t>(((icf * (color - 16)) / 63) + 16);
165+
}
166+
167+
/** \brief Apply the given Image Contribution Factor to the given color component (V.5.9). */
168+
static constexpr Pixel applyICF(const Pixel pixel, const int icf) noexcept
169+
{
170+
// return intByteMult((icf >> 4) + (icf << 2), pixel - 16) + 16;
171+
172+
uint8_t r = pixel >> 16;
173+
uint8_t g = pixel >> 8;
174+
uint8_t b = pixel;
175+
176+
return (pixel & 0xFF'00'00'00) | applyICFComponent(r, icf) << 16 | applyICFComponent(g, icf) << 8 | applyICFComponent(b, icf);
177+
}
178+
179+
/** \brief Apply mixing to the given color components after ICF (V.5.9.1). */
180+
static constexpr Pixel mix(const Pixel a, const Pixel b) noexcept
181+
{
182+
// return limu8(a + b - 16);
183+
184+
uint8_t ra = a >> 16;
185+
uint8_t ga = a >> 8;
186+
uint8_t ba = a;
187+
188+
uint8_t rb = b >> 16;
189+
uint8_t gb = b >> 8;
190+
uint8_t bb = b;
191+
192+
return limu8(ra + rb - 16) << 16 | limu8(ga + gb - 16) << 8 | limu8(ba + bb - 16);
193+
}
194+
195+
/** \brief Overlays or mix all the planes to the final screen.
196+
* \tparam MIX true to use mixing, false to use overlay.
197+
* \tparam PLANE_ORDER true when plane B in front of plane A, false for A in front of B.
198+
*/
199+
template<bool MIX, bool PLANE_ORDER>
200+
void RendererSoftwareU32::OverlayMix() noexcept
201+
{
202+
Pixel* screen = m_screenARGB.GetLinePointer(m_lineNumber);
203+
Pixel* planeA = m_planeLine[A].data();
204+
Pixel* planeB = m_planeLine[B].data();
205+
206+
for(uint16_t i = 0; i < m_plane[A].m_width; i++) // TODO: width[B].
207+
{
208+
HandleMatte<A>(i);
209+
HandleMatte<B>(i);
210+
211+
HandleTransparencyU32<A>(*planeA);
212+
HandleTransparencyU32<B>(*planeB);
213+
214+
Pixel pa = applyICF(*planeA++, m_icf[A]);
215+
Pixel pb = applyICF(*planeB++, m_icf[B]);
216+
217+
Pixel fp, bp;
218+
if constexpr(PLANE_ORDER) // Plane B in front.
219+
{
220+
fp = pb;
221+
bp = pa;
222+
}
223+
else // Plane A in front.
224+
{
225+
fp = pa;
226+
bp = pb;
227+
}
228+
229+
Pixel pixel;
230+
if constexpr(MIX)
231+
{
232+
if((bp & 0xFF'00'00'00) == 0) // When mixing transparent pixels are black (V.5.9.1).
233+
{
234+
bp = BLACK_PIXEL;
235+
}
236+
237+
if((fp & 0xFF'00'00'00) == 0)
238+
{
239+
fp = BLACK_PIXEL;
240+
}
241+
242+
pixel = mix(fp, bp);
243+
}
244+
else // Overlay.
245+
{
246+
// Plane transparency is either 0 or 255.
247+
if((fp & 0xFF'00'00'00) == 0 && (bp & 0xFF'00'00'00) == 0) // Front and back plane transparent: only show background.
248+
{
249+
pixel = m_backdropColorARGB;
250+
}
251+
else if((fp & 0xFF'00'00'00) == 0) // Front plane transparent: show back plane.
252+
{
253+
pixel = bp;
254+
}
255+
else // Front plane visible: only show front plane.
256+
{
257+
pixel = fp;
258+
}
259+
}
260+
261+
*screen++ = pixel;
262+
}
263+
}
264+
265+
} // namespace Video

src/CDI/Video/RendererSoftwareU32.hpp

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#ifndef CDI_VIDEO_RENDERERSOFTWAREU32_HPP
2+
#define CDI_VIDEO_RENDERERSOFTWAREU32_HPP
3+
4+
#include "Renderer.hpp"
5+
#include "../common/VideoU32.hpp"
6+
7+
namespace Video
8+
{
9+
10+
/** \brief CD-i video renderer implementation using only C++ code and uint32_t to represent pixels.
11+
*/
12+
class RendererSoftwareU32 final : public Renderer
13+
{
14+
public:
15+
static constexpr Pixel BLACK_PIXEL = 0x00'10'10'10;
16+
17+
std::array<std::array<Pixel, PlaneU32::MAX_WIDTH>, 2> m_planeLine{};
18+
PlaneU32 m_screenARGB{384, 280, PlaneU32::MAX_SIZE};
19+
Pixel m_backdropColorARGB{0};
20+
PlaneU32 m_cursorPlaneARGB{PlaneU32::CURSOR_WIDTH, PlaneU32::CURSOR_HEIGHT, PlaneU32::CURSOR_SIZE};
21+
22+
RendererSoftwareU32() {}
23+
virtual ~RendererSoftwareU32() noexcept {}
24+
25+
std::pair<uint16_t, uint16_t> DrawLine(const uint8_t* lineA, const uint8_t* lineB) noexcept override;
26+
const Plane& RenderFrame() noexcept override;
27+
28+
template<ImagePlane PLANE>
29+
uint16_t DrawLinePlane(const uint8_t* lineMain, const uint8_t* lineA) noexcept;
30+
void DrawLineBackdrop() noexcept;
31+
void DrawCursor() noexcept;
32+
33+
template<bool MIX, bool PLANE_ORDER> void OverlayMix() noexcept;
34+
35+
/** \brief Handles the transparency of the current pixel for each plane.
36+
* \param pixel The ARGB pixel.
37+
*/
38+
template<ImagePlane PLANE> void HandleTransparencyU32(uint32_t& pixel) noexcept
39+
{
40+
static constexpr uint32_t VISIBLE = 0xFF'00'00'00;
41+
42+
const bool boolean = !bit<3>(m_transparencyControl[PLANE]);
43+
uint32_t color = pixel & 0x00'FF'FF'FF;
44+
color = clutColorKey(color | m_maskColorRgb[PLANE]);
45+
const bool colorKey = color == clutColorKey(m_transparentColorRgb[PLANE] | m_maskColorRgb[PLANE]); // TODO: don't compute if not CLUT.
46+
47+
pixel |= VISIBLE;
48+
49+
switch(bits<0, 2>(m_transparencyControl[PLANE]))
50+
{
51+
case 0b000: // Always/Never.
52+
if(boolean)
53+
pixel &= ~VISIBLE;
54+
break;
55+
56+
case 0b001: // Color Key.
57+
if(colorKey == boolean)
58+
pixel &= ~VISIBLE;
59+
break;
60+
61+
case 0b010: // Transparent Bit.
62+
// TODO: currently decodeRGB555 make the pixel visible if the bit is set.
63+
// TODO: disable if not RGB555.
64+
if(((pixel & VISIBLE) == VISIBLE) != boolean)
65+
pixel &= ~VISIBLE;
66+
break;
67+
68+
case 0b011: // Matte Flag 0.
69+
if(m_matteFlags[0] == boolean)
70+
pixel &= ~VISIBLE;
71+
break;
72+
73+
case 0b100: // Matte Flag 1.
74+
if(m_matteFlags[1] == boolean)
75+
pixel &= ~VISIBLE;
76+
break;
77+
78+
case 0b101: // Matte Flag 0 or Color Key.
79+
if(m_matteFlags[0] == boolean || colorKey == boolean)
80+
pixel &= ~VISIBLE;
81+
break;
82+
83+
case 0b110: // Matte Flag 1 or Color Key.
84+
if(m_matteFlags[1] == boolean || colorKey == boolean)
85+
pixel &= ~VISIBLE;
86+
break;
87+
88+
default: // Reserved.
89+
break;
90+
}
91+
}
92+
};
93+
94+
} // namespace Video
95+
96+
#endif // CDI_VIDEO_RENDERERSOFTWAREU32_HPP

src/CDI/common/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@ target_sources(CeDImu
1111
Video.hpp
1212
# VideoSIMD.cpp
1313
# VideoSIMD.hpp
14+
# VideoU32.cpp
15+
# VideoU32.hpp
1416
)

0 commit comments

Comments
 (0)