Serenity Operating System
1/*
2 * Copyright (c) 2021, Stephan Unverwerth <s.unverwerth@serenityos.org>
3 * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <LibGfx/Bitmap.h>
9#include <LibGfx/Painter.h>
10#include <LibGfx/Size.h>
11#include <LibSoftGPU/Image.h>
12#include <LibSoftGPU/PixelConverter.h>
13
14namespace SoftGPU {
15
16Image::Image(void const* ownership_token, GPU::PixelFormat const& pixel_format, u32 width, u32 height, u32 depth, u32 max_levels)
17 : GPU::Image(ownership_token, pixel_format, width, height, depth, max_levels)
18 , m_mipmap_buffers(FixedArray<RefPtr<Typed3DBuffer<FloatVector4>>>::must_create_but_fixme_should_propagate_errors(number_of_levels()))
19{
20 VERIFY(pixel_format == GPU::PixelFormat::Alpha
21 || pixel_format == GPU::PixelFormat::Intensity
22 || pixel_format == GPU::PixelFormat::Luminance
23 || pixel_format == GPU::PixelFormat::LuminanceAlpha
24 || pixel_format == GPU::PixelFormat::RGB
25 || pixel_format == GPU::PixelFormat::RGBA);
26
27 m_width_is_power_of_two = is_power_of_two(width);
28 m_height_is_power_of_two = is_power_of_two(height);
29 m_depth_is_power_of_two = is_power_of_two(depth);
30
31 for (u32 level = 0; level < number_of_levels(); ++level) {
32 m_mipmap_buffers[level] = MUST(Typed3DBuffer<FloatVector4>::try_create(width_at_level(level), height_at_level(level), depth_at_level(level)));
33 }
34}
35
36GPU::ImageDataLayout Image::image_data_layout(u32 level, Vector3<i32> offset) const
37{
38 auto const width = width_at_level(level);
39 auto const height = height_at_level(level);
40 auto const depth = depth_at_level(level);
41
42 // FIXME: we are directly writing to FloatVector4s. We should probably find a better way to do this
43 return {
44 .pixel_type = {
45 .format = GPU::PixelFormat::RGBA,
46 .bits = GPU::PixelComponentBits::AllBits,
47 .data_type = GPU::PixelDataType::Float,
48 },
49 .dimensions = {
50 .width = width,
51 .height = height,
52 .depth = depth,
53 },
54 .selection = {
55 .offset_x = offset.x(),
56 .offset_y = offset.y(),
57 .offset_z = offset.z(),
58 .width = width - offset.x(),
59 .height = height - offset.y(),
60 .depth = depth - offset.z(),
61 },
62 };
63}
64
65void Image::write_texels(u32 level, Vector3<i32> const& output_offset, void const* input_data, GPU::ImageDataLayout const& input_layout)
66{
67 VERIFY(level < number_of_levels());
68
69 auto output_layout = image_data_layout(level, output_offset);
70 auto texel_data = texel_pointer(level, 0, 0, 0);
71
72 PixelConverter converter { input_layout, output_layout };
73 ErrorOr<void> conversion_result;
74 switch (pixel_format()) {
75 case GPU::PixelFormat::Luminance:
76 case GPU::PixelFormat::RGB:
77 // Both Luminance and RGB set the alpha to 1, regardless of the source texel
78 conversion_result = converter.convert(input_data, texel_data, [](auto& components) { components[3] = 1.f; });
79 break;
80 default:
81 conversion_result = converter.convert(input_data, texel_data, {});
82 }
83 if (conversion_result.is_error())
84 dbgln("Pixel conversion failed: {}", conversion_result.error().string_literal());
85}
86
87void Image::read_texels(u32 level, Vector3<i32> const& input_offset, void* output_data, GPU::ImageDataLayout const& output_layout) const
88{
89 VERIFY(level < number_of_levels());
90
91 auto input_layout = image_data_layout(level, input_offset);
92
93 PixelConverter converter { input_layout, output_layout };
94 auto conversion_result = converter.convert(texel_pointer(level, 0, 0, 0), output_data, {});
95 if (conversion_result.is_error())
96 dbgln("Pixel conversion failed: {}", conversion_result.error().string_literal());
97}
98
99void Image::copy_texels(GPU::Image const& source, u32 source_level, Vector3<u32> const& source_offset, Vector3<u32> const& size, u32 destination_level, Vector3<u32> const& destination_offset)
100{
101 VERIFY(source.has_same_ownership_token(*this));
102
103 auto const& src_image = static_cast<Image const&>(source);
104
105 VERIFY(source_level < src_image.number_of_levels());
106 VERIFY(source_offset.x() + size.x() <= src_image.width_at_level(source_level));
107 VERIFY(source_offset.y() + size.y() <= src_image.height_at_level(source_level));
108 VERIFY(source_offset.z() + size.z() <= src_image.depth_at_level(source_level));
109 VERIFY(destination_level < number_of_levels());
110 VERIFY(destination_offset.x() + size.x() <= width_at_level(destination_level));
111 VERIFY(destination_offset.y() + size.y() <= height_at_level(destination_level));
112 VERIFY(destination_offset.z() + size.z() <= depth_at_level(destination_level));
113
114 for (u32 z = 0; z < size.z(); ++z) {
115 for (u32 y = 0; y < size.y(); ++y) {
116 for (u32 x = 0; x < size.x(); ++x) {
117 auto const& color = src_image.texel(source_level, source_offset.x() + x, source_offset.y() + y, source_offset.z() + z);
118 set_texel(destination_level, destination_offset.x() + x, destination_offset.y() + y, destination_offset.z() + z, color);
119 }
120 }
121 }
122}
123
124static GPU::ImageDataLayout image_data_layout_for_bitmap(Gfx::Bitmap& bitmap)
125{
126 VERIFY(bitmap.format() == Gfx::BitmapFormat::BGRA8888);
127 return GPU::ImageDataLayout {
128 .pixel_type = {
129 .format = GPU::PixelFormat::BGRA,
130 .bits = GPU::PixelComponentBits::B8_8_8_8,
131 .data_type = GPU::PixelDataType::UnsignedInt,
132 .components_order = GPU::ComponentsOrder::Reversed,
133 },
134 .dimensions = {
135 .width = static_cast<u32>(bitmap.width()),
136 .height = static_cast<u32>(bitmap.height()),
137 .depth = 1,
138 },
139 .selection = {
140 .width = static_cast<u32>(bitmap.width()),
141 .height = static_cast<u32>(bitmap.height()),
142 .depth = 1,
143 },
144 };
145}
146
147void Image::regenerate_mipmaps()
148{
149 // FIXME: currently this only works for 2D Images
150 VERIFY(depth_at_level(0) == 1);
151
152 auto empty_bitmap_for_level = [&](u32 level) -> NonnullRefPtr<Gfx::Bitmap> {
153 Gfx::IntSize size = { width_at_level(level), height_at_level(level) };
154 return MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, size));
155 };
156 auto copy_image_into_bitmap = [&](u32 level) -> NonnullRefPtr<Gfx::Bitmap> {
157 auto bitmap = empty_bitmap_for_level(level);
158
159 auto input_layout = image_data_layout(level, { 0, 0, 0 });
160 auto const* input_data = texel_pointer(level, 0, 0, 0);
161
162 auto output_layout = image_data_layout_for_bitmap(bitmap);
163 auto* output_data = bitmap->scanline(0);
164
165 PixelConverter converter { input_layout, output_layout };
166 MUST(converter.convert(input_data, output_data, {}));
167 return bitmap;
168 };
169 auto copy_bitmap_into_level = [&](NonnullRefPtr<Gfx::Bitmap> bitmap, u32 level) {
170 VERIFY(level >= 1);
171
172 auto input_layout = image_data_layout_for_bitmap(bitmap);
173 auto const* input_data = bitmap->scanline(0);
174
175 auto output_layout = image_data_layout(level, { 0, 0, 0 });
176 auto* output_data = texel_pointer(level, 0, 0, 0);
177
178 PixelConverter converter { input_layout, output_layout };
179 MUST(converter.convert(input_data, output_data, {}));
180 };
181
182 // For levels 1..number_of_levels-1, we generate downscaled versions of the level above
183 for (u32 level = 1; level < number_of_levels(); ++level) {
184 auto higher_level_bitmap = copy_image_into_bitmap(level - 1);
185 auto current_level_bitmap = empty_bitmap_for_level(level);
186
187 Gfx::Painter current_level_painter { current_level_bitmap };
188 current_level_painter.draw_scaled_bitmap(
189 current_level_bitmap->rect(),
190 higher_level_bitmap,
191 higher_level_bitmap->rect(),
192 1.f,
193 Gfx::Painter::ScalingMode::BilinearBlend);
194
195 copy_bitmap_into_level(current_level_bitmap, level);
196 }
197}
198
199}