mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 12:07:45 +00:00
JPGLoader: Use HashMap instead of Vector for storing components
The JPEG spec allows component IDs to be chosen arbitrarily from the interval [0, 255]. Storing components in a vector corrupts the decoder when component IDs are not in the range 0-3. Normally, encoders don't use IDs outside of that range because JPEG doesn't support more than 4 channels. But since there is a chance that a spec compliant JPEG would have component IDs outside of [0-3], we should consider replacing the vector, which enforces serial component access based on component IDs, with a HashMap<u8, ComponentSpec>.
This commit is contained in:
parent
0b252c31b2
commit
f107c70652
1 changed files with 29 additions and 28 deletions
|
@ -26,6 +26,7 @@
|
||||||
|
|
||||||
#include <AK/Bitmap.h>
|
#include <AK/Bitmap.h>
|
||||||
#include <AK/ByteBuffer.h>
|
#include <AK/ByteBuffer.h>
|
||||||
|
#include <AK/HashMap.h>
|
||||||
#include <AK/LexicalPath.h>
|
#include <AK/LexicalPath.h>
|
||||||
#include <AK/MappedFile.h>
|
#include <AK/MappedFile.h>
|
||||||
#include <AK/MemoryStream.h>
|
#include <AK/MemoryStream.h>
|
||||||
|
@ -35,7 +36,7 @@
|
||||||
#include <LibGfx/JPGLoader.h>
|
#include <LibGfx/JPGLoader.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
//#define JPG_DEBUG
|
#define JPG_DEBUG
|
||||||
|
|
||||||
#define JPG_INVALID 0X0000
|
#define JPG_INVALID 0X0000
|
||||||
|
|
||||||
|
@ -142,7 +143,8 @@ struct MacroblockMeta {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ComponentSpec {
|
struct ComponentSpec {
|
||||||
i8 id { -1 };
|
u8 serial_id { 255 }; // In the interval [0, 3).
|
||||||
|
u8 id { 0 };
|
||||||
u8 hsample_factor { 1 }; // Horizontal sampling factor.
|
u8 hsample_factor { 1 }; // Horizontal sampling factor.
|
||||||
u8 vsample_factor { 1 }; // Vertical sampling factor.
|
u8 vsample_factor { 1 }; // Vertical sampling factor.
|
||||||
u8 ac_destination_id { 0 };
|
u8 ac_destination_id { 0 };
|
||||||
|
@ -205,9 +207,9 @@ struct JPGLoadingContext {
|
||||||
StartOfFrame frame;
|
StartOfFrame frame;
|
||||||
u8 hsample_factor { 0 };
|
u8 hsample_factor { 0 };
|
||||||
u8 vsample_factor { 0 };
|
u8 vsample_factor { 0 };
|
||||||
bool has_zero_based_ids { false };
|
|
||||||
u8 component_count { 0 };
|
u8 component_count { 0 };
|
||||||
ComponentSpec components[3];
|
HashMap<u8, ComponentSpec> components;
|
||||||
|
// ComponentSpec components[3];
|
||||||
RefPtr<Gfx::Bitmap> bitmap;
|
RefPtr<Gfx::Bitmap> bitmap;
|
||||||
u16 dc_reset_interval { 0 };
|
u16 dc_reset_interval { 0 };
|
||||||
Vector<HuffmanTableSpec> dc_tables;
|
Vector<HuffmanTableSpec> dc_tables;
|
||||||
|
@ -294,8 +296,8 @@ static Optional<u8> get_next_symbol(HuffmanStreamState& hstream, const HuffmanTa
|
||||||
*/
|
*/
|
||||||
static bool build_macroblocks(JPGLoadingContext& context, Vector<Macroblock>& macroblocks, u8 hcursor, u8 vcursor)
|
static bool build_macroblocks(JPGLoadingContext& context, Vector<Macroblock>& macroblocks, u8 hcursor, u8 vcursor)
|
||||||
{
|
{
|
||||||
for (u32 cindex = 0; cindex < context.component_count; cindex++) {
|
for (auto it = context.components.begin(); it != context.components.end(); ++it) {
|
||||||
auto& component = context.components[cindex];
|
ComponentSpec& component = it->value;
|
||||||
|
|
||||||
if (component.dc_destination_id >= context.dc_tables.size())
|
if (component.dc_destination_id >= context.dc_tables.size())
|
||||||
return false;
|
return false;
|
||||||
|
@ -334,8 +336,8 @@ static bool build_macroblocks(JPGLoadingContext& context, Vector<Macroblock>& ma
|
||||||
if (dc_length != 0 && dc_diff < (1 << (dc_length - 1)))
|
if (dc_length != 0 && dc_diff < (1 << (dc_length - 1)))
|
||||||
dc_diff -= (1 << dc_length) - 1;
|
dc_diff -= (1 << dc_length) - 1;
|
||||||
|
|
||||||
i32* select_component = component.id == 1 ? block.y : (component.id == 2 ? block.cb : block.cr);
|
i32* select_component = component.serial_id == 0 ? block.y : (component.serial_id == 1 ? block.cb : block.cr);
|
||||||
auto& previous_dc = context.previous_dc_values[cindex];
|
auto& previous_dc = context.previous_dc_values[component.serial_id];
|
||||||
select_component[0] = previous_dc += dc_diff;
|
select_component[0] = previous_dc += dc_diff;
|
||||||
|
|
||||||
// Compute the AC coefficients.
|
// Compute the AC coefficients.
|
||||||
|
@ -398,6 +400,7 @@ static Optional<Vector<Macroblock>> decode_huffman_stream(JPGLoadingContext& con
|
||||||
dbg() << "Image height: " << context.frame.height;
|
dbg() << "Image height: " << context.frame.height;
|
||||||
dbg() << "Macroblocks in a row: " << context.mblock_meta.hpadded_count;
|
dbg() << "Macroblocks in a row: " << context.mblock_meta.hpadded_count;
|
||||||
dbg() << "Macroblocks in a column: " << context.mblock_meta.vpadded_count;
|
dbg() << "Macroblocks in a column: " << context.mblock_meta.vpadded_count;
|
||||||
|
dbg() << "Mblock meta padded total: " << context.mblock_meta.padded_total;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Compute huffman codes for DC and AC tables.
|
// Compute huffman codes for DC and AC tables.
|
||||||
|
@ -546,15 +549,12 @@ static bool read_start_of_scan(InputMemoryStream& stream, JPGLoadingContext& con
|
||||||
stream >> component_id;
|
stream >> component_id;
|
||||||
if (stream.handle_any_error())
|
if (stream.handle_any_error())
|
||||||
return false;
|
return false;
|
||||||
component_id += context.has_zero_based_ids ? 1 : 0;
|
|
||||||
|
|
||||||
if (component_id == context.components[0].id)
|
auto it = context.components.find(component_id);
|
||||||
component = &context.components[0];
|
if (it != context.components.end()) {
|
||||||
else if (component_id == context.components[1].id)
|
component = &it->value;
|
||||||
component = &context.components[1];
|
ASSERT(i == component->serial_id);
|
||||||
else if (component_id == context.components[2].id)
|
} else {
|
||||||
component = &context.components[2];
|
|
||||||
else {
|
|
||||||
#ifdef JPG_DEBUG
|
#ifdef JPG_DEBUG
|
||||||
dbg() << stream.offset() << String::format(": Unsupported component id: %i!", component_id);
|
dbg() << stream.offset() << String::format(": Unsupported component id: %i!", component_id);
|
||||||
#endif
|
#endif
|
||||||
|
@ -824,15 +824,13 @@ static bool read_start_of_frame(InputMemoryStream& stream, JPGLoadingContext& co
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < context.component_count; i++) {
|
for (u8 i = 0; i < context.component_count; i++) {
|
||||||
ComponentSpec& component = context.components[i];
|
ComponentSpec component;
|
||||||
|
component.serial_id = i;
|
||||||
|
|
||||||
stream >> component.id;
|
stream >> component.id;
|
||||||
if (stream.handle_any_error())
|
if (stream.handle_any_error())
|
||||||
return false;
|
return false;
|
||||||
if (i == 0)
|
|
||||||
context.has_zero_based_ids = component.id == 0;
|
|
||||||
component.id += context.has_zero_based_ids ? 1 : 0;
|
|
||||||
|
|
||||||
u8 subsample_factors = 0;
|
u8 subsample_factors = 0;
|
||||||
stream >> subsample_factors;
|
stream >> subsample_factors;
|
||||||
|
@ -841,7 +839,7 @@ static bool read_start_of_frame(InputMemoryStream& stream, JPGLoadingContext& co
|
||||||
component.hsample_factor = subsample_factors >> 4;
|
component.hsample_factor = subsample_factors >> 4;
|
||||||
component.vsample_factor = subsample_factors & 0x0F;
|
component.vsample_factor = subsample_factors & 0x0F;
|
||||||
|
|
||||||
if (component.id == 1) {
|
if (component.serial_id == 0) {
|
||||||
// By convention, downsampling is applied only on chroma components. So we should
|
// By convention, downsampling is applied only on chroma components. So we should
|
||||||
// hope to see the maximum sampling factor in the luma component.
|
// hope to see the maximum sampling factor in the luma component.
|
||||||
if (!validate_luma_and_modify_context(component, context)) {
|
if (!validate_luma_and_modify_context(component, context)) {
|
||||||
|
@ -871,7 +869,10 @@ static bool read_start_of_frame(InputMemoryStream& stream, JPGLoadingContext& co
|
||||||
#endif
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.components.set(component.id, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -946,14 +947,14 @@ static void dequantize(JPGLoadingContext& context, Vector<Macroblock>& macrobloc
|
||||||
{
|
{
|
||||||
for (u32 vcursor = 0; vcursor < context.mblock_meta.vcount; vcursor += context.vsample_factor) {
|
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 (u32 hcursor = 0; hcursor < context.mblock_meta.hcount; hcursor += context.hsample_factor) {
|
||||||
for (u8 cindex = 0; cindex < context.component_count; cindex++) {
|
for (auto it = context.components.begin(); it != context.components.end(); ++it) {
|
||||||
auto& component = context.components[cindex];
|
auto& component = it->value;
|
||||||
const u32* table = component.qtable_id == 0 ? context.luma_table : context.chroma_table;
|
const u32* table = component.qtable_id == 0 ? context.luma_table : context.chroma_table;
|
||||||
for (u32 vfactor_i = 0; vfactor_i < component.vsample_factor; vfactor_i++) {
|
for (u32 vfactor_i = 0; vfactor_i < component.vsample_factor; vfactor_i++) {
|
||||||
for (u32 hfactor_i = 0; hfactor_i < component.hsample_factor; hfactor_i++) {
|
for (u32 hfactor_i = 0; hfactor_i < component.hsample_factor; hfactor_i++) {
|
||||||
u32 mb_index = (vcursor + vfactor_i) * context.mblock_meta.hpadded_count + (hfactor_i + hcursor);
|
u32 mb_index = (vcursor + vfactor_i) * context.mblock_meta.hpadded_count + (hfactor_i + hcursor);
|
||||||
Macroblock& block = macroblocks[mb_index];
|
Macroblock& block = macroblocks[mb_index];
|
||||||
int* block_component = cindex == 0 ? block.y : (cindex == 1 ? block.cb : block.cr);
|
int* block_component = component.serial_id == 0 ? block.y : (component.serial_id == 1 ? block.cb : block.cr);
|
||||||
for (u32 k = 0; k < 64; k++)
|
for (u32 k = 0; k < 64; k++)
|
||||||
block_component[k] *= table[k];
|
block_component[k] *= table[k];
|
||||||
}
|
}
|
||||||
|
@ -982,13 +983,13 @@ static void inverse_dct(const JPGLoadingContext& context, Vector<Macroblock>& ma
|
||||||
|
|
||||||
for (u32 vcursor = 0; vcursor < context.mblock_meta.vcount; vcursor += context.vsample_factor) {
|
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 (u32 hcursor = 0; hcursor < context.mblock_meta.hcount; hcursor += context.hsample_factor) {
|
||||||
for (u8 cindex = 0; cindex < context.component_count; cindex++) {
|
for (auto it = context.components.begin(); it != context.components.end(); ++it) {
|
||||||
auto& component = context.components[cindex];
|
auto& component = it->value;
|
||||||
for (u8 vfactor_i = 0; vfactor_i < component.vsample_factor; vfactor_i++) {
|
for (u8 vfactor_i = 0; vfactor_i < component.vsample_factor; vfactor_i++) {
|
||||||
for (u8 hfactor_i = 0; hfactor_i < component.hsample_factor; hfactor_i++) {
|
for (u8 hfactor_i = 0; hfactor_i < component.hsample_factor; hfactor_i++) {
|
||||||
u32 mb_index = (vcursor + vfactor_i) * context.mblock_meta.hpadded_count + (hfactor_i + hcursor);
|
u32 mb_index = (vcursor + vfactor_i) * context.mblock_meta.hpadded_count + (hfactor_i + hcursor);
|
||||||
Macroblock& block = macroblocks[mb_index];
|
Macroblock& block = macroblocks[mb_index];
|
||||||
i32* block_component = cindex == 0 ? block.y : (cindex == 1 ? block.cb : block.cr);
|
i32* block_component = component.serial_id == 0 ? block.y : (component.serial_id == 1 ? block.cb : block.cr);
|
||||||
for (u32 k = 0; k < 8; ++k) {
|
for (u32 k = 0; k < 8; ++k) {
|
||||||
const float g0 = block_component[0 * 8 + k] * s0;
|
const float g0 = block_component[0 * 8 + k] * s0;
|
||||||
const float g1 = block_component[4 * 8 + k] * s4;
|
const float g1 = block_component[4 * 8 + k] * s4;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue