Calculate required image and frame sizes

* Refactor simpleprocess.cc to be more clear on resize dimensions
* Implement image and frame sizing calculations
* Resizes the image based on adjusted framing calculations
* Missing functionality to draw border around image after resizing
This commit is contained in:
Daniel Gao 2024-11-11 14:03:22 -05:00
parent b88ad569c2
commit a96dc4cdde
3 changed files with 687 additions and 24 deletions

View File

@ -213,12 +213,31 @@ enum class BlurType {
void sharpening(LabImage* lab, const procparams::SharpeningParams &sharpenParam, bool showMask = false);
void sharpeningcam(CieImage* ncie, float** buffer, bool showMask = false);
void transform(Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const FramesMetaData *metadata, int rawRotationDeg, bool fullImage, bool useOriginalBuffer = false);
float resizeScale(const procparams::ProcParams* params, int fw, int fh, int &imw, int &imh);
void lab2monitorRgb(LabImage* lab, Image8* image);
double resizeScale(const procparams::ProcParams* params, int fw, int fh, int &imw, int &imh);
void resize(Imagefloat* src, Imagefloat* dst, float dScale);
void Lanczos(const LabImage* src, LabImage* dst, float scale);
void Lanczos(const Imagefloat* src, Imagefloat* dst, float scale);
struct FramingArgs {
const procparams::ProcParams* params = nullptr;
int cropWidth = 0;
int cropHeight = 0;
int resizeWidth = 0;
int resizeHeight = 0;
double resizeScale = 1.0f;
};
struct FramingData {
bool enabled = false;
int imgWidth = 0;
int imgHeight = 0;
double scale = 1.0;
int framedWidth = 0;
int framedHeight = 0;
};
FramingData framing(const FramingArgs& args) const;
void deconvsharpening(float** luminance, float** buffer, const float* const * blend, int W, int H, const procparams::SharpeningParams &sharpenParam, double Scale);
void deconvsharpeningloc(float** luminance, float** buffer, int W, int H, float** loctemp, int damp, double radi, int ite, int amo, int contrast, double blurrad, int sk);

View File

@ -33,6 +33,582 @@
# include <iostream>
#endif
namespace
{
using ProcParams = rtengine::procparams::ProcParams;
using FramingParams = rtengine::procparams::FramingParams;
using Basis = FramingParams::Basis;
using BorderSizing = FramingParams::BorderSizing;
using FramingMethod = FramingParams::FramingMethod;
enum class Orientation { LANDSCAPE, PORTRAIT };
enum class Side { WIDTH, HEIGHT };
struct Dimensions {
double width;
double height;
Dimensions() : width(0), height(0) {}
Dimensions(double w, double h) : width(w), height(h) {}
bool isDegenerate() const { return width <= 0.0 || height <= 0.0; }
double aspectRatio() const {
if (isDegenerate()) return 1.0;
else return static_cast<double>(width) / static_cast<double>(height);
}
Orientation orient() const {
return width >= height ? Orientation::LANDSCAPE : Orientation::PORTRAIT;
}
bool inside(const Dimensions& other) const {
return width <= other.width && height <= other.height;
}
void rotate(Orientation newOrient) {
if (newOrient != orient()) {
std::swap(width, height);
}
}
bool operator==(const Dimensions& other) const {
return width == other.width && height == other.height;
}
bool operator!=(const Dimensions& other) const { return !(*this == other); }
Dimensions intersect(const Dimensions& other) const {
return Dimensions(std::min(width, other.width), std::min(height, other.height));
}
void debug(const char* prefix) const {
printf("%s w=%f h=%f ar=%f\n", prefix, width, height, aspectRatio());
}
};
struct ResizeArgs {
Dimensions size;
double scale = 1.0;
ResizeArgs(const Dimensions& aSize, double aScale) : size(aSize), scale(aScale) {}
};
class Framing {
public:
Framing(const ProcParams& params, int fullWidth, int fullHeight);
ResizeArgs adjustResizeForFraming(const ResizeArgs& resize) const;
Dimensions computeFramedSize(const Dimensions& imgSize) const;
private:
Dimensions clampResize(const Dimensions& imgSize, const Dimensions& bounds) const;
ResizeArgs adjustResize(const ResizeArgs& resize, const Dimensions& newSize) const;
Dimensions computeRelativeImageBBoxInFrame(const Dimensions& imgSize,
const Dimensions& framedSize) const;
ResizeArgs resizeForFixedFrame(const ResizeArgs& resize) const;
ResizeArgs resizeForBBox(const ResizeArgs& resize) const;
Dimensions computeSizeWithBorders(const Dimensions& imgSize) const;
const ProcParams& allParams;
const FramingParams framing; // Make a copy to sanitize inputs
const Dimensions postCropImageSize;
const Dimensions maxUpscalingBBox;
};
// Downscaling limit is 32x32px
constexpr double MIN_DOWNSCALE_PX = 32.0;
// Upscaling limit is 16x image size
constexpr double MAX_UPSCALE_FACTOR = 16.0;
int computeSize(int dim, double scale)
{
return static_cast<int>(static_cast<double>(dim) * scale + 0.5);
}
Orientation orient(const FramingParams& params, const Dimensions& imgSize) {
switch (params.orientation) {
case FramingParams::Orientation::LANDSCAPE:
return Orientation::LANDSCAPE;
case FramingParams::Orientation::PORTRAIT:
return Orientation::PORTRAIT;
case FramingParams::Orientation::AS_IMAGE:
default:
return imgSize.orient();
}
}
double flipAspectRatioByOrientation(double aspectRatio, Orientation orient)
{
switch (orient) {
case Orientation::LANDSCAPE:
return aspectRatio >= 1.0 ? aspectRatio : 1.0 / aspectRatio;
case Orientation::PORTRAIT:
return aspectRatio <= 1.0 ? aspectRatio : 1.0 / aspectRatio;
default:
return aspectRatio;
}
}
Side autoPickBasis(const FramingParams& params, const Dimensions& imgSize)
{
if (imgSize.isDegenerate()) {
if (imgSize.width <= 0) return Side::HEIGHT;
else return Side::WIDTH;
}
Orientation imgOrient = imgSize.orient();
double imgAspectRatio = imgSize.aspectRatio();
Orientation frameOrient = orient(params, imgSize);
double frameAspectRatio = flipAspectRatioByOrientation(params.aspectRatio, frameOrient);
if (frameOrient == imgOrient) {
// Pick the more constrained side (i.e. hits 0 border width first)
return imgAspectRatio >= frameAspectRatio ? Side::WIDTH : Side::HEIGHT;
} else if (imgOrient == Orientation::LANDSCAPE) {
// Image in landscape, frame in portrait
return Side::WIDTH;
} else {
// Image in portrait, frame in landscape
return Side::HEIGHT;
}
}
Side pickReferenceSide(const FramingParams& params, const Dimensions& imgSize)
{
switch (params.basis) {
case Basis::WIDTH:
return Side::WIDTH;
case Basis::HEIGHT:
return Side::HEIGHT;
case Basis::LONG:
return imgSize.width >= imgSize.height ? Side::WIDTH : Side::HEIGHT;
case Basis::SHORT:
return imgSize.width <= imgSize.height ? Side::WIDTH : Side::HEIGHT;
case Basis::AUTO:
default:
return autoPickBasis(params, imgSize);
}
}
constexpr bool INSIDE_BBOX = true;
constexpr bool OUTSIDE_BBOX = false;
Dimensions clampToBBox(const Dimensions& img, const Dimensions& bbox, bool clampInside) {
double widthScale = 1.0;
double heightScale = 1.0;
if (bbox.width > 0) {
widthScale = img.width / bbox.width;
}
if (bbox.height > 0) {
heightScale = img.height / bbox.height;
}
Dimensions newSize = img;
if (clampInside) {
// If a side exceeds the bbox, scale down to bbox
double scale = std::max(widthScale, heightScale);
if (scale > 1.0) {
if (widthScale >= heightScale) {
newSize.width = bbox.width;
newSize.height = img.height / widthScale;
} else {
newSize.width = img.width / heightScale;
newSize.height = bbox.height;
}
}
} else {
// If a side is within the bbox, scale up to bbox
double scale = std::min(widthScale, heightScale);
if (scale < 1.0) {
if (widthScale <= heightScale) {
newSize.width = bbox.width;
newSize.height = img.height / widthScale;
} else {
newSize.width = img.width / heightScale;
newSize.height = bbox.height;
}
}
}
return newSize;
}
Dimensions downscaleToTouchBBox(const Dimensions& img, const Dimensions& bbox) {
if (bbox.isDegenerate()) return Dimensions(0, 0);
if (!bbox.inside(img)) return img;
double widthScale = img.width / bbox.width;
double heightScale = img.height / bbox.height;
Dimensions downscaled;
if (widthScale <= heightScale) {
downscaled.width = bbox.width;
downscaled.height = img.height / widthScale;
} else {
downscaled.height = bbox.height;
downscaled.width = img.width / heightScale;
}
return downscaled;
}
Dimensions upscaleToBBox(const Dimensions& img, const Dimensions& bbox) {
if (bbox.isDegenerate()) return Dimensions(0, 0);
if (!img.inside(bbox)) return img;
double widthScale = img.width / bbox.width;
double heightScale = img.height / bbox.height;
Dimensions upscaled;
if (widthScale >= heightScale) {
upscaled.width = bbox.width;
upscaled.height = img.height / widthScale;
} else {
upscaled.height = bbox.height;
upscaled.width = img.width / heightScale;
}
return upscaled;
}
double orientAspectRatio(const FramingParams& framing, const Dimensions& imgSize)
{
double aspectRatio = framing.aspectRatio;
Orientation borderOrient = orient(framing, imgSize);
if ((borderOrient == Orientation::PORTRAIT && aspectRatio > 1.0) ||
(borderOrient == Orientation::LANDSCAPE && aspectRatio < 1.0)) {
aspectRatio = 1.0 / aspectRatio;
}
return aspectRatio;
}
Dimensions fromAspectRatio(const Dimensions& size, double aspectRatio)
{
Dimensions result;
if (aspectRatio >= 1.0) {
result.height = size.height;
result.width = result.height * aspectRatio;
} else {
result.width = size.width;
result.height = result.width / aspectRatio;
}
return result;
}
FramingParams sanitize(const FramingParams& dirty)
{
FramingParams framing = dirty;
framing.framedWidth = std::max(static_cast<int>(MIN_DOWNSCALE_PX), framing.framedWidth);
framing.framedHeight = std::max(static_cast<int>(MIN_DOWNSCALE_PX), framing.framedHeight);
framing.relativeBorderSize = std::max(0.0, std::min(1.0, framing.relativeBorderSize));
framing.minWidth = std::max(0, framing.minWidth);
framing.minHeight = std::max(0, framing.minHeight);
framing.absWidth = std::max(0, framing.absWidth);
framing.absHeight = std::max(0, framing.absHeight);
return framing;
}
Framing::Framing(const ProcParams& params, int fullWidth, int fullHeight) :
allParams(params),
framing(sanitize(params.framing)),
postCropImageSize(params.crop.enabled ?
Dimensions(params.crop.w, params.crop.h) :
Dimensions(fullWidth, fullHeight)),
maxUpscalingBBox(Dimensions(
computeSize(postCropImageSize.width, MAX_UPSCALE_FACTOR),
computeSize(postCropImageSize.height, MAX_UPSCALE_FACTOR)))
{
}
Dimensions Framing::clampResize(const Dimensions& imgSize, const Dimensions& bounds) const
{
// Don't adjust above upscaling limit.
//
// If the upscaling limit is contained inside the target bounds, scale
// down the bounds to outside the upscaling limit. This is needed since
// scaling the bounds to fit inside the upscaling bbox may artificially
// reduce the upscaling limit due to aspect ratio differences.
Dimensions clampedBounds = maxUpscalingBBox.inside(bounds) ?
downscaleToTouchBBox(bounds, maxUpscalingBBox) :
clampToBBox(bounds, maxUpscalingBBox, INSIDE_BBOX);
if (!imgSize.inside(clampedBounds)) {
// Downscale large images to fit inside bounds (only if above limit)
Dimensions minSize(MIN_DOWNSCALE_PX, MIN_DOWNSCALE_PX);
if (!minSize.inside(imgSize)) {
// Skip images below downscaling limit
return imgSize;
} else if (!minSize.inside(clampedBounds)) {
// Go as small as possible without exceeding downscaling limit
return downscaleToTouchBBox(imgSize, minSize);
} else {
// Downscale large images to fit inside bounds
return clampToBBox(imgSize, clampedBounds, INSIDE_BBOX);
}
} else {
// Consider upscaling...
if (!framing.allowUpscaling ||
imgSize.width == clampedBounds.width ||
imgSize.height == clampedBounds.height) {
return imgSize;
} else {
return upscaleToBBox(imgSize, clampedBounds);
}
}
}
ResizeArgs Framing::adjustResize(const ResizeArgs& resize, const Dimensions& bbox) const
{
Dimensions newSize = clampResize(resize.size, bbox);
double newScale = newSize.width / resize.size.width;
return ResizeArgs(newSize, newScale);
}
Dimensions Framing::computeRelativeImageBBoxInFrame(const Dimensions& imgSize,
const Dimensions& framedSize) const
{
if (imgSize.isDegenerate() || framedSize.isDegenerate()) {
return Dimensions(0, 0);
}
double imgAspectRatio = imgSize.aspectRatio();
// Compute the width:height ratio of the border size for the requested
// image size and framed size.
//
// We do this by creating a dummy image. Then, scale the framed size to be
// larger than the dummy image such that there is a non-zero difference for
// widths and heights.
double borderAspectRatio = [&]()
{
Dimensions fakeImage = fromAspectRatio(framedSize, imgAspectRatio);
Dimensions bigFrame = clampToBBox(framedSize, fakeImage, OUTSIDE_BBOX);
bigFrame.width *= 2.0;
bigFrame.height *= 2.0;
Dimensions diff(bigFrame.width - fakeImage.width, bigFrame.height - fakeImage.height);
return diff.aspectRatio();
}();
Side side = pickReferenceSide(framing, imgSize);
double scale = framing.relativeBorderSize;
// Compute image and border lengths on basis side
double frameBasis = side == Side::WIDTH ? framedSize.width : framedSize.height;
double frameOther = side == Side::WIDTH ? framedSize.height : framedSize.width;
// frame_len = img_len + 2 * scale * img_len = (1 + 2 * scale) * img_len
double imgFrameScale = (1.0 + 2.0 * scale);
double imgBasis = frameBasis / imgFrameScale;
// border_len = (scale * img_len)
// = frame_len / (1 / scale + 2)
// = frame_len * scale / (1 + 2 * scale)
double borderBasis = frameBasis * scale / imgFrameScale;
// Compute image and border lengths for the non-basis side.
double imgBasisToOther = side == Side::WIDTH ? 1.0 / imgAspectRatio : imgAspectRatio;
double borderBasisToOther = side == Side::WIDTH ? 1.0 / borderAspectRatio : borderAspectRatio;
double imgOther = imgBasis * imgBasisToOther;
double borderOther = borderBasis * borderBasisToOther;
double maxImageBasis = frameBasis;
double maxImageOther = frameOther;
if (framing.minSizeEnabled) {
double minBorderBasis = static_cast<double>(
side == Side::WIDTH ? framing.minWidth : framing.minHeight);
double minBorderOther = static_cast<double>(
side == Side::WIDTH ? framing.minHeight : framing.minWidth);
if (borderOther < minBorderOther) {
maxImageOther = std::floor(frameOther - 2.0 * minBorderOther);
}
if (borderBasis < minBorderBasis) {
maxImageBasis = std::floor(frameBasis - 2.0 * minBorderBasis);
}
}
if (imgOther > maxImageOther) {
imgOther = maxImageOther;
imgBasis = imgOther / imgBasisToOther;
}
if (imgBasis > maxImageBasis) {
imgBasis = maxImageBasis;
imgOther = imgBasis * imgBasisToOther;
}
if (side == Side::WIDTH) {
return Dimensions(imgBasis, imgOther);
} else {
return Dimensions(imgOther, imgBasis);
}
}
ResizeArgs Framing::adjustResizeForFraming(const ResizeArgs& resize) const
{
if (!framing.enabled) return resize;
switch (framing.framingMethod) {
case FramingMethod::BBOX:
return resizeForBBox(resize);
case FramingMethod::FIXED_SIZE:
return resizeForFixedFrame(resize);
case FramingMethod::STANDARD:
default:
// No limits on framed size so do nothing
return resize;
}
}
ResizeArgs Framing::resizeForFixedFrame(const ResizeArgs& args) const
{
double framedWidth = framing.framedWidth;
double framedHeight = framing.framedHeight;
Dimensions frameSize(framedWidth, framedHeight);
Dimensions bbox;
if (framing.borderSizingMethod == BorderSizing::FIXED_SIZE) {
auto length = [](double frame, double border) {
return std::max(0.0, frame - 2.0 * border);
};
bbox = {
length(framedWidth, framing.absWidth),
length(framedHeight, framing.absHeight)
};
} else {
bbox = computeRelativeImageBBoxInFrame(args.size, frameSize);
}
return adjustResize(args, bbox);
}
ResizeArgs Framing::resizeForBBox(const ResizeArgs& args) const
{
Dimensions boundary(framing.framedWidth, framing.framedHeight);
Dimensions bbox;
if (framing.borderSizingMethod == BorderSizing::FIXED_SIZE) {
auto length = [](double frame, double border) {
return std::max(0.0, frame - 2.0 * border);
};
bbox = {
length(boundary.width, framing.absWidth),
length(boundary.height, framing.absHeight)
};
} else {
// For the requested aspect ratio, it must fit inside the requested
// bounding box
double aspectRatio = orientAspectRatio(framing, args.size);
Dimensions ratioBBox = fromAspectRatio(boundary, aspectRatio);
ratioBBox = clampToBBox(ratioBBox, boundary, INSIDE_BBOX);
// Now we have the true max bounds for the framed image. Determine how the
// original image fits inside these bounds. This process is the same as
// in the fixed frame mode.
bbox = computeRelativeImageBBoxInFrame(args.size, ratioBBox);
}
return adjustResize(args, bbox);
}
Dimensions Framing::computeFramedSize(const Dimensions& imgSize) const
{
if (!framing.enabled) return imgSize;
// For constrained frame sizes, check if the image size (without frame)
// exceeds the constrained size. This indicates that a combination of
// parameters caused the downscaling limit to be hit. In which case,
// just return the original image size (i.e. don't insert border).
//
// If the image fits the constrained size, assume that previous resize
// calculations were correct and trim off any excess borders. The excess
// may be from rounding errors or hitting some downscaling limit.
switch (framing.framingMethod) {
case FramingMethod::BBOX:
{
Dimensions fixed(framing.framedWidth, framing.framedHeight);
if (imgSize.inside(fixed)) {
Dimensions framedSize = computeSizeWithBorders(imgSize);
return clampToBBox(framedSize, fixed, INSIDE_BBOX);
} else {
return imgSize;
}
}
case FramingMethod::FIXED_SIZE:
{
Dimensions fixed(framing.framedWidth, framing.framedHeight);
return imgSize.inside(fixed) ? fixed : imgSize;
}
case FramingMethod::STANDARD:
default:
return computeSizeWithBorders(imgSize);
}
}
Dimensions Framing::computeSizeWithBorders(const Dimensions& imgSize) const
{
auto length = [](double img, double border) {
return img + 2.0 * border;
};
auto evalBorder = [](double side, double scale, double img) {
double otherSide = side * scale;
double totalBorder = otherSide - img;
return std::max(totalBorder, 0.0) * 0.5;
};
if (framing.borderSizingMethod == BorderSizing::FIXED_SIZE) {
return Dimensions(length(imgSize.width, framing.absWidth),
length(imgSize.height, framing.absHeight));
}
Side side = pickReferenceSide(framing, imgSize);
double aspectRatio = orientAspectRatio(framing, imgSize);
// Compute the size with borders given the requested reference side length
// and aspect ratio
Dimensions borderSize;
double scale = framing.relativeBorderSize;
if (side == Side::WIDTH) {
borderSize.width = imgSize.width * scale;
double totalWidth = length(imgSize.width, borderSize.width);
borderSize.height = evalBorder(totalWidth, 1.0 / aspectRatio, imgSize.height);
} else {
borderSize.height = imgSize.height * scale;
double totalHeight = length(imgSize.height, borderSize.height);
borderSize.width = evalBorder(totalHeight, aspectRatio, imgSize.width);
}
if (framing.minSizeEnabled) {
Dimensions minSize(static_cast<double>(framing.minWidth),
static_cast<double>(framing.minHeight));
borderSize = clampToBBox(borderSize, minSize, OUTSIDE_BBOX);
}
Dimensions framedSize(length(imgSize.width, borderSize.width),
length(imgSize.height, borderSize.height));
// Check if the computed frame size satsifies the requested aspect ratio
// without cutting off the original image. If the image is cut off, use
// the smallest frame that preserves the original image and still
// satisfies the requested aspect ratio.
Dimensions minFramedSize = fromAspectRatio(imgSize, aspectRatio);
if (framing.minSizeEnabled) {
Dimensions limit = imgSize;
limit.width += 2.0 * framing.minWidth;
limit.height += 2.0 * framing.minHeight;
minFramedSize = clampToBBox(minFramedSize, limit, OUTSIDE_BBOX);
}
if (minFramedSize.inside(framedSize)) {
return framedSize;
} else {
return minFramedSize;
}
}
} // namespace
namespace rtengine
{
@ -333,7 +909,7 @@ void ImProcFunctions::Lanczos (const LabImage* src, LabImage* dst, float scale)
delete[] wwh;
}
float ImProcFunctions::resizeScale (const ProcParams* params, int fw, int fh, int &imw, int &imh)
double ImProcFunctions::resizeScale (const ProcParams* params, int fw, int fh, int &imw, int &imh)
{
imw = fw;
imh = fh;
@ -406,10 +982,6 @@ float ImProcFunctions::resizeScale (const ProcParams* params, int fw, int fh, in
break;
}
if (fabs (dScale - 1.0) <= 1e-5) {
return 1.0;
}
if (params->crop.enabled && params->resize.appliesTo == "Full image") {
imw = params->crop.w;
imh = params->crop.h;
@ -418,9 +990,13 @@ float ImProcFunctions::resizeScale (const ProcParams* params, int fw, int fh, in
imh = refh;
}
imw = (int) ( (double)imw * dScale + 0.5 );
imh = (int) ( (double)imh * dScale + 0.5 );
return (float)dScale;
if (fabs (dScale - 1.0) <= 1e-5) {
return 1.0;
} else {
imw = computeSize(imw, dScale);
imh = computeSize(imh, dScale);
return dScale;
}
}
void ImProcFunctions::resize (Imagefloat* src, Imagefloat* dst, float dScale)
@ -458,4 +1034,35 @@ void ImProcFunctions::resize (Imagefloat* src, Imagefloat* dst, float dScale)
#endif
}
ImProcFunctions::FramingData ImProcFunctions::framing(const FramingArgs& args) const
{
FramingData result;
result.enabled = false;
result.imgWidth = args.resizeWidth;
result.imgHeight = args.resizeHeight;
result.scale = args.resizeScale;
result.framedWidth = args.resizeWidth;
result.framedHeight = args.resizeHeight;
if (!args.params || !args.params->resize.enabled) return result;
if (!args.params->framing.enabled) return result;
// For these calculations, try to keep everything as doubles to minimize
// rounding errors from intermediate steps!
Framing util(*params, args.cropWidth, args.cropHeight);
ResizeArgs resize(Dimensions(args.resizeWidth, args.resizeHeight), args.resizeScale);
ResizeArgs adjusted = util.adjustResizeForFraming(resize);
Dimensions framedSize = util.computeFramedSize(adjusted.size);
result.enabled = true;
result.imgWidth = std::round(adjusted.size.width);
result.imgHeight = std::round(adjusted.size.height);
result.scale = adjusted.scale;
result.framedWidth = std::round(framedSize.width);
result.framedHeight = std::round(framedSize.height);
return result;
}
} // namespace rtengine

