A learning experience with Mesa project

Recently, I’ve been interested on starting to contribute to the mesa project. My current knowledge may not be enough to do great stuff, but I would learn a lot for sure, and here it is my first experience.

I’ve learnt about some topics related to the workflow needed to contributing at a big project like mesa, like sending patches with git. I have been using git for quite a while on personal projects, informal projects with people and a little more formal projects at work, but I don’t stop learning, and got a few tricks here.

Setting up the repos.

First of all, it’s good to have your own remote repository to place all the changes before submitting a patch. I created a new empty github repo for this task. Then, I had to set up both remotes (my github repo and the official freedesktop mesa).

# create clean local git repository
$ mkdir mesa
$ cd mesa
$ git init

# add both remotes, github and freedesktop
$ git remote add github git@github.com:alesegdia/mesa.git
$ git remote add freedesktop git://anongit.freedesktop.org/mesa/mesa

# fetch freedesktop state and pull changes into local repo
$ git fetch freedesktop master
$ git pull freedesktop master

# update github repo with all the history from freedesktop repo
$ git push --all github

With all that set, then I proceeded to make those little changes needed for the task. It was a small task, suitable for a warm-up, and it consisted on changing the use of an old hash table for a new faster one. I won’t go into details for that.

Testing it out.

Once the changes are done, testing them is a must, but first things first. We need to compile the project. Mesa people have chosen autotools for this task.

$ ./autogen.sh
$ ./configure --prefix=/home/me/mesainstall
$ make -j8
$ make install

Then, to test our freshly compiled lib, we have to tell the system to use the folder it’s in. For that, we make use of LD_LIBRARY_PATH environment variable.

$ export LD_LIBRARY_PATH=/home/me/mesainstall/lib

Now, every GL app will use our compiled lib. Coming back to where we were, we need to run some tests in order to check that we did a good job. For that purpose, mesa has some tests inside the repo [1].

$ make check

We have to take into account that some tests may fail even for a clean tree. This may be because of features not made, or work in progress, so we need to perform the tests before and after using our newly compiled mesa driver, and compare then.

There is also a test framework called piglit [2] that let us get fancy outputs and run a subset of tests by category. For this change, I just needed to run the glslparser tests after having properly built piglit. There are some flags added taken from [3] to avoid some hangs on X you can have.

$ ./piglit-run.py -x glx -x streaming-texture-leak -1 --dmesg tests/glslparse.py \
	~/piglit-results/output

As I mentioned earlier, I ran this test with and without the changes. I got the same output so I took that as a guarantee that I did things ok.

Sending patches via email.

After making those changes, I commited them. In order to submit patches for review, git-send-email comes in handy. First of all, you need to set up your config to be able to send emails [4].

# global email server settings
$ git config --global sendemail.smtpencryption tls
$ git config --global sendemail.smtpserver mail.messagingengine.com
$ git config --global sendemail.smtpuser user@server.com
$ git config --global sendemail.smtpserverport 587

# local email recipient address setting
$ git config sendemail.to mesa-dev@lists.freedesktop.org

At this point, everything is good to send patches by email. The next step was to actually send the patch by mail. This can be done in two ways. The first one does the whole thing in one step.

$ git send-email -1 <commit reference>

Where N is the number of commits to send within the patch, and <commit reference> the commit where it counts. This will build a mail and send it at once, but we can split this in two steps if we want, in order to elaborate a little more with the message.

# build the email
$ git format-patch -1 <commit reference>

# edit it
$ vim 0001-first-commit-line.patch

# actually send it
$ git send-email 0001-first-commit-line.patch

And that’s it, patch sent to the mailing list!

Rebasing, not so evil.

So I’ve always heard about the evilness of rebasing, but I just found out it’s a really cool feature thanks to mesa people at freenode. It can break a repo though, but since we’re working on a personal branch at github before actually commiting to the official mesa repo, there was no problem with it.

I was told a few things to change in my commit, and a few more commits were pushed to the official mesa repo. I had already pulled them to local, then pushed to github, so I thought I was in trouble. Luckily I was told about rebase to fix my commit.

