mirror of
https://github.com/outbackdingo/UltraGrid.git
synced 2026-03-20 20:40:15 +00:00
Conflicts: libgpujpeg/gpujpeg_huffman_cpu_decoder.c libgpujpeg/gpujpeg_huffman_cpu_encoder.c libgpujpeg/gpujpeg_preprocessor.cu
844 lines
31 KiB
C
844 lines
31 KiB
C
/**
|
|
* Copyright (c) 2011, CESNET z.s.p.o
|
|
* Copyright (c) 2011, Silicon Genome, LLC.
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "gpujpeg_reader.h"
|
|
#include "gpujpeg_decoder.h"
|
|
#include "gpujpeg_util.h"
|
|
|
|
/** Documented at declaration */
|
|
struct gpujpeg_reader*
|
|
gpujpeg_reader_create()
|
|
{
|
|
struct gpujpeg_reader* reader = malloc(sizeof(struct gpujpeg_reader));
|
|
if ( reader == NULL )
|
|
return NULL;
|
|
reader->comp_count = 0;
|
|
reader->scan_count = 0;
|
|
reader->segment_count = 0;
|
|
reader->segment_info_count = 0;
|
|
reader->segment_info_size = 0;
|
|
|
|
return reader;
|
|
}
|
|
|
|
/** Documented at declaration */
|
|
int
|
|
gpujpeg_reader_destroy(struct gpujpeg_reader* reader)
|
|
{
|
|
assert(reader != NULL);
|
|
free(reader);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Read byte from image data
|
|
*
|
|
* @param image
|
|
* @return byte
|
|
*/
|
|
#define gpujpeg_reader_read_byte(image) \
|
|
(uint8_t)(*(image)++)
|
|
|
|
/**
|
|
* Read two-bytes from image data
|
|
*
|
|
* @param image
|
|
* @return 2 bytes
|
|
*/
|
|
#define gpujpeg_reader_read_2byte(image) \
|
|
(uint16_t)(((*(image)) << 8) + (*((image) + 1))); \
|
|
image += 2;
|
|
|
|
/**
|
|
* Read marker from image data
|
|
*
|
|
* @param image
|
|
* @return marker code or -1 if failed
|
|
*/
|
|
int
|
|
gpujpeg_reader_read_marker(uint8_t** image)
|
|
{
|
|
uint8_t byte = gpujpeg_reader_read_byte(*image);
|
|
if( byte != 0xFF ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] Failed to read marker from JPEG data (0xFF was expected but 0x%X was presented)\n", byte);
|
|
return -1;
|
|
}
|
|
int marker = gpujpeg_reader_read_byte(*image);
|
|
return marker;
|
|
}
|
|
|
|
/**
|
|
* Skip marker content (read length and that much bytes - 2)
|
|
*
|
|
* @param image
|
|
* @return void
|
|
*/
|
|
void
|
|
gpujpeg_reader_skip_marker_content(uint8_t** image)
|
|
{
|
|
int length = (int)gpujpeg_reader_read_2byte(*image);
|
|
|
|
*image += length - 2;
|
|
}
|
|
|
|
/**
|
|
* Read application ifno block from image
|
|
*
|
|
* @param image
|
|
* @return 0 if succeeds, otherwise nonzero
|
|
*/
|
|
int
|
|
gpujpeg_reader_read_app0(uint8_t** image)
|
|
{
|
|
int length = (int)gpujpeg_reader_read_2byte(*image);
|
|
if ( length != 16 ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] APP0 marker length should be 16 but %d was presented!\n", length);
|
|
return -1;
|
|
}
|
|
|
|
char jfif[4];
|
|
jfif[0] = gpujpeg_reader_read_byte(*image);
|
|
jfif[1] = gpujpeg_reader_read_byte(*image);
|
|
jfif[2] = gpujpeg_reader_read_byte(*image);
|
|
jfif[3] = gpujpeg_reader_read_byte(*image);
|
|
jfif[4] = gpujpeg_reader_read_byte(*image);
|
|
if ( strcmp(jfif, "JFIF") != 0 ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] APP0 marker identifier should be 'JFIF' but '%s' was presented!\n", jfif);
|
|
return -1;
|
|
}
|
|
|
|
int version_major = gpujpeg_reader_read_byte(*image);
|
|
int version_minor = gpujpeg_reader_read_byte(*image);
|
|
if ( version_major != 1 || version_minor != 1 ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] APP0 marker version should be 1.1 but %d.%d was presented!\n", version_major, version_minor);
|
|
return -1;
|
|
}
|
|
|
|
int pixel_units = gpujpeg_reader_read_byte(*image);
|
|
int pixel_xdpu = gpujpeg_reader_read_2byte(*image);
|
|
int pixel_ydpu = gpujpeg_reader_read_2byte(*image);
|
|
int thumbnail_width = gpujpeg_reader_read_byte(*image);
|
|
int thumbnail_height = gpujpeg_reader_read_byte(*image);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Read quantization table definition block from image
|
|
*
|
|
* @param decoder
|
|
* @param image
|
|
* @return 0 if succeeds, otherwise nonzero
|
|
*/
|
|
int
|
|
gpujpeg_reader_read_dqt(struct gpujpeg_decoder* decoder, uint8_t** image)
|
|
{
|
|
int length = (int)gpujpeg_reader_read_2byte(*image);
|
|
length -= 2;
|
|
|
|
if ( length != 65 ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] DQT marker length should be 65 but %d was presented!\n", length);
|
|
return -1;
|
|
}
|
|
|
|
int index = gpujpeg_reader_read_byte(*image);
|
|
struct gpujpeg_table_quantization* table;
|
|
if( index == 0 ) {
|
|
table = &decoder->table_quantization[GPUJPEG_COMPONENT_LUMINANCE];
|
|
} else if ( index == 1 ) {
|
|
table = &decoder->table_quantization[GPUJPEG_COMPONENT_CHROMINANCE];
|
|
} else {
|
|
fprintf(stderr, "[GPUJPEG] [Error] DQT marker index should be 0 or 1 but %d was presented!\n", index);
|
|
return -1;
|
|
}
|
|
|
|
for ( int i = 0; i < 64; i++ ) {
|
|
table->table_raw[i] = gpujpeg_reader_read_byte(*image);
|
|
}
|
|
|
|
// Prepare quantization table for read raw table
|
|
gpujpeg_table_quantization_decoder_compute(table);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Read start of frame block from image
|
|
*
|
|
* @param image
|
|
* @return 0 if succeeds, otherwise nonzero
|
|
*/
|
|
int
|
|
gpujpeg_reader_read_sof0(struct gpujpeg_decoder* decoder, uint8_t** image)
|
|
{
|
|
int length = (int)gpujpeg_reader_read_2byte(*image);
|
|
if ( length < 6 ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] SOF0 marker length should be greater than 6 but %d was presented!\n", length);
|
|
return -1;
|
|
}
|
|
length -= 2;
|
|
|
|
int precision = (int)gpujpeg_reader_read_byte(*image);
|
|
if ( precision != 8 ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] SOF0 marker precision should be 8 but %d was presented!\n", precision);
|
|
return -1;
|
|
}
|
|
|
|
decoder->reader->param_image.height = (int)gpujpeg_reader_read_2byte(*image);
|
|
decoder->reader->param_image.width = (int)gpujpeg_reader_read_2byte(*image);
|
|
decoder->reader->param_image.comp_count = (int)gpujpeg_reader_read_byte(*image);
|
|
length -= 6;
|
|
|
|
for ( int comp = 0; comp < decoder->reader->param_image.comp_count; comp++ ) {
|
|
int index = (int)gpujpeg_reader_read_byte(*image);
|
|
if ( index != (comp + 1) ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] SOF0 marker component %d id should be %d but %d was presented!\n", comp, comp + 1, index);
|
|
return -1;
|
|
}
|
|
|
|
int sampling = (int)gpujpeg_reader_read_byte(*image);
|
|
decoder->reader->param.sampling_factor[comp].horizontal = (sampling >> 4) & 15;
|
|
decoder->reader->param.sampling_factor[comp].vertical = (sampling) & 15;
|
|
|
|
int table_index = (int)gpujpeg_reader_read_byte(*image);
|
|
if ( comp == 0 && table_index != 0 ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] SOF0 marker component Y should have quantization table index 0 but %d was presented!\n", table_index);
|
|
return -1;
|
|
}
|
|
if ( (comp == 1 || comp == 2) && table_index != 1 ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] SOF0 marker component Cb or Cr should have quantization table index 1 but %d was presented!\n", table_index);
|
|
return -1;
|
|
}
|
|
length -= 3;
|
|
}
|
|
|
|
// Check length
|
|
if ( length > 0 ) {
|
|
fprintf(stderr, "[GPUJPEG] [Warning] SOF0 marker contains %d more bytes than needed!\n", length);
|
|
*image += length;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Read huffman table definition block from image
|
|
*
|
|
* @param decoder
|
|
* @param image
|
|
* @return 0 if succeeds, otherwise nonzero
|
|
*/
|
|
int
|
|
gpujpeg_reader_read_dht(struct gpujpeg_decoder* decoder, uint8_t** image)
|
|
{
|
|
int length = (int)gpujpeg_reader_read_2byte(*image);
|
|
length -= 2;
|
|
|
|
int index = gpujpeg_reader_read_byte(*image);
|
|
struct gpujpeg_table_huffman_decoder* table = NULL;
|
|
struct gpujpeg_table_huffman_decoder* d_table = NULL;
|
|
switch(index) {
|
|
case 0:
|
|
table = &decoder->table_huffman[GPUJPEG_COMPONENT_LUMINANCE][GPUJPEG_HUFFMAN_DC];
|
|
d_table = decoder->d_table_huffman[GPUJPEG_COMPONENT_LUMINANCE][GPUJPEG_HUFFMAN_DC];
|
|
break;
|
|
case 16:
|
|
table = &decoder->table_huffman[GPUJPEG_COMPONENT_LUMINANCE][GPUJPEG_HUFFMAN_AC];
|
|
d_table = decoder->d_table_huffman[GPUJPEG_COMPONENT_LUMINANCE][GPUJPEG_HUFFMAN_AC];
|
|
break;
|
|
case 1:
|
|
table = &decoder->table_huffman[GPUJPEG_COMPONENT_CHROMINANCE][GPUJPEG_HUFFMAN_DC];
|
|
d_table = decoder->d_table_huffman[GPUJPEG_COMPONENT_CHROMINANCE][GPUJPEG_HUFFMAN_DC];
|
|
break;
|
|
case 17:
|
|
table = &decoder->table_huffman[GPUJPEG_COMPONENT_CHROMINANCE][GPUJPEG_HUFFMAN_AC];
|
|
d_table = decoder->d_table_huffman[GPUJPEG_COMPONENT_CHROMINANCE][GPUJPEG_HUFFMAN_AC];
|
|
break;
|
|
default:
|
|
fprintf(stderr, "[GPUJPEG] [Error] DHT marker index should be 0, 1, 16 or 17 but %d was presented!\n", index);
|
|
return -1;
|
|
}
|
|
length -= 1;
|
|
|
|
// Read in bits[]
|
|
table->bits[0] = 0;
|
|
int count = 0;
|
|
for ( int i = 1; i <= 16; i++ ) {
|
|
table->bits[i] = gpujpeg_reader_read_byte(*image);
|
|
count += table->bits[i];
|
|
if ( length > 0 ) {
|
|
length--;
|
|
} else {
|
|
fprintf(stderr, "[GPUJPEG] [Error] DHT marker unexpected end when reading bit counts!\n", index);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// Read in huffval
|
|
for ( int i = 0; i < count; i++ ){
|
|
table->huffval[i] = gpujpeg_reader_read_byte(*image);
|
|
if ( length > 0 ) {
|
|
length--;
|
|
} else {
|
|
fprintf(stderr, "[GPUJPEG] [Error] DHT marker unexpected end when reading huffman values!\n", index);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// Check length
|
|
if ( length > 0 ) {
|
|
fprintf(stderr, "[GPUJPEG] [Warning] DHT marker contains %d more bytes than needed!\n", length);
|
|
*image += length;
|
|
}
|
|
|
|
// Compute huffman table for read values
|
|
gpujpeg_table_huffman_decoder_compute(table, d_table);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Read restart interval block from image
|
|
*
|
|
* @param decoder
|
|
* @param image
|
|
* @return 0 if succeeds, otherwise nonzero
|
|
*/
|
|
int
|
|
gpujpeg_reader_read_dri(struct gpujpeg_decoder* decoder, uint8_t** image)
|
|
{
|
|
int length = (int)gpujpeg_reader_read_2byte(*image);
|
|
if ( length != 4 ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] DRI marker length should be 4 but %d was presented!\n", length);
|
|
return -1;
|
|
}
|
|
|
|
int restart_interval = gpujpeg_reader_read_2byte(*image);
|
|
if ( restart_interval == decoder->reader->param.restart_interval )
|
|
return 0;
|
|
|
|
if ( decoder->reader->param.restart_interval != 0 ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] DRI marker can't redefine restart interval!");
|
|
fprintf(stderr, "This may be caused when more DRI markers are presented which is not supported!\n");
|
|
return -1;
|
|
}
|
|
|
|
decoder->reader->param.restart_interval = restart_interval;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Read segment info for following scan
|
|
*
|
|
* @param decoder
|
|
* @param image
|
|
* @return 0 if succeeds, otherwise nonzero
|
|
*/
|
|
int
|
|
gpujpeg_reader_read_segment_info(struct gpujpeg_decoder* decoder, uint8_t** image)
|
|
{
|
|
int length = (int)gpujpeg_reader_read_2byte(*image);
|
|
int scan_index = (int)gpujpeg_reader_read_byte(*image);
|
|
if ( length <= 3 ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] %s marker (segment info) length should be greater than 3 but %d was presented!\n",
|
|
gpujpeg_marker_name(GPUJPEG_MARKER_SEGMENT_INFO), length);
|
|
return -1;
|
|
}
|
|
if ( scan_index != decoder->reader->scan_count ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] %s marker (segment info) scan index should be %d but %d was presented!\n",
|
|
gpujpeg_marker_name(GPUJPEG_MARKER_SEGMENT_INFO), decoder->reader->scan_count, scan_index);
|
|
return -1;
|
|
}
|
|
|
|
int data_size = length - 3;
|
|
decoder->reader->segment_info[decoder->reader->segment_info_count] = *image;
|
|
decoder->reader->segment_info_count++;
|
|
decoder->reader->segment_info_size += data_size;
|
|
|
|
*image += data_size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Read scan content by parsing byte-by-byte
|
|
*
|
|
* @param decoder
|
|
* @param image
|
|
* @param image_end
|
|
* @param scan
|
|
* @param scan_index
|
|
* @return 0 if succeeds, otherwise nonzero
|
|
*/
|
|
int
|
|
gpujpeg_reader_read_scan_content_by_parsing(struct gpujpeg_decoder* decoder, uint8_t** image, uint8_t* image_end,
|
|
struct gpujpeg_reader_scan* scan, int scan_index)
|
|
{
|
|
// Get first segment in scan
|
|
struct gpujpeg_segment* segment = &decoder->coder.segment[scan->segment_index];
|
|
segment->scan_index = scan_index;
|
|
segment->scan_segment_index = scan->segment_count;
|
|
segment->data_compressed_index = decoder->reader->data_compressed_size;
|
|
scan->segment_count++;
|
|
|
|
// Read scan data
|
|
int result = -1;
|
|
uint8_t byte = 0;
|
|
uint8_t byte_previous = 0;
|
|
uint8_t previous_marker = GPUJPEG_MARKER_RST0 - 1;
|
|
do {
|
|
byte_previous = byte;
|
|
byte = gpujpeg_reader_read_byte(*image);
|
|
decoder->coder.data_compressed[decoder->reader->data_compressed_size] = byte;
|
|
decoder->reader->data_compressed_size++;
|
|
|
|
// Check markers
|
|
if ( byte_previous == 0xFF ) {
|
|
// Check zero byte
|
|
if ( byte == 0 ) {
|
|
continue;
|
|
}
|
|
// Check restart marker
|
|
else if ( byte >= GPUJPEG_MARKER_RST0 && byte <= GPUJPEG_MARKER_RST7 ) {
|
|
// Check expected marker
|
|
uint8_t expected_marker = (previous_marker < GPUJPEG_MARKER_RST7) ? (previous_marker + 1) : GPUJPEG_MARKER_RST0;
|
|
if ( expected_marker != byte ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] Expected marker 0x%X but 0x%X was presented!\n", expected_marker, byte);
|
|
|
|
// Skip bytes to expected marker
|
|
int found_expected_marker = 0;
|
|
int skip_count = 0;
|
|
byte_previous = byte;
|
|
while ( *image < image_end ) {
|
|
skip_count++;
|
|
byte = gpujpeg_reader_read_byte(*image);
|
|
if ( byte_previous == 0xFF ) {
|
|
// Expected marker was found so notify about it
|
|
if ( byte == expected_marker ) {
|
|
fprintf(stderr, "[GPUJPEG] [Recovery] Skipping %d bytes of data until marker 0x%X was found!\n", skip_count, expected_marker, byte);
|
|
found_expected_marker = 1;
|
|
break;
|
|
} else if ( byte == GPUJPEG_MARKER_EOI || byte == GPUJPEG_MARKER_SOS ) {
|
|
// Go back last marker (will be read again by main read cycle)
|
|
*image -= 2;
|
|
break;
|
|
}
|
|
}
|
|
byte_previous = byte;
|
|
}
|
|
|
|
// If expected marker was not found to end of stream
|
|
if ( found_expected_marker == 0 ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] No marker 0x%X was found until end of current scan!\n", expected_marker);
|
|
continue;
|
|
}
|
|
}
|
|
// Set previous marker
|
|
previous_marker = byte;
|
|
|
|
decoder->reader->data_compressed_size -= 2;
|
|
|
|
// Set segment byte count
|
|
segment->data_compressed_size = decoder->reader->data_compressed_size - segment->data_compressed_index;
|
|
|
|
// Start new segment in scan
|
|
segment = &decoder->coder.segment[scan->segment_index + scan->segment_count];
|
|
segment->scan_index = scan_index;
|
|
segment->scan_segment_index = scan->segment_count;
|
|
segment->data_compressed_index = decoder->reader->data_compressed_size;
|
|
scan->segment_count++;
|
|
}
|
|
// Check scan end
|
|
else if ( byte == GPUJPEG_MARKER_EOI || byte == GPUJPEG_MARKER_SOS || (byte >= GPUJPEG_MARKER_APP0 && byte <= GPUJPEG_MARKER_APP15) ) {
|
|
*image -= 2;
|
|
decoder->reader->data_compressed_size -= 2;
|
|
|
|
// Set segment byte count
|
|
segment->data_compressed_size = decoder->reader->data_compressed_size - segment->data_compressed_index;
|
|
|
|
// Add scan segment count to decoder segment count
|
|
decoder->reader->segment_count += scan->segment_count;
|
|
|
|
// Successfully read end of scan, so the result is OK
|
|
result = 0;
|
|
break;
|
|
} else {
|
|
fprintf(stderr, "[GPUJPEG] [Error] JPEG scan contains unexpected marker 0x%X!\n", byte);
|
|
return -1;
|
|
}
|
|
}
|
|
} while( *image < image_end );
|
|
|
|
if ( result == -1) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] JPEG data unexpected ended while reading SOS marker!\n");
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Read scan content by segment info contained inside the JPEG stream
|
|
*
|
|
* @param decoder
|
|
* @param image
|
|
* @param image_end
|
|
* @param scan
|
|
* @param scan_index
|
|
* @return 0 if succeeds, otherwise nonzero
|
|
*/
|
|
int
|
|
gpujpeg_reader_read_scan_content_by_segment_info(struct gpujpeg_decoder* decoder, uint8_t** image, uint8_t* image_end,
|
|
struct gpujpeg_reader_scan* scan, int scan_index)
|
|
{
|
|
// Calculate segment count
|
|
int segment_count = decoder->reader->segment_info_size / 4 - 1;
|
|
|
|
// Read first record from segment info, which means beginning of the first segment
|
|
int scan_start = (decoder->reader->segment_info[0][0] << 24)
|
|
+ (decoder->reader->segment_info[0][1] << 16)
|
|
+ (decoder->reader->segment_info[0][2] << 8)
|
|
+ (decoder->reader->segment_info[0][3] << 0);
|
|
|
|
// Read all segments from segment info
|
|
for ( int segment_index = 0; segment_index < segment_count; segment_index++ ) {
|
|
// Determine header index
|
|
int header_index = ((segment_index + 1) * 4) / GPUJPEG_MAX_HEADER_SIZE;
|
|
|
|
// Determine header data index
|
|
int header_data_index = ((segment_index + 1) * 4) % GPUJPEG_MAX_HEADER_SIZE;
|
|
|
|
// Determine segment ending in the scan
|
|
int scan_end = (decoder->reader->segment_info[header_index][header_data_index + 0] << 24)
|
|
+ (decoder->reader->segment_info[header_index][header_data_index + 1] << 16)
|
|
+ (decoder->reader->segment_info[header_index][header_data_index + 2] << 8)
|
|
+ (decoder->reader->segment_info[header_index][header_data_index + 3] << 0);
|
|
|
|
// Setup segment
|
|
struct gpujpeg_segment* segment = &decoder->coder.segment[scan->segment_index + segment_index];
|
|
segment->scan_index = scan_index;
|
|
segment->scan_segment_index = segment_index;
|
|
segment->data_compressed_index = decoder->reader->data_compressed_size + scan_start;
|
|
segment->data_compressed_size = decoder->reader->data_compressed_size + scan_end;
|
|
|
|
// If segment is not last it contains restart marker at the end so remove it
|
|
if ( (segment_index + 1) < segment_count ) {
|
|
segment->data_compressed_size -= 2;
|
|
}
|
|
|
|
// Move info for next segment
|
|
scan_start = scan_end;
|
|
}
|
|
|
|
// Set segment count in the scan
|
|
scan->segment_count = segment_count;
|
|
|
|
// Increase number of segment count in reader
|
|
decoder->reader->segment_count += scan->segment_count;
|
|
|
|
// Copy scan data to buffer
|
|
memcpy(
|
|
&decoder->coder.data_compressed[decoder->reader->data_compressed_size],
|
|
*image,
|
|
scan_start
|
|
);
|
|
*image += scan_start;
|
|
decoder->reader->data_compressed_size += scan_start;
|
|
|
|
// Reset segment info, for next scan it has to be loaded again from other header
|
|
decoder->reader->segment_info_count = 0;
|
|
decoder->reader->segment_info_size = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Read start of scan block from image
|
|
*
|
|
* @param decoder
|
|
* @param image
|
|
* @param image_end
|
|
* @return 0 if succeeds, otherwise nonzero
|
|
*/
|
|
int
|
|
gpujpeg_reader_read_sos(struct gpujpeg_decoder* decoder, uint8_t** image, uint8_t* image_end)
|
|
{
|
|
int length = (int)gpujpeg_reader_read_2byte(*image);
|
|
length -= 2;
|
|
|
|
int comp_count = (int)gpujpeg_reader_read_byte(*image);
|
|
// Not interleaved mode
|
|
if ( comp_count == 1 ) {
|
|
decoder->reader->param.interleaved = 0;
|
|
}
|
|
// Interleaved mode
|
|
else if ( comp_count == decoder->reader->param_image.comp_count ) {
|
|
if ( decoder->reader->comp_count != 0 ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] SOS marker component count %d is not supported for multiple scans!\n", comp_count);
|
|
return -1;
|
|
}
|
|
decoder->reader->param.interleaved = 1;
|
|
}
|
|
// Unknown mode
|
|
else {
|
|
fprintf(stderr, "[GPUJPEG] [Error] SOS marker component count %d is not supported (should be 1 or equals to total component count)!\n", comp_count);
|
|
return -1;
|
|
}
|
|
|
|
// We must init decoder before data is loaded into it
|
|
if ( decoder->reader->comp_count == 0 ) {
|
|
// Init decoder
|
|
gpujpeg_decoder_init(decoder, &decoder->reader->param, &decoder->reader->param_image);
|
|
}
|
|
|
|
// Check maximum component count
|
|
decoder->reader->comp_count += comp_count;
|
|
if ( decoder->reader->comp_count > decoder->reader->param_image.comp_count ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] SOS marker component count for all scans %d exceeds maximum component count %d!\n",
|
|
decoder->reader->comp_count, decoder->reader->param_image.comp_count);
|
|
}
|
|
|
|
// Collect the component-spec parameters
|
|
for ( int comp = 0; comp < comp_count; comp++ )
|
|
{
|
|
int index = (int)gpujpeg_reader_read_byte(*image);
|
|
int table = (int)gpujpeg_reader_read_byte(*image);
|
|
int table_dc = (table >> 4) & 15;
|
|
int table_ac = table & 15;
|
|
|
|
if ( index == 1 && (table_ac != 0 || table_dc != 0) ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] SOS marker for Y should have huffman tables 0,0 but %d,%d was presented!\n", table_dc, table_ac);
|
|
return -1;
|
|
}
|
|
if ( (index == 2 || index == 3) && (table_ac != 1 || table_dc != 1) ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] SOS marker for Cb or Cr should have huffman tables 1,1 but %d,%d was presented!\n", table_dc, table_ac);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// Collect the additional scan parameters Ss, Se, Ah/Al.
|
|
int Ss = (int)gpujpeg_reader_read_byte(*image);
|
|
int Se = (int)gpujpeg_reader_read_byte(*image);
|
|
int Ax = (int)gpujpeg_reader_read_byte(*image);
|
|
int Ah = (Ax >> 4) & 15;
|
|
int Al = (Ax) & 15;
|
|
|
|
// Check maximum scan count
|
|
if ( decoder->reader->scan_count >= GPUJPEG_MAX_COMPONENT_COUNT ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] SOS marker reached maximum number of scans (3)!\n");
|
|
return -1;
|
|
}
|
|
|
|
int scan_index = decoder->reader->scan_count;
|
|
|
|
// Get scan structure
|
|
struct gpujpeg_reader_scan* scan = &decoder->reader->scan[scan_index];
|
|
decoder->reader->scan_count++;
|
|
// Scan segments begin at the end of previous scan segments or from zero index
|
|
scan->segment_index = decoder->reader->segment_count;
|
|
scan->segment_count = 0;
|
|
|
|
// Read scan content
|
|
if ( decoder->reader->segment_info_count > 0 ) {
|
|
// Read scan content by segment info contained in special header
|
|
if ( gpujpeg_reader_read_scan_content_by_segment_info(decoder, image, image_end, scan, scan_index) != 0 )
|
|
return -1;
|
|
} else {
|
|
// Read scan content byte-by-byte
|
|
if ( gpujpeg_reader_read_scan_content_by_parsing(decoder, image, image_end, scan, scan_index) != 0 )
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** Documented at declaration */
|
|
int
|
|
gpujpeg_reader_read_image(struct gpujpeg_decoder* decoder, uint8_t* image, int image_size)
|
|
{
|
|
// Setup reader and decoder
|
|
decoder->reader->param = decoder->coder.param;
|
|
decoder->reader->param_image = decoder->coder.param_image;
|
|
decoder->reader->comp_count = 0;
|
|
decoder->reader->scan_count = 0;
|
|
decoder->reader->segment_count = 0;
|
|
decoder->reader->data_compressed_size = 0;
|
|
decoder->reader->segment_info_count = 0;
|
|
decoder->reader->segment_info_size = 0;
|
|
|
|
// Get image end
|
|
uint8_t* image_end = image + image_size;
|
|
|
|
// Check first SOI marker
|
|
int marker_soi = gpujpeg_reader_read_marker(&image);
|
|
if ( marker_soi != GPUJPEG_MARKER_SOI ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] JPEG data should begin with SOI marker, but marker %s was found!\n", gpujpeg_marker_name((enum gpujpeg_marker_code)marker_soi));
|
|
return -1;
|
|
}
|
|
|
|
int eoi_presented = 0;
|
|
while ( eoi_presented == 0 ) {
|
|
// Read marker
|
|
int marker = gpujpeg_reader_read_marker(&image);
|
|
if ( marker == -1 ) {
|
|
return -1;
|
|
}
|
|
|
|
// Read more info according to the marker
|
|
switch (marker)
|
|
{
|
|
case GPUJPEG_MARKER_SEGMENT_INFO:
|
|
if ( gpujpeg_reader_read_segment_info(decoder, &image) != 0 )
|
|
return -1;
|
|
break;
|
|
|
|
case GPUJPEG_MARKER_APP0:
|
|
if ( gpujpeg_reader_read_app0(&image) != 0 )
|
|
return -1;
|
|
break;
|
|
case GPUJPEG_MARKER_APP1:
|
|
case GPUJPEG_MARKER_APP2:
|
|
case GPUJPEG_MARKER_APP3:
|
|
case GPUJPEG_MARKER_APP4:
|
|
case GPUJPEG_MARKER_APP5:
|
|
case GPUJPEG_MARKER_APP6:
|
|
case GPUJPEG_MARKER_APP7:
|
|
case GPUJPEG_MARKER_APP8:
|
|
case GPUJPEG_MARKER_APP9:
|
|
case GPUJPEG_MARKER_APP10:
|
|
case GPUJPEG_MARKER_APP11:
|
|
case GPUJPEG_MARKER_APP12:
|
|
//case GPUJPEG_MARKER_APP13:
|
|
case GPUJPEG_MARKER_APP14:
|
|
case GPUJPEG_MARKER_APP15:
|
|
fprintf(stderr, "[GPUJPEG] [Warning] JPEG data contains not supported %s marker\n", gpujpeg_marker_name((enum gpujpeg_marker_code)marker));
|
|
gpujpeg_reader_skip_marker_content(&image);
|
|
break;
|
|
|
|
case GPUJPEG_MARKER_DQT:
|
|
if ( gpujpeg_reader_read_dqt(decoder, &image) != 0 )
|
|
return -1;
|
|
break;
|
|
|
|
case GPUJPEG_MARKER_SOF0:
|
|
// Baseline
|
|
if ( gpujpeg_reader_read_sof0(decoder, &image) != 0 )
|
|
return -1;
|
|
break;
|
|
case GPUJPEG_MARKER_SOF1:
|
|
// Extended sequential with Huffman coder
|
|
fprintf(stderr, "[GPUJPEG] [Warning] Reading SOF1 as it was SOF0 marker (should work but verify it)!\n", gpujpeg_marker_name((enum gpujpeg_marker_code)marker));
|
|
if ( gpujpeg_reader_read_sof0(decoder, &image) != 0 )
|
|
return -1;
|
|
break;
|
|
case GPUJPEG_MARKER_SOF2:
|
|
fprintf(stderr, "[GPUJPEG] [Error] Marker SOF2 (Progressive with Huffman coding) is not supported!");
|
|
return -1;
|
|
case GPUJPEG_MARKER_SOF3:
|
|
fprintf(stderr, "[GPUJPEG] [Error] Marker SOF3 (Lossless with Huffman coding) is not supported!");
|
|
return -1;
|
|
case GPUJPEG_MARKER_SOF5:
|
|
fprintf(stderr, "[GPUJPEG] [Error] Marker SOF5 (Differential sequential with Huffman coding) is not supported!");
|
|
return -1;
|
|
case GPUJPEG_MARKER_SOF6:
|
|
fprintf(stderr, "[GPUJPEG] [Error] Marker SOF6 (Differential progressive with Huffman coding) is not supported!");
|
|
return -1;
|
|
case GPUJPEG_MARKER_SOF7:
|
|
fprintf(stderr, "[GPUJPEG] [Error] Marker SOF7 (Extended lossless with Arithmetic coding) is not supported!");
|
|
return -1;
|
|
case GPUJPEG_MARKER_JPG:
|
|
fprintf(stderr, "[GPUJPEG] [Error] Marker JPG (Reserved for JPEG extensions ) is not supported!");
|
|
return -1;
|
|
case GPUJPEG_MARKER_SOF10:
|
|
fprintf(stderr, "[GPUJPEG] [Error] Marker SOF10 (Progressive with Arithmetic coding) is not supported!");
|
|
return -1;
|
|
case GPUJPEG_MARKER_SOF11:
|
|
fprintf(stderr, "[GPUJPEG] [Error] Marker SOF11 (Lossless with Arithmetic coding) is not supported!");
|
|
return -1;
|
|
case GPUJPEG_MARKER_SOF13:
|
|
fprintf(stderr, "[GPUJPEG] [Error] Marker SOF13 (Differential sequential with Arithmetic coding) is not supported!");
|
|
return -1;
|
|
case GPUJPEG_MARKER_SOF14:
|
|
fprintf(stderr, "[GPUJPEG] [Error] Marker SOF14 (Differential progressive with Arithmetic coding) is not supported!");
|
|
return -1;
|
|
case GPUJPEG_MARKER_SOF15:
|
|
fprintf(stderr, "[GPUJPEG] [Error] Marker SOF15 (Differential lossless with Arithmetic coding) is not supported!");
|
|
return -1;
|
|
|
|
case GPUJPEG_MARKER_DHT:
|
|
if ( gpujpeg_reader_read_dht(decoder, &image) != 0 )
|
|
return -1;
|
|
break;
|
|
|
|
case GPUJPEG_MARKER_DRI:
|
|
if ( gpujpeg_reader_read_dri(decoder, &image) != 0 )
|
|
return -1;
|
|
break;
|
|
|
|
case GPUJPEG_MARKER_SOS:
|
|
if ( gpujpeg_reader_read_sos(decoder, &image, image_end) != 0 )
|
|
return -1;
|
|
break;
|
|
|
|
case GPUJPEG_MARKER_EOI:
|
|
eoi_presented = 1;
|
|
break;
|
|
|
|
case GPUJPEG_MARKER_COM:
|
|
case GPUJPEG_MARKER_DAC:
|
|
case GPUJPEG_MARKER_DNL:
|
|
fprintf(stderr, "[GPUJPEG] [Warning] JPEG data contains not supported %s marker\n", gpujpeg_marker_name((enum gpujpeg_marker_code)marker));
|
|
gpujpeg_reader_skip_marker_content(&image);
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr, "[GPUJPEG] [Error] JPEG data contains not supported %s marker!\n", gpujpeg_marker_name((enum gpujpeg_marker_code)marker));
|
|
gpujpeg_reader_skip_marker_content(&image);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// Check EOI marker
|
|
if ( eoi_presented == 0 ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] JPEG data should end with EOI marker!\n");
|
|
return -1;
|
|
}
|
|
|
|
// Set decoder parameters
|
|
decoder->segment_count = decoder->reader->segment_count;
|
|
decoder->data_compressed_size = decoder->reader->data_compressed_size;
|
|
|
|
if ( decoder->segment_count > decoder->coder.segment_count ) {
|
|
fprintf(stderr, "[GPUJPEG] [Error] Decoder can't decode image that has segment count %d (maximum segment count for specified parameters is %d)!\n",
|
|
decoder->segment_count, decoder->coder.segment_count);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|