Skip to content

Commit 01cde97

Browse files
authored
Merge pull request #290 from fjtrujy/take_screenshot
Add an option to take an screenshot in the `debug` library.
2 parents 0f6bf94 + ee30cb2 commit 01cde97

File tree

6 files changed

+331
-0
lines changed

6 files changed

+331
-0
lines changed

src/debug/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ libpspdebuginclude_HEADERS = \
2424
lib_LIBRARIES = libpspdebug.a libpspgdb.a libpspgdb_user.a libpspgdb_kernel.a libpspdebugkb.a
2525

2626
libpspdebug_a_SOURCES = \
27+
bitmap.c \
2728
callstack.c \
2829
callstackget.S \
2930
font.c \
@@ -33,6 +34,7 @@ libpspdebug_a_SOURCES = \
3334
kprintf.c \
3435
stacktrace.c \
3536
profiler.c \
37+
screenshot.c \
3638
stdio.c \
3739
sio.c
3840

src/debug/bitmap.c

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
/*
2+
* PSP Software Development Kit - https://github.com/pspdev
3+
* -----------------------------------------------------------------------
4+
* Licensed under the BSD license, see LICENSE in PSPSDK root for details.
5+
*
6+
* bitmap.c - Take a screenshot and save it as a bitmap file
7+
*
8+
* Copyright (c) 2025, Francisco Javier Trujillo Mata <[email protected]>
9+
*
10+
*/
11+
12+
#include <pspkernel.h>
13+
#include <pspdisplay.h>
14+
#include <stdio.h>
15+
#include <stdint.h>
16+
#include <string.h>
17+
18+
#define BMP_ID "BM"
19+
#define PSP_SCREEN_WIDTH 480
20+
#define PSP_SCREEN_HEIGHT 272
21+
#define PSP_LINE_SIZE 512
22+
#define BMP_RGB_BYTES_PER_PIXEL 3 // BMP format uses 24-bit RGB (3 bytes per pixel)
23+
24+
// Helper function to get pixel depth in bytes for a given format
25+
static int get_pixel_depth(int format)
26+
{
27+
switch (format)
28+
{
29+
case PSP_DISPLAY_PIXEL_FORMAT_565:
30+
case PSP_DISPLAY_PIXEL_FORMAT_5551:
31+
case PSP_DISPLAY_PIXEL_FORMAT_4444:
32+
return 2; // 16-bit formats
33+
case PSP_DISPLAY_PIXEL_FORMAT_8888:
34+
return 4; // 32-bit format
35+
default:
36+
return 0; // Invalid format
37+
}
38+
}
39+
40+
struct BitmapHeader
41+
{
42+
char id[2];
43+
uint32_t filesize;
44+
uint32_t reserved;
45+
uint32_t offset;
46+
uint32_t headsize;
47+
uint32_t width;
48+
uint32_t height;
49+
uint16_t planes;
50+
uint16_t bpp;
51+
uint32_t comp;
52+
uint32_t bitmapsize;
53+
uint32_t hres;
54+
uint32_t vres;
55+
uint32_t colors;
56+
uint32_t impcolors;
57+
} __attribute__((packed));
58+
59+
static int fixed_write(int fd, void *data, int len)
60+
{
61+
int writelen = 0;
62+
63+
while (writelen < len)
64+
{
65+
int ret;
66+
67+
ret = sceIoWrite(fd, data + writelen, len - writelen);
68+
if (ret <= 0)
69+
{
70+
writelen = -1;
71+
break;
72+
}
73+
writelen += ret;
74+
}
75+
76+
return writelen;
77+
}
78+
79+
void write_8888_line(void *frame, void *line_buf, int line)
80+
{
81+
uint8_t *line_data = line_buf;
82+
const int pixel_depth = get_pixel_depth(PSP_DISPLAY_PIXEL_FORMAT_8888);
83+
uint8_t *p = frame + (line * PSP_LINE_SIZE * pixel_depth);
84+
int i;
85+
86+
for (i = 0; i < PSP_SCREEN_WIDTH; i++)
87+
{
88+
line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 2] = p[i * pixel_depth];
89+
line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 1] = p[(i * pixel_depth) + 1];
90+
line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 0] = p[(i * pixel_depth) + 2];
91+
}
92+
}
93+
94+
void write_5551_line(void *frame, void *line_buf, int line)
95+
{
96+
uint8_t *line_data = line_buf;
97+
const int pixel_depth = get_pixel_depth(PSP_DISPLAY_PIXEL_FORMAT_5551);
98+
uint16_t *p = frame + (line * PSP_LINE_SIZE * pixel_depth);
99+
int i;
100+
101+
for (i = 0; i < PSP_SCREEN_WIDTH; i++)
102+
{
103+
line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 2] = (p[i] & 0x1F) << 3;
104+
line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 1] = ((p[i] >> 5) & 0x1F) << 3;
105+
line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 0] = ((p[i] >> 10) & 0x1F) << 3;
106+
}
107+
}
108+
109+
void write_565_line(void *frame, void *line_buf, int line)
110+
{
111+
uint8_t *line_data = line_buf;
112+
const int pixel_depth = get_pixel_depth(PSP_DISPLAY_PIXEL_FORMAT_565);
113+
uint16_t *p = frame + (line * PSP_LINE_SIZE * pixel_depth);
114+
int i;
115+
116+
for (i = 0; i < PSP_SCREEN_WIDTH; i++)
117+
{
118+
line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 2] = (p[i] & 0x1F) << 3;
119+
line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 1] = ((p[i] >> 5) & 0x3F) << 2;
120+
line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 0] = ((p[i] >> 11) & 0x1F) << 3;
121+
}
122+
}
123+
124+
void write_4444_line(void *frame, void *line_buf, int line)
125+
{
126+
uint8_t *line_data = line_buf;
127+
const int pixel_depth = get_pixel_depth(PSP_DISPLAY_PIXEL_FORMAT_4444);
128+
uint16_t *p = frame + (line * PSP_LINE_SIZE * pixel_depth);
129+
int i;
130+
131+
for (i = 0; i < PSP_SCREEN_WIDTH; i++)
132+
{
133+
line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 2] = (p[i] & 0xF) << 4;
134+
line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 1] = ((p[i] >> 4) & 0xF) << 4;
135+
line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 0] = ((p[i] >> 8) & 0xF) << 4;
136+
}
137+
}
138+
139+
int bitmapWrite(void *frame_addr, int format, const char *file)
140+
{
141+
struct BitmapHeader bmp;
142+
uint8_t line_buf[PSP_SCREEN_WIDTH * BMP_RGB_BYTES_PER_PIXEL]; // Stack buffer for one line of RGB data
143+
int fd;
144+
int line;
145+
146+
// Initialize header
147+
memset(&bmp, 0, sizeof(struct BitmapHeader));
148+
memcpy(bmp.id, BMP_ID, sizeof(bmp.id));
149+
bmp.filesize = PSP_SCREEN_WIDTH * PSP_SCREEN_HEIGHT * BMP_RGB_BYTES_PER_PIXEL + sizeof(struct BitmapHeader);
150+
bmp.offset = sizeof(struct BitmapHeader);
151+
bmp.headsize = 0x28;
152+
bmp.width = PSP_SCREEN_WIDTH;
153+
bmp.height = PSP_SCREEN_HEIGHT;
154+
bmp.planes = 1;
155+
bmp.bpp = BMP_RGB_BYTES_PER_PIXEL * 8; // Convert bytes to bits
156+
bmp.bitmapsize = PSP_SCREEN_WIDTH * PSP_SCREEN_HEIGHT * BMP_RGB_BYTES_PER_PIXEL;
157+
bmp.hres = 2834;
158+
bmp.vres = 2834;
159+
160+
fd = sceIoOpen(file, PSP_O_WRONLY | PSP_O_CREAT | PSP_O_TRUNC, 0777);
161+
if (fd < 0)
162+
{
163+
return -1;
164+
}
165+
166+
// Write header first
167+
if (fixed_write(fd, &bmp, sizeof(struct BitmapHeader)) != sizeof(struct BitmapHeader))
168+
{
169+
sceIoClose(fd);
170+
return -1;
171+
}
172+
173+
// Process and write each line (from bottom to top as required by BMP format)
174+
for (line = PSP_SCREEN_HEIGHT - 1; line >= 0; line--)
175+
{
176+
switch (format)
177+
{
178+
case PSP_DISPLAY_PIXEL_FORMAT_565:
179+
write_565_line(frame_addr, line_buf, line);
180+
break;
181+
case PSP_DISPLAY_PIXEL_FORMAT_5551:
182+
write_5551_line(frame_addr, line_buf, line);
183+
break;
184+
case PSP_DISPLAY_PIXEL_FORMAT_4444:
185+
write_4444_line(frame_addr, line_buf, line);
186+
break;
187+
case PSP_DISPLAY_PIXEL_FORMAT_8888:
188+
write_8888_line(frame_addr, line_buf, line);
189+
break;
190+
}
191+
192+
if (fixed_write(fd, line_buf, sizeof(line_buf)) != sizeof(line_buf))
193+
{
194+
sceIoClose(fd);
195+
return -1;
196+
}
197+
}
198+
199+
sceIoClose(fd);
200+
return 0;
201+
}