$ git rebase --interactive HEAD~6
$ git rebase --interactive 3a28098^

Rebase let you perform some actions from some commit to HEAD. With this instruction, we wanted to do that from six commits back from HEAD, but you can use a commit reference hash as well [5].

pick 3a28098 glsl: replace old hash table with new and faster one
pick 111b69c radeonsi: flush if the memory usage for an IB is too high
pick 366c590 r600g: fix the single-sample fast clear setup
pick 24c4455 r600g: fix the CB_SHADER_MASK setup
pick 803cc5a r600g: re-enable single-sample fast clear
 
# Rebase 4d7e0fa..803cc5a onto 4d7e0fa (5 command(s))
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

To be honest, at first I wanted to study the rebase command a little more before doing anything, so I ZZed it. I pushed the changes made so I had commits duplicated. I undid those duplicated commits [6] and took it slower.

$ git reset HEAD~5
$ git push --force github

The danger there resides at forced git push, since it rewrites github with what we have at the local repo, but knowing what we’re doing, there’s no need to fear. Anyway, I read more carefully about rebase docs with the –interactive option, and you are able to perform some commands on previous commits to rewrite history.

(...)
edit 3a28098 glsl: replace old hash table with new and faster one
(...)
ZZ

$ vim src/file.cpp
$ git add src/file.cpp
$ git commit -m "new commit message"

I needed to edit the commit, so that I could edit the file to apply those little style changes. Made a new commit with a more elaborated message. Then pushed forcing and the history was rewritten, but that way, two commits were made: one with the previous changes and another before that one with the little style change.

This was not what I intended to do, since I wanted one commit with all changes, so I executed rebase again to fix it. There is the squash command for this purpose.

(...)
pick 3a28098 glsl: replace old hash table with new and faster one
squash 4bc4356 glsl: fancier new message
(...)
ZZ

Then, an editor view is prompted to be able to mix both commit messages. When saving, both commits are squashed into one, and the new commit message is associated. Before this, the interactive mode lets you perform a few commands:

  • –continue: until the next instruction needing user interaction
  • –abort: all rebase commands
  • –skip: this current rebase command
# edit commit message
$ git rebase --continue
$ git push --force github

And that’s it! history rewritten.

Lessons learnt.

This was a great learning experience, and I just wanted to write it down. The workflow to submit patches sending mails and a little more git knowledge were definitely worth. From now on, I hope further learning into actual mesa drivers architecture.

[1] http://www.mesa3d.org/devinfo.html
[2] http://piglit.freedesktop.org/
[3] http://people.freedesktop.org/~imirkin/
[4] http://www.freedesktop.org/wiki/Software/PulseAudio/HowToUseGitSendEmail/
[5] http://stackoverflow.com/questions/1186535/how-to-modify-a-specified-commit-in-git
Anuncios

Descubriendo las Boost (II): destrozando el scoped_ptr.

Después de haber presentado los punteros scoped_ptr en el post anterior, queda lo más divertido: destrozarlo. En principio, scoped_ptr es bastante útil, ya que hace el trabajo sucio por nosotros. Además, un puntero de este tipo es prácticamente igual de rápido que un puntero nativo, según la documentación de las boost. Depurando algunos tests, puedo confirmar esto, tanto al construir como al destruir se utilizan unas 4 o 5 instrucciones simples.

Pero no nos ibamos a escapar de rositas. Esta clase también tiene sus defectos. La primera cuestión es, ¿qué pasa si quiero que varios scoped_ptr hagan referencia a la misma zona de memoria?. La respuesta es que no se puede. Al menos no de forma segura, ya que en cuanto se elimine uno de ellos, se elimina el puntero interno, y el otro que referencia a la misma zona de memoria, queda en comportamiento indefinido.

Para solventar este problema, surgen los shared pointers, que mantienen la cuenta de las referencias que se ha hecho a dicha zona de memoria, y solo desaloja memoria cuando la cuenta cae a 0. Estos punteros serán tratados en el próximo post, por ahora vamos a divertirnos un poco destripando nuestro MyScopedPtr anterior y el propio de las boost.

Por comodidad, se ha movido el código de MyScopedPtr a un fichero aparte, que es el siguiente:

