mirror of
https://github.com/love2d/love.git
synced 2026-08-18 03:34:23 +02:00
Implemented efficient FFI versions of ImageData methods.
Note that ImageData:mapPixel (with its optional sub-rectangle parameters) is often many times faster than a loop of ImageData:setPixel and/or getPixel, for anything more than a few pixels.
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
R"luastring"--(
|
||||
-- DO NOT REMOVE THE ABOVE LINE. It is used to load this file as a C++ string.
|
||||
-- There is a matching delimiter at the bottom of the file.
|
||||
|
||||
--[[
|
||||
Copyright (c) 2006-2015 LOVE Development Team
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
--]]
|
||||
|
||||
local ImageData_mt, ffifuncspointer = ...
|
||||
|
||||
local tonumber, assert = tonumber, assert
|
||||
local type, pcall = type, pcall
|
||||
|
||||
local function inside(x, y, w, h)
|
||||
return x >= 0 and x < w and y >= 0 and y < h
|
||||
end
|
||||
|
||||
-- Implement thread-safe ImageData:mapPixel regardless of whether the FFI is
|
||||
-- used or not.
|
||||
function ImageData_mt.__index:mapPixel(func, ix, iy, iw, ih)
|
||||
local idw, idh = self:getDimensions()
|
||||
|
||||
ix = ix or 0
|
||||
iy = iy or 0
|
||||
iw = iw or idw
|
||||
ih = ih or idh
|
||||
|
||||
assert(type(ix) == "number", "Invalid argument #2 to ImageData:mapPixel (expected number)")
|
||||
assert(type(iy) == "number", "Invalid argument #3 to ImageData:mapPixel (expected number)")
|
||||
assert(type(iw) == "number", "Invalid argument #4 to ImageData:mapPixel (expected number)")
|
||||
assert(type(ih) == "number", "Invalid argument #5 to ImageData:mapPixel (expected number)")
|
||||
|
||||
assert(type(func) == "function", "Invalid argument #1 to ImageData:mapPixel (expected function)")
|
||||
assert(inside(ix, iy, idw, idh) and inside(ix+iw-1, iy+ih-1, idw, idh), "Invalid rectangle dimensions")
|
||||
|
||||
-- performAtomic and mapPixelUnsafe have Lua-C API and FFI versions.
|
||||
self:_performAtomic(self._mapPixelUnsafe, self, func, ix, iy, iw, ih)
|
||||
end
|
||||
|
||||
|
||||
-- Everything below this point is efficient FFI replacements for existing
|
||||
-- ImageData functionality.
|
||||
|
||||
if type(jit) ~= "table" or not jit.status() then
|
||||
-- LuaJIT's FFI is *much* slower than LOVE's regular methods when the JIT
|
||||
-- compiler is disabled.
|
||||
return
|
||||
end
|
||||
|
||||
local status, ffi = pcall(require, "ffi")
|
||||
if not status then return end
|
||||
|
||||
pcall(ffi.cdef, [[
|
||||
typedef struct Proxy Proxy;
|
||||
|
||||
typedef struct FFI_ImageData
|
||||
{
|
||||
void (*lockMutex)(Proxy *p);
|
||||
void (*unlockMutex)(Proxy *p);
|
||||
} FFI_ImageData;
|
||||
|
||||
typedef struct ImageData_Pixel
|
||||
{
|
||||
uint8_t r, g, b, a;
|
||||
} ImageData_Pixel;
|
||||
]])
|
||||
|
||||
local ffifuncs = ffi.cast("FFI_ImageData *", ffifuncspointer)
|
||||
|
||||
local pixelpointer = ffi.typeof("ImageData_Pixel *")
|
||||
|
||||
local _getWidth = ImageData_mt.__index.getWidth
|
||||
local _getHeight = ImageData_mt.__index.getHeight
|
||||
local _getDimensions = ImageData_mt.__index.getDimensions
|
||||
|
||||
-- Table which holds ImageData objects as keys, and information about the objects
|
||||
-- as values. Uses weak keys so the ImageData objects can still be GC'd properly.
|
||||
local objectcache = setmetatable({}, {
|
||||
__mode = "k",
|
||||
__index = function(self, imagedata)
|
||||
local width, height = _getDimensions(imagedata)
|
||||
local pointer = ffi.cast(pixelpointer, imagedata:getPointer())
|
||||
|
||||
local p = {
|
||||
width = width,
|
||||
height = height,
|
||||
pointer = pointer,
|
||||
}
|
||||
|
||||
self[imagedata] = p
|
||||
return p
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
-- Overwrite existing functions with new FFI versions.
|
||||
|
||||
function ImageData_mt.__index:_performAtomic(...)
|
||||
ffifuncs.lockMutex(self)
|
||||
local success, err = pcall(...)
|
||||
ffifuncs.unlockMutex(self)
|
||||
|
||||
if not success then
|
||||
error(err, 3)
|
||||
end
|
||||
end
|
||||
|
||||
function ImageData_mt.__index:_mapPixelUnsafe(func, ix, iy, iw, ih)
|
||||
local p = objectcache[self]
|
||||
local idw, idh = p.width, p.height
|
||||
|
||||
local pixels = p.pointer
|
||||
|
||||
for y=iy, iy+ih-1 do
|
||||
for x=ix, ix+iw-1 do
|
||||
local p = pixels[y*idw+x]
|
||||
local r, g, b, a = func(x, y, tonumber(p.r), tonumber(p.g), tonumber(p.b), tonumber(p.a))
|
||||
pixels[y*idw+x].r = r
|
||||
pixels[y*idw+x].g = g
|
||||
pixels[y*idw+x].b = b
|
||||
pixels[y*idw+x].a = a == nil and 255 or a
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ImageData_mt.__index:getPixel(x, y)
|
||||
local p = objectcache[self]
|
||||
assert(inside(x, y, p.width, p.height), "Attempt to get out-of-range pixel!")
|
||||
|
||||
ffifuncs.lockMutex(self)
|
||||
local pixel = p.pointer[y * p.width + x]
|
||||
local r, g, b, a = tonumber(pixel.r), tonumber(pixel.g), tonumber(pixel.b), tonumber(pixel.a)
|
||||
ffifuncs.unlockMutex(self)
|
||||
|
||||
return r, g, b, a
|
||||
end
|
||||
|
||||
local temppixel = ffi.new("ImageData_Pixel")
|
||||
|
||||
function ImageData_mt.__index:setPixel(x, y, r, g, b, a)
|
||||
local p = objectcache[self]
|
||||
assert(inside(x, y, p.width, p.height), "Attempt to set out-of-range pixel!")
|
||||
|
||||
if type(r) == "table" then
|
||||
local t = r
|
||||
r, g, b, a = t[1], t[2], t[3], t[4]
|
||||
end
|
||||
|
||||
temppixel.r = r
|
||||
temppixel.g = g
|
||||
temppixel.b = b
|
||||
temppixel.a = a == nil and 255 or a
|
||||
|
||||
ffifuncs.lockMutex(self)
|
||||
p.pointer[y * p.width + x] = temppixel
|
||||
ffifuncs.unlockMutex(self)
|
||||
end
|
||||
|
||||
function ImageData_mt.__index:getWidth()
|
||||
return objectcache[self].width
|
||||
end
|
||||
|
||||
function ImageData_mt.__index:getHeight()
|
||||
return objectcache[self].height
|
||||
end
|
||||
|
||||
function ImageData_mt.__index:getDimensions()
|
||||
local p = objectcache[self]
|
||||
return p.width, p.height
|
||||
end
|
||||
|
||||
-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
|
||||
--)luastring"--"
|
||||
Reference in New Issue
Block a user