modularizing
This commit is contained in:
156
flexlove/ImageScaler.lua
Normal file
156
flexlove/ImageScaler.lua
Normal file
@@ -0,0 +1,156 @@
|
||||
--[[
|
||||
ImageScaler.lua - Image scaling utilities for FlexLove
|
||||
Provides nearest-neighbor and bilinear interpolation scaling algorithms
|
||||
]]
|
||||
|
||||
-- ====================
|
||||
-- Error Handling Utilities
|
||||
-- ====================
|
||||
|
||||
--- Standardized error message formatter
|
||||
---@param module string -- Module name (e.g., "Color", "Theme", "Units")
|
||||
---@param message string -- Error message
|
||||
---@return string -- Formatted error message
|
||||
local function formatError(module, message)
|
||||
return string.format("[FlexLove.%s] %s", module, message)
|
||||
end
|
||||
|
||||
-- ====================
|
||||
-- ImageScaler
|
||||
-- ====================
|
||||
|
||||
local ImageScaler = {}
|
||||
|
||||
--- Scale an ImageData region using nearest-neighbor sampling
|
||||
--- Produces sharp, pixelated scaling - ideal for pixel art
|
||||
---@param sourceImageData love.ImageData -- Source image data
|
||||
---@param srcX number -- Source region X (0-based)
|
||||
---@param srcY number -- Source region Y (0-based)
|
||||
---@param srcW number -- Source region width
|
||||
---@param srcH number -- Source region height
|
||||
---@param destW number -- Destination width
|
||||
---@param destH number -- Destination height
|
||||
---@return love.ImageData -- Scaled image data
|
||||
function ImageScaler.scaleNearest(sourceImageData, srcX, srcY, srcW, srcH, destW, destH)
|
||||
if not sourceImageData then
|
||||
error(formatError("ImageScaler", "Source ImageData cannot be nil"))
|
||||
end
|
||||
|
||||
if srcW <= 0 or srcH <= 0 or destW <= 0 or destH <= 0 then
|
||||
error(formatError("ImageScaler", "Dimensions must be positive"))
|
||||
end
|
||||
|
||||
-- Create destination ImageData
|
||||
local destImageData = love.image.newImageData(destW, destH)
|
||||
|
||||
-- Calculate scale ratios (cached outside loops for performance)
|
||||
local scaleX = srcW / destW
|
||||
local scaleY = srcH / destH
|
||||
|
||||
-- Nearest-neighbor sampling
|
||||
for destY = 0, destH - 1 do
|
||||
for destX = 0, destW - 1 do
|
||||
-- Calculate source pixel coordinates using floor (nearest-neighbor)
|
||||
local srcPixelX = math.floor(destX * scaleX) + srcX
|
||||
local srcPixelY = math.floor(destY * scaleY) + srcY
|
||||
|
||||
-- Clamp to source bounds (safety check)
|
||||
srcPixelX = math.min(srcPixelX, srcX + srcW - 1)
|
||||
srcPixelY = math.min(srcPixelY, srcY + srcH - 1)
|
||||
|
||||
-- Sample source pixel
|
||||
local r, g, b, a = sourceImageData:getPixel(srcPixelX, srcPixelY)
|
||||
|
||||
-- Write to destination
|
||||
destImageData:setPixel(destX, destY, r, g, b, a)
|
||||
end
|
||||
end
|
||||
|
||||
return destImageData
|
||||
end
|
||||
|
||||
--- Linear interpolation helper
|
||||
--- Blends between two values based on interpolation factor
|
||||
---@param a number -- Start value
|
||||
---@param b number -- End value
|
||||
---@param t number -- Interpolation factor [0, 1]
|
||||
---@return number -- Interpolated value
|
||||
local function lerp(a, b, t)
|
||||
return a + (b - a) * t
|
||||
end
|
||||
|
||||
--- Scale an ImageData region using bilinear interpolation
|
||||
--- Produces smooth, filtered scaling - ideal for high-quality upscaling
|
||||
---@param sourceImageData love.ImageData -- Source image data
|
||||
---@param srcX number -- Source region X (0-based)
|
||||
---@param srcY number -- Source region Y (0-based)
|
||||
---@param srcW number -- Source region width
|
||||
---@param srcH number -- Source region height
|
||||
---@param destW number -- Destination width
|
||||
---@param destH number -- Destination height
|
||||
---@return love.ImageData -- Scaled image data
|
||||
function ImageScaler.scaleBilinear(sourceImageData, srcX, srcY, srcW, srcH, destW, destH)
|
||||
if not sourceImageData then
|
||||
error(formatError("ImageScaler", "Source ImageData cannot be nil"))
|
||||
end
|
||||
|
||||
if srcW <= 0 or srcH <= 0 or destW <= 0 or destH <= 0 then
|
||||
error(formatError("ImageScaler", "Dimensions must be positive"))
|
||||
end
|
||||
|
||||
-- Create destination ImageData
|
||||
local destImageData = love.image.newImageData(destW, destH)
|
||||
|
||||
-- Calculate scale ratios
|
||||
local scaleX = srcW / destW
|
||||
local scaleY = srcH / destH
|
||||
|
||||
-- Bilinear interpolation
|
||||
for destY = 0, destH - 1 do
|
||||
for destX = 0, destW - 1 do
|
||||
-- Calculate fractional source position
|
||||
local srcXf = destX * scaleX
|
||||
local srcYf = destY * scaleY
|
||||
|
||||
-- Get integer coordinates for 2x2 sampling grid
|
||||
local x0 = math.floor(srcXf)
|
||||
local y0 = math.floor(srcYf)
|
||||
local x1 = math.min(x0 + 1, srcW - 1)
|
||||
local y1 = math.min(y0 + 1, srcH - 1)
|
||||
|
||||
-- Get fractional parts for interpolation
|
||||
local fx = srcXf - x0
|
||||
local fy = srcYf - y0
|
||||
|
||||
-- Sample 4 neighboring pixels (with source offset)
|
||||
local r00, g00, b00, a00 = sourceImageData:getPixel(srcX + x0, srcY + y0)
|
||||
local r10, g10, b10, a10 = sourceImageData:getPixel(srcX + x1, srcY + y0)
|
||||
local r01, g01, b01, a01 = sourceImageData:getPixel(srcX + x0, srcY + y1)
|
||||
local r11, g11, b11, a11 = sourceImageData:getPixel(srcX + x1, srcY + y1)
|
||||
|
||||
-- Interpolate horizontally (top and bottom rows)
|
||||
local rTop = lerp(r00, r10, fx)
|
||||
local gTop = lerp(g00, g10, fx)
|
||||
local bTop = lerp(b00, b10, fx)
|
||||
local aTop = lerp(a00, a10, fx)
|
||||
|
||||
local rBottom = lerp(r01, r11, fx)
|
||||
local gBottom = lerp(g01, g11, fx)
|
||||
local bBottom = lerp(b01, b11, fx)
|
||||
local aBottom = lerp(a01, a11, fx)
|
||||
|
||||
-- Interpolate vertically (final result)
|
||||
local r = lerp(rTop, rBottom, fy)
|
||||
local g = lerp(gTop, gBottom, fy)
|
||||
local b = lerp(bTop, bBottom, fy)
|
||||
local a = lerp(aTop, aBottom, fy)
|
||||
|
||||
-- Write to destination
|
||||
destImageData:setPixel(destX, destY, r, g, b, a)
|
||||
end
|
||||
end
|
||||
|
||||
return destImageData
|
||||
end
|
||||
|
||||
return ImageScaler
|
||||
Reference in New Issue
Block a user