From 95dba45a0e9b2e263e52d05d9e71f0c6ccf9041a Mon Sep 17 00:00:00 2001 From: Jon Thysell Date: Sun, 21 Nov 2021 16:29:38 -0800 Subject: [PATCH] Added offscreen rendering via WindowBuffer to remove drawing flicker --- src/GameWindow.c | 11 +++++++-- src/GameWindow.h | 2 ++ src/LevelEndScene.c | 3 +-- src/MacLO.pi.bin | Bin 16000 -> 16000 bytes src/MacLO.pi.rsrc.bin | Bin 34432 -> 34432 bytes src/PlayScene.c | 4 +-- src/TitleScene.c | 2 -- src/WindowBuffer.c | 56 ++++++++++++++++++++++++++++++++++++++++++ src/WindowBuffer.h | 17 +++++++++++++ 9 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 src/WindowBuffer.c create mode 100644 src/WindowBuffer.h diff --git a/src/GameWindow.c b/src/GameWindow.c index b671aa5..d59714d 100644 --- a/src/GameWindow.c +++ b/src/GameWindow.c @@ -27,8 +27,12 @@ void GameWindow_Init(GameWindow *pGameWindow) // Load snd resources Sounds_Init(&(pGameWindow->Sounds)); - // Set port for first draw + // Initialize window buffer + WindowBuffer_Init(&(pGameWindow->WindowBuffer), pGameWindow->Window); + + // Setup graphics before first draw SetPort(pGameWindow->Window); + FillRect(&(pGameWindow->Window->portRect), WindowPattern); GameWindow_SetScene(pGameWindow, Title); } @@ -38,9 +42,10 @@ void GameWindow_Draw(const GameWindow *pGameWindow, bool fullRefresh) GrafPtr oldPort; const Rect *pContentRect = &(pGameWindow->Window->portRect); + // Save the current port GetPort(&oldPort); - SetPort(pGameWindow->Window); + WindowBuffer_StartDraw(&(pGameWindow->WindowBuffer)); if (fullRefresh) { @@ -64,6 +69,8 @@ void GameWindow_Draw(const GameWindow *pGameWindow, bool fullRefresh) break; } + WindowBuffer_EndDraw(&(pGameWindow->WindowBuffer)); + SetPort(oldPort); } diff --git a/src/GameWindow.h b/src/GameWindow.h index 367e8c2..8248fcd 100644 --- a/src/GameWindow.h +++ b/src/GameWindow.h @@ -5,6 +5,7 @@ #define GAMEWINDOW_H #include "MacCommon.h" +#include "WindowBuffer.h" #include "GameEngine.h" #include "Bitmaps.h" #include "Sounds.h" @@ -15,6 +16,7 @@ typedef struct sGameWindow { WindowPtr Window; + WindowBuffer WindowBuffer; GameEngine Engine; Bitmaps Bitmaps; Sounds Sounds; diff --git a/src/LevelEndScene.c b/src/LevelEndScene.c index c28721d..48b40f2 100644 --- a/src/LevelEndScene.c +++ b/src/LevelEndScene.c @@ -85,13 +85,12 @@ void LevelEndScene_Click(GameWindow *pGameWindow, const Point *pPosition) { if (PtInRect(*pPosition, &(pGameWindow->LevelEndScene.RetryButtonRect))) { - Sounds_PlayRetrySnd(&(pGameWindow->Sounds)); GameEngine_ResetLevel(&(pGameWindow->Engine)); GameWindow_SetScene(pGameWindow, Play); + Sounds_PlayRetrySnd(&(pGameWindow->Sounds)); } else if (PtInRect(*pPosition, &(pGameWindow->LevelEndScene.NextButtonRect))) { - Sounds_PlayClickSnd(&(pGameWindow->Sounds)); GameEngine_NextLevel(&(pGameWindow->Engine)); if (GameEngine_IsGameOver(&(pGameWindow->Engine))) diff --git a/src/MacLO.pi.bin b/src/MacLO.pi.bin index 208f15fab626ed1aa4a58d82c0f889efa095da72..5b06658fefc5fa0a166daf0ec5959ec3073f3ba3 100644 GIT binary patch delta 2419 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 SE&toV=gzX@M-+CI>;4N<$(Zv1 diff --git a/src/MacLO.pi.rsrc.bin b/src/MacLO.pi.rsrc.bin index 62104544ee3ffeb4e80d2c18556d79f5a9c5a476..f1e7fefbf9912eceb1e3fd95b530e7dd9b832ae1 100644 GIT binary patch delta 33 lcmZqZWoqbUny{B~*~ERO+y|64Fn~ep&&iC8O^X{CTLH{e3vvJe delta 33 lcmZqZWoqbUny{BqZsI;u?k(~`3}Ddub21}i)8YokRsgs>3Vi?o diff --git a/src/PlayScene.c b/src/PlayScene.c index ce8d252..69c42e4 100644 --- a/src/PlayScene.c +++ b/src/PlayScene.c @@ -161,9 +161,9 @@ void PlayScene_Click(GameWindow *pGameWindow, const Point *pPosition) if (PtInRect(*pPosition, &lightRect)) { - Sounds_PlayClickSnd(&(pGameWindow->Sounds)); GameEngine_ToggleLights(&(pGameWindow->Engine), c, r); GameWindow_Draw(pGameWindow, false); + Sounds_PlayClickSnd(&(pGameWindow->Sounds)); break; } } @@ -183,9 +183,9 @@ void PlayScene_Click(GameWindow *pGameWindow, const Point *pPosition) if (PtInRect(*pPosition, &(pGameWindow->PlayScene.RetryButtonRect))) { - Sounds_PlayRetrySnd(&(pGameWindow->Sounds)); GameEngine_ResetLevel(&(pGameWindow->Engine)); GameWindow_Draw(pGameWindow, false); + Sounds_PlayRetrySnd(&(pGameWindow->Sounds)); } else if (PtInRect(*pPosition, &(pGameWindow->PlayScene.SoundButtonRect))) { diff --git a/src/TitleScene.c b/src/TitleScene.c index ee3ebc9..ecc2e22 100644 --- a/src/TitleScene.c +++ b/src/TitleScene.c @@ -54,13 +54,11 @@ void TitleScene_Click(GameWindow *pGameWindow, const Point *pPosition) { if (PtInRect(*pPosition, &(pGameWindow->TitleScene.SetARect))) { - Sounds_PlayClickSnd(&(pGameWindow->Sounds)); GameEngine_NewGame(&(pGameWindow->Engine), false); GameWindow_SetScene(pGameWindow, Play); } else if (PtInRect(*pPosition, &(pGameWindow->TitleScene.SetBRect))) { - Sounds_PlayClickSnd(&(pGameWindow->Sounds)); GameEngine_NewGame(&(pGameWindow->Engine), true); GameWindow_SetScene(pGameWindow, Play); } diff --git a/src/WindowBuffer.c b/src/WindowBuffer.c new file mode 100644 index 0000000..477a3b2 --- /dev/null +++ b/src/WindowBuffer.c @@ -0,0 +1,56 @@ +// Copyright (c) Jon Thysell +// Licensed under the MIT License. + +#include "WindowBuffer.h" +#include "MacCommon.h" + +void WindowBuffer_Init(WindowBuffer *pWindowBuffer, const WindowPtr window) +{ + int16_t width, height; + BitMap newBits; + + pWindowBuffer->Window = window; + + // Create a BitMap as the backing for the buffer, + // since System 6 doesn't have fancy GWorlds + width = window->portRect.right - window->portRect.left; + height = window->portRect.bottom - window->portRect.top; + SetRect(&newBits.bounds, 0, 0, width, height); + newBits.rowBytes = ((width + 31)/32) * 4; + newBits.baseAddr = NewPtr(height * newBits.rowBytes); + + // Clear the bitmap + CopyBits(&newBits, + &newBits, + &newBits.bounds, + &newBits.bounds, + srcXor, + nil); + + // Create the buffer and get it ready + pWindowBuffer->Buffer = (GrafPtr)NewPtr(sizeof(GrafPort)); + OpenPort(pWindowBuffer->Buffer); + SetPort(pWindowBuffer->Buffer); + + pWindowBuffer->Buffer->portRect = newBits.bounds; + RectRgn(pWindowBuffer->Buffer->visRgn, &newBits.bounds); + SetPortBits(&newBits); +} + +void WindowBuffer_StartDraw(const WindowBuffer *pWindowBuffer) +{ + // Set the buffer as the port for future QuickDraw commands + SetPort(pWindowBuffer->Buffer); +} + +void WindowBuffer_EndDraw(const WindowBuffer *pWindowBuffer) +{ + SetPort(pWindowBuffer->Window); + + // Copy the buffer to the window + CopyBits(&(pWindowBuffer->Buffer->portBits), + &(pWindowBuffer->Window->portBits), + &(pWindowBuffer->Buffer->portRect), + &(pWindowBuffer->Window->portRect), + srcCopy, nil); +} diff --git a/src/WindowBuffer.h b/src/WindowBuffer.h new file mode 100644 index 0000000..7350572 --- /dev/null +++ b/src/WindowBuffer.h @@ -0,0 +1,17 @@ +// Copyright (c) Jon Thysell +// Licensed under the MIT License. + +#ifndef WINDOWBUFFER_H +#define WINDOWBUFFER_H + +typedef struct sWindowBuffer +{ + WindowPtr Window; + GrafPtr Buffer; +} WindowBuffer; + +void WindowBuffer_Init(WindowBuffer *pWindowBuffer, const WindowPtr window); +void WindowBuffer_StartDraw(const WindowBuffer *pWindowBuffer); +void WindowBuffer_EndDraw(const WindowBuffer *pWindowBuffer); + +#endif \ No newline at end of file