2

我正在尝试将云添加到 Xscreensaver。我喜欢“等离子”云的外观,所以我试图在 Xscreensaver 的背景上绘制一个基于 perlin 噪声的云。我有创建一个数组的代码,该数组具有构成柏林噪声的颜色的值。我需要做的就是从这个数组创建一个图像并将其设置为这个 Xscreensaver 中的背景。

如何从此数组生成图像?我已经研究过使用纯粹的 Xlib,但这是一项艰巨的任务。因此,如果有一种方法可以使用 Cairo 从数组中生成图像,那就太好了。此外,数组中的值介于 0 和 1 之间。

4

2 回答 2

1

对此的 Cairo 函数是cairo_image_surface_create_for_data(),但是将数据放入适当的布局以便 Cairo 读取它可能有点令人生畏。内存图像格式(在此处描述)的详细信息在cairo.h头文件本身(在注释中)中,而不是手册中。

另一个问题是:确保使用cairo_format_stride_for_width ()来获取行的大小,因为可能存在您无法(不需要)自己计算的填充要求。

尝试一下可能很诱人,CAIRO_FORMAT_A8或者CAIRO_FORMAT_A1因为这是一个 1 位深的图像;但我发现使用 1 位图像更容易,CAIRO_FORMAT_RGB24并将红色绿色和蓝色值设置为 0 或 255。A* 格式影响alpha通道,而不是图像数据通道,所以使用它,你仍然需要另一个RGB数据的来源。不透明的虚无与透明的虚无一样是不可见的。此外,位的排列取决于底层机器的字节序,因此直接使用 32 位值(uint32_t如果需要,您可以使用,但是您的 printf 格式说明符会变得很糟糕;所以我坚持使用long)。如果您要移动整个整数值,那么数据的字节序自然会反映机器的字节序。

以下是来自的相关信息cairo.h

/**
 * 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;

我在另一个答案中的示例代码是一个非常糟糕的示例。但就是这样,因为我写的时候根本找不到任何例子,所以... ex nihilo nihil。它尝试使用已知为大端的源数据打包适当的数据数组,并且可能是 1 位、2 位、4 位或 8 位深。Egad,它甚至还有无辜的小声明

         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宽)。

哦,这是我在这里用作命名约定的一种反向抛光的“apps Hungarian”。所以imagedata意味着“图像<--数据”。datasamp意思是“数据<--采样”。

于 2013-05-01T05:49:33.257 回答
0

在旧目录中翻找,我发现了一个使用的例子CAIRO_FORMAT_A1cairo_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;
}
于 2013-05-15T06:04:42.400 回答