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:
282
rdbmp.c
282
rdbmp.c
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user