Web2.0 style logo reflection with Ruby and Cairo 2008-03-22


I keep trying to learn more about Cairo, and cheesy logo effects seems to be as good a way as any. This evening I was playing around with clip paths, and decided to use it to create some reflections. Here are the results:


You might want to take a look at my previous examples. This one, as my last one, uses functions from the first example. It also requires that Cairo and the Cairo Ruby binding are installed.

And here's what I did.

First I took the script I used to draw the logo with gradient and replaced the background with black instead of white, and made the border blue. You can find the logo I used here

I named the file "in.png".

Then it was time to write the script - You need to require 'cairo' and add the cairo_image_surface helper function from my first example. As usual I then defined a few variables, starting width/height. The script assumes that the input logo ends at h/2, so adjust accordingly if you use a different image. I then create the surface and open the block


    h = 170
    w = 350
    
    black  = [0.0,0.0,0.0, 1]
    
    cairo_image_surface(w,h, black) do |cr|

The next step is new: I create an image surface from the PNG file. I then set the image as the source. The second and third argument to set_source() are the coordinates where the upper left point of the surface should map to the context (cr) we're drawing with.

I then draw a rectangle with the surface, and fill it. This has the effect of copying the logo onto the image surface we're drawing with.


      image = Cairo::ImageSurface.from_png("in.png")
      cr.set_source(image,40,0)
      cr.rectangle(0,0,w,h/2)
      cr.fill

Then it's time for the reflection effect. The left one first:


     (0..h/2).each do |y|
        cr.rectangle(0,h/2 + y,w,1)
        cr.clip
        cr.set_source(image,40-0.3*(y+1),y*2)
        cr.paint
        cr.set_source_rgba(*black)
        cr.paint(0.1+y/60.0)
        cr.reset_clip
      end

This code is fairly straightforward:

For the lower half of the image, we first create a rectangle of 1.0 unit high and the full width. We use "cr.clip" to define this rectangle as the current clip mask. The clip mask defines what part of what you draw subsequently that show. In effect we're creating a "slit" one pixel high we can draw on.

I then use set_source to reposition the source image (the original logo). By manipulating the y position we turn the image upside down. By manipulating the x position we slant the reflection to the left.

I then use cr.paint to pain the source image onto the "slit" I established by clipping.

Then I use set_source_rgba to switch to black "paint" and paint with alpha blending to gradually erase more and more the further down we get by painting over the slit with black. I chose this approach to making the reflection fade because the logo I used didn't have a transparent background. I could make the background transparent, but didn't think about it at the time. So the alternative approach is to drop painting over with the background color and instead set the alpha mask for the first "cr.paint" call and gradually reduce the alpha value instead of increasing it as we do for the second paint call.

All you need to do to get the wave effect instead is replace the set_source call we used to move the logo each line:


        cr.set_source(image,40-Math.sin(y*0.2)*y/7.0,y*2)

Experiment with different factors to increase the wave amplitude or frequency.

Last but not least you need to write the surface out, and close the block:


      cr.target.write_to_png("test.png")
    end

.

blog comments powered by Disqus