105 lines
2.9 KiB
C
105 lines
2.9 KiB
C
/*
|
|
* @author xamidev <xamidev@riseup.net>
|
|
* @brief Screen/framebuffer system calls
|
|
* @license GPL-3.0-only
|
|
*/
|
|
|
|
// This is not part of POSIX or anything btw
|
|
|
|
#include <stdint.h>
|
|
#include <kernel.h>
|
|
|
|
extern struct boot_context boot_ctx;
|
|
|
|
/*
|
|
* pack_rbga_to_fb - Convert rgb values to rgba pixel for framebuffer
|
|
* @r: red value
|
|
* @g: green value
|
|
* @b: blue value
|
|
*
|
|
* This function uses the mask sizes and shifts of the Limine
|
|
* framebuffer to convert raw rgb values into usable pixels.
|
|
*
|
|
* Return:
|
|
* <pixel> - usable pixel
|
|
*/
|
|
static uint32_t pack_rgba_to_fb(uint8_t r, uint8_t g, uint8_t b)
|
|
{
|
|
uint32_t pixel = 0;
|
|
|
|
if (boot_ctx.fb->red_mask_size) {
|
|
pixel |= ((uint32_t)(r >> (8 - boot_ctx.fb->red_mask_size)) << boot_ctx.fb->red_mask_shift);
|
|
}
|
|
if (boot_ctx.fb->green_mask_size) {
|
|
pixel |= ((uint32_t)(g >> (8 - boot_ctx.fb->green_mask_size)) << boot_ctx.fb->green_mask_shift);
|
|
}
|
|
if (boot_ctx.fb->blue_mask_size) {
|
|
pixel |= ((uint32_t)(b >> (8 - boot_ctx.fb->blue_mask_size)) << boot_ctx.fb->blue_mask_shift);
|
|
}
|
|
|
|
return pixel;
|
|
}
|
|
|
|
/*
|
|
* sys_draw_fb - Draw a framebuffer subset
|
|
* @src: Source frame
|
|
* @width: width of the frame
|
|
* @height: height of the frame
|
|
* @channels: how many channels (RGB, RGBA)
|
|
*
|
|
* This system call draws the frame @src, having some
|
|
* @width and @height, in the top right of the Limine
|
|
* framebuffer. (Used for DOOM)
|
|
*
|
|
* Return:
|
|
* %0 - on success
|
|
* On error, a negative value with an error code is returned.
|
|
*/
|
|
int sys_draw_fb(const uint8_t* src, int width, int height, int channels)
|
|
{
|
|
if (!boot_ctx.fb || !src) {
|
|
return -EINVAL;
|
|
}
|
|
if (channels != 4 || width <= 0 || height <= 0) {
|
|
return -EINVAL;
|
|
}
|
|
if (boot_ctx.fb->bpp < 24) {
|
|
return -EIO;
|
|
}
|
|
|
|
uint32_t* dst = (uint32_t*)boot_ctx.fb->address;
|
|
uint64_t dst_w = boot_ctx.fb->width;
|
|
uint64_t dst_h = boot_ctx.fb->height;
|
|
uint64_t dst_pitch_px = boot_ctx.fb->pitch / 4;
|
|
uint64_t scale = 2;
|
|
uint64_t scaled_w = (uint64_t)width * scale;
|
|
uint64_t scaled_h = (uint64_t)height * scale;
|
|
|
|
if (scaled_w > dst_w || scaled_h > dst_h) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
uint64_t dst_x = dst_w - scaled_w;
|
|
uint64_t dst_y = 0;
|
|
|
|
for (uint64_t y = 0; y < (uint64_t)height; ++y) {
|
|
const uint8_t* src_row = src + y * (uint64_t)width * 4ULL;
|
|
uint32_t* dst_row0 = dst + (dst_y + y * scale) * dst_pitch_px + dst_x;
|
|
uint32_t* dst_row1 = dst_row0 + dst_pitch_px;
|
|
|
|
for (uint64_t x = 0; x < (uint64_t)width; ++x) {
|
|
const uint8_t* p = src_row + x * 4ULL;
|
|
uint32_t pixel = pack_rgba_to_fb(p[0], p[1], p[2]);
|
|
uint64_t dx = x * scale;
|
|
|
|
// Write 2x2 block. This is because source DOOM frame (320x200)
|
|
// is quite small.
|
|
dst_row0[dx + 0] = pixel;
|
|
dst_row0[dx + 1] = pixel;
|
|
dst_row1[dx + 0] = pixel;
|
|
dst_row1[dx + 1] = pixel;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
} |