local love11 = love.getVersion() == 11 local getDPI = love11 and love.window.getDPIScale or love.window.getPixelScale local windowUpdateMode = love11 and love.window.updateMode or function(width, height, settings) local _, _, flags = love.window.getMode() for k, v in pairs(settings) do flags[k] = v end love.window.setMode(width, height, flags) end local push = { defaults = { fullscreen = false, resizable = false, pixelperfect = false, highdpi = true, canvas = true, stencil = true } } setmetatable(push, push) function push:applySettings(settings) for k, v in pairs(settings) do self["_" .. k] = v end end function push:resetSettings() return self:applySettings(self.defaults) end function push:setupScreen(WWIDTH, WHEIGHT, RWIDTH, RHEIGHT, settings) settings = settings or {} self._WWIDTH, self._WHEIGHT = WWIDTH, WHEIGHT self._RWIDTH, self._RHEIGHT = RWIDTH, RHEIGHT self:applySettings(self.defaults) --set defaults first self:applySettings(settings) --then fill with custom settings windowUpdateMode(self._RWIDTH, self._RHEIGHT, { fullscreen = self._fullscreen, resizable = self._resizable, highdpi = self._highdpi }) self:initValues() if self._canvas then self:setupCanvas({ "default" }) --setup canvas end self._borderColor = {0, 0, 0} self._drawFunctions = { ["start"] = self.start, ["end"] = self.finish } return self end function push:setupCanvas(canvases) table.insert(canvases, { name = "_render", private = true }) --final render self._canvas = true self.canvases = {} for i = 1, #canvases do push:addCanvas(canvases[i]) end return self end function push:addCanvas(params) table.insert(self.canvases, { name = params.name, private = params.private, shader = params.shader, canvas = love.graphics.newCanvas(self._WWIDTH, self._WHEIGHT), stencil = params.stencil or self._stencil }) end function push:setCanvas(name) if not self._canvas then return true end return love.graphics.setCanvas(self:getCanvasTable(name).canvas) end function push:getCanvasTable(name) for i = 1, #self.canvases do if self.canvases[i].name == name then return self.canvases[i] end end end function push:setShader(name, shader) if not shader then self:getCanvasTable("_render").shader = name else self:getCanvasTable(name).shader = shader end end function push:initValues() self._PSCALE = (not love11 and self._highdpi) and getDPI() or 1 self._SCALE = { x = self._RWIDTH/self._WWIDTH * self._PSCALE, y = self._RHEIGHT/self._WHEIGHT * self._PSCALE } if self._stretched then --if stretched, no need to apply offset self._OFFSET = {x = 0, y = 0} else local scale = math.min(self._SCALE.x, self._SCALE.y) if self._pixelperfect then scale = math.floor(scale) end self._OFFSET = {x = (self._SCALE.x - scale) * (self._WWIDTH/2), y = (self._SCALE.y - scale) * (self._WHEIGHT/2)} self._SCALE.x, self._SCALE.y = scale, scale --apply same scale to X and Y end self._GWIDTH = self._RWIDTH * self._PSCALE - self._OFFSET.x * 2 self._GHEIGHT = self._RHEIGHT * self._PSCALE - self._OFFSET.y * 2 end function push:apply(operation, shader) self._drawFunctions[operation](self, shader) end function push:start() if self._canvas then love.graphics.push() love.graphics.setCanvas({ self.canvases[1].canvas, stencil = self.canvases[1].stencil }) else love.graphics.translate(self._OFFSET.x, self._OFFSET.y) love.graphics.setScissor(self._OFFSET.x, self._OFFSET.y, self._WWIDTH*self._SCALE.x, self._WHEIGHT*self._SCALE.y) love.graphics.push() love.graphics.scale(self._SCALE.x, self._SCALE.y) end end function push:applyShaders(canvas, shaders) local _shader = love.graphics.getShader() if #shaders <= 1 then love.graphics.setShader(shaders[1]) love.graphics.draw(canvas) else local _canvas = love.graphics.getCanvas() local _tmp = self:getCanvasTable("_tmp") if not _tmp then --create temp canvas only if needed self:addCanvas({ name = "_tmp", private = true, shader = nil }) _tmp = self:getCanvasTable("_tmp") end love.graphics.push() love.graphics.origin() local outputCanvas for i = 1, #shaders do local inputCanvas = i % 2 == 1 and canvas or _tmp.canvas outputCanvas = i % 2 == 0 and canvas or _tmp.canvas love.graphics.setCanvas(outputCanvas) love.graphics.clear() love.graphics.setShader(shaders[i]) love.graphics.draw(inputCanvas) love.graphics.setCanvas(inputCanvas) end love.graphics.pop() love.graphics.setCanvas(_canvas) love.graphics.draw(outputCanvas) end love.graphics.setShader(_shader) end function push:finish(shader) love.graphics.setBackgroundColor(unpack(self._borderColor)) if self._canvas then local _render = self:getCanvasTable("_render") love.graphics.pop() local white = love11 and 1 or 255 love.graphics.setColor(white, white, white) --draw canvas love.graphics.setCanvas(_render.canvas) for i = 1, #self.canvases do --do not draw _render yet local _table = self.canvases[i] if not _table.private then local _canvas = _table.canvas local _shader = _table.shader self:applyShaders(_canvas, type(_shader) == "table" and _shader or { _shader }) end end love.graphics.setCanvas() --draw render love.graphics.translate(self._OFFSET.x, self._OFFSET.y) local shader = shader or _render.shader love.graphics.push() love.graphics.scale(self._SCALE.x, self._SCALE.y) self:applyShaders(_render.canvas, type(shader) == "table" and shader or { shader }) love.graphics.pop() --clear canvas for i = 1, #self.canvases do love.graphics.setCanvas(self.canvases[i].canvas) love.graphics.clear() end love.graphics.setCanvas() love.graphics.setShader() else love.graphics.pop() love.graphics.setScissor() end end function push:setBorderColor(color, g, b) self._borderColor = g and {color, g, b} or color end function push:toGame(x, y) x, y = x - self._OFFSET.x, y - self._OFFSET.y local normalX, normalY = x / self._GWIDTH, y / self._GHEIGHT x = (x >= 0 and x <= self._WWIDTH * self._SCALE.x) and normalX * self._WWIDTH or nil y = (y >= 0 and y <= self._WHEIGHT * self._SCALE.y) and normalY * self._WHEIGHT or nil return x, y end --doesn't work - TODO function push:toReal(x, y) return x + self._OFFSET.x, y + self._OFFSET.y end function push:switchFullscreen(winw, winh) self._fullscreen = not self._fullscreen local windowWidth, windowHeight = love.window.getDesktopDimensions() if self._fullscreen then --save windowed dimensions for later self._WINWIDTH, self._WINHEIGHT = self._RWIDTH, self._RHEIGHT elseif not self._WINWIDTH or not self._WINHEIGHT then self._WINWIDTH, self._WINHEIGHT = windowWidth * .5, windowHeight * .5 end self._RWIDTH = self._fullscreen and windowWidth or winw or self._WINWIDTH self._RHEIGHT = self._fullscreen and windowHeight or winh or self._WINHEIGHT self:initValues() love.window.setFullscreen(self._fullscreen, "desktop") if not self._fullscreen and (winw or winh) then windowUpdateMode(self._RWIDTH, self._RHEIGHT) --set window dimensions end end function push:resize(w, h) if self._highdpi then w, h = w / self._PSCALE, h / self._PSCALE end self._RWIDTH = w self._RHEIGHT = h self:initValues() end function push:getWidth() return self._WWIDTH end function push:getHeight() return self._WHEIGHT end function push:getDimensions() return self._WWIDTH, self._WHEIGHT end return push