1 // license:BSD-3-Clause 2 // copyright-holders:Aaron Giles 3 /*************************************************************************** 4 5 General sprite handling helpers 6 7 ***************************************************************************/ 8 9 #ifndef MAME_VIDEO_SPRITE_H 10 #define MAME_VIDEO_SPRITE_H 11 12 #pragma once 13 14 15 // ======================> sparse_dirty_rect 16 17 // class representing a single dirty region 18 class sparse_dirty_rect : public rectangle 19 { 20 friend class simple_list<sparse_dirty_rect>; 21 22 public: sparse_dirty_rect()23 sparse_dirty_rect(): m_next(nullptr) { } 24 // getters next()25 const sparse_dirty_rect *next() const { return m_next; } 26 27 private: 28 // internal state 29 sparse_dirty_rect * m_next; 30 }; 31 32 33 // ======================> sparse_dirty_bitmap 34 35 class sparse_dirty_bitmap 36 { 37 public: 38 // construction/destruction 39 sparse_dirty_bitmap(int granularity = 3); 40 sparse_dirty_bitmap(int width, int height, int granularity = 3); 41 42 // dirtying operations - partially intersecting tiles are dirtied dirty(const rectangle & rect)43 void dirty(const rectangle &rect) { dirty(rect.left(), rect.right(), rect.top(), rect.bottom()); } 44 void dirty(int32_t left, int32_t right, int32_t top, int32_t bottom); dirty_all()45 void dirty_all() { dirty(0, m_width - 1, 0, m_height - 1); } 46 47 // cleaning operations - partially intersecting tiles are NOT cleaned clean(const rectangle & rect)48 void clean(const rectangle &rect) { clean(rect.left(), rect.right(), rect.top(), rect.bottom()); } 49 void clean(int32_t left, int32_t right, int32_t top, int32_t bottom); clean_all()50 void clean_all() { clean(0, m_width - 1, 0, m_height - 1); } 51 52 // convert to rect list first_dirty_rect()53 sparse_dirty_rect *first_dirty_rect() { rectangle fullrect(0, m_width - 1, 0, m_height - 1); return first_dirty_rect(fullrect); } 54 sparse_dirty_rect *first_dirty_rect(const rectangle &cliprect); 55 56 // dynamic resizing 57 void resize(int width, int height); 58 59 private: 60 // invalidate cached rect list invalidate_rect_list()61 void invalidate_rect_list() { m_rect_list_bounds.set(0, -1, 0, -1); } 62 63 // internal state 64 int m_width; 65 int m_height; 66 int m_granularity; 67 bitmap_ind8 m_bitmap; 68 rectangle m_rect_list_bounds; 69 fixed_allocator<sparse_dirty_rect> m_rect_allocator; 70 simple_list<sparse_dirty_rect> m_rect_list; 71 }; 72 73 74 // ======================> sprite_device 75 76 template<typename _SpriteRAMType, class _BitmapType> 77 class sprite_device : public device_t 78 { 79 // constants 80 static const int BITMAP_SLOP = 16; 81 82 protected: 83 // construction/destruction - only for subclasses 84 sprite_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, int dirty_granularity = 3) 85 : device_t(mconfig, type, tag, owner, 0) 86 , m_xorigin(0) 87 , m_yorigin(0) 88 , m_spriteram(nullptr) 89 , m_spriteram_bytes(0) 90 , m_dirty(dirty_granularity) 91 { 92 force_clear(); 93 } 94 95 public: 96 // getters xorigin()97 int32_t xorigin() const { return m_xorigin; } yorigin()98 int32_t yorigin() const { return m_yorigin; } bitmap()99 _BitmapType &bitmap() { return m_bitmap; } first_dirty_rect()100 sparse_dirty_rect *first_dirty_rect() { return m_dirty.first_dirty_rect(); } first_dirty_rect(const rectangle & cliprect)101 sparse_dirty_rect *first_dirty_rect(const rectangle &cliprect) { return m_dirty.first_dirty_rect(cliprect); } spriteram()102 _SpriteRAMType *spriteram() const { return m_spriteram; } spriteram_bytes()103 uint32_t spriteram_bytes() const { return m_spriteram_bytes; } spriteram_elements()104 uint32_t spriteram_elements() const { return m_spriteram_bytes / sizeof(_SpriteRAMType); } buffer()105 _SpriteRAMType *buffer() { return &m_buffer[0]; } 106 107 // configuration set_spriteram(_SpriteRAMType * base,uint32_t bytes)108 void set_spriteram(_SpriteRAMType *base, uint32_t bytes) { assert(base != nullptr && bytes != 0); m_spriteram = base; m_spriteram_bytes = bytes; m_buffer.resize(m_spriteram_bytes / sizeof(_SpriteRAMType)); } 109 void set_origin(int32_t xorigin = 0, int32_t yorigin = 0) { m_xorigin = xorigin; m_yorigin = yorigin; } set_xorigin(int32_t xorigin)110 void set_xorigin(int32_t xorigin) { m_xorigin = xorigin; } set_yorigin(int32_t yorigin)111 void set_yorigin(int32_t yorigin) { m_yorigin = yorigin; } 112 113 // buffering copy_to_buffer()114 void copy_to_buffer() { assert(m_spriteram != nullptr); memcpy(m_buffer, m_spriteram, m_spriteram_bytes); } 115 116 // clearing clear()117 void clear() { clear(m_bitmap.cliprect()); } clear(const rectangle & cliprect)118 void clear(const rectangle &cliprect) 119 { 120 for (const sparse_dirty_rect *rect = m_dirty.first_dirty_rect(cliprect); rect != nullptr; rect = rect->next()) 121 m_bitmap.fill(~0, *rect); 122 m_dirty.clean(cliprect); 123 } 124 125 // force clear (don't use dirty rects) force_clear()126 void force_clear() 127 { 128 m_bitmap.fill(~0); 129 m_dirty.clean_all(); 130 } 131 132 // drawing 133 void draw_async(const rectangle &cliprect, bool clearit = true) 134 { 135 // if the cliprect exceeds our current bitmap dimensions, expand 136 if (cliprect.right() >= m_bitmap.width() || cliprect.bottom() >= m_bitmap.height()) 137 { 138 int new_width = std::max(cliprect.right() + 1, m_bitmap.width()); 139 int new_height = std::max(cliprect.bottom() + 1, m_bitmap.height()); 140 m_bitmap.resize(new_width, new_height, BITMAP_SLOP, BITMAP_SLOP); 141 m_dirty.resize(new_width, new_height); 142 } 143 144 // clear out the region 145 if (clearit) 146 clear(cliprect); 147 148 // wrap the bitmap, adjusting for x/y origins 149 _BitmapType wrapped(&m_bitmap.pix(0) - m_xorigin - m_yorigin * m_bitmap.rowpixels(), m_xorigin + cliprect.right() + 1, m_yorigin + cliprect.bottom() + 1, m_bitmap.rowpixels()); 150 151 // compute adjusted cliprect in source space 152 rectangle adjusted = cliprect; 153 adjusted.offset(m_xorigin, m_yorigin); 154 155 // render 156 draw(wrapped, adjusted); 157 } 158 159 protected: 160 // device-level overrides device_start()161 virtual void device_start() override 162 { 163 // find spriteram 164 memory_share *spriteram = owner()->memshare(tag()); 165 if (spriteram != nullptr) 166 { 167 set_spriteram(reinterpret_cast<_SpriteRAMType *>(spriteram->ptr()), spriteram->bytes()); 168 169 // save states 170 save_item(NAME(m_buffer)); 171 } 172 } 173 174 // subclass overrides 175 virtual void draw(_BitmapType &bitmap, const rectangle &cliprect) = 0; 176 177 // subclass helpers mark_dirty(const rectangle & rect)178 void mark_dirty(const rectangle &rect) { mark_dirty(rect.left(), rect.right(), rect.top(), rect.bottom()); } mark_dirty(int32_t left,int32_t right,int32_t top,int32_t bottom)179 void mark_dirty(int32_t left, int32_t right, int32_t top, int32_t bottom) { m_dirty.dirty(left - m_xorigin, right - m_xorigin, top - m_yorigin, bottom - m_yorigin); } 180 181 private: 182 // configuration 183 int32_t m_xorigin; // X origin for drawing 184 int32_t m_yorigin; // Y origin for drawing 185 186 // memory pointers and buffers 187 _SpriteRAMType * m_spriteram; // pointer to spriteram pointer 188 int32_t m_spriteram_bytes; // size of sprite RAM in bytes 189 std::vector<_SpriteRAMType> m_buffer; // buffered spriteram for those that use it 190 191 // bitmaps 192 _BitmapType m_bitmap; // live bitmap 193 sparse_dirty_bitmap m_dirty; // dirty bitmap 194 }; 195 196 typedef sprite_device<uint8_t, bitmap_ind16> sprite8_device_ind16; 197 typedef sprite_device<uint16_t, bitmap_ind16> sprite16_device_ind16; 198 typedef sprite_device<uint32_t, bitmap_ind16> sprite32_device_ind16; 199 200 typedef sprite_device<uint8_t, bitmap_ind32> sprite8_device_ind32; 201 typedef sprite_device<uint16_t, bitmap_ind32> sprite16_device_ind32; 202 typedef sprite_device<uint32_t, bitmap_ind32> sprite32_device_ind32; 203 204 205 #endif // MAME_VIDEO_SPRITE_H 206