Sunday, April 30, 2006

Buffy the Vampire Slayer Audio CDs

Yes, I am a fan of BtVS given than I watched all 144 episodes over 2 months. To cut a long story short, the soundtrack (on 3 CDs released at different points when the series was on TV) finally arrived from Amazon after a few months.

In my opinion, Buffy The Vampire Slayer: The Album (1999 Television Series) [SOUNDTRACK] is really not worth buying except for theme music and "Close Your Eyes", which is played when Buffy murders her boyfriend Angel at the end of Season 2 in order to save the world (so much for "boyfriend"; and yes, Ashlee Simpson can make a song entitled that if she wishes).

Ditto, for Buffy The Vampire Slayer: Radio Sunnydale [SOUNDTRACK] (2003 US Version). A good track is "The Final Fight" - the music for the last episode, when the slayers are somehow kicking all the Ubervamps' butts (a nice plot inconsistency: even though Buffy the veteran slayer had trouble killing a single Ubervamp throughout all of Season 7, in the last episode, other slayers and Giles/Spike/Xander/Dawn had no problems; won't say the same about Anya though...).

The last CD, the infamous Once More, with Feeling (2002) musical soundtrack is pretty good esp. Sarah Michelle Gellar's singing in "Going Through the Motions" and "Walk Through the Fire". A nice addition was not Joss Whedon's wife singing :) but rather "Sacrifice", the track for the Season 5 finale, where Buffy selfishly jumps off the tower to "to save the world" when in reality, she knows she's going to go to heaven instead of her sister (yes, my postmodernist interpretation). Jokes aside, this is probably the best finale in the whole series (it's also the 100th episode by accident).

On the other hand, Season 4 was hopeless and in its final episode, literally nothing happened!

I would really like to see an 8th season, set 10 years after the last, when all the actors are older and can't walk but still have to battle vampires etc. :)

So what's up with KolourPaint in KDE4?

These last couple of weeks I've been trying to find a 1:1 mapping between the Qt3 graphics functions and the Qt4 ones. Later, I'll blog in the detail how to really port QPainter/QPixmap/QImage. The Qt4 porting guide is too scanty on important semantic changes.

In the meantime, KolourPaint in trunk/ has regressed a bit because of the KToolBar/KAction changes:



As you can see, the size hints for the Colour Box and Tool Box are not working and since those 6x6 tool icons are so hard to click, I had to manually add them to the main toolbar. I'll fix this stuff after I get the paint engine porting under control.

And apart from not working, KolourPaint is really slow (and blocks the X server) because of unneeded QPixmap <-> QImage transformations in the deprecated copyBlt() (my local changes have dropped copyBlt()) and calls to QPixmap::mask(). The latter has this rather amusing comment in qt-copy/src/gui/image/qpixmap_x11.cpp:


#ifndef QT_NO_XRENDER
if (data->picture && data->d == 32) {
// #### slow - there must be a better way
mask = QBitmap::fromImage(toImage().createAlphaMask());
} else
#endif


Well, at least it was amusing at 1am... See, QPixmap::mask() in Qt3 returned a pointer to a precomputed mask so it was real fast. In Qt4 however, it does X serverside magic and in the worst case (the above snippet of code for QPixmap's with alpha channels), it does 2 X server roundtrips plus computation. In fact, it takes 450ms for an 800x600 image on a 2.2Ghz processor. Simply unbearable.

But the good news is that most calls to QPixmap::mask() in KolourPaint are either for drawing on the mask (since QPainter3 would happily draw on the QPixmap but not update the mask; for Qt4, I will drop this code shortly) or determining whether the pixmap has a mask. For the latter, this Qt3 code:


if (pixmap.mask ())


should not be ported to:


if (!pixmap.mask ().isNull ())


contrary to a previous blog entry since QPixmap::mask() does expensive computation that we throw away anyway. Do this instead:


if (pixmap.hasAlpha ())


So in a couple of weeks, expect a commit that will make painting work. Also, the next time Trolltech and KDE make such sweeping changes to their APIs, they should feel free to send me a cheque :) I am simply spending far too much time porting 50 thousand lines of code (which feels like a "NOP" job) rather than writing new features or doing real work (uni).

