TurboJPEG C API: Add BMP/PPM load/save functions

The main justification for this is to provide new libjpeg-turbo users
with a quick & easy way of developing a complete JPEG
compression/decompression program without requiring them to build
libjpeg-turbo from source (which was necessary in order to use the
project-private bmp API) or to use external libraries.  These new
functions build upon significant enhancements to rdbmp.c, wrbmp.c,
rdppm.c, and wrppm.c which allow those engines to convert directly
between the native pixel format of the file and a pixel format
("colorspace" in libjpeg parlance) specified by the calling program.
rdbmp.c and wrbmp.c have also been modified such that the calling
program can choose to read or write image rows in the native (bottom-up)
order of the file format, thus eliminating the need to use an inversion
array.  tjLoadImage() and tjSaveImage() leverage these new underlying
features in order to significantly improve upon the performance of the
old bmp API.

Because these new functions cannot work without the libjpeg-turbo
colorspace extensions, the libjpeg-compatible code in turbojpeg.c has
been removed.  That code was only there to serve as an example of how
to use the TurboJPEG API on top of libjpeg, but more specific, buildable
examples now exist in the https://github.com/libjpeg-turbo/ijg
repository.
This commit is contained in:
DRC
2017-11-16 18:09:07 -06:00
parent 087ec126c1
commit aa7459050d
24 changed files with 1653 additions and 906 deletions

282
rdbmp.c
View File

