From 22ce8ac538066682a8fca1cc8d4b2e6656d84cdb Mon Sep 17 00:00:00 2001 From: Muzychenko Andrey <33288308+k4zmu2a@users.noreply.github.com> Date: Sat, 25 Sep 2021 16:52:19 +0300 Subject: [PATCH] gdrv: blit no more, present render:vScreen directly. Improved split bitmap handling. --- SpaceCadetPinball/GroupData.cpp | 53 +++-- SpaceCadetPinball/GroupData.h | 10 +- SpaceCadetPinball/TTextBox.cpp | 29 +-- SpaceCadetPinball/fullscrn.cpp | 2 +- SpaceCadetPinball/gdrv.cpp | 152 ++---------- SpaceCadetPinball/gdrv.h | 22 +- SpaceCadetPinball/nudge.cpp | 5 +- SpaceCadetPinball/options.cpp | 1 - SpaceCadetPinball/partman.cpp | 1 + SpaceCadetPinball/pb.cpp | 14 +- SpaceCadetPinball/pb.h | 1 - SpaceCadetPinball/render.cpp | 393 ++++++++++++++++---------------- SpaceCadetPinball/render.h | 35 +-- SpaceCadetPinball/score.cpp | 21 +- SpaceCadetPinball/winmain.cpp | 12 +- SpaceCadetPinball/zdrv.cpp | 6 +- 16 files changed, 290 insertions(+), 467 deletions(-) diff --git a/SpaceCadetPinball/GroupData.cpp b/SpaceCadetPinball/GroupData.cpp index 396e5b8..4722551 100644 --- a/SpaceCadetPinball/GroupData.cpp +++ b/SpaceCadetPinball/GroupData.cpp @@ -18,16 +18,6 @@ EntryData::~EntryData() zdrv::destroy_zmap(reinterpret_cast(Buffer)); memory::free(Buffer); } - if (DerivedBmp) - { - gdrv::destroy_bitmap(DerivedBmp); - memory::free(DerivedBmp); - } - if (DerivedZMap) - { - zdrv::destroy_zmap(DerivedZMap); - memory::free(DerivedZMap); - } } @@ -38,8 +28,7 @@ GroupData::GroupData(int groupId) void GroupData::AddEntry(EntryData* entry) { - Entries.push_back(entry); - + auto addEntry = true; switch (entry->EntryType) { case FieldTypes::GroupName: @@ -47,21 +36,23 @@ void GroupData::AddEntry(EntryData* entry) break; case FieldTypes::Bitmap8bit: { - auto bmp = reinterpret_cast(entry->Buffer); - if (bmp->BitmapType == BitmapTypes::Spliced) + auto srcBmp = reinterpret_cast(entry->Buffer); + if (srcBmp->BitmapType == BitmapTypes::Spliced) { // Get rid of spliced bitmap early on, to simplify render pipeline - auto splitBmp = memory::allocate(); - auto splitZMap = memory::allocate(); - SplitSplicedBitmap(*bmp, *splitBmp, *splitZMap); - entry->DerivedBmp = splitBmp; - entry->DerivedZMap = splitZMap; - SetBitmap(splitBmp); - SetZMap(splitZMap); + auto bmp = memory::allocate(); + auto zMap = memory::allocate(); + SplitSplicedBitmap(*srcBmp, *bmp, *zMap); + + NeedsSort = true; + addEntry = false; + AddEntry(new EntryData(FieldTypes::Bitmap8bit, reinterpret_cast(bmp))); + AddEntry(new EntryData(FieldTypes::Bitmap16bit, reinterpret_cast(zMap))); + delete entry; } else { - SetBitmap(bmp); + SetBitmap(srcBmp); } break; } @@ -72,6 +63,24 @@ void GroupData::AddEntry(EntryData* entry) } default: break; } + + if (addEntry) + Entries.push_back(entry); +} + +void GroupData::FinalizeGroup() +{ + if (NeedsSort) + { + // Entries within a group are sorted by EntryType, in ascending order. + // Dat files follow this rule, zMaps inserted in the middle break it. + NeedsSort = false; + std::sort(Entries.begin(), Entries.end(), [](const EntryData* lhs, const EntryData* rhs) + { + return lhs->EntryType < rhs->EntryType; + }); + Entries.shrink_to_fit(); + } } gdrv_bitmap8* GroupData::GetBitmap(int resolution) const diff --git a/SpaceCadetPinball/GroupData.h b/SpaceCadetPinball/GroupData.h index d966fe6..f754ad2 100644 --- a/SpaceCadetPinball/GroupData.h +++ b/SpaceCadetPinball/GroupData.h @@ -43,12 +43,16 @@ enum class FieldTypes : int16_t struct EntryData { + EntryData() = default; + + EntryData(FieldTypes entryType, char* buffer): EntryType(entryType), FieldSize(-1), Buffer(buffer) + { + } + ~EntryData(); FieldTypes EntryType{}; int FieldSize{}; char* Buffer{}; - gdrv_bitmap8* DerivedBmp{}; - zmap_header_type* DerivedZMap{}; }; class GroupData @@ -59,6 +63,7 @@ public: GroupData(int groupId); void AddEntry(EntryData* entry); + void FinalizeGroup(); const std::vector& GetEntries() const { return Entries; } const EntryData* GetEntry(size_t index) const { return Entries[index]; } size_t EntryCount() const { return Entries.size(); } @@ -70,6 +75,7 @@ private: std::vector Entries; gdrv_bitmap8* Bitmaps[3]{}; zmap_header_type* ZMaps[3]{}; + bool NeedsSort = false; static void SplitSplicedBitmap(const gdrv_bitmap8& srcBmp, gdrv_bitmap8& bmp, zmap_header_type& zMap); diff --git a/SpaceCadetPinball/TTextBox.cpp b/SpaceCadetPinball/TTextBox.cpp index 24975bb..fd66558 100644 --- a/SpaceCadetPinball/TTextBox.cpp +++ b/SpaceCadetPinball/TTextBox.cpp @@ -85,15 +85,7 @@ void TTextBox::Clear() OffsetX, OffsetY); else - gdrv::fill_bitmap(&render::vscreen, Width, Height, OffsetX, OffsetY, 0); - gdrv::blit( - &render::vscreen, - OffsetX, - OffsetY, - OffsetX + render::vscreen.XPosition, - OffsetY + render::vscreen.YPosition, - Width, - Height); + gdrv::fill_bitmap(&render::vscreen, Width, Height, OffsetX, OffsetY, 0); if (Timer) { if (Timer != -1) @@ -198,14 +190,6 @@ void TTextBox::Draw() auto font = Font; if (!font) { - gdrv::blit( - &render::vscreen, - OffsetX, - OffsetY, - OffsetX + render::vscreen.XPosition, - OffsetY + render::vscreen.YPosition, - Width, - Height); gdrv::grtext_draw_ttext_in_box( Message1->Text, render::vscreen.XPosition + OffsetX, @@ -275,14 +259,5 @@ void TTextBox::Draw() if ((*text & 0x7F) == '\n') ++text; } - } - - gdrv::blit( - &render::vscreen, - OffsetX, - OffsetY, - OffsetX + render::vscreen.XPosition, - OffsetY + render::vscreen.YPosition, - Width, - Height); + } } diff --git a/SpaceCadetPinball/fullscrn.cpp b/SpaceCadetPinball/fullscrn.cpp index e013c4d..fa838c3 100644 --- a/SpaceCadetPinball/fullscrn.cpp +++ b/SpaceCadetPinball/fullscrn.cpp @@ -121,7 +121,7 @@ void fullscrn::window_size_changed() OffsetY = static_cast(floor((height - res->TableHeight * ScaleY) / 2)); } - gdrv::DestinationRect = SDL_Rect + render::DestinationRect = SDL_Rect { OffsetX, OffsetY, width - OffsetX * 2, height - OffsetY * 2 diff --git a/SpaceCadetPinball/gdrv.cpp b/SpaceCadetPinball/gdrv.cpp index cc5475a..6eb5e6b 100644 --- a/SpaceCadetPinball/gdrv.cpp +++ b/SpaceCadetPinball/gdrv.cpp @@ -9,41 +9,7 @@ #include "score.h" #include "winmain.h" -SDL_Texture* gdrv::vScreenTex = nullptr; -ColorRgba* gdrv::vScreenPixels = nullptr; -int gdrv::vScreenWidth, gdrv::vScreenHeight; ColorRgba gdrv::current_palette[256]{}; -SDL_Rect gdrv::DestinationRect{}; - -int gdrv::init(int width, int height) -{ - { - UsingSdlHint hint{SDL_HINT_RENDER_SCALE_QUALITY, options::Options.LinearFiltering ? "linear" : "nearest"}; - vScreenTex = SDL_CreateTexture - ( - winmain::Renderer, - SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_STREAMING, - width, height - ); - } - vScreenWidth = width; - vScreenHeight = height; - vScreenPixels = new ColorRgba[width * height]; - - return 0; -} - -int gdrv::uninit() -{ - SDL_DestroyTexture(vScreenTex); - delete[] vScreenPixels; - return 0; -} - -void gdrv::get_focus() -{ -} int gdrv::create_bitmap(gdrv_bitmap8* bmp, int width, int height, int stride, bool indexed) { @@ -122,23 +88,23 @@ int gdrv::display_palette(ColorRgba* plt) { const uint32_t sysPaletteColors[] { - 0x00000000, // Color 0: transparent - 0x00000080, - 0x00008000, - 0x00008080, - 0x00800000, - 0x00800080, - 0x00808000, - 0x00C0C0C0, - 0x00C0DCC0, - 0x00F0CAA6 + 0xff000000, // Color 0: transparent + 0xff000080, + 0xff008000, + 0xff008080, + 0xff800000, + 0xff800080, + 0xff808000, + 0xffC0C0C0, + 0xffC0DCC0, + 0xffF0CAA6 }; memcpy(current_palette, sysPaletteColors, sizeof sysPaletteColors); for (int i = 0; i < 256; i++) { - current_palette[i].rgba.peFlags = 0; + current_palette[i].rgba.Alpha = 0; } auto pltSrc = &plt[10]; @@ -147,18 +113,16 @@ int gdrv::display_palette(ColorRgba* plt) { if (plt) { - pltDst->rgba.peRed = pltSrc->rgba.peRed; - pltDst->rgba.peGreen = pltSrc->rgba.peGreen; - pltDst->rgba.peBlue = pltSrc->rgba.peBlue; + pltDst->rgba.Blue = pltSrc->rgba.Blue; + pltDst->rgba.Green = pltSrc->rgba.Green; + pltDst->rgba.Red = pltSrc->rgba.Red; } - pltDst->rgba.peFlags = 4; + pltDst->rgba.Alpha = 0xFF; pltSrc++; pltDst++; } - current_palette[255].rgba.peBlue = -1; - current_palette[255].rgba.peGreen = -1; - current_palette[255].rgba.peRed = -1; + current_palette[255].Color = 0xffFFFFFF; score::ApplyPalette(); for (const auto group : pb::record_table->Groups) @@ -194,32 +158,6 @@ int gdrv::destroy_bitmap(gdrv_bitmap8* bmp) return 0; } -void gdrv::blit(gdrv_bitmap8* bmp, int xSrc, int ySrc, int xDest, int yDest, int width, int height) -{ - StretchDIBitsScaled( - xSrc, - ySrc, - xDest, - yDest, - width, - height, - bmp - ); -} - -void gdrv::blat(gdrv_bitmap8* bmp, int xDest, int yDest) -{ - StretchDIBitsScaled( - 0, - 0, - xDest, - yDest, - bmp->Width, - bmp->Height, - bmp - ); -} - void gdrv::fill_bitmap(gdrv_bitmap8* bmp, int width, int height, int xOff, int yOff, uint8_t fillChar) { auto color = current_palette[fillChar]; @@ -272,64 +210,6 @@ void gdrv::grtext_draw_ttext_in_box(LPCSTR text, int xOff, int yOff, int width, { } -int gdrv::StretchDIBitsScaled(int xSrc, int ySrc, int xDst, int yDst, - int width, int height, gdrv_bitmap8* bmp) -{ - // Negative dst == positive src offset - if (xDst < 0) - { - xSrc -= xDst; - xDst = 0; - } - if (yDst < 0) - { - ySrc -= yDst; - yDst = 0; - } - - // Clamp out of bounds rectangles - xSrc = std::max(0, std::min(xSrc, bmp->Width)); - ySrc = std::max(0, std::min(ySrc, bmp->Height)); - if (xSrc + width > bmp->Width) - width = bmp->Width - xSrc; - if (ySrc + height > bmp->Height) - height = bmp->Height - ySrc; - - xDst = std::max(0, std::min(xDst, vScreenWidth)); - yDst = std::max(0, std::min(yDst, vScreenHeight)); - if (xDst + width > vScreenWidth) - width = vScreenWidth - xDst; - if (yDst + height > vScreenHeight) - height = vScreenHeight - yDst; - - auto srcPtr = &bmp->BmpBufPtr1[bmp->Stride * ySrc + xSrc]; - auto dstPtr = &vScreenPixels[vScreenWidth * yDst + xDst]; - for (int y = height; y > 0; --y) - { - std::memcpy(dstPtr, srcPtr, width * sizeof(ColorRgba)); - srcPtr += bmp->Stride; - dstPtr += vScreenWidth; - } - - return 0; -} - -void gdrv::BlitScreen() -{ - int pitch = 0; - void* lockedPixels; - SDL_LockTexture - ( - vScreenTex, - nullptr, - &lockedPixels, - &pitch - ); - std::memcpy(lockedPixels, vScreenPixels, vScreenWidth * vScreenHeight * sizeof(ColorRgba)); - SDL_UnlockTexture(vScreenTex); - SDL_RenderCopy(winmain::Renderer, vScreenTex, nullptr, &DestinationRect); -} - void gdrv::ApplyPalette(gdrv_bitmap8& bmp) { if (bmp.BitmapType == BitmapTypes::None) diff --git a/SpaceCadetPinball/gdrv.h b/SpaceCadetPinball/gdrv.h index 5ae9986..f8038fb 100644 --- a/SpaceCadetPinball/gdrv.h +++ b/SpaceCadetPinball/gdrv.h @@ -11,10 +11,10 @@ enum class BitmapTypes : uint8_t struct Rgba { - uint8_t peRed; - uint8_t peGreen; - uint8_t peBlue; - uint8_t peFlags; + uint8_t Blue; + uint8_t Green; + uint8_t Red; + uint8_t Alpha; }; union ColorRgba @@ -57,32 +57,18 @@ struct gdrv_bitmap8 class gdrv { public: - static SDL_Rect DestinationRect; - - static int init(int width, int height); - static int uninit(); - static void get_focus(); static int create_bitmap(gdrv_bitmap8* bmp, int width, int height, int stride = -1, bool indexed = true); static int create_bitmap(gdrv_bitmap8& bmp, const struct dat8BitBmpHeader& header); static int destroy_bitmap(gdrv_bitmap8* bmp); static int display_palette(ColorRgba* plt); - static void blit(gdrv_bitmap8* bmp, int xSrc, int ySrc, int xDest, int yDest, int width, int height); - static void blat(gdrv_bitmap8* bmp, int xDest, int yDest); static void fill_bitmap(gdrv_bitmap8* bmp, int width, int height, int xOff, int yOff, uint8_t fillChar); static void copy_bitmap(gdrv_bitmap8* dstBmp, int width, int height, int xOff, int yOff, gdrv_bitmap8* srcBmp, int srcXOff, int srcYOff); static void copy_bitmap_w_transparency(gdrv_bitmap8* dstBmp, int width, int height, int xOff, int yOff, gdrv_bitmap8* srcBmp, int srcXOff, int srcYOff); static void grtext_draw_ttext_in_box(LPCSTR text, int xOff, int yOff, int width, int height, int a6); - static void BlitScreen(); static void ApplyPalette(gdrv_bitmap8& bmp); static void CreatePreview(gdrv_bitmap8& bmp); private: - static SDL_Texture* vScreenTex; - static ColorRgba* vScreenPixels; - static int vScreenWidth, vScreenHeight; static ColorRgba current_palette[256]; - - static int StretchDIBitsScaled(int xSrc, int ySrc, int xDst, int yDst, - int width, int height, gdrv_bitmap8* bmp); }; diff --git a/SpaceCadetPinball/nudge.cpp b/SpaceCadetPinball/nudge.cpp index 5caaed0..e54c70c 100644 --- a/SpaceCadetPinball/nudge.cpp +++ b/SpaceCadetPinball/nudge.cpp @@ -68,7 +68,6 @@ void nudge::_nudge(float xDiff, float yDiff) vector_type accelMod; float invAccelX, invAccelY; - auto table = pb::MainTable; auto ballList = pb::MainTable->BallList; accelMod.X = xDiff * 0.5f; accelMod.Y = yDiff * 0.5f; @@ -91,10 +90,8 @@ void nudge::_nudge(float xDiff, float yDiff) else invAccelY = 1.0f / ball->Acceleration.Y; ball->InvAcceleration.Y = invAccelY; - table = pb::MainTable; } } - render::shift(static_cast(floor(xDiff + 0.5f)), static_cast(floor(0.5f - yDiff)), 0, 0, table->Width, - table->Height); + render::shift(static_cast(floor(xDiff + 0.5f)), static_cast(floor(0.5f - yDiff))); } diff --git a/SpaceCadetPinball/options.cpp b/SpaceCadetPinball/options.cpp index cc7037b..d2fc6e7 100644 --- a/SpaceCadetPinball/options.cpp +++ b/SpaceCadetPinball/options.cpp @@ -217,7 +217,6 @@ void options::toggle(Menu1 uIDCheckItem) case Menu1::WindowUniformScale: Options.UniformScaling ^= true; fullscrn::window_size_changed(); - pb::paint(); break; case Menu1::WindowLinearFilter: Options.LinearFiltering ^= true; diff --git a/SpaceCadetPinball/partman.cpp b/SpaceCadetPinball/partman.cpp index 84523a7..2df27fc 100644 --- a/SpaceCadetPinball/partman.cpp +++ b/SpaceCadetPinball/partman.cpp @@ -130,6 +130,7 @@ DatFile* partman::load_records(LPCSTR lpFileName, bool fullTiltMode) groupData->AddEntry(entryData); } + groupData->FinalizeGroup(); datFile->Groups.push_back(groupData); } diff --git a/SpaceCadetPinball/pb.cpp b/SpaceCadetPinball/pb.cpp index d4a8ce6..cf147dc 100644 --- a/SpaceCadetPinball/pb.cpp +++ b/SpaceCadetPinball/pb.cpp @@ -75,8 +75,7 @@ int pb::init() zMin = cameraInfo[1]; zScaler = cameraInfo[2]; } - - gdrv::init(resInfo->TableWidth, resInfo->TableHeight); + render::init(nullptr, zMin, zScaler, resInfo->TableWidth, resInfo->TableHeight); gdrv::copy_bitmap( &render::vscreen, @@ -116,7 +115,6 @@ int pb::uninit() if (MainTable) delete MainTable; MainTable = nullptr; - gdrv::get_focus(); timer::uninit(); render::uninit(); return 0; @@ -131,14 +129,7 @@ void pb::reset_table() void pb::firsttime_setup() { - render::blit = 0; render::update(); - render::blit = 1; -} - -void pb::paint() -{ - render::paint(); } void pb::mode_change(int mode) @@ -493,9 +484,6 @@ void pb::keydown(int key) case 's': MainTable->AddScore(static_cast(RandFloat() * 1000000.0f)); break; - case SDLK_F11: - gdrv::get_focus(); - break; case SDLK_F12: MainTable->port_draw(); break; diff --git a/SpaceCadetPinball/pb.h b/SpaceCadetPinball/pb.h index b1e0dd5..956ec71 100644 --- a/SpaceCadetPinball/pb.h +++ b/SpaceCadetPinball/pb.h @@ -44,7 +44,6 @@ public: static int uninit(); static void reset_table(); static void firsttime_setup(); - static void paint(); static void mode_change(int mode); static void toggle_demo(); static void replay_level(int demoMode); diff --git a/SpaceCadetPinball/render.cpp b/SpaceCadetPinball/render.cpp index 6b48569..b5f2e88 100644 --- a/SpaceCadetPinball/render.cpp +++ b/SpaceCadetPinball/render.cpp @@ -3,9 +3,11 @@ #include "GroupData.h" #include "memory.h" +#include "options.h" #include "pb.h" +#include "TPinballTable.h" +#include "winmain.h" -int render::blit = 0; int render::many_dirty, render::many_sprites, render::many_balls; render_sprite_type_struct **render::dirty_list, **render::sprite_list, **render::ball_list; zmap_header_type* render::background_zmap; @@ -14,6 +16,8 @@ float render::zscaler, render::zmin, render::zmax; rectangle_type render::vscreen_rect; gdrv_bitmap8 render::vscreen, *render::background_bitmap, render::ball_bitmap[20]; zmap_header_type render::zscreen; +SDL_Texture* render::vScreenTex = nullptr; +SDL_Rect render::DestinationRect{}; void render::init(gdrv_bitmap8* bmp, float zMin, float zScaler, int width, int height) { @@ -41,6 +45,18 @@ void render::init(gdrv_bitmap8* bmp, float zMin, float zScaler, int width, int h gdrv::copy_bitmap(&vscreen, width, height, 0, 0, bmp, 0, 0); else gdrv::fill_bitmap(&vscreen, vscreen.Width, vscreen.Height, 0, 0, 0); + + { + UsingSdlHint hint{SDL_HINT_RENDER_SCALE_QUALITY, options::Options.LinearFiltering ? "linear" : "nearest"}; + vScreenTex = SDL_CreateTexture + ( + winmain::Renderer, + SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STREAMING, + width, height + ); + SDL_SetTextureBlendMode(vScreenTex, SDL_BLENDMODE_NONE); + } --memory::critical_allocation; } @@ -60,154 +76,73 @@ void render::uninit() many_sprites = 0; many_dirty = 0; many_balls = 0; + SDL_DestroyTexture(vScreenTex); } void render::update() { - rectangle_type overlapRect{}; + unpaint_balls(); - auto dirtyPtr = dirty_list; - for (int index = 0; index < many_dirty; ++dirtyPtr, ++index) + // Clip dirty sprites with vScreen, clear clipping (dirty) rectangles + for (int index = 0; index < many_dirty; ++index) { - auto curSprite = *dirtyPtr; - if ((*dirtyPtr)->VisualType != VisualTypes::None) + bool clearSprite = false; + auto curSprite = dirty_list[index]; + switch (curSprite->VisualType) { - if ((*dirtyPtr)->VisualType == VisualTypes::Sprite) - { - if (curSprite->BmpRectCopy.Width > 0) - maths::enclosing_box(&curSprite->BmpRectCopy, &curSprite->BmpRect, &curSprite->DirtyRect); + case VisualTypes::Sprite: + if (curSprite->DirtyRectPrev.Width > 0) + maths::enclosing_box(&curSprite->DirtyRectPrev, &curSprite->BmpRect, &curSprite->DirtyRect); - if (!maths::rectangle_clip(&curSprite->DirtyRect, &vscreen_rect, &curSprite->DirtyRect)) - { - curSprite->DirtyRect.Width = -1; - continue; - } - - auto yPos = curSprite->DirtyRect.YPosition; - auto width = curSprite->DirtyRect.Width; - auto xPos = curSprite->DirtyRect.XPosition; - auto height = curSprite->DirtyRect.Height; - zdrv::fill(&zscreen, width, height, xPos, yPos, 0xFFFF); - if (background_bitmap) - gdrv::copy_bitmap(&vscreen, width, height, xPos, yPos, background_bitmap, xPos, yPos); - else - gdrv::fill_bitmap(&vscreen, width, height, xPos, yPos, 0); - } - } - else - { - if (!maths::rectangle_clip(&curSprite->BmpRect, &vscreen_rect, &curSprite->DirtyRect)) - { + if (maths::rectangle_clip(&curSprite->DirtyRect, &vscreen_rect, &curSprite->DirtyRect)) + clearSprite = true; + else curSprite->DirtyRect.Width = -1; - continue; - } - if (!curSprite->Bmp) - { - auto yPos = curSprite->DirtyRect.YPosition; - auto width = curSprite->DirtyRect.Width; - auto xPos = curSprite->DirtyRect.XPosition; - auto height = curSprite->DirtyRect.Height; - zdrv::fill(&zscreen, width, height, xPos, yPos, 0xFFFF); - if (background_bitmap) - gdrv::copy_bitmap(&vscreen, width, height, xPos, yPos, background_bitmap, xPos, yPos); - else - gdrv::fill_bitmap(&vscreen, width, height, xPos, yPos, 0); - } + break; + case VisualTypes::None: + if (maths::rectangle_clip(&curSprite->BmpRect, &vscreen_rect, &curSprite->DirtyRect)) + clearSprite = !curSprite->Bmp; + else + curSprite->DirtyRect.Width = -1; + break; + default: break; + } + + if (clearSprite) + { + auto yPos = curSprite->DirtyRect.YPosition; + auto width = curSprite->DirtyRect.Width; + auto xPos = curSprite->DirtyRect.XPosition; + auto height = curSprite->DirtyRect.Height; + zdrv::fill(&zscreen, width, height, xPos, yPos, 0xFFFF); + if (background_bitmap) + gdrv::copy_bitmap(&vscreen, width, height, xPos, yPos, background_bitmap, xPos, yPos); + else + gdrv::fill_bitmap(&vscreen, width, height, xPos, yPos, 0); } } - dirtyPtr = dirty_list; + // Paint dirty rectangles of dirty sprites for (int index = 0; index < many_dirty; ++index) { - auto sprite = *dirtyPtr; - if ((*dirtyPtr)->DirtyRect.Width > 0 && (sprite->VisualType == VisualTypes::None || sprite->VisualType == + auto sprite = dirty_list[index]; + if (sprite->DirtyRect.Width > 0 && (sprite->VisualType == VisualTypes::None || sprite->VisualType == VisualTypes::Sprite)) - repaint(*dirtyPtr); - ++dirtyPtr; + repaint(sprite); } paint_balls(); - if (blit) + + // In the original, this used to blit dirty sprites and balls + for (int index = 0; index < many_dirty; ++index) { - auto xPos = vscreen.XPosition + offset_x; - auto yPos = vscreen.YPosition + offset_y; - dirtyPtr = dirty_list; - for (int index = 0; index < many_dirty; ++dirtyPtr, ++index) - { - auto sprite = *dirtyPtr; - auto dirtyRect = &(*dirtyPtr)->DirtyRect; - auto width2 = (*dirtyPtr)->DirtyRect.Width; - if (width2 > 0) - gdrv::blit( - &vscreen, - dirtyRect->XPosition, - dirtyRect->YPosition, - dirtyRect->XPosition + xPos, - dirtyRect->YPosition + yPos, - width2, - dirtyRect->Height); - - auto rect = &sprite->BmpRectCopy; - rect->XPosition = dirtyRect->XPosition; - rect->YPosition = dirtyRect->YPosition; - rect->Width = dirtyRect->Width; - rect->Height = dirtyRect->Height; - - if (sprite->UnknownFlag != 0) - remove_sprite(sprite); - } - - dirtyPtr = ball_list; - for (int index = 0; index < many_balls; ++dirtyPtr, ++index) - { - auto rectCopy = &(*dirtyPtr)->BmpRectCopy; - auto dirtyRect = &(*dirtyPtr)->DirtyRect; - if (maths::overlapping_box(dirtyRect, rectCopy, &overlapRect) && dirtyRect->Width > 0) - { - if (overlapRect.Width > 0) - gdrv::blit( - &vscreen, - overlapRect.XPosition, - overlapRect.YPosition, - overlapRect.XPosition + xPos, - overlapRect.YPosition + yPos, - overlapRect.Width, - overlapRect.Height); - } - else - { - if (dirtyRect->Width > 0) - gdrv::blit( - &vscreen, - dirtyRect->XPosition, - dirtyRect->YPosition, - dirtyRect->XPosition + xPos, - dirtyRect->YPosition + yPos, - dirtyRect->Width, - dirtyRect->Height); - if (rectCopy->Width > 0) - gdrv::blit( - &vscreen, - rectCopy->XPosition, - rectCopy->YPosition, - rectCopy->XPosition + xPos, - rectCopy->YPosition + yPos, - rectCopy->Width, - rectCopy->Height); - } - } + auto sprite = dirty_list[index]; + sprite->DirtyRectPrev = sprite->DirtyRect; + if (sprite->UnknownFlag != 0) + remove_sprite(sprite); } many_dirty = 0; - unpaint_balls(); -} - - -void render::paint() -{ - paint_balls(); - gdrv::blat(&vscreen, vscreen.XPosition, vscreen.YPosition); - unpaint_balls(); } void render::sprite_modified(render_sprite_type_struct* sprite) @@ -260,7 +195,7 @@ render_sprite_type_struct* render::create_sprite(VisualTypes visualType, gdrv_bi sprite->ZMapOffestY = xPosition - zmap_offset; sprite->ZMapOffestX = yPosition - zmap_offsetY; } - sprite->BmpRectCopy = sprite->BmpRect; + sprite->DirtyRectPrev = sprite->BmpRect; if (visualType == VisualTypes::Ball) { ball_list[many_balls++] = sprite; @@ -389,10 +324,10 @@ void render::repaint(struct render_sprite_type_struct* sprite) return; for (int index = 0; index < sprite->SpriteCount; index++) { - render_sprite_type_struct* curSprite = sprite->SpriteArray[index]; - if (!curSprite->UnknownFlag && curSprite->Bmp) + auto refSprite = sprite->SpriteArray[index]; + if (!refSprite->UnknownFlag && refSprite->Bmp) { - if (maths::rectangle_clip(&curSprite->BmpRect, &sprite->DirtyRect, &clipRect)) + if (maths::rectangle_clip(&refSprite->BmpRect, &sprite->DirtyRect, &clipRect)) zdrv::paint( clipRect.Width, clipRect.Height, @@ -402,12 +337,12 @@ void render::repaint(struct render_sprite_type_struct* sprite) &zscreen, clipRect.XPosition, clipRect.YPosition, - curSprite->Bmp, - clipRect.XPosition - curSprite->BmpRect.XPosition, - clipRect.YPosition - curSprite->BmpRect.YPosition, - curSprite->ZMap, - clipRect.XPosition + curSprite->ZMapOffestY - curSprite->BmpRect.XPosition, - clipRect.YPosition + curSprite->ZMapOffestX - curSprite->BmpRect.YPosition); + refSprite->Bmp, + clipRect.XPosition - refSprite->BmpRect.XPosition, + clipRect.YPosition - refSprite->BmpRect.YPosition, + refSprite->ZMap, + clipRect.XPosition + refSprite->ZMapOffestY - refSprite->BmpRect.XPosition, + clipRect.YPosition + refSprite->ZMapOffestX - refSprite->BmpRect.YPosition); } } } @@ -415,62 +350,55 @@ void render::repaint(struct render_sprite_type_struct* sprite) void render::paint_balls() { - int ballCount = many_balls; - if (many_balls > 1) + // Sort ball sprites by depth + for (auto i = 0; i < many_balls; i++) { - for (int index = 0; index < ballCount; index++) + for (auto j = i; j < many_balls / 2; ++j) { - for (int i = index; i < ballCount / 2; ++i) + auto ballA = ball_list[j]; + auto ballB = ball_list[i]; + if (ballB->Depth > ballA->Depth) { - auto curBall = ball_list[i]; - auto firstBallPtr = &ball_list[index]; - if ((*firstBallPtr)->Depth > curBall->Depth) - { - auto firstBall = *firstBallPtr; - *firstBallPtr = curBall; - ball_list[i] = firstBall; - } + ball_list[i] = ballA; + ball_list[j] = ballB; } } } - auto ballPtr = ball_list; - auto ballBmpPtr = ball_bitmap; - for (int index2 = 0; index2 < many_balls; ++index2) + // For balls that clip vScreen: save original vScreen contents and paint ball bitmap. + for (auto index = 0; index < many_balls; ++index) { - struct render_sprite_type_struct* sprite = *ballPtr; - rectangle_type* rect2 = &(*ballPtr)->DirtyRect; - if ((*ballPtr)->Bmp && maths::rectangle_clip(&sprite->BmpRect, &vscreen_rect, &(*ballPtr)->DirtyRect)) + auto ball = ball_list[index]; + auto dirty = &ball->DirtyRect; + if (ball->Bmp && maths::rectangle_clip(&ball->BmpRect, &vscreen_rect, &ball->DirtyRect)) { - int xPos = rect2->XPosition; - int yPos = rect2->YPosition; - gdrv::copy_bitmap(ballBmpPtr, rect2->Width, rect2->Height, 0, 0, &vscreen, xPos, yPos); + int xPos = dirty->XPosition; + int yPos = dirty->YPosition; + gdrv::copy_bitmap(&ball_bitmap[index], dirty->Width, dirty->Height, 0, 0, &vscreen, xPos, yPos); zdrv::paint_flat( - rect2->Width, - rect2->Height, + dirty->Width, + dirty->Height, &vscreen, xPos, yPos, &zscreen, xPos, yPos, - sprite->Bmp, - xPos - sprite->BmpRect.XPosition, - yPos - sprite->BmpRect.YPosition, - sprite->Depth); + ball->Bmp, + xPos - ball->BmpRect.XPosition, + yPos - ball->BmpRect.YPosition, + ball->Depth); } else { - rect2->Width = -1; + dirty->Width = -1; } - - ++ballBmpPtr; - ++ballPtr; } } void render::unpaint_balls() { + // Restore portions of vScreen saved during previous paint_balls call. for (int index = many_balls - 1; index >= 0; index--) { auto curBall = ball_list[index]; @@ -485,63 +413,54 @@ void render::unpaint_balls() 0, 0); - curBall->BmpRectCopy = curBall->DirtyRect; + curBall->DirtyRectPrev = curBall->DirtyRect; } } -void render::shift(int offsetX, int offsetY, int xSrc, int ySrc, int DestWidth, int DestHeight) +void render::shift(int offsetX, int offsetY) { offset_x += offsetX; offset_y += offsetY; - paint_balls(); - gdrv::blit( - &vscreen, - xSrc, - ySrc, - xSrc + offset_x + vscreen.XPosition, - ySrc + offset_y + vscreen.YPosition, - DestWidth, - DestHeight); - unpaint_balls(); } void render::build_occlude_list() { ++memory::critical_allocation; render_sprite_type_struct** spriteArr = nullptr; - auto spritePtr1 = sprite_list; - for (int index = 0; index < many_sprites; ++index, ++spritePtr1) + for (int index = 0; index < many_sprites; ++index) { - auto curSprite = *spritePtr1; - if ((*spritePtr1)->SpriteArray) + auto mainSprite = sprite_list[index]; + if (mainSprite->SpriteArray) { - memory::free((*spritePtr1)->SpriteArray); - curSprite->SpriteArray = nullptr; - curSprite->SpriteCount = 0; + memory::free(mainSprite->SpriteArray); + mainSprite->SpriteArray = nullptr; + mainSprite->SpriteCount = 0; } - if (!curSprite->UnknownFlag && curSprite->BoundingRect.Width != -1) + + if (!mainSprite->UnknownFlag && mainSprite->BoundingRect.Width != -1) { if (!spriteArr) spriteArr = memory::allocate(1000); + int occludeCount = 0; - auto spritePtr2 = sprite_list; - for (int i = 0; i < many_sprites; ++i, ++spritePtr2) + for (int i = 0; i < many_sprites; ++i) { - auto sprite = *spritePtr2; - if (!sprite->UnknownFlag - && sprite->BoundingRect.Width != -1 - && maths::rectangle_clip(&curSprite->BoundingRect, &sprite->BoundingRect, nullptr) + auto refSprite = sprite_list[i]; + if (!refSprite->UnknownFlag + && refSprite->BoundingRect.Width != -1 + && maths::rectangle_clip(&mainSprite->BoundingRect, &refSprite->BoundingRect, nullptr) && spriteArr) { - spriteArr[occludeCount++] = sprite; + spriteArr[occludeCount++] = refSprite; } } - if (!curSprite->UnknownFlag && curSprite->Bmp && occludeCount < 2) + + if (!mainSprite->UnknownFlag && mainSprite->Bmp && occludeCount < 2) occludeCount = 0; if (occludeCount) { - curSprite->SpriteArray = memory::realloc(spriteArr, sizeof(void*) * occludeCount); - curSprite->SpriteCount = occludeCount; + mainSprite->SpriteArray = memory::realloc(spriteArr, sizeof(void*) * occludeCount); + mainSprite->SpriteCount = occludeCount; spriteArr = nullptr; } } @@ -644,3 +563,81 @@ void render::SpriteViewer(bool* show) } ImGui::End(); } + +void render::BlitVScreen() +{ + int pitch = 0; + ColorRgba* lockedPixels; + SDL_LockTexture + ( + vScreenTex, + nullptr, + reinterpret_cast(&lockedPixels), + &pitch + ); + assertm(pitch == vscreen.Width * sizeof(ColorRgba), "Padding on vScreen texture"); + + if (offset_x == 0 && offset_y == 0) + { + // No offset - direct copy + std::memcpy(lockedPixels, vscreen.BmpBufPtr1, vscreen.Width * vscreen.Height * sizeof(ColorRgba)); + } + else + { + // Copy offset table and fixed side bar + auto tableWidth = pb::MainTable->Width; + auto scoreWidth = vscreen.Width - pb::MainTable->Width; + auto tableStride = tableWidth * sizeof(ColorRgba); + auto scoreStride = scoreWidth * sizeof(ColorRgba); + auto srcScorePtr = &vscreen.BmpBufPtr1[tableWidth]; + + auto xSrc = 0, ySrc = 0, xDst = offset_x, yDst = offset_y, height = vscreen.Height; + + // Negative dst == positive src offset + if (xDst < 0) + { + xSrc -= xDst; + xDst = 0; + } + if (yDst < 0) + { + ySrc -= yDst; + yDst = 0; + } + + if (xSrc) + { + tableStride -= xSrc * sizeof(ColorRgba); + } + if (xDst) + { + tableStride -= xDst * sizeof(ColorRgba); + tableWidth -= xDst; + scoreWidth += xDst; + } + if (ySrc) + height -= ySrc; + + auto srcBmpPtr = &vscreen.BmpBufPtr1[vscreen.Width * ySrc + xSrc]; + auto dstPtr = &lockedPixels[vscreen.Width * yDst + xDst]; + for (int y = height; y > 0; --y) + { + std::memcpy(dstPtr, srcBmpPtr, tableStride); + dstPtr += tableWidth; + std::memcpy(dstPtr, srcScorePtr, scoreStride); + dstPtr += scoreWidth; + + srcBmpPtr += vscreen.Stride; + srcScorePtr += vscreen.Stride; + } + } + + + SDL_UnlockTexture(vScreenTex); +} + +void render::PresentVScreen() +{ + BlitVScreen(); + SDL_RenderCopy(winmain::Renderer, vScreenTex, nullptr, &DestinationRect); +} diff --git a/SpaceCadetPinball/render.h b/SpaceCadetPinball/render.h index 7075e7b..832e604 100644 --- a/SpaceCadetPinball/render.h +++ b/SpaceCadetPinball/render.h @@ -18,7 +18,7 @@ struct render_sprite_type_struct char UnknownFlag; VisualTypes VisualType; int16_t Depth; - rectangle_type BmpRectCopy; + rectangle_type DirtyRectPrev; int ZMapOffestY; int ZMapOffestX; rectangle_type DirtyRect; @@ -31,20 +31,12 @@ struct render_sprite_type_struct class render { public: - static int blit; - static int many_dirty, many_sprites, many_balls; - static render_sprite_type_struct **dirty_list, **sprite_list, **ball_list; - static zmap_header_type* background_zmap; - static int zmap_offset, zmap_offsetY, offset_x, offset_y; - static float zscaler, zmin, zmax; - static rectangle_type vscreen_rect; - static gdrv_bitmap8 vscreen, *background_bitmap, ball_bitmap[20]; - static zmap_header_type zscreen; + static gdrv_bitmap8 vscreen, *background_bitmap; + static SDL_Rect DestinationRect; static void init(gdrv_bitmap8* bmp, float zMin, float zScaler, int width, int height); static void uninit(); static void update(); - static void paint(); static void sprite_modified(render_sprite_type_struct* sprite); static render_sprite_type_struct* create_sprite(VisualTypes visualType, gdrv_bitmap8* bmp, zmap_header_type* zMap, @@ -55,11 +47,24 @@ public: int yPos); static void sprite_set_bitmap(render_sprite_type_struct* sprite, gdrv_bitmap8* bmp); static void set_background_zmap(struct zmap_header_type* zMap, int offsetX, int offsetY); - static void ball_set(render_sprite_type_struct* sprite, gdrv_bitmap8* bmp, float depth, int xPos, int yPos); + static void ball_set(render_sprite_type_struct* sprite, gdrv_bitmap8* bmp, float depth, int xPos, int yPos); + static void shift(int offsetX, int offsetY); + static void build_occlude_list(); + static void SpriteViewer(bool* show); + static void PresentVScreen(); +private: + static int many_dirty, many_sprites, many_balls; + static render_sprite_type_struct **dirty_list, **sprite_list, **ball_list; + static zmap_header_type* background_zmap; + static int zmap_offset, zmap_offsetY, offset_x, offset_y; + static float zscaler, zmin, zmax; + static rectangle_type vscreen_rect; + static gdrv_bitmap8 ball_bitmap[20]; + static zmap_header_type zscreen; + static SDL_Texture* vScreenTex; + static void repaint(struct render_sprite_type_struct* sprite); static void paint_balls(); static void unpaint_balls(); - static void shift(int offsetX, int offsetY, int xSrc, int ySrc, int DestWidth, int DestHeight); - static void build_occlude_list(); - static void SpriteViewer(bool* show); + static void BlitVScreen(); }; diff --git a/SpaceCadetPinball/score.cpp b/SpaceCadetPinball/score.cpp index 7562fa5..3c33e5f 100644 --- a/SpaceCadetPinball/score.cpp +++ b/SpaceCadetPinball/score.cpp @@ -207,16 +207,7 @@ void score::erase(scoreStruct* score, int blitFlag) score->OffsetX, score->OffsetY); else - gdrv::fill_bitmap(&render::vscreen, score->Width, score->Height, score->OffsetX, score->OffsetY, 0); - if (blitFlag) - gdrv::blit( - &render::vscreen, - score->OffsetX, - score->OffsetY, - score->OffsetX + render::vscreen.XPosition, - score->OffsetY + render::vscreen.YPosition, - score->Width, - score->Height); + gdrv::fill_bitmap(&render::vscreen, score->Width, score->Height, score->OffsetX, score->OffsetY, 0); } } @@ -255,15 +246,7 @@ void score::update(scoreStruct* score) else gdrv::copy_bitmap(&render::vscreen, width, height, x, y, bmp, 0, 0); } - } - gdrv::blit( - &render::vscreen, - score->OffsetX, - score->OffsetY, - score->OffsetX + render::vscreen.XPosition, - score->OffsetY + render::vscreen.YPosition, - score->Width, - score->Height); + } } } diff --git a/SpaceCadetPinball/winmain.cpp b/SpaceCadetPinball/winmain.cpp index e5167d8..1a40297 100644 --- a/SpaceCadetPinball/winmain.cpp +++ b/SpaceCadetPinball/winmain.cpp @@ -212,7 +212,7 @@ int winmain::WinMain(LPCSTR lpCmdLine) gdrv::create_bitmap(&gfr_display, 400, 15, 400, false); } - gdrv::blit(&gfr_display, 0, 0, 0, 30, 300, 10); + gdrv::copy_bitmap(&render::vscreen, 300, 10, 0, 30, &gfr_display, 0, 0); gdrv::fill_bitmap(&gfr_display, 300, 10, 0, 0, 0); } } @@ -284,7 +284,7 @@ int winmain::WinMain(LPCSTR lpCmdLine) RenderUi(); SDL_RenderClear(renderer); - gdrv::BlitScreen(); + render::PresentVScreen(); ImGui::Render(); ImGuiSDL::Render(ImGui::GetDrawData()); @@ -307,7 +307,6 @@ int winmain::WinMain(LPCSTR lpCmdLine) midi::music_shutdown(); pb::uninit(); Sound::Close(); - gdrv::uninit(); ImGuiSDL::Deinitialize(); ImGui_ImplSDL2_Shutdown(); SDL_DestroyRenderer(renderer); @@ -649,8 +648,6 @@ int winmain::event_handler(const SDL_Event* event) midi::play_pb_theme(0); no_time_loss = 1; has_focus = 1; - gdrv::get_focus(); - pb::paint(); break; case SDL_WINDOWEVENT_FOCUS_LOST: case SDL_WINDOWEVENT_HIDDEN: @@ -660,7 +657,6 @@ int winmain::event_handler(const SDL_Event* event) Sound::Deactivate(); midi::music_stop(); has_focus = 0; - gdrv::get_focus(); pb::loose_focus(); break; case SDL_WINDOWEVENT_SIZE_CHANGED: @@ -698,7 +694,6 @@ void winmain::memalloc_failure() { midi::music_stop(); Sound::Close(); - gdrv::uninit(); char* caption = pinball::get_rc_string(170, 0); char* text = pinball::get_rc_string(179, 0); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, caption, text, MainWindow); @@ -723,7 +718,10 @@ void winmain::a_dialog() ImGui::TextUnformatted("Decompiled -> Ported to SDL"); if (ImGui::SmallButton("Project home: https://github.com/k4zmu2a/SpaceCadetPinball")) { +#if SDL_VERSION_ATLEAST(2, 0, 14) + // Relatively new feature, skip with older SDL SDL_OpenURL("https://github.com/k4zmu2a/SpaceCadetPinball"); +#endif } ImGui::Separator(); diff --git a/SpaceCadetPinball/zdrv.cpp b/SpaceCadetPinball/zdrv.cpp index 191400e..3eee96b 100644 --- a/SpaceCadetPinball/zdrv.cpp +++ b/SpaceCadetPinball/zdrv.cpp @@ -127,9 +127,9 @@ void zdrv::CreatePreview(zmap_header_type& zMap) for (auto x = 0; x < zMap.Width; x++) { auto depth = static_cast((0xffff - *src++) / 0xff); - color.rgba.peRed = depth; - color.rgba.peGreen = depth; - color.rgba.peBlue = depth; + color.rgba.Blue = depth; + color.rgba.Green = depth; + color.rgba.Red = depth; *dst++ = color; } src += zMap.Stride - zMap.Width;