2 votes

Générer du bruit en Perlin avec Xlib

J'essaie d'ajouter un nuage à un économiseur d'écran. J'aime l'aspect d'un nuage "plasma", donc j'essaie de dessiner un nuage à base de bruit de perlin à l'arrière-plan de l'économiseur d'écran. J'ai un code qui crée un tableau contenant les valeurs des couleurs qui composent le bruit de perlin. Tout ce que j'ai besoin de faire est de créer une image à partir de ce tableau et de la définir comme arrière-plan dans cet économiseur d'écran.

Comment générer une image à partir de ce tableau ? J'ai cherché à utiliser purement la Xlib, mais c'est une tâche intimidante. Donc s'il y a un moyen d'utiliser Cairo pour générer l'image à partir du tableau, ce serait bien. De plus, les valeurs du tableau sont comprises entre 0 et 1.

1voto

luser droog Points 9030

La fonction Cairo pour cela est cairo_image_surface_create_for_data() Mais il est parfois difficile de présenter les données de manière à ce que le Caire puisse les lire. Les détails des formats d'images en mémoire (décrits dans la section aquí ) se trouvent dans le cairo.h (dans les commentaires), et non dans le manuel.

Autre gotcha est : assurez-vous d'utiliser cairo_format_stride_for_width() pour obtenir la taille d'une ligne, car il peut y avoir des exigences en matière de remplissage que vous ne pouvez pas (ou ne devez pas) calculer vous-même.

Il peut être tentant d'essayer CAIRO_FORMAT_A8 o CAIRO_FORMAT_A1 puisqu'il s'agit d'une image d'une profondeur de 1 bit ; mais j'ai trouvé qu'il était plus facile pour les images de 1 bit d'utiliser CAIRO_FORMAT_RGB24 et réglez les valeurs rouge, vert et bleu sur 0 ou 255. Les formats A* affectent les alpha et non les canaux de données de l'image, de sorte qu'en l'utilisant, vous avez toujours besoin d'une autre source de données RVB. Opaque rien est tout aussi invisible que Transparent rien . De plus, la disposition des bits dépend de l'endiannesse de la machine sous-jacente, de sorte qu'en travaillant directement avec des valeurs de 32 bits (vous pourriez utiliser uint32_t si vous le souhaitez, mais les spécificateurs de format printf deviennent alors atroces ; je m'en tiens donc à long ). Si vous déplacez des valeurs entières, le caractère endiablé des données reflétera naturellement le caractère endiablé de la machine.

Voici les informations pertinentes de 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;

Mon exemple de code dans l'autre réponse est un très mauvais exemple. Mais il en est ainsi parce que je n'ai pas trouvé d'exemple du tout lorsque je l'ai écrit, alors ... ex nihilo nihil . Il tente de remplir le tableau de données approprié en utilisant des données sources connues pour être big-endian et pouvant avoir une profondeur de 1, 2, 4 ou 8 bits. Egad, il y a même la petite déclaration innocente

         run(st);

qui invoque tout l'interpréteur de manière récursive, ce qui était l'enfer pour obtenir la fonction error mécanisme de longjmp à la correctes de l'interprète. C'est un très mauvais exemple de l'utilisation de cairo_image_surface_create_for_data() . Mais ... que quelqu'un m'en montre une meilleure. Je vous en prie !


Voici un exemple plus simple. Je ne l'ai pas testé, mais je pense que c'est une façon plus simple de faire ce dont vous avez besoin.

#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;
}

Les données renvoyées seront appropriées pour être transmises à cairo_image_surface_create_for_data . L'important est d'utiliser stride pour la largeur de la ligne, même si les données sources sont disposées différemment (ici, il s'agit seulement de la largeur de la ligne). wid large).

Oh, et c'est une sorte de polissage inversé des "apps hongroises" que j'utilise ici comme convention de dénomination. Donc, c'est imagedata signifie "image <-- données". datasamp signifie "data <-- samp".

0voto

luser droog Points 9030

En fouillant dans de vieux répertoires, j'ai trouvé un exemple utilisant CAIRO_FORMAT_A1 y cairo_mask_surface qui pourrait être plus proche de ce que vous souhaitez que les autres exemples (et qui dément certaines de mes affirmations ci-dessus). Et celui-ci est complet. Compilez avec ce makefile

CFLAGS=-I/usr/include/cairo #-Wa,-alh
LDLIBS=-lcairo

en utilisant 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;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X