View File

@ -1893,20 +1893,50 @@ private:
pl->setProgress(0.60);
}
int imw, imh;
double tmpScale = ipf.resizeScale(&params, fw, fh, imw, imh);
bool labResize = params.resize.enabled && params.resize.method != "Nearest" && (tmpScale != 1.0 || params.prsharpening.enabled);
LabImage *tmplab;
// crop and convert to rgb16
int cx = 0, cy = 0, cw = labView->W, ch = labView->H;
if (params.crop.enabled) {
cx = params.crop.x;
cy = params.crop.y;
cw = params.crop.w;
ch = params.crop.h;
}
ImProcFunctions::FramingArgs framingArgs;
framingArgs.params = &params;
framingArgs.cropWidth = cw;
framingArgs.cropHeight = ch;
{
int imw, imh;
double tmpScale = ipf.resizeScale(&params, fw, fh, imw, imh);
framingArgs.resizeWidth = imw;
framingArgs.resizeHeight = imh;
framingArgs.resizeScale = tmpScale;
// If upscaling is not permitted, keep original sizing
if ((cw < imw || ch < imh) && !params.resize.allowUpscaling) {
framingArgs.resizeWidth = cw;
framingArgs.resizeHeight = ch;
framingArgs.resizeScale = 1.0;
}
}
// If framing is not enabled, resize values simply pass through to output
ImProcFunctions::FramingData framingData = ipf.framing(framingArgs);
printf("Framing Parameters (enabled=%s)\n", framingData.enabled ? "yes" : "no");
printf(" Crop: w=%d h=%d\n", cw, ch);
printf(" Original resize: w=%d h=%d s=%f\n",
framingArgs.resizeWidth, framingArgs.resizeHeight, framingArgs.resizeScale);
printf(" Framed image size: w=%d h=%d s=%f\n",
framingData.imgWidth, framingData.imgHeight, framingData.scale);
printf(" Total size: w=%d h=%d\n",
framingData.framedWidth, framingData.framedHeight);
bool labResize = params.resize.enabled && params.resize.method != "Nearest" &&
(framingData.scale != 1.0 || params.prsharpening.enabled || framingData.enabled);
LabImage *tmplab = nullptr;
if (params.crop.enabled) {
if (labResize) { // crop lab data
tmplab = new LabImage(cw, ch);
@ -1926,11 +1956,12 @@ private:
}
if (labResize) { // resize lab data
if ((labView->W != imw || labView->H != imh) &&
(params.resize.allowUpscaling || (labView->W >= imw && labView->H >= imh))) {
int imw = framingData.imgWidth;
int imh = framingData.imgHeight;
if (labView->W != imw || labView->H != imh) {
// resize image
tmplab = new LabImage(imw, imh);
ipf.Lanczos(labView, tmplab, tmpScale);
ipf.Lanczos(labView, tmplab, framingData.scale);
delete labView;
labView = tmplab;
}
@ -1984,14 +2015,20 @@ private:
pl->setProgress(0.70);
}
if (tmpScale != 1.0 && params.resize.method == "Nearest" &&
(params.resize.allowUpscaling || (readyImg->getWidth() >= imw && readyImg->getHeight() >= imh))) { // resize rgb data (gamma applied)
Imagefloat* tempImage = new Imagefloat(imw, imh);
ipf.resize(readyImg, tempImage, tmpScale);
delete readyImg;
readyImg = tempImage;
if (framingData.scale != 1.0 && params.resize.method == "Nearest") {
int imw = framingData.imgWidth;
int imh = framingData.imgHeight;
if (readyImg->getWidth() != imw || readyImg->getHeight() != imh) {
// resize rgb data (gamma applied)
Imagefloat* tempImage = new Imagefloat(imw, imh);
ipf.resize(readyImg, tempImage, framingData.scale);
delete readyImg;
readyImg = tempImage;
}
}
// TODO: Blit framing border + image
Exiv2Metadata info(imgsrc->getFileName());
switch (params.metadata.mode) {