awesome/draw.c
Uli Schlachter bcf70f3740 Drop our use of iconv (#1772)
We were only using this for tag names. This means we are assuming that
everything is UTF8, but tag names are provided in the local locale and
need to be translated into UTF8? That makes no sense, so just drop this.

Fixes: https://github.com/awesomeWM/awesome/issues/1753
Signed-off-by: Uli Schlachter <psychon@znc.in>
2017-05-07 17:59:53 +02:00

257 lines
8.1 KiB
C

/*
* draw.c - draw functions
*
* Copyright © 2007-2009 Julien Danjou <julien@danjou.info>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include "config.h"
#include "draw.h"
#include "globalconf.h"
#include <langinfo.h>
#include <errno.h>
#include <ctype.h>
#include <math.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <cairo-xcb.h>
#include <lauxlib.h>
static cairo_user_data_key_t data_key;
static inline void
free_data(void *data)
{
p_delete(&data);
}
/** Create a surface object from this image data.
* \param width The width of the image.
* \param height The height of the image
* \param data The image's data in ARGB format, will be copied by this function.
* \return Number of items pushed on the lua stack.
*/
cairo_surface_t *
draw_surface_from_data(int width, int height, uint32_t *data)
{
unsigned long int len = width * height;
unsigned long int i;
uint32_t *buffer = p_new(uint32_t, len);
cairo_surface_t *surface;
/* Cairo wants premultiplied alpha, meh :( */
for(i = 0; i < len; i++)
{
uint8_t a = (data[i] >> 24) & 0xff;
double alpha = a / 255.0;
uint8_t r = ((data[i] >> 16) & 0xff) * alpha;
uint8_t g = ((data[i] >> 8) & 0xff) * alpha;
uint8_t b = ((data[i] >> 0) & 0xff) * alpha;
buffer[i] = (a << 24) | (r << 16) | (g << 8) | b;
}
surface =
cairo_image_surface_create_for_data((unsigned char *) buffer,
CAIRO_FORMAT_ARGB32,
width,
height,
width*4);
/* This makes sure that buffer will be freed */
cairo_surface_set_user_data(surface, &data_key, buffer, &free_data);
return surface;
}
/** Create a surface object from this pixbuf
* \param buf The pixbuf
* \return Number of items pushed on the lua stack.
*/
static cairo_surface_t *
draw_surface_from_pixbuf(GdkPixbuf *buf)
{
int width = gdk_pixbuf_get_width(buf);
int height = gdk_pixbuf_get_height(buf);
int pix_stride = gdk_pixbuf_get_rowstride(buf);
guchar *pixels = gdk_pixbuf_get_pixels(buf);
int channels = gdk_pixbuf_get_n_channels(buf);
cairo_surface_t *surface;
int cairo_stride;
unsigned char *cairo_pixels;
cairo_format_t format = CAIRO_FORMAT_ARGB32;
if (channels == 3)
format = CAIRO_FORMAT_RGB24;
surface = cairo_image_surface_create(format, width, height);
cairo_surface_flush(surface);
cairo_stride = cairo_image_surface_get_stride(surface);
cairo_pixels = cairo_image_surface_get_data(surface);
for (int y = 0; y < height; y++)
{
guchar *row = pixels;
uint32_t *cairo = (uint32_t *) cairo_pixels;
for (int x = 0; x < width; x++) {
if (channels == 3)
{
uint8_t r = *row++;
uint8_t g = *row++;
uint8_t b = *row++;
*cairo++ = (r << 16) | (g << 8) | b;
} else {
uint8_t r = *row++;
uint8_t g = *row++;
uint8_t b = *row++;
uint8_t a = *row++;
double alpha = a / 255.0;
r = r * alpha;
g = g * alpha;
b = b * alpha;
*cairo++ = (a << 24) | (r << 16) | (g << 8) | b;
}
}
pixels += pix_stride;
cairo_pixels += cairo_stride;
}
cairo_surface_mark_dirty(surface);
return surface;
}
static void
get_surface_size(cairo_surface_t *surface, int *width, int *height)
{
double x1, y1, x2, y2;
cairo_t *cr = cairo_create(surface);
cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
cairo_destroy(cr);
*width = x2 - x1;
*height = y2 - y1;
}
/** Duplicate the specified image surface.
* \param surface The surface to copy
* \return A pointer to a new cairo image surface.
*/
cairo_surface_t *
draw_dup_image_surface(cairo_surface_t *surface)
{
cairo_surface_t *res;
int width, height;
get_surface_size(surface, &width, &height);
#if CAIRO_VERSION_MAJOR == 1 && CAIRO_VERSION_MINOR > 12
res = cairo_surface_create_similar_image(surface, CAIRO_FORMAT_ARGB32, width, height);
#else
res = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
#endif
cairo_t *cr = cairo_create(res);
cairo_set_source_surface(cr, surface, 0, 0);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_paint(cr);
cairo_destroy(cr);
return res;
}
/** Load the specified path into a cairo surface
* \param L Lua state
* \param path file to load
* \param error A place to store an error message, if needed
* \return A cairo image surface or NULL on error.
*/
cairo_surface_t *
draw_load_image(lua_State *L, const char *path, GError **error)
{
cairo_surface_t *ret;
GdkPixbuf *buf = gdk_pixbuf_new_from_file(path, error);
if (!buf)
/* error was set above */
return NULL;
ret = draw_surface_from_pixbuf(buf);
g_object_unref(buf);
return ret;
}
xcb_visualtype_t *draw_find_visual(const xcb_screen_t *s, xcb_visualid_t visual)
{
xcb_depth_iterator_t depth_iter = xcb_screen_allowed_depths_iterator(s);
if(depth_iter.data)
for(; depth_iter.rem; xcb_depth_next (&depth_iter))
for(xcb_visualtype_iterator_t visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
visual_iter.rem; xcb_visualtype_next (&visual_iter))
if(visual == visual_iter.data->visual_id)
return visual_iter.data;
return NULL;
}
xcb_visualtype_t *draw_default_visual(const xcb_screen_t *s)
{
return draw_find_visual(s, s->root_visual);
}
xcb_visualtype_t *draw_argb_visual(const xcb_screen_t *s)
{
xcb_depth_iterator_t depth_iter = xcb_screen_allowed_depths_iterator(s);
if(depth_iter.data)
for(; depth_iter.rem; xcb_depth_next (&depth_iter))
if(depth_iter.data->depth == 32)
for(xcb_visualtype_iterator_t visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
visual_iter.rem; xcb_visualtype_next (&visual_iter))
return visual_iter.data;
return NULL;
}
uint8_t draw_visual_depth(const xcb_screen_t *s, xcb_visualid_t vis)
{
xcb_depth_iterator_t depth_iter = xcb_screen_allowed_depths_iterator(s);
if(depth_iter.data)
for(; depth_iter.rem; xcb_depth_next (&depth_iter))
for(xcb_visualtype_iterator_t visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
visual_iter.rem; xcb_visualtype_next (&visual_iter))
if(vis == visual_iter.data->visual_id)
return depth_iter.data->depth;
fatal("Could not find a visual's depth");
}
void draw_test_cairo_xcb(void)
{
xcb_pixmap_t pixmap = xcb_generate_id(globalconf.connection);
xcb_create_pixmap(globalconf.connection, globalconf.default_depth, pixmap,
globalconf.screen->root, 1, 1);
cairo_surface_t *surface = cairo_xcb_surface_create(globalconf.connection,
pixmap, globalconf.visual, 1, 1);
if(cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS)
fatal("Could not set up display: got cairo surface with status %s",
cairo_status_to_string(cairo_surface_status(surface)));
cairo_surface_finish(surface);
cairo_surface_destroy(surface);
xcb_free_pixmap(globalconf.connection, pixmap);
}
// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80