diff --git a/typo3/sysext/core/Classes/Imaging/GraphicalFunctions.php b/typo3/sysext/core/Classes/Imaging/GraphicalFunctions.php
index f9ea1256cfc2c59cee98fd25a93f41f1a4b48481..7c49ec2f381830e8639b5013c4a4e5def70977d0 100644
--- a/typo3/sysext/core/Classes/Imaging/GraphicalFunctions.php
+++ b/typo3/sysext/core/Classes/Imaging/GraphicalFunctions.php
@@ -339,13 +339,15 @@ class GraphicalFunctions
         $frame = $this->addFrameSelection && isset($options['frame']) ? (int)$options['frame'] : 0;
 
         $processingInstructions = ImageProcessingInstructions::fromCropScaleValues($info->getWidth(), $info->getHeight(), $width, $height, $options);
-        $w = $processingInstructions->originalWidth;
-        $h = $processingInstructions->originalHeight;
+
+        $originalWidth = $info->getWidth() ?: $width;
+        $originalHeight = $info->getHeight() ?: $height;
+
         // Check if conversion should be performed ($noScale - no processing needed).
         // $noScale flag is TRUE if the width / height does NOT dictate the image to be scaled. That is if no
         // width / height is given or if the destination w/h matches the original image dimensions, or if
         // the option to not scale the image is set.
-        $noScale = !$processingInstructions->originalWidth && !$processingInstructions->originalHeight || $processingInstructions->width === $info->getWidth() && $processingInstructions->height === $info->getHeight() || !empty($options['noScale']);
+        $noScale = !$originalWidth && !$originalHeight || $processingInstructions->width === $info->getWidth() && $processingInstructions->height === $info->getHeight() || !empty($options['noScale']);
         if ($noScale && !$processingInstructions->cropArea && !$additionalParameters && !$frame && $targetFileExtension === $info->getExtension() && !$forceCreation) {
             // Set the new width and height before returning,
             // if the noScale option is set, otherwise the incoming
@@ -360,12 +362,18 @@ class GraphicalFunctions
             return $info;
         }
 
+        $command = '';
+        if ($processingInstructions->cropArea) {
+            $cropArea = $processingInstructions->cropArea;
+            $command .= ' -crop ' . $cropArea->getWidth() . 'x' . $cropArea->getHeight() . '+' . $cropArea->getOffsetLeft() . '+' . $cropArea->getOffsetTop() . '! +repage ';
+        }
+
         // Start with the default scale command
         // check if we should use -sample or -geometry
         if ($options['sample'] ?? false) {
-            $command = '-auto-orient -sample';
+            $command .= '-auto-orient -sample';
         } else {
-            $command = $this->scalecmd;
+            $command .= $this->scalecmd;
         }
         // from the IM docs -- https://imagemagick.org/script/command-line-processing.php
         // "We see that ImageMagick is very good about preserving aspect ratios of images, to prevent distortion
@@ -374,10 +382,6 @@ class GraphicalFunctions
         // operator to the geometry. This will force the image size to exactly what you specify.
         // So, for example, if you specify 100x200! the dimensions will become exactly 100x200"
         $command .= ' ' . $processingInstructions->width . 'x' . $processingInstructions->height . '!';
-        if ($processingInstructions->cropArea) {
-            $cropArea = $processingInstructions->cropArea;
-            $command .= ' -crop ' . $cropArea->getWidth() . 'x' . $cropArea->getHeight() . '+' . $cropArea->getOffsetLeft() . '+' . $cropArea->getOffsetTop() . '! +repage';
-        }
         // Add params
         $additionalParameters = $this->modifyImageMagickStripProfileParameters($additionalParameters, $options);
         $command .= ($additionalParameters ? ' ' . $additionalParameters : $this->cmds[$targetFileExtension] ?? '');
@@ -451,36 +455,6 @@ class GraphicalFunctions
         return $result?->toLegacyArray();
     }
 
-    /**
-     * This only crops the image, but does not take other "options" such as maxWidth etc. not into account. Do not use
-     * standalone if you don't know what you are doing.
-     *
-     * @internal until API is finalized
-     */
-    public function crop(string $imageFile, string $targetFileExtension, string $cropInformation, array $options): ?ImageProcessingResult
-    {
-        // check if it is a json object
-        $cropData = json_decode($cropInformation);
-        if ($cropData) {
-            $offsetLeft = (int)($cropData->x ?? 0);
-            $offsetTop = (int)($cropData->y ?? 0);
-            $newWidth = (int)($cropData->width ?? 0);
-            $newHeight = (int)($cropData->height ?? 0);
-        } else {
-            [$offsetLeft, $offsetTop, $newWidth, $newHeight] = explode(',', $cropInformation, 4);
-        }
-
-        return $this->resize(
-            $imageFile,
-            $targetFileExtension,
-            '',
-            '',
-            sprintf('-crop %dx%d+%d+%d! +repage', $newWidth, $newHeight, $offsetLeft, $offsetTop),
-            isset($options['skipProfile']) ? ['skipProfile' => $options['skipProfile']] : [],
-            true
-        );
-    }
-
     /**
      * This applies an image onto the $inputFile with an additional backgroundImage for the mask
      * @internal until API is finalized
diff --git a/typo3/sysext/core/Classes/Imaging/ImageDimension.php b/typo3/sysext/core/Classes/Imaging/ImageDimension.php
index 676ff087b4dfcacbc96bdec08e07852bfa319823..ce17cfe0a2bd6eb565a20407163e8cfabd1787fc 100644
--- a/typo3/sysext/core/Classes/Imaging/ImageDimension.php
+++ b/typo3/sysext/core/Classes/Imaging/ImageDimension.php
@@ -18,8 +18,6 @@ declare(strict_types=1);
 namespace TYPO3\CMS\Core\Imaging;
 
 use TYPO3\CMS\Core\Imaging\Exception\ZeroImageDimensionException;
-use TYPO3\CMS\Core\Imaging\ImageManipulation\Area;
-use TYPO3\CMS\Core\Resource\ProcessedFile;
 use TYPO3\CMS\Core\Resource\Processing\TaskInterface;
 
 /**
@@ -53,117 +51,12 @@ class ImageDimension
         return $this->height;
     }
 
-    public static function fromProcessingTask(TaskInterface $task): self
-    {
-        $config = self::getConfigurationForImageCropScaleMask($task);
-        $processedFile = $task->getTargetFile();
-        $isCropped = false;
-        if (($config['crop'] ?? null) instanceof Area) {
-            $isCropped = true;
-            $imageWidth = (int)round($config['crop']->getWidth());
-            $imageHeight = (int)round($config['crop']->getHeight());
-        } else {
-            $imageWidth = (int)$processedFile->getOriginalFile()->getProperty('width');
-            $imageHeight = (int)$processedFile->getOriginalFile()->getProperty('height');
-        }
-        if ($imageWidth <= 0 || $imageHeight <= 0) {
-            throw new ZeroImageDimensionException('Width and height of the image must be greater than zero.', 1597310560);
-        }
-        $result = ImageProcessingInstructions::fromCropScaleValues(
-            $imageWidth,
-            $imageHeight,
-            $config['width'] ?? '',
-            $config['height'] ?? '',
-            $config
-        );
-        $imageWidth = $geometryWidth = $result->width;
-        $imageHeight = $geometryHeight = $result->height;
-
-        if ($result->useCropScaling) {
-            $cropWidth = $result->originalWidth;
-            $cropHeight = $result->originalHeight;
-            // If the image is crop-scaled, use the dimension of the crop
-            // unless crop area exceeds the dimension of the scaled image
-            if ($cropWidth <= $geometryWidth && $cropHeight <= $geometryHeight) {
-                $imageWidth = $cropWidth;
-                $imageHeight = $cropHeight;
-            }
-            if (!$isCropped && $task->getTargetFileExtension() === 'svg') {
-                // Keep aspect ratio of SVG files, when crop-scaling is requested
-                // but no crop is applied
-                if ($geometryWidth > $geometryHeight) {
-                    $imageHeight = (int)round($imageWidth * $geometryHeight / $geometryWidth);
-                } else {
-                    $imageWidth = (int)round($imageHeight * $geometryWidth / $geometryHeight);
-                }
-            }
-        }
-
-        return new self($imageWidth, $imageHeight);
-    }
-
     /**
-     * @return array{
-     *           width?: int<0, max>|string,
-     *           height?: int<0, max>|string,
-     *           maxWidth?: int<0, max>,
-     *           maxHeight?: int<0, max>,
-     *           maxW?: int<0, max>,
-     *           maxH?: int<0, max>,
-     *           minW?: int<0, max>,
-     *           minH?: int<0, max>,
-     *           crop?: Area,
-     *           noScale?: bool
-     *         }
+     * @throws ZeroImageDimensionException
      */
