diff --git a/api/applyEffects.php b/api/applyEffects.php index 1525711f6..b8913225e 100644 --- a/api/applyEffects.php +++ b/api/applyEffects.php @@ -125,14 +125,39 @@ } if (!$vars['isCollage'] || $vars['editSingleCollage']) { - // apply filter + // apply filter (optionally downscale first for performance) if ($vars['imageFilter'] !== null && $vars['imageFilter'] !== ImageFilterEnum::PLAIN) { + $originalWidth = imagesx($imageResource); + $originalHeight = imagesy($imageResource); + $filterResource = $imageResource; + + $filterProcessSize = intval($config['filters']['process_size'] ?? 0); + if ($filterProcessSize > 0 && ($originalWidth > $filterProcessSize || $originalHeight > $filterProcessSize)) { + $downscaled = $imageHandler->resizeImage($imageResource, $filterProcessSize); + if ($downscaled instanceof \GdImage) { + $filterResource = $downscaled; + } + } + try { - ImageUtility::applyFilter($vars['imageFilter'], $imageResource); + ImageUtility::applyFilter($vars['imageFilter'], $filterResource); $imageHandler->imageModified = true; } catch (\Exception $e) { throw new \Exception('Error applying image filter.'); } + + if ($filterResource !== $imageResource) { + $imageResource = $filterResource; + // Maybe we want this later or configurable, will take some time to process upscale again + // Upscale back to original size + // $restored = $imageHandler->resizeImage($filterResource, $originalWidth, $originalHeight); + // if ($restored instanceof \GdImage) { + // if ($filterResource instanceof \GdImage) { + // unset($filterResource); + // } + // $imageResource = $restored; + // } + } } if ($config['picture']['flip'] !== 'off') { diff --git a/assets/js/core.js b/assets/js/core.js index b2bf638e7..5e3196023 100644 --- a/assets/js/core.js +++ b/assets/js/core.js @@ -67,6 +67,7 @@ const photoBooth = (function () { chromaFile = '', currentCollageFile = '', imgFilter = config.filters.defaults, + isProcessingEffects = false, command, startTime, endTime, @@ -91,7 +92,7 @@ const photoBooth = (function () { photoboothTools.console.log('Timeout for auto reload cleared.'); if (!api.takingPic) { - photoboothTools.console.logDev('Timeout for auto reload set to' + timeToLive + ' milliseconds.'); + photoboothTools.console.logDev('Timeout for auto reload set to ' + timeToLive + ' milliseconds.'); timeOut = setTimeout(function () { photoboothTools.reloadPage(); }, timeToLive); @@ -154,6 +155,12 @@ const photoBooth = (function () { } }; + const setFiltersEnabled = (enabled) => { + isProcessingEffects = !enabled; + filternav.css('pointer-events', enabled ? '' : 'none'); + filternav.toggleClass('filters--disabled', !enabled); + }; + api.stopPreviewAndCaptureFromVideo = () => { if (config.preview.camTakesPic) { if (photoboothPreview.stream) { @@ -811,6 +818,7 @@ const photoBooth = (function () { setTimeout(function () { api.cheese.destroy(); api.shutter.destroy(); + setFiltersEnabled(true); loaderMessage.empty(); loaderButtonBar.empty(); @@ -850,6 +858,10 @@ const photoBooth = (function () { api.processPic = function (result) { startTime = new Date().getTime(); + loader.addClass('stage--active'); + startPage.removeClass('stage--active'); + resultPage.removeClass('stage--active'); + setFiltersEnabled(false); loaderMessage.html( 'end() ->end() + ->integerNode('process_size') + ->info('Downscale images to this maximum width/height before applying filters to speed up processing. Set to 0 to disable.') + ->defaultValue(0) + ->min(0) + ->max(5000) + ->beforeNormalization() + ->ifString() + ->then(function (string $value): int { return intval($value); }) + ->end() + ->end() ->arrayNode('disabled') ->enumPrototype() ->values(ImageFilterEnum::cases())