mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 11:32:43 +00:00 
			
		
		
		
	LibDraw: Start work on a GIF decoder (not yet functional!)
Here comes the first part of a GIF decoder. It decodes up to the point of gathering all the LZW-compressed data. The next step is to implement decompression, and then turn the decompressed data into a bitmap using the color maps, etc.
This commit is contained in:
		
							parent
							
								
									630d5b3ffd
								
							
						
					
					
						commit
						00ab9488ad
					
				
					 3 changed files with 257 additions and 0 deletions
				
			
		
							
								
								
									
										235
									
								
								Libraries/LibDraw/GIFLoader.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								Libraries/LibDraw/GIFLoader.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,235 @@ | |||
| #include <AK/BufferStream.h> | ||||
| #include <AK/ByteBuffer.h> | ||||
| #include <AK/FileSystemPath.h> | ||||
| #include <AK/NonnullOwnPtrVector.h> | ||||
| #include <LibDraw/GIFLoader.h> | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| static RefPtr<GraphicsBitmap> load_gif_impl(const u8*, size_t); | ||||
| 
 | ||||
| RefPtr<GraphicsBitmap> load_gif(const StringView& path) | ||||
| { | ||||
|     MappedFile mapped_file(path); | ||||
|     if (!mapped_file.is_valid()) | ||||
|         return nullptr; | ||||
|     auto bitmap = load_gif_impl((const u8*)mapped_file.data(), mapped_file.size()); | ||||
|     if (bitmap) | ||||
|         bitmap->set_mmap_name(String::format("GraphicsBitmap [%dx%d] - Decoded GIF: %s", bitmap->width(), bitmap->height(), canonicalized_path(path).characters())); | ||||
|     return bitmap; | ||||
| } | ||||
| 
 | ||||
| RefPtr<GraphicsBitmap> load_gif_from_memory(const u8* data, size_t length) | ||||
| { | ||||
|     auto bitmap = load_gif_impl(data, length); | ||||
|     if (bitmap) | ||||
|         bitmap->set_mmap_name(String::format("GraphicsBitmap [%dx%d] - Decoded GIF: <memory>", bitmap->width(), bitmap->height())); | ||||
|     return bitmap; | ||||
| } | ||||
| 
 | ||||
| enum class GIFFormat { | ||||
|     GIF87a, | ||||
|     GIF89a, | ||||
| }; | ||||
| 
 | ||||
| struct RGB { | ||||
|     u8 r; | ||||
|     u8 g; | ||||
|     u8 b; | ||||
| }; | ||||
| 
 | ||||
| struct LogicalScreen { | ||||
|     u16 width; | ||||
|     u16 height; | ||||
|     RGB color_map[256]; | ||||
| }; | ||||
| 
 | ||||
| struct ImageDescriptor { | ||||
|     u16 x; | ||||
|     u16 y; | ||||
|     u16 width; | ||||
|     u16 height; | ||||
|     bool use_global_color_map; | ||||
|     RGB color_map[256]; | ||||
|     u8 lzw_min_code_size; | ||||
|     Vector<u8> lzw_encoded_bytes; | ||||
| }; | ||||
| 
 | ||||
