Commit ff98145d1 for imagemagick.org
commit ff98145d10eb3baa1afed201c0090dc0e43b1bde
Author: Greg B <64932474+gregbenz@users.noreply.github.com>
Date: Tue Sep 8 18:49:39 2026 -0500
Compose gain-map crop transforms before rounding (#8948)
diff --git a/coders/uhdr.c b/coders/uhdr.c
index d590f75a1..93823cee4 100644
--- a/coders/uhdr.c
+++ b/coders/uhdr.c
@@ -736,6 +736,249 @@ static MagickBooleanType IsGainMapBaseGeometry(const size_t base_columns,
return(MagickTrue);
}
+typedef struct _UltraHDRGainMapTransformState
+{
+ size_t
+ columns,
+ rows,
+ source_columns,
+ source_rows;
+
+ ssize_t
+ origin_x,
+ origin_y;
+
+ int
+ basis_x_x,
+ basis_x_y,
+ basis_y_x,
+ basis_y_y;
+} UltraHDRGainMapTransformState;
+
+typedef struct _UltraHDRGainMapOrientation
+{
+ int
+ origin_columns,
+ origin_rows,
+ basis_x_columns,
+ basis_x_rows,
+ basis_y_columns,
+ basis_y_rows,
+ swaps;
+} UltraHDRGainMapOrientation;
+
+static const UltraHDRGainMapOrientation
+ gainmap_orientations[7] =
+ {
+ { 0, 1, 0,-1, 1, 0, 1 }, /* rotate 90 degrees clockwise */
+ { 1, 1,-1, 0, 0,-1, 0 }, /* rotate 180 degrees */
+ { 1, 0, 0, 1,-1, 0, 1 }, /* rotate 270 degrees clockwise */
+ { 0, 1, 1, 0, 0,-1, 0 }, /* flip */
+ { 1, 0,-1, 0, 0, 1, 0 }, /* flop */
+ { 0, 0, 0, 1, 1, 0, 1 }, /* transpose */
+ { 1, 1, 0,-1,-1, 0, 1 } /* transverse */
+ };
+
+static void InitializeGainMapTransformState(
+ UltraHDRGainMapTransformState *state,const size_t columns,
+ const size_t rows)
+{
+ state->columns=columns;
+ state->rows=rows;
+ state->source_columns=columns;
+ state->source_rows=rows;
+ state->origin_x=0;
+ state->origin_y=0;
+ state->basis_x_x=1;
+ state->basis_x_y=0;
+ state->basis_y_x=0;
+ state->basis_y_y=1;
+}
+
+static MagickBooleanType AddGainMapTransformOffset(const ssize_t origin,
+ const size_t offset,const int direction,ssize_t *result)
+{
+ if (offset > (size_t) MAGICK_SSIZE_MAX)
+ return(MagickFalse);
+ if (direction == 0)
+ {
+ *result=origin;
+ return(MagickTrue);
+ }
+ if (direction > 0)
+ {
+ if (origin > (MAGICK_SSIZE_MAX-(ssize_t) offset))
+ return(MagickFalse);
+ *result=origin+(ssize_t) offset;
+ }
+ else
+ {
+ if (origin < (MAGICK_SSIZE_MIN+(ssize_t) offset))
+ return(MagickFalse);
+ *result=origin-(ssize_t) offset;
+ }
+ return(MagickTrue);
+}
+
+static MagickBooleanType AddGainMapTransformTerm(const ssize_t origin,
+ const size_t x,const int x_direction,const size_t y,const int y_direction,
+ ssize_t *result)
+{
+ ssize_t
+ value;
+
+ if (AddGainMapTransformOffset(origin,x,x_direction,&value) == MagickFalse)
+ return(MagickFalse);
+ return(AddGainMapTransformOffset(value,y,y_direction,result));
+}
+
+static MagickBooleanType UpdateGainMapTransformCrop(
+ UltraHDRGainMapTransformState *state,const size_t columns,
+ const size_t rows,const ssize_t x,const ssize_t y)
+{
+ ssize_t
+ origin_x,
+ origin_y;
+
+ if ((columns == 0) || (rows == 0) || (columns > state->columns) ||
+ (rows > state->rows) || (x < 0) || (y < 0) ||
+ ((size_t) x > state->columns-columns) ||
+ ((size_t) y > state->rows-rows))
+ return(MagickFalse);
+ if (AddGainMapTransformTerm(state->origin_x,(size_t) x,
+ state->basis_x_x,(size_t) y,state->basis_y_x,&origin_x) == MagickFalse)
+ return(MagickFalse);
+ if (AddGainMapTransformTerm(state->origin_y,(size_t) x,
+ state->basis_x_y,(size_t) y,state->basis_y_y,&origin_y) == MagickFalse)
+ return(MagickFalse);
+ state->origin_x=origin_x;
+ state->origin_y=origin_y;
+ state->columns=columns;
+ state->rows=rows;
+ return(MagickTrue);
+}
+
+static MagickBooleanType UpdateGainMapTransformOrientation(
+ UltraHDRGainMapTransformState *state,const size_t orientation)
+{
+ const UltraHDRGainMapOrientation
+ *transform;
+
+ ssize_t
+ origin_x,
+ origin_y;
+
+ int
+ basis_x_x,
+ basis_x_y,
+ basis_y_x,
+ basis_y_y;
+
+ size_t
+ columns,
+ rows;
+
+ if (orientation >= (sizeof(gainmap_orientations)/
+ sizeof(*gainmap_orientations)))
+ return(MagickFalse);
+ transform=gainmap_orientations+orientation;
+ columns=state->columns;
+ rows=state->rows;
+ if (AddGainMapTransformTerm(state->origin_x,
+ transform->origin_columns > 0 ? columns : 0,
+ transform->origin_columns*state->basis_x_x,
+ transform->origin_rows > 0 ? rows : 0,
+ transform->origin_rows*state->basis_y_x,
+ &origin_x) == MagickFalse ||
+ AddGainMapTransformTerm(state->origin_y,
+ transform->origin_columns > 0 ? columns : 0,
+ transform->origin_columns*state->basis_x_y,
+ transform->origin_rows > 0 ? rows : 0,
+ transform->origin_rows*state->basis_y_y,
+ &origin_y) == MagickFalse)
+ return(MagickFalse);
+ basis_x_x=transform->basis_x_columns*state->basis_x_x+
+ transform->basis_x_rows*state->basis_y_x;
+ basis_x_y=transform->basis_x_columns*state->basis_x_y+
+ transform->basis_x_rows*state->basis_y_y;
+ basis_y_x=transform->basis_y_columns*state->basis_x_x+
+ transform->basis_y_rows*state->basis_y_x;
+ basis_y_y=transform->basis_y_columns*state->basis_x_y+
+ transform->basis_y_rows*state->basis_y_y;
+ if (transform->swaps != 0)
+ Swap(columns,rows);
+ state->origin_x=origin_x;
+ state->origin_y=origin_y;
+ state->basis_x_x=basis_x_x;
+ state->basis_x_y=basis_x_y;
+ state->basis_y_x=basis_y_x;
+ state->basis_y_y=basis_y_y;
+ state->columns=columns;
+ state->rows=rows;
+ return(MagickTrue);
+}
+
+static MagickBooleanType UpdateGainMapTransformStateFromRecord(
+ UltraHDRGainMapTransformState *state,const char *transform)
+{
+ const char
+ *geometry;
+
+ double
+ rotations,
+ source_columns,
+ source_rows;
+
+ size_t
+ orientation;
+
+ int
+ fields;
+
+ if (LocaleNCompare(transform,"rotate ",7) == 0)
+ {
+ geometry=transform+7;
+ fields=sscanf(geometry,"%lfx%lf %lf",&source_columns,
+ &source_rows,&rotations);
+ if (fields != 3)
+ return(MagickFalse);
+ if ((IsNaN(source_columns) != 0) ||
+ (IsNaN(source_rows) != 0) || (IsNaN(rotations) != 0) ||
+ (source_columns > (double) MAGICK_SSIZE_MAX) ||
+ (source_rows > (double) MAGICK_SSIZE_MAX) ||
+ (rotations < 0.0) || (rotations > (double) MAGICK_SSIZE_MAX) ||
+ (rotations != floor(rotations)) ||
+ (IsGainMapBaseGeometry(state->columns,state->rows,source_columns,
+ source_rows) == MagickFalse))
+ return(MagickFalse);
+ orientation=CastDoubleToSizeT(rotations)%4;
+ if (orientation == 0)
+ return(MagickTrue);
+ return(UpdateGainMapTransformOrientation(state,orientation-1));
+ }
+ if (LocaleNCompare(transform,"flip ",5) == 0)
+ orientation=3;
+ else if (LocaleNCompare(transform,"flop ",5) == 0)
+ orientation=4;
+ else if (LocaleNCompare(transform,"transpose ",10) == 0)
+ orientation=5;
+ else if (LocaleNCompare(transform,"transverse ",11) == 0)
+ orientation=6;
+ else
+ return(MagickFalse);
+ geometry=transform+(orientation < 5 ? 5 : orientation == 5 ? 10 : 11);
+ fields=sscanf(geometry,"%lfx%lf",&source_columns,&source_rows);
+ if (fields != 2)
+ return(MagickFalse);
+ if ((IsNaN(source_columns) != 0) || (IsNaN(source_rows) != 0) ||
+ (source_columns > (double) MAGICK_SSIZE_MAX) ||
+ (source_rows > (double) MAGICK_SSIZE_MAX) ||
+ (IsGainMapBaseGeometry(state->columns,state->rows,source_columns,
+ source_rows) == MagickFalse))
+ return(MagickFalse);
+ return(UpdateGainMapTransformOrientation(state,orientation));
+}
+
static MagickBooleanType ReplaceGainMapImage(Image **gainmap_image,
Image *transform_image)
{
@@ -830,15 +1073,7 @@ static MagickBooleanType ApplyGainMapTransform(Image **gainmap_image,
columns,
rows,
source_columns,
- source_rows,
- x,
- y;
-
- Image
- *transform_image;
-
- size_t
- rotations;
+ source_rows;
FilterType
filter_type;
@@ -847,27 +1082,21 @@ static MagickBooleanType ApplyGainMapTransform(Image **gainmap_image,
fields,
filter_value;
- if (LocaleNCompare(transform,"crop ",5) == 0)
- {
- if (sscanf(transform+5,"%lfx%lf %lfx%lf%lf%lf",&source_columns,
- &source_rows,&columns,&rows,&x,&y) != 6)
- return(MagickFalse);
- if (IsGainMapBaseGeometry(*base_columns,*base_rows,source_columns,
- source_rows) == MagickFalse)
- return(MagickFalse);
- if (CropGainMapImage(gainmap_image,*base_columns,*base_rows,columns,
- rows,x,y,exception) == MagickFalse)
- return(MagickFalse);
- *base_columns=CastDoubleToSizeT(columns);
- *base_rows=CastDoubleToSizeT(rows);
- return(MagickTrue);
- }
if (LocaleNCompare(transform,"resize ",7) == 0)
{
fields=sscanf(transform+7,"%lfx%lf %lfx%lf %d",&source_columns,
&source_rows,&columns,&rows,&filter_value);
if ((fields != 4) && (fields != 5))
return(MagickFalse);
+ if ((IsNaN(source_columns) != 0) || (IsNaN(source_rows) != 0) ||
+ (IsNaN(columns) != 0) || (IsNaN(rows) != 0) ||
+ (source_columns > (double) MAGICK_SSIZE_MAX) ||
+ (source_rows > (double) MAGICK_SSIZE_MAX) ||
+ (columns <= 0.0) || (rows <= 0.0) ||
+ (columns > (double) MAGICK_SSIZE_MAX) ||
+ (rows > (double) MAGICK_SSIZE_MAX) ||
+ (columns != floor(columns)) || (rows != floor(rows)))
+ return(MagickFalse);
filter_type=UndefinedFilter;
if (fields == 5)
{
@@ -885,90 +1114,102 @@ static MagickBooleanType ApplyGainMapTransform(Image **gainmap_image,
CastDoubleToSizeT(columns),CastDoubleToSizeT(rows),filter_type,
exception));
}
- if (LocaleNCompare(transform,"flip ",5) == 0)
- {
- if (sscanf(transform+5,"%lfx%lf",&source_columns,&source_rows) != 2)
- return(MagickFalse);
- if (IsGainMapBaseGeometry(*base_columns,*base_rows,source_columns,
- source_rows) == MagickFalse)
- return(MagickFalse);
- transform_image=FlipImage(*gainmap_image,exception);
- return(ReplaceGainMapImage(gainmap_image,transform_image));
- }
- if (LocaleNCompare(transform,"flop ",5) == 0)
- {
- if (sscanf(transform+5,"%lfx%lf",&source_columns,&source_rows) != 2)
- return(MagickFalse);
- if (IsGainMapBaseGeometry(*base_columns,*base_rows,source_columns,
- source_rows) == MagickFalse)
- return(MagickFalse);
- transform_image=FlopImage(*gainmap_image,exception);
- return(ReplaceGainMapImage(gainmap_image,transform_image));
- }
- if (LocaleNCompare(transform,"rotate ",7) == 0)
- {
- size_t
- next_columns,
- next_rows;
+ return(MagickFalse);
+}
- if (sscanf(transform+7,"%lfx%lf %lf",&source_columns,&source_rows,
- &x) != 3)
- return(MagickFalse);
- if (IsGainMapBaseGeometry(*base_columns,*base_rows,source_columns,
- source_rows) == MagickFalse)
- return(MagickFalse);
- rotations=CastDoubleToSizeT(x) % 4;
- if (rotations == 0)
- return(MagickTrue);
- transform_image=IntegralRotateImage(*gainmap_image,rotations,exception);
- if (ReplaceGainMapImage(gainmap_image,transform_image) == MagickFalse)
- return(MagickFalse);
- if ((rotations == 1) || (rotations == 3))
- {
- next_columns=(*base_rows);
- next_rows=(*base_columns);
- *base_columns=next_columns;
- *base_rows=next_rows;
- }
- return(MagickTrue);
- }
- if (LocaleNCompare(transform,"transpose ",10) == 0)
- {
- size_t
- next_columns;
+static MagickBooleanType ApplyGainMapTransformOrientation(
+ Image **gainmap_image,const UltraHDRGainMapTransformState *state,
+ ExceptionInfo *exception)
+{
+ Image
+ *transform_image;
- if (sscanf(transform+10,"%lfx%lf",&source_columns,&source_rows) != 2)
- return(MagickFalse);
- if (IsGainMapBaseGeometry(*base_columns,*base_rows,source_columns,
- source_rows) == MagickFalse)
- return(MagickFalse);
- transform_image=TransposeImage(*gainmap_image,exception);
- if (ReplaceGainMapImage(gainmap_image,transform_image) == MagickFalse)
- return(MagickFalse);
- next_columns=(*base_rows);
- *base_rows=(*base_columns);
- *base_columns=next_columns;
- return(MagickTrue);
- }
- if (LocaleNCompare(transform,"transverse ",11) == 0)
- {
- size_t
- next_columns;
+ size_t
+ orientation,
+ rotations;
- if (sscanf(transform+11,"%lfx%lf",&source_columns,&source_rows) != 2)
- return(MagickFalse);
- if (IsGainMapBaseGeometry(*base_columns,*base_rows,source_columns,
- source_rows) == MagickFalse)
- return(MagickFalse);
- transform_image=TransverseImage(*gainmap_image,exception);
- if (ReplaceGainMapImage(gainmap_image,transform_image) == MagickFalse)
- return(MagickFalse);
- next_columns=(*base_rows);
- *base_rows=(*base_columns);
- *base_columns=next_columns;
- return(MagickTrue);
- }
- return(MagickFalse);
+ if ((state->basis_x_x == 1) && (state->basis_x_y == 0) &&
+ (state->basis_y_x == 0) && (state->basis_y_y == 1))
+ return(MagickTrue);
+ for (orientation=0; orientation < (sizeof(gainmap_orientations)/
+ sizeof(*gainmap_orientations)); orientation++)
+ if ((state->basis_x_x == gainmap_orientations[orientation].basis_x_columns) &&
+ (state->basis_x_y == gainmap_orientations[orientation].basis_x_rows) &&
+ (state->basis_y_x == gainmap_orientations[orientation].basis_y_columns) &&
+ (state->basis_y_y == gainmap_orientations[orientation].basis_y_rows))
+ break;
+ if (orientation >= (sizeof(gainmap_orientations)/
+ sizeof(*gainmap_orientations)))
+ return(MagickFalse);
+ if (orientation < 3)
+ rotations=orientation+1;
+ else if (orientation == 3)
+ transform_image=FlipImage(*gainmap_image,exception);
+ else if (orientation == 4)
+ transform_image=FlopImage(*gainmap_image,exception);
+ else if (orientation == 5)
+ transform_image=TransposeImage(*gainmap_image,exception);
+ else
+ transform_image=TransverseImage(*gainmap_image,exception);
+ if (orientation >= 3)
+ return(ReplaceGainMapImage(gainmap_image,transform_image));
+ transform_image=IntegralRotateImage(*gainmap_image,rotations,exception);
+ return(ReplaceGainMapImage(gainmap_image,transform_image));
+}
+
+static MagickBooleanType FlushGainMapTransform(
+ Image **gainmap_image,const UltraHDRGainMapTransformState *state,
+ ExceptionInfo *exception)
+{
+ ssize_t
+ origin_x,
+ origin_y,
+ max_x,
+ max_y,
+ min_x,
+ min_y,
+ opposite_x,
+ opposite_y;
+
+ size_t
+ crop_height,
+ crop_width;
+
+ if ((state->source_columns == 0) || (state->source_rows == 0) ||
+ (state->source_columns > (size_t) MAGICK_SSIZE_MAX) ||
+ (state->source_rows > (size_t) MAGICK_SSIZE_MAX))
+ return(MagickFalse);
+ origin_x=state->origin_x;
+ origin_y=state->origin_y;
+ if (AddGainMapTransformTerm(state->origin_x,state->columns,
+ state->basis_x_x,state->rows,state->basis_y_x,&opposite_x) ==
+ MagickFalse ||
+ AddGainMapTransformTerm(state->origin_y,state->columns,
+ state->basis_x_y,state->rows,state->basis_y_y,&opposite_y) ==
+ MagickFalse)
+ return(MagickFalse);
+ min_x=MagickMin(origin_x,opposite_x);
+ min_y=MagickMin(origin_y,opposite_y);
+ max_x=MagickMax(origin_x,opposite_x);
+ max_y=MagickMax(origin_y,opposite_y);
+ if ((min_x < 0) || (min_y < 0) ||
+ (max_x > (ssize_t) state->source_columns) ||
+ (max_y > (ssize_t) state->source_rows) || (max_x <= min_x) ||
+ (max_y <= min_y))
+ return(MagickFalse);
+ crop_width=(size_t) (max_x-min_x);
+ crop_height=(size_t) (max_y-min_y);
+ if ((min_x != 0) || (min_y != 0) ||
+ (max_x != (ssize_t) state->source_columns) ||
+ (max_y != (ssize_t) state->source_rows))
+ if (CropGainMapImage(gainmap_image,state->source_columns,
+ state->source_rows,(double) crop_width,(double) crop_height,
+ (double) min_x,(double) min_y,exception) == MagickFalse)
+ return(MagickFalse);
+ if (ApplyGainMapTransformOrientation(gainmap_image,state,exception) ==
+ MagickFalse)
+ return(MagickFalse);
+ return(MagickTrue);
}
static StringInfo *EncodeBaseImageProfile(const ImageInfo *image_info,
@@ -1035,7 +1276,11 @@ static StringInfo *TransformGainMapProfile(const ImageInfo *image_info,
MagickBooleanType
status,
transformed,
- transform_required;
+ transform_required,
+ transform_pending;
+
+ UltraHDRGainMapTransformState
+ transform_state;
size_t
base_columns,
@@ -1094,6 +1339,8 @@ static StringInfo *TransformGainMapProfile(const ImageInfo *image_info,
}
status=MagickTrue;
transformed=MagickFalse;
+ transform_pending=MagickFalse;
+ InitializeGainMapTransformState(&transform_state,base_columns,base_rows);
if ((transforms != (const char *) NULL) && (*transforms != '\0'))
{
char
@@ -1109,16 +1356,138 @@ static StringInfo *TransformGainMapProfile(const ImageInfo *image_info,
*next++='\0';
if (*transform != '\0')
{
- status=ApplyGainMapTransform(&gainmap_images,&base_columns,
- &base_rows,image,transform,exception);
+ if (LocaleNCompare(transform,"crop ",5) == 0)
+ {
+ double
+ columns,
+ rows,
+ source_columns,
+ source_rows,
+ x,
+ y;
+
+ size_t
+ crop_columns,
+ crop_rows;
+
+ ssize_t
+ crop_x,
+ crop_y;
+
+ if (sscanf(transform+5,"%lfx%lf %lfx%lf%lf%lf",
+ &source_columns,&source_rows,&columns,&rows,&x,&y) != 6)
+ status=MagickFalse;
+ else if ((IsNaN(source_columns) != 0) ||
+ (IsNaN(source_rows) != 0) || (IsNaN(columns) != 0) ||
+ (IsNaN(rows) != 0) || (IsNaN(x) != 0) ||
+ (IsNaN(y) != 0) ||
+ (source_columns > (double) MAGICK_SSIZE_MAX) ||
+ (source_rows > (double) MAGICK_SSIZE_MAX) ||
+ (columns > (double) MAGICK_SSIZE_MAX) ||
+ (rows > (double) MAGICK_SSIZE_MAX) ||
+ (x > (double) MAGICK_SSIZE_MAX) ||
+ (y > (double) MAGICK_SSIZE_MAX) ||
+ (columns != floor(columns)) ||
+ (rows != floor(rows)) || (x != floor(x)) ||
+ (y != floor(y)))
+ status=MagickFalse;
+ else if (IsGainMapBaseGeometry(transform_state.columns,
+ transform_state.rows,
+ source_columns,source_rows) == MagickFalse)
+ status=MagickFalse;
+ else if ((columns <= 0.0) || (rows <= 0.0) ||
+ (x < 0.0) || (y < 0.0) ||
+ (columns > (double) transform_state.columns) ||
+ (rows > (double) transform_state.rows) ||
+ (x > ((double) transform_state.columns-columns)) ||
+ (y > ((double) transform_state.rows-rows)))
+ status=MagickFalse;
+ if (status != MagickFalse)
+ {
+ crop_columns=CastDoubleToSizeT(columns);
+ crop_rows=CastDoubleToSizeT(rows);
+ crop_x=CastDoubleToSsizeT(x);
+ crop_y=CastDoubleToSsizeT(y);
+ if (transform_pending == MagickFalse)
+ {
+ InitializeGainMapTransformState(&transform_state,
+ base_columns,base_rows);
+ transform_pending=MagickTrue;
+ }
+ status=UpdateGainMapTransformCrop(&transform_state,
+ crop_columns,crop_rows,crop_x,crop_y);
+ if (status != MagickFalse)
+ {
+ base_columns=transform_state.columns;
+ base_rows=transform_state.rows;
+ transformed=MagickTrue;
+ }
+ }
+ }
+ else if (LocaleNCompare(transform,"resize ",7) == 0)
+ {
+ if (transform_pending != MagickFalse)
+ {
+ status=FlushGainMapTransform(&gainmap_images,
+ &transform_state,exception);
+ transform_pending=MagickFalse;
+ }
+ if (status != MagickFalse)
+ status=ApplyGainMapTransform(&gainmap_images,&base_columns,
+ &base_rows,image,transform,exception);
+ if (status != MagickFalse)
+ {
+ InitializeGainMapTransformState(&transform_state,
+ base_columns,base_rows);
+ transformed=MagickTrue;
+ }
+ }
+ else if ((LocaleNCompare(transform,"flip ",5) == 0) ||
+ (LocaleNCompare(transform,"flop ",5) == 0) ||
+ (LocaleNCompare(transform,"rotate ",7) == 0) ||
+ (LocaleNCompare(transform,"transpose ",10) == 0) ||
+ (LocaleNCompare(transform,"transverse ",11) == 0))
+ {
+ if (transform_pending == MagickFalse)
+ {
+ InitializeGainMapTransformState(&transform_state,
+ base_columns,base_rows);
+ transform_pending=MagickTrue;
+ }
+ status=UpdateGainMapTransformStateFromRecord(
+ &transform_state,transform);
+ if (status != MagickFalse)
+ {
+ base_columns=transform_state.columns;
+ base_rows=transform_state.rows;
+ transformed=MagickTrue;
+ }
+ }
+ else
+ {
+ if (transform_pending != MagickFalse)
+ {
+ status=FlushGainMapTransform(&gainmap_images,
+ &transform_state,exception);
+ transform_pending=MagickFalse;
+ }
+ if (status != MagickFalse)
+ status=ApplyGainMapTransform(&gainmap_images,&base_columns,
+ &base_rows,image,transform,exception);
+ if (status != MagickFalse)
+ transformed=MagickTrue;
+ }
if (status == MagickFalse)
break;
- transformed=MagickTrue;
}
transform=next;
}
transform_list=DestroyString(transform_list);
}
+ if ((status != MagickFalse) && (transform_pending != MagickFalse))
+ {
+ status=FlushGainMapTransform(&gainmap_images,&transform_state,exception);
+ }
if ((status != MagickFalse) &&
((base_columns != image->columns) || (base_rows != image->rows)))
status=MagickFalse;
diff --git a/tests/cli-uhdr.tap b/tests/cli-uhdr.tap
index 79331cd08..a28e62c50 100755
--- a/tests/cli-uhdr.tap
+++ b/tests/cli-uhdr.tap
@@ -34,6 +34,16 @@ crop_sequential=cli-uhdr-crop-replay-sequential.jpg
crop_direct=cli-uhdr-crop-replay-direct.jpg
crop_sequential_linear=cli-uhdr-crop-replay-sequential-linear.miff
crop_direct_linear=cli-uhdr-crop-replay-direct-linear.miff
+reduced_seed=cli-uhdr-crop-replay-reduced-seed.jpg
+reduced_source=cli-uhdr-crop-replay-reduced.jpg
+reduced_sequential=cli-uhdr-crop-replay-reduced-sequential.jpg
+reduced_direct=cli-uhdr-crop-replay-reduced-direct.jpg
+reduced_sequential_linear=cli-uhdr-crop-replay-reduced-sequential-linear.miff
+reduced_direct_linear=cli-uhdr-crop-replay-reduced-direct-linear.miff
+reduced_phase_sequential=cli-uhdr-crop-replay-reduced-phase-sequential.jpg
+reduced_phase_direct=cli-uhdr-crop-replay-reduced-phase-direct.jpg
+reduced_phase_sequential_linear=cli-uhdr-crop-replay-reduced-phase-sequential-linear.miff
+reduced_phase_direct_linear=cli-uhdr-crop-replay-reduced-phase-direct-linear.miff
capacity_min=1.00000012
capacity_max=2.00000024
expected_offsets='1.00000001e-07,1.00000001e-07,1.00000001e-07 1.00000001e-07,1.00000001e-07,1.00000001e-07'
@@ -46,7 +56,11 @@ cleanup()
"$locale_output" "$jpeg_probe" "$whitespace_output" "$invalid_output"
rm -f "$commented_source" "$comment_roundtrip" \
"$comment_linear" "$crop_sequential" "$crop_direct" \
- "$crop_sequential_linear" "$crop_direct_linear"
+ "$crop_sequential_linear" "$crop_direct_linear" "$reduced_seed" \
+ "$reduced_source" "$reduced_sequential" "$reduced_direct" \
+ "$reduced_sequential_linear" "$reduced_direct_linear" \
+ "$reduced_phase_sequential" "$reduced_phase_direct" \
+ "$reduced_phase_sequential_linear" "$reduced_phase_direct_linear"
}
check_value()
@@ -86,7 +100,7 @@ if [ "X$jpeg_format" != "XJPEG" ]; then
exit 0
fi
-echo "1..14"
+echo "1..16"
if ! ${MAGICK} \( "${SRCDIR}/rose.pnm" -depth 8 \) \
\( "${SRCDIR}/rose.pnm" -depth 16 \) \
-define uhdr:hdr-color-transfer=linear "UHDR:$raw_source" \
@@ -109,6 +123,18 @@ if ! ${MAGICK} "UHDR:$source" -set comment "$source_comment" \
cleanup
exit 0
fi
+# Enlarge only the base while retaining the original 35x23 map profile.
+if ! ${MAGICK} \( "${SRCDIR}/rose.pnm" -depth 8 -resize 50% \) \
+ \( "${SRCDIR}/rose.pnm" -depth 16 -resize 50% \) \
+ -define uhdr:hdr-color-transfer=linear "UHDR:$reduced_seed" \
+ >/dev/null 2>&1 ||
+ ! ${MAGICK} "UHDR:$reduced_seed" -resize 200% \
+ -set hdrgm:BaseWidth 70 -set hdrgm:BaseHeight 46 \
+ -set hdrgm:Transform '' "UHDR:$reduced_source" >/dev/null 2>&1; then
+ echo "Bail out! unable to create reduced-map UHDR fixture"
+ cleanup
+ exit 0
+fi
if ${MAGICK} "UHDR:$source" \
-set hdrgm:OffsetSDR ' 0.0000001 , 0.0000001 , 0.0000001 ' \
@@ -269,6 +295,57 @@ else
echo "# unable to create or decode crop replay outputs"
fi
+if ${MAGICK} "UHDR:$reduced_source" -crop 40x30+1+3 +repage \
+ -crop 19x15+3+1 +repage -quality 85 "UHDR:$reduced_sequential" \
+ >/dev/null 2>&1 &&
+ ${MAGICK} "UHDR:$reduced_source" -crop 19x15+4+4 +repage \
+ -quality 85 "UHDR:$reduced_direct" >/dev/null 2>&1 &&
+ ${MAGICK} -define uhdr:output-color-transfer=linear \
+ "UHDR:$reduced_sequential" "$reduced_sequential_linear" >/dev/null 2>&1 &&
+ ${MAGICK} -define uhdr:output-color-transfer=linear \
+ "UHDR:$reduced_direct" "$reduced_direct_linear" >/dev/null 2>&1; then
+ crop_metric=`${MAGICK} compare -metric AE "$reduced_sequential_linear" \
+ "$reduced_direct_linear" null: 2>&1`
+ crop_status=$?
+ crop_value=`echo "$crop_metric" | awk '{print $1}'`
+ if [ "$crop_status" = "0" ] && [ "X$crop_value" = "X0" ]; then
+ echo "ok - repeated reduced-map crops match direct HDR pixels"
+ else
+ echo "not ok - repeated reduced-map crops match direct HDR pixels"
+ echo "# expected zero absolute error, got '$crop_metric'"
+ fi
+else
+ echo "not ok - repeated reduced-map crops match direct HDR pixels"
+ echo "# unable to create or decode reduced-map crop replay outputs"
+fi
+
+if ${MAGICK} "UHDR:$reduced_source" -crop 40x30+5+3 +repage \
+ -rotate 90 -crop 15x13+3+5 +repage -quality 85 \
+ "UHDR:$reduced_phase_sequential" >/dev/null 2>&1 &&
+ ${MAGICK} "UHDR:$reduced_source" -crop 13x15+10+15 +repage \
+ -rotate 90 -quality 85 "UHDR:$reduced_phase_direct" >/dev/null 2>&1 &&
+ ${MAGICK} -define uhdr:output-color-transfer=linear \
+ "UHDR:$reduced_phase_sequential" "$reduced_phase_sequential_linear" \
+ >/dev/null 2>&1 &&
+ ${MAGICK} -define uhdr:output-color-transfer=linear \
+ "UHDR:$reduced_phase_direct" "$reduced_phase_direct_linear" \
+ >/dev/null 2>&1; then
+ crop_metric=`${MAGICK} compare -metric AE \
+ "$reduced_phase_sequential_linear" "$reduced_phase_direct_linear" \
+ null: 2>&1`
+ crop_status=$?
+ crop_value=`echo "$crop_metric" | awk '{print $1}'`
+ if [ "$crop_status" = "0" ] && [ "X$crop_value" = "X0" ]; then
+ echo "ok - reduced-map crop and rotate replay matches direct geometry"
+ else
+ echo "not ok - reduced-map crop and rotate replay matches direct geometry"
+ echo "# expected zero absolute error, got '$crop_metric'"
+ fi
+else
+ echo "not ok - reduced-map crop and rotate replay matches direct geometry"
+ echo "# unable to create or decode reduced-map crop and rotate outputs"
+fi
+
comma_locale=
for candidate in de_DE.UTF-8 fr_FR.UTF-8 nl_NL.UTF-8; do
decimal_point=`LC_ALL=$candidate locale decimal_point 2>/dev/null`