From: sgunderson@bigfoot.com <> Date: Sun, 3 Feb 2008 16:11:45 +0000 (+0100) Subject: Add JPEG support. X-Git-Url: https://git.sesse.net/?p=qscale;a=commitdiff_plain;h=82b2da83a6924b542e0cc399437ad848315ed8c5 Add JPEG support. --- diff --git a/qscale.c b/qscale.c index 042d341..c847a12 100644 --- a/qscale.c +++ b/qscale.c @@ -2,30 +2,9 @@ #include #include #include +#include "jpeglib.h" -#define CACHE_LINE_FACTOR 1 - -void myreadline(char *buf) -{ - char *ptr; - - if (fgets(buf, 256, stdin) == NULL) { - fprintf(stderr, "Premuture EOF\n"); - exit(1); - } - - if (strlen(buf) == 0) { - return; - } - - ptr = &buf[strlen(buf) - 1]; - if (*ptr == '\n') { - *ptr-- = 0; - if (*ptr == '\r') { - *ptr = 0; - } - } -} +#define CACHE_LINE_FACTOR 8 double sinc(double x) { @@ -46,22 +25,25 @@ double lanczos_tap(double x) return sinc(x*M_PI) * sinc(x*M_PI / 3.0); } - +struct filter { + struct pix_desc *pd; + float *coeffs; +}; struct pix_desc { unsigned start, end; unsigned startcoeff; }; -void hscale(float *pix, unsigned char *npix, unsigned w, unsigned h, unsigned nw) +void hscale_calc_filter(struct filter *filter, unsigned w, unsigned h, unsigned nw) { struct pix_desc *pd = (struct pix_desc *)malloc(nw * sizeof(struct pix_desc)); int size_coeffs = 8; float *coeffs = (float *)malloc(size_coeffs * sizeof(float)); int num_coeffs = 0; - int x, y, sx; + int x, sx; double sf = (double)w / (double)nw; double support = (w > nw) ? (3.0 * sf) : (3.0 / sf); - + /* calculate the filter */ for (x = 0; x < nw; ++x) { int start = ceil(x * sf - support); @@ -95,32 +77,39 @@ void hscale(float *pix, unsigned char *npix, unsigned w, unsigned h, unsigned nw coeffs[pd[x].startcoeff + sx - start] /= sum; } } - + + filter->pd = pd; + filter->coeffs = coeffs; +} + +void hscale(struct filter *filter, unsigned char *pix, float *npix, unsigned w, unsigned h, unsigned nw, unsigned sstride, unsigned dstride) +{ + struct pix_desc *pd = filter->pd; + float *coeffs = filter->coeffs; + int y; + for (y = 0; y < h; ++y) { - float *sptr = pix + y*w; - unsigned char *dptr = npix + y*nw; + unsigned char *sptr = pix + y*sstride; + float *dptr = npix + y*dstride; + float acc = 0.0; + int x; for (x = 0; x < nw; ++x) { - float acc = 0.0; float *cf = &coeffs[pd[x].startcoeff]; unsigned sx; - unsigned char ch; + acc = 0.0; for (sx = pd[x].start; sx <= pd[x].end; ++sx) { acc += sptr[sx] * *cf++; } - - if (acc < 0.0) - ch = 0; - else if (acc > 255.0) - ch = 255; - else - ch = (unsigned char)acc; - *dptr++ = ch; + *dptr++ = acc; + } + for ( ; x < dstride; ++x) { + *dptr++ = acc; } } } -void vscale(unsigned char *pix, float *npix, unsigned w, unsigned h, unsigned nh) +void vscale(float *pix, unsigned char *npix, unsigned w, unsigned h, unsigned nh, unsigned dstride) { struct pix_desc *pd = (struct pix_desc *)malloc(nh * sizeof(struct pix_desc)); int size_coeffs = 8; @@ -166,8 +155,8 @@ void vscale(unsigned char *pix, float *npix, unsigned w, unsigned h, unsigned nh #if CACHE_LINE_FACTOR > 1 for (x = 0; x < w; x += CACHE_LINE_FACTOR) { - unsigned char *sptr = pix + x; - float *dptr = npix + x; + float *sptr = pix + x; + unsigned char *dptr = npix + x; for (y = 0; y < nh; ++y) { int i; float acc[CACHE_LINE_FACTOR]; @@ -184,17 +173,24 @@ void vscale(unsigned char *pix, float *npix, unsigned w, unsigned h, unsigned nh } for (i = 0; i < CACHE_LINE_FACTOR; ++i) { - dptr[i] = acc[i]; + unsigned char ch; + if (acc[i] < 0.0) + ch = 0; + else if (acc[i] > 255.0) + ch = 255; + else + ch = (unsigned char)acc[i]; + dptr[i] = ch; } - dptr += w; + dptr += dstride; } } for (x = (x/CACHE_LINE_FACTOR)*CACHE_LINE_FACTOR; x < w; ++x) { #else for (x = 0; x < w; ++x) { #endif - unsigned char *sptr = pix + x; - float *dptr = npix + x; + float *sptr = pix + x; + unsigned char *dptr = npix + x; for (y = 0; y < nh; ++y) { float acc = 0.0; float *cf = &coeffs[pd[y].startcoeff]; @@ -204,75 +200,192 @@ void vscale(unsigned char *pix, float *npix, unsigned w, unsigned h, unsigned nh acc += sptr[sy * w] * *cf++; } - *dptr = acc; - dptr += w; + unsigned char ch; + if (acc < 0.0) + ch = 0; + else if (acc > 255.0) + ch = 255; + else + ch = (unsigned char)acc; + *dptr = ch; + dptr += dstride; } } } -int main(void) +int main(int argc, char **argv) { - unsigned w, h; - unsigned nw = 600, nh = 400; - //unsigned nw = 3504, nh = 2336; - char buf[256]; - unsigned char *pix; - float *npix; - unsigned char *npix2; - - myreadline(buf); - if (strcmp(buf, "P5") != 0) { - fprintf(stderr, "Error: Not a PGM file\n"); - exit(1); - } - myreadline(buf); /* comment */ - myreadline(buf); - - if (sscanf(buf, "%u %u", &w, &h) != 2) { - fprintf(stderr, "Error: Expected width/height\n"); - exit(1); - } - - myreadline(buf); - if (strcmp(buf, "255") != 0) { - fprintf(stderr, "Error: Expected 8-bit\n"); - exit(1); - } - - pix = (unsigned char *)malloc(w * h); - if (pix == NULL) { - perror("malloc"); - exit(1); + unsigned nominal_w = atoi(argv[1]); + unsigned nominal_h = atoi(argv[2]); + + unsigned samp_h0 = 2, samp_v0 = 2; + unsigned samp_h1 = 1, samp_v1 = 1; + unsigned samp_h2 = 1, samp_v2 = 1; + unsigned max_samp_h = 2, max_samp_v = 2; + + unsigned nw0 = nominal_w * samp_h0 / max_samp_h, nh0 = nominal_h * samp_v0 / max_samp_v; + unsigned nw1 = nominal_w * samp_h1 / max_samp_h, nh1 = nominal_h * samp_v1 / max_samp_v; + unsigned nw2 = nominal_w * samp_h2 / max_samp_h, nh2 = nominal_h * samp_v2 / max_samp_v; + + unsigned stride0 = (nw0 + DCTSIZE-1) & ~(DCTSIZE-1); + unsigned stride1 = (nw1 + DCTSIZE-1) & ~(DCTSIZE-1); + unsigned stride2 = (nw2 + DCTSIZE-1) & ~(DCTSIZE-1); + + struct jpeg_decompress_struct dinfo; + struct jpeg_error_mgr jerr; + dinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&dinfo); + jpeg_stdio_src(&dinfo, stdin); + jpeg_read_header(&dinfo, TRUE); + dinfo.raw_data_out = TRUE; + jpeg_start_decompress(&dinfo); + + fprintf(stderr, "Scaling using Lanczos filter:\n"); + fprintf(stderr, " Y component: %ux%u -> %ux%u\n", dinfo.comp_info[0].width_in_blocks * DCTSIZE, dinfo.comp_info[0].height_in_blocks * DCTSIZE, nw0, nh0); + fprintf(stderr, " Cb component: %ux%u -> %ux%u\n", dinfo.comp_info[1].width_in_blocks * DCTSIZE, dinfo.comp_info[1].height_in_blocks * DCTSIZE, nw1, nh1); + fprintf(stderr, " Cr component: %ux%u -> %ux%u\n", dinfo.comp_info[2].width_in_blocks * DCTSIZE, dinfo.comp_info[2].height_in_blocks * DCTSIZE, nw2, nh2); + + float *npix_y = (float *)malloc((dinfo.comp_info[0].height_in_blocks + dinfo.comp_info[0].v_samp_factor - 1) * DCTSIZE * stride0 * sizeof(float)); + float *npix_cb = (float *)malloc((dinfo.comp_info[1].height_in_blocks + dinfo.comp_info[1].v_samp_factor - 1) * DCTSIZE * stride1 * sizeof(float)); + float *npix_cr = (float *)malloc((dinfo.comp_info[2].height_in_blocks + dinfo.comp_info[2].v_samp_factor - 1) * DCTSIZE * stride2 * sizeof(float)); + JSAMPLE *data_y = (unsigned char *)malloc(nh0 * stride0); + JSAMPLE *data_cb = (unsigned char *)malloc(nh1 * stride1); + JSAMPLE *data_cr = (unsigned char *)malloc(nh2 * stride2); + + struct filter filt0, filt1, filt2; + + hscale_calc_filter(&filt0, + /* w= */ dinfo.image_width * dinfo.comp_info[0].h_samp_factor / dinfo.max_h_samp_factor, + /* h= */ dinfo.comp_info[0].v_samp_factor * DCTSIZE, + /* nw= */ nw0 + ); + hscale_calc_filter(&filt1, + /* w= */ dinfo.image_width * dinfo.comp_info[1].h_samp_factor / dinfo.max_h_samp_factor, + /* h= */ dinfo.comp_info[1].v_samp_factor * DCTSIZE, + /* nw= */ nw1 + ); + hscale_calc_filter(&filt2, + /* w= */ dinfo.image_width * dinfo.comp_info[2].h_samp_factor / dinfo.max_h_samp_factor, + /* h= */ dinfo.comp_info[2].v_samp_factor * DCTSIZE, + /* nw= */ nw2 + ); + + int total_lines = 0, blocks = 0; + while (total_lines < dinfo.comp_info[0].height_in_blocks * DCTSIZE) { + unsigned max_lines = dinfo.max_v_samp_factor * DCTSIZE; + JSAMPLE tmp_y [(dinfo.comp_info[0].width_in_blocks * DCTSIZE) * (DCTSIZE * dinfo.comp_info[0].v_samp_factor)]; + JSAMPLE tmp_cb[(dinfo.comp_info[1].width_in_blocks * DCTSIZE) * (DCTSIZE * dinfo.comp_info[1].v_samp_factor)]; + JSAMPLE tmp_cr[(dinfo.comp_info[2].width_in_blocks * DCTSIZE) * (DCTSIZE * dinfo.comp_info[2].v_samp_factor)]; + + JSAMPROW y_row_ptrs[max_lines]; + JSAMPROW cb_row_ptrs[max_lines]; + JSAMPROW cr_row_ptrs[max_lines]; + JSAMPROW* ptrs[] = { y_row_ptrs, cb_row_ptrs, cr_row_ptrs }; + int i; + + for (i = 0; i < max_lines; ++i) { + y_row_ptrs[i] = tmp_y + i * dinfo.comp_info[0].width_in_blocks * DCTSIZE; + cb_row_ptrs[i] = tmp_cb + i * dinfo.comp_info[1].width_in_blocks * DCTSIZE; + cr_row_ptrs[i] = tmp_cr + i * dinfo.comp_info[2].width_in_blocks * DCTSIZE; + } + + int lines = jpeg_read_raw_data(&dinfo, ptrs, max_lines); + if (lines == 0) + break; + + hscale(/* filter= */ &filt0, + /* from= */ tmp_y, + /* to= */ npix_y + blocks * DCTSIZE * dinfo.comp_info[0].v_samp_factor * stride0, + /* w= */ dinfo.image_width * dinfo.comp_info[0].h_samp_factor / dinfo.max_h_samp_factor, + /* h= */ dinfo.comp_info[0].v_samp_factor * DCTSIZE, + /* nw= */ nw0, + /* sstride= */ dinfo.comp_info[0].width_in_blocks * DCTSIZE, + /* dstride= */ stride0); + + hscale(/* filter= */ &filt1, + /* from= */ tmp_cb, + /* to= */ npix_cb + blocks * DCTSIZE * dinfo.comp_info[1].v_samp_factor * stride1, + /* w= */ dinfo.image_width * dinfo.comp_info[1].h_samp_factor / dinfo.max_h_samp_factor, + /* h= */ dinfo.comp_info[1].v_samp_factor * DCTSIZE, + /* nw= */ nw1, + /* sstride= */ dinfo.comp_info[1].width_in_blocks * DCTSIZE, + /* dstride= */ stride1); + + hscale(/* filter= */ &filt2, + /* from= */ tmp_cr, + /* to= */ npix_cr + blocks * DCTSIZE * dinfo.comp_info[2].v_samp_factor * stride2, + /* w= */ dinfo.image_width * dinfo.comp_info[2].h_samp_factor / dinfo.max_h_samp_factor, + /* h= */ dinfo.comp_info[2].v_samp_factor * DCTSIZE, + /* nw= */ nw2, + /* sstride= */ dinfo.comp_info[2].width_in_blocks * DCTSIZE, + /* dstride= */ stride2); + + total_lines += max_lines; + ++blocks; } - if (fread(pix, w*h, 1, stdin) != 1) { - fprintf(stderr, "Error: Short read\n"); - exit(1); - } + vscale(npix_y, data_y, stride0, dinfo.image_height * dinfo.comp_info[0].v_samp_factor / dinfo.max_v_samp_factor, nh0, stride0); + vscale(npix_cb, data_cb, stride1, dinfo.image_height * dinfo.comp_info[1].v_samp_factor / dinfo.max_v_samp_factor, nh1, stride1); + vscale(npix_cr, data_cr, stride2, dinfo.image_height * dinfo.comp_info[2].v_samp_factor / dinfo.max_v_samp_factor, nh2, stride2); + jpeg_destroy_decompress(&dinfo); - npix = (float *)malloc(sizeof(float) * nw * h); - if (npix == NULL) { - perror("malloc"); - exit(1); - } - npix2 = (unsigned char *)malloc(nw * nh); - if (npix2 == NULL) { - perror("malloc"); - exit(1); - } - - vscale(pix, npix, w, h, nh); - hscale(npix, npix2, w, nh, nw); - - printf("P5\n# COMMENT: Made by qscale\n%u %u\n255\n", nw, nh); - { - int y, x; - for (y = 0; y < nh; ++y) { - for (x = 0; x < nw; ++x) { - putchar(npix2[y * nw + x]); - } + struct jpeg_compress_struct cinfo; + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + jpeg_stdio_dest(&cinfo, stdout); + cinfo.input_components = 3; + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, 85, FALSE); + cinfo.image_width = nominal_w; + cinfo.image_height = nominal_h; + cinfo.raw_data_in = TRUE; + jpeg_set_colorspace(&cinfo, JCS_YCbCr); + cinfo.comp_info[0].h_samp_factor = samp_h0; + cinfo.comp_info[0].v_samp_factor = samp_v0; + cinfo.comp_info[1].h_samp_factor = samp_h1; + cinfo.comp_info[1].v_samp_factor = samp_v1; + cinfo.comp_info[2].h_samp_factor = samp_h2; + cinfo.comp_info[2].v_samp_factor = samp_v2; + jpeg_start_compress(&cinfo, TRUE); + + total_lines = 0; + blocks = 0; + while (total_lines < cinfo.comp_info[0].height_in_blocks * DCTSIZE) { + unsigned max_lines = cinfo.max_v_samp_factor * DCTSIZE; + + JSAMPROW y_row_ptrs[max_lines]; + JSAMPROW cb_row_ptrs[max_lines]; + JSAMPROW cr_row_ptrs[max_lines]; + JSAMPROW* ptrs[] = { y_row_ptrs, cb_row_ptrs, cr_row_ptrs }; + int i; + + for (i = 0; i < max_lines; ++i) { + // simple edge extension + int yline = i + blocks*DCTSIZE*cinfo.comp_info[0].v_samp_factor; + if (yline > nh0 - 1) + yline = nh0 - 1; + + int cbline = i + blocks*DCTSIZE*cinfo.comp_info[1].v_samp_factor; + if (cbline > nh1 - 1) + cbline = nh1 - 1; + + int crline = i + blocks*DCTSIZE*cinfo.comp_info[2].v_samp_factor; + if (crline > nh2 - 1) + crline = nh2 - 1; + + y_row_ptrs[i] = data_y + yline * stride0; + cb_row_ptrs[i] = data_cb + cbline * stride1; + cr_row_ptrs[i] = data_cr + crline * stride2; } + + total_lines += max_lines; + ++blocks; + + jpeg_write_raw_data(&cinfo, ptrs, max_lines); } + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); return 0; } +