From 5b5f91524ab71c9aa76b8eab0cf93a73ad6a8a88 Mon Sep 17 00:00:00 2001 From: xgoff Date: Wed, 1 Aug 2012 22:24:34 -0500 Subject: initial commit --- fakecanvas.lua | 254 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 fakecanvas.lua (limited to 'fakecanvas.lua') diff --git a/fakecanvas.lua b/fakecanvas.lua new file mode 100644 index 0000000..bc2e457 --- /dev/null +++ b/fakecanvas.lua @@ -0,0 +1,254 @@ +assert(debug, "fake canvases require access to the debug library") + +local canvas_supported = love.graphics.isSupported "canvas" +local npot_supported = love.graphics.isSupported "npot" +local pixeleffect_supported = love.graphics.isSupported "pixeleffect" + +local function nextpo2 (x) return 2^math.ceil(math.log(x) / math.log(2)) end +local function prevpo2 (x) return 2^math.floor(math.log(x) / math.log(2)) end + +local _types = { "Canvas" == true, "Object" == true, "Drawable" == true } +local canvas = { } +local canvasmt = { __index = canvas } + +local canvases = setmetatable({ }, { __mode = "k" }) + +function canvas:clear (...) -- other option is chucking out the imagedata and creating a new one, but i'd probably end up using mapPixel anyway + local nargs = select("#", ...) + + if nargs == 0 then + canvases[self]._imagedata:mapPixel(function () return 0, 0, 0, 0 end) + elseif nargs == 1 and type(...) == "table" then + local t = ... + local r, g, b, a = tonumber(t[1]) or 0, tonumber(t[2]) or 0, tonumber(t[3]) or 0, tonumber(t[4]) or 255 + canvases[self]._imagedata:mapPixel(function () return r, g, b, a end) + else + local r, g, b, a = ... + r, g, b, a = tonumber(r) or 0, tonumber(g) or 0, tonumber(b) or 0, tonumber(a) or 255 + canvases[self]._imagedata:mapPixel(function () return r, g, b, a end) + end +end + +function canvas:getFilter () + return canvases[self]._image:getFilter() +end + +function canvas:getImageData () + return canvases[self]._imagedata +end + +function canvas:getWrap () + return canvases[self]._image:getWrap() +end + +function canvas:renderTo (renderfunc) + love.graphics.setCanvas(self) + renderfunc() + love.graphics.setCanvas() +end + +function canvas:setFilter (min, mag) + canvases[self]._image:setFilter(min, mag) +end + +function canvas:setWrap (h, v) + canvases[self]._image:setWrap(h, v) +end + +function canvas:type () + return "Canvas" +end + +function canvas:typeOf (type) + return _types[type] +end + +-- internal +function canvas:_getImage () + return canvases[self]._image +end + +function canvas:_getQuad () + return canvases[self]._quad +end + +local function Canvas (width, height) + local c = { } + + local sw, sh = love.graphics.getWidth(), love.graphics.getHeight() + + local w, h + if npot_supported then -- awesome, just limit to screen size + w = math.min(sw, tonumber(width) or sw) + h = math.min(sh, tonumber(height) or sh) + else -- well that sucks + w = math.min(prevpo2(sw), nextpo2(tonumber(width) or 1)) + h = math.min(prevpo2(sh), nextpo2(tonumber(height) or 1)) + end + + c._imagedata = love.image.newImageData(w, h) + c._image = love.graphics.newImage(c._imagedata) + c._quad = love.graphics.newQuad(0, 0, w, h, w, h) + + c._quad:flip(false, true) -- flip vertically part 0 + + local p = newproxy(true) + + canvases[p] = c + + getmetatable(p).__index = canvasmt.__index + + return p --setmetatable(p, canvasmt) +end + +local current_canvas +local function getCanvas () + return current_canvas +end + +local _fb_state +local function savefbstate () + _fb_state = { + color = { love.graphics.getBackgroundColor() }, + data = love.graphics.newScreenshot(), + scissor = { love.graphics.getScissor() }, + } + _fb_state.image = love.graphics.newImage(_fb_state.data) + --_fb_state.data:encode("__fb.png") +end + +savefbstate() + +local function setCanvas (...) + assert(select("#", ...) == 0 or (select("#", ...) == 1 and type(...) == "userdata"), "Incorrect parameter type: expected userdata") + local to = ... + if to then + current_canvas = canvases[to] + --print "saving background state" + savefbstate() + + --print "rendering setup" + love.graphics.setScissor() + love.graphics.setBackgroundColor(0, 0, 0, 0) + love.graphics.clear() + else + --print "saving to canvas" + local tempdata = love.graphics.newScreenshot() + + love.graphics.setBackgroundColor(0, 0, 0, 0) + love.graphics.clear() + + -- flip vertically (unfortunately) so it can later be drawn unflipped in order to match texcoords of real canvases. part 1 + local flipped = love.graphics.newImage(tempdata) + love.graphics.draw(flipped, 0, current_canvas._imagedata:getHeight(), 0, 1, -1) + + local newdata = love.graphics.newScreenshot() + + --newdata:encode("__canvas.png") + + current_canvas._imagedata:paste(newdata, 0, 0, 0, 0, current_canvas._imagedata:getWidth(), current_canvas._imagedata:getHeight()) + current_canvas._image = love.graphics.newImage(current_canvas._imagedata) -- apparently images don't update when their imagedata changes, so + + --print "restoring background state" + love.graphics.setBackgroundColor(0, 0, 0, 0) + love.graphics.clear() + love.graphics.clear() + love.graphics.setScissor() + love.graphics.draw(_fb_state.image, 0, 0) + love.graphics.setBackgroundColor(unpack(_fb_state.color)) + love.graphics.setScissor(unpack(_fb_state.scissor)) + current_canvas = nil + end +end + +local registry = debug.getregistry() -- naughty! + + -- throwaway, forces LOVE to load the :send() method +love.graphics.newPixelEffect [[vec4 effect( vec4 x, Image y, vec2 z, vec2 w) { return vec4(0, 0, 0, 0); }]] + +local _love_funcs = { + getCanvas = love.graphics.getCanvas, + setCanvas = love.graphics.setCanvas, + newCanvas = love.graphics.newCanvas, + + draw = love.graphics.draw, + drawq = love.graphics.drawq, + + pe_send = registry.PixelEffect.send, + --technically sendCanvas should also be wrapped but that's not officially exposed +} +local _wrap_funcs = { + getCanvas = getCanvas, + setCanvas = setCanvas, + newCanvas = Canvas, + + draw = function (obj, x, y, r, sw, sh, ox, oy, kx, ky) + if canvases[obj] then + return _love_funcs.drawq(obj:_getImage(), obj:_getQuad(), x, y, r, sw, sh, ox, oy, kx, ky) -- flip texcoords part 2 + end + return _love_funcs.draw(obj, x, y, r, sw, sh, ox, oy, kx, ky) + end, + drawq = function (obj, x, y, r, sw, sh, ox, oy, kx, ky) + if canvases[obj] then + return _love_funcs.drawq(obj:_getImage(), x, y, r, sw, sh, ox, oy, kx, ky) + end + return _love_funcs.drawq(obj, x, y, r, sw, sh, ox, oy, kx, ky) + end, + + pe_send = function (pe, name, data) + if canvases[data] then + return _love_funcs.pe_send(pe, name, data:_getImage()) + end + return _love_funcs.pe_send(pe, name, data) + end, +} + +local M = { } + +-- enable use of fake canvases +-- state: +-- true: use fake canvases even if real ones are supported +-- false: disable canvases entirely +-- nil: use real or fake canvases based on support +function M.enable (state) + if state == true or not canvas_supported then + love.graphics.getCanvas = _wrap_funcs.getCanvas + love.graphics.setCanvas = _wrap_funcs.setCanvas + love.graphics.newCanvas = _wrap_funcs.newCanvas + love.graphics.draw = _wrap_funcs.draw + love.graphics.drawq = _wrap_funcs.drawq + + registry.PixelEffect.send = _wrap_funcs.pe_send + elseif state == false then + love.graphics.getCanvas = function () return nil end + love.graphics.setCanvas = function () end + love.graphics.newCanvas = function () error("canvases disabled", 2) end + + love.graphics.draw = _love_funcs.draw + love.graphics.drawq = _love_funcs.drawq + + registry.PixelEffect.send = _love_funcs.pe_send + elseif state == nil and canvas_supported then + love.graphics.getCanvas = _love_funcs.getCanvas + love.graphics.setCanvas = _love_funcs.setCanvas + love.graphics.newCanvas = _love_funcs.newCanvas + love.graphics.draw = _love_funcs.draw + love.graphics.drawq = _love_funcs.drawq + + registry.PixelEffect.send = _love_funcs.pe_send + end + return M +end + +function M.getMaxCanvasSize (hw, hh) + local w, h = love.graphics.getWidth(), love.graphics.getHeight() + if npot_supported then + return math.min(hw, w), math.min(hh, h) + else + return prevpo2(math.min(hw, w)), prevpo2(math.min(hh, h)) + end +end + +M.enable() + +return M \ No newline at end of file -- cgit v1.2.3-54-g00ecf