July 9th, 2007 @ 22:43

This afternoon, while making some posters for the French Free Software World Meetings (RMLL), I was willing to get a high resolution PNG version of the Compiz SVG logo. I fired up Inkscape, created a landscape A3 document loaded the original logo file, scaled it and ran the export tool to get a 300 dpi A3 PNG. 20 minutes later it was still running… While it was still running I took my laptop and wrote a quick script using python cairo and librsvg bindings. 5 minutes later (yeah, reading documentations/googling for examples takes a while), I ran it and got my high resolution PNG. I cleaned it up this evening, here is the end result script =)

Usage : svntopng [--width WIDTH] [--height HEIGHT] [-o OUTPUTFILE] SOURCEFILE

#!/usr/bin/python
 
'''svgtopng - SVG to PNG converter
Copyright (c) 2007 Guillaume Seguin <guillaume@segu.in>
Licensed under GNU GPLv2'''
 
import cairo
import rsvg
from sys import argv
from os.path import exists
import getopt
 
def usage ():
    print "Usage : %s [--width WIDTH] [--height HEIGHT] [-o OUTPUTFILE] FILE" % argv[0]
    raise SystemExit
 
if __name__ == "__main__":
    try:
        opts, args = getopt.getopt (argv[1:], 'o:h',
                                    ['width=', 'height=', 'output=', 'help'])
    except getopt.GetoptError:
        usage ()
    output = None
    width = None
    height = None
    for o, a in opts:
            if o in ('-o', '--output'):
                output = str (a)
            elif o == '--width':
                width = int (a)
            elif o == '--height':
                height = int (a)
            elif o in ('-h', '--help'):
                usage ()
    if len (args) == 0:
        usage ()
    file = args[0]
 
    if not exists (file):
        usage ()
 
    svg = rsvg.Handle (file = file)
 
    if not output:
        if file[-4:] == ".svg":
            file = file[:-4]
        output = "%s.png" % file
        base = "%s%d.png"
        i = 1
        while exists (output):
            output = base % (file, i)
            i += 1
 
    if width == 0 and height == 0:
        width = svg.props.width
        height = svg.props.width
    elif width != 0:
        ratio = float (width) / svg.props.width
        height = int (ratio * svg.props.height)
    elif height != 0:
        ratio = float (height) / svg.props.height
        width = int (ratio * svg.props.width)
 
    surface = cairo.ImageSurface (cairo.FORMAT_ARGB32, width, height)
    cr = cairo.Context (surface)
 
    wscale = float (width) / svg.props.width
    hscale = float (height) / svg.props.height
 
    cr.scale (wscale, hscale)
 
    svg.render_cairo (cr)
 
    surface.write_to_png (output)
July 9th, 2007 @ 16:36

My libcompizconfig patch to enable software to create an empty context and load a custom set of plugins afterwars has just been accepted =) The compizconfig-python bindings were updated as well!

Here is an updated “plugin reloader” script :

#!/usr/bin/env python
 
'''Compiz plugin reloader (through compizconfig)
Copyright (c) 2007 Guillaume Seguin <guillaume@segu.in>
Licensed under GNU GPLv2'''
 
import compizconfig
from sys import argv, exit
from time import sleep
 
if __name__ == "__main__":
    if len (argv) < 2:
        print "Usage : %s plugin1 [plugin2 ... pluginN]" % argv[0]
        exit (2)
    plugins = argv[1:]
    context = compizconfig.Context (plugins = plugins)
    print "Unloading " + " ".join (plugins)
    for plugin in plugins:
        if plugin not in context.Plugins:
            print "Warning : %s plugin not found" % plugin
            plugins.remove (plugin)
            continue
        context.Plugins[plugin].Enabled = False
    if len (plugins) == 0:
        print "Error : no plugin found"
        exit (1)
    context.Write ()
    print "Waiting before reloading"
    sleep (2)
    print "Loading " + " ".join (plugins)
    for plugin in plugins:
        context.Plugins[plugin].Enabled = True
    context.Write ()
July 9th, 2007 @ 04:14

