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 <cstdint>
14#include <span>
15
16namespace openmsx {
17
21{
22public:
23 using Pixel = uint32_t;
24
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 if (int before = minX - x; before > 0) {
70 if (before >= 32) {
71 // 32 pixels before minX -> not visible
72 return false;
73 }
74 pattern <<= before;
75 x = minX;
76 }
77 if (int after = maxX - x; after < 32) {
78 // close to maxX (or past)
79 if (after <= 0) {
80 // past maxX -> not visible
81 return false;
82 }
83 auto mask = narrow_cast<int>(0x8000'0000);
84 pattern &= (mask >> (after - 1));
85 }
86 return true; // visible
87 }
88
96 void drawMode1(int absLine, int minX, int maxX, std::span<Pixel> pixelPtr) const
97 {
98 // Determine sprites visible on this line.
99 auto visibleSprites = spriteChecker.getSprites(absLine);
100 // Optimisation: return at once if no sprites on this line.
101 // Lines without any sprites are very common in most programs.
102 if (visibleSprites.empty()) return;
103
104 // Render using overdraw.
105 for (const auto& si : view::reverse(visibleSprites)) {
106 // Get sprite info.
107 Pixel colIndex = si.colorAttrib & 0x0F;
108 // Don't draw transparent sprites in sprite mode 1.
109 // Verified on real V9958: TP bit also has effect in
110 // sprite mode 1.
111 if (colIndex == 0 && transparency) continue;
112 Pixel color = palette[colIndex];
113 SpriteChecker::SpritePattern pattern = si.pattern;
114 int x = si.x;
115 // Clip sprite pattern to render range.
116 if (!clipPattern(x, pattern, minX, maxX)) continue;
117 // Convert pattern to pixels.
118 Pixel* p = &pixelPtr[x];
119 while (pattern) {
120 // Draw pixel if sprite has a dot.
121 if (pattern & 0x8000'0000) {
122 *p = color;
123 }
124 // Advancing behaviour.
125 pattern <<= 1;
126 p++;
127 }
128 }
129 }
130
141 template<unsigned MODE>
142 void drawMode2(int absLine, int minX, int maxX, std::span<Pixel> pixelPtr) const
143 {
144 // Determine sprites visible on this line.
145 auto visibleSprites = spriteChecker.getSprites(absLine);
146 // Optimisation: return at once if no sprites on this line.
147 // Lines without any sprites are very common in most programs.
148 if (visibleSprites.empty()) return;
149 std::span visibleSpritesWithSentinel{visibleSprites.data(),
150 visibleSprites.size() +1};
151
152 // Sprites with CC=1 are only visible if preceded by a sprite
153 // with CC=0. Therefor search for first sprite with CC=0.
154 int first = 0;
155 do {
156 if ((visibleSprites[first].colorAttrib & 0x40) == 0) [[likely]] {
157 break;
158 }
159 ++first;
160 } while (first < int(visibleSprites.size()));
161 for (int i = narrow<int>(visibleSprites.size() - 1); i >= first; --i) {
162 const SpriteChecker::SpriteInfo& info = visibleSprites[i];
163 int x = info.x;
165 // Clip sprite pattern to render range.
166 if (!clipPattern(x, pattern, minX, maxX)) continue;
167 uint8_t c = info.colorAttrib & 0x0F;
168 if (c == 0 && transparency) continue;
169 while (pattern) {
170 if (pattern & 0x8000'0000) {
171 uint8_t color = c;
172 // Merge in any following CC=1 sprites.
173 for (int j = i + 1; /*sentinel*/; ++j) {
174 const SpriteChecker::SpriteInfo& info2 =
175 visibleSpritesWithSentinel[j];
176 if (!(info2.colorAttrib & 0x40)) break;
177 unsigned shift2 = x - info2.x;
178 if ((shift2 < 32) &&
179 ((info2.pattern << shift2) & 0x8000'0000)) {
180 color |= info2.colorAttrib & 0x0F;
181 }
182 }
183 if constexpr (MODE == DisplayMode::GRAPHIC5) {
184 Pixel pixL = palette[color >> 2];
185 Pixel pixR = palette[color & 3];
186 pixelPtr[x * 2 + 0] = pixL;
187 pixelPtr[x * 2 + 1] = pixR;
188 } else {
189 Pixel pix = palette[color];
190 if constexpr (MODE == DisplayMode::GRAPHIC6) {
191 pixelPtr[x * 2 + 0] = pix;
192 pixelPtr[x * 2 + 1] = pix;
193 } else {
194 pixelPtr[x] = pix;
195 }
196 }
197 }
198 ++x;
199 pattern <<= 1;
200 }
201 }
202 }
203
204private:
205 SpriteChecker& spriteChecker;
206
209 std::span<const Pixel, 16> palette;
210
213 bool transparency;
214
217 DisplayMode mode;
218};
219
220} // namespace openmsx
221
222#endif
Represents a VDP display mode.
static constexpr uint8_t GRAPHIC5
static constexpr uint8_t GRAPHIC6
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.
static bool clipPattern(int &x, SpriteChecker::SpritePattern &pattern, int minX, int maxX)
SpriteConverter(SpriteChecker &spriteChecker_, std::span< const Pixel, 16 > pal)
Constructor.
void drawMode1(int absLine, int minX, int maxX, std::span< Pixel > pixelPtr) const
Draw sprites in sprite mode 1.
void setTransparency(bool enabled)
Update the transparency setting.
void drawMode2(int absLine, int minX, int maxX, std::span< Pixel > pixelPtr) const
Draw sprites in sprite mode 2.
void setDisplayMode(DisplayMode newMode)
Notify SpriteConverter of a display mode change.
void setPalette(std::span< const Pixel, 16 > newPalette)
Set palette to use for converting sprites.
This file implemented 3 utility functions:
Definition Autofire.cc:11
constexpr auto reverse(Range &&range)
Definition view.hh:514
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.