Browse Source

JPEGLoader: Fix infinite loop on incomplete data

If the image data to decode is incomplete, e.g. a corrupt image missing
its last scanlines the decoder would previously keep looping for ever.
By breaking out of the loop if no more scanlines were produced we can at
least display the partial image up to that point.
0x4261756D 9 months ago
parent
commit
6923008a55
1 changed files with 17 additions and 3 deletions
  1. 17 3
      Userland/Libraries/LibGfx/ImageFormats/JPEGLoader.cpp

+ 17 - 3
Userland/Libraries/LibGfx/ImageFormats/JPEGLoader.cpp

@@ -90,18 +90,29 @@ ErrorOr<void> JPEGLoadingContext::decode()
     }
 
     jpeg_start_decompress(&cinfo);
+    bool could_read_all_scanlines = true;
 
     if (cinfo.out_color_space == JCS_EXT_BGRX) {
         rgb_bitmap = TRY(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, { static_cast<int>(cinfo.output_width), static_cast<int>(cinfo.output_height) }));
         while (cinfo.output_scanline < cinfo.output_height) {
             auto* row_ptr = (u8*)rgb_bitmap->scanline(cinfo.output_scanline);
-            jpeg_read_scanlines(&cinfo, &row_ptr, 1);
+            auto out_size = jpeg_read_scanlines(&cinfo, &row_ptr, 1);
+            if (cinfo.output_scanline < cinfo.output_height && out_size == 0) {
+                dbgln("JPEG Warning: Decoding produced no more scanlines in scanline {}/{}.", cinfo.output_scanline, cinfo.output_height);
+                could_read_all_scanlines = false;
+                break;
+            }
         }
     } else {
         cmyk_bitmap = TRY(CMYKBitmap::create_with_size({ static_cast<int>(cinfo.output_width), static_cast<int>(cinfo.output_height) }));
         while (cinfo.output_scanline < cinfo.output_height) {
             auto* row_ptr = (u8*)cmyk_bitmap->scanline(cinfo.output_scanline);
-            jpeg_read_scanlines(&cinfo, &row_ptr, 1);
+            auto out_size = jpeg_read_scanlines(&cinfo, &row_ptr, 1);
+            if (cinfo.output_scanline < cinfo.output_height && out_size == 0) {
+                dbgln("JPEG Warning: Decoding produced no more scanlines in scanline {}/{}.", cinfo.output_scanline, cinfo.output_height);
+                could_read_all_scanlines = false;
+                break;
+            }
         }
     }
 
@@ -113,7 +124,10 @@ ErrorOr<void> JPEGLoadingContext::decode()
         free(icc_data_ptr);
     }
 
-    jpeg_finish_decompress(&cinfo);
+    if (could_read_all_scanlines)
+        jpeg_finish_decompress(&cinfo);
+    else
+        jpeg_abort_decompress(&cinfo);
     jpeg_destroy_decompress(&cinfo);
 
     if (cmyk_bitmap && !rgb_bitmap)