| RefPtr<GraphicsBitmap> load_gif_impl(const u8* data, size_t data_size) | ||||
| { | ||||
|     if (data_size < 32) | ||||
|         return nullptr; | ||||
| 
 | ||||
|     auto buffer = ByteBuffer::wrap(data, data_size); | ||||
|     BufferStream stream(buffer); | ||||
| 
 | ||||
|     static const char valid_header_87[] = "GIF87a"; | ||||
|     static const char valid_header_89[] = "GIF89a"; | ||||
| 
 | ||||
|     char header[6]; | ||||
|     for (int i = 0; i < 6; ++i) | ||||
|         stream >> header[i]; | ||||
| 
 | ||||
|     GIFFormat format; | ||||
|     if (!memcmp(header, valid_header_87, sizeof(header))) | ||||
|         format = GIFFormat::GIF87a; | ||||
|     else if (!memcmp(header, valid_header_89, sizeof(header))) | ||||
|         format = GIFFormat::GIF89a; | ||||
|     else | ||||
|         return nullptr; | ||||
| 
 | ||||
|     printf("Format is %s\n", format == GIFFormat::GIF89a ? "GIF89a" : "GIF87a"); | ||||
| 
 | ||||
|     LogicalScreen logical_screen; | ||||
|     stream >> logical_screen.width; | ||||
|     stream >> logical_screen.height; | ||||
|     if (stream.handle_read_failure()) | ||||
|         return nullptr; | ||||
| 
 | ||||
|     u8 gcm_info = 0; | ||||
|     stream >> gcm_info; | ||||
| 
 | ||||
|     if (stream.handle_read_failure()) | ||||
|         return nullptr; | ||||
| 
 | ||||
|     bool global_color_map_follows_descriptor = gcm_info & 0x80; | ||||
|     u8 bits_per_pixel = (gcm_info & 7) + 1; | ||||
|     u8 bits_of_color_resolution = (gcm_info >> 4) & 7; | ||||
| 
 | ||||
|     printf("LogicalScreen: %dx%d\n", logical_screen.width, logical_screen.height); | ||||
|     printf("global_color_map_follows_descriptor: %u\n", global_color_map_follows_descriptor); | ||||
|     printf("bits_per_pixel: %u\n", bits_per_pixel); | ||||
|     printf("bits_of_color_resolution: %u\n", bits_of_color_resolution); | ||||
| 
 | ||||
|     u8 background_color = 0; | ||||
|     stream >> background_color; | ||||
|     if (stream.handle_read_failure()) | ||||
|         return nullptr; | ||||
| 
 | ||||
|     printf("background_color: %u\n", background_color); | ||||
| 
 | ||||
|     u8 pixel_aspect_ratio = 0; | ||||
|     stream >> pixel_aspect_ratio; | ||||
|     if (stream.handle_read_failure()) | ||||
|         return nullptr; | ||||
| 
 | ||||
|     int color_map_entry_count = 1; | ||||
|     for (int i = 0; i < bits_per_pixel; ++i) | ||||
|         color_map_entry_count *= 2; | ||||
| 
 | ||||
|     printf("color_map_entry_count: %d\n", color_map_entry_count); | ||||
| 
 | ||||
|     for (int i = 0; i < color_map_entry_count; ++i) { | ||||
|         stream >> logical_screen.color_map[i].r; | ||||
|         stream >> logical_screen.color_map[i].g; | ||||
|         stream >> logical_screen.color_map[i].b; | ||||
|     } | ||||
| 
 | ||||
|     if (stream.handle_read_failure()) | ||||
|         return nullptr; | ||||
| 
 | ||||
|     for (int i = 0; i < color_map_entry_count; ++i) { | ||||
|         auto& rgb = logical_screen.color_map[i]; | ||||
|         printf("[%02x]: %s\n", i, Color(rgb.r, rgb.g, rgb.b).to_string().characters()); | ||||
|     } | ||||
| 
 | ||||
|     NonnullOwnPtrVector<ImageDescriptor> images; | ||||
| 
 | ||||
|     for (;;) { | ||||
|         u8 sentinel = 0; | ||||
|         stream >> sentinel; | ||||
|         printf("Sentinel: %02x\n", sentinel); | ||||
| 
 | ||||
|         if (sentinel == 0x21) { | ||||
|             u8 extension_type = 0; | ||||
|             stream >> extension_type; | ||||
|             if (stream.handle_read_failure()) | ||||
|                 return nullptr; | ||||
| 
 | ||||
|             printf("Extension block of type %02x\n", extension_type); | ||||
| 
 | ||||
|             u8 sub_block_length = 0; | ||||
| 
 | ||||
|             for (;;) { | ||||
|                 stream >> sub_block_length; | ||||
| 
 | ||||
|                 if (stream.handle_read_failure()) | ||||
|                     return nullptr; | ||||
| 
 | ||||
|                 if (sub_block_length == 0) | ||||
|                     break; | ||||
| 
 | ||||
|                 u8 dummy; | ||||
|                 for (u16 i = 0; i < sub_block_length; ++i) | ||||
|                     stream >> dummy; | ||||
| 
 | ||||
|                 if (stream.handle_read_failure()) | ||||
|                     return nullptr; | ||||
|             } | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         if (sentinel == 0x2c) { | ||||
|             images.append(make<ImageDescriptor>()); | ||||
|             auto& image = images.last(); | ||||
|             u8 packed_fields; | ||||
|             stream >> image.x; | ||||
|             stream >> image.y; | ||||
|             stream >> image.width; | ||||
|             stream >> image.height; | ||||
|             stream >> packed_fields; | ||||
|             if (stream.handle_read_failure()) | ||||
|                 return nullptr; | ||||
|             printf("Image descriptor: %d,%d %dx%d, %02x\n", image.x, image.y, image.width, image.height, packed_fields); | ||||
| 
 | ||||
|             stream >> image.lzw_min_code_size; | ||||
| 
 | ||||
|             printf("min code size: %u\n", image.lzw_min_code_size); | ||||
| 
 | ||||
|             u8 lzw_encoded_bytes_expected = 0; | ||||
| 
 | ||||
|             for (;;) { | ||||
|                 stream >> lzw_encoded_bytes_expected; | ||||
| 
 | ||||
|                 if (stream.handle_read_failure()) | ||||
|                     return nullptr; | ||||
| 
 | ||||
|                 if (lzw_encoded_bytes_expected == 0) | ||||
|                     break; | ||||
| 
 | ||||
|                 u8 buffer[256]; | ||||
|                 for (int i = 0; i < lzw_encoded_bytes_expected; ++i) { | ||||
|                     stream >> buffer[i]; | ||||
|                 } | ||||
| 
 | ||||
|                 if (stream.handle_read_failure()) | ||||
|                     return nullptr; | ||||
| 
 | ||||
|                 for (int i = 0; i < lzw_encoded_bytes_expected; ++i) { | ||||
|                     image.lzw_encoded_bytes.append(buffer[i]); | ||||
|                 } | ||||
|             } | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         if (sentinel == 0x3b) { | ||||
|             printf("Trailer! Awesome :)\n"); | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         return nullptr; | ||||
|     } | ||||
| 
 | ||||
|     // We exited the block loop after finding a trailer. We should have everything needed.
 | ||||
|     printf("Image count: %d\n", images.size()); | ||||
|     if (images.is_empty()) | ||||
|         return nullptr; | ||||
| 
 | ||||
|     for (int i = 0; i < images.size(); ++i) { | ||||
|         auto& image = images.at(i); | ||||
|         printf("Image %d: %d,%d %dx%d  %d bytes LZW-encoded\n", i, image.x, image.y, image.width, image.height, image.lzw_encoded_bytes.size()); | ||||
| 
 | ||||
|         // FIXME: Decode the LZW-encoded bytes and turn them into an image.
 | ||||
|     } | ||||
| 
 | ||||
|     return nullptr; | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling