mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 06:12:43 +00:00 
			
		
		
		
	WebP/Lossy: Add code to read macroblock metadata
This commit is contained in:
		
							parent
							
								
									8159709c83
								
							
						
					
					
						commit
						bd5290dd45
					
				
					 1 changed files with 114 additions and 0 deletions
				
			
		|  | @ -386,6 +386,110 @@ ErrorOr<void> decode_VP8_frame_header_coefficient_probabilities(BooleanDecoder& | |||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| // https://datatracker.ietf.org/doc/html/rfc6386#section-8.1 "Tree Coding Implementation"
 | ||||
| ErrorOr<u8> tree_decode(BooleanDecoder& decoder, ReadonlySpan<TreeIndex> tree, ReadonlyBytes probabilities, TreeIndex initial_i = 0) | ||||
| { | ||||
|     TreeIndex i = initial_i; | ||||
|     while (true) { | ||||
|         u8 b = TRY(B(probabilities[i >> 1])); | ||||
|         i = tree[i + b]; | ||||
|         if (i <= 0) | ||||
|             return -i; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Similar to BlockContext in LibVideo/VP9/Context.h
 | ||||
| struct MacroblockMetadata { | ||||
|     // https://datatracker.ietf.org/doc/html/rfc6386#section-10 "Segment-Based Feature Adjustments"
 | ||||
|     // Read only if `update_mb_segmentation_map` is set.
 | ||||
|     int segment_id { 0 }; // 0, 1, 2, or 3. Fits in two bits.
 | ||||
| 
 | ||||
|     // https://datatracker.ietf.org/doc/html/rfc6386#section-11.1 "mb_skip_coeff"
 | ||||
|     bool skip_coefficients { false }; | ||||
| 
 | ||||
|     IntraMetablockMode intra_y_mode; | ||||
|     IntraMetablockMode uv_mode; | ||||
| 
 | ||||
|     IntraBlockMode intra_b_modes[16]; | ||||
| }; | ||||
| 
 | ||||
| ErrorOr<Vector<MacroblockMetadata>> decode_VP8_macroblock_metadata(BooleanDecoder& decoder, FrameHeader const& header, int macroblock_width, int macroblock_height) | ||||
| { | ||||
|     // https://datatracker.ietf.org/doc/html/rfc6386#section-19.3
 | ||||
| 
 | ||||
|     // Corresponds to "macroblock_header()" in section 19.3 of the spec.
 | ||||
| 
 | ||||
|     Vector<MacroblockMetadata> macroblock_metadata; | ||||
| 
 | ||||
|     // Key frames must use intra prediction, that is new macroblocks are predicted from old macroblocks in the same frame.
 | ||||
|     // (Inter prediction on the other hand predicts new macroblocks from the corresponding macroblock in the previous frame.)
 | ||||
| 
 | ||||
|     // https://datatracker.ietf.org/doc/html/rfc6386#section-11.3 "Subblock Mode Contexts"
 | ||||
|     // "For macroblocks on the top row or left edge of the image, some of
 | ||||
|     //  the predictors will be non-existent.  Such predictors are taken
 | ||||
|     //  to have had the value B_DC_PRED, which, perhaps conveniently,
 | ||||
|     //  takes the value 0 in the enumeration above.
 | ||||
|     //  A simple management scheme for these contexts might maintain a row
 | ||||
|     //  of above predictors and four left predictors.  Before decoding the
 | ||||
|     //  frame, the entire row is initialized to B_DC_PRED; before decoding
 | ||||
|     //  each row of macroblocks, the four left predictors are also set to
 | ||||
|     //  B_DC_PRED.  After decoding a macroblock, the bottom four subblock
 | ||||
|     //  modes are copied into the row predictor (at the current position,
 | ||||
|     //  which then advances to be above the next macroblock), and the
 | ||||
|     //  right four subblock modes are copied into the left predictor."
 | ||||
|     Vector<IntraBlockMode> above; | ||||
|     TRY(above.try_resize(macroblock_width * 4)); // One per 4x4 subblock.
 | ||||
| 
 | ||||
|     for (int mb_y = 0; mb_y < macroblock_height; ++mb_y) { | ||||
|         IntraBlockMode left[4] {}; | ||||
| 
 | ||||
|         for (int mb_x = 0; mb_x < macroblock_width; ++mb_x) { | ||||
|             MacroblockMetadata metadata; | ||||
| 
 | ||||
|             if (header.segmentation.update_metablock_segmentation_map) | ||||
|                 metadata.segment_id = TRY(tree_decode(decoder, METABLOCK_SEGMENT_TREE, header.segmentation.metablock_segment_tree_probabilities)); | ||||
| 
 | ||||
|             if (header.enable_skipping_of_metablocks_containing_only_zero_coefficients) | ||||
|                 metadata.skip_coefficients = TRY(B(header.probability_skip_false)); | ||||
| 
 | ||||
|             int intra_y_mode = TRY(tree_decode(decoder, KEYFRAME_YMODE_TREE, KEYFRAME_YMODE_PROBABILITIES)); | ||||
|             metadata.intra_y_mode = (IntraMetablockMode)intra_y_mode; | ||||
| 
 | ||||
|             // "If the Ymode is B_PRED, it is followed by a (tree-coded) mode for each of the 16 Y subblocks."
 | ||||
|             if (intra_y_mode == B_PRED) { | ||||
|                 for (int y = 0; y < 4; ++y) { | ||||
|                     for (int x = 0; x < 4; ++x) { | ||||
|                         // "The outer two dimensions of this array are indexed by the already-
 | ||||
|                         //  coded subblock modes above and to the left of the current block,
 | ||||
|                         //  respectively."
 | ||||
|                         int A = above[mb_x * 4 + x]; | ||||
|                         int L = left[y]; | ||||
| 
 | ||||
|                         auto intra_b_mode = static_cast<IntraBlockMode>(TRY(tree_decode(decoder, BLOCK_MODE_TREE, KEYFRAME_BLOCK_MODE_PROBABILITIES[A][L]))); | ||||
|                         metadata.intra_b_modes[y * 4 + x] = intra_b_mode; | ||||
| 
 | ||||
|                         above[mb_x * 4 + x] = intra_b_mode; | ||||
|                         left[y] = intra_b_mode; | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 VERIFY(intra_y_mode < B_PRED); | ||||
|                 constexpr IntraBlockMode b_mode_from_y_mode[] = { B_DC_PRED, B_VE_PRED, B_HE_PRED, B_TM_PRED }; | ||||
|                 IntraBlockMode intra_b_mode = b_mode_from_y_mode[intra_y_mode]; | ||||
|                 for (int i = 0; i < 4; ++i) { | ||||
|                     above[mb_x * 4 + i] = intra_b_mode; | ||||
|                     left[i] = intra_b_mode; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             metadata.uv_mode = (IntraMetablockMode)TRY(tree_decode(decoder, UV_MODE_TREE, KEYFRAME_UV_MODE_PROBABILITIES)); | ||||
| 
 | ||||
|             TRY(macroblock_metadata.try_append(metadata)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return macroblock_metadata; | ||||
| } | ||||
| } | ||||
| 
 | ||||
| ErrorOr<NonnullRefPtr<Bitmap>> decode_webp_chunk_VP8_contents(VP8Header const& vp8_header, bool include_alpha_channel) | ||||
|  | @ -398,6 +502,16 @@ ErrorOr<NonnullRefPtr<Bitmap>> decode_webp_chunk_VP8_contents(VP8Header const& v | |||
| 
 | ||||
|     auto header = TRY(decode_VP8_frame_header(decoder)); | ||||
| 
 | ||||
|     // https://datatracker.ietf.org/doc/html/rfc6386#section-2 "Format Overview"
 | ||||
|     // "Internally, VP8 decomposes each output frame into an array of
 | ||||
|     //  macroblocks.  A macroblock is a square array of pixels whose Y
 | ||||
|     //  dimensions are 16x16 and whose U and V dimensions are 8x8."
 | ||||
|     int macroblock_width = ceil_div(vp8_header.width, 16); | ||||
|     int macroblock_height = ceil_div(vp8_header.height, 16); | ||||
| 
 | ||||
|     auto macroblock_metadata = TRY(decode_VP8_macroblock_metadata(decoder, header, macroblock_width, macroblock_height)); | ||||
|     (void)macroblock_metadata; | ||||
| 
 | ||||
|     if (header.number_of_dct_partitions > 1) | ||||
|         return Error::from_string_literal("WebPImageDecoderPlugin: decoding lossy webps with more than one dct partition not yet implemented"); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Nico Weber
						Nico Weber