/* * Copyright (c) 2024, the Ladybird developers. * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include namespace Gfx { StringView export_format_name(ExportFormat format) { switch (format) { #define ENUMERATE_EXPORT_FORMAT(format) \ case Gfx::ExportFormat::format: \ return #format##sv; ENUMERATE_EXPORT_FORMATS(ENUMERATE_EXPORT_FORMAT) #undef ENUMERATE_EXPORT_FORMAT } VERIFY_NOT_REACHED(); } static int bytes_per_pixel_for_export_format(ExportFormat format) { switch (format) { case ExportFormat::Gray8: case ExportFormat::Alpha8: return 1; case ExportFormat::RGB565: case ExportFormat::RGBA5551: case ExportFormat::RGBA4444: return 2; case ExportFormat::RGB888: return 3; case ExportFormat::RGBA8888: return 4; default: VERIFY_NOT_REACHED(); } } static SkColorType export_format_to_skia_color_type(ExportFormat format) { switch (format) { case ExportFormat::Gray8: return SkColorType::kGray_8_SkColorType; case ExportFormat::Alpha8: return SkColorType::kAlpha_8_SkColorType; case ExportFormat::RGB565: return SkColorType::kRGB_565_SkColorType; case ExportFormat::RGBA5551: dbgln("FIXME: Support conversion to RGBA5551."); return SkColorType::kUnknown_SkColorType; case ExportFormat::RGBA4444: return SkColorType::kARGB_4444_SkColorType; case ExportFormat::RGB888: // This one needs to be converted manually because Skia has no valid 24-bit color type. VERIFY_NOT_REACHED(); case ExportFormat::RGBA8888: return SkColorType::kRGBA_8888_SkColorType; default: VERIFY_NOT_REACHED(); } } ErrorOr export_bitmap_to_byte_buffer( Bitmap const& bitmap, ColorSpace const& color_space, ExportFormat format, int flags, Optional target_width, Optional target_height) { int width = target_width.value_or(bitmap.width()); int height = target_height.value_or(bitmap.height()); if (format == ExportFormat::RGB888 && (width != bitmap.width() || height != bitmap.height())) { dbgln("FIXME: Ignoring target width and height because scaling is not implemented for this export format."); width = bitmap.width(); height = bitmap.height(); } Checked buffer_pitch = width; int number_of_bytes = bytes_per_pixel_for_export_format(format); buffer_pitch *= number_of_bytes; if (buffer_pitch.has_overflow()) return Error::from_string_literal("Gfx::export_bitmap_to_byte_buffer size overflow"); if (Checked::multiplication_would_overflow(buffer_pitch.value(), height)) return Error::from_string_literal("Gfx::export_bitmap_to_byte_buffer size overflow"); auto buffer = MUST(ByteBuffer::create_zeroed(buffer_pitch.value() * height)); if (width > 0 && height > 0) { if (format == ExportFormat::RGB888) { // 24 bit RGB is not supported by Skia, so we need to handle this format ourselves. auto* raw_buffer = buffer.data(); for (auto y = 0; y < height; y++) { auto target_y = flags & ExportFlags::FlipY ? height - y - 1 : y; for (auto x = 0; x < width; x++) { auto pixel = bitmap.get_pixel(x, y); auto buffer_offset = (target_y * buffer_pitch.value()) + (x * 3ull); raw_buffer[buffer_offset + 0] = pixel.red(); raw_buffer[buffer_offset + 1] = pixel.green(); raw_buffer[buffer_offset + 2] = pixel.blue(); } } } else { auto image = sk_image_from_bitmap(bitmap, color_space); if (!image) return Error::from_string_literal("Failed to create a Skia image for this Bitmap"); auto skia_format = export_format_to_skia_color_type(format); auto skia_color_space = SkColorSpace::MakeSRGB(); auto image_info = SkImageInfo::Make( width, height, skia_format, flags & ExportFlags::PremultiplyAlpha ? SkAlphaType::kPremul_SkAlphaType : SkAlphaType::kUnpremul_SkAlphaType, skia_color_space); auto surface = SkSurfaces::WrapPixels(image_info, buffer.data(), buffer_pitch.value()); VERIFY(surface); auto* surface_canvas = surface->getCanvas(); auto dst_rect = Gfx::to_skia_rect(Gfx::Rect { 0, 0, width, height }); if (flags & ExportFlags::FlipY) { surface_canvas->translate(0, dst_rect.height()); surface_canvas->scale(1, -1); } surface_canvas->drawImageRect( image.get(), dst_rect, Gfx::to_skia_sampling_options(Gfx::ScalingMode::NearestNeighbor)); } } else { VERIFY(buffer.is_empty()); } return BitmapExportResult { .buffer = move(buffer), .width = width, .height = height, }; } }