#ifndef __MYSCOPEDPTR_HPP
#define __MYSCOPEDPTR_HPP

#include <iostream>

template
class MyScopedPtr
{
    private:
        T* m_ptr;

    public:

        MyScopedPtr(const MyScopedPtr& other) {
            m_ptr = other.m_ptr;
        }

        MyScopedPtr(T* ptr) {
            std::cout << "Asignando MyScopedPtr" << std::endl;
            m_ptr = ptr;
        }

        ~MyScopedPtr() {
            std::cout << "Liberando MyScopedPtr" << std::endl;
            if (m_ptr != NULL) {
                delete m_ptr;
                m_ptr = NULL;
            }
        }

        T& operator* () {
            return *m_ptr;
        }
};

#endif

La idea al hacer el test, era provocar la situación antes mencionada. Para ello se ha hecho uso del test siguiente:

#include <iostream>
#include <boost/shared_ptr.hpp>

#include "MyScopedPtr.hpp"

int main (int argc, char** argv)
{
    MyScopedPtr ptr(new int);
    *ptr = 3;

    // BLOQUE PROBLEMÁTICO
    // Al desaparecer ptr_prob MyScopedPtr del ámbito, se elimina el contenido del
    // puntero, con lo cual nuestro smart pointer declarado fuera queda en bragas
    {
        MyScopedPtr ptr_prob(ptr);

        (*ptr_prob)++;
        std::cout << *ptr_prob << "," << *ptr << std::endl;
    }

    // Ahora, a saber lo que habrá en la dirección del puntero, puede estar incluso
    // la misma posición de memoria y puede hasta que tengamos permisos aún y que 
    // conserve incluso el valor que tenía antes porque no hay trasteo de por medio,
    // pero esto es un undefined behaviour en toda regla.

    (*ptr)++;
    std::cout << *ptr << std::endl;

    return 0;
}

En una de las ejecuciones, sale el siguiente curioso resultado:

razieliyo@blackjack:~/cpp$ bin/my_scoped_ptr_fail 
Asignando MyScopedPtr
4,4
Liberando MyScopedPtr
1
Liberando MyScopedPtr
*** glibc detected *** bin/my_scoped_ptr_fail: double free or corruption (fasttop): 0x09bc9008 ***
[....]

Como podemos ver, dentro del bloque nuevamente creado la cosa parece que va bien, ambos valores dan lo mismo. El problema es cuando salimos de dicho ámbito. Se libera el puntero que creamos dentro del bloque, como era de esperar, y después, al mostrar la variable, se muestra 1, que no es lo que se esperaba. Además, obtenemos el fallo que le continua.

Ahora vamos a tocarle las narices al boost::scoped_ptr. Se ha creado una clase auxiliar para reflejar el error de otra forma:

#ifndef __MYSCOPEDPTR_HPP
#define __MYSCOPEDPTR_HPP

#include <iostream>

class Foo
{
    private:
        int a;
    public:
        Foo () { a = 0; }
        void foo () { a++; std::cout << "fooish" << a << std::endl; }
};

#endif

Sí, soy muy original. El test ha sido el siguiente, para intentar provocar la misma situación que antes:

#include <iostream>
#include <boost/scoped_ptr.hpp>
#include "Foo.hpp"

int main (int argc, char** argv)
{
    boost::scoped_ptr ptr1(new Foo);
    boost::scoped_ptr ptrInt(new int);

    // asignamos normalmente y funciona
    *ptrInt = 12345;
    std::cout << *ptrInt << std::endl;

    // PELIGRO!!
    // cuando aux sale del ámbito, se elimina el contenido del puntero
    {
        // se transfiere el puntero de forma normal
        boost::scoped_ptr ptr2(ptr1.get());
        // llamada válida, muestra 'fooish1' como era de esperar
        (*ptr2).foo();

        // intentamos fastidiar también al puntero a int
        boost::scoped_ptr int2(ptrInt.get());
    }

    // FAIL!! el objeto ptr1 ya apunta a una zona que no controlamos 
    // con suerte, se muestra por pantalla algo en plan fooish123883427
    (*ptr1).foo();

    // igualmente, no controlamos ya la zona de memoria del puntero a entero
    // da un resultado fallido igual 
    std::cout << *ptrInt << std::endl;

    delete [] buffer;

    return 0;
}