@@ -6,7 +6,7 @@
* Modified 2009-2010 by Guido Vollbeding.
* libjpeg-turbo Modifications:
* Modified 2011 by Siarhei Siamashka.
* Copyright (C) 2015, D. R. Commander.
* Copyright (C) 2015, 2017, D. R. Commander.
* For conditions of distribution and use, see the accompanying README.ijg
* file.
*
@@ -27,6 +27,7 @@
*/
#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */
#include "cmyk.h"
#ifdef BMP_SUPPORTED
@@ -49,6 +50,10 @@ typedef char U_CHAR;
#define ReadOK(file,buffer,len) (JFREAD(file,buffer,len) == ((size_t) (len)))
static int alpha_index[JPEG_NUMCS] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, 3, 0, 0, -1
};
/* Private version of data source object */
@@ -66,6 +71,17 @@ typedef struct _bmp_source_struct {
JDIMENSION row_width; /* Physical width of scanlines in file */
int bits_per_pixel; /* remembers 8- or 24-bit format */
boolean use_inversion_array; /* TRUE = preload the whole image, which is
stored in bottom-up order, and feed it to
the calling program in top-down order
FALSE = the calling program will maintain
its own image buffer and read the rows in
bottom-up order */
U_CHAR *iobuffer; /* I/O buffer (used to buffer a single row from
disk if use_inversion_array == FALSE) */
} bmp_source_struct;
@@ -131,20 +147,58 @@ get_8bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
register JSAMPROW inptr, outptr;
register JDIMENSION col;
/* Fetch next row from virtual array */
source->source_row--;
image_ptr = (*cinfo->mem->access_virt_sarray)
((j_common_ptr) cinfo, source->whole_image,
source->source_row, (JDIMENSION) 1, FALSE);
if (source->use_inversion_array) {
/* Fetch next row from virtual array */
source->source_row--;
image_ptr = (*cinfo->mem->access_virt_sarray)
((j_common_ptr) cinfo, source->whole_image,
source->source_row, (JDIMENSION) 1, FALSE);
inptr = image_ptr[0];
} else {
if (! ReadOK(source->pub.input_file, source->iobuffer, source->row_width))
ERREXIT(cinfo, JERR_INPUT_EOF);
inptr = source->iobuffer;
}
/* Expand the colormap indexes to real data */
inptr = image_ptr[0];
outptr = source->pub.buffer[0];
for (col = cinfo->image_width; col > 0; col--) {
t = GETJSAMPLE(*inptr++);
*outptr++ = colormap[0][t]; /* can omit GETJSAMPLE() safely */
*outptr++ = colormap[1][t];
*outptr++ = colormap[2][t];
if (cinfo->in_color_space == JCS_GRAYSCALE) {
for (col = cinfo->image_width; col > 0; col--) {
t = GETJSAMPLE(*inptr++);
*outptr++ = RGB2GRAY(colormap[0][t], colormap[1][t], colormap[2][t]);
}
} else if (cinfo->in_color_space == JCS_CMYK) {
for (col = cinfo->image_width; col > 0; col--) {
t = GETJSAMPLE(*inptr++);
rgb_to_cmyk(colormap[0][t], colormap[1][t], colormap[2][t], outptr,
outptr + 1, outptr + 2, outptr + 3);
outptr += 4;
}
} else {
register int rindex = rgb_red[cinfo->in_color_space];
register int gindex = rgb_green[cinfo->in_color_space];
register int bindex = rgb_blue[cinfo->in_color_space];
register int aindex = alpha_index[cinfo->in_color_space];
register int ps = rgb_pixelsize[cinfo->in_color_space];
if (aindex >= 0) {
for (col = cinfo->image_width; col > 0; col--) {
t = GETJSAMPLE(*inptr++);
outptr[rindex] = colormap[0][t];
outptr[gindex] = colormap[1][t];
outptr[bindex] = colormap[2][t];
outptr[aindex] = 0xFF;
outptr += ps;
}
} else {
for (col = cinfo->image_width; col > 0; col--) {
t = GETJSAMPLE(*inptr++);
outptr[rindex] = colormap[0][t];
outptr[gindex] = colormap[1][t];
outptr[bindex] = colormap[2][t];
outptr += ps;
}
}
}
return 1;
@@ -160,22 +214,61 @@ get_24bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
register JSAMPROW inptr, outptr;
register JDIMENSION col;
/* Fetch next row from virtual array */
source->source_row--;
image_ptr = (*cinfo->mem->access_virt_sarray)
((j_common_ptr) cinfo, source->whole_image,
source->source_row, (JDIMENSION) 1, FALSE);
if (source->use_inversion_array) {
/* Fetch next row from virtual array */
source->source_row--;
image_ptr = (*cinfo->mem->access_virt_sarray)
((j_common_ptr) cinfo, source->whole_image,
source->source_row, (JDIMENSION) 1, FALSE);
inptr = image_ptr[0];
} else {
if (! ReadOK(source->pub.input_file, source->iobuffer, source->row_width))
ERREXIT(cinfo, JERR_INPUT_EOF);
inptr = source->iobuffer;
}
/* Transfer data. Note source values are in BGR order
* (even though Microsoft's own documents say the opposite).
*/
inptr = image_ptr[0];
outptr = source->pub.buffer[0];
for (col = cinfo->image_width; col > 0; col--) {
outptr[2] = *inptr++; /* can omit GETJSAMPLE() safely */
outptr[1] = *inptr++;
outptr[0] = *inptr++;
outptr += 3;
if (cinfo->in_color_space == JCS_EXT_BGR) {
MEMCOPY(outptr, inptr, source->row_width);
} else if (cinfo->in_color_space == JCS_GRAYSCALE) {
for (col = cinfo->image_width; col > 0; col--) {
/* can omit GETJSAMPLE() safely */
JSAMPLE b = *inptr++, g = *inptr++, r = *inptr++;
*outptr++ = RGB2GRAY(r, g, b);
}
} else if (cinfo->in_color_space == JCS_CMYK) {
for (col = cinfo->image_width; col > 0; col--) {
/* can omit GETJSAMPLE() safely */
JSAMPLE b = *inptr++, g = *inptr++, r = *inptr++;
rgb_to_cmyk(r, g, b, outptr, outptr + 1, outptr + 2, outptr + 3);
outptr += 4;
}
} else {
register int rindex = rgb_red[cinfo->in_color_space];
register int gindex = rgb_green[cinfo->in_color_space];
register int bindex = rgb_blue[cinfo->in_color_space];
register int aindex = alpha_index[cinfo->in_color_space];
register int ps = rgb_pixelsize[cinfo->in_color_space];
if (aindex >= 0) {
for (col = cinfo->image_width; col > 0; col--) {
outptr[bindex] = *inptr++; /* can omit GETJSAMPLE() safely */
outptr[gindex] = *inptr++;
outptr[rindex] = *inptr++;
outptr[aindex] = 0xFF;
outptr += ps;
}
} else {
for (col = cinfo->image_width; col > 0; col--) {
outptr[bindex] = *inptr++; /* can omit GETJSAMPLE() safely */
outptr[gindex] = *inptr++;
outptr[rindex] = *inptr++;
outptr += ps;
}
}
}
return 1;
@@ -191,22 +284,64 @@ get_32bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
register JSAMPROW inptr, outptr;
register JDIMENSION col;
/* Fetch next row from virtual array */
source->source_row--;
image_ptr = (*cinfo->mem->access_virt_sarray)
((j_common_ptr) cinfo, source->whole_image,
source->source_row, (JDIMENSION) 1, FALSE);
if (source->use_inversion_array) {
/* Fetch next row from virtual array */
source->source_row--;
image_ptr = (*cinfo->mem->access_virt_sarray)
((j_common_ptr) cinfo, source->whole_image,
source->source_row, (JDIMENSION) 1, FALSE);
inptr = image_ptr[0];
} else {
if (! ReadOK(source->pub.input_file, source->iobuffer, source->row_width))
ERREXIT(cinfo, JERR_INPUT_EOF);
inptr = source->iobuffer;
}
/* Transfer data. Note source values are in BGR order
* (even though Microsoft's own documents say the opposite).
*/
inptr = image_ptr[0];
outptr = source->pub.buffer[0];
for (col = cinfo->image_width; col > 0; col--) {
outptr[2] = *inptr++; /* can omit GETJSAMPLE() safely */
outptr[1] = *inptr++;
outptr[0] = *inptr++;
inptr++; /* skip the 4th byte (Alpha channel) */
outptr += 3;
if (cinfo->in_color_space == JCS_EXT_BGRX ||
cinfo->in_color_space == JCS_EXT_BGRA) {
MEMCOPY(outptr, inptr, source->row_width);
} else if (cinfo->in_color_space == JCS_GRAYSCALE) {
for (col = cinfo->image_width; col > 0; col--) {
/* can omit GETJSAMPLE() safely */
JSAMPLE b = *inptr++, g = *inptr++, r = *inptr++;
*outptr++ = RGB2GRAY(r, g, b);
}
} else if (cinfo->in_color_space == JCS_CMYK) {
for (col = cinfo->image_width; col > 0; col--) {
/* can omit GETJSAMPLE() safely */
JSAMPLE b = *inptr++, g = *inptr++, r = *inptr++;
rgb_to_cmyk(r, g, b, outptr, outptr + 1, outptr + 2, outptr + 3);
inptr++; /* skip the 4th byte (Alpha channel) */
outptr += 4;
}
} else {
register int rindex = rgb_red[cinfo->in_color_space];
register int gindex = rgb_green[cinfo->in_color_space];
register int bindex = rgb_blue[cinfo->in_color_space];
register int aindex = alpha_index[cinfo->in_color_space];
register int ps = rgb_pixelsize[cinfo->in_color_space];
if (aindex >= 0) {
for (col = cinfo->image_width; col > 0; col--) {
outptr[bindex] = *inptr++; /* can omit GETJSAMPLE() safely */
outptr[gindex] = *inptr++;
outptr[rindex] = *inptr++;
outptr[aindex] = *inptr++;
outptr += ps;
}
} else {
for (col = cinfo->image_width; col > 0; col--) {
outptr[bindex] = *inptr++; /* can omit GETJSAMPLE() safely */
outptr[gindex] = *inptr++;
outptr[rindex] = *inptr++;
inptr++; /* skip the 4th byte (Alpha channel) */
outptr += ps;
}
}
}
return 1;
@@ -297,7 +432,7 @@ start_input_bmp (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
unsigned int biClrUsed = 0;
int mapentrysize = 0; /* 0 indicates no colormap */
int bPad;
JDIMENSION row_width;
JDIMENSION row_width = 0;
/* Read and verify the bitmap file header */
if (! ReadOK(source->pub.input_file, bmpfileheader, 14))
@@ -415,33 +550,72 @@ start_input_bmp (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
}
/* Compute row width in file, including padding to 4-byte boundary */
if (source->bits_per_pixel == 24)
row_width = (JDIMENSION) (biWidth * 3);
else if (source->bits_per_pixel == 32)
row_width = (JDIMENSION) (biWidth * 4);
else
switch (source->bits_per_pixel) {
case 8:
if (cinfo->in_color_space == JCS_UNKNOWN)
cinfo->in_color_space = JCS_EXT_RGB;
row_width = (JDIMENSION) biWidth;
break;
case 24:
if (cinfo->in_color_space == JCS_UNKNOWN)
cinfo->in_color_space = JCS_EXT_BGR;
row_width = (JDIMENSION) (biWidth * 3);
break;
case 32:
if (cinfo->in_color_space == JCS_UNKNOWN)
cinfo->in_color_space = JCS_EXT_BGRA;
row_width = (JDIMENSION) (biWidth * 4);
break;
default:
ERREXIT(cinfo, JERR_BMP_BADDEPTH);
}
while ((row_width & 3) != 0) row_width++;
source->row_width = row_width;
/* Allocate space for inversion array, prepare for preload pass */
source->whole_image = (*cinfo->mem->request_virt_sarray)
((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
row_width, (JDIMENSION) biHeight, (JDIMENSION) 1);
source->pub.get_pixel_rows = preload_image;
if (cinfo->progress != NULL) {
cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress;
progress->total_extra_passes++; /* count file input as separate pass */
if (source->use_inversion_array) {
/* Allocate space for inversion array, prepare for preload pass */
source->whole_image = (*cinfo->mem->request_virt_sarray)
((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
row_width, (JDIMENSION) biHeight, (JDIMENSION) 1);
source->pub.get_pixel_rows = preload_image;
if (cinfo->progress != NULL) {
cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress;
progress->total_extra_passes++; /* count file input as separate pass */
}
} else {
source->iobuffer = (U_CHAR *)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
row_width);
switch (source->bits_per_pixel) {
case 8:
source->pub.get_pixel_rows = get_8bit_row;
break;
case 24:
source->pub.get_pixel_rows = get_24bit_row;
break;
case 32:
source->pub.get_pixel_rows = get_32bit_row;
break;
default:
ERREXIT(cinfo, JERR_BMP_BADDEPTH);
}
}
if (IsExtRGB(cinfo->in_color_space))
cinfo->input_components = rgb_pixelsize[cinfo->in_color_space];
else if (cinfo->in_color_space == JCS_GRAYSCALE)
cinfo->input_components = 1;
else if (cinfo->in_color_space == JCS_CMYK)
cinfo->input_components = 4;
else
ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
/* Allocate one-row buffer for returned data */
source->pub.buffer = (*cinfo->mem->alloc_sarray)
((j_common_ptr) cinfo, JPOOL_IMAGE,
(JDIMENSION) (biWidth * 3), (JDIMENSION) 1);
(JDIMENSION) (biWidth * cinfo->input_components), (JDIMENSION) 1);
source->pub.buffer_height = 1;
cinfo->in_color_space = JCS_RGB;
cinfo->input_components = 3;
cinfo->data_precision = 8;
cinfo->image_width = (JDIMENSION) biWidth;
cinfo->image_height = (JDIMENSION) biHeight;
@@ -464,7 +638,7 @@ finish_input_bmp (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
*/
GLOBAL(cjpeg_source_ptr)
jinit_read_bmp (j_compress_ptr cinfo)
jinit_read_bmp (j_compress_ptr cinfo, boolean use_inversion_array)
{
bmp_source_ptr source;
@@ -477,6 +651,8 @@ jinit_read_bmp (j_compress_ptr cinfo)
source->pub.start_input = start_input_bmp;
source->pub.finish_input = finish_input_bmp;
source->use_inversion_array = use_inversion_array;
return (cjpeg_source_ptr) source;
}