openMSX
SpriteConverter.hh
Go to the documentation of this file.
1/*
2TODO:
3- Implement sprite pixels in Graphic 5.
4*/
5
6#ifndef SPRITECONVERTER_HH
7#define SPRITECONVERTER_HH
8
9#include "SpriteChecker.hh"
10#include "DisplayMode.hh"
11#include "view.hh"
12#include "narrow.hh"
13#include <concepts>
14#include <cstdint>
15#include <span>
16
17namespace openmsx {
18
21template<std::unsigned_integral Pixel>
23{
24public:
25 // TODO: Move some methods to .cc?
26
33 explicit SpriteConverter(SpriteChecker& spriteChecker_,
34 std::span<const Pixel, 16> pal)
35 : spriteChecker(spriteChecker_)
36 , palette(pal)
37 {
38 }
39
43 void setTransparency(bool enabled)
44 {
45 transparency = enabled;
46 }
47
52 {
53 mode = newMode;
54 }
55
61 void setPalette(std::span<const Pixel, 16> newPalette)
62 {
63 palette = newPalette;
64 }
65
66 static bool clipPattern(int& x, SpriteChecker::SpritePattern& pattern,
67 int minX, int maxX)
68 {
69 int before = minX - x;
70 if (before > 0) {
71 if (before >= 32) {
72 // 32 pixels before minX -> not visible
73 return false;
74 }
75 pattern <<= before;
76 x = minX;
77 }
78 int after = maxX - x;
79 if (after < 32) {
80 // close to maxX (or past)
81 if (after <= 0) {
82 // past maxX -> not visible
83 return false;
84 }
85 auto mask = narrow_cast<int>(0x80000000);
86 pattern &= (mask >> (after - 1));
87 }
88 return true; // visible
89 }
90
98 void drawMode1(int absLine, int minX, int maxX, std::span<Pixel> pixelPtr)
99 {
100 // Determine sprites visible on this line.
101 auto visibleSprites = spriteChecker.getSprites(absLine);
102 // Optimisation: return at once if no sprites on this line.
103 // Lines without any sprites are very common in most programs.
104 if (visibleSprites.empty()) return;
105
106 // Render using overdraw.
107 for (const auto& si : view::reverse(visibleSprites)) {
108 // Get sprite info.
109 Pixel colIndex = si.colorAttrib & 0x0F;
110 // Don't draw transparent sprites in sprite mode 1.
111 // Verified on real V9958: TP bit also has effect in
112 // sprite mode 1.
113 if (colIndex == 0 && transparency) continue;
114 Pixel color = palette[colIndex];
115 SpriteChecker::SpritePattern pattern = si.pattern;
116 int x = si.x;
117 // Clip sprite pattern to render range.
118 if (!clipPattern(x, pattern, minX, maxX)) continue;
119 // Convert pattern to pixels.
120 Pixel* p = &pixelPtr[x];
121 while (pattern) {
122 // Draw pixel if sprite has a dot.
123 if (pattern & 0x80000000) {
124 *p = color;
125 }
126 // Advancing behaviour.
127 pattern <<= 1;
128 p++;
129 }
130 }
131 }
132
143 template<unsigned MODE>
144 void drawMode2(int absLine, int minX, int maxX, std::span<Pixel> pixelPtr)
145 {
146 // Determine sprites visible on this line.
147 auto visibleSprites = spriteChecker.getSprites(absLine);
148 // Optimisation: return at once if no sprites on this line.
149 // Lines without any sprites are very common in most programs.
150 if (visibleSprites.empty()) return;
151 std::span visibleSpritesWithSentinel{visibleSprites.data(),
152 visibleSprites.size() +1};
153
154 // Sprites with CC=1 are only visible if preceded by a sprite
155 // with CC=0. Therefor search for first sprite with CC=0.
156 int first = 0;
157 do {
158 if ((visibleSprites[first].colorAttrib & 0x40) == 0) [[likely]] {
159 break;
160 }
161 ++first;
162 } while (first < int(visibleSprites.size()));
163 for (int i = narrow<int>(visibleSprites.size() - 1); i >= first; --i) {
164 const SpriteChecker::SpriteInfo& info = visibleSprites[i];
165 int x = info.x;
167 // Clip sprite pattern to render range.
168 if (!clipPattern(x, pattern, minX, maxX)) continue;
169 uint8_t c = info.colorAttrib & 0x0F;
170 if (c == 0 && transparency) continue;
171 while (pattern) {
172 if (pattern & 0x80000000) {
173 uint8_t color = c;
174 // Merge in any following CC=1 sprites.
175 for (int j = i + 1; /*sentinel*/; ++j) {
176 const SpriteChecker::SpriteInfo& info2 =
177 visibleSpritesWithSentinel[j];
178 if (!(info2.colorAttrib & 0x40)) break;
179 unsigned shift2 = x - info2.x;
180 if ((shift2 < 32) &&
181 ((info2.pattern << shift2) & 0x80000000)) {
182 color |= info2.colorAttrib & 0x0F;
183 }
184 }
185 if constexpr (MODE == DisplayMode::GRAPHIC5) {
186 Pixel pixL = palette[color >> 2];
187 Pixel pixR = palette[color & 3];
188 pixelPtr[x * 2 + 0] = pixL;
189 pixelPtr[x * 2 + 1] = pixR;
190 } else {
191 Pixel pix = palette[color];
192 if constexpr (MODE == DisplayMode::GRAPHIC6) {
193 pixelPtr[x * 2 + 0] = pix;
194 pixelPtr[x * 2 + 1] = pix;
195 } else {
196 pixelPtr[x] = pix;
197 }
198 }
199 }
200 ++x;
201 pattern <<= 1;
202 }
203 }
204 }
205
206private:
207 SpriteChecker& spriteChecker;
208
211 std::span<const Pixel, 16> palette;
212
215 bool transparency;
216
219 DisplayMode mode;
220};
221
222} // namespace openmsx
223
224#endif
Represents a VDP display mode.
Definition: DisplayMode.hh:16
uint32_t SpritePattern
Bitmap of length 32 describing a sprite pattern.
std::span< const SpriteInfo > getSprites(int line) const
Get sprites for a display line.
Utility class for converting VRAM contents to host pixels.
SpriteConverter(SpriteChecker &spriteChecker_, std::span< const Pixel, 16 > pal)
Constructor.
void drawMode2(int absLine, int minX, int maxX, std::span< Pixel > pixelPtr)
Draw sprites in sprite mode 2.
void setPalette(std::span< const Pixel, 16 > newPalette)
Set palette to use for converting sprites.
void drawMode1(int absLine, int minX, int maxX, std::span< Pixel > pixelPtr)
Draw sprites in sprite mode 1.
void setTransparency(bool enabled)
Update the transparency setting.
void setDisplayMode(DisplayMode newMode)
Notify SpriteConverter of a display mode change.
static bool clipPattern(int &x, SpriteChecker::SpritePattern &pattern, int minX, int maxX)
This file implemented 3 utility functions:
Definition: Autofire.cc:9
uint32_t Pixel
constexpr auto reverse(Range &&range)
Definition: view.hh:452
Contains all the information to draw a line of a sprite.
SpritePattern pattern
Pattern of this sprite line, corrected for magnification.
byte colorAttrib
Bit 3..0 are index in palette.
int16_t x
X-coordinate of sprite, corrected for early clock.