Erm, I just found what was wrong with gShaderEdit on Intel & al. I wasn’t redrawing the window *cough*. This *problem* is now fixed, but I don’t like the end result anyway : moving the window is dodgy, enabling/disabling the shader is dodgy (by dodgy I mean that black frames or part of black frames appear for a few seconds), the gl area is drawn directly on the screen and not on the window (so that if you have a window above the gl area you still see the gl stuff on top of it). I am probably going to add some kind of buffering tomorrow, so that I would just draw the frame once (it would be a lot lighter for CPU/GPU by the way), or just completely change the way I am doing things *sigh*.

Update : I was unable to sleep with this issue running.. I have just fixed it in a much better way : the application just draws the GL frame once, dumps the pixels with glReadPixels, creates a python array from the pixels data and creates a Cairo ImageSurface from the data array. A quick Cairo transformation flips the surface vertically (the initial pixels are y-inverted). The result surface is then simply drawn on the widget, saving precious GPU and CPU clocks and avoiding ugly bugs.

Just for the record, I haven’t found a single “gl Pixels to cairo surface” function using Google Code Search & al, so I thought I could post mine here :

def dump (width, height):
    '''Dump current GL context pixels into a cairo surface'''
    glPixelStorei (GL_PACK_ALIGNMENT, 1)
    # Dump pixels to a string
    pixels = glReadPixels (0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE)
    # Create a python array from pixels data
    data = array.array ("c", pixels)
    s = cairo.ImageSurface.create_for_data (data, cairo.FORMAT_ARGB32,
                                            width, height, 4 * width)
    # Flip surface vertically
    surface = cairo.ImageSurface (cairo.FORMAT_ARGB32, width, height)
    cr = cairo.Context (surface)
    matrix = cairo.Matrix (yy = -1, y0 = height)
    cr.transform (matrix)
    cr.set_source_surface (s)
    cr.paint ()
    return surface

Update 2 : *sigh* I just figured the current code is buggy. It looks like the color format is wrong : glReadPixels produces RGBA data while cairo.ImageSurface.create_for_data expects BGRA… (I already faced this issue while writing the hue picker, but I’m not sure on the way I should fix it there :/)

Update 3 : actually, it was easy ^^ Just had to define GL_BGRA (PyOpenGL is definitely kinda outdated) and that did it!

July 8th, 2007 @ 22:50

We are now reaching the half of this Summer of Code, and it is about time to review what has been done so far.

Read the rest of this entry »

July 4th, 2007 @ 20:29

One of the things that are particularly boring when working on a Compiz plugin is that you have to reload it all the time, and there is no quick way to do it (I usually unload/reload it through ccsm, which involves some mouse deplacement and needs a ccsm window, or or I just reload compiz, which takes quite a bit of time).

Consequently I thought I could write a little script that would reload one or more plugins for me quickly. It took less than 5 minutes with the neat libcompizconfig python bindings, and works pretty well as far as I can see. (so yes, it depends on libcompizconfig, but heh, that’s worth it)

Here is the code :

#!/usr/bin/env python
 
'''Compiz plugin reloader (through compizconfig)
Copyright (c) 2007 Guillaume Seguin <guillaume@segu.in>
Licensed under GNU GPLv2'''
 
import compizconfig
from sys import argv, exit
from time import sleep
 
if __name__ == "__main__":
    if len (argv) < 2:
        print "Usage : %s plugin1 [plugin2 ... pluginN]" % argv[0]
        exit (2)
    plugins = argv[1:]
    context = compizconfig.Context (basic_metadata = True)
    print "Unloading " + " ".join (plugins)
    for plugin in plugins:
        if plugin not in context.Plugins:
            print "Warning : %s plugin not found" % plugin
            plugins.remove (plugin)
            continue
        context.Plugins[plugin].Enabled = False
    if len (plugins) == 0:
        print "Error : no plugin found"
        exit (1)
    context.Write ()
    print "Waiting for settings update"
    sleep (2)
    print "Loading " + " ".join (plugins)
    for plugin in plugins:
        context.Plugins[plugin].Enabled = True
    context.Write ()
July 2nd, 2007 @ 02:30

Here is a quick preview of the filter generator UI that will allow users to create their own color filters :

