Skip to content

Commit

Permalink
Simpified resizing in export
Browse files Browse the repository at this point in the history
This is based on those 2 rules:

- Resizing by scaling factor or by max size are independent and select
  one will cover the other value, because user cannot see both in UI.
- We don't do upscaling when exporting.

So the actual logic on determine final size is simple:

1. If user set value <= 0, this means don't resize and use the original
   image size.
2. If user set value > original image size, this also means don't resize
   and use the original image size.
3. For max size, if user does not set the value according to image
   ratio, we will recalculate the value to keep image ratio and make
   sure the calculated size is smaller than user's max size.
4. If by scaling factor, calculate max size according to scaling factor.
   If by max size, calculate scaling factor according to max size.
5. Limit size by max size, and pass correct size and scaling factor to
   pixelpipe.

Fixes <#362>.
  • Loading branch information
AlynxZhou committed Sep 26, 2024
1 parent 85f2b8b commit de508cc
Showing 1 changed file with 50 additions and 96 deletions.
146 changes: 50 additions & 96 deletions src/common/imageio.c
Original file line number Diff line number Diff line change
Expand Up @@ -794,107 +794,61 @@ int dt_imageio_export_with_flags(const int32_t imgid, const char *filename,
// find output color profile for this image:
int sRGB = (icc_type == DT_COLORSPACE_SRGB);

int width = MAX(format_params->max_width, 0);
int height = MAX(format_params->max_height, 0);
double scale = 1.;

int processed_width = 0;
int processed_height = 0;
int width = 0;
int height = 0;
double _num, _denum;
dt_imageio_resizing_factor_get_and_parsing(&_num, &_denum);
double scale = _num / _denum;
int max_width = format_params->max_width;
int max_height = format_params->max_height;
float origin[] = { 0.0f, 0.0f };

// Set to TRUE if width size is explicitly set by user
gboolean force_width;
// Set to TRUE if height size is explicitly set by user
gboolean force_height;

if(dt_dev_distort_backtransform_plus(&dev, &pipe, 0.f, DT_DEV_TRANSFORM_DIR_ALL, origin, 1))
{
// We resize either by scaling factor or by max size, not both.
if(is_scaling)
{
double _num, _denum;
dt_imageio_resizing_factor_get_and_parsing(&_num, &_denum);
const double scale_factor = _num / _denum;
scale = fmin(scale_factor, 1.);
force_width = TRUE;
force_height = TRUE;
// Setting scale <= 0 means don't resize.
if(scale <= 0)
scale = 1.;
// We don't upscale on exporting.
scale = MIN(scale, 1.);
// Ignore max size and calculate max size via scaling factor.
max_width = round(scale * pipe.processed_width);
max_height = round(scale * pipe.processed_height);
}
else
{
double scalex = 1.;
double scaley = 1.;

if(width == 0)
{
force_width = FALSE;
width = pipe.processed_width;
}
else
{
force_width = TRUE;
scalex = fmin((double)width / (double)pipe.processed_width, 1.);
}

if(height == 0)
{
force_height = FALSE;
height = pipe.processed_height;
}
// Setting max size <= 0 means don't resize.
if(max_width <= 0)
max_width = pipe.processed_width;
if(max_height <= 0)
max_height = pipe.processed_height;
// We don't upscale on exporting.
max_width = MIN(max_width, pipe.processed_width);
max_height = MIN(max_height, pipe.processed_height);
// Keep image ratio even if user does not set max size by ratio.
// We make sure the calculated size will be smaller than user's max size.
if(pipe.processed_width > pipe.processed_height)
max_height = MIN(round(max_width / image_ratio), max_height);
else
{
force_height = TRUE;
scaley = fmin((double)height / (double)pipe.processed_height, 1.);
}

scale = fmin(scalex, scaley);
max_width = MIN(round(max_height * image_ratio), max_width);
// Ignore scaling factor and calculate scaling factor via max size.
// NOTE: This is important because pixelpipe needs scaling factor.
scale = (double)max_width / pipe.processed_width;
}
width = MIN(pipe.processed_width, max_width);
height = MIN(pipe.processed_height, max_height);
}
else
{
// error reading pipeline pieces for distort transforms
goto error;
}

if(force_height && force_width)
{
// width and height both specified by user in pixels
if(pipe.processed_width > pipe.processed_height)
{
processed_width = MIN(round(scale * pipe.processed_width), width);
processed_height = round(processed_width / image_ratio);
}
else if(pipe.processed_width < pipe.processed_height)
{
processed_height = MIN(round(scale * pipe.processed_height), height);
processed_width = round(processed_height * image_ratio);
}
else
{
processed_width = MIN(round(scale * pipe.processed_width), width);
processed_height = MIN(round(scale * pipe.processed_height), height);
}
}
else if(force_width)
{
// width only specified by user in pixels, fluid height
processed_width = MIN(round(scale * pipe.processed_width), width);
processed_height = round(processed_width / image_ratio);
}
else if(force_height)
{
// height only specified by user in pixels, fluid width
processed_height = MIN(round(scale * pipe.processed_height), height);
processed_width = round(processed_height * image_ratio);
}
else
{
// nothing specified by user aka full resolution given cropping, lens and perspective distortions
processed_width = pipe.processed_width;
processed_height = pipe.processed_height;
}

dt_print(DT_DEBUG_IMAGEIO,"[dt_imageio_export] (direct) imgid %d, hq %i, pipe %ix%i, range %ix%i --> size %ix%i / %ix%i - ratio %.5f\n",
imgid, high_quality, pipe.processed_width, pipe.processed_height, format_params->max_width, format_params->max_height,
processed_width, processed_height, width, height, image_ratio);
dt_print(DT_DEBUG_IMAGEIO,"[dt_imageio_export] (direct) imgid %d, hq %i, pipe %ix%i, range %ix%i, scale %.5f --> size %ix%i - ratio %.5f\n",
imgid, high_quality, pipe.processed_width, pipe.processed_height, max_width, max_height, scale,
width, height, image_ratio);


const int bpp = format->bpp(format_params);
Expand All @@ -906,7 +860,7 @@ int dt_imageio_export_with_flags(const int32_t imgid, const char *filename,
* if high quality processing was requested, downsampling will be done
* at the very end of the pipe (just before border and watermark)
*/
dt_dev_pixelpipe_process_no_gamma(&pipe, &dev, 0, 0, processed_width, processed_height, scale);
dt_dev_pixelpipe_process_no_gamma(&pipe, &dev, 0, 0, width, height, scale);
}
else
{
Expand All @@ -932,9 +886,9 @@ int dt_imageio_export_with_flags(const int32_t imgid, const char *filename,

// do the processing (8-bit with special treatment, to make sure we can use openmp further down):
if(bpp == 8)
dt_dev_pixelpipe_process(&pipe, &dev, 0, 0, processed_width, processed_height, scale);
dt_dev_pixelpipe_process(&pipe, &dev, 0, 0, width, height, scale);
else
dt_dev_pixelpipe_process_no_gamma(&pipe, &dev, 0, 0, processed_width, processed_height, scale);
dt_dev_pixelpipe_process_no_gamma(&pipe, &dev, 0, 0, width, height, scale);

if(finalscale) finalscale->enabled = 1;
}
Expand All @@ -956,7 +910,7 @@ int dt_imageio_export_with_flags(const int32_t imgid, const char *filename,
if(high_quality)
{
const float *const inbuf = (float *)outbuf;
for(size_t k = 0; k < (size_t)processed_width * processed_height; k++)
for(size_t k = 0; k < (size_t)width * height; k++)
{
// convert in place, this is unfortunately very serial..
const uint8_t r = roundf(CLAMP(inbuf[4 * k + 2] * 0xff, 0, 0xff));
Expand All @@ -975,7 +929,7 @@ int dt_imageio_export_with_flags(const int32_t imgid, const char *filename,
if(high_quality)
{
const float *const inbuf = (float *)outbuf;
for(size_t k = 0; k < (size_t)processed_width * processed_height; k++)
for(size_t k = 0; k < (size_t)width * height; k++)
{
// convert in place, this is unfortunately very serial..
const uint8_t r = roundf(CLAMP(inbuf[4 * k + 0] * 0xff, 0, 0xff));
Expand All @@ -991,11 +945,11 @@ int dt_imageio_export_with_flags(const int32_t imgid, const char *filename,
uint8_t *const buf8 = pipe.backbuf;
#ifdef _OPENMP
#pragma omp parallel for default(none) \
dt_omp_firstprivate(processed_width, processed_height, buf8) \
dt_omp_firstprivate(width, height, buf8) \
schedule(static)
#endif
// just flip byte order
for(size_t k = 0; k < (size_t)processed_width * processed_height; k++)
for(size_t k = 0; k < (size_t)width * height; k++)
{
uint8_t tmp = buf8[4 * k + 0];
buf8[4 * k + 0] = buf8[4 * k + 2];
Expand All @@ -1009,17 +963,17 @@ int dt_imageio_export_with_flags(const int32_t imgid, const char *filename,
// uint16_t per color channel
float *buff = (float *)outbuf;
uint16_t *buf16 = (uint16_t *)outbuf;
for(int y = 0; y < processed_height; y++)
for(int x = 0; x < processed_width; x++)
for(int y = 0; y < height; y++)
for(int x = 0; x < width; x++)
{
const size_t k = (size_t)processed_width * y + x;
const size_t k = (size_t)width * y + x;
for(int i = 0; i < 3; i++) buf16[4 * k + i] = roundf(CLAMP(buff[4 * k + i] * 0xffff, 0, 0xffff));
}
}
// else output float, no further harm done to the pixels :)

format_params->width = processed_width;
format_params->height = processed_height;
format_params->width = width;
format_params->height = height;

if(!ignore_exif)
{
Expand All @@ -1031,7 +985,7 @@ int dt_imageio_export_with_flags(const int32_t imgid, const char *filename,
gboolean from_cache = TRUE;
dt_image_full_path(imgid, pathname, sizeof(pathname), &from_cache, __FUNCTION__);
// last param is dng mode, it's false here
length = dt_exif_read_blob(&exif_profile, pathname, imgid, sRGB, processed_width, processed_height, 0);
length = dt_exif_read_blob(&exif_profile, pathname, imgid, sRGB, width, height, 0);

res = format->write_image(format_params, filename, outbuf, icc_type, icc_filename, exif_profile, length, imgid,
num, total, &pipe, export_masks);
Expand Down

0 comments on commit de508cc

Please sign in to comment.