| 
									
										
										
										
											2020-06-22 21:35:22 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2021-04-22 01:24:48 -07:00
										 |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							| 
									
										
										
										
											2020-06-22 21:35:22 +02:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-21 20:08:03 +02:00
										 |  |  | #include <AK/Debug.h>
 | 
					
						
							| 
									
										
										
										
											2024-06-26 13:44:42 -06:00
										 |  |  | #include <AK/IDAllocator.h>
 | 
					
						
							| 
									
										
										
										
											2022-02-25 12:18:30 +02:00
										 |  |  | #include <ImageDecoder/ConnectionFromClient.h>
 | 
					
						
							| 
									
										
										
										
											2020-06-22 21:35:22 +02:00
										 |  |  | #include <ImageDecoder/ImageDecoderClientEndpoint.h>
 | 
					
						
							|  |  |  | #include <LibGfx/Bitmap.h>
 | 
					
						
							| 
									
										
										
										
											2023-03-21 14:58:06 -04:00
										 |  |  | #include <LibGfx/ImageFormats/ImageDecoder.h>
 | 
					
						
							| 
									
										
										
										
											2024-02-14 01:15:06 -05:00
										 |  |  | #include <LibGfx/ImageFormats/TIFFMetadata.h>
 | 
					
						
							| 
									
										
										
										
											2020-06-22 21:35:22 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace ImageDecoder { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 13:44:42 -06:00
										 |  |  | static HashMap<int, RefPtr<ConnectionFromClient>> s_connections; | 
					
						
							|  |  |  | static IDAllocator s_client_ids; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-08 23:05:44 +01:00
										 |  |  | ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr<Core::LocalSocket> socket) | 
					
						
							| 
									
										
										
										
											2024-06-26 13:44:42 -06:00
										 |  |  |     : IPC::ConnectionFromClient<ImageDecoderClientEndpoint, ImageDecoderServerEndpoint>(*this, move(socket), s_client_ids.allocate()) | 
					
						
							| 
									
										
										
										
											2020-06-22 21:35:22 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-26 13:44:42 -06:00
										 |  |  |     s_connections.set(client_id(), *this); | 
					
						
							| 
									
										
										
										
											2020-06-22 21:35:22 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-25 12:18:30 +02:00
										 |  |  | void ConnectionFromClient::die() | 
					
						
							| 
									
										
										
										
											2020-06-22 21:35:22 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-04-23 18:58:52 -06:00
										 |  |  |     for (auto& [_, job] : m_pending_jobs) { | 
					
						
							|  |  |  |         job->cancel(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     m_pending_jobs.clear(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 13:44:42 -06:00
										 |  |  |     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<IPC::File> 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(move(client_socket))); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return IPC::File::adopt_fd(socket_fds[1]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Messages::ImageDecoderServer::ConnectNewClientsResponse ConnectionFromClient::connect_new_clients(size_t count) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     Vector<IPC::File> 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<IPC::File> {}; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         files.unchecked_append(file_or_error.release_value()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return files; | 
					
						
							| 
									
										
										
										
											2020-06-22 21:35:22 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-17 19:14:44 +02:00
										 |  |  | static void decode_image_to_bitmaps_and_durations_with_decoder(Gfx::ImageDecoder const& decoder, Optional<Gfx::IntSize> ideal_size, Vector<Optional<NonnullRefPtr<Gfx::Bitmap>>>& bitmaps, Vector<u32>& durations) | 
					
						
							| 
									
										
										
										
											2020-06-22 21:35:22 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-01-15 21:50:32 +02:00
										 |  |  |     for (size_t i = 0; i < decoder.frame_count(); ++i) { | 
					
						
							| 
									
										
										
										
											2023-12-23 13:56:23 -05:00
										 |  |  |         auto frame_or_error = decoder.frame(i, ideal_size); | 
					
						
							| 
									
										
										
										
											2023-01-15 21:50:32 +02:00
										 |  |  |         if (frame_or_error.is_error()) { | 
					
						
							| 
									
										
										
										
											2024-07-17 19:14:44 +02:00
										 |  |  |             bitmaps.append({}); | 
					
						
							| 
									
										
										
										
											2023-01-15 21:50:32 +02:00
										 |  |  |             durations.append(0); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             auto frame = frame_or_error.release_value(); | 
					
						
							| 
									
										
										
										
											2024-07-17 19:14:44 +02:00
										 |  |  |             bitmaps.append(frame.image.release_nonnull()); | 
					
						
							| 
									
										
										
										
											2023-01-15 21:50:32 +02:00
										 |  |  |             durations.append(frame.duration); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-06-22 21:35:22 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-15 21:50:32 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-23 18:58:52 -06:00
										 |  |  | static ErrorOr<ConnectionFromClient::DecodeResult> decode_image_to_details(Core::AnonymousBuffer const& encoded_buffer, Optional<Gfx::IntSize> ideal_size, Optional<ByteString> const& known_mime_type) | 
					
						
							| 
									
										
										
										
											2023-01-15 21:50:32 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-04-23 18:58:52 -06:00
										 |  |  |     auto decoder = TRY(Gfx::ImageDecoder::try_create_for_raw_bytes(ReadonlyBytes { encoded_buffer.data<u8>(), encoded_buffer.size() }, known_mime_type)); | 
					
						
							| 
									
										
										
										
											2020-06-22 21:35:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-23 18:58:52 -06:00
										 |  |  |     if (!decoder) | 
					
						
							|  |  |  |         return Error::from_string_literal("Could not find suitable image decoder plugin for data"); | 
					
						
							| 
									
										
										
										
											2020-06-22 21:35:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-23 18:58:52 -06:00
										 |  |  |     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(); | 
					
						
							| 
									
										
										
										
											2024-02-14 01:15:06 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (auto maybe_metadata = decoder->metadata(); maybe_metadata.has_value() && is<Gfx::ExifMetadata>(*maybe_metadata)) { | 
					
						
							|  |  |  |         auto const& exif = static_cast<Gfx::ExifMetadata const&>(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) | 
					
						
							| 
									
										
										
										
											2024-04-23 18:58:52 -06:00
										 |  |  |                 result.scale.set_y(x_resolution / y_resolution); | 
					
						
							| 
									
										
										
										
											2024-02-14 01:15:06 -05:00
										 |  |  |             else | 
					
						
							| 
									
										
										
										
											2024-04-23 18:58:52 -06:00
										 |  |  |                 result.scale.set_x(y_resolution / x_resolution); | 
					
						
							| 
									
										
										
										
											2024-02-14 01:15:06 -05:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-23 18:58:52 -06:00
										 |  |  |     decode_image_to_bitmaps_and_durations_with_decoder(*decoder, move(ideal_size), result.bitmaps, result.durations); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (result.bitmaps.is_empty()) | 
					
						
							|  |  |  |         return Error::from_string_literal("Could not decode image"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return result; | 
					
						
							| 
									
										
										
										
											2023-01-15 21:50:32 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-06-22 21:35:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-23 18:58:52 -06:00
										 |  |  | NonnullRefPtr<ConnectionFromClient::Job> ConnectionFromClient::make_decode_image_job(i64 image_id, Core::AnonymousBuffer encoded_buffer, Optional<Gfx::IntSize> ideal_size, Optional<ByteString> mime_type) | 
					
						
							| 
									
										
										
										
											2024-04-19 14:23:16 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-04-23 18:58:52 -06:00
										 |  |  |     return Job::construct( | 
					
						
							|  |  |  |         [encoded_buffer = move(encoded_buffer), ideal_size = move(ideal_size), mime_type = move(mime_type)](auto&) -> ErrorOr<DecodeResult> { | 
					
						
							|  |  |  |             return TRY(decode_image_to_details(encoded_buffer, ideal_size, mime_type)); | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         [strong_this = NonnullRefPtr(*this), image_id](DecodeResult result) -> ErrorOr<void> { | 
					
						
							|  |  |  |             strong_this->async_did_decode_image(image_id, result.is_animated, result.loop_count, result.bitmaps, result.durations, result.scale); | 
					
						
							|  |  |  |             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); | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2024-04-19 14:23:16 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-23 13:56:23 -05:00
										 |  |  | Messages::ImageDecoderServer::DecodeImageResponse ConnectionFromClient::decode_image(Core::AnonymousBuffer const& encoded_buffer, Optional<Gfx::IntSize> const& ideal_size, Optional<ByteString> const& mime_type) | 
					
						
							| 
									
										
										
										
											2023-01-15 21:50:32 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-04-19 14:23:16 -06:00
										 |  |  |     auto image_id = m_next_image_id++; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-15 21:50:32 +02:00
										 |  |  |     if (!encoded_buffer.is_valid()) { | 
					
						
							|  |  |  |         dbgln_if(IMAGE_DECODER_DEBUG, "Encoded data is invalid"); | 
					
						
							| 
									
										
										
										
											2024-04-19 14:23:16 -06:00
										 |  |  |         async_did_fail_to_decode_image(image_id, "Encoded data is invalid"_string); | 
					
						
							|  |  |  |         return image_id; | 
					
						
							| 
									
										
										
										
											2021-01-29 22:30:48 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-19 14:23:16 -06:00
										 |  |  |     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()) { | 
					
						
							| 
									
										
										
										
											2024-04-23 18:58:52 -06:00
										 |  |  |         job.value()->cancel(); | 
					
						
							| 
									
										
										
										
											2024-04-19 14:23:16 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-06-22 21:35:22 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } |