2012-05-27 19:20:34 +02:00
|
|
|
---------------------------------------------------------------------------
|
|
|
|
-- @author Uli Schlachter
|
|
|
|
-- @copyright 2012 Uli Schlachter
|
|
|
|
-- @release @AWESOME_VERSION@
|
2014-05-19 15:15:39 +02:00
|
|
|
-- @module gears.surface
|
2012-05-27 19:20:34 +02:00
|
|
|
---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
local setmetatable = setmetatable
|
|
|
|
local type = type
|
|
|
|
local capi = { awesome = awesome }
|
|
|
|
local cairo = require("lgi").cairo
|
2016-01-15 18:27:22 +01:00
|
|
|
local gdebug = require("gears.debug")
|
2012-05-27 19:20:34 +02:00
|
|
|
|
Bump minimum lgi dependency to 0.7.0
Before commit 1b2826 in lgi, the get_rgba() function on cairo SolidPatterns was
specified like this:
get_rgba = { ret = cairo.Status,
{ ti.double, dir = 'out' },
{ ti.double, dir = 'out' },
{ ti.double, dir = 'out' } },
The above commit fixed this (without saying so) and the code became:
get_rgba = { ret = cairo.Status,
{ ti.double, dir = 'out' },
{ ti.double, dir = 'out' },
{ ti.double, dir = 'out' },
{ ti.double, dir = 'out' } },
The prototype for the corresponding cairo function is:
cairo_public cairo_status_t
cairo_pattern_get_rgba (cairo_pattern_t *pattern,
double *red, double *green,
double *blue, double *alpha);
As you see, this functions gets four double* as arguments and it will save its
result via those pointers. Old versions of lgi call this function with too few
arguments and this will cause a segmentation fault when cairo dereferences an
invalid pointer.
Signed-off-by: Uli Schlachter <psychon@znc.in>
2014-03-17 17:52:09 +01:00
|
|
|
-- Keep this in sync with build-utils/lgi-check.sh!
|
2015-08-11 18:52:10 +02:00
|
|
|
local ver_major, ver_minor, ver_patch = string.match(require('lgi.version'), '(%d)%.(%d)%.(%d)')
|
|
|
|
if tonumber(ver_major) <= 0 and (tonumber(ver_minor) < 7 or (tonumber(ver_minor) == 7 and tonumber(ver_patch) < 1)) then
|
|
|
|
error("lgi too old, need at least version 0.7.1")
|
2012-06-05 16:22:04 +02:00
|
|
|
end
|
|
|
|
|
2012-06-12 10:13:46 +02:00
|
|
|
local surface = { mt = {} }
|
2014-03-29 21:41:47 +01:00
|
|
|
local surface_cache = setmetatable({}, { __mode = 'v' })
|
2012-05-27 19:20:34 +02:00
|
|
|
|
2016-01-15 18:27:22 +01:00
|
|
|
local function get_empty_surface()
|
|
|
|
return cairo.ImageSurface(cairo.Format.ARGB32, 0, 0)
|
|
|
|
end
|
|
|
|
|
2012-05-27 19:20:34 +02:00
|
|
|
--- Try to convert the argument into an lgi cairo surface.
|
|
|
|
-- This is usually needed for loading images by file name.
|
2016-01-15 18:27:22 +01:00
|
|
|
-- @param _surface The surface to load or nil
|
|
|
|
-- @param default The default value to return on error; when nil, then a surface
|
|
|
|
-- in an error state is returned.
|
|
|
|
-- @return The loaded surface, or the replacement default
|
|
|
|
-- @return An error message, or nil on success
|
|
|
|
function surface.load_uncached_silently(_surface, default)
|
2014-04-09 21:55:07 +02:00
|
|
|
local file
|
2016-01-15 18:27:22 +01:00
|
|
|
-- On nil, return an empty surface
|
2012-06-12 10:13:46 +02:00
|
|
|
if not _surface then
|
2016-01-15 18:27:22 +01:00
|
|
|
return get_empty_surface()
|
2012-05-27 19:20:34 +02:00
|
|
|
end
|
2014-03-29 21:41:47 +01:00
|
|
|
-- Remove from cache if it was cached
|
|
|
|
surface_cache[_surface] = nil
|
2012-05-27 19:20:34 +02:00
|
|
|
-- lgi cairo surfaces don't get changed either
|
2012-06-12 10:13:46 +02:00
|
|
|
if cairo.Surface:is_type_of(_surface) then
|
|
|
|
return _surface
|
2012-05-27 19:20:34 +02:00
|
|
|
end
|
|
|
|
-- Strings are assumed to be file names and get loaded
|
2012-06-12 10:13:46 +02:00
|
|
|
if type(_surface) == "string" then
|
2016-01-15 18:27:22 +01:00
|
|
|
local err
|
2014-04-09 21:55:07 +02:00
|
|
|
file = _surface
|
2016-01-15 18:27:22 +01:00
|
|
|
_surface, err = capi.awesome.load_image(file)
|
|
|
|
if not _surface then
|
|
|
|
if type(default) == 'nil' then
|
|
|
|
default = get_empty_surface()
|
|
|
|
end
|
|
|
|
return default, err
|
|
|
|
end
|
2012-05-27 19:20:34 +02:00
|
|
|
end
|
|
|
|
-- Everything else gets forced into a surface
|
2014-04-09 21:55:07 +02:00
|
|
|
_surface = cairo.Surface(_surface, true)
|
|
|
|
-- If we loaded a file, cache it
|
|
|
|
if file then
|
|
|
|
surface_cache[file] = _surface
|
|
|
|
end
|
|
|
|
return _surface
|
2012-05-27 19:20:34 +02:00
|
|
|
end
|
|
|
|
|
2016-01-15 18:27:22 +01:00
|
|
|
--- Try to convert the argument into an lgi cairo surface.
|
|
|
|
-- This is usually needed for loading images by file name and uses a cache.
|
|
|
|
-- In contrast to `load()`, errors are returned to the caller.
|
|
|
|
-- @param _surface The surface to load or nil
|
|
|
|
-- @param default The default value to return on error; when nil, then a surface
|
|
|
|
-- in an error state is returned.
|
|
|
|
-- @return The loaded surface, or the replacement default, or nil if called with
|
|
|
|
-- nil.
|
|
|
|
-- @return An error message, or nil on success
|
|
|
|
function surface.load_silently(_surface, default)
|
2014-03-29 21:41:47 +01:00
|
|
|
if type(_surface) == "string" then
|
|
|
|
local cache = surface_cache[_surface]
|
2014-04-09 21:55:07 +02:00
|
|
|
if cache then
|
|
|
|
return cache
|
2014-03-29 21:41:47 +01:00
|
|
|
end
|
|
|
|
end
|
2016-01-15 18:27:22 +01:00
|
|
|
return surface.load_uncached_silently(_surface, default)
|
|
|
|
end
|
|
|
|
|
|
|
|
local function do_load_and_handle_errors(_surface, func)
|
|
|
|
if type(_surface) == 'nil' then
|
|
|
|
return get_empty_surface()
|
|
|
|
end
|
|
|
|
local result, err = func(_surface, false)
|
|
|
|
if result then
|
|
|
|
return result
|
|
|
|
end
|
|
|
|
gdebug.print_error("Failed to load '" .. tostring(_surface) .. "': " .. tostring(err))
|
|
|
|
return get_empty_surface()
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Try to convert the argument into an lgi cairo surface.
|
|
|
|
-- This is usually needed for loading images by file name. Errors are handled
|
|
|
|
-- via `gears.debug.print_error`.
|
|
|
|
-- @param _surface The surface to load or nil
|
|
|
|
-- @return The loaded surface, or nil
|
|
|
|
function surface.load_uncached(_surface)
|
|
|
|
return do_load_and_handle_errors(_surface, surface.load_uncached_silently)
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Try to convert the argument into an lgi cairo surface.
|
|
|
|
-- This is usually needed for loading images by file name. Errors are handled
|
|
|
|
-- via `gears.debug.print_error`.
|
|
|
|
-- @param _surface The surface to load or nil
|
|
|
|
-- @return The loaded surface, or nil
|
|
|
|
function surface.load(_surface)
|
|
|
|
return do_load_and_handle_errors(_surface, surface.load_silently)
|
2014-03-29 21:41:47 +01:00
|
|
|
end
|
|
|
|
|
2012-06-12 10:13:46 +02:00
|
|
|
function surface.mt:__call(...)
|
|
|
|
return surface.load(...)
|
|
|
|
end
|
|
|
|
|
2014-03-16 03:00:23 +01:00
|
|
|
--- Get the size of a cairo surface
|
|
|
|
-- @param surf The surface you are interested in
|
|
|
|
-- @return The surface's width and height
|
|
|
|
function surface.get_size(surf)
|
|
|
|
local cr = cairo.Context(surf)
|
|
|
|
local x, y, w, h = cr:clip_extents()
|
|
|
|
return w - x, h - y
|
|
|
|
end
|
|
|
|
|
2015-09-05 21:12:09 +02:00
|
|
|
--- Create a copy of a cairo surface.
|
|
|
|
-- The surfaces returned by `surface.load` are cached and must not be
|
|
|
|
-- modified to avoid unintended side-effects. This function allows to create
|
|
|
|
-- a copy of a cairo surface. This copy can then be freely modified.
|
|
|
|
-- The surface returned will be as compatible as possible to the input
|
|
|
|
-- surface. For example, it will likely be of the same surface type as the
|
|
|
|
-- input. The details are explained in the `create_similar` function on a cairo
|
|
|
|
-- surface.
|
2015-08-06 18:54:06 +02:00
|
|
|
-- @param s Source surface.
|
|
|
|
-- @return The surface's duplicate.
|
|
|
|
function surface.duplicate_surface(s)
|
|
|
|
s = surface.load(s)
|
|
|
|
|
|
|
|
-- Figure out surface size (this does NOT work for unbounded recording surfaces)
|
|
|
|
local cr = cairo.Context(s)
|
|
|
|
local x, y, w, h = cr:clip_extents()
|
|
|
|
|
|
|
|
-- Create a copy
|
|
|
|
local result = s:create_similar(s.content, w - x, h - y)
|
|
|
|
cr = cairo.Context(result)
|
|
|
|
cr:set_source_surface(s, 0, 0)
|
|
|
|
cr.operator = cairo.Operator.SOURCE
|
|
|
|
cr:paint()
|
|
|
|
return result
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2012-06-12 10:13:46 +02:00
|
|
|
return setmetatable(surface, surface.mt)
|
2012-05-27 19:20:34 +02:00
|
|
|
|
|
|
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|