Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to crop after scaling #9

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 49 additions & 16 deletions src/bin/epeg_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,25 @@
static int verbose_flag = 0;
static int thumb_width = 0; // < 0 means % of input
static int thumb_height = 0; // < 0 means % of input
static int crop_top = 0; // Pixels to crop from the top
static int crop_bottom = 0; // Pixels to crop from the bottom
static int crop_left = 0; // Pixels to crop from the left
static int crop_right = 0; // Pixels to crop from the right
static int max_dimension = 0; // > 0 means we reduce max(w,h) to max_dimension, with aspect preserved
static int inset_flag = 0; // Ensure specified dimensions will be covered
static int outbound_flag = 0; // Ensure specified dimensions will be covered
static int crop_flag = 0; // Crop thumbnail after scaling
static int thumb_quality = 85; // Quality value from 1 to 100
static char *thumb_comment = NULL;
static struct option long_options[] =
{
{"verbose", no_argument, 0, 'v'},
{"width", required_argument, 0, 'w'},
{"height", required_argument, 0, 'h'},
{"max", required_argument, 0, 'm'},
{"inset", no_argument, 0, 'i'},
{"quality", required_argument, 0, 'q'},
{"comment", required_argument, 0, 'c'},
{"verbose", no_argument, 0, 'v'},
{"width", required_argument, 0, 'w'},
{"height", required_argument, 0, 'h'},
{"max", required_argument, 0, 'm'},
{"outbound", no_argument, 0, 'o'},
{"crop", no_argument, 0, 'r'},
{"quality", required_argument, 0, 'q'},
{"comment", required_argument, 0, 'c'},
{0, 0, 0, 0}
};

Expand All @@ -33,7 +39,8 @@ usage(const char *myname)
" -w, --width=<width>[%%] set thumbnail width [%% of input]\n"
" -h, --height=<heigth>[%%] set thumbnail heigth [%% of input]\n"
" -m, --max=<maximum> reduce max(w,h) to maximum, with aspect preserved\n"
" -i, --inset cover at least the specified size (no upscaling or cropping)\n"
" -o, --outbound cover at least the specified size (no upscaling or cropping)\n"
" -r, --crop crop the resulting thumbnail (to be used with --outbound)\n"
" -c, --comment=<comment> put a comment in thumbnail\n"
" -q, --quality=<quality> set thumbnail quality (1-100)\n", myname);
exit(0);
Expand All @@ -48,7 +55,7 @@ main(int argc, char **argv)
char *input_file = NULL, *output_file = NULL;
char *p;

