+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+
+#include "libqscale.h"
+
+qscale_img *qscale_load_jpeg(const char *filename)
+{
+ FILE *file = fopen(filename, "rb");
+ qscale_img *img;
+ if (file == NULL) {
+ return NULL;
+ }
+
+ img = qscale_load_jpeg_from_stdio(file);
+
+ fclose(file);
+ return img;
+}
+
+qscale_img *qscale_load_jpeg_from_stdio(FILE *file)
+{
+ qscale_img *img = (qscale_img *)malloc(sizeof(qscale_img));
+ if (img == NULL) {
+ return NULL;
+ }
+
+ img->data_y = img->data_cb = img->data_cr = NULL;
+
+ /* FIXME: Better error handling here (ie., return NULL). */
+ 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);
+
+ /* We do not handle anything but YCbCr images (yet?). */
+ if (dinfo.num_components != 3) {
+ qscale_destroy(img);
+ return NULL;
+ }
+
+ img->width = dinfo.image_width;
+ img->height = dinfo.image_height;
+
+ img->w0 = dinfo.image_width * dinfo.comp_info[0].h_samp_factor / dinfo.max_h_samp_factor;
+ img->h0 = dinfo.image_height * dinfo.comp_info[0].v_samp_factor / dinfo.max_v_samp_factor;
+
+ img->w1 = dinfo.image_width * dinfo.comp_info[1].h_samp_factor / dinfo.max_h_samp_factor;
+ img->h1 = dinfo.image_height * dinfo.comp_info[1].v_samp_factor / dinfo.max_v_samp_factor;
+
+ img->w2 = dinfo.image_width * dinfo.comp_info[2].h_samp_factor / dinfo.max_h_samp_factor;
+ img->h2 = dinfo.image_height * dinfo.comp_info[2].v_samp_factor / dinfo.max_v_samp_factor;
+
+ img->data_y = (JSAMPLE*)memalign(16, dinfo.comp_info[0].height_in_blocks * dinfo.comp_info[0].width_in_blocks * DCTSIZE * DCTSIZE);
+ img->data_cb = (JSAMPLE*)memalign(16, dinfo.comp_info[1].height_in_blocks * dinfo.comp_info[1].width_in_blocks * DCTSIZE * DCTSIZE);
+ img->data_cr = (JSAMPLE*)memalign(16, dinfo.comp_info[2].height_in_blocks * dinfo.comp_info[2].width_in_blocks * DCTSIZE * DCTSIZE);
+
+ if (img->data_y == NULL || img->data_cb == NULL || img->data_cr == NULL) {
+ qscale_destroy(img);
+ return NULL;
+ }
+
+ 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;
+
+ 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] = img->data_y + (i+blocks*DCTSIZE*dinfo.comp_info[0].v_samp_factor) * dinfo.comp_info[0].width_in_blocks * DCTSIZE;
+ cb_row_ptrs[i] = img->data_cb + (i+blocks*DCTSIZE*dinfo.comp_info[1].v_samp_factor) * dinfo.comp_info[1].width_in_blocks * DCTSIZE;
+ cr_row_ptrs[i] = img->data_cr + (i+blocks*DCTSIZE*dinfo.comp_info[2].v_samp_factor) * dinfo.comp_info[2].width_in_blocks * DCTSIZE;
+ }
+
+ total_lines += max_lines;
+ ++blocks;
+
+ if (jpeg_read_raw_data(&dinfo, ptrs, max_lines) == 0)
+ break;
+ }
+
+ jpeg_destroy_decompress(&dinfo);
+ return img;
+}
+
+void qscale_destroy(qscale_img *img)
+{
+ free(img->data_y);
+ free(img->data_cb);
+ free(img->data_cr);
+ free(img);
+}