#include "DisplaySDL.hpp" #include "Peripherals.hpp" #include #include DisplaySDL::DisplaySDL(int w, int h, uint32_t fgColor, uint32_t bgColor): mpFramebuffer{std::make_unique()}, mpDisplayState{std::make_unique()}, mDoClear{true} { // Create SDL Window mpWindow = SDL_CreateWindow( "ChocoChip-8", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, w, h, 0 ); if(mpWindow == NULL) { throw std::runtime_error(SDL_GetError()); } // Convert our foreground/background colors to the format expected by SDL SDL_PixelFormat *fmt = SDL_AllocFormat(SDL_GetWindowPixelFormat(mpWindow)); mFgColor = SDL_MapRGB( fmt, (fgColor & 0x00FF0000) >> 16, (fgColor & 0x0000FF00) >> 8, (fgColor & 0x000000FF) ); mBgColor = SDL_MapRGB( fmt, (bgColor & 0x00FF0000) >> 16, (bgColor & 0x0000FF00) >> 8, (bgColor & 0x000000FF) ); SDL_FreeFormat(fmt); }; DisplaySDL::~DisplaySDL() { SDL_DestroyWindow(mpWindow); } void DisplaySDL::clear() { for(auto &scanline : *mpFramebuffer) { scanline.reset(); } mDoClear = true; } int DisplaySDL::blit(const chocochip8::Scanline &spriteScanline, int y) { using chocochip8::Scanline; Scanline &targetScanline = mpFramebuffer->at(y); bool collision = (spriteScanline & targetScanline).any(); targetScanline ^= spriteScanline; return collision; } void DisplaySDL::updateWindow(bool doWindowUpdate) const { using chocochip8::Scanline; SDL_Surface *pSurface = SDL_GetWindowSurface(mpWindow); if(mDoClear) { for(auto &scanline : *mpDisplayState) { scanline.reset(); } SDL_FillRect(pSurface, NULL, mBgColor); mDoClear = false; doWindowUpdate = true; } for(int y = 0; y < mpFramebuffer->size(); y++) { Scanline &rNewScanline = (*mpFramebuffer)[y]; Scanline &rOldScanline = (*mpDisplayState)[y]; if(rNewScanline == rOldScanline) { // Skip scanlines that haven't changed since the last update continue; } for(int x = 0; x < rNewScanline.size(); x++) { bool isSet = rNewScanline._Unchecked_test(x); bool wasSet = rOldScanline._Unchecked_test(x); if(isSet == wasSet) { continue; } // Map framebuffer pixel to SDL surface rectangle, // note that the MSB of an scanline's bitset is the leftmost pixel. int z = (chocochip8::gcWidth - 1) - x; int x1 = ( z * pSurface->w) / chocochip8::gcWidth; int x2 = ((z + 1) * pSurface->w) / chocochip8::gcWidth; int y1 = ( y * pSurface->h) / chocochip8::gcHeight; int y2 = ((y + 1) * pSurface->h) / chocochip8::gcHeight; SDL_Rect rect; rect.x = x1; rect.y = y1; rect.w = x2 - x1; rect.h = y2 - y1; Uint32 color = (isSet ? mFgColor : mBgColor); SDL_FillRect(pSurface, &rect, color); doWindowUpdate = true; } } if(doWindowUpdate) { *mpDisplayState = *mpFramebuffer; SDL_UpdateWindowSurface(mpWindow); } }