/* * Copyright (c) 2020, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include namespace ImageDecoder { static HashMap> s_connections; static IDAllocator s_client_ids; ConnectionFromClient::ConnectionFromClient(IPC::Transport transport) : IPC::ConnectionFromClient(*this, move(transport), s_client_ids.allocate()) { s_connections.set(client_id(), *this); } void ConnectionFromClient::die() { for (auto& [_, job] : m_pending_jobs) { job->cancel(); } m_pending_jobs.clear(); auto client_id = this->client_id(); s_connections.remove(client_id); s_client_ids.deallocate(client_id); if (s_connections.is_empty()) { Threading::quit_background_thread(); Core::EventLoop::current().quit(0); } } ErrorOr ConnectionFromClient::connect_new_client() { int socket_fds[2] {}; if (auto err = Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, socket_fds); err.is_error()) return err.release_error(); auto client_socket_or_error = Core::LocalSocket::adopt_fd(socket_fds[0]); if (client_socket_or_error.is_error()) { close(socket_fds[0]); close(socket_fds[1]); return client_socket_or_error.release_error(); } auto client_socket = client_socket_or_error.release_value(); // Note: A ref is stored in the static s_connections map auto client = adopt_ref(*new ConnectionFromClient(IPC::Transport(move(client_socket)))); return IPC::File::adopt_fd(socket_fds[1]); } Messages::ImageDecoderServer::ConnectNewClientsResponse ConnectionFromClient::connect_new_clients(size_t count) { Vector files; files.ensure_capacity(count); for (size_t i = 0; i < count; ++i) { auto file_or_error = connect_new_client(); if (file_or_error.is_error()) { dbgln("Failed to connect new client: {}", file_or_error.error()); return Vector {}; } files.unchecked_append(file_or_error.release_value()); } return files; } static void decode_image_to_bitmaps_and_durations_with_decoder(Gfx::ImageDecoder const& decoder, Optional ideal_size, Vector>>& bitmaps, Vector& durations) { for (size_t i = 0; i < decoder.frame_count(); ++i) { auto frame_or_error = decoder.frame(i, ideal_size); if (frame_or_error.is_error()) { bitmaps.append({}); durations.append(0); } else { auto frame = frame_or_error.release_value(); bitmaps.append(frame.image.release_nonnull()); durations.append(frame.duration); } } } static ErrorOr decode_image_to_details(Core::AnonymousBuffer const& encoded_buffer, Optional ideal_size, Optional const& known_mime_type) { auto decoder = TRY(Gfx::ImageDecoder::try_create_for_raw_bytes(ReadonlyBytes { encoded_buffer.data(), encoded_buffer.size() }, known_mime_type)); if (!decoder) return Error::from_string_literal("Could not find suitable image decoder plugin for data"); if (!decoder->frame_count()) return Error::from_string_literal("Could not decode image from encoded data"); ConnectionFromClient::DecodeResult result; result.is_animated = decoder->is_animated(); result.loop_count = decoder->loop_count(); if (auto maybe_icc_data = decoder->color_space(); !maybe_icc_data.is_error()) result.color_profile = maybe_icc_data.value(); Vector>> bitmaps; if (auto maybe_metadata = decoder->metadata(); maybe_metadata.has_value() && is(*maybe_metadata)) { auto const& exif = static_cast(maybe_metadata.value()); if (exif.x_resolution().has_value() && exif.y_resolution().has_value()) { auto const x_resolution = exif.x_resolution()->as_double(); auto const y_resolution = exif.y_resolution()->as_double(); if (x_resolution < y_resolution) result.scale.set_y(x_resolution / y_resolution); else result.scale.set_x(y_resolution / x_resolution); } } decode_image_to_bitmaps_and_durations_with_decoder(*decoder, move(ideal_size), bitmaps, result.durations); if (bitmaps.is_empty()) return Error::from_string_literal("Could not decode image"); result.bitmaps = Gfx::BitmapSequence { bitmaps }; return result; } NonnullRefPtr ConnectionFromClient::make_decode_image_job(i64 image_id, Core::AnonymousBuffer encoded_buffer, Optional ideal_size, Optional mime_type) { return Job::construct( [encoded_buffer = move(encoded_buffer), ideal_size = move(ideal_size), mime_type = move(mime_type)](auto&) -> ErrorOr { return TRY(decode_image_to_details(encoded_buffer, ideal_size, mime_type)); }, [strong_this = NonnullRefPtr(*this), image_id](DecodeResult result) -> ErrorOr { strong_this->async_did_decode_image(image_id, result.is_animated, result.loop_count, result.bitmaps, result.durations, result.scale, result.color_profile); strong_this->m_pending_jobs.remove(image_id); return {}; }, [strong_this = NonnullRefPtr(*this), image_id](Error error) -> void { if (strong_this->is_open()) strong_this->async_did_fail_to_decode_image(image_id, MUST(String::formatted("Decoding failed: {}", error))); strong_this->m_pending_jobs.remove(image_id); }); } Messages::ImageDecoderServer::DecodeImageResponse ConnectionFromClient::decode_image(Core::AnonymousBuffer const& encoded_buffer, Optional const& ideal_size, Optional const& mime_type) { auto image_id = m_next_image_id++; if (!encoded_buffer.is_valid()) { dbgln_if(IMAGE_DECODER_DEBUG, "Encoded data is invalid"); async_did_fail_to_decode_image(image_id, "Encoded data is invalid"_string); return image_id; } m_pending_jobs.set(image_id, make_decode_image_job(image_id, encoded_buffer, ideal_size, mime_type)); return image_id; } void ConnectionFromClient::cancel_decoding(i64 image_id) { if (auto job = m_pending_jobs.take(image_id); job.has_value()) { job.value()->cancel(); } } }