mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 15:32:46 +00:00 
			
		
		
		
	LibGfx/JPEG: Bring IDCT and YCbCr conversion closer to specification
As mentioned in F.2.1.5 - Inverse DCT (IDCT), the decoder needs to perform a level shift by adding 128. This used to be done in `ycbcr_to_rgb` after the conversion. Now, we do it in `inverse_dct` in order to ensure that the task is done unconditionally. Consequences of this are that we are no longer required to explicitly do it for RGB images and also, the `ycbcr_to_rgb` function is exactly like the specification.
This commit is contained in:
		
							parent
							
								
									62290d57f7
								
							
						
					
					
						commit
						df12e70541
					
				
					 1 changed files with 30 additions and 27 deletions
				
			
		|  | @ -1294,10 +1294,33 @@ static void inverse_dct(JPEGLoadingContext const& context, Vector<Macroblock>& m | |||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // F.2.1.5 - Inverse DCT (IDCT)
 | ||||
| 
 | ||||
|     for (u32 vcursor = 0; vcursor < context.mblock_meta.vcount; vcursor += context.vsample_factor) { | ||||
|         for (u32 hcursor = 0; hcursor < context.mblock_meta.hcount; hcursor += context.hsample_factor) { | ||||
|             for (u8 vfactor_i = 0; vfactor_i < context.vsample_factor; ++vfactor_i) { | ||||
|                 for (u8 hfactor_i = 0; hfactor_i < context.hsample_factor; ++hfactor_i) { | ||||
|                     u32 mb_index = (vcursor + vfactor_i) * context.mblock_meta.hpadded_count + (hcursor + hfactor_i); | ||||
|                     for (u8 i = 0; i < 8; ++i) { | ||||
|                         for (u8 j = 0; j < 8; ++j) { | ||||
|                             macroblocks[mb_index].r[i * 8 + j] = clamp(macroblocks[mb_index].r[i * 8 + j] + 128, 0, 255); | ||||
|                             macroblocks[mb_index].g[i * 8 + j] = clamp(macroblocks[mb_index].g[i * 8 + j] + 128, 0, 255); | ||||
|                             macroblocks[mb_index].b[i * 8 + j] = clamp(macroblocks[mb_index].b[i * 8 + j] + 128, 0, 255); | ||||
|                             macroblocks[mb_index].k[i * 8 + j] = clamp(macroblocks[mb_index].b[i * 8 + j] + 128, 0, 255); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void ycbcr_to_rgb(JPEGLoadingContext const& context, Vector<Macroblock>& macroblocks) | ||||
| { | ||||
|     // Conversion from YCbCr to RGB isn't specified in the first JPEG specification but in the JFIF extension:
 | ||||
|     // See: https://www.itu.int/rec/dologin_pub.asp?lang=f&id=T-REC-T.871-201105-I!!PDF-E&type=items
 | ||||
|     // 7 - Conversion to and from RGB
 | ||||
|     for (u32 vcursor = 0; vcursor < context.mblock_meta.vcount; vcursor += context.vsample_factor) { | ||||
|         for (u32 hcursor = 0; hcursor < context.mblock_meta.hcount; hcursor += context.hsample_factor) { | ||||
|             const u32 chroma_block_index = vcursor * context.mblock_meta.hpadded_count + hcursor; | ||||
|  | @ -1315,32 +1338,12 @@ static void ycbcr_to_rgb(JPEGLoadingContext const& context, Vector<Macroblock>& | |||
|                             const u32 chroma_pxrow = (i / context.vsample_factor) + 4 * vfactor_i; | ||||
|                             const u32 chroma_pxcol = (j / context.hsample_factor) + 4 * hfactor_i; | ||||
|                             const u32 chroma_pixel = chroma_pxrow * 8 + chroma_pxcol; | ||||
|                             int r = y[pixel] + 1.402f * chroma.cr[chroma_pixel] + 128; | ||||
|                             int g = y[pixel] - 0.344f * chroma.cb[chroma_pixel] - 0.714f * chroma.cr[chroma_pixel] + 128; | ||||
|                             int b = y[pixel] + 1.772f * chroma.cb[chroma_pixel] + 128; | ||||
|                             y[pixel] = r < 0 ? 0 : (r > 255 ? 255 : r); | ||||
|                             cb[pixel] = g < 0 ? 0 : (g > 255 ? 255 : g); | ||||
|                             cr[pixel] = b < 0 ? 0 : (b > 255 ? 255 : b); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void signed_rgb_to_unsigned(JPEGLoadingContext const& context, Vector<Macroblock>& macroblocks) | ||||
| { | ||||
|     for (u32 vcursor = 0; vcursor < context.mblock_meta.vcount; vcursor += context.vsample_factor) { | ||||
|         for (u32 hcursor = 0; hcursor < context.mblock_meta.hcount; hcursor += context.hsample_factor) { | ||||
|             for (u8 vfactor_i = 0; vfactor_i < context.vsample_factor; ++vfactor_i) { | ||||
|                 for (u8 hfactor_i = 0; hfactor_i < context.hsample_factor; ++hfactor_i) { | ||||
|                     u32 mb_index = (vcursor + vfactor_i) * context.mblock_meta.hpadded_count + (hcursor + hfactor_i); | ||||
|                     for (u8 i = 0; i < 8; ++i) { | ||||
|                         for (u8 j = 0; j < 8; ++j) { | ||||
|                             macroblocks[mb_index].r[i * 8 + j] = clamp(macroblocks[mb_index].r[i * 8 + j] + 128, 0, 255); | ||||
|                             macroblocks[mb_index].g[i * 8 + j] = clamp(macroblocks[mb_index].g[i * 8 + j] + 128, 0, 255); | ||||
|                             macroblocks[mb_index].b[i * 8 + j] = clamp(macroblocks[mb_index].b[i * 8 + j] + 128, 0, 255); | ||||
|                             int r = y[pixel] + 1.402f * (chroma.cr[chroma_pixel] - 128); | ||||
|                             int g = y[pixel] - 0.3441f * (chroma.cb[chroma_pixel] - 128) - 0.7141f * (chroma.cr[chroma_pixel] - 128); | ||||
|                             int b = y[pixel] + 1.772f * (chroma.cb[chroma_pixel] - 128); | ||||
|                             y[pixel] = clamp(r, 0, 255); | ||||
|                             cb[pixel] = clamp(g, 0, 255); | ||||
|                             cr[pixel] = clamp(b, 0, 255); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | @ -1361,7 +1364,7 @@ static ErrorOr<void> handle_color_transform(JPEGLoadingContext const& context, V | |||
|                 // FIXME: implement CMYK
 | ||||
|                 dbgln("CMYK isn't supported yet"); | ||||
|             } else if (context.components.size() == 3) { | ||||
|                 signed_rgb_to_unsigned(context, macroblocks); | ||||
|                 // Note: components.size() == 3 means that we have an RGB image, so no color transformation is needed.
 | ||||
|             } else { | ||||
|                 return Error::from_string_literal("Wrong number of components for CMYK or RGB, aborting."); | ||||
|             } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lucas CHOLLET
						Lucas CHOLLET