|
| 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 | +} |
0 commit comments