Filter generator preview

That’s just a preview of the UI though, mainly because I need to write a fragment program that can handle HSV color representation (HSV => RGB and RGB => HSV translations).
Comments welcome, especially since I really suck when it comes to usable & humane user interface design :p

July 2nd, 2007 @ 01:36

After yesterday’s rant, I wrote my very own Hue Picker in pyGTK. It is meant to be very simple and straight forward : it just exposes a “changed” signal and two useful functions (get_hue / set_hue).

The usual preview :
Hue Picker Preview
(the “Source hue : [23 ^]” is just a placeholder and isn’t part of the widget)

Full source included below.
The ring drawing code is a mostly direct python port of C GtkHSV’s one.

Read the rest of this entry »

July 1st, 2007 @ 15:11

pyGTK seriously lacks a good and simple color picker!
The current color picking widget, gtk.ColorSelection, is just way too huge and complicated : it provides several numeric entries for finer selection of HSV or RGB values, or can feature a customizable palette, but I don’t need all that (indeed I just need a Hue picker)
The worst thing is that it looks like there’s already a much simpler widget, which is available in C gtk+ (namely GtkHSV) and that it even has some form of bindings in pyGTK (type (gtk.ColorSelection ().get_children ()[0].get_children ()[0].get_children ()[0]) returns <class ‘__main__.gtk.HSV’>) but it doesn’t expose any of the interesting functions (such as get_color)
Anyway, I guess it’s about time to write another custom widget.

June 20th, 2007 @ 01:20

Hurray! School is (pretty much) done for this year, and my some of my long delayed work-in-progress projects are getting to an usable point. Summer of Code is now – at last – my main and only focus for the rest of the summer :)

I have just packed all my previous thoughts and finally decided on how to do it all.

  1. Use HSV (Hue, Saturation, Value) color representation – it is just the most appropriate color space for that kind of stuff, especially since it’s the representation used by most color pickers (a color ring to choose hue and a triangle to choose saturation and value).
  2. Write a generic fragment program loading plugin for Compiz. This generic plugin will have to parse fragment program source to plug it into Compiz’s fragment interface (which makes programs combinations (for example blur + negative + blur) possible). I’m probably going to make sure that other plugins would be able to easily reuse the loader (it would have been pretty easy with the basic library functions in Beryl, I will have to check what can be done at the moment in Compiz, using the newly introduced cube/rotate sharing bits might just be the solution, but I haven’t had the time to actually read it).
  3. Extend the generic plugin to match the required features (mainly cumulative filters and filter quick switch).

I will hopefully have a “working” generic plugin by the end of the week. The parser shouldn’t be a huge deal (5 or 6 hours in the worst case), and wrapping the generated code in a Compiz plugin shouldn’t take more than 2 hours. Sounds doable :))

On a side note, gShaderEdit now also supports vertex programs (the detection of the program type is done on the fly according to the header of the program, which should either be !!ARBvp1.0 or !!ARBfp1.0 as standardized by the ARB)

June 18th, 2007 @ 22:03

As part of my courses, I had to learn camllight programming this year. Sadly, all my school’s computers are powered by Windows, a non free closed source operating system, and the default frontend is the less usable thing around : copy pasting is completely broken, dumb default configuration, oldish user interface, no syntax highligting… The worst thing is that the sources aren’t published, I was just able to get those of the Objective Camlui frontend (which is quite the same, but in a much better fashion).

I know that there are already several other ways to have better “frontends”, such as cmdcaml, or using XEmacs with the tuareg mode, but heh, I was willing to provide something all-in-one that looks good. And I needed a good excuse to learn Python and PyGTK.

After a huge rewrite, exploding the original 1500 lines long source, here is a publishable version, embedding the camllight interpreter and using gtksourceview2 for syntax highlighting.

The usual preview :
CamlUI preview (thumbnail)

Feisty debs are published at http://guillaume.segu.in/code/debs/camlui/, with a helper script which fetches the 4 required debs (camlui + gtksourceview2(-common) + python-gtksourceview2) and installs them.
Source is versionned in a git repo at http://guillaume.segu.in/code/camlui.git/

Feedback welcome :)