1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 15:27:35 +00:00

LibGL: Implement glReadPixels()

This commit is contained in:
Stephan Unverwerth 2021-05-24 17:52:24 +02:00 committed by Linus Groh
parent d6c84ca4df
commit e54d96d53e
3 changed files with 249 additions and 0 deletions

View file

@ -1113,10 +1113,239 @@ void SoftwareGLContext::gl_read_pixels(GLint x, GLint y, GLsizei width, GLsizei
} }
} }
// Some helper functions for converting float values to integer types
auto float_to_i8 = [](float f) -> GLchar {
return static_cast<GLchar>((0x7f * min(max(f, 0.0f), 1.0f) - 1) / 2);
};
auto float_to_i16 = [](float f) -> GLshort {
return static_cast<GLshort>((0x7fff * min(max(f, 0.0f), 1.0f) - 1) / 2);
};
auto float_to_i32 = [](float f) -> GLint {
return static_cast<GLint>((0x7fffffff * min(max(f, 0.0f), 1.0f) - 1) / 2);
};
auto float_to_u8 = [](float f) -> GLubyte {
return static_cast<GLubyte>(0xff * min(max(f, 0.0f), 1.0f));
};
auto float_to_u16 = [](float f) -> GLushort {
return static_cast<GLushort>(0xffff * min(max(f, 0.0f), 1.0f));
};
auto float_to_u32 = [](float f) -> GLuint {
return static_cast<GLuint>(0xffffffff * min(max(f, 0.0f), 1.0f));
};
if (format == GL_DEPTH_COMPONENT) { if (format == GL_DEPTH_COMPONENT) {
// Read from depth buffer // Read from depth buffer
for (size_t i = 0; i < height; ++i) {
for (size_t j = 0; j < width; ++j) {
float depth = m_rasterizer.get_depthbuffer_value(x + j, y + i);
switch (type) {
case GL_BYTE:
reinterpret_cast<GLchar*>(pixels)[i * width + j] = float_to_i8(depth);
break;
case GL_SHORT:
reinterpret_cast<GLshort*>(pixels)[i * width + j] = float_to_i16(depth);
break;
case GL_INT:
reinterpret_cast<GLint*>(pixels)[i * width + j] = float_to_i32(depth);
break;
case GL_UNSIGNED_BYTE:
reinterpret_cast<GLubyte*>(pixels)[i * width + j] = float_to_u8(depth);
break;
case GL_UNSIGNED_SHORT:
reinterpret_cast<GLushort*>(pixels)[i * width + j] = float_to_u16(depth);
break;
case GL_UNSIGNED_INT:
reinterpret_cast<GLuint*>(pixels)[i * width + j] = float_to_u32(depth);
break;
case GL_FLOAT:
reinterpret_cast<GLfloat*>(pixels)[i * width + j] = min(max(depth, 0.0f), 1.0f);
break;
}
}
}
return; return;
} }
bool write_red = false;
bool write_green = false;
bool write_blue = false;
bool write_alpha = false;
size_t component_count = 0;
size_t component_size = 0;
size_t red_offset = 0;
size_t green_offset = 0;
size_t blue_offset = 0;
size_t alpha_offset = 0;
char* red_ptr = nullptr;
char* green_ptr = nullptr;
char* blue_ptr = nullptr;
char* alpha_ptr = nullptr;
switch (format) {
case GL_RGB:
write_red = true;
write_green = true;
write_blue = true;
component_count = 3;
red_offset = 2;
green_offset = 1;
blue_offset = 0;
break;
case GL_RGBA:
write_red = true;
write_green = true;
write_blue = true;
write_alpha = true;
component_count = 4;
red_offset = 3;
green_offset = 2;
blue_offset = 1;
alpha_offset = 0;
break;
case GL_RED:
write_red = true;
component_count = 1;
red_offset = 0;
break;
case GL_GREEN:
write_green = true;
component_count = 1;
green_offset = 0;
break;
case GL_BLUE:
write_blue = true;
component_count = 1;
blue_offset = 0;
break;
case GL_ALPHA:
write_alpha = true;
component_count = 1;
alpha_offset = 0;
break;
}
switch (type) {
case GL_BYTE:
case GL_UNSIGNED_BYTE:
component_size = 1;
break;
case GL_SHORT:
case GL_UNSIGNED_SHORT:
component_size = 2;
break;
case GL_INT:
case GL_UNSIGNED_INT:
case GL_FLOAT:
component_size = 4;
break;
}
char* out_ptr = reinterpret_cast<char*>(pixels);
for (int i = 0; i < (int)height; ++i) {
for (int j = 0; j < (int)width; ++j) {
Gfx::RGBA32 color {};
if (m_current_read_buffer == GL_FRONT || m_current_read_buffer == GL_LEFT || m_current_read_buffer == GL_FRONT_LEFT) {
if (y + i >= m_frontbuffer->width() || x + j >= m_frontbuffer->height())
color = 0;
else
color = m_frontbuffer->scanline(y + i)[x + j];
} else {
color = m_rasterizer.get_backbuffer_pixel(x + j, y + i);
}
float red = ((color >> 24) & 0xff) / 255.0f;
float green = ((color >> 16) & 0xff) / 255.0f;
float blue = ((color >> 8) & 0xff) / 255.0f;
float alpha = (color & 0xff) / 255.0f;
// FIXME: Set up write pointers based on selected endianness (glPixelStore)
red_ptr = out_ptr + (component_size * red_offset);
green_ptr = out_ptr + (component_size * green_offset);
blue_ptr = out_ptr + (component_size * blue_offset);
alpha_ptr = out_ptr + (component_size * alpha_offset);
switch (type) {
case GL_BYTE:
if (write_red)
*reinterpret_cast<GLchar*>(red_ptr) = float_to_i8(red);
if (write_green)
*reinterpret_cast<GLchar*>(green_ptr) = float_to_i8(green);
if (write_blue)
*reinterpret_cast<GLchar*>(blue_ptr) = float_to_i8(blue);
if (write_alpha)
*reinterpret_cast<GLchar*>(alpha_ptr) = float_to_i8(alpha);
break;
case GL_UNSIGNED_BYTE:
if (write_red)
*reinterpret_cast<GLubyte*>(red_ptr) = float_to_u8(red);
if (write_green)
*reinterpret_cast<GLubyte*>(green_ptr) = float_to_u8(green);
if (write_blue)
*reinterpret_cast<GLubyte*>(blue_ptr) = float_to_u8(blue);
if (write_alpha)
*reinterpret_cast<GLubyte*>(alpha_ptr) = float_to_u8(alpha);
break;
case GL_SHORT:
if (write_red)
*reinterpret_cast<GLshort*>(red_ptr) = float_to_i16(red);
if (write_green)
*reinterpret_cast<GLshort*>(green_ptr) = float_to_i16(green);
if (write_blue)
*reinterpret_cast<GLshort*>(blue_ptr) = float_to_i16(blue);
if (write_alpha)
*reinterpret_cast<GLshort*>(alpha_ptr) = float_to_i16(alpha);
break;
case GL_UNSIGNED_SHORT:
if (write_red)
*reinterpret_cast<GLushort*>(red_ptr) = float_to_u16(red);
if (write_green)
*reinterpret_cast<GLushort*>(green_ptr) = float_to_u16(green);
if (write_blue)
*reinterpret_cast<GLushort*>(blue_ptr) = float_to_u16(blue);
if (write_alpha)
*reinterpret_cast<GLushort*>(alpha_ptr) = float_to_u16(alpha);
break;
case GL_INT:
if (write_red)
*reinterpret_cast<GLint*>(red_ptr) = float_to_i32(red);
if (write_green)
*reinterpret_cast<GLint*>(green_ptr) = float_to_i32(green);
if (write_blue)
*reinterpret_cast<GLint*>(blue_ptr) = float_to_i32(blue);
if (write_alpha)
*reinterpret_cast<GLint*>(alpha_ptr) = float_to_i32(alpha);
break;
case GL_UNSIGNED_INT:
if (write_red)
*reinterpret_cast<GLuint*>(red_ptr) = float_to_u32(red);
if (write_green)
*reinterpret_cast<GLuint*>(green_ptr) = float_to_u32(green);
if (write_blue)
*reinterpret_cast<GLuint*>(blue_ptr) = float_to_u32(blue);
if (write_alpha)
*reinterpret_cast<GLuint*>(alpha_ptr) = float_to_u32(alpha);
break;
case GL_FLOAT:
if (write_red)
*reinterpret_cast<GLfloat*>(red_ptr) = min(max(red, 0.0f), 1.0f);
if (write_green)
*reinterpret_cast<GLfloat*>(green_ptr) = min(max(green, 0.0f), 1.0f);
if (write_blue)
*reinterpret_cast<GLfloat*>(blue_ptr) = min(max(blue, 0.0f), 1.0f);
if (write_alpha)
*reinterpret_cast<GLfloat*>(alpha_ptr) = min(max(alpha, 0.0f), 1.0f);
break;
}
out_ptr += component_size * component_count;
}
}
} }
void SoftwareGLContext::present() void SoftwareGLContext::present()

