385 lines
9.9 KiB
C
385 lines
9.9 KiB
C
/* Copyright (C) 2009
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
#include "cjpeg.h"
|
|
#include "cio.h"
|
|
|
|
/* YCbCr to RGB transformation */
|
|
|
|
/*
|
|
* precalculated tables for a faster YCbCr->RGB transformation.
|
|
* use a INT32 table because we'll scale values by 2^16 and
|
|
* work with integers.
|
|
*/
|
|
|
|
ycbcr_tables ycc_tables;
|
|
|
|
void
|
|
init_ycbcr_tables()
|
|
{
|
|
UINT16 i;
|
|
for (i = 0; i < 256; i++) {
|
|
ycc_tables.r2y[i] = (INT32)(65536 * 0.299 + 0.5) * i;
|
|
ycc_tables.r2cb[i] = (INT32)(65536 * -0.16874 + 0.5) * i;
|
|
ycc_tables.r2cr[i] = (INT32)(32768) * i;
|
|
ycc_tables.g2y[i] = (INT32)(65536 * 0.587 + 0.5) * i;
|
|
ycc_tables.g2cb[i] = (INT32)(65536 * -0.33126 + 0.5) * i;
|
|
ycc_tables.g2cr[i] = (INT32)(65536 * -0.41869 + 0.5) * i;
|
|
ycc_tables.b2y[i] = (INT32)(65536 * 0.114 + 0.5) * i;
|
|
ycc_tables.b2cb[i] = (INT32)(32768) * i;
|
|
ycc_tables.b2cr[i] = (INT32)(65536 * -0.08131 + 0.5) * i;
|
|
}
|
|
}
|
|
|
|
void
|
|
rgb_to_ycbcr(UINT8 *rgb_unit, ycbcr_unit *ycc_unit, int x, int w)
|
|
{
|
|
ycbcr_tables *tbl = &ycc_tables;
|
|
UINT8 r, g, b;
|
|
#ifdef REVERSED
|
|
int src_pos = (x + w * (DCTSIZE - 1)) * 3;
|
|
#else
|
|
int src_pos = x * 3;
|
|
#endif
|
|
int dst_pos = 0;
|
|
int i, j;
|
|
for (j = 0; j < DCTSIZE; j++) {
|
|
for (i = 0; i < DCTSIZE; i++) {
|
|
b = rgb_unit[src_pos];
|
|
g = rgb_unit[src_pos+1];
|
|
r = rgb_unit[src_pos+2];
|
|
ycc_unit->y[dst_pos] = (INT8) ((UINT8)
|
|
((tbl->r2y[r] + tbl->g2y[g] + tbl->b2y[b]) >> 16) - 128);
|
|
ycc_unit->cb[dst_pos] = (INT8) ((UINT8)
|
|
((tbl->r2cb[r] + tbl->g2cb[g] + tbl->b2cb[b]) >> 16));
|
|
ycc_unit->cr[dst_pos] = (INT8) ((UINT8)
|
|
((tbl->r2cr[r] + tbl->g2cr[g] + tbl->b2cr[b]) >> 16));
|
|
src_pos += 3;
|
|
dst_pos++;
|
|
}
|
|
#ifdef REVERSED
|
|
src_pos -= (w + DCTSIZE) * 3;
|
|
#else
|
|
src_pos += (w - DCTSIZE) * 3;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
/* quantization */
|
|
|
|
quant_tables q_tables;
|
|
|
|
void
|
|
init_quant_tables(UINT32 scale_factor)
|
|
{
|
|
quant_tables *tbl = &q_tables;
|
|
int temp1, temp2;
|
|
int i;
|
|
for (i = 0; i < DCTSIZE2; i++) {
|
|
temp1 = ((UINT32) STD_LU_QTABLE[i] * scale_factor + 50) / 100;
|
|
if (temp1 < 1)
|
|
temp1 = 1;
|
|
if (temp1 > 255)
|
|
temp1 = 255;
|
|
tbl->lu[ZIGZAG[i]] = (UINT8) temp1;
|
|
|
|
temp2 = ((UINT32) STD_CH_QTABLE[i] * scale_factor + 50) / 100;
|
|
if (temp2 < 1)
|
|
temp2 = 1;
|
|
if (temp2 > 255)
|
|
temp2 = 255;
|
|
tbl->ch[ZIGZAG[i]] = (UINT8) temp2;
|
|
}
|
|
}
|
|
|
|
void
|
|
jpeg_quant(ycbcr_unit *ycc_unit, quant_unit *q_unit)
|
|
{
|
|
quant_tables *tbl = &q_tables;
|
|
float q_lu, q_ch;
|
|
int x, y, i = 0;
|
|
for (x = 0; x < DCTSIZE; x++) {
|
|
for (y = 0; y < DCTSIZE; y++) {
|
|
q_lu = 1.0 / ((double) tbl->lu[ZIGZAG[i]] * \
|
|
AAN_SCALE_FACTOR[x] * AAN_SCALE_FACTOR[y] * 8.0);
|
|
q_ch = 1.0 / ((double) tbl->ch[ZIGZAG[i]] * \
|
|
AAN_SCALE_FACTOR[x] * AAN_SCALE_FACTOR[y] * 8.0);
|
|
|
|
q_unit->y[i] = (INT16)(ycc_unit->y[i]*q_lu + 16384.5) - 16384;
|
|
q_unit->cb[i] = (INT16)(ycc_unit->cb[i]*q_ch + 16384.5) - 16384;
|
|
q_unit->cr[i] = (INT16)(ycc_unit->cr[i]*q_ch + 16384.5) - 16384;
|
|
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* huffman compression */
|
|
|
|
huff_tables h_tables;
|
|
|
|
void
|
|
set_huff_table(UINT8 *nrcodes, UINT8 *values, BITS *h_table)
|
|
{
|
|
int i, j, k;
|
|
j = 0;
|
|
UINT16 value = 0;
|
|
for (i = 1; i <= 16; i++) {
|
|
for (k = 0; k < nrcodes[i]; k++) {
|
|
h_table[values[j]].len = i;
|
|
h_table[values[j]].val = value;
|
|
j++;
|
|
value++;
|
|
}
|
|
value <<= 1;
|
|
}
|
|
}
|
|
|
|
void
|
|
init_huff_tables()
|
|
{
|
|
huff_tables *tbl = &h_tables;
|
|
set_huff_table(STD_LU_DC_NRCODES, STD_LU_DC_VALUES, tbl->lu_dc);
|
|
set_huff_table(STD_LU_AC_NRCODES, STD_LU_AC_VALUES, tbl->lu_ac);
|
|
set_huff_table(STD_CH_DC_NRCODES, STD_CH_DC_VALUES, tbl->ch_dc);
|
|
set_huff_table(STD_CH_AC_NRCODES, STD_CH_AC_VALUES, tbl->ch_ac);
|
|
}
|
|
|
|
void
|
|
set_bits(BITS *bits, INT16 data)
|
|
{
|
|
UINT16 pos_data;
|
|
int i;
|
|
pos_data = data < 0 ? ~data + 1 : data;
|
|
for (i = 15; i >= 0; i--)
|
|
if ((pos_data & (1 << i)) != 0)
|
|
break;
|
|
bits->len = i + 1;
|
|
bits->val = data < 0 ? data + (1 << bits->len) - 1 : data;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void
|
|
print_bits(BITS bits)
|
|
{
|
|
printf("%hu %hu\t", bits.len, bits.val);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* compress JPEG
|
|
*/
|
|
void
|
|
jpeg_compress(compress_io *cio,
|
|
INT16 *data, INT16 *dc, BITS *dc_htable, BITS *ac_htable)
|
|
{
|
|
INT16 zigzag_data[DCTSIZE2];
|
|
BITS bits;
|
|
INT16 diff;
|
|
int i, j;
|
|
int zero_num;
|
|
int mark;
|
|
|
|
/* zigzag encode */
|
|
for (i = 0; i < DCTSIZE2; i++)
|
|
zigzag_data[ZIGZAG[i]] = data[i];
|
|
|
|
/* write DC */
|
|
diff = zigzag_data[0] - *dc;
|
|
*dc = zigzag_data[0];
|
|
|
|
if (diff == 0)
|
|
write_bits(cio, dc_htable[0]);
|
|
else {
|
|
set_bits(&bits, diff);
|
|
write_bits(cio, dc_htable[bits.len]);
|
|
write_bits(cio, bits);
|
|
}
|
|
|
|
/* write AC */
|
|
int end = DCTSIZE2 - 1;
|
|
while (zigzag_data[end] == 0 && end > 0)
|
|
end--;
|
|
for (i = 1; i <= end; i++) {
|
|
j = i;
|
|
while (zigzag_data[j] == 0 && j <= end)
|
|
j++;
|
|
zero_num = j - i;
|
|
for (mark = 0; mark < zero_num / 16; mark++)
|
|
write_bits(cio, ac_htable[0xF0]);
|
|
zero_num = zero_num % 16;
|
|
set_bits(&bits, zigzag_data[j]);
|
|
write_bits(cio, ac_htable[zero_num * 16 + bits.len]);
|
|
write_bits(cio, bits);
|
|
i = j;
|
|
}
|
|
|
|
/* write end of unit */
|
|
if (end != DCTSIZE2 - 1)
|
|
write_bits(cio, ac_htable[0]);
|
|
}
|
|
|
|
|
|
/*
|
|
* main JPEG encoding
|
|
*/
|
|
void
|
|
jpeg_encode(compress_io *cio, bmp_info *binfo)
|
|
{
|
|
/* init tables */
|
|
UINT32 scale = 50;
|
|
init_ycbcr_tables();
|
|
init_quant_tables(scale);
|
|
init_huff_tables();
|
|
|
|
/* write info */
|
|
write_file_header(cio);
|
|
write_frame_header(cio, binfo);
|
|
write_scan_header(cio);
|
|
|
|
/* encode */
|
|
mem_mgr *in = cio->in;
|
|
ycbcr_unit ycc_unit;
|
|
quant_unit q_unit;
|
|
INT16 dc_y = 0,
|
|
dc_cb = 0,
|
|
dc_cr = 0;
|
|
int x, y;
|
|
|
|
#ifdef REVERSED
|
|
int in_size = in->end - in->set;
|
|
int offset = binfo->offset + (binfo->datasize/in_size - 1) * in_size;
|
|
#else
|
|
int offset = binfo->offset;
|
|
#endif
|
|
fseek(in->fp, offset, SEEK_SET);
|
|
|
|
for (y = 0; y < binfo->height; y += 8) {
|
|
|
|
/* flush input buffer */
|
|
if (! (in->flush_buffer) (cio))
|
|
err_exit(BUFFER_READ_ERR);
|
|
|
|
for (x = 0; x < binfo->width; x += 8) {
|
|
|
|
/* convert RGB unit to YCbCr unit */
|
|
rgb_to_ycbcr(in->set, &ycc_unit, x, binfo->width);
|
|
|
|
/* forward DCT on YCbCr unit */
|
|
jpeg_fdct(ycc_unit.y);
|
|
jpeg_fdct(ycc_unit.cb);
|
|
jpeg_fdct(ycc_unit.cr);
|
|
|
|
/* quantization, store in quant unit */
|
|
jpeg_quant(&ycc_unit, &q_unit);
|
|
|
|
/* huffman compression, write */
|
|
jpeg_compress(cio, q_unit.y, &dc_y,
|
|
h_tables.lu_dc, h_tables.lu_ac);
|
|
jpeg_compress(cio, q_unit.cb,&dc_cb,
|
|
h_tables.ch_dc, h_tables.ch_ac);
|
|
jpeg_compress(cio, q_unit.cr,&dc_cr,
|
|
h_tables.ch_dc, h_tables.ch_ac);
|
|
}
|
|
}
|
|
write_align_bits(cio);
|
|
|
|
/* write file end */
|
|
write_file_trailer(cio);
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
is_bmp(FILE *fp)
|
|
{
|
|
UINT8 marker[3];
|
|
if (fread(marker, sizeof(UINT16), 2, fp) != 2)
|
|
err_exit(FILE_READ_ERR);
|
|
if (marker[0] != 0x42 || marker[1] != 0x4D)
|
|
return false;
|
|
rewind(fp);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
err_exit(const char *error_string, int exit_num)
|
|
{
|
|
printf(error_string);
|
|
exit(exit_num);
|
|
}
|
|
|
|
|
|
void
|
|
print_help()
|
|
{
|
|
printf("compress BMP file into JPEG file.\n");
|
|
printf("Usage:\n");
|
|
printf(" cjpeg {BMP} {JPEG}\n");
|
|
printf("\n");
|
|
printf("Author: Yu, Le <yeolar@gmail.com>\n");
|
|
}
|
|
|
|
|
|
#ifdef main_lib
|
|
int main(int argc, char *argv[])
|
|
{
|
|
if (argc == 3) {
|
|
/* open bmp file */
|
|
FILE *bmp_fp = fopen(argv[1], "rb");
|
|
if (!bmp_fp)
|
|
err_exit(FILE_OPEN_ERR);
|
|
if (!is_bmp(bmp_fp))
|
|
err_exit(FILE_TYPE_ERR);
|
|
|
|
/* open jpeg file */
|
|
FILE *jpeg_fp = fopen(argv[2], "wb");
|
|
if (!jpeg_fp)
|
|
err_exit(FILE_OPEN_ERR);
|
|
|
|
/* get bmp info */
|
|
bmp_info binfo;
|
|
read_bmp(bmp_fp, &binfo);
|
|
|
|
/* init memory for input and output */
|
|
compress_io cio;
|
|
int in_size = (binfo.width * 3 + 3) / 4 * 4 * DCTSIZE;
|
|
int out_size = MEM_OUT_SIZE;
|
|
init_mem(&cio, bmp_fp, in_size, jpeg_fp, out_size);
|
|
|
|
/* main encode process */
|
|
jpeg_encode(&cio, &binfo);
|
|
|
|
/* flush and free memory, close files */
|
|
if (! (cio.out->flush_buffer) (&cio))
|
|
err_exit(BUFFER_WRITE_ERR);
|
|
free_mem(&cio);
|
|
fclose(bmp_fp);
|
|
fclose(jpeg_fp);
|
|
}
|
|
else
|
|
print_help();
|
|
exit(0);
|
|
}
|
|
#endif // main_lib
|