/* * Copyright (c) 2021, Stephan Unverwerth * Copyright (c) 2022, Jelle Raaijmakers * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include namespace SoftGPU { Image::Image(void const* ownership_token, GPU::PixelFormat const& pixel_format, u32 width, u32 height, u32 depth, u32 max_levels) : GPU::Image(ownership_token) , m_pixel_format(pixel_format) , m_mipmap_buffers(FixedArray>>::must_create_but_fixme_should_propagate_errors(max_levels)) { VERIFY(pixel_format == GPU::PixelFormat::Alpha || pixel_format == GPU::PixelFormat::Intensity || pixel_format == GPU::PixelFormat::Luminance || pixel_format == GPU::PixelFormat::LuminanceAlpha || pixel_format == GPU::PixelFormat::RGB || pixel_format == GPU::PixelFormat::RGBA); VERIFY(width > 0); VERIFY(height > 0); VERIFY(depth > 0); VERIFY(max_levels > 0); m_width_is_power_of_two = is_power_of_two(width); m_height_is_power_of_two = is_power_of_two(height); m_depth_is_power_of_two = is_power_of_two(depth); u32 level; for (level = 0; level < max_levels; ++level) { m_mipmap_buffers[level] = MUST(Typed3DBuffer::try_create(width, height, depth)); if (width <= 1 && height <= 1 && depth <= 1) break; width = max(width / 2, 1); height = max(height / 2, 1); depth = max(depth / 2, 1); } m_number_of_levels = level + 1; } GPU::ImageDataLayout Image::image_data_layout(u32 level, Vector3 offset) const { auto const width = width_at_level(level); auto const height = height_at_level(level); auto const depth = depth_at_level(level); // FIXME: we are directly writing to FloatVector4s. We should probably find a better way to do this return { .pixel_type = { .format = GPU::PixelFormat::RGBA, .bits = GPU::PixelComponentBits::AllBits, .data_type = GPU::PixelDataType::Float, }, .dimensions = { .width = width, .height = height, .depth = depth, }, .selection = { .offset_x = offset.x(), .offset_y = offset.y(), .offset_z = offset.z(), .width = width - offset.x(), .height = height - offset.y(), .depth = depth - offset.z(), }, }; } void Image::write_texels(u32 level, Vector3 const& output_offset, void const* input_data, GPU::ImageDataLayout const& input_layout) { VERIFY(level < number_of_levels()); auto output_layout = image_data_layout(level, output_offset); auto texel_data = texel_pointer(level, 0, 0, 0); PixelConverter converter { input_layout, output_layout }; ErrorOr conversion_result; switch (m_pixel_format) { case GPU::PixelFormat::Luminance: case GPU::PixelFormat::RGB: // Both Luminance and RGB set the alpha to 1, regardless of the source texel conversion_result = converter.convert(input_data, texel_data, [](auto& components) { components[3] = 1.f; }); break; default: conversion_result = converter.convert(input_data, texel_data, {}); } if (conversion_result.is_error()) dbgln("Pixel conversion failed: {}", conversion_result.error().string_literal()); } void Image::read_texels(u32 level, Vector3 const& input_offset, void* output_data, GPU::ImageDataLayout const& output_layout) const { VERIFY(level < number_of_levels()); auto input_layout = image_data_layout(level, input_offset); PixelConverter converter { input_layout, output_layout }; auto conversion_result = converter.convert(texel_pointer(level, 0, 0, 0), output_data, {}); if (conversion_result.is_error()) dbgln("Pixel conversion failed: {}", conversion_result.error().string_literal()); } void Image::copy_texels(GPU::Image const& source, u32 source_level, Vector3 const& source_offset, Vector3 const& size, u32 destination_level, Vector3 const& destination_offset) { VERIFY(source.has_same_ownership_token(*this)); auto const& src_image = static_cast(source); VERIFY(source_level < src_image.number_of_levels()); VERIFY(source_offset.x() + size.x() <= src_image.width_at_level(source_level)); VERIFY(source_offset.y() + size.y() <= src_image.height_at_level(source_level)); VERIFY(source_offset.z() + size.z() <= src_image.depth_at_level(source_level)); VERIFY(destination_level < number_of_levels()); VERIFY(destination_offset.x() + size.x() <= width_at_level(destination_level)); VERIFY(destination_offset.y() + size.y() <= height_at_level(destination_level)); VERIFY(destination_offset.z() + size.z() <= depth_at_level(destination_level)); for (u32 z = 0; z < size.z(); ++z) { for (u32 y = 0; y < size.y(); ++y) { for (u32 x = 0; x < size.x(); ++x) { auto const& color = src_image.texel(source_level, source_offset.x() + x, source_offset.y() + y, source_offset.z() + z); set_texel(destination_level, destination_offset.x() + x, destination_offset.y() + y, destination_offset.z() + z, color); } } } } static GPU::ImageDataLayout image_data_layout_for_bitmap(Gfx::Bitmap& bitmap) { VERIFY(bitmap.format() == Gfx::BitmapFormat::BGRA8888); return GPU::ImageDataLayout { .pixel_type = { .format = GPU::PixelFormat::BGRA, .bits = GPU::PixelComponentBits::B8_8_8_8, .data_type = GPU::PixelDataType::UnsignedInt, .components_order = GPU::ComponentsOrder::Reversed, }, .dimensions = { .width = static_cast(bitmap.width()), .height = static_cast(bitmap.height()), .depth = 1, }, .selection = { .width = static_cast(bitmap.width()), .height = static_cast(bitmap.height()), .depth = 1, }, }; } void Image::regenerate_mipmaps() { // FIXME: currently this only works for 2D Images VERIFY(depth_at_level(0) == 1); auto empty_bitmap_for_level = [&](u32 level) -> NonnullRefPtr { Gfx::IntSize size = { width_at_level(level), height_at_level(level) }; return MUST(Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRA8888, size)); }; auto copy_image_into_bitmap = [&](u32 level) -> NonnullRefPtr { auto bitmap = empty_bitmap_for_level(level); auto input_layout = image_data_layout(level, { 0, 0, 0 }); auto const* input_data = texel_pointer(level, 0, 0, 0); auto output_layout = image_data_layout_for_bitmap(bitmap); auto* output_data = bitmap->scanline(0); PixelConverter converter { input_layout, output_layout }; MUST(converter.convert(input_data, output_data, {})); return bitmap; }; auto copy_bitmap_into_level = [&](NonnullRefPtr bitmap, u32 level) { VERIFY(level >= 1); auto input_layout = image_data_layout_for_bitmap(bitmap); auto const* input_data = bitmap->scanline(0); auto output_layout = image_data_layout(level, { 0, 0, 0 }); auto* output_data = texel_pointer(level, 0, 0, 0); PixelConverter converter { input_layout, output_layout }; MUST(converter.convert(input_data, output_data, {})); }; // For levels 1..number_of_levels-1, we generate downscaled versions of the level above for (u32 level = 1; level < m_number_of_levels; ++level) { auto higher_level_bitmap = copy_image_into_bitmap(level - 1); auto current_level_bitmap = empty_bitmap_for_level(level); Gfx::Painter current_level_painter { current_level_bitmap }; current_level_painter.draw_scaled_bitmap( current_level_bitmap->rect(), higher_level_bitmap, higher_level_bitmap->rect(), 1.f, Gfx::Painter::ScalingMode::BilinearBlend); copy_bitmap_into_level(current_level_bitmap, level); } } }