An old joke is that if you don't understand what "deprecated method" means, you should update to Java 1.2 (or maybe it was 1.4?). Now, they can add Qt to that. I now have literally 500 "deprecated" warnings to go through. coolo, mueller and montel have been doing a lot of fixes though - thanks! But on the flip side, I'm duplicating some work (I don't commit often due to internet time limitations) and it means I have to review a lot of patches:


$ wc -l *.diff
36 521415.diff
859 528186.diff
492 530108-533650.diff
1000 535185-535614.diff
0 ALSO_REVIEW_CMAKE.diff
2387 total


Speaking of which, cmake works quite well. In fact, I'm going to remove the remaining Makefile.am's since compat/Makefile.am is already gone. cmake is faster than unsermake, which was faster than automake. cmake supports "make -t". Also, if you want to hack CXXFLAGS long after running "cmake", the file you want is kdegraphics-build/kolourpaint/CMakeFiles/kolourpaint.dir/flags.make.

Now, back to work!

Tuesday, April 11, 2006

Determining if a type is a pointer at compile time

http://doc.trolltech.com/4.1/qlist.html:

"QList is represented as an array of pointers to items. (Exceptionally, if T is a pointer type, a basic type of the size of a pointer, or one of Qt's shared classes, QList stores the item directly in the pointer.)"

So a QList<QRect> is going to be an array of QRect * but QList<QWidget *> will be an array of QWidget * - not QWidget **. But how can we determine whether a type is a pointer at compile time?

Update: This describes the method used in Qt for compilers lacking support for partial template specialisation. If you have partial specialisation, it can be done in a very straightforward way (see blog comments).

Well it seems to be done by abusing our favourite language. QList uses the QTypeInfo class - the interesting bits being:


template <typename T> char QTypeInfoHelper(T*(*)());
void* QTypeInfoHelper(...);

template <typename T>
class QTypeInfo
{
public:
enum {
isPointer = (1 == sizeof(QTypeInfoHelper((T(*)())0))),


int *



Now, consider the case of QTypeInfo<int *>. the last line expands to:

isPointer = (1 == sizeof(QTypeInfoHelper( (int * (*) ()) 0))).

Now I've added some spaces so that it can actually be read. It's saying, pass NULL - casted to a pointer to a function that accepts nothing and returns an int * - to one of the QTypeInfo functions.

But to which 1 of the 2 overloads? Well, the C++ rule for resolving overloaded function calls is to select the function with the most specific matching argument types, assuming no ambiguity. In this case, our function call matches this candidate:

template <typename T> char QTypeInfoHelper(T * (*) ());

because T is int. Now you won't actually find QTypeInfoHelper defined anywhere because all sizeof is interested in is the size of the return value of the function - it doesn't actually execute it. Now, the return type is a char, which is of size 1, therefore isPointer is true.

int



Now we look at a non-pointer type, int. The tricky line expands to:

isPointer = (1 == sizeof(QTypeInfoHelper( (int (*) ()) 0))).

That function pointer argument type accepts nothing and returns an int. It's not going to match the same overload because it's looking for a pointer (the T *):

template <typename T> char QTypeInfoHelper(T * (*) ());

And int is no pointer. Therefore, it can only match the catch-all overload:

void* QTypeInfoHelper(...);

which returns a void* and guess what:

isPointer = (1 == sizeof(void *)).

is false (unless on an 8-bit machine :)). Therefore, int is correctly detected as not a pointer.

Conclusion



QTypeInfo manages to determine whether a type is a pointer without:


  1. actually invoking a function (by merely checking the size of the return type)

  2. ever constructing an element of type T (which might otherwise cause side effects)

Thursday, April 06, 2006

Moving messages in PINE

I'm stuck with using PINE/Mutt at uni - partially because they don't have KMail (or much of KDE for that matter) and mainly because I want to be able to access it over SSH from anywhere. Today my inbox finally got too big to manage so I started splitting it up into multiple folders and combined the scheme with filters.

And guess how you save a message in PINE? Well it's not by pressing 'S' for "Save" (that moves messages between folders). It's 'E' for "Export".

In other news, I had a dream where I swear I saw Terminator 4. Great sequel, plot but as with all good dreams, I forgot by the time I had woken up. Too bad. Would have sold the plot to the Governor of California otherwise.