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

Simpified resizing in export #368

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
157 changes: 66 additions & 91 deletions src/common/imageio.c
Original file line number Diff line number Diff line change
Expand Up @@ -794,109 +794,84 @@ 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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is used to parse the user param only when defining export size as a sub-multiple of the original size (aka is_scaling is TRUE, that case is fairly recent and was hacked in-place). It shouldn't be used here in the general, unconditionnal case, especially since it reads a config key that's irrelevant when defining export size by pixel count (or mm/inches).

double scale = _num / _denum;
// Well, they are not "max" size actually, more like "requested" size.
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.);
processed_height = pipe.processed_height * scale;
processed_width = pipe.processed_width * scale;
force_width = TRUE;
force_height = TRUE;
// Setting scale <= 0 means we 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)
// Setting both max dimensions <= 0 means we don't resize.
// Setting one max dimension <= 0 means we calculate it via image ratio.
if(max_width <= 0 && max_height <= 0)
{
force_width = FALSE;
width = pipe.processed_width;
max_width = pipe.processed_width;
max_height = pipe.processed_height;
}
else
else if(max_width <= 0)
{
force_width = TRUE;
scalex = fmin((double)width / (double)pipe.processed_width, 1.);
max_width = round(max_height * image_ratio);
}

if(height == 0)
else if(max_height <= 0)
{
force_height = FALSE;
height = pipe.processed_height;
max_height = round(max_width / image_ratio);
}
else
// If user sets max size in wrong orientation, we correct it.
gboolean is_landscape = image_ratio >= 1;
gboolean is_max_landscape = max_width >= max_height;
if(is_max_landscape != is_landscape)
{
force_height = TRUE;
scaley = fmin((double)height / (double)pipe.processed_height, 1.);
int temp = max_width;
max_width = max_height;
max_height = temp;
}

scale = fmin(scalex, scaley);
// If the ratios are different, we respect the image ratio and use the
// longer max dimension to calculate the other max dimension.
if(fabs((double)max_width / max_height - image_ratio) > 0.001)
{
if(is_landscape)
max_height = round(max_width / image_ratio);
else
max_width = round(max_height * image_ratio);
}
// We don't upscale on exporting.
max_width = MIN(max_width, pipe.processed_width);
max_height = MIN(max_height, pipe.processed_height);
// 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 @@ -908,7 +883,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 @@ -934,9 +909,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 @@ -958,7 +933,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 @@ -977,7 +952,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 @@ -993,11 +968,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 @@ -1011,17 +986,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 @@ -1033,7 +1008,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