Concretamente una ejecución es ésta:

12345
fooish1
fooish157716497
0

Como vemos, son bastante peligrosos si queremos hacer algo medianamente complicado, y lo peor de todo es que los fallos de este tipo suelen ser silenciosos. Los ejemplos que se han puesto son muy inútiles, pero ha sido simplemente para provocar fallas.

En el próximo post, veremos la solución a nuestro problema: boost::shared_ptr, y en los siguientes se describirán otros tipos como boost::weak_ptr o boost::unique_ptr, ya que se usan dentro de los que estamos viendo.

Descubriendo las Boost (I): scoped_ptr.

Ya que voy a tener que inmiscuirme en estas bibliotecas tan ampliamente usadas, voy a crear una serie de artículos hablando de algunos de sus componentes y usos. Empezaré por lo primero que me llamó la atención de las bibliotecas: punteros inteligentes.

Gracias a Virako por animarme a ésto y hacerme poner a escribir de una @#$% vez.

Tenemos varios tipos de estos punteros, y el primero que veremos será el boost::scoped_ptr. Esta clase de puntero, nos permite olvidarnos tanto de la reserva como la liberación de memoria. Ésta característica se comparte entre todos los punteros de los que disponemos en la biblioteca boost, pero con una política de ámbito distinta, aparte de alguna que otra peculiaridad.

En concreto, un boost::scoped_ptr es un puntero que se autolibera al salir del ámbito. Una implementación de un scoped_ptr podría ser la siguiente:

#include <iostream>

/**
 * OUTPUT:
 *      Asignando MyScopedPtr
 *      4
 *      Liberando MyScopedPtr
 */

class Foo 
{
    public:
        void foo () { std::cout << "fooish" << std::endl; }
};  

template 
class MyScopedPtr
{
    private:
        T* m_ptr;

    public:
        MyScopedPtr(T* ptr) {
            std::cout << "Asignando MyScopedPtr" << std::endl;
            m_ptr = ptr;
        }

        ~MyScopedPtr() {
            std::cout << "Liberando MyScopedPtr" << std::endl;
            delete m_ptr;
        }

        T& operator* () {
            return *m_ptr;
        }
};

