diff --git a/src/gba/gba-serialize.c b/src/gba/gba-serialize.c index a07848c53..a27136a9b 100644 --- a/src/gba/gba-serialize.c +++ b/src/gba/gba-serialize.c @@ -130,11 +130,15 @@ static bool _loadPNGState(struct GBA* gba, struct VFile* vf) { PNGReadClose(png, info, end); return false; } + uint32_t pixels[VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4]; + PNGInstallChunkHandler(png, gba, _loadPNGChunkHandler, "gbAs"); PNGReadHeader(png, info); - PNGIgnorePixels(png, info); + PNGReadPixels(png, info, &pixels, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, VIDEO_HORIZONTAL_PIXELS); PNGReadFooter(png, end); PNGReadClose(png, info, end); + gba->video.renderer->putPixels(gba->video.renderer, VIDEO_HORIZONTAL_PIXELS, pixels); + GBASyncPostFrame(gba->sync); return true; } diff --git a/src/gba/gba-thread.c b/src/gba/gba-thread.c index 37ccabdf7..6d7cbff83 100644 --- a/src/gba/gba-thread.c +++ b/src/gba/gba-thread.c @@ -535,6 +535,10 @@ void GBASyncPostFrame(struct GBASync* sync) { MutexUnlock(&sync->videoFrameMutex); struct GBAThread* thread = GBAThreadGetContext(); + if (!thread) { + return; + } + if (thread->rewindBuffer) { --thread->rewindBufferNext; if (thread->rewindBufferNext <= 0) { @@ -554,10 +558,12 @@ bool GBASyncWaitFrameStart(struct GBASync* sync, int frameskip) { MutexLock(&sync->videoFrameMutex); ConditionWake(&sync->videoFrameRequiredCond); - if (!sync->videoFrameOn) { + if (!sync->videoFrameOn && !sync->videoFramePending) { return false; } - ConditionWait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex); + if (!sync->videoFramePending) { + ConditionWait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex); + } sync->videoFramePending = 0; sync->videoFrameSkip = frameskip; return true; diff --git a/src/gba/gba-video.h b/src/gba/gba-video.h index 9f22b2412..6e5838ce0 100644 --- a/src/gba/gba-video.h +++ b/src/gba/gba-video.h @@ -185,6 +185,7 @@ struct GBAVideoRenderer { void (*finishFrame)(struct GBAVideoRenderer* renderer); void (*getPixels)(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels); + void (*putPixels)(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels); uint16_t* palette; uint16_t* vram; diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c index f53e8dd67..46562f673 100644 --- a/src/gba/renderers/video-software.c +++ b/src/gba/renderers/video-software.c @@ -30,6 +30,7 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y); static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer); static void GBAVideoSoftwareRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels); +static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels); static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer); static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value); @@ -69,6 +70,7 @@ void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) { renderer->d.drawScanline = GBAVideoSoftwareRendererDrawScanline; renderer->d.finishFrame = GBAVideoSoftwareRendererFinishFrame; renderer->d.getPixels = GBAVideoSoftwareRendererGetPixels; + renderer->d.putPixels = GBAVideoSoftwareRendererPutPixels; } static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) { @@ -510,6 +512,16 @@ static void GBAVideoSoftwareRendererGetPixels(struct GBAVideoRenderer* renderer, *pixels = softwareRenderer->outputBuffer; } +static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels) { + struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer; + + uint32_t* colorPixels = pixels; + unsigned i; + for (i = 0; i < VIDEO_VERTICAL_PIXELS; ++i) { + memmove(&softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * i], &colorPixels[stride * i], VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL); + } +} + static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) { renderer->bg[0].enabled = renderer->dispcnt.bg0Enable; renderer->bg[1].enabled = renderer->dispcnt.bg1Enable; diff --git a/src/util/png-io.c b/src/util/png-io.c index fdc6239f0..b6408c912 100644 --- a/src/util/png-io.c +++ b/src/util/png-io.c @@ -135,6 +135,37 @@ bool PNGIgnorePixels(png_structp png, png_infop info) { return true; } +bool PNGReadPixels(png_structp png, png_infop info, void* pixels, unsigned width, unsigned height, unsigned stride) { + if (setjmp(png_jmpbuf(png))) { + return false; + } + + uint8_t* pixelData = pixels; + unsigned pngHeight = png_get_image_height(png, info); + if (height > pngHeight) { + height = pngHeight; + } + + unsigned pngWidth = png_get_image_width(png, info); + if (width > pngWidth) { + width = pngWidth; + } + + unsigned i; + png_bytep row = malloc(png_get_rowbytes(png, info)); + for (i = 0; i < height; ++i) { + png_read_row(png, row, 0); + unsigned x; + for (x = 0; x < width; ++x) { + pixelData[stride * i * 4 + x * 4] = row[x * 3]; + pixelData[stride * i * 4 + x * 4 + 1] = row[x * 3 + 1]; + pixelData[stride * i * 4 + x * 4 + 2] = row[x * 3 + 2]; + } + } + free(row); + return true; +} + bool PNGReadFooter(png_structp png, png_infop end) { if (setjmp(png_jmpbuf(png))) { return false; diff --git a/src/util/png-io.h b/src/util/png-io.h index 05bff7bc4..257368090 100644 --- a/src/util/png-io.h +++ b/src/util/png-io.h @@ -23,6 +23,7 @@ bool isPNG(struct VFile* source); png_structp PNGReadOpen(struct VFile* source, unsigned offset); bool PNGInstallChunkHandler(png_structp png, void* context, ChunkHandler handler, const char* chunkName); bool PNGReadHeader(png_structp png, png_infop info); +bool PNGReadPixels(png_structp png, png_infop info, void* pixels, unsigned width, unsigned height, unsigned stride); bool PNGIgnorePixels(png_structp png, png_infop info); bool PNGReadFooter(png_structp png, png_infop end); void PNGReadClose(png_structp png, png_infop info, png_infop end);