while ((c = getopt_long(argc, argv, "w:h:vic:m:q:", long_options, &option_index)) != -1) {
while ((c = getopt_long(argc, argv, "w:h:vorc:m:q:", long_options, &option_index)) != -1) {
switch (c) {
case 0:
usage(argv[0]);
Expand Down Expand Up @@ -90,8 +97,11 @@ main(int argc, char **argv)
}
if (verbose_flag) printf("thumb_quality = %d\n", thumb_quality);
break;
case 'i':
inset_flag = 1;
case 'o':
outbound_flag = 1;
break;
case 'r':
crop_flag = 1;
break;
case 'c':
thumb_comment = strdup(optarg);
Expand Down Expand Up @@ -130,6 +140,7 @@ main(int argc, char **argv)
const char *com;
Epeg_Thumbnail_Info info;
int w, h;
int scaled_w, scaled_h;

com = epeg_comment_get(im);
if (verbose_flag) if (com) printf("Comment: %s\n", com);
Expand All @@ -154,24 +165,46 @@ main(int argc, char **argv)
}

if (max_dimension > 0) {
if (w > h ^ inset_flag) {
if (w > h ^ outbound_flag) {
thumb_width = max_dimension;
thumb_height = max_dimension * h / w;
} else {
thumb_height = max_dimension;
thumb_width = max_dimension * w / h;
}
} else if (inset_flag) {
thumb_width = MAX(thumb_width, thumb_height * w / h);
thumb_height = MAX(thumb_height, thumb_width * h / w);
if (outbound_flag && crop_flag) {
crop_top = (thumb_height - max_dimension) / 2;
crop_bottom = (thumb_height - max_dimension - crop_top);
crop_left = (thumb_width - max_dimension) / 2;
crop_right = (thumb_width - max_dimension - crop_left);
}
} else if (outbound_flag) {
scaled_w = thumb_height * w / h;
scaled_h = thumb_width * h / w;
if(scaled_w > thumb_width) {
if(crop_flag) {
crop_left = (scaled_w - thumb_width) / 2;
crop_right = scaled_w - thumb_width - crop_left;
}
thumb_width = scaled_w;
}
if(scaled_h > thumb_height) {
if(crop_flag) {
crop_top = (scaled_h - thumb_height) / 2;
crop_bottom = scaled_h - thumb_height - crop_top;
}
thumb_height = scaled_h;
}
}
}

if (verbose_flag) printf("Thumb size: %dx%d\n", thumb_width, thumb_height);
if (verbose_flag) printf("Crop (TxBxLxR): %dx%dx%dx%d\n", crop_top, crop_bottom, crop_left, crop_right);
epeg_decode_size_set(im, thumb_width, thumb_height);
epeg_quality_set (im, thumb_quality);
epeg_thumbnail_comments_enable (im, 1);
epeg_comment_set (im, thumb_comment);
epeg_crop_set (im, crop_top, crop_bottom, crop_left, crop_right);
epeg_file_output_set (im, output_file);
epeg_encode (im);
epeg_close (im);
Expand Down
1 change: 1 addition & 0 deletions src/lib/Epeg.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ extern "C" {
EAPI void epeg_thumbnail_comments_get (Epeg_Image *im, Epeg_Thumbnail_Info *info);
EAPI void epeg_comment_set (Epeg_Image *im, const char *comment);
EAPI void epeg_quality_set (Epeg_Image *im, int quality);
EAPI void epeg_crop_set (Epeg_Image *im, int top, int bottom, int left, int right);
EAPI void epeg_thumbnail_comments_enable (Epeg_Image *im, int onoff);
EAPI void epeg_file_output_set (Epeg_Image *im, const char *file);
EAPI void epeg_memory_output_set (Epeg_Image *im, unsigned char **data, int *size);
Expand Down
54 changes: 47 additions & 7 deletions src/lib/epeg_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,23 @@ epeg_quality_set(Epeg_Image *im, int quality)
im->out.quality = quality;
}

/**
* Crop a thumbnail after scaling
* @param im A handle to an opened Epeg image.
* @param top Pixels to remove from the top
* @param bottom Pixels to remove from the bottom
* @param left Pixels to remove from the left
* @param right Pixels to remove from the right
*/
EAPI void
epeg_crop_set(Epeg_Image *im, int top, int bottom, int left, int right)
{
im->out.crop_t = (top > 0 ? top : 0);
im->out.crop_b = (bottom > 0 ? bottom : 0);
im->out.crop_l = (left > 0 ? left : 0);
im->out.crop_r = (right > 0 ? right : 0);
}

/**
* Enable thumbnail comments in saved image.
* @param im A handle to an opened Epeg image.
Expand Down Expand Up @@ -708,8 +725,8 @@ epeg_memory_output_set(Epeg_Image *im, unsigned char **data, int *size)
*
* This saves the image @p im to its destination specified by
* epeg_file_output_set() or epeg_memory_output_set(). The image will be
* encoded at the deoded pixel size, using the quality, comment and thumbnail
* comment settings set on the image.
* encoded at the deoded pixel size, using the quality, crop, comment and
* thumbnail comment settings set on the image.
*
* retval 1 - error scale
* 2 - error encode
Expand Down Expand Up @@ -762,6 +779,7 @@ epeg_close(Epeg_Image *im)
if (!im) return;
if (im->pixels) free(im->pixels);
if (im->lines) free(im->lines);
if (im->cropped_lines) free(im->cropped_lines);
if (im->in.file) free(im->in.file);
if (!im->in.file) free(im->in.jinfo.src);
if (im->in.f || im->in.mem.data) jpeg_destroy_decompress(&(im->in.jinfo));
Expand Down Expand Up @@ -983,10 +1001,22 @@ _epeg_decode(Epeg_Image *im)
return 1;
}

im->cropped_lines = malloc(im->in.jinfo.output_height * sizeof(char *));
if (!im->cropped_lines)
{
free(im->pixels);
im->pixels = NULL;
free(im->lines);
im->lines = NULL;
return 1;
}

jpeg_start_decompress(&(im->in.jinfo));

for (y = 0; y < im->in.jinfo.output_height; y++)
for (y = 0; y < im->in.jinfo.output_height; y++) {
im->lines[y] = im->pixels + (y * im->in.jinfo.output_components * im->in.jinfo.output_width);
im->cropped_lines[y] = im->lines[y] + (im->out.crop_l * im->in.jinfo.output_components);
}

while (im->in.jinfo.output_scanline < im->in.jinfo.output_height)
{
Expand Down Expand Up @@ -1025,7 +1055,7 @@ _epeg_scale(Epeg_Image *im)
row = im->pixels + (((y * im->in.jinfo.output_height) / h) * im->in.jinfo.output_components * im->in.jinfo.output_width);
dst = im->pixels + (y * im->in.jinfo.output_components * im->in.jinfo.output_width);

for (x = 0; x < im->out.w; x++)
for (x = 0; x < w; x++)
{
src = row + (((x * im->in.jinfo.output_width) / w) * im->in.jinfo.output_components);
for (i = 0; i < im->in.jinfo.output_components; i++)
Expand Down Expand Up @@ -1163,7 +1193,8 @@ _epeg_encode(Epeg_Image *im)
struct epeg_destination_mgr *dst_mgr = NULL;
int ok = 0;

if ((im->out.w < 1) || (im->out.h < 1)) return 1;
if ((im->out.w - im->out.crop_l - im->out.crop_r) < 1) return 1;
if ((im->out.h - im->out.crop_t - im->out.crop_b) < 1) return 1;
if (im->out.f) return 1;

if (im->out.file)
Expand Down Expand Up @@ -1218,6 +1249,10 @@ _epeg_encode(Epeg_Image *im)
}
im->out.jinfo.image_width = im->out.w;
im->out.jinfo.image_height = im->out.h;
if(im->cropped_lines) {
im->out.jinfo.image_width -= (im->out.crop_l + im->out.crop_r);
im->out.jinfo.image_height -= (im->out.crop_t + im->out.crop_b);
}
im->out.jinfo.input_components = im->in.jinfo.output_components;
im->out.jinfo.in_color_space = im->in.jinfo.out_color_space;
im->out.jinfo.dct_method = im->in.jinfo.dct_method;
Expand Down Expand Up @@ -1277,8 +1312,13 @@ _epeg_encode(Epeg_Image *im)
jpeg_write_marker(&(im->out.jinfo), JPEG_APP0 + 7, buf, strlen(buf));
}

while (im->out.jinfo.next_scanline < im->out.h)
jpeg_write_scanlines(&(im->out.jinfo), &(im->lines[im->out.jinfo.next_scanline]), 1);
if(im->cropped_lines) {
while (im->out.jinfo.next_scanline < im->out.jinfo.image_height)
jpeg_write_scanlines(&(im->out.jinfo), &(im->cropped_lines[im->out.crop_t + im->out.jinfo.next_scanline]), 1);
} else {
while (im->out.jinfo.next_scanline < im->out.jinfo.image_height)
jpeg_write_scanlines(&(im->out.jinfo), &(im->lines[im->out.jinfo.next_scanline]), 1);
}
jpeg_finish_compress(&(im->out.jinfo));

done:
Expand Down
2 changes: 2 additions & 0 deletions src/lib/epeg_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ struct _Epeg_Image
struct stat stat_info;
unsigned char *pixels;
unsigned char **lines;
unsigned char **cropped_lines;

char scaled : 1;

Expand Down Expand Up @@ -64,6 +65,7 @@ struct _Epeg_Image
} mem;
int x, y;
int w, h;
int crop_t, crop_b, crop_l, crop_r;
char *comment;
FILE *f;
struct jpeg_compress_struct jinfo;
Expand Down