src/debug/pspdebug.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,16 @@ void pspDebugSioEnableKprintf(void);
433433
*/
434434
void pspDebugSioDisableKprintf(void);
435435

436+
/**
437+
* Save a screenshot to a file
438+
*
439+
* @param filename - The filename to save the screenshot for the current frame buffer displayed on the screen.
440+
* The filename will be saved with a BMP extension.
441+
*
442+
* @return 0 on success, -1 on error
443+
*/
444+
int pspScreenshotSave(const char *filename);
445+
436446
/**@}*/
437447

438448
#ifdef __cplusplus

src/debug/screenshot.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* PSP Software Development Kit - https://github.com/pspdev
3+
* -----------------------------------------------------------------------
4+
* Licensed under the BSD license, see LICENSE in PSPSDK root for details.
5+
*
6+
* screenshot.c - Simple screen debug keyboard
7+
*
8+
* Copyright (c) 2025, Francisco Javier Trujillo Mata <[email protected]>
9+
*
10+
*/
11+
12+
#include "pspdebug.h"
13+
#include "pspdisplay.h"
14+
#include "pspuser.h"
15+
16+
#define PSP_SCREEN_HEIGHT 272
17+
18+
int bitmapWrite(void *frame_addr, int format, const char *file);
19+
20+
int pspScreenshotSave(const char *filename)
21+
{
22+
void* buff;
23+
int bufferwidth, pixelformat;
24+
25+
int ret = sceDisplayGetFrameBuf(&buff, &bufferwidth, &pixelformat, PSP_DISPLAY_SETBUF_NEXTHSYNC);
26+
if (ret != 0 || buff == NULL || bufferwidth == 0) {
27+
return -1;
28+
}
29+
30+
// write the screenshot to the file
31+
return bitmapWrite(buff, pixelformat, filename);
32+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
TARGET = screenshot
2+
OBJS = main.o
3+
4+
INCDIR =
5+
CFLAGS = -O2 -Wall
6+
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
7+
ASFLAGS = $(CFLAGS)
8+
9+
LIBDIR =
10+
LDFLAGS =
11+
LIBS=
12+
13+
EXTRA_TARGETS = EBOOT.PBP
14+
PSP_EBOOT_TITLE = Screenshot Test
15+
16+
PSPSDK=$(shell psp-config --pspsdk-path)
17+
include $(PSPSDK)/lib/build.mak
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* PSP Software Development Kit - https://github.com/pspdev
3+
* -----------------------------------------------------------------------
4+
* Licensed under the BSD license, see LICENSE in PSPSDK root for details.
5+
*
6+
* main.c - Basic sample to demonstrate the kprintf handler.
7+
*
8+
* Copyright (c) 2005 Marcus R. Brown <[email protected]>
9+
* Copyright (c) 2005 James Forshaw <[email protected]>
10+
* Copyright (c) 2005 John Kelley <[email protected]>
11+
*
12+
*/
13+
14+
#include <stdlib.h>
15+
#include <pspkernel.h>
16+
#include <pspdebug.h>
17+
#include <psptypes.h>
18+
#include <pspdisplay.h>
19+
20+
PSP_MODULE_INFO("Screenshot Sample", 0, 1, 1);
21+
PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER);
22+
23+
/* Exit callback */
24+
int exit_callback(int arg1, int arg2, void *common)
25+
{
26+
exit(0);
27+
return 0;
28+
}
29+
30+
/* Callback thread */
31+
int CallbackThread(SceSize args, void *argp)
32+
{
33+
int cbid;
34+
35+
cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
36+
sceKernelRegisterExitCallback(cbid);
37+
38+
sceKernelSleepThreadCB();
39+
40+
return 0;
41+
}
42+
43+
/* Sets up the callback thread and returns its thread id */
44+
int SetupCallbacks(void)
45+
{
46+
int thid = 0;
47+
48+
thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, THREAD_ATTR_USER, 0);
49+
if(thid >= 0)
50+
{
51+
sceKernelStartThread(thid, 0, 0);
52+
}
53+
54+
return thid;
55+
}
56+
57+
int main(void)
58+
{
59+
pspDebugScreenInit();
60+
SetupCallbacks();
61+
62+
pspDebugScreenSetTextColor(0xFF);
63+
pspDebugScreenPrintf("\n\n\n\n\n******************** Screenshot Sample *********************\n\n\n\n");
64+
65+
sceDisplayWaitVblankStart();
66+
pspScreenshotSave("screenshot.bmp");
67+
68+
while(1);
69+
}

0 commit comments

Comments
 (0)