Sfoglia il codice sorgente

LibGfx/JPEG: Support refinement scans

These scans are only present in progressive JPEGs and contains bits to
increase the precision of values acquired in previous scans.
Lucas CHOLLET 2 anni fa
parent
commit
fbad9a70fc
1 ha cambiato i file con 66 aggiunte e 12 eliminazioni
  1. 66 12
      Userland/Libraries/LibGfx/ImageFormats/JPEGLoader.cpp

+ 66 - 12
Userland/Libraries/LibGfx/ImageFormats/JPEGLoader.cpp

@@ -321,6 +321,17 @@ static inline i32* get_component(Macroblock& block, unsigned component)
     }
 }
 
+static ErrorOr<void> refine_coefficient(Scan& scan, i32& coefficient)
+{
+    // G.1.2.3 - Coding model for subsequent scans of successive approximation
+    // See the correction bit from rule b.
+    u8 const bit = TRY(read_huffman_bits(scan.huffman_stream, 1));
+    if (bit == 1)
+        coefficient |= 1 << scan.successive_approximation_low;
+
+    return {};
+}
+
 static ErrorOr<void> add_dc(JPEGLoadingContext& context, Macroblock& macroblock, ScanComponent const& scan_component)
 {
     auto maybe_table = context.dc_tables.get(scan_component.dc_destination_id);
@@ -332,6 +343,14 @@ static ErrorOr<void> add_dc(JPEGLoadingContext& context, Macroblock& macroblock,
     auto& dc_table = maybe_table.value();
     auto& scan = context.current_scan;
 
+    auto* select_component = get_component(macroblock, scan_component.component.index);
+    auto& coefficient = select_component[0];
+
+    if (context.current_scan.successive_approximation_high > 0) {
+        TRY(refine_coefficient(scan, coefficient));
+        return {};
+    }
+
     // For DC coefficients, symbol encodes the length of the coefficient.
     auto dc_length = TRY(get_next_symbol(scan.huffman_stream, dc_table));
     if (dc_length > 11) {
@@ -346,9 +365,9 @@ static ErrorOr<void> add_dc(JPEGLoadingContext& context, Macroblock& macroblock,
     if (dc_length != 0 && dc_diff < (1 << (dc_length - 1)))
         dc_diff -= (1 << dc_length) - 1;
 
-    auto* select_component = get_component(macroblock, scan_component.component.index);
     auto& previous_dc = context.previous_dc_values[scan_component.component.index];
-    select_component[0] = previous_dc += dc_diff;
+    previous_dc += dc_diff;
+    coefficient = previous_dc << scan.successive_approximation_low;
 
     return {};
 }
@@ -375,6 +394,14 @@ static ErrorOr<bool> read_eob(Scan& scan, u32 symbol)
     return false;
 }
 
+static bool is_progressive(StartOfFrame::FrameType frame_type)
+{
+    return frame_type == StartOfFrame::FrameType::Progressive_DCT
+        || frame_type == StartOfFrame::FrameType::Progressive_DCT_Arithmetic
+        || frame_type == StartOfFrame::FrameType::Differential_Progressive_DCT
+        || frame_type == StartOfFrame::FrameType::Differential_Progressive_DCT_Arithmetic;
+}
+
 static ErrorOr<void> add_ac(JPEGLoadingContext& context, Macroblock& macroblock, ScanComponent const& scan_component)
 {
     auto maybe_table = context.ac_tables.get(scan_component.ac_destination_id);
@@ -395,9 +422,12 @@ static ErrorOr<void> add_ac(JPEGLoadingContext& context, Macroblock& macroblock,
 
     u32 to_skip = 0;
     Optional<u8> saved_symbol;
+    Optional<u8> saved_bit_for_rule_a;
     bool in_zrl = false;
 
     for (int j = first_coefficient; j <= scan.spectral_selection_end; ++j) {
+        auto& coefficient = select_component[zigzag_map[j]];
+
         // AC symbols encode 2 pieces of information, the high 4 bits represent
         // number of zeroes to be stuffed before reading the coefficient. Low 4
         // bits represent the magnitude of the coefficient.
@@ -412,9 +442,20 @@ static ErrorOr<void> add_ac(JPEGLoadingContext& context, Macroblock& macroblock,
                     to_skip++;
                     saved_symbol.clear();
                 }
+
+                if (!in_zrl && is_progressive(context.frame.type) && scan.successive_approximation_high != 0) {
+                    // G.1.2.3 - Coding model for subsequent scans of successive approximation
+                    // Bit sign from rule a
+                    saved_bit_for_rule_a = TRY(read_huffman_bits(scan.huffman_stream, 1));
+                }
             }
         }
 
+        if (coefficient != 0) {
+            TRY(refine_coefficient(scan, coefficient));
+            continue;
+        }
+
         if (to_skip > 0) {
             --to_skip;
             if (to_skip == 0)
@@ -425,18 +466,31 @@ static ErrorOr<void> add_ac(JPEGLoadingContext& context, Macroblock& macroblock,
         if (scan.end_of_bands_run_count > 0)
             continue;
 
-        u8 coeff_length = *saved_symbol & 0x0F;
-        if (coeff_length > 10) {
-            dbgln_if(JPEG_DEBUG, "AC coefficient too long: {}!", coeff_length);
-            return Error::from_string_literal("AC coefficient too long");
-        }
+        if (is_progressive(context.frame.type) && scan.successive_approximation_high != 0) {
+            // G.1.2.3 - Coding model for subsequent scans of successive approximation
+            if (auto const low_bits = *saved_symbol & 0x0F; low_bits != 1) {
+                dbgln_if(JPEG_DEBUG, "AC coefficient low bits isn't equal to 1: {}!", low_bits);
+                return Error::from_string_literal("AC coefficient low bits isn't equal to 1");
+            }
 
-        if (coeff_length != 0) {
-            i32 ac_coefficient = TRY(read_huffman_bits(scan.huffman_stream, coeff_length));
-            if (ac_coefficient < (1 << (coeff_length - 1)))
-                ac_coefficient -= (1 << coeff_length) - 1;
+            coefficient = (*saved_bit_for_rule_a == 0 ? -1 : 1) << scan.successive_approximation_low;
+            saved_bit_for_rule_a.clear();
+        } else {
+            // F.1.2.2 - Huffman encoding of AC coefficients
+            u8 const coeff_length = *saved_symbol & 0x0F;
 
-            select_component[zigzag_map[j]] = ac_coefficient;
+            if (coeff_length > 10) {
+                dbgln_if(JPEG_DEBUG, "AC coefficient too long: {}!", coeff_length);
+                return Error::from_string_literal("AC coefficient too long");
+            }
+
+            if (coeff_length != 0) {
+                i32 ac_coefficient = TRY(read_huffman_bits(scan.huffman_stream, coeff_length));
+                if (ac_coefficient < (1 << (coeff_length - 1)))
+                    ac_coefficient -= (1 << coeff_length) - 1;
+
+                coefficient = ac_coefficient * (1 << scan.successive_approximation_low);
+            }
         }
 
         saved_symbol.clear();