/* Copyright (c) 2008 Thomas Lavergne * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include typedef unsigned char byte; typedef struct image_s { unsigned width; unsigned height; unsigned quality; byte **data; } image_t; static image_t *img_new(unsigned width, unsigned height) { image_t *img; unsigned size, i; if (width == 0 || height == 0) return NULL; size = sizeof(byte *) * height + 3 * width * height; img = (image_t *)malloc(sizeof(image_t) + size); if (img == NULL) return NULL; img->data = (byte **)((byte *)img + sizeof(image_t)); for (i = 0; i < height; ++i) img->data[i] = (byte *)(img->data + height) + i * 3 * width; img->width = width; img->height = height; return img; } static void img_free(image_t *img) { free(img); } static image_t *jpg_load(const char *filename) { struct jpeg_decompress_struct dinfo; struct jpeg_error_mgr jerr; FILE *file; image_t *img; file = fopen(filename, "rb"); if (file == NULL) return NULL; dinfo.err = jpeg_std_error(&jerr); jpeg_create_decompress(&dinfo); jpeg_stdio_src(&dinfo, file); jpeg_read_header(&dinfo, TRUE); jpeg_start_decompress(&dinfo); img = img_new(dinfo.output_width, dinfo.output_height); if (img == NULL) { jpeg_destroy_decompress(&dinfo); fclose(file); return NULL; } img->quality = dinfo.quant_tbl_ptrs[0]->quantval[6]; if (img->quality > 51) img->quality = (50 * 51 + img->quality / 2) / img->quality; else img->quality = (5126 - 50 * img->quality) / 51; while (dinfo.output_scanline < img->height) jpeg_read_scanlines(&dinfo, img->data + dinfo.output_scanline, 16); jpeg_finish_decompress(&dinfo); jpeg_destroy_decompress(&dinfo); fclose(file); return img; } static int jpg_save(image_t *img, const char *filename) { struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; FILE *file; file = fopen(filename, "wb"); if (file == NULL) return 0; cinfo.err = jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); jpeg_stdio_dest(&cinfo, file); cinfo.image_width = img->width; cinfo.image_height = img->height; cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo, img->quality, TRUE); jpeg_start_compress(&cinfo, TRUE); jpeg_write_scanlines(&cinfo, img->data, img->height); jpeg_finish_compress(&cinfo); jpeg_destroy_compress(&cinfo); fclose(file); return 1; } static image_t *img_load(const char *filename) { return jpg_load(filename); } static int img_save(image_t *img, const char *filename) { return jpg_save(img, filename); } static image_t *img_resample(image_t *src, unsigned width, unsigned height) { image_t *dst; unsigned *offset_x; unsigned *offset_y; unsigned i, x, y; dst = img_new(width, height); if (dst == NULL) return NULL; offset_x = (unsigned *)malloc(sizeof(long) * dst->width); offset_y = (unsigned *)malloc(sizeof(long) * dst->height); if (offset_x == NULL || offset_y == NULL) { free(offset_x); free(offset_y); free(dst); } for (i = 0; i < dst->width; ++i) offset_x[i] = ((double)i + 0.5) * src->width / dst->width; for (i = 0; i < dst->height; ++i) offset_y[i] = ((double)i + 0.5) * src->height / dst->height; for (y = 0; y < dst->height; ++y) { byte *src_line = src->data[offset_y[y]]; byte *dst_line = dst->data[y]; for (x = 0; x < dst->width; ++x) { dst_line[3 * x + 0] = src_line[3 * offset_x[x] + 0]; dst_line[3 * x + 1] = src_line[3 * offset_x[x] + 1]; dst_line[3 * x + 2] = src_line[3 * offset_x[x] + 2]; } } free(offset_x); free(offset_y); return dst; } static image_t *img_resize(image_t *src, unsigned width, unsigned height) { int coefs[25]= {1, 1, 2, 1, 1, 1, 2, 4, 2, 1, 2, 4, 8, 4, 2, 1, 2, 4, 2, 1, 1, 1, 2, 1, 1}; image_t *tmp, *dst; unsigned x, y; tmp = img_resample(src, width * 5, height * 5); if (tmp == NULL) return NULL; dst = img_new(width, height); if (dst == NULL) { free(tmp); return NULL; } dst->quality = src->quality; for (y = 0; y < dst->height; ++y) { for (x = 0; x < dst->width; ++x) { int c, r = 0, g = 0, b = 0; for (c = 0; c < 25; ++c) { int tx = 5 * x + c % 5; int ty = 5 * y + c / 5; r += tmp->data[ty][3 * tx + 0] * coefs[c]; g += tmp->data[ty][3 * tx + 1] * coefs[c]; b += tmp->data[ty][3 * tx + 2] * coefs[c]; } dst->data[y][3 * x + 0] = r / 52; dst->data[y][3 * x + 1] = g / 52; dst->data[y][3 * x + 2] = b / 52; } } free(tmp); return dst; } static int decodesize(const char *str, unsigned *width, unsigned *height) { unsigned p = 0, w = 0, h = 0; if (str == NULL || width == NULL || height == NULL) return 0; while (isdigit(str[p])) w = w * 10 + (str[p++] - '0'); if (str[p++] == 'x') while (isdigit(str[p])) h = h * 10 + (str[p++] - '0'); *width = w; *height = h; return 1; } int main(int argc, char *argv[]) { unsigned width, height; double ratio; image_t *src, *dst; if (argc <= 1 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { fprintf(stderr, "usage: apr -h | --help\n"); fprintf(stderr, " apr input output width\n"); fprintf(stderr, " apr input output widthxheight\n"); return EXIT_SUCCESS; } if (argc != 4) { fprintf(stderr, "error: wrong number of arguments\n"); return EXIT_FAILURE; } src = img_load(argv[1]); if (!decodesize(argv[3], &width, &height)) { fprintf(stderr, "error: invalid size format"); return EXIT_FAILURE; } ratio = (double)src->width / src->height; if (height == 0 || width / ratio < height) height = width / ratio; else width = height * ratio; dst = img_resize(src, width, height); img_save(dst, argv[2]); img_free(src); img_free(dst); return EXIT_SUCCESS; }