Serenity Operating System
at hosted 267 lines 8.2 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <AK/BufferStream.h> 28#include <AK/ByteBuffer.h> 29#include <AK/FileSystemPath.h> 30#include <AK/MappedFile.h> 31#include <AK/NonnullOwnPtrVector.h> 32#include <LibGfx/GIFLoader.h> 33#include <stdio.h> 34#include <string.h> 35 36namespace Gfx { 37 38static RefPtr<Gfx::Bitmap> load_gif_impl(const u8*, size_t); 39 40RefPtr<Gfx::Bitmap> load_gif(const StringView& path) 41{ 42 MappedFile mapped_file(path); 43 if (!mapped_file.is_valid()) 44 return nullptr; 45 auto bitmap = load_gif_impl((const u8*)mapped_file.data(), mapped_file.size()); 46 if (bitmap) 47 bitmap->set_mmap_name(String::format("Gfx::Bitmap [%dx%d] - Decoded GIF: %s", bitmap->width(), bitmap->height(), canonicalized_path(path).characters())); 48 return bitmap; 49} 50 51RefPtr<Gfx::Bitmap> load_gif_from_memory(const u8* data, size_t length) 52{ 53 auto bitmap = load_gif_impl(data, length); 54 if (bitmap) 55 bitmap->set_mmap_name(String::format("Gfx::Bitmap [%dx%d] - Decoded GIF: <memory>", bitmap->width(), bitmap->height())); 56 return bitmap; 57} 58 59enum class GIFFormat { 60 GIF87a, 61 GIF89a, 62}; 63 64struct RGB { 65 u8 r; 66 u8 g; 67 u8 b; 68}; 69 70struct LogicalScreen { 71 u16 width; 72 u16 height; 73 RGB color_map[256]; 74}; 75 76struct ImageDescriptor { 77 u16 x; 78 u16 y; 79 u16 width; 80 u16 height; 81 bool use_global_color_map; 82 RGB color_map[256]; 83 u8 lzw_min_code_size; 84 Vector<u8> lzw_encoded_bytes; 85}; 86 87RefPtr<Gfx::Bitmap> load_gif_impl(const u8* data, size_t data_size) 88{ 89 if (data_size < 32) 90 return nullptr; 91 92 auto buffer = ByteBuffer::wrap(data, data_size); 93 BufferStream stream(buffer); 94 95 static const char valid_header_87[] = "GIF87a"; 96 static const char valid_header_89[] = "GIF89a"; 97 98 char header[6]; 99 for (int i = 0; i < 6; ++i) 100 stream >> header[i]; 101 102 GIFFormat format; 103 if (!memcmp(header, valid_header_87, sizeof(header))) 104 format = GIFFormat::GIF87a; 105 else if (!memcmp(header, valid_header_89, sizeof(header))) 106 format = GIFFormat::GIF89a; 107 else 108 return nullptr; 109 110 printf("Format is %s\n", format == GIFFormat::GIF89a ? "GIF89a" : "GIF87a"); 111 112 LogicalScreen logical_screen; 113 stream >> logical_screen.width; 114 stream >> logical_screen.height; 115 if (stream.handle_read_failure()) 116 return nullptr; 117 118 u8 gcm_info = 0; 119 stream >> gcm_info; 120 121 if (stream.handle_read_failure()) 122 return nullptr; 123 124 bool global_color_map_follows_descriptor = gcm_info & 0x80; 125 u8 bits_per_pixel = (gcm_info & 7) + 1; 126 u8 bits_of_color_resolution = (gcm_info >> 4) & 7; 127 128 printf("LogicalScreen: %dx%d\n", logical_screen.width, logical_screen.height); 129 printf("global_color_map_follows_descriptor: %u\n", global_color_map_follows_descriptor); 130 printf("bits_per_pixel: %u\n", bits_per_pixel); 131 printf("bits_of_color_resolution: %u\n", bits_of_color_resolution); 132 133 u8 background_color = 0; 134 stream >> background_color; 135 if (stream.handle_read_failure()) 136 return nullptr; 137 138 printf("background_color: %u\n", background_color); 139 140 u8 pixel_aspect_ratio = 0; 141 stream >> pixel_aspect_ratio; 142 if (stream.handle_read_failure()) 143 return nullptr; 144 145 int color_map_entry_count = 1; 146 for (int i = 0; i < bits_per_pixel; ++i) 147 color_map_entry_count *= 2; 148 149 printf("color_map_entry_count: %d\n", color_map_entry_count); 150 151 for (int i = 0; i < color_map_entry_count; ++i) { 152 stream >> logical_screen.color_map[i].r; 153 stream >> logical_screen.color_map[i].g; 154 stream >> logical_screen.color_map[i].b; 155 } 156 157 if (stream.handle_read_failure()) 158 return nullptr; 159 160 for (int i = 0; i < color_map_entry_count; ++i) { 161 auto& rgb = logical_screen.color_map[i]; 162 printf("[%02x]: %s\n", i, Color(rgb.r, rgb.g, rgb.b).to_string().characters()); 163 } 164 165 NonnullOwnPtrVector<ImageDescriptor> images; 166 167 for (;;) { 168 u8 sentinel = 0; 169 stream >> sentinel; 170 printf("Sentinel: %02x\n", sentinel); 171 172 if (sentinel == 0x21) { 173 u8 extension_type = 0; 174 stream >> extension_type; 175 if (stream.handle_read_failure()) 176 return nullptr; 177 178 printf("Extension block of type %02x\n", extension_type); 179 180 u8 sub_block_length = 0; 181 182 for (;;) { 183 stream >> sub_block_length; 184 185 if (stream.handle_read_failure()) 186 return nullptr; 187 188 if (sub_block_length == 0) 189 break; 190 191 u8 dummy; 192 for (u16 i = 0; i < sub_block_length; ++i) 193 stream >> dummy; 194 195 if (stream.handle_read_failure()) 196 return nullptr; 197 } 198 continue; 199 } 200 201 if (sentinel == 0x2c) { 202 images.append(make<ImageDescriptor>()); 203 auto& image = images.last(); 204 u8 packed_fields { 0 }; 205 stream >> image.x; 206 stream >> image.y; 207 stream >> image.width; 208 stream >> image.height; 209 stream >> packed_fields; 210 if (stream.handle_read_failure()) 211 return nullptr; 212 printf("Image descriptor: %d,%d %dx%d, %02x\n", image.x, image.y, image.width, image.height, packed_fields); 213 214 stream >> image.lzw_min_code_size; 215 216 printf("min code size: %u\n", image.lzw_min_code_size); 217 218 u8 lzw_encoded_bytes_expected = 0; 219 220 for (;;) { 221 stream >> lzw_encoded_bytes_expected; 222 223 if (stream.handle_read_failure()) 224 return nullptr; 225 226 if (lzw_encoded_bytes_expected == 0) 227 break; 228 229 u8 buffer[256]; 230 for (int i = 0; i < lzw_encoded_bytes_expected; ++i) { 231 stream >> buffer[i]; 232 } 233 234 if (stream.handle_read_failure()) 235 return nullptr; 236 237 for (int i = 0; i < lzw_encoded_bytes_expected; ++i) { 238 image.lzw_encoded_bytes.append(buffer[i]); 239 } 240 } 241 continue; 242 } 243 244 if (sentinel == 0x3b) { 245 printf("Trailer! Awesome :)\n"); 246 break; 247 } 248 249 return nullptr; 250 } 251 252 // We exited the block loop after finding a trailer. We should have everything needed. 253 printf("Image count: %zu\n", images.size()); 254 if (images.is_empty()) 255 return nullptr; 256 257 for (size_t i = 0; i < images.size(); ++i) { 258 auto& image = images.at(i); 259 printf("Image %zu: %d,%d %dx%d %zu bytes LZW-encoded\n", i, image.x, image.y, image.width, image.height, image.lzw_encoded_bytes.size()); 260 261 // FIXME: Decode the LZW-encoded bytes and turn them into an image. 262 } 263 264 return nullptr; 265} 266 267}