Sombras con Gtkmm y Cairo (Cairomm) en C++

No es sencillo utilizar Cairo, y menos desde C++. De hecho, el correspondiente mapping a C++, Cairomm, a pesar de ser magnífico, no incluía hasta hace poco la implementación de gradientes, y la documentación no es muy completa (casi inexistente), y hay que leer la de C e imaginarse cómo se hará en C++ muchas veces.

De hecho, antes, para hacer la sombra que quería hacer usaba una imagen con una máscara alpha bastante fea, pero era la única forma de hacerlo con los contextos Gdk.

Con la adición de los gradientes, esa sombra se puede generar en tiempo de ejecución. Así que hoy, saturado de escribir tesis, me he dedicado un poco al propio código de la tesis. Incluye un editor de ensamblados de componentes, al que le añadí el efecto de la sombra. Así, primero se crea el gradiente:


shadow_ = Cairo::RadialGradient::create (0, 0, 0, 0, 0, .5);

// Gradient color stops
shadow_->add_color_stop_rgba (0, 0, 0, 0, .25);
shadow_->add_color_stop_rgba (1, 0, 0, 0, 0);


Creo un gradiente radial (circular) desde el centro, hasta 0.5 (trabajaré en unidades desde 0 a 1). A ese gradiente le añado dos "stops" de color. En realidad, ambos negros, (0,0,0), pero juego con el canal alfa (alpha), dejándolo desde transparente del todo hasta casi transparente (0.25). No quiero que la sombra despiste. A continuación dibujo el componente y la sombra:


const double shadow_size = 100;

const double circle_x = rect().get_x() + rect().get_width() / 2.0;
const double circle_y = rect().get_y() + rect().get_height();

// Access to cairo context
Cairo::RefPtr<Cairo::Context>& cr = ...;

// Transform & draw
cr->save();
cr->translate (circle_x, circle_y);
cr->scale ((double)rect().get_width(), shadow_size / 2.0);
cr->set_source (shadow_);
cr->arc (0, 0, shadow_size / 2.0, 0, 2* M_PI);
cr->fill();
cr->restore();


El componente se dibuja en el rectángulo "rect()". Primero, el contexto de Cairo se traslada al centro del círculo. Se escala de forma irregular para convertir la sombra en una elipse. Finalmente se dibuja el círculo que se rellenará con el patrón "shadow_", y se utiliza el método "fill()" para dibujar la sombra. El resultado queda bastante aparente, aunque tengo que ver si optimizar un poco, porque las operaciones de escalado del gradiente no son muy rápidas. Quizá puedo generar una imagen que sea el gradiente y escalar esa imagen al tamaño exacto. Esto no será tan complejo, aunque el gradiente no será tan perfecto. El resultado:



blog comments powered by Disqus