mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 21:52:45 +00:00 
			
		
		
		
	LibGfx: Commonize functions in P*MLoader class implementations
Problem: - Functions are duplicated in [PBM,PGM,PPM]Loader class implementations. They are functionally equivalent. This does not follow the DRY (Don't Repeat Yourself) principle. Solution: - Factor out the common functions into a separate file. - Refactor common code to generic functions. - Change `PPM_DEBUG` macro to be `PORTABLE_IMAGE_LOADER_DEBUG` to work with all the supported types. This requires adding the image type to the debug log messages for easier debugging.
This commit is contained in:
		
							parent
							
								
									6fac1abac4
								
							
						
					
					
						commit
						a95d230a3e
					
				
					 6 changed files with 367 additions and 685 deletions
				
			
		|  | @ -25,6 +25,7 @@ | |||
|  */ | ||||
| 
 | ||||
| #include "PBMLoader.h" | ||||
| #include "PortableImageLoaderCommon.h" | ||||
| #include "Streamer.h" | ||||
| #include <AK/Endian.h> | ||||
| #include <AK/LexicalPath.h> | ||||
|  | @ -37,8 +38,8 @@ namespace Gfx { | |||
| struct PBMLoadingContext { | ||||
|     enum Type { | ||||
|         Unknown, | ||||
|         P1_ASCII, | ||||
|         P4_RAWBITS | ||||
|         ASCII, | ||||
|         RAWBITS | ||||
|     }; | ||||
| 
 | ||||
|     enum State { | ||||
|  | @ -51,6 +52,10 @@ struct PBMLoadingContext { | |||
|         Decoded | ||||
|     }; | ||||
| 
 | ||||
|     static constexpr auto ascii_magic_number = '1'; | ||||
|     static constexpr auto binary_magic_number = '4'; | ||||
|     static constexpr auto image_type = "PBM"; | ||||
| 
 | ||||
|     Type type { Type::Unknown }; | ||||
|     State state { State::NotDecoded }; | ||||
|     const u8* data { nullptr }; | ||||
|  | @ -60,138 +65,12 @@ struct PBMLoadingContext { | |||
|     RefPtr<Gfx::Bitmap> bitmap; | ||||
| }; | ||||
| 
 | ||||
| static int read_number(Streamer& streamer) | ||||
| { | ||||
|     u8 byte; | ||||
|     StringBuilder sb; | ||||
| 
 | ||||
|     while (streamer.read(byte)) { | ||||
|         if (byte == ' ' || byte == '\t' || byte == '\n' || byte == '\r') { | ||||
|             streamer.step_back(); | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         sb.append(byte); | ||||
|     } | ||||
| 
 | ||||
|     return sb.to_string().to_uint().value_or(0); | ||||
| } | ||||
| 
 | ||||
| static bool read_comment([[maybe_unused]] PBMLoadingContext& context, Streamer& streamer) | ||||
| { | ||||
|     bool exist = false; | ||||
|     u8 byte; | ||||
| 
 | ||||
|     while (streamer.read(byte)) { | ||||
|         switch (byte) { | ||||
|         case '#': { | ||||
|             exist = true; | ||||
|             break; | ||||
|         } | ||||
|         case '\t': | ||||
|         case '\n': { | ||||
|             return exist; | ||||
|         } | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return exist; | ||||
| } | ||||
| 
 | ||||
| static bool read_magic_number(PBMLoadingContext& context, Streamer& streamer) | ||||
| { | ||||
|     if (context.state >= PBMLoadingContext::MagicNumber) | ||||
|         return true; | ||||
| 
 | ||||
|     if (!context.data || context.data_size < 2) { | ||||
|         context.state = PBMLoadingContext::State::Error; | ||||
|         dbg() << "There is no enough data."; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     u8 magic_number[2]; | ||||
|     if (!streamer.read_bytes(magic_number, 2)) { | ||||
|         context.state = PBMLoadingContext::State::Error; | ||||
|         dbg() << "We can't read magic number."; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if (magic_number[0] == 'P' && magic_number[1] == '1') { | ||||
|         context.type = PBMLoadingContext::P1_ASCII; | ||||
|         context.state = PBMLoadingContext::MagicNumber; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     if (magic_number[0] == 'P' && magic_number[1] == '4') { | ||||
|         context.type = PBMLoadingContext::P4_RAWBITS; | ||||
|         context.state = PBMLoadingContext::MagicNumber; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     context.state = PBMLoadingContext::State::Error; | ||||
|     dbg() << "Magic number is not valid." << (char)magic_number[0] << (char)magic_number[1]; | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| static bool read_white_space(PBMLoadingContext& context, Streamer& streamer) | ||||
| { | ||||
|     bool exist = false; | ||||
|     u8 byte; | ||||
| 
 | ||||
|     while (streamer.read(byte)) { | ||||
|         switch (byte) { | ||||
|         case ' ': | ||||
|         case '\t': | ||||
|         case '\n': | ||||
|         case '\r': { | ||||
|             exist = true; | ||||
|             break; | ||||
|         } | ||||
|         case '#': { | ||||
|             streamer.step_back(); | ||||
|             read_comment(context, streamer); | ||||
|             break; | ||||
|         } | ||||
|         default: { | ||||
|             streamer.step_back(); | ||||
|             return exist; | ||||
|         } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return exist; | ||||
| } | ||||
| 
 | ||||
| static bool read_width(PBMLoadingContext& context, Streamer& streamer) | ||||
| { | ||||
|     context.width = read_number(streamer); | ||||
|     if (context.width == 0) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     context.state = PBMLoadingContext::Width; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static bool read_height(PBMLoadingContext& context, Streamer& streamer) | ||||
| { | ||||
|     context.height = read_number(streamer); | ||||
|     if (context.height == 0) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     context.state = PBMLoadingContext::Height; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static bool read_image_data(PBMLoadingContext& context, Streamer& streamer) | ||||
| { | ||||
|     u8 byte; | ||||
|     Vector<Gfx::Color> color_data; | ||||
| 
 | ||||
|     if (context.type == PBMLoadingContext::P1_ASCII) { | ||||
|     if (context.type == PBMLoadingContext::ASCII) { | ||||
|         while (streamer.read(byte)) { | ||||
|             if (byte == '0') { | ||||
|                 color_data.append(Color::White); | ||||
|  | @ -199,7 +78,7 @@ static bool read_image_data(PBMLoadingContext& context, Streamer& streamer) | |||
|                 color_data.append(Color::Black); | ||||
|             } | ||||
|         } | ||||
|     } else if (context.type == PBMLoadingContext::P4_RAWBITS) { | ||||
|     } else if (context.type == PBMLoadingContext::RAWBITS) { | ||||
|         size_t color_index = 0; | ||||
| 
 | ||||
|         while (streamer.read(byte)) { | ||||
|  | @ -222,83 +101,24 @@ static bool read_image_data(PBMLoadingContext& context, Streamer& streamer) | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     context.bitmap = Bitmap::create_purgeable(BitmapFormat::RGB32, { context.width, context.height }); | ||||
|     if (!context.bitmap) { | ||||
|         context.state = PBMLoadingContext::State::Error; | ||||
|     if (!create_bitmap(context)) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     size_t index = 0; | ||||
|     for (int y = 0; y < context.height; ++y) { | ||||
|         for (int x = 0; x < context.width; ++x) { | ||||
|             context.bitmap->set_pixel(x, y, color_data.at(index)); | ||||
|             index++; | ||||
|         } | ||||
|     } | ||||
|     set_pixels(context, color_data); | ||||
| 
 | ||||
|     context.state = PBMLoadingContext::State::Bitmap; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static bool decode_pbm(PBMLoadingContext& context) | ||||
| { | ||||
|     if (context.state >= PBMLoadingContext::State::Decoded) | ||||
|         return true; | ||||
| 
 | ||||
|     Streamer streamer(context.data, context.data_size); | ||||
| 
 | ||||
|     if (!read_magic_number(context, streamer)) | ||||
|         return false; | ||||
|     if (!read_white_space(context, streamer)) | ||||
|         return false; | ||||
| 
 | ||||
|     if (!read_width(context, streamer)) | ||||
|         return false; | ||||
| 
 | ||||
|     if (!read_white_space(context, streamer)) | ||||
|         return false; | ||||
| 
 | ||||
|     if (!read_height(context, streamer)) | ||||
|         return false; | ||||
| 
 | ||||
|     if (!read_white_space(context, streamer)) | ||||
|         return false; | ||||
| 
 | ||||
|     if (!read_image_data(context, streamer)) | ||||
|         return false; | ||||
| 
 | ||||
|     context.state = PBMLoadingContext::State::Decoded; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static RefPtr<Gfx::Bitmap> load_pbm_impl(const u8* data, size_t data_size) | ||||
| { | ||||
|     PBMLoadingContext context; | ||||
|     context.data = data; | ||||
|     context.data_size = data_size; | ||||
| 
 | ||||
|     if (!decode_pbm(context)) | ||||
|         return nullptr; | ||||
| 
 | ||||
|     return context.bitmap; | ||||
| } | ||||
| 
 | ||||
| RefPtr<Gfx::Bitmap> load_pbm(const StringView& path) | ||||
| { | ||||
|     MappedFile mapped_file(path); | ||||
|     if (!mapped_file.is_valid()) { | ||||
|         return nullptr; | ||||
|     } | ||||
| 
 | ||||
|     auto bitmap = load_pbm_impl((const u8*)mapped_file.data(), mapped_file.size()); | ||||
|     if (bitmap) | ||||
|         bitmap->set_mmap_name(String::format("Gfx::Bitmap [%dx%d] - Decoded PBM: %s", bitmap->width(), bitmap->height(), LexicalPath::canonicalized_path(path).characters())); | ||||
|     return bitmap; | ||||
|     return load<PBMLoadingContext>(path); | ||||
| } | ||||
| 
 | ||||
| RefPtr<Gfx::Bitmap> load_pbm_from_memory(const u8* data, size_t length) | ||||
| { | ||||
|     auto bitmap = load_pbm_impl(data, length); | ||||
|     auto bitmap = load_impl<PBMLoadingContext>(data, length); | ||||
|     if (bitmap) | ||||
|         bitmap->set_mmap_name(String::format("Gfx::Bitmap [%dx%d] - Decoded PBM: <memory>", bitmap->width(), bitmap->height())); | ||||
|     return bitmap; | ||||
|  | @ -321,7 +141,7 @@ IntSize PBMImageDecoderPlugin::size() | |||
|         return {}; | ||||
| 
 | ||||
|     if (m_context->state < PBMLoadingContext::State::Decoded) { | ||||
|         bool success = decode_pbm(*m_context); | ||||
|         bool success = decode(*m_context); | ||||
|         if (!success) | ||||
|             return {}; | ||||
|     } | ||||
|  | @ -335,7 +155,7 @@ RefPtr<Gfx::Bitmap> PBMImageDecoderPlugin::bitmap() | |||
|         return nullptr; | ||||
| 
 | ||||
|     if (m_context->state < PBMLoadingContext::State::Decoded) { | ||||
|         bool success = decode_pbm(*m_context); | ||||
|         bool success = decode(*m_context); | ||||
|         if (!success) | ||||
|             return nullptr; | ||||
|     } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lenny Maiorani
						Lenny Maiorani