int main (int argc, char** argv)
{
    MyScopedPtr my_ptr (new int);
    *my_ptr = 4;
    std::cout << *my_ptr << std::endl;

    MyScopedPtr my_foo_ptr(new Foo);
    (*my_foo_ptr).foo();

    return 0;

Como vemos, no nos es necesario liberar explícitamente la memoria, de eso ya se encarga el destructor. ¿Qué es lo que pasa realmente cuando un nuestro objeto sale del ámbito donde fue creado?

  1. Al salir de ámbito, se llama al destructor ~MyScopedPtr().
  2. Éste libera la memoria reservada para el tipo que hemos alojado previamente en el montón (heap).
  3. Por último, cuando el destructor vuelve, se elimina la memoria reservada en la pila (stack) para almacenar el puntero en nuestra clase MyScopedPtr.

Digamos que boost::scoped_ptr funciona así, con algunos esteroides de más. Con valgrind podemos ver que nuestro programa no presenta ninguna anomalía en cuanto a leaks:

shell$ valgrind --leak-check=full ./my_scoped_ptr
==5471== Memcheck, a memory error detector
==5471== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==5471== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==5471== Command: ./my_scoped_ptr
==5471== 
Asignando MyScopedPtr
4
Asignando MyScopedPtr
fooish
Liberando MyScopedPtr
Liberando MyScopedPtr
==5471== 
==5471== HEAP SUMMARY:
==5471==     in use at exit: 0 bytes in 0 blocks
==5471==   total heap usage: 2 allocs, 2 frees, 5 bytes allocated
==5471== 
==5471== All heap blocks were freed -- no leaks are possible
==5471== 
==5471== For counts of detected and suppressed errors, rerun with: -v
==5471== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Todos los punteros han sido desalojados automáticamente sin problemas. Mola, ¿verdad?. Es bastante cómodo poder usar punteros sin preocuparnos de la liberación de memoria.

Un ejemplo del uso de scoped_ptr se refleja en el siguiente fragmento.

#include <boost/scoped_ptr.hpp>

int main (int argc, char** argv)
{ 
  boost::scoped_ptr i(new int); 
  *i = 1; 
  *i.get() = 2; 
  i.reset(new int); 

  return 0;
}

Referencias.

Degradado en SDL.

Dentro de tres horas empiezo mi primer día de clase y no puedo dormir. Llevo dando vueltas en la cama por lo menos tres horas, y al final me he decidido a levantarme y adecentar un test que hice en C para crear un gradiente y, de camino, lo publico por aquí, con eso hago tiempo hasta la hora.

OST del POST

El degradado se realizará desde la parte superior de la ventana hasta la inferior por elegir algo, aunque se podría hacer de forma horizontal igualmente. Lo que todavía no soy capaz de concebir es como hacerlo en un ángulo aleatorio, pero bueno, ya me quebraré los sesos otro día, ahora solo estoy para escupir.

Bueno, la idea básica es tomar dos colores inicial y final. Pintaríamos una línea del color inicial y lo incrementaríamos cierto valor. Pintaríamos la siguiente y volveríamos a incrementarlo la misma cantidad, y así hasta que obtuviéramos el último, o se llegue a la última línea (estas dos condiciones deberían de darse juntas). Para ello, es necesario tener por separado cada tono RGB, ya que cada tono irá variando de forma independiente a los demás.

Cada tono tendrá un incremento independiente al de los demás. La idea para calcular el incremento en general sería la siguiente:

color_inicial = (Ri, Gi, Bi)
color_final   = (Rf, Gf, Bf)
incremento    = (color_final - color_inicial) / L
= ((Rf-Ri) / L, (Gf-Gi) / L, (Gf-Gi) / L)

Así, obtendríamos el incremento que hay que darle a cada color para cada pasito o línea a dibujar. El parámetro L no es más que el ancho o el alto de la superficie donde queremos renderizar el degradado. Elegimos uno u otro en función de si queremos hacer un degradado vertical (elegiríamos el alto) u horizontal (elegiríamos el ancho). En nuestro caso, como vamos a hacerlo vertical, elegiremos el alto.

Un detalle a destacar con el que casi me vuelvo loco, es el tipo de dato para guardar los colores. Como bien sabemos, un color no puede tener parte fraccionaria. A ninguno se le ocurriría definir el color casi rojo como (254.9, 0, 0). Es absurdo.

Pero para lo que nos concierne, tendremos que usar colores fraccionarios, que luego pasaremos a un entero para que se lo trague bien el SDL. Esto es porque si resulta que el incremento resulta un número fraccionario, podría fallar. Se ve bien lo que quiero decir si pensamos que por ejemplo, el incremento estuviera entre 0 y 1, en cuyo caso, se establecería como 0 al ser un entero y nunca incrementaría una mierda, con lo que nuestro precioso efecto de degradado se quedaría en un feo y monocromático fondo. Es por esto que necesitamos usar datos fraccionarios.

Por lo demás, decir que he optado por no permitir cambiar la orientación del degradado (por pereza más que nada), y por la orientación a objetos. En un principio un degradado no era más que un struct con los dos colores y algunos datos más, pero así parece más bonito, y encima si se pasa algún señorito de la metodología y las cosas bien hechas, al menos no me matará.

Usaremos un tipo color definido por nosotros, que no será más que un struct con las componentes RGB en formato fraccionario:

typedef struct {
    float R;
    float G;
    float B;
} color;

A la clase la llamaremos Gradient y tendrá (en principio) dos atributos: los colores inicial y final, que los denotaremos como init y end. Tendremos un método draw que dibujará el gradiente en la pantalla. Para ello, lo primero que tenemos que hacer es calcular el incremento tal y como dijimos:

    const int SCREEN_HEIGHT = screen->h;
    const int SCREEN_WIDTH = screen->w;
    
    float deltaR = (end.R - init.R)/SCREEN_HEIGHT;
    float deltaG = (end.G - init.G)/SCREEN_HEIGHT;
    float deltaB = (end.B - init.B)/SCREEN_HEIGHT;
    
    float auxR = init.R;
    float auxG = init.G;
    float auxB = init.B;
    
    Uint32 sdl_color;

Las variables auxiliares las usaremos para ir incrementando ahí los colores y sdl_color para guardar el color en formato que entienda el SDL.

Ahora que tenemos el incremento que tenemos que ir dándole al color, solo tenemos que iterar por la pantalla e ir dibujando línea a línea. Como vamos a ir degradando en vertical, iremos rellenando en cada iteración, una línea horizontal. Después de esto, incrementaremos las variables auxiliares según corresponda:

    /* draw the gradient */
    for(int i=0; i<SCREEN_HEIGHT; i++)
    {

        sdl_color = SDL_MapRGB (screen->format,
                                (Uint32)auxR,
                                (Uint32)auxG,
                                (Uint32)auxB);
        
        /* draw a line */
        for(int j=0; j<SCREEN_WIDTH; j++)
        {
            putpixel (screen, j, i, sdl_color);
        }

        auxR += deltaR;
        auxG += deltaG;
        auxB += deltaB;
        
    }

Y ya está. Con este código, iremos pintando línea a línea incrementando el color inicial hasta llegar al final.

He decidido añadirle un bonito efecto para que vaya cambiando los colores inicial y final, como una especie de efecto fading que ni voy a explicar. Por si interesa, dejo el código fuente completo:

main.cpp

#include "SDL/SDL.h"
#include "Gradient.h"

#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 400

bool key_pressed();

SDL_Surface *screen;
SDL_Event event;

color init = {128,0,128};
color end = {0,128,0};

int main( int argc, char* args[] )
{
    Gradient g(init,end);
    SDL_Init(SDL_INIT_VIDEO);
    screen=SDL_SetVideoMode (SCREEN_WIDTH, SCREEN_HEIGHT,
                                0, SDL_HWSURFACE | SDL_DOUBLEBUF);

    g.setFadingEffect (true);

    while(!key_pressed()) {

        SDL_FillRect (screen, &screen->clip_rect,
                        SDL_MapRGB(screen->format, 0,0,0));

        g.draw(screen);
        SDL_Flip (screen);
    }

    SDL_Quit();

    return 0;
}

bool key_pressed() {
    while(SDL_PollEvent(&event))
    {
        switch(event.type) {
            case SDL_KEYDOWN:
                switch (event.key.keysym.sym) {
                    case SDLK_ESCAPE:
                        return true;
                }
            break;
        }
    }
    return false;
}

Gradient.h

#ifndef __GRADIENT__
#define __GRADIENT__

#include "SDL/SDL.h"

typedef struct {
    float R;
    float G;
    float B;
} color;


class Gradient {
public:
    Gradient(color init, color end);
    void draw(SDL_Surface *screen);
    bool setFadingEffect(bool t) { fade=t; }
    
private:
    color init;
    color end;
    color s_init;
    color s_end;  
    bool fade;
    
    void cycleColors();
};

void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel);

