using StitcherApi.Models; using StitcherApi.Services.Fast; using StitcherApi.Services.Streaming; using StitcherApi.Services.Utilities; namespace StitcherApi.Services; public class ImageService : IImageService { private readonly ILogger _logger; private readonly FastProcessor _fastProcessor; private readonly StreamingProcessor _streamingProcessor; private const double ASPECT_RATIO_THRESHOLD_TALL = 0.25; private const double ASPECT_RATIO_THRESHOLD_WIDE = 8.0; private const int TILE_COUNT_THRESHOLD = 30; public ImageService(IConfiguration config, ILoggerFactory loggerFactory) { _logger = loggerFactory.CreateLogger(); string assetPath = config["AssetPath"] ?? throw new InvalidOperationException("AssetPath not configured."); _fastProcessor = new FastProcessor(assetPath, loggerFactory.CreateLogger()); _streamingProcessor = new StreamingProcessor( assetPath, loggerFactory.CreateLogger() ); } public async Task GenerateImageAsync(GenerateImageRequest request) { (int minRow, int minCol, int maxRow, int maxCol) = CoordinateParser.ParseCanvasRect( request.CanvasRect ); int tileGridWidth = maxCol - minCol + 1; int tileGridHeight = maxRow - minRow + 1; int totalTiles = tileGridWidth * tileGridHeight; StitchRequest stitchRequest = CreateStitchRequest(request, minRow, minCol, maxRow, maxCol); double aspectRatio = (tileGridHeight > 0) ? (double)tileGridWidth / tileGridHeight : 0; if ( totalTiles > TILE_COUNT_THRESHOLD || aspectRatio > ASPECT_RATIO_THRESHOLD_WIDE || (aspectRatio > 0 && aspectRatio < ASPECT_RATIO_THRESHOLD_TALL) ) { _logger.LogInformation("Large, Tall, or Wide canvas detected. Using fast processor."); return await _fastProcessor.ProcessAsync(stitchRequest); } else { _logger.LogInformation( "Small, block-shaped canvas detected. Using robust streaming processor." ); return await _streamingProcessor.ProcessAsync(stitchRequest); } } private StitchRequest CreateStitchRequest( GenerateImageRequest request, int minRow, int minCol, int maxRow, int maxCol ) { const int TILE_SIZE = 720; int stitchedCanvasWidth = (maxCol - minCol + 1) * TILE_SIZE; int stitchedCanvasHeight = (maxRow - minRow + 1) * TILE_SIZE; int cropX = (int)(request.CropOffset[0] * stitchedCanvasWidth); int cropY = (int)(request.CropOffset[1] * stitchedCanvasHeight); int cropW = (int)(request.CropSize[0] * stitchedCanvasWidth); int cropH = (int)(request.CropSize[1] * stitchedCanvasHeight); int startTileCol = minCol + cropX / TILE_SIZE; int endTileCol = minCol + (cropX + cropW - 1) / TILE_SIZE; int startTileRow = minRow + cropY / TILE_SIZE; int endTileRow = minRow + (cropY + cropH - 1) / TILE_SIZE; return new StitchRequest( minRow, minCol, startTileRow, startTileCol, endTileRow, endTileCol, cropX, cropY, cropW, cropH, request.OutputScale ); } }