View file

@ -470,4 +470,22 @@ void SoftwareRasterizer::set_options(const RasterizerOptions& options)
// FIXME: Recreate or reinitialize render threads here when multithreading is being implemented // FIXME: Recreate or reinitialize render threads here when multithreading is being implemented
} }
Gfx::RGBA32 SoftwareRasterizer::get_backbuffer_pixel(int x, int y)
{
// FIXME: Reading individual pixels is very slow, rewrite this to transfer whole blocks
if (x < 0 || y < 0 || x >= m_render_target->width() || y >= m_render_target->height())
return 0;
return m_render_target->scanline(y)[x];
}
float SoftwareRasterizer::get_depthbuffer_value(int x, int y)
{
// FIXME: Reading individual pixels is very slow, rewrite this to transfer whole blocks
if (x < 0 || y < 0 || x >= m_render_target->width() || y >= m_render_target->height())
return 1.0f;
return m_depth_buffer->scanline(y)[x];
}
} }

View file

@ -38,6 +38,8 @@ public:
void wait_for_all_threads() const; void wait_for_all_threads() const;
void set_options(const RasterizerOptions&); void set_options(const RasterizerOptions&);
RasterizerOptions options() const { return m_options; } RasterizerOptions options() const { return m_options; }
Gfx::RGBA32 get_backbuffer_pixel(int x, int y);
float get_depthbuffer_value(int x, int y);
private: private:
RefPtr<Gfx::Bitmap> m_render_target; RefPtr<Gfx::Bitmap> m_render_target;