#endif

Gradient.cpp

#include "Gradient.h"

Gradient::Gradient(color init, color end)
{
    this->init  = init;
    this->end   = end;
    s_init.R    = 1;
    s_init.G    = 1;
    s_init.B    = 1;
    s_end.R     = 1;
    s_end.G     = 1;
    s_end.B     = 1;
    fade        = false;
}

void Gradient::draw (SDL_Surface *screen)
{
    const int SCREEN_HEIGHT = screen->h;
    const int SCREEN_WIDTH = screen->w;
    
    float deltaR = (end.R - init.R)/SCREEN_HEIGHT;
    float deltaG = (end.G - init.G)/SCREEN_HEIGHT;
    float deltaB = (end.B - init.B)/SCREEN_HEIGHT;
    
    float auxR = init.R;
    float auxG = init.G;
    float auxB = init.B;
    
    Uint32 sdl_color;
           
    /* draw the gradient */
    for(int i=0; i<SCREEN_HEIGHT; i++)
    {

        sdl_color = SDL_MapRGB (screen->format,
                                (Uint32)auxR,
                                (Uint32)auxG,
                                (Uint32)auxB);
        
        /* draw a line */
        for(int j=0; j<SCREEN_WIDTH; j++)
        {
            putpixel (screen, j, i, sdl_color);
        }

        auxR += deltaR;
        auxG += deltaG;
        auxB += deltaB;
        
    }
    
    if (fade)
        cycleColors();
}