-    private static function getConfigurationForImageCropScaleMask(TaskInterface $task): array
+    public static function fromProcessingTask(TaskInterface $task): self
     {
-        $configuration = $task->getConfiguration();
-
-        if ($task->getTargetFile()->getTaskIdentifier() === ProcessedFile::CONTEXT_IMAGEPREVIEW) {
-            $task->sanitizeConfiguration();
-            // @todo: this transformation needs to happen in the PreviewTask, but if we do this,
-            // all preview images would be re-created, so we should be careful when to do this.
-            $configuration = $task->getConfiguration();
-            $configuration['maxWidth'] = $configuration['width'];
-            unset($configuration['width']);
-            $configuration['maxHeight'] = $configuration['height'];
-            unset($configuration['height']);
-        }
-
-        $options = $configuration;
-        if ($configuration['maxWidth'] ?? null) {
-            $options['maxW'] = $configuration['maxWidth'];
-        }
-        if ($configuration['maxHeight'] ?? null) {
-            $options['maxH'] = $configuration['maxHeight'];
-        }
-        if ($configuration['minWidth'] ?? null) {
-            $options['minW'] = $configuration['minWidth'];
-        }
-        if ($configuration['minHeight'] ?? null) {
-            $options['minH'] = $configuration['minHeight'];
-        }
-        if ($configuration['crop'] ?? null) {
-            $options['crop'] = $configuration['crop'];
-            if (is_string($configuration['crop'])) {
-                // check if it is a json object
-                $cropData = json_decode($configuration['crop']);
-                if ($cropData) {
-                    $options['crop'] = new Area((float)$cropData->x, (float)$cropData->y, (float)$cropData->width, (float)$cropData->height);
-                } else {
-                    [$offsetLeft, $offsetTop, $newWidth, $newHeight] = explode(',', $configuration['crop'], 4);
-                    $options['crop'] = new Area((float)$offsetLeft, (float)$offsetTop, (float)$newWidth, (float)$newHeight);
-                }
-                if ($options['crop']->isEmpty()) {
-                    unset($options['crop']);
-                }
-            }
-        }
-        if ($configuration['noScale'] ?? null) {
-            $options['noScale'] = $configuration['noScale'];
-        }
-
-        return $options;
+        $result = ImageProcessingInstructions::fromProcessingTask($task);
+        return new self($result->width, $result->height);
     }
 }
diff --git a/typo3/sysext/core/Classes/Imaging/ImageProcessingInstructions.php b/typo3/sysext/core/Classes/Imaging/ImageProcessingInstructions.php
index 19f4eb87258f1f1fddb0b6aa50b8873eaf592e19..f6adc0c8464e1ad83ad5b571ed7648e469ff98a6 100644
--- a/typo3/sysext/core/Classes/Imaging/ImageProcessingInstructions.php
+++ b/typo3/sysext/core/Classes/Imaging/ImageProcessingInstructions.php
@@ -17,7 +17,10 @@ declare(strict_types=1);
 
 namespace TYPO3\CMS\Core\Imaging;
 
+use TYPO3\CMS\Core\Imaging\Exception\ZeroImageDimensionException;
 use TYPO3\CMS\Core\Imaging\ImageManipulation\Area;
+use TYPO3\CMS\Core\Resource\ProcessedFile;
+use TYPO3\CMS\Core\Resource\Processing\TaskInterface;
 
 /**
  * A DTO representing all information needed to process an image,
@@ -31,19 +34,42 @@ use TYPO3\CMS\Core\Imaging\ImageManipulation\Area;
  *
  * @internal This object is still internal as long as cropping isn't migrated yet to the Crop API.
  */
