public static void tjei_write_DQT(TJEState state, byte *matrix, byte id) { ushort DQT = (ushort)(tjei_be_word((ushort)(0xffdb))); tjei_write(state, &DQT, (ulong)(sizeof(ushort)), (ulong)(1)); ushort len = (ushort)(tjei_be_word((ushort)(0x0043))); tjei_write(state, &len, (ulong)(sizeof(ushort)), (ulong)(1)); byte precision_and_id = (byte)(id); tjei_write(state, &precision_and_id, (ulong)(sizeof(byte)), (ulong)(1)); tjei_write(state, matrix, (ulong)(64 * sizeof(byte)), (ulong)(1)); }
public static void tjei_write_DHT(TJEState state, byte *matrix_len, byte *matrix_val, int ht_class, byte id) { int num_values = (int)(0); for (int i = (int)(0); (i) < (16); ++i) { num_values += (int)(matrix_len[i]); } ushort DHT = (ushort)(tjei_be_word((ushort)(0xffc4))); ushort len = (ushort)(tjei_be_word((ushort)(2 + 1 + 16 + (ushort)(num_values)))); byte tc_th = (byte)((((byte)(ht_class)) << 4) | id); tjei_write(state, &DHT, (ulong)(sizeof(ushort)), (ulong)(1)); tjei_write(state, &len, (ulong)(sizeof(ushort)), (ulong)(1)); tjei_write(state, &tc_th, (ulong)(sizeof(byte)), (ulong)(1)); tjei_write(state, matrix_len, (ulong)(sizeof(byte)), (ulong)(16)); tjei_write(state, matrix_val, (ulong)(sizeof(byte)), (ulong)(num_values)); }
public static void tjei_write_bits(TJEState state, uint *bitbuffer, uint *location, ushort num_bits, ushort bits) { uint nloc = (uint)(*location + num_bits); *bitbuffer |= ((uint)(bits << (int)(32 - nloc))); *location = (uint)(nloc); while ((*location) >= (8)) { byte c = (byte)((*bitbuffer) >> 24); tjei_write(state, &c, (ulong)(1), (ulong)(1)); if ((c) == (0xff)) { sbyte z = (sbyte)(0); tjei_write(state, &z, (ulong)(1), (ulong)(1)); } *bitbuffer <<= 8; *location -= (uint)(8); } }
public static unsafe void tjei_huff_expand(TJEState state) { state.ht_bits[TJEI_LUMA_DC] = tjei_default_ht_luma_dc_len; state.ht_bits[TJEI_LUMA_AC] = tjei_default_ht_luma_ac_len; state.ht_bits[TJEI_CHROMA_DC] = tjei_default_ht_chroma_dc_len; state.ht_bits[TJEI_CHROMA_AC] = tjei_default_ht_chroma_ac_len; state.ht_vals[TJEI_LUMA_DC] = tjei_default_ht_luma_dc; state.ht_vals[TJEI_LUMA_AC] = tjei_default_ht_luma_ac; state.ht_vals[TJEI_CHROMA_DC] = tjei_default_ht_chroma_dc; state.ht_vals[TJEI_CHROMA_AC] = tjei_default_ht_chroma_ac; var spec_tables_len = stackalloc int[4]; for (var i = 0; i < 4; ++i) { spec_tables_len[i] = 0; for (var k = 0; k < 16; ++k) { spec_tables_len[i] += state.ht_bits[i][k]; } } var huffsize = stackalloc byte[4 * 257]; var huffcode = stackalloc ushort[4 * 256]; for (var i = 0; i < 4; ++i) { tjei_huff_get_code_lengths(huffsize + i * 257, state.ht_bits[i]); tjei_huff_get_codes(huffcode + i * 256, huffsize + i * 257, spec_tables_len[i]); } for (var i = 0; i < 4; ++i) { var count = spec_tables_len[i]; tjei_huff_get_extended((byte *)state.ehuffsize + i * 257, (ushort *)state.ehuffcode + i * 256, state.ht_vals[i], huffsize + i * 257, huffcode + i * 256, count); } }
public static void tjei_write(TJEState state, void *data, ulong num_bytes, ulong num_elements) { ulong to_write = (ulong)(num_bytes * num_elements); ulong capped_count = (ulong) (((to_write) < (1024 - 1 - state.output_buffer_count)) ? (to_write) : (1024 - 1 - state.output_buffer_count)); memcpy((byte *)state.output_buffer + state.output_buffer_count, data, (ulong)(capped_count)); state.output_buffer_count += (ulong)(capped_count); if ((state.output_buffer_count) == (1024 - 1)) { state.write_context.func(state.write_context.context, state.output_buffer, (int)(state.output_buffer_count)); state.output_buffer_count = (ulong)(0); } if ((capped_count) < (to_write)) { tjei_write(state, (byte *)(data) + capped_count, (ulong)(to_write - capped_count), (ulong)(1)); } }
public static int tjei_encode_main(TJEState state, byte *src_data, int width, int height, int src_num_components) { if ((src_num_components != 3) && (src_num_components != 4)) { return((int)(0)); } if (((width) > (0xffff)) || ((height) > (0xffff))) { return((int)(0)); } TJEProcessedQT pqt = new TJEProcessedQT(); float * aan_scales = stackalloc float[8]; aan_scales[0] = (float)(1.0f); aan_scales[1] = (float)(1.387039845f); aan_scales[2] = (float)(1.306562965f); aan_scales[3] = (float)(1.175875602f); aan_scales[4] = (float)(1.0f); aan_scales[5] = (float)(0.785694958f); aan_scales[6] = (float)(0.541196100f); aan_scales[7] = (float)(0.275899379f); for (int y = (int)(0); (y) < (8); y++) { for (int x = (int)(0); (x) < (8); x++) { int i = (int)(y * 8 + x); pqt.luma[y * 8 + x] = (float)(1.0f / (8 * aan_scales[x] * aan_scales[y] * state.qt_luma[tjei_zig_zag[i]])); pqt.chroma[y * 8 + x] = (float)(1.0f / (8 * aan_scales[x] * aan_scales[y] * state.qt_chroma[tjei_zig_zag[i]])); } } { TJEJPEGHeader header = new TJEJPEGHeader(); header.SOI = (ushort)(tjei_be_word((ushort)(0xffd8))); header.APP0 = (ushort)(tjei_be_word((ushort)(0xffe0))); ushort jfif_len = (ushort)(20 - 4); header.jfif_len = (ushort)(tjei_be_word((ushort)(jfif_len))); memcpy(header.jfif_id, (void *)(tjeik_jfif_id), (ulong)(5)); header.version = (ushort)(tjei_be_word((ushort)(0x0102))); header.units = (byte)(0x01); header.x_density = (ushort)(tjei_be_word((ushort)(0x0060))); header.y_density = (ushort)(tjei_be_word((ushort)(0x0060))); header.x_thumb = (byte)(0); header.y_thumb = (byte)(0); tjei_write(state, &header, (ulong)sizeof(TJEJPEGHeader), (ulong)(1)); } { TJEJPEGComment com = new TJEJPEGComment(); ushort com_len = (ushort)(2 + tjeik_com_str.Count); com.com = (ushort)(tjei_be_word((ushort)(0xfffe))); com.com_len = (ushort)(tjei_be_word((ushort)(com_len))); memcpy(com.com_str, (void *)(tjeik_com_str), (ulong)(tjeik_com_str.Count)); tjei_write(state, &com, (ulong)sizeof(TJEJPEGComment), 1); } tjei_write_DQT(state, state.qt_luma, (byte)(0x00)); tjei_write_DQT(state, state.qt_chroma, (byte)(0x01)); { TJEFrameHeader header = new TJEFrameHeader(); header.SOF = (ushort)(tjei_be_word((ushort)(0xffc0))); header.len = (ushort)(tjei_be_word((ushort)(8 + 3 * 3))); header.precision = (byte)(8); header.width = (ushort)(tjei_be_word((ushort)(width))); header.height = (ushort)(tjei_be_word((ushort)(height))); header.num_components = (byte)(3); byte *tables = stackalloc byte[3]; tables[0] = (byte)(0); tables[1] = (byte)(1); tables[2] = (byte)(1); TJEComponentSpec spec = new TJEComponentSpec(); spec.component_id = ((byte)(0 + 1)); spec.sampling_factors = ((byte)(0x11)); spec.qt = (byte)(tables[0]); header.component_spec0 = (TJEComponentSpec)(spec); spec.component_id = ((byte)(1 + 1)); spec.qt = (byte)(tables[1]); header.component_spec1 = (TJEComponentSpec)(spec); spec.component_id = ((byte)(2 + 1)); spec.qt = (byte)(tables[2]); header.component_spec2 = (TJEComponentSpec)(spec); tjei_write(state, &header, (ulong)sizeof(TJEFrameHeader), 1); } tjei_write_DHT(state, state.ht_bits[TJEI_LUMA_DC], state.ht_vals[TJEI_LUMA_DC], (int)(TJEI_DC), (byte)(0)); tjei_write_DHT(state, state.ht_bits[TJEI_LUMA_AC], state.ht_vals[TJEI_LUMA_AC], (int)(TJEI_AC), (byte)(0)); tjei_write_DHT(state, state.ht_bits[TJEI_CHROMA_DC], state.ht_vals[TJEI_CHROMA_DC], (int)(TJEI_DC), (byte)(1)); tjei_write_DHT(state, state.ht_bits[TJEI_CHROMA_AC], state.ht_vals[TJEI_CHROMA_AC], (int)(TJEI_AC), (byte)(1)); { TJEScanHeader header = new TJEScanHeader(); header.SOS = (ushort)(tjei_be_word((ushort)(0xffda))); header.len = (ushort)(tjei_be_word((ushort)(6 + (sizeof(TJEFrameComponentSpec) * 3)))); header.num_components = (byte)(3); byte *tables = stackalloc byte[3]; tables[0] = (byte)(0x00); tables[1] = (byte)(0x11); tables[2] = (byte)(0x11); TJEFrameComponentSpec cs = new TJEFrameComponentSpec(); cs.component_id = ((byte)(0 + 1)); cs.dc_ac = (byte)(tables[0]); header.component_spec0 = (TJEFrameComponentSpec)(cs); cs.component_id = ((byte)(1 + 1)); cs.dc_ac = (byte)(tables[1]); header.component_spec1 = (TJEFrameComponentSpec)(cs); cs.component_id = ((byte)(2 + 1)); cs.dc_ac = (byte)(tables[2]); header.component_spec2 = (TJEFrameComponentSpec)(cs); header.first = (byte)(0); header.last = (byte)(63); header.ah_al = (byte)(0); tjei_write(state, &header, (ulong)sizeof(TJEScanHeader), 1); } float *du_y = stackalloc float[64]; float *du_b = stackalloc float[64]; float *du_r = stackalloc float[64]; int pred_y = (int)(0); int pred_b = (int)(0); int pred_r = (int)(0); uint bitbuffer = (uint)(0); uint location = (uint)(0); for (int y = (int)(0); (y) < (height); y += (int)(8)) { for (int x = (int)(0); (x) < (width); x += (int)(8)) { for (int off_y = (int)(0); (off_y) < (8); ++off_y) { for (int off_x = (int)(0); (off_x) < (8); ++off_x) { int block_index = (int)(off_y * 8 + off_x); int src_index = (int)((((y + off_y) * width) + (x + off_x)) * src_num_components); int col = (int)(x + off_x); int row = (int)(y + off_y); if ((row) >= (height)) { src_index -= (int)((width * (row - height + 1)) * src_num_components); } if ((col) >= (width)) { src_index -= (int)((col - width + 1) * src_num_components); } byte r = (byte)(src_data[src_index + 0]); byte g = (byte)(src_data[src_index + 1]); byte b = (byte)(src_data[src_index + 2]); float luma = (float)(0.299f * r + 0.587f * g + 0.114f * b - 128); float cb = (float)(-0.1687f * r - 0.3313f * g + 0.5f * b); float cr = (float)(0.5f * r - 0.4187f * g - 0.0813f * b); du_y[block_index] = (float)(luma); du_b[block_index] = (float)(cb); du_r[block_index] = (float)(cr); } } tjei_encode_and_write_MCU(state, du_y, pqt.luma, ((byte *)state.ehuffsize + TJEI_LUMA_DC * 257), ((ushort *)state.ehuffcode + TJEI_LUMA_DC * 256), ((byte *)state.ehuffsize + TJEI_LUMA_AC * 257), ((ushort *)state.ehuffcode + TJEI_LUMA_AC * 256), &pred_y, &bitbuffer, &location); tjei_encode_and_write_MCU(state, du_b, pqt.chroma, ((byte *)state.ehuffsize + TJEI_CHROMA_DC * 257), ((ushort *)state.ehuffcode + TJEI_CHROMA_DC * 256), ((byte *)state.ehuffsize + TJEI_CHROMA_AC * 257), ((ushort *)state.ehuffcode + TJEI_CHROMA_AC * 256), &pred_b, &bitbuffer, &location); tjei_encode_and_write_MCU(state, du_r, pqt.chroma, ((byte *)state.ehuffsize + TJEI_CHROMA_DC * 257), ((ushort *)state.ehuffcode + TJEI_CHROMA_DC * 256), ((byte *)state.ehuffsize + TJEI_CHROMA_AC * 257), ((ushort *)state.ehuffcode + TJEI_CHROMA_AC * 256), &pred_r, &bitbuffer, &location); } } { if (((location) > (0)) && ((location) < (8))) { tjei_write_bits(state, &bitbuffer, &location, (ushort)(8 - location), (ushort)(0)); } } ushort EOI = (ushort)(tjei_be_word((ushort)(0xffd9))); tjei_write(state, &EOI, (ulong)(sizeof(ushort)), (ulong)(1)); if ((state.output_buffer_count) != 0) { state.write_context.func(state.write_context.context, state.output_buffer, (int)(state.output_buffer_count)); state.output_buffer_count = (ulong)(0); } return((int)(1)); }
public static void tjei_encode_and_write_MCU(TJEState state, float *mcu, float *qt, byte *huff_dc_len, ushort *huff_dc_code, byte *huff_ac_len, ushort *huff_ac_code, int *pred, uint *bitbuffer, uint *location) { int * du = stackalloc int[64]; float *dct_mcu = stackalloc float[64]; memcpy(dct_mcu, mcu, (ulong)(64 * sizeof(float))); tjei_fdct(dct_mcu); for (int i = (int)(0); (i) < (64); ++i) { state.fval = (float)(dct_mcu[i]); state.fval *= (float)(qt[i]); state.fval = (float)(floorf((float)(state.fval + 1024 + 0.5f))); state.fval -= (float)(1024); int val = (int)(state.fval); du[tjei_zig_zag[i]] = (int)(val); } ushort *vli = stackalloc ushort[2]; int diff = (int)(du[0] - *pred); *pred = (int)(du[0]); if (diff != 0) { tjei_calculate_variable_length_int((int)(diff), vli); tjei_write_bits(state, bitbuffer, location, (ushort)(huff_dc_len[vli[1]]), (ushort)(huff_dc_code[vli[1]])); tjei_write_bits(state, bitbuffer, location, (ushort)(vli[1]), (ushort)(vli[0])); } else { tjei_write_bits(state, bitbuffer, location, (ushort)(huff_dc_len[0]), (ushort)(huff_dc_code[0])); } int last_non_zero_i = (int)(0); for (int i = (int)(63); (i) > (0); --i) { if (du[i] != 0) { last_non_zero_i = (int)(i); break; } } for (int i = (int)(1); i <= last_non_zero_i; ++i) { int zero_count = (int)(0); while ((du[i]) == (0)) { ++zero_count; ++i; if ((zero_count) == (16)) { tjei_write_bits(state, bitbuffer, location, (ushort)(huff_ac_len[0xf0]), (ushort)(huff_ac_code[0xf0])); zero_count = (int)(0); } } tjei_calculate_variable_length_int((int)(du[i]), vli); ushort sym1 = (ushort)((ushort)(((ushort)(zero_count) << 4) | vli[1])); tjei_write_bits(state, bitbuffer, location, (ushort)(huff_ac_len[sym1]), (ushort)(huff_ac_code[sym1])); tjei_write_bits(state, bitbuffer, location, (ushort)(vli[1]), (ushort)(vli[0])); } if (last_non_zero_i != 63) { tjei_write_bits(state, bitbuffer, location, (ushort)(huff_ac_len[0]), (ushort)(huff_ac_code[0])); } return; }
public static unsafe int tje_encode_with_func(WriteCallback2 func, void *context, int quality, int width, int height, int num_components, byte *src_data) { if (quality < 1 || quality > 3) { throw new Exception("[ERROR] -- Valid 'quality' values are 1 (lowest), 2, or 3 (highest)"); } var state = new TJEState(); byte qt_factor = 1; switch (quality) { case 3: for (int i = 0; i < 64; ++i) { state.qt_luma[i] = 1; state.qt_chroma[i] = 1; } break; case 1: case 2: if (quality == 2) { qt_factor = 10; } // don't break. fall through. for (int i = 0; i < 64; ++i) { state.qt_luma[i] = (byte)(tjei_default_qt_luma_from_spec[i] / qt_factor); if (state.qt_luma[i] == 0) { state.qt_luma[i] = 1; } state.qt_chroma[i] = (byte)(tjei_default_qt_chroma_from_paper[i] / qt_factor); if (state.qt_chroma[i] == 0) { state.qt_chroma[i] = 1; } } break; default: throw new Exception("invalid code path"); } var wc = new TJEWriteContext { context = context, func = func }; state.write_context = wc; tjei_huff_expand(state); var result = tjei_encode_main(state, src_data, width, height, num_components); return(result); }