#define tokenpaste(a,b) a . b

#define change_color(col ,sign ,x) \
    if (tokenpaste(sign,x)) { \
        if (tokenpaste(col,x)+1>255) tokenpaste(sign,x)=0; \
        else tokenpaste(col,x)++; \
    } else { \
        if (tokenpaste(col,x)-1<0) tokenpaste(sign,x)=1; \
        else tokenpaste(col,x)--; \
    }

void Gradient::cycleColors() {
    change_color (init,s_init,R);
    change_color (init,s_init,G);
    change_color (init,s_init,B);
    change_color (end,s_end,R);
    change_color (end,s_end,G);
    change_color (end,s_end,B);
}

void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
    int bpp = surface->format->BytesPerPixel;
    /* Here p is the address to the pixel we want to set */
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

    switch(bpp) {
    case 1:
        *p = pixel;
        break;

    case 2:
        *(Uint16 *)p = pixel;
        break;

    case 3:
        if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
            p[0] = (pixel >> 16) & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = pixel & 0xff;
        } else {
            p[0] = pixel & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = (pixel >> 16) & 0xff;
        }
        break;

    case 4:
        *(Uint32 *)p = pixel;
        break;
    }
}

Listas intensionales en Haskell.

Me he adentrado de nuevo en el mundo de haskell a través de ésta golosina. He recordado con esta vuelta un elemento realmente poderoso en este lenguaje, cuyo nombre aparece en el título del post: las listas intensionales.

La idea es poder inyectar listas de haskell en elementos de una definición matemática, cuyo uso en programación, visto desde buenos ojos, puede ser una herramienta bastante útil. Con este concepto, somos capaces de aplicar muchísimas transformaciones a una entrada (usualmente una lista) y, digamos, recorrerlas de manera muy breve y concisa.

[ f(x) | x <- xs, pred1, pred2, ..., predn ] donde:

  • f(x) es una función aplicable a x
  • x es el iterador de la lista
  • xs es la lista o entrada
  • predm son predicados o condiciones que ha de cumplir cada x procesado para resultar en la salida

Así, como ejemplo, dicho tutorial nos propone la siguiente línea, evaluación, expresión, llamadlo como queráis:

boomBangs xs = [ if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x]
La función boomBangs toma una lista xs como argumento, su salida será una lista de BOOM!’s y BANG!’s:
f(x) simplemente devuelve BOOM! si el x es menor que 10 y BANG! si es mayor. Existe un solo predicado, odd x, que sera cierto si x es impar y falso si no lo es:


ghci> let boomBangs xs = [ if x < 10 then "BOOM!" else "BANG!" | x boomBangs [7..14]
["BOOM!","BOOM!","BANG!","BANG!"]

Et voilà.

CLisp, Slime y Emacs: guía de instalación definitiva en Ubuntu.

Siempre que instalo a los tres mosqueteros es una verdadera odisea para hacerlo funcionar, así que allé voy.

Instalamos los tres paquetes:

sudo apt-get install emacs clisp slime

Editamos el archivo de configuración de emacs (/home/$USR/.emacs) y añadimos lo siguiente:

(setq inferior-lisp-program "/usr/bin/clisp") ; interprete lisp a usar
(setq slime-path "/usr/share/common-lisp/source/slime") ; fuentes de slime
(add-to-list 'load-path "/usr/share/emacs/site-lisp/slime") ; directorio slime
(require 'slime) ; cargamos el módulo
(slime-setup '(slime-fancy)) ; cargamos la mayoría de los componentes de slime

Nota mental: tengo que ponerme con lisp.