-class ImageProcessingInstructions
+readonly class ImageProcessingInstructions
 {
-    /** @var int<0, max> */
-    public int $originalWidth = 0;
-    /** @var int<0, max> */
-    public int $originalHeight = 0;
-    /** @var int<0, max> */
-    public int $width = 0;
-    /** @var int<0, max> */
-    public int $height = 0;
-    public bool $useCropScaling = false;
-    public ?Area $cropArea = null;
-    public array $options = [];
+    /**
+     * @param int<0, max> $width
+     * @param int<0, max> $height
+     */
+    public function __construct(
+        public int $width = 0,
+        public int $height = 0,
+        public ?Area $cropArea = null,
+    ) {}
+
+    public static function fromProcessingTask(TaskInterface $task): ImageProcessingInstructions
+    {
+        $config = self::getConfigurationForImageCropScaleMask($task);
+        $processedFile = $task->getTargetFile();
+        $isCropped = false;
+        if (($config['crop'] ?? null) instanceof Area) {
+            $isCropped = true;
+            $imageWidth = (int)round($config['crop']->getWidth());
+            $imageHeight = (int)round($config['crop']->getHeight());
+        } else {
+            $imageWidth = (int)$processedFile->getOriginalFile()->getProperty('width');
+            $imageHeight = (int)$processedFile->getOriginalFile()->getProperty('height');
+        }
+        if ($imageWidth <= 0 || $imageHeight <= 0) {
+            throw new ZeroImageDimensionException('Width and height of the image must be greater than zero.', 1597310560);
+        }
+        return ImageProcessingInstructions::fromCropScaleValues(
+            $imageWidth,
+            $imageHeight,
+            $config['width'] ?? '',
+            $config['height'] ?? '',
+            $config
+        );
+    }
 
     /**
      * Get numbers for scaling the image based on input.
@@ -85,147 +111,176 @@ class ImageProcessingInstructions
      *   whereas $incomingWidth and $incomingHeight contain the target width and height that could be larger than originally requested.
      *
      * ----------------------------
-     * @param int<0, max> $incomingWidth the width of an original image for example, can be "0" if there is no original image (thus, it will remain "0" in the "originalWidth")
-     * @param int<0, max> $incomingHeight the height of an original image for example, can be "0" if there is no original image (thus, it will remain "0" in the "origHeight")
+     * @param int<0, max> $incomingWidth the width of an original image for example, can be "0" if there is no original image
+     * @param int<0, max> $incomingHeight the height of an original image for example, can be "0" if there is no original image
      * @param int<0, max>|string $width "required" width that is requested, can be "" or "0" or a number of a magic "m" or "c" appended
      * @param int<0, max>|string $height "required" height that is requested, can be "" or "0" or a number of a magic "m" or "c" appended
      * @param array $options Options: Keys are like "maxW", "maxH", "minW", "minH"
      */
     public static function fromCropScaleValues(int $incomingWidth, int $incomingHeight, int|string $width, int|string $height, array $options): self
     {
-        $cropOffsetHorizontal = 0;
-        $cropOffsetVertical = 0;
         $options = self::streamlineOptions($options);
-        $obj = new self();
+
+        if ($incomingWidth === 0 || $incomingHeight === 0) {
+            // @todo incomingWidth/Height makes no sense, we should ideally throw an exception here…
+            // this code is here to make existing unit tests happy and should be dropped
+            return new self(
+                width: 0,
+                height: 0,
+                cropArea: null
+            );
+        }
+
+        $cropArea = ($options['crop'] ?? null) instanceof Area ? $options['crop'] : new Area(0, 0, $incomingWidth, $incomingHeight);
+
         // If both the width and the height are set and one of the numbers is appended by an m, the proportions will
         // be preserved and thus width and height are treated as maximum dimensions for the image. The image will be
         // scaled to fit into the rectangle of the dimensions width and height.
         $useWidthOrHeightAsMaximumLimits = str_contains($width . $height, 'm');
-        if (($options['crop'] ?? null) instanceof Area) {
-            $obj->cropArea = $options['crop'];
-            unset($options['crop']);
-        } elseif (str_contains($width . $height, 'c')) {
+        $useCropScaling = str_contains($width . $height, 'c');
+
+        if ($useWidthOrHeightAsMaximumLimits && $useCropScaling) {
+            throw new \InvalidArgumentException('Cannot mix m and c modifiers for width/height', 1709840402);
+        }
+
+        if ($useWidthOrHeightAsMaximumLimits) {
+            if (str_contains($width, 'm')) {
+                $options['maxWidth'] = min((int)$width, $options['maxWidth'] ?? PHP_INT_MAX);
+                // width: auto
+                $width = 0;
+            }
+            if (str_contains($height, 'm')) {
+                $options['maxHeight'] = min((int)$height, $options['maxHeight'] ?? PHP_INT_MAX);
+                // height: auto
+                $height = 0;
+            }
+        }
+
+        if ((int)$width !== 0 && (int)$height !== 0 && $useCropScaling) {
             $cropOffsetHorizontal = (int)substr((string)strstr((string)$width, 'c'), 1);
             $cropOffsetVertical = (int)substr((string)strstr((string)$height, 'c'), 1);
-            $obj->useCropScaling = true;
+            $width = (int)$width;
+            $height = (int)$height;
+
+            $cropArea = self::applyCropScaleToCropArea($cropArea, $width, $height, $cropOffsetVertical, $cropOffsetHorizontal);
         }
+
         $width = (int)$width;
         $height = (int)$height;
+
+        if ($width > 0 && $height === 0) {
+            $height = (int)round($cropArea->getHeight() * ($width / $cropArea->getWidth()));
+        }
+        if ($height > 0 && $width === 0) {
+            $width = (int)round($cropArea->getWidth() * ($height / $cropArea->getHeight()));
+        }
+
         // If there are max-values...
         if (!empty($options['maxWidth'])) {
-            // If width is given...
-            if ($width > 0) {
-                if ($width > $options['maxWidth']) {
-                    $width = $options['maxWidth'];
-                    // Height should follow
-                    $useWidthOrHeightAsMaximumLimits = true;
-                }
-            } else {
-                if ($incomingWidth > $options['maxWidth']) {
-                    $width = $options['maxWidth'];
-                    // Height should follow
-                    $useWidthOrHeightAsMaximumLimits = true;
-                }
+            if ($width > $options['maxWidth'] || ($width === 0 && $cropArea->getWidth() > $options['maxWidth'])) {
+                $width = $options['maxWidth'];
+                $height = (int)round($cropArea->getHeight() * ($width / $cropArea->getWidth()));
             }
         }
         if (!empty($options['maxHeight'])) {
-            // If height is given...
-            if ($height > 0) {
-                if ($height > $options['maxHeight']) {
-                    $height = $options['maxHeight'];
-                    // Height should follow
-                    $useWidthOrHeightAsMaximumLimits = true;
-                }
-            } else {
-                // Changed [0] to [1] 290801
-                if ($incomingHeight > $options['maxHeight']) {
-                    $height = $options['maxHeight'];
-                    // Height should follow
-                    $useWidthOrHeightAsMaximumLimits = true;
-                }
+            if ($height > $options['maxHeight'] || ($height === 0 && $cropArea->getHeight() > $options['maxHeight'])) {
+                $height = $options['maxHeight'];
+                $width = (int)round($cropArea->getWidth() * ($height / $cropArea->getHeight()));
             }
         }
-        $obj->originalWidth = $width;
-        $obj->originalHeight = $height;
-        if (!($GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_allowUpscaling'] ?? false)) {
-            if ($width > $incomingWidth) {
-                $width = $incomingWidth;
-            }
-            if ($height > $incomingHeight) {
-                $height = $incomingHeight;
+
+        if (!empty($options['minWidth'])) {
+            if ($width < $options['minWidth'] || ($width === 0 && $cropArea->getWidth() < $options['minWidth'])) {
+                $width = $options['minWidth'];
+                $height = (int)round($cropArea->getHeight() * ($width / $cropArea->getWidth()));
             }
         }
-        // If scaling should be performed. Check that input "info" array will not cause division-by-zero
-        if (($width > 0 || $height > 0) && $incomingWidth && $incomingHeight) {
-            if ($width > 0 && $height === 0) {
-                $incomingHeight = (int)ceil($incomingHeight * ($width / $incomingWidth));
-                $incomingWidth = $width;
+        if (!empty($options['minHeight'])) {
+            if ($height < $options['minHeight'] || ($height === 0 && $cropArea->getHeight() < $options['minHeight'])) {
+                $height = $options['minHeight'];
+                $width = (int)round($cropArea->getWidth() * ($height / $cropArea->getHeight()));
             }
-            if ((int)$width === 0 && $height > 0) {
-                $incomingWidth = (int)ceil($incomingWidth * ($height / $incomingHeight));
-                $incomingHeight = $height;
+        }
+
+        if ($width === 0 && $height === 0) {
+            $width = (int)round($cropArea->getWidth());
+            $height = (int)round($cropArea->getHeight());
+        }
+        if ($width === 0 || $height === 0) {
+            throw new \LogicException('Image processing instructions did not resolve into coherent positive width and height values. This is a bug. Please report.', 1709806820);
+        }
+
+        if (!($GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_allowUpscaling'] ?? false)) {
+            if ($width > $cropArea->getWidth()) {
+                $width = (int)round($cropArea->getWidth());
+                $height = (int)round($cropArea->getHeight() * ($width / $cropArea->getWidth()));
             }
-            if ($width !== 0 && $height !== 0) {
-                if ($useWidthOrHeightAsMaximumLimits) {
-                    $ratio = $incomingWidth / $incomingHeight;
-                    if ($height * $ratio > $width) {
-                        $height = (int)round($width / $ratio);
-                    } else {
-                        $width = (int)round($height * $ratio);
-                    }
-                }
-                if ($obj->useCropScaling) {
-                    $ratio = $incomingWidth / $incomingHeight;
-                    if ($height * $ratio < $width) {
-                        $height = (int)round($width / $ratio);
-                    } else {
-                        $width = (int)round($height * $ratio);
-                    }
-                }
-                $incomingWidth = $width;
-                $incomingHeight = $height;
+            if ($height > $cropArea->getHeight()) {
+                $height = (int)round($cropArea->getHeight());
+                $width = (int)round($cropArea->getWidth() * ($height / $cropArea->getHeight()));
             }
         }
-        $resultWidth = $incomingWidth;
-        $resultHeight = $incomingHeight;
-        // Set minimum-measures!
-        if (isset($options['minWidth']) && $resultWidth < $options['minWidth']) {
-            if (($useWidthOrHeightAsMaximumLimits || $obj->useCropScaling) && $resultWidth) {
-                $resultHeight = (int)round($resultHeight * $options['minWidth'] / $resultWidth);
-            }
-            $resultWidth = $options['minWidth'];
+
+        if ((int)$cropArea->getOffsetLeft() === 0 &&
+            (int)$cropArea->getOffsetTop() === 0 &&
+            (int)$cropArea->getWidth() === $incomingWidth &&
+            (int)$cropArea->getHeight() === $incomingHeight) {
+            $cropArea = null;
         }
-        if (isset($options['minHeight']) && $resultHeight < $options['minHeight']) {
-            if (($useWidthOrHeightAsMaximumLimits || $obj->useCropScaling) && $resultHeight) {
-                $resultWidth = (int)round($resultWidth * $options['minHeight'] / $resultHeight);
-            }
-            $resultHeight = $options['minHeight'];
-        }
-        $obj->width = $resultWidth;
-        $obj->height = $resultHeight;
-
-        // The incoming values are percentage values, and need to be calculated in
-        // the actual width and height of the target file size, see https://docs.typo3.org/m/typo3/reference-typoscript/main/en-us/Functions/Imgresource.html#width
-        // This needs a special calculation "magic", instead of using the "cropArea" feature.
-        // which TYPO3 uses since v8 which ships with a "cropArea" object right away.
-        if ($obj->useCropScaling && !$obj->cropArea) {
-            $cropWidth = $obj->originalWidth ?: $obj->width;
-            $cropHeight = $obj->originalHeight ?: $obj->height;
-            $offsetX = (float)(($obj->width - $obj->originalWidth) * ($cropOffsetHorizontal + 100) / 200);
-            $offsetY = (float)(($obj->height - $obj->originalHeight) * ($cropOffsetVertical + 100) / 200);
-
-            $obj->cropArea = new Area(
-                $offsetX,
-                $offsetY,
-                (float)$cropWidth,
-                (float)$cropHeight,
-            );
+
+        return new self(
+            width: $width,
+            height: $height,
+            cropArea: $cropArea,
+        );
+    }
+
+    /**
+     * @param Area $cropArea with absolute crop data (not relative!)
+     * @param positive-int $width
+     * @param positive-int $height
+     * @param int<-100,100> $cropOffsetVertical
+     * @param int<-100,100> $cropOffsetHorizontal
+     */
+    private static function applyCropScaleToCropArea(
+        Area $cropArea,
+        int $width,
+        int $height,
+        int $cropOffsetVertical,
+        int $cropOffsetHorizontal
+    ): Area {
+        // @phpstan-ignore-next-line
+        if (!($width > 0 && $height > 0 && $cropArea->getWidth() > 0 && $cropArea->getHeight() > 0)) {
+            throw new \InvalidArgumentException('Apply crop scale must use concrete width and height', 1709810881);
         }
+        $destRatio = $width / $height;
+        $cropRatio = $cropArea->getWidth() / $cropArea->getHeight();
 
-        return $obj;
+        if ($destRatio > $cropRatio) {
+            $w = $cropArea->getWidth();
+            $h = $cropArea->getWidth() / $destRatio;
+            $x = $cropArea->getOffsetLeft();
+            $y = $cropArea->getOffsetTop() + (float)(($cropArea->getHeight() - $h) * ($cropOffsetVertical + 100) / 200);
+        } else {
+            $w = $cropArea->getHeight() * $destRatio;
+            $h = $cropArea->getHeight();
+            $x = $cropArea->getOffsetLeft() + (float)(($cropArea->getWidth() - $w) * ($cropOffsetHorizontal + 100) / 200);
+            $y = $cropArea->getOffsetTop();
+        }
+
+        return new Area($x, $y, $w, $h);
     }
 
-    public static function streamlineOptions(array $options): array
+    /**
+     * @return array{
+     *             maxWidth?: int,
+     *             maxHeight?: int,
+     *             minWidth?: int,
+     *             minHeight?: int,
+     *             crop?: Area,
+     *         }
+     */
+    private static function streamlineOptions(array $options): array
     {
         if (isset($options['maxW'])) {
             $options['maxWidth'] = $options['maxW'];
@@ -243,17 +298,36 @@ class ImageProcessingInstructions
             $options['minHeight'] = $options['minH'];
             unset($options['minH']);
         }
+
+        if (($options['maxWidth'] ?? null) <= 0) {
+            unset($options['maxWidth']);
+        }
+        if (($options['maxHeight'] ?? null) <= 0) {
+            unset($options['maxHeight']);
+        }
+        if (($options['minWidth'] ?? null) <= 0) {
+            unset($options['minWidth']);
+        }
+        if (($options['minHeight'] ?? null) <= 0) {
+            unset($options['minHeight']);
+        }
+
         if (isset($options['crop'])) {
             if (is_string($options['crop'])) {
                 // check if it is a json object
                 $cropData = json_decode($options['crop']);
                 if ($cropData) {
-                    $options['crop'] = new Area((float)$cropData->x, (float)$cropData->y, (float)$cropData->width, (float)$cropData->height);
+                    // happens when $options['crop'] = '{"default":{"cropArea":{"x":0,"y":0,"width":1,"height":1},"selectedRatio":"NaN","focusArea":null}}'
+                    if (!isset($cropData->x) || !isset($cropData->y) || !isset($cropData->width) || !isset($cropData->height)) {
+                        unset($options['crop']);
+                    } else {
+                        $options['crop'] = new Area((float)$cropData->x, (float)$cropData->y, (float)$cropData->width, (float)$cropData->height);
+                    }
                 } else {
                     [$offsetLeft, $offsetTop, $newWidth, $newHeight] = explode(',', $options['crop'], 4);
                     $options['crop'] = new Area((float)$offsetLeft, (float)$offsetTop, (float)$newWidth, (float)$newHeight);
                 }
-                if ($options['crop']->isEmpty()) {
+                if (isset($options['crop']) && $options['crop']->isEmpty()) {
                     unset($options['crop']);
                 }
             } elseif (!$options['crop'] instanceof Area) {
@@ -262,4 +336,69 @@ class ImageProcessingInstructions
         }
         return $options;
     }
+
+    /**
+     * @return array{
+     *           width?: int<0, max>|string,
+     *           height?: int<0, max>|string,
+     *           maxWidth?: int<0, max>,
+     *           maxHeight?: int<0, max>,
+     *           maxW?: int<0, max>,
+     *           maxH?: int<0, max>,
+     *           minW?: int<0, max>,
+     *           minH?: int<0, max>,
+     *           crop?: Area,
+     *           noScale?: bool
+     *         }
+     */
+    private static function getConfigurationForImageCropScaleMask(TaskInterface $task): array
+    {
+        $configuration = $task->getConfiguration();
+
+        if ($task->getTargetFile()->getTaskIdentifier() === ProcessedFile::CONTEXT_IMAGEPREVIEW) {
+            $task->sanitizeConfiguration();
+            // @todo: this transformation needs to happen in the PreviewTask, but if we do this,
+            // all preview images would be re-created, so we should be careful when to do this.
+            $configuration = $task->getConfiguration();
+            $configuration['maxWidth'] = $configuration['width'];
+            unset($configuration['width']);
+            $configuration['maxHeight'] = $configuration['height'];
+            unset($configuration['height']);
+        }
+
+        $options = $configuration;
+        if ($configuration['maxWidth'] ?? null) {
+            $options['maxW'] = $configuration['maxWidth'];
+        }
+        if ($configuration['maxHeight'] ?? null) {
+            $options['maxH'] = $configuration['maxHeight'];
+        }
+        if ($configuration['minWidth'] ?? null) {
+            $options['minW'] = $configuration['minWidth'];
+        }
+        if ($configuration['minHeight'] ?? null) {
+            $options['minH'] = $configuration['minHeight'];
+        }
+        if ($configuration['crop'] ?? null) {
+            $options['crop'] = $configuration['crop'];
+            if (is_string($configuration['crop'])) {
+                // check if it is a json object
+                $cropData = json_decode($configuration['crop']);
+                if ($cropData) {
+                    $options['crop'] = new Area((float)$cropData->x, (float)$cropData->y, (float)$cropData->width, (float)$cropData->height);
+                } else {
+                    [$offsetLeft, $offsetTop, $newWidth, $newHeight] = explode(',', $configuration['crop'], 4);
+                    $options['crop'] = new Area((float)$offsetLeft, (float)$offsetTop, (float)$newWidth, (float)$newHeight);
+                }
+                if ($options['crop']->isEmpty()) {
+                    unset($options['crop']);
+                }
+            }
+        }
+        if ($configuration['noScale'] ?? null) {
+            $options['noScale'] = $configuration['noScale'];
+        }
+
+        return $options;
+    }
 }
diff --git a/typo3/sysext/core/Classes/Resource/Processing/LocalCropScaleMaskHelper.php b/typo3/sysext/core/Classes/Resource/Processing/LocalCropScaleMaskHelper.php
index 66a5ddb64515c00e673164d18e40b25fa76ceeab..10cf5fe87638382ecc94ff445961c56137a5e327 100644
--- a/typo3/sysext/core/Classes/Resource/Processing/LocalCropScaleMaskHelper.php
+++ b/typo3/sysext/core/Classes/Resource/Processing/LocalCropScaleMaskHelper.php
@@ -65,16 +65,6 @@ class LocalCropScaleMaskHelper
         $configuration = $targetFile->getProcessingConfiguration();
         $configuration['additionalParameters'] ??= '';
 
-        $croppedImage = null;
-        if (!empty($configuration['crop'])) {
-            $result = $imageOperations->crop($originalFileName, $targetFileExtension, $configuration['crop'], $configuration);
-            // @todo: in the future, we want this to be one crop call (together with the scale command)
-            unset($configuration['crop']);
-            if ($result !== null) {
-                $originalFileName = $croppedImage = $result->getRealPath();
-            }
-        }
-
         // Normal situation (no masking) - just scale the image
         if (!is_array($configuration['maskImages'] ?? null)) {
             // the result info is an array with 0=width,1=height,2=extension,3=filename
@@ -85,8 +75,6 @@ class LocalCropScaleMaskHelper
                 $configuration['height'] ?? '',
                 $configuration['additionalParameters'],
                 $configuration,
-                // in case file is in `/typo3temp/` from the crop operation above, it must create a result
-                $result !== null
             );
         } else {
             $temporaryFileName = $this->getFilenameForImageCropScaleMask($task);
@@ -133,7 +121,7 @@ class LocalCropScaleMaskHelper
 
         // check if the processing really generated a new file (scaled and/or cropped)
         if ($result !== null) {
-            if ($result->getRealPath() !== $originalFileName || $originalFileName === $croppedImage) {
+            if ($result->getRealPath() !== $originalFileName) {
                 $result = [
                     'width' => $result->getWidth(),
                     'height' => $result->getHeight(),
@@ -145,11 +133,6 @@ class LocalCropScaleMaskHelper
             }
         }
 
-        // Cleanup temp file if it isn't used as result
-        if ($croppedImage && ($result === null || $croppedImage !== $result['filePath'])) {
-            GeneralUtility::unlink_tempfile($croppedImage);
-        }
-
         // If noScale option is applied, we need to reset the width and height to ensure the scaled values
         // are used for the generated image tag even if the image itself is not scaled. This is needed, as
         // the result is discarded due to the fact that the original image is used.
diff --git a/typo3/sysext/core/Tests/Unit/Imaging/ImageDimensionTest.php b/typo3/sysext/core/Tests/Unit/Imaging/ImageDimensionTest.php
index 374d1c871c560e18e6f8adf2af9b55763934e958..e9b6a3b47ef342852a87fdc2f959d7999417d12f 100644
--- a/typo3/sysext/core/Tests/Unit/Imaging/ImageDimensionTest.php
+++ b/typo3/sysext/core/Tests/Unit/Imaging/ImageDimensionTest.php
@@ -105,7 +105,7 @@ final class ImageDimensionTest extends UnitTestCase
                 new ImageDimension(1000, 500),
                 'jpg',
                 ImageCropScaleMaskTask::class,
-                new ImageDimension(50, 25),
+                new ImageDimension(50, 50),
             ],
             'width and height are applied as given' => [
                 [
@@ -155,7 +155,7 @@ final class ImageDimensionTest extends UnitTestCase
                 new ImageDimension(1000, 500),
                 'svg',
                 ImageCropScaleMaskTask::class,
-                new ImageDimension(100, 50),
+                new ImageDimension(100, 100),
             ],
             'cropping is applied on SVGs' => [
                 [
diff --git a/typo3/sysext/core/Tests/Unit/Imaging/ImageProcessingInstructionsTest.php b/typo3/sysext/core/Tests/Unit/Imaging/ImageProcessingInstructionsTest.php
index 451d1ed4bf4ba16b0d04b6b91785582dfe07360b..52c123db66facee4226fc4e86037ef69220ad3f7 100644
--- a/typo3/sysext/core/Tests/Unit/Imaging/ImageProcessingInstructionsTest.php
+++ b/typo3/sysext/core/Tests/Unit/Imaging/ImageProcessingInstructionsTest.php
@@ -33,11 +33,10 @@ final class ImageProcessingInstructionsTest extends UnitTestCase
      */
     public static function fromCropScaleValuesImageDataProvider(): iterable
     {
-        $result = new ImageProcessingInstructions();
-        $result->width = 150;
-        $result->height = 120;
-        $result->originalWidth = 150;
-        $result->originalHeight = 0;
+        $result = new ImageProcessingInstructions(
+            width: 150,
+            height: 120,
+        );
         yield 'Get image scale for a width of 150px' => [
             170,
             136,
@@ -48,11 +47,10 @@ final class ImageProcessingInstructionsTest extends UnitTestCase
             $result,
         ];
 
-        $result = new ImageProcessingInstructions();
-        $result->width = 100;
-        $result->height = 80;
-        $result->originalWidth = 100;
-        $result->originalHeight = 0;
+        $result = new ImageProcessingInstructions(
+            width: 100,
+            height: 80,
+        );
         yield 'Get image scale with a maximum width of 100px' => [
             170,
             136,
@@ -63,11 +61,10 @@ final class ImageProcessingInstructionsTest extends UnitTestCase
             $result,
         ];
 
-        $result = new ImageProcessingInstructions();
-        $result->width = 200;
-        $result->height = 136;
-        $result->originalWidth = 0;
-        $result->originalHeight = 0;
+        $result = new ImageProcessingInstructions(
+            width: 200,
+            height: 160,
+        );
         yield 'Get image scale with a minimum width of 200px' => [
             170,
             136,
@@ -78,10 +75,10 @@ final class ImageProcessingInstructionsTest extends UnitTestCase
             $result,
         ];
 
-        $result = new ImageProcessingInstructions();
-        $result->originalWidth = 50;
-        $result->width = 0;
-        $result->height = 0;
+        $result = new ImageProcessingInstructions(
+            width: 0,
+            height: 0,
+        );
         yield 'No PHP warning for zero in input dimensions when scaling' => [
             0,
             0,
@@ -92,10 +89,10 @@ final class ImageProcessingInstructionsTest extends UnitTestCase
             $result,
         ];
 
-        $result = new ImageProcessingInstructions();
-        $result->originalWidth = 50;
-        $result->width = 50;
-        $result->height = 450;
+        $result = new ImageProcessingInstructions(
+            width: 50,
+            height: 450,
+        );
         yield 'width from original image and explicitly given scales an image down' => [
             100,
             900,
@@ -106,11 +103,10 @@ final class ImageProcessingInstructionsTest extends UnitTestCase
             $result,
         ];
 
-        $result = new ImageProcessingInstructions();
-        $result->originalWidth = 50;
-        $result->originalHeight = 300;
-        $result->width = 33;
-        $result->height = 300;
+        $result = new ImageProcessingInstructions(
+            width: 33,
+            height: 300,
+        );
         yield 'width from original image with maxH set, also fills "origH" value' => [
             100,
             900,
@@ -121,11 +117,10 @@ final class ImageProcessingInstructionsTest extends UnitTestCase
             $result,
         ];
 
-        $result = new ImageProcessingInstructions();
-        $result->originalWidth = 150;
-        $result->originalHeight = 0;
-        $result->width = 150;
-        $result->height = 1350;
+        $result = new ImageProcessingInstructions(
+            width: 150,
+            height: 1350,
+        );
         yield 'width from original image and explicitly given scales an image up' => [
             100,
             900,
@@ -136,11 +131,10 @@ final class ImageProcessingInstructionsTest extends UnitTestCase
             $result,
         ];
 
-        $result = new ImageProcessingInstructions();
-        $result->originalWidth = 150;
-        $result->originalHeight = 0;
-        $result->width = 100;
-        $result->height = 900;
+        $result = new ImageProcessingInstructions(
+            width: 100,
+            height: 900,
+        );
         yield 'width from original image and explicitly given scales an image up but is disabled' => [
             100,
             900,
@@ -151,12 +145,11 @@ final class ImageProcessingInstructionsTest extends UnitTestCase
             $result,
         ];
 
-        $result = new ImageProcessingInstructions();
-        $result->originalWidth = 0;
-        $result->originalHeight = 0;
-        $result->width = 150;
-        $result->height = 900;
-        yield 'min width explicitly given scales an image up but is disabled resulting in do not keep aspect ratio' => [
+        $result = new ImageProcessingInstructions(
+            width: 100,
+            height: 900,
+        );
+        yield 'min width explicitly given scales an image up but is disabled' => [
             100,
             900,
             '',
@@ -166,11 +159,10 @@ final class ImageProcessingInstructionsTest extends UnitTestCase
             $result,
         ];
 
-        $result = new ImageProcessingInstructions();
-        $result->originalWidth = 50;
-        $result->originalHeight = 100;
-        $result->width = 0;
-        $result->height = 0;
+        $result = new ImageProcessingInstructions(
+            width: 0,
+            height: 0,
+        );
         yield 'no orig image given monitors "origW"' => [
             0,
             0,
@@ -181,11 +173,10 @@ final class ImageProcessingInstructionsTest extends UnitTestCase
             $result,
         ];
 
-        $result = new ImageProcessingInstructions();
-        $result->originalWidth = 50;
-        $result->originalHeight = 800;
-        $result->width = 50;
-        $result->height = 450;
+        $result = new ImageProcessingInstructions(
+            width: 50,
+            height: 450,
+        );
         yield 'Incoming instructions use "m" in width with given height' => [
             100,
             900,
@@ -196,11 +187,10 @@ final class ImageProcessingInstructionsTest extends UnitTestCase
             $result,
         ];
 
-        $result = new ImageProcessingInstructions();
-        $result->originalWidth = 50;
-        $result->originalHeight = 0;
-        $result->width = 50;
-        $result->height = 450;
+        $result = new ImageProcessingInstructions(
+            width: 50,
+            height: 450,
+        );
         yield 'Incoming instructions use "m" in width without height' => [
             100,
             900,
@@ -211,13 +201,11 @@ final class ImageProcessingInstructionsTest extends UnitTestCase
             $result,
         ];
 
-        $result = new ImageProcessingInstructions();
-        $result->originalWidth = 50;
-        $result->originalHeight = 800;
-        $result->width = 89;
-        $result->height = 800;
-        $result->useCropScaling = true;
-        $result->cropArea = new Area(19.5, 0, 50, 800);
+        $result = new ImageProcessingInstructions(
+            width: 50,
+            height: 800,
+            cropArea: new Area(21.875, 0, 56.25 /* 900 / 800 * 50 */, 900),
+        );
         yield 'Incoming instructions use "c" in width with given height' => [
             100,
             900,
@@ -228,13 +216,11 @@ final class ImageProcessingInstructionsTest extends UnitTestCase
             $result,
         ];
 
-        $result = new ImageProcessingInstructions();
-        $result->originalWidth = 50;
-        $result->originalHeight = 0;
-        $result->width = 50;
-        $result->height = 450;
-        $result->useCropScaling = true;
-        $result->cropArea = new Area(0, 225, 50, 450);
+        $result = new ImageProcessingInstructions(
+            width: 50,
+            height: 450,
+            cropArea: null,
+        );
         yield 'Incoming instructions use "c" in width but without height' => [
             100,
             900,
@@ -245,13 +231,11 @@ final class ImageProcessingInstructionsTest extends UnitTestCase
             $result,
         ];
 
-        $result = new ImageProcessingInstructions();
-        $result->originalWidth = 50;
-        $result->originalHeight = 650;
-        $result->width = 72;
-        $result->height = 650;
-        $result->useCropScaling = true;
-        $result->cropArea = new Area(13.2, 0, 50, 650);
+        $result = new ImageProcessingInstructions(
+            width: 50,
+            height: 650,
+            cropArea: new Area(18.461538461538456, 0, 69.23076923076924 /* 900 / 650 * 50 */, 900),
+        );
         yield 'Incoming instructions use "c" in width on both sides' => [
             100,
             900,
@@ -262,13 +246,11 @@ final class ImageProcessingInstructionsTest extends UnitTestCase
             $result,
         ];
 
-        $result = new ImageProcessingInstructions();
-        $result->originalWidth = 50;
-        $result->originalHeight = 800;
-        $result->width = 89;
-        $result->height = 800;
-        $result->useCropScaling = true;
-        $result->cropArea = new Area(23.4, 0, 50, 800);
+        $result = new ImageProcessingInstructions(
+            width: 50,
+            height: 800,
+            cropArea: new Area(26.25, 0, 56.25 /* 900 / 800 * 50 */, 900),
+        );
         yield 'Incoming instructions use "c" in width on both sides with given height' => [
             100,
             900,
diff --git a/typo3/sysext/fluid/Tests/Functional/ViewHelpers/ImageViewHelperTest.php b/typo3/sysext/fluid/Tests/Functional/ViewHelpers/ImageViewHelperTest.php
index 8c9d5f6cc96bac0a9e5b4f8dcecd236862616fd1..11dd0e3dc60f6c6af1b2bbe7522d923fb35134e0 100644
--- a/typo3/sysext/fluid/Tests/Functional/ViewHelpers/ImageViewHelperTest.php
+++ b/typo3/sysext/fluid/Tests/Functional/ViewHelpers/ImageViewHelperTest.php
@@ -198,22 +198,21 @@ final class ImageViewHelperTest extends FunctionalTestCase
         ];
         yield 'inline-max width does not upscale' => [
             '<f:image src="fileadmin/ImageViewHelperTest.jpg" width="500m" />',
-            '@^<img src="(fileadmin/_processed_/5/3/csm_ImageViewHelperTest_.*\.jpg)" width="500" height="375" alt="" />$@',
-            500,
-            375,
+            '@^<img src="(fileadmin/ImageViewHelperTest\.jpg)" width="400" height="300" alt="" />$@',
+            400,
+            300,
         ];
         yield 'inline-max height does not upscale' => [
             '<f:image src="fileadmin/ImageViewHelperTest.jpg" height="350m" />',
-            '@^<img src="(fileadmin/_processed_/5/3/csm_ImageViewHelperTest_.*\.jpg)" width="467" height="350" alt="" />$@',
-            467,
-            350,
+            '@^<img src="(fileadmin/ImageViewHelperTest\.jpg)" width="400" height="300" alt="" />$@',
+            400,
+            300,
         ];
-        // would be 200x150, but image will be stretched (why!?) up to have a width of 250
         yield 'min width' => [
             '<f:image src="fileadmin/ImageViewHelperTest.jpg" height="150" minWidth="250" />',
-            '@^<img src="(fileadmin/_processed_/5/3/csm_ImageViewHelperTest_.*\.jpg)" width="250" height="150" alt="" />$@',
+            '@^<img src="(fileadmin/_processed_/5/3/csm_ImageViewHelperTest_.*\.jpg)" width="250" height="188" alt="" />$@',
             250,
-            150,
+            188,
         ];
         // would be 200x150, but image will be scaled down to have a width of 100
         yield 'max width' => [
@@ -222,11 +221,10 @@ final class ImageViewHelperTest extends FunctionalTestCase
             100,
             75,
         ];
-        // would be 200x150, but image will be stretched (why!?) up to have a height of 200
         yield 'min height' => [
             '<f:image src="fileadmin/ImageViewHelperTest.jpg" width="200" minHeight="200" />',
-            '@^<img src="(fileadmin/_processed_/5/3/csm_ImageViewHelperTest_.*\.jpg)" width="200" height="200" alt="" />$@',
-            200,
+            '@^<img src="(fileadmin/_processed_/5/3/csm_ImageViewHelperTest_.*\.jpg)" width="267" height="200" alt="" />$@',
+            267,
             200,
         ];
         // would be 200x150, but image will be scaled down to have a height of 75
diff --git a/typo3/sysext/fluid/Tests/Functional/ViewHelpers/Uri/ImageViewHelperTest.php b/typo3/sysext/fluid/Tests/Functional/ViewHelpers/Uri/ImageViewHelperTest.php
index 5cbaf0b79821731e184300e5a4a7397d56e5904f..e643a9901c37faad59dfd1618d258a68d7342fdb 100644
--- a/typo3/sysext/fluid/Tests/Functional/ViewHelpers/Uri/ImageViewHelperTest.php
+++ b/typo3/sysext/fluid/Tests/Functional/ViewHelpers/Uri/ImageViewHelperTest.php
@@ -198,22 +198,21 @@ final class ImageViewHelperTest extends FunctionalTestCase
         ];
         yield 'inline-max width does not upscale' => [
             '<f:uri.image src="fileadmin/ImageViewHelperTest.jpg" width="500m" />',
-            '@^(fileadmin/_processed_/5/3/csm_ImageViewHelperTest_.*\.jpg)$@',
-            500,
-            375,
+            '@^(fileadmin/ImageViewHelperTest\.jpg)$@',
+            400,
+            300,
         ];
         yield 'inline-max height does not upscale' => [
             '<f:uri.image src="fileadmin/ImageViewHelperTest.jpg" height="350m" />',
-            '@^(fileadmin/_processed_/5/3/csm_ImageViewHelperTest_.*\.jpg)$@',
-            467,
-            350,
+            '@^(fileadmin/ImageViewHelperTest\.jpg)$@',
+            400,
+            300,
         ];
-        // would be 200x150, but image will be stretched (why!?) up to have a width of 250
         yield 'min width' => [
             '<f:uri.image src="fileadmin/ImageViewHelperTest.jpg" height="150" minWidth="250" />',
             '@^(fileadmin/_processed_/5/3/csm_ImageViewHelperTest_.*\.jpg)$@',
             250,
-            150,
+            188,
         ];
         // would be 200x150, but image will be scaled down to have a width of 100
         yield 'max width' => [
@@ -222,11 +221,10 @@ final class ImageViewHelperTest extends FunctionalTestCase
             100,
             75,
         ];
-        // would be 200x150, but image will be stretched (why!?) up to have a height of 200
         yield 'min height' => [
             '<f:uri.image src="fileadmin/ImageViewHelperTest.jpg" width="200" minHeight="200" />',
             '@^(fileadmin/_processed_/5/3/csm_ImageViewHelperTest_.*\.jpg)$@',
-            200,
+            267,
             200,
         ];
         // would be 200x150, but image will be scaled down to have a height of 75
diff --git a/typo3/sysext/frontend/Tests/Functional/ContentObject/ContentObjectRendererTest.php b/typo3/sysext/frontend/Tests/Functional/ContentObject/ContentObjectRendererTest.php
index 8224b81bb67cf1348159ed95f4e3e43ac884020d..55297841507b0d90fb3b080cf16e3fac1d28b552 100644
--- a/typo3/sysext/frontend/Tests/Functional/ContentObject/ContentObjectRendererTest.php
+++ b/typo3/sysext/frontend/Tests/Functional/ContentObject/ContentObjectRendererTest.php
@@ -1229,7 +1229,7 @@ And another one';
         $result = $subject->getImgResource($fileReference, []);
 
         $expectedWidth = 512;
-        $expectedHeight = 341;
+        $expectedHeight = 342;
 
         self::assertEquals($expectedWidth, $result->getWidth());
         self::assertEquals($expectedHeight, $result->getHeight());
diff --git a/typo3/sysext/seo/Tests/Functional/MetaTag/MetaTagGeneratorTest.php b/typo3/sysext/seo/Tests/Functional/MetaTag/MetaTagGeneratorTest.php
index e487e79ee482daccc0c542ece6a4c9466bef7635..15b92a5639658bfeee19e44327c8bbacff041d9c 100644
--- a/typo3/sysext/seo/Tests/Functional/MetaTag/MetaTagGeneratorTest.php
+++ b/typo3/sysext/seo/Tests/Functional/MetaTag/MetaTagGeneratorTest.php
@@ -89,7 +89,8 @@ final class MetaTagGeneratorTest extends FunctionalTestCase
         yield 'social: 3000x600 enforced ratio (no up-scaling)' => [
             true,
             ['width' => 3000, 'height' => 600],
-            ['width' => 1142, 'height' => 600],
+            // width = round(1200/630*600)
+            ['width' => 1143, 'height' => 600],
             ProcessedFile::class,
         ];