我正在尝试将云添加到Xscreensaver。我喜欢“等离子”云的外观,所以我试图将基于perlin噪声的云绘制到Xscreensaver的背景中。我有一些代码,它创建一个数组,其中包含构成perlin噪音的颜色值。我需要做的就是从这个数组中创建一个图像,并将其设置为此Xscreensaver中的背景。
如何从此阵列生成图像?我已经研究过使用纯Xlib,但这是一项艰巨的任务。所以,如果有一种方法可以使用Cairo从数组生成图像,这将是很好的。此外,数组中的值介于0和1之间。
答案 0 :(得分:1)
开罗的功能是cairo_image_surface_create_for_data(),但是将数据放入适当的布局以供开罗阅读它可能有点令人生畏。内存中图像格式的详细信息(描述为here)位于cairo.h
头文件本身(在注释中),而不是手册中。
另一个 gotcha 是:确保使用cairo_format_stride_for_width()来获取行的大小,因为可能存在您不能(不需要)计算的填充要求你自己。
尝试CAIRO_FORMAT_A8
或CAIRO_FORMAT_A1
可能很有吸引力,因为这是一个1位深的图像;但我发现1位图像更容易使用CAIRO_FORMAT_RGB24
并将红色绿色和蓝色值设置为0或255. A *格式会影响 alpha 频道,而不是图像数据通道,所以使用它,你仍然需要另一个RGB数据源。不透明没有与Transparent nothing 一样不可见。位的排列也取决于底层机器的字节顺序,因此直接使用32位值(如果需要,可以使用uint32_t
,但是你的printf格式说明符会变得很残忍;所以我坚持使用{{ 1}})。如果要移动整数值,则数据的字节顺序将自然地反映机器的字节顺序。
以下是long
的相关信息:
cairo.h
另一个答案中我的example code是一个非常糟糕的例子。但就是这样,因为我写这篇文章时根本找不到任何例子,所以... ex nihilo nihil 。它尝试使用已知为big-endian的源数据打包相应的数据阵列,并且可以是1位,2位,4位或8位深。 Egad,它甚至有无辜的小声明
/**
* cairo_format_t:
* @CAIRO_FORMAT_INVALID: no such format exists or is supported.
* @CAIRO_FORMAT_ARGB32: each pixel is a 32-bit quantity, with
* alpha in the upper 8 bits, then red, then green, then blue.
* The 32-bit quantities are stored native-endian. Pre-multiplied
* alpha is used. (That is, 50% transparent red is 0x80800000,
* not 0x80ff0000.) (Since 1.0)
* @CAIRO_FORMAT_RGB24: each pixel is a 32-bit quantity, with
* the upper 8 bits unused. Red, Green, and Blue are stored
* in the remaining 24 bits in that order. (Since 1.0)
* @CAIRO_FORMAT_A8: each pixel is a 8-bit quantity holding
* an alpha value. (Since 1.0)
* @CAIRO_FORMAT_A1: each pixel is a 1-bit quantity holding
* an alpha value. Pixels are packed together into 32-bit
* quantities. The ordering of the bits matches the
* endianess of the platform. On a big-endian machine, the
* first pixel is in the uppermost bit, on a little-endian
* machine the first pixel is in the least-significant bit. (Since 1.0)
* @CAIRO_FORMAT_RGB16_565: each pixel is a 16-bit quantity
* with red in the upper 5 bits, then green in the middle
* 6 bits, and blue in the lower 5 bits. (Since 1.2)
* @CAIRO_FORMAT_RGB30: like RGB24 but with 10bpc. (Since 1.12)
*
* #cairo_format_t is used to identify the memory format of
* image data.
*
* New entries may be added in future versions.
*
* Since: 1.0
**/
typedef enum _cairo_format {
CAIRO_FORMAT_INVALID = -1,
CAIRO_FORMAT_ARGB32 = 0,
CAIRO_FORMAT_RGB24 = 1,
CAIRO_FORMAT_A8 = 2,
CAIRO_FORMAT_A1 = 3,
CAIRO_FORMAT_RGB16_565 = 4,
CAIRO_FORMAT_RGB30 = 5
} cairo_format_t;
以递归方式调用整个解释器,这使 run(st);
机制到error
机制到正确的解释器实例。这是使用longjmp
的一个非常可怕的例子。但是......有人告诉我一个更好的。请!
这是一个更简单的例子。我没有对它进行测试,但我认为这是一种更简单的方法来做你需要的事情。
cairo_image_surface_create_for_data()
返回的数据适合传递给#include <stdint.h> /* uint32_t */
uint32_t datasamp(double d) { // convert floating point to rgb-byte-field integer
uint32_t u;
u = d * 255; // [0.0 .. 1.0] -> [0 .. 255]
return u<<16 | u<<8 | u; // r = g = b = u 0x00rrggbb
}
// samp is a 2D double array
// double samp[hgt][wid];
uint32_t *imagedata(int wid, int hgt, double *samp){
int stride;
uint32_t *data;
int i,j;
stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, wid);
data = malloc(stride*hgt*sizeof(uint32_t)); //use stride instead of width
for (i=0; i < hgt; i++) {
for (j=0; j < wid; j++) {
data[i*stride + j] = // use stride for data row width
datasamp(samp[i*wid + j]); // use wid as normal for source array
}
}
return data;
}
。重要的是使用cairo_image_surface_create_for_data
作为行宽,即使源数据的排列方式不同(这里只有stride
宽)。
哦,这是一种反向抛光的“应用程序匈牙利语”,我在这里用于命名约定。所以wid
表示“图像&lt; - 数据”。 imagedata
表示“data&lt; - samp”。
答案 1 :(得分:0)
在旧目录中捣乱,我找到了一个使用CAIRO_FORMAT_A1
和cairo_mask_surface
的示例,它可能比其他示例更接近您想要的内容(并且掩盖了我上面的一些断言)。这一个是完整的。使用此makefile编译
CFLAGS=-I/usr/include/cairo #-Wa,-alh
LDLIBS=-lcairo
使用make mask
/* mask.c test program for cairo bit mask
makes a big blue turkey from the Postscript manual */
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <cairo.h>
#include <X11/Xlib.h>
#include <cairo-xlib.h>
#include <unistd.h>
enum { little, big } endian = little;
unsigned char reversebits (unsigned char b) {
return (b & 0x01? 0x80: 0)
| (b & 0x02? 0x40: 0)
| (b & 0x04? 0x20: 0)
| (b & 0x08? 0x10: 0)
| (b & 0x10? 0x08: 0)
| (b & 0x20? 0x04: 0)
| (b & 0x40? 0x02: 0)
| (b & 0x80? 0x01: 0)
;
}
void paintmask(cairo_t *cr, unsigned char *samp, int w, int h) {
int span; /* width in bytes */
int stride; /* width in words */
cairo_surface_t *mask;
unsigned char *data;
int i,j,k;
uint32_t u;
stride = cairo_format_stride_for_width(CAIRO_FORMAT_A1, w);
/* stride = (w/32) + (w%32 ? 1 : 0) */
span = w/8 + (w%8? 1: 0);
printf("stride = %d\n", stride);
data = malloc(h * stride);
/* convert bytes to 32bit quantities matching
endianness of the machine */
/* each row */
for (i = 0; i < h; i++) {
/* each 32bit int in row */
for (j = 0; j < stride/4; j++) {
u = 0; /* zero the word */
/* each 8bit byte in 32bit int from samples */
for (k = 0; k < 4; k++) {
uint8_t b;
u <<= 8;
if (j*4+k < span) {
/* postscript input is always big-endian */
/* so grab most-significant byte */
b = samp[i*span + j*4 + k];
if (endian == little) {
//b = samp[i*span + j*4 + (4-1-k)];
b = reversebits(b);
}
u |= b;
}
//printf("%X\n", u);
} /* k */
printf("%08X\n", u);
*((uint32_t *)(data + i*stride + j)) = u;
} /* j */
} /* i */
mask = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_A1, w, h, stride);
cairo_mask_surface(cr, mask, 0, 0);
}
int main (int argc, char *argv[])
{
int width = 480;
int height = 460;
Display *dis;
int scr;
int depth;
Visual *vis;
XSetWindowAttributes attr;
unsigned long attrmask;
Window win;
cairo_surface_t *surface;
cairo_t *cr;
dis = XOpenDisplay(NULL);
scr = DefaultScreen(dis);
depth = DefaultDepth(dis, scr);
vis = DefaultVisual(dis, scr);
attr.background_pixel = WhitePixel(dis, scr);
attr.border_pixel = BlackPixel(dis, scr);
attr.event_mask = ExposureMask | StructureNotifyMask | ButtonPressMask;
attrmask = CWColormap | CWBackPixel | CWBorderPixel | CWEventMask;
win = XCreateWindow(dis, RootWindow(dis, scr),
200, 10, //pos
width, height, 5, //width height border
depth,
InputOutput,
vis,
attrmask, &attr);
XMapWindow(dis, win);
surface = cairo_xlib_surface_create(dis, win, vis, width, height);
cr = cairo_create(surface);
cairo_scale(cr, 10, 10);
cairo_set_source_rgb(cr, 0, 0, 1);
{
unsigned char samp[] = {
0x00, 0x3B, 0x00,
0x00, 0x27, 0x00,
0x00, 0x24, 0x80,
0x0E, 0x49, 0x40,
0x11, 0x49, 0x20,
0x14, 0xB2, 0x20,
0x3C, 0xB6, 0x50,
0x75, 0xFE, 0x88,
0x17, 0xFF, 0x8C,
0x17, 0x5F, 0x14,
0x1C, 0x07, 0xE2,
0x38, 0x03, 0xC4,
0x70, 0x31, 0x82,
0xF8, 0xED, 0xFC,
0xB2, 0xBB, 0xC2,
0xBB, 0x6F, 0x84,
0x31, 0xBF, 0xC2,
0x18, 0xEA, 0x3C,
0x0E, 0x3E, 0x00,
0x07, 0xFC, 0x00,
0x03, 0xF8, 0x00,
0x1E, 0x18, 0x00,
0x1F, 0xF8, 0x00 };
/*
*/
unsigned char samp2[] = {
0x00, 0x3B, 0x00, 0x00, 0x3B, 0x00,
0x00, 0x27, 0x00, 0x00, 0x27, 0x00,
0x00, 0x24, 0x80, 0x00, 0x24, 0x80,
0x0E, 0x49, 0x40, 0x0E, 0x49, 0x40,
0x11, 0x49, 0x20, 0x11, 0x49, 0x20,
0x14, 0xB2, 0x20, 0x14, 0xB2, 0x20,
0x3C, 0xB6, 0x50, 0x3C, 0xB6, 0x50,
0x75, 0xFE, 0x88, 0x75, 0xFE, 0x88,
0x17, 0xFF, 0x8C, 0x17, 0xFF, 0x8C,
0x17, 0x5F, 0x14, 0x17, 0x5F, 0x14,
0x1C, 0x07, 0xE2, 0x1C, 0x07, 0xE2,
0x38, 0x03, 0xC4, 0x38, 0x03, 0xC4,
0x70, 0x31, 0x82, 0x70, 0x31, 0x82,
0xF8, 0xED, 0xFC, 0xF8, 0xED, 0xFC,
0xB2, 0xBB, 0xC2, 0xB2, 0xBB, 0xC2,
0xBB, 0x6F, 0x84, 0xBB, 0x6F, 0x84,
0x31, 0xBF, 0xC2, 0x31, 0xBF, 0xC2,
0x18, 0xEA, 0x3C, 0x18, 0xEA, 0x3C,
0x0E, 0x3E, 0x00, 0x0E, 0x3E, 0x00,
0x07, 0xFC, 0x00, 0x07, 0xFC, 0x00,
0x03, 0xF8, 0x00, 0x03, 0xF8, 0x00,
0x1E, 0x18, 0x00, 0x1E, 0x18, 0x00,
0x1F, 0xF8, 0x00, 0x1F, 0xF8, 0x00 };
//paintmask(cr, samp, 24, 23);
paintmask(cr, samp2, 48, 23);
XFlush(dis);
}
sleep(20);
cairo_destroy(cr);
cairo_surface_destroy(surface);
return 0;
}