#include "views.h"
#include <assert.h>
#include <dirent.h>
#include <unistd.h>

#define PERSON_DIV	12
#define HEAD_FRAC	.29
#define HAND_FRAC	.2

/* Gimp needs fonts placed in ~/.fonts, but cairo needs them in ~/Library/Fonts/ */

struct col { float red, green, blue; };
static struct col person_colors[] = {
    { 1.0, 0.1, 0.1 },
    { 0.0, 1.0, 0.0 },
    { 0.1, 0.1, 1.0 },
    { 1.0, 1.0, 0.0 },
    { 0.0, 1.0, 1.0 },
    { 1.0, 0.0, 1.0 },
    { 0.6, 0.6, 0.6 },
    { 0.5, 0.5, 0.7 }
};
static struct col edge_colors[] = {
    { 0.9, 0.0, 0.0 },
    { 0.0, 0.7, 0.0 },
    { 0.0, 0.0, 0.9 },
    { 0.7, 0.7, 0.0 },
    { 0.0, 0.7, 0.7 },
    { 0.7, 0.0, 0.7 },
    { 0.4, 0.4, 0.4 },
    { 0.3, 0.3, 0.5 }
};

void danceGetCoupleColor(int c, int *edge,int *body) {
    if ( c>=0 && c<=8 ) {
	if ( edge!=NULL ) {
	    *edge = (((int) (edge_colors[c].red  *255))<<16) |
		    (((int) (edge_colors[c].green*255))<<8) |
		    (((int) (edge_colors[c].blue *255)));
	}
	if ( body!=NULL ) {
	    *body = (((int) (person_colors[c].red  *255))<<16) |
		    (((int) (person_colors[c].green*255))<<8) |
		    (((int) (person_colors[c].blue *255)));
	}
    }
}

int dancePersonWidth(DanceView *dv) {
    Dance *d = dv->dance;

return( d->units/PERSON_DIV * dv->scale );
}

static cairo_pattern_t *danceMakePattern(DanceView *dv,int which,struct col *colors,int avail) {
    int person_width = dancePersonWidth(dv);
    int c1, c2;
    cairo_pattern_t *pat;
    cairo_surface_t *temp;
    int square_size = person_width/8;
    cairo_t *cr;

    if ( square_size<2 ) square_size = 2;
    temp = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
	    2*square_size, 2*square_size);
    cr = cairo_create(temp);

    if ( which<0 ) {
	/* this happens int triple minor sets when two couples are out at bottom */
	/*  they must perform the dance with ghosts, and are half in */
	c1 = -1;
	c2 = -which;
	square_size *= 2;
    } else {
	which -= avail;
	c1 = which%avail;
	c2 = which/avail;
	if ( c2>=c1 )
	    ++c2;		/* can't have c1==c2. A checkerboard with one color isn't a checkerboard */
    }

    if ( c1==-1 )
	cairo_set_source_rgb(cr,0,0,0);
    else
	cairo_set_source_rgb(cr,colors[c1].red,
				    colors[c1].green,
				    colors[c1].blue);
    cairo_rectangle(cr, 0,0, square_size, square_size );
    cairo_fill(cr);
    cairo_rectangle(cr, square_size,square_size, square_size, square_size );
    cairo_fill(cr);
    cairo_set_source_rgb(cr,colors[c2].red,
				colors[c2].green,
				colors[c2].blue);
    cairo_rectangle(cr, square_size,0, square_size, square_size );
    cairo_fill(cr);
    cairo_rectangle(cr, 0,square_size, square_size, square_size );
    cairo_fill(cr);
    cairo_surface_flush(temp);

    pat = cairo_pattern_create_for_surface(temp);
    cairo_destroy(cr);
    cairo_surface_destroy(temp);
    cairo_pattern_set_extend(pat,CAIRO_EXTEND_REPEAT);
return( pat );
}

static void setPersonColor(DanceView *dv,int which) {
    int avail = sizeof(person_colors)/sizeof(person_colors[0]);

    /* We have avail different colors. */
    /*  so we have enough colors for avail people with solid colors */
    /*  if we want a two color checkerboard we have avail*(avail-1) options */
    /* together we have avail+(avail-1)*avail==avail^2 options */
    which %= avail*avail;
    if ( which<avail ) {
	cairo_set_source_rgb(dv->cr,person_colors[which].red,
				    person_colors[which].green,
				    person_colors[which].blue);
    } else {
	if ( dv->person_patterns==NULL ) {
	    int cnt = dv->dance->people<avail*avail ? dv->dance->people : avail*avail;
	    dv->person_patterns = calloc(cnt,sizeof( cairo_pattern_t * ));
	}
	if ( dv->person_patterns[which]==NULL )
	    dv->person_patterns[which] = danceMakePattern(dv,which,person_colors,avail);
	cairo_set_source(dv->cr,dv->person_patterns[which]);
    }
}

static void setEdgeColor(DanceView *dv,int which) {
    int avail = sizeof(edge_colors)/sizeof(edge_colors[0]);

    which %= (avail + avail*avail);

    if ( which==-1 )
	/* People out of the set temporarily get a special edge */
	cairo_set_source_rgb(dv->cr,0,0,0);
    else if ( which<0 ) {
	/* Triple minor. When two couples out at bottom they must dance with */
	/*  ghosts for the progression to work. So they are sort of in and out */
	if ( dv->tripleMinorOutAtBottom[3+which]==NULL )
	    dv->tripleMinorOutAtBottom[3+which]=danceMakePattern(dv,which,edge_colors,avail);
	cairo_set_source(dv->cr,dv->tripleMinorOutAtBottom[3+which]);
    } else if ( which<avail ) {
	cairo_set_source_rgb(dv->cr,edge_colors[which].red,
				    edge_colors[which].green,
				    edge_colors[which].blue);
    } else {
	if ( dv->edge_patterns==NULL ) {
	    int cnt = dv->dance->people<avail*avail ? dv->dance->people : avail*avail;
	    dv->edge_patterns = calloc(cnt,sizeof( cairo_pattern_t * ));
	}
	assert( which<avail*avail && which<dv->dance->people );
	if ( dv->edge_patterns[which]==NULL )
	    dv->edge_patterns[which] = danceMakePattern(dv,which,edge_colors,avail);
	cairo_set_source(dv->cr,dv->edge_patterns[which]);
    }
}

static void splineDrawSegment(DanceView *dv,FigureSegment *cs) {
    if ( cs->cmd==c_lineto ) {
	cairo_line_to(dv->cr,dv->xorigin + dv->scale*cs->endPoint.x,
			     dv->height-( dv->yorigin + dv->scale*cs->endPoint.y));
    } else if ( cs->cmd==c_curveto ) {
	cairo_curve_to(dv->cr,
			    dv->xorigin + dv->scale*cs->cp1.x,
			    dv->height-( dv->yorigin + dv->scale*cs->cp1.y),
			    dv->xorigin + dv->scale*cs->cp2.x,
			    dv->height-( dv->yorigin + dv->scale*cs->cp2.y),
			    dv->xorigin + dv->scale*cs->endPoint.x,
			    dv->height-( dv->yorigin + dv->scale*cs->endPoint.y));
    }
}

static void _danceStrokeSplinePath(DanceView *dv,Person *dancer, int frame_millibars,
	int start_millibars, int end_millibars) {
    FigureSegment *cs = dancer->current;
    FigureSegment *top = dancer->figures.moves+dancer->figures.move_cnt;
    FigureSegment *start, *last;
    FigureSegment split[2], split2[2], *drawcs;
    int soff, eoff;
    static double dashes[1] = { 10 };
    /* Don't try to show the track around a danceReset because the dancer */
    /*  will probably be dancing (or have danced) a different role then so */
    /*  the moves will be/were different */

    if ( frame_millibars==0 )		/* Don't display track on title page */
return;

    if ( dv->dont_show_couples_above_me>0 &&
	    dancer->couple_number > dv->dont_show_couples_above_me )
return;

    soff = frame_millibars - dancer->millibars_start_of_current + start_millibars;
    start = cs;
    if ( soff<0 ) {
	for ( --start ; start>=dancer->figures.moves && soff<0 ; --start )
	    soff += start->millibars;
	++start;
    }
    if ( start==dancer->figures.moves ) {
	soff = 0;
	start = dancer->figures.moves+1;
	/* Start of first segment in current minor set */
    }
    assert(soff>=0);
    eoff = frame_millibars - dancer->millibars_start_of_current + end_millibars;
    last = cs;
    while ( last<top && eoff>last->millibars ) {
	eoff -= last->millibars;
	++last;
    }
    if ( last>=top ) {
	last = top-1;
	eoff = last->millibars;
	/* End of last segment in current minorset */
    }

    while ( start<last && start->cmd!=c_lineto && start->cmd!=c_curveto ) {
	++start;
	soff = 0;
    }
    while ( last>dancer->figures.moves && last->cmd!=c_lineto && last->cmd!=c_curveto ) {
	--last;
	eoff = last->millibars;
    }
    if ( last<start || (last==start && soff>=eoff) )	/* Only a bunch of pauses in the time we track, nothing to display */
return;

    setPersonColor(dv,dancer->couple_number-1);
    if ( start_millibars<0 )
	cairo_set_dash(dv->cr,dashes,1,0);
    drawcs = start;
    if ( soff!=0 ) {
	splineBisect(start,split,soff/(double) start->millibars);
	drawcs = split+1;
    }
    if ( start==last && eoff<start->millibars ) {
	splineBisect(drawcs,split2,eoff/(double) start->millibars);
	drawcs=split2;
    }
    cairo_new_path(dv->cr);
    cairo_move_to(dv->cr,dv->xorigin + dv->scale*drawcs->splines[0].d,
			 dv->height-(dv->yorigin + dv->scale*drawcs->splines[1].d));
    splineDrawSegment(dv,drawcs);
    if ( start<last ) {
	++start;
	while ( start<last ) {
	    splineDrawSegment(dv,start);
	    ++start;
	}
	if ( eoff!=0 ) {
	    splineBisect(last,split,eoff/(double) start->millibars);
	    splineDrawSegment(dv,split);
	}
    }
    cairo_stroke(dv->cr);
    if ( start_millibars<0 )
	cairo_set_dash(dv->cr,dashes,0,0);
}

static void danceStrokeSplinePath(DanceView *dv,Person *dancer, int frame_millibars) {
    if ( dv->past_path_millibars>0 )
	_danceStrokeSplinePath(dv,dancer,frame_millibars,-dv->past_path_millibars,0);
    if ( dv->future_path_millibars>0 )
	_danceStrokeSplinePath(dv,dancer,frame_millibars,0,dv->future_path_millibars);
}

static void drawDancerOutline(DanceView *dv,Person *dancer,int x,int y,int person_width,double c,double s, int levitation_len, float my_honour) {
	/*cairo_arc(dv->cr, x,y,person_width/2,0,2*PI);*/
	/* For an elipse 1x.5 centered at 0,0 */
	/* -.5,0  -.5,.138   -.276,.25   0,.25  */
	/* 0,.25   .276,.25   .5,.138    .5,0   */
	/* .5,0    .5,-.138   .275,-.25  0,-.25 */
	/* 0,-.25 -.276,-.25 -.5,-.138  -.5,0   */
    if ( dancer->sex==s_female || dancer->sex==s_right ) {
	double lx=x+s*levitation_len,ly=y+c*levitation_len;

	cairo_move_to(dv->cr,lx-c*person_width/2,dv->height-(ly+s*person_width/2));
	cairo_curve_to(dv->cr,
	    lx-c*person_width/2+s*.138*person_width,dv->height-(ly+s*person_width/2+c*.138*person_width),
	    lx-c*.276*person_width+s*person_width/4,dv->height-(ly+s*.276*person_width+c*person_width/4),
	    lx+s*person_width/4,dv->height-(ly+c*person_width/4) );
	cairo_curve_to(dv->cr,
	    lx+c*.276*person_width+s*person_width/4,dv->height-(ly-s*.276*person_width+c*.25*person_width),
	    lx+c*person_width/2+s*.138*person_width,dv->height-(ly-s*person_width/2+c*.138*person_width),
	    lx+c*person_width/2,dv->height-(ly-s*person_width/2));
	if ( levitation_len!=0 ) {
	    cairo_line_to(dv->cr,x+c*person_width/2,dv->height-(y-s*person_width/2));
	}
	cairo_curve_to(dv->cr,
	    x+c*person_width/2-s*.138*person_width,dv->height-(y-s*person_width/2-c*.138*person_width),
	    x+c*.276*person_width-s*person_width/4,dv->height-(y-s*.276*person_width-c*person_width/4),
	    x-s*person_width/4,dv->height-(y-c*person_width/4));
	cairo_curve_to(dv->cr,
	    x-c*.276*person_width-s*person_width/4,dv->height-(y+s*.276*person_width-c*person_width/4),
	    x-c*person_width/2-s*.138*person_width,dv->height-(y+s*person_width/2-c*.138*person_width),
	    x-c*person_width/2,dv->height-(y+s*person_width/2));
	if ( levitation_len!=0 )
	    cairo_line_to(dv->cr,lx-c*person_width/2,dv->height-(ly+s*person_width/2));
    } else {
	int width  = person_width;
	int height = person_width/2*(1+my_honour) + levitation_len;
	int start_x = x+c*person_width/2 - s*person_width/4;
	int start_y = y-s*person_width/2 - c*person_width/4;
	cairo_move_to(dv->cr,start_x,dv->height-start_y);
	cairo_line_to(dv->cr,start_x-c*width,dv->height-start_y-s*width);
	cairo_line_to(dv->cr,start_x-c*width+s*height,dv->height-start_y-s*width-c*height);
	if ( dancer->sex==s_center ) {
	    int extra = height+person_width/6;
	    cairo_line_to(dv->cr,start_x-c*width/2+s*extra,dv->height-start_y-s*width/2-c*extra);
	}
	cairo_line_to(dv->cr,start_x+s*height,dv->height-start_y-c*height);
/*
	cairo_line_to(dv->cr,lx+(c-s/2)*person_width/2,dv->height-(ly-(s+c/2)*person_width/2*(1+my_honour)));
	cairo_line_to(dv->cr,x-(s/2+c)*person_width/2,dv->height-(y+(s-c/2)*person_width/2*(1+my_honour)));
	cairo_line_to(dv->cr,x+(s/2-c)*person_width/2,dv->height-(y+(c/2+s)*person_width/2));
*/
    }
}

static void drawDancerStripes(DanceView *dv,Person *dancer,int x,int y,int person_width,double c,double s, int levitation_len, float my_honour) {

    cairo_save(dv->cr);
    cairo_clip(dv->cr);

    cairo_move_to(dv->cr, x+c*person_width/2 - s*person_width/4, dv->height-(y-s*person_width/2 - c*person_width/4 ) );
    cairo_line_to(dv->cr, x-c*person_width/2 + s*person_width/4, dv->height-(y+s*person_width/2 + c*person_width/4 ) );
    cairo_move_to(dv->cr, x+c*person_width/2 + s*person_width/4, dv->height-(y-s*person_width/2 + c*person_width/4 ) );
    cairo_line_to(dv->cr, x-c*person_width/2 - s*person_width/4, dv->height-(y+s*person_width/2 - c*person_width/4 ) );

    cairo_set_source_rgb(dv->cr,0,0,0);
    cairo_stroke(dv->cr);
    cairo_restore(dv->cr);
    drawDancerOutline(dv,dancer,x,y,person_width,c,s,levitation_len,my_honour);
    cairo_close_path(dv->cr);
}

static void danceDrawDancer(DanceView *dv,Person *dancer) {
    int person_width = dancePersonWidth(dv);
    int head_diam    = HEAD_FRAC * person_width;
    float head_center_x, head_center_y;
    float degrees = dancer->now_orientation;
    double c = -cos(degrees*PI/180.0);
    double s = sin(degrees*PI/180.0);
    int which = dancer->couple_number-1;		/* Couple number is 1 based for humans */
    int x = dv->xorigin + dancer->now.x*dv->scale;
    int y = dv->yorigin + dancer->now.y*dv->scale;
    int levitation_len = 1.5*person_width * dancer->levitation.now/100.0;
    float my_honour = dancer->now_honour;
    enum role display_role;

    display_role = dancer->display_role == r_last_unused ? dancer->effective_role : dancer->display_role;

    if ( dv->dont_show_couples_above_me>0 &&
	    dancer->couple_number > dv->dont_show_couples_above_me )
return;
    if ( my_honour==0 )
	my_honour = dv->dance->honour;

    if ( my_honour!=0 && (dancer->sex==s_female || dancer->sex==s_right) ) {
	drawDancerOutline(dv,dancer,x,y,(1+my_honour)*person_width,c,s,0, my_honour);
	setPersonColor(dv,which);
	cairo_fill(dv->cr);
    }

    if ( levitation_len!=0 ) {
	/* draw legs first as bits will overlap body */
	int leg_x_top, leg_y_top;
	cairo_save(dv->cr);
	cairo_set_line_width(dv->cr,person_width/6);
	setPersonColor(dv,which);
	leg_x_top = x + s*levitation_len - c*person_width/4;
	leg_y_top = y + c*levitation_len + s*person_width/4;
	cairo_move_to(dv->cr,leg_x_top,dv->height-leg_y_top);
	cairo_line_to(dv->cr,leg_x_top + s*1.5*levitation_len,dv->height-(leg_y_top + c*1.5*levitation_len));
	leg_x_top += c*person_width/2;
	leg_y_top -= s*person_width/2;
	cairo_move_to(dv->cr,leg_x_top,dv->height-leg_y_top);
	cairo_line_to(dv->cr,leg_x_top + s*1.5*levitation_len,dv->height-(leg_y_top + c*1.5*levitation_len));
	cairo_stroke(dv->cr);
	cairo_restore(dv->cr);
    }

    /* When a bow happens (men) it's the head that moves forward, while the butt */
    /*  remains fixed. Not only that but from our view point above the head will */
    /*  also appear bigger as the bow deepens */
    if ( my_honour!=0 && dancer->sex==s_male ) {
	int bow_len = person_width/2*my_honour;
	y -= c*bow_len;
	x -= s*bow_len;
    }

    head_center_x = x - s*person_width/5;
    head_center_y = y - c*person_width/5;
    if ( my_honour!=0 && (dancer->sex==s_male || dancer->sex==s_left || dancer->sex==s_center) ) {
	int head_extra = my_honour*person_width/4;	/* Let the head stick out a little bit on the bow */
	head_center_y -= c*head_extra;
	head_center_x -= s*head_extra;
    }

    if ( !dancer->face_hide_normal ) {
	cairo_arc(dv->cr, head_center_x,dv->height-head_center_y,head_diam,0,2*PI);
	cairo_close_path(dv->cr);
	setPersonColor(dv,which);
	cairo_fill_preserve(dv->cr);
	if ( dv->dance->is_figure )
	    setEdgeColor(dv,which);
	else
	    setEdgeColor(dv,role2couple[display_role]-1);
	cairo_stroke(dv->cr);

	drawDancerOutline(dv,dancer,x,y,person_width,c,s,levitation_len, my_honour);
	cairo_close_path(dv->cr);
    }

    setPersonColor(dv,which);
    cairo_fill_preserve(dv->cr);
    if ( display_role==r_waiting_left || display_role==r_waiting_right )
	display_role = r_waiting;
    if ( display_role==r_waiting ||
	    ((dv->dance->formation==sp_scottishTripleMinor || dv->dance->formation==sp_scottishDupleMinor ) &&
	     (display_role==r_outAtBottom || display_role==r_outAtTop)))
	drawDancerStripes(dv,dancer,x,y,(1+my_honour)*person_width,c,s,0, my_honour);
    if ( dv->dance->is_figure )
	setEdgeColor(dv,which);
    else
	setEdgeColor(dv,role2couple[display_role]-1);
    cairo_stroke(dv->cr);

    if ( dancer->face!=NULL ) {
	float scale;
	/* If the dancer has an image associated with it, then draw the image */
	/*  on top of what we've done so far. Image should be rotated like */
	/*  the person */
	/* I do this in a two step process because figuring out the back */
	/*  transform of a rotate and scale is beyond me */
	cairo_save(dv->fcr);
	cairo_set_source_rgba(dv->fcr,0,0,0,0);
	/* I can't fill with a transparent forground to clear the surface */
	cairo_set_operator(dv->fcr,CAIRO_OPERATOR_CLEAR);
	cairo_rectangle(dv->fcr,0,0,dv->face_size,dv->face_size);
	cairo_fill(dv->fcr);
	cairo_restore(dv->fcr);
	cairo_save(dv->fcr);
	cairo_translate(dv->fcr,dv->face_size/2,dv->face_size/2);
	cairo_rotate(dv->fcr,-degrees*PI/180.0);
	cairo_translate(dv->fcr,-dv->face_size/2,-dv->face_size/2);
	cairo_rectangle(dv->fcr,0,0,dv->face_size,dv->face_size);
	gdk_cairo_set_source_pixbuf(dv->fcr,dancer->face,
		0,0 );
	cairo_fill(dv->fcr);
	cairo_restore(dv->fcr);

	scale = 1.125*person_width*dancer->facescale/(double) dv->face_size;
	cairo_save(dv->cr);
	cairo_scale(dv->cr,scale,scale);
	cairo_rectangle(dv->cr,x/scale-dv->face_size/2,(dv->height-y)/scale-dv->face_size/2,
		dv->face_size,dv->face_size);
	cairo_set_source_surface(dv->cr,dv->face_surface, x/scale-dv->face_size/2,(dv->height-y)/scale-dv->face_size/2 );
	cairo_fill(dv->cr);
	cairo_new_path(dv->cr);
	cairo_restore(dv->cr);
    }

    if ( dv->show_person ) {
	char buf[20];
	PangoRectangle r;
#if 0
	sprintf( buf, "%d(%d%c)", dv->dance->formation!=sp_threeFaceThree ?
		2*(dancer->couple_number-1) + dancer->sex :
		3*(dancer->couple_number-1) + dancer->sex-s_left,
		dancer->couple_number, sexChars[dancer->sex]);
#else
	sprintf( buf, "%d%c", dancer->couple_number, toupper(sexChars[dancer->sex]));
#endif
	cairo_save(dv->cr);
	pango_layout_set_text(dv->person_layout,buf,-1);
	pango_layout_get_pixel_extents(dv->person_layout,NULL,&r);
	cairo_move_to(dv->cr,x-r.width/2+r.width/15,dv->height-(y+r.height/2-r.height/15));
	if (dancer->couple_number==1 || dancer->couple_number==3 || dancer->couple_number==6 )
	    cairo_set_source_rgb(dv->cr,1,1,1);
	else
	    cairo_set_source_rgb(dv->cr,0,0,0);
/*	    cairo_set_source_rgb(dv->cr,(dv->dance->text_color>>16)/256.0,((dv->dance->text_color>>8)&0xff)/256.0,(dv->dance->text_color&0xff)/256.0); */
	pango_cairo_show_layout(dv->cr,dv->person_layout);
	cairo_new_path(dv->cr);
	cairo_restore(dv->cr);
    }
}

int dancerOnTheRight( Person *p1, Person *p2) {
    /* Is p2 on the right of p1 */
    /* Note that rightness depends on orientation, so if people face opposite */
    /*  directions it is possible for both to be on the right, but if they face*/
    /*  the same way, only one will be on the right */
    float degrees1 = p1->now_orientation;
    double c1 = cos(degrees1*PI/180.0);
    double s1 = sin(degrees1*PI/180.0);

    if ( (p2->now.x-p1->now.x)*c1 + (p2->now.y-p1->now.y)*s1 >0 )
return( true );

return( false );
}

static void danceDrawClasp(DanceView *dv, int h1, int h2) {
    /* there is a hand clasp between the person in h1 and the person in h2 */
    /*  the bit masks in h1 and h2 will say which hand each is using */

    if ( (h1&HAND_PERSON_MASK)>dv->dance->people  )
return;
    if ( dv->dont_show_couples_above_me>0 &&
	    (h1&HAND_PERSON_MASK)>=dv->dont_show_couples_above_me*danceCoupleSize(dv->dance)  ) {
	/* hands are measured in people and there are two people to a couple */
return;
    }

    int person_width = dancePersonWidth(dv);
    int handClaspDiam = person_width*HAND_FRAC;
    Person *p1 = &dv->dance->dancers[(h1&HAND_PERSON_MASK)];
    float degrees1 = p1->now_orientation;
    double c1 = -cos(degrees1*PI/180.0);
    double s1 = sin(degrees1*PI/180.0);
    int x1,x2,x3,y1,y2,y3;
    double factor;
    int debug=0;

    factor=1.0;
    /* Women are elipses, so moving the arms up, or back means moving the shoulders in a bit */
    if ( (p1->sex==s_female || p1->sex==s_right) && (h2&(HAND_FRONT_MASK|HAND_BACK_MASK)) )
	factor = .8;
    if ( h1&HAND_LEFT_MASK ) {
	x1 = dv->xorigin + p1->now.x*dv->scale + factor*c1*person_width/2;
	y1 = dv->yorigin + p1->now.y*dv->scale - factor*s1*person_width/2;
    } else {
	x1 = dv->xorigin + p1->now.x*dv->scale - factor*c1*person_width/2;
	y1 = dv->yorigin + p1->now.y*dv->scale + factor*s1*person_width/2;
    }
    /* p->now is in the center of the body. x1,y1 place the shoulder in the middle of the side */
    if ( h2&HAND_FRONT_MASK ) {
	/* Move the shoulder up to the front edge of the body */
	x1 -= s1*person_width/4;
	y1 -= c1*person_width/4;
    } else if ( h2&HAND_BACK_MASK ) {
	/* Or the back edge */
	x1 += s1*person_width/4;
	y1 += c1*person_width/4;
    }

    if ( (h2&HAND_PERSON_MASK)==HAND_STAR ) {
	x2 = dv->xorigin + p1->now_star_center.x*dv->scale;
	y2 = dv->yorigin + p1->now_star_center.y*dv->scale;
	cairo_set_source_rgb(dv->cr,1.0,.78,0);
    } else {
	int ballroom = (h2&HAND_BALLROOM_MASK);
	if ( (h2&HAND_INDIVIDUAL_MASK)>dv->dance->people )
return;
	if ( dv->dont_show_couples_above_me>0 && (
		(h2&HAND_INDIVIDUAL_MASK)>=dv->dont_show_couples_above_me*danceCoupleSize(dv->dance)) )
return;
	Person *p2 = &dv->dance->dancers[(h2&HAND_INDIVIDUAL_MASK)];
	float degrees2;
	double c2;
	double s2;

	if ( (h2&HAND_INDIVIDUAL_MASK)<(h1&HAND_INDIVIDUAL_MASK) ) {
	    debug=1;
	    cairo_save(dv->cr);
	    cairo_set_line_width(dv->cr,1);
	}
	degrees2 = p2->now_orientation;
	c2 = -cos(degrees2*PI/180.0);
	s2 = sin(degrees2*PI/180.0);
	factor=1.0;
	/* Women are elipses, so moving the arms up, or back means moving the shoulders in a bit */
	if ( (p2->sex==s_female || p2->sex==s_right) && (h2&(HAND_FRONT_MASK|HAND_BACK_MASK)) )
	    factor = .8;
	if ( h2&HAND_LEFT_MASK ) {
	    x2 = dv->xorigin + p2->now.x*dv->scale + factor*c2*person_width/2;
	    y2 = dv->yorigin + p2->now.y*dv->scale - factor*s2*person_width/2;
	} else {
	    x2 = dv->xorigin + p2->now.x*dv->scale - factor*c2*person_width/2;
	    y2 = dv->yorigin + p2->now.y*dv->scale + factor*s2*person_width/2;
	}
	if ( h2&HAND_FRONT_MASK ) {
	    /* Move the shoulder up to the front edge of the body */
	    x2 -= s2*person_width/4;
	    y2 -= c2*person_width/4;
	} else if ( h2&HAND_BACK_MASK ) {
	    /* Or the back edge */
	    x2 += s2*person_width/4;
	    y2 += c2*person_width/4;
	}
	if ( debug )
	    cairo_set_source_rgb(dv->cr,1.0,0,1.0);
	else
	    cairo_set_source_rgb(dv->cr,1.0,.78,0);
	if ( ballroom ) {
	    if ( (h2&HAND_LEFT_MASK) && (h1&HAND_RIGHT_MASK) ) {
		Person *p = p2;
		int h = h2;
		double s=s2,c=c2;
		p2 = p1; h2=h1;
		p1 = p;  h1=h;
		s2 = s1; c2 = c1;
		s1 = s; c1 = c;
	    }
	    if ( (h1&HAND_LEFT_MASK) && (h2&HAND_RIGHT_MASK) ) {
		x3 = dv->xorigin + (p1->now.x*dv->scale + 3*c1*person_width/2 +
				    p2->now.x*dv->scale - 4*c2*person_width/2)/2;
		y3 = dv->yorigin + (p1->now.y*dv->scale - 3*s1*person_width/2 +
				    p2->now.y*dv->scale + 4*s2*person_width/2)/2;
		cairo_move_to(dv->cr,x1,dv->height-y1);
		cairo_line_to(dv->cr,x3,dv->height-y3);
		cairo_move_to(dv->cr,x2,dv->height-y2);
		cairo_line_to(dv->cr,x3,dv->height-y3);
		cairo_stroke(dv->cr);
		cairo_arc(dv->cr, x3,dv->height-y3,handClaspDiam/2,0,2*PI);
		cairo_close_path(dv->cr);
		cairo_fill(dv->cr);
		/* Put the other hands on shoulders maybe? */
		if ( debug )
		    cairo_restore(dv->cr);
return;
	    }
	} else if ( h2&HAND_OUT_MASK ) {
	    double unitx, unity, len;
	    if ( h2&HAND_RIGHT_MASK ) {
		unitx = x1-x2;
		unity = y1-y2;
	    } else {
		unitx = x2-x1;
		unity = y2-y1;
	    }
	    len = sqrt(unitx*unitx+unity*unity);
	    unitx /= len; unity/=len;
	    len *= p1->outness.now/100.0;
	    x3 = (x1+x2)/2 + unity*len;
	    y3 = (y1+y2)/2 - unitx*len;
	    cairo_move_to(dv->cr,x1,dv->height-y1);
	    cairo_line_to(dv->cr,x3,dv->height-y3);
	    cairo_move_to(dv->cr,x2,dv->height-y2);
	    cairo_line_to(dv->cr,x3,dv->height-y3);
	    cairo_stroke(dv->cr);
	    cairo_arc(dv->cr, x3,dv->height-y3,handClaspDiam/2,0,2*PI);
	    cairo_close_path(dv->cr);
	    cairo_fill(dv->cr);
return;	    
	} else if ( h2&(HAND_FRONT_MASK|HAND_BACK_MASK) ) {
	    double unitx, unity, len;
	    unitx = x2-x1;
	    unity = y2-y1;
	    len = sqrt(unitx*unitx + unity*unity);
	    unitx /= len;
	    unity /= len;

	    len = person_width/6;
	    if ( h2&HAND_FRONT_MASK )
		len = -len;
	    if ( dancerOnTheRight(p1,p2) )
		len = -len;
	    x3 = (x1+x2)/2.0 - unity*len;
	    y3 = (y1+y2)/2.0 + unitx*len;
	    cairo_move_to(dv->cr,x1,dv->height-y1);
	    cairo_line_to(dv->cr,x3,dv->height-y3);
	    cairo_move_to(dv->cr,x2,dv->height-y2);
	    cairo_line_to(dv->cr,x3,dv->height-y3);
	    cairo_stroke(dv->cr);
	    cairo_arc(dv->cr, x3,dv->height-y3,handClaspDiam/2,0,2*PI);
	    cairo_close_path(dv->cr);
	    cairo_fill(dv->cr);
return;
	}
    }
    cairo_move_to(dv->cr,x1,dv->height-y1);
    cairo_line_to(dv->cr,x2,dv->height-y2);
    cairo_stroke(dv->cr);
    if ( (h2&HAND_PERSON_MASK)==HAND_STAR )
	cairo_arc(dv->cr, x2,dv->height-y2,handClaspDiam/2,0,2*PI);
    else
	cairo_arc(dv->cr, (x1+x2)/2,dv->height-(y1+y2)/2,handClaspDiam/2,0,2*PI);
    cairo_close_path(dv->cr);
    cairo_fill(dv->cr);
    if ( debug )
	cairo_restore(dv->cr);
}

static Position *dancerRightShoulderPos(DanceView *dv, Person *dancer, Position *pos) {
    double c1 = -cos(dancer->now_orientation*PI/180.0);
    double s1 = sin(dancer->now_orientation*PI/180.0);
    int person_width = dancePersonWidth(dv);

    pos->x = dv->xorigin + dancer->now.x*dv->scale - c1*person_width/2;
    pos->y = dv->yorigin + dancer->now.y*dv->scale + s1*person_width/2;
return( pos );
}

static Position *dancerLeftShoulderPos(DanceView *dv, Person *dancer, Position *pos) {
    double c1 = -cos(dancer->now_orientation*PI/180.0);
    double s1 = sin(dancer->now_orientation*PI/180.0);
    int person_width = dancePersonWidth(dv);

	pos->x = dv->xorigin + dancer->now.x*dv->scale + c1*person_width/2;
	pos->y = dv->yorigin + dancer->now.y*dv->scale - s1*person_width/2;
return( pos );
}

static Position *dancerRightHandPos(DanceView *dv, Person *dancer, Position *shoulder, Position *pos) {
    double theta = dancer->rightHand.now.theta + dancer->now_orientation;
    double c1 = -cos(theta*PI/180.0);
    double s1 = sin(theta*PI/180.0);
    double arm_len = dancePersonWidth(dv) * 1.5 * dancer->rightHand.now.r;

    pos->x = shoulder->x - s1 * arm_len;
    pos->y = shoulder->y - c1 * arm_len;
return( pos );
}

static Position *dancerSwordpointPos(DanceView *dv, Person *dancer, Position *hand, Position *pos, Position *hilt) {
    double theta = dancer->swordPoint.now.theta + dancer->now_orientation;
    double c1 = -cos(theta*PI/180.0);
    double s1 = sin(theta*PI/180.0);
    double sword_len = dancePersonWidth(dv) * 3.0 * dancer->swordPoint.now.r;
    double hilt_len = sword_len/15.0;
    int handClaspDiam = dancePersonWidth(dv)*HAND_FRAC;

    if ( hilt_len<handClaspDiam )
	hilt_len = handClaspDiam*1.2;

    pos->x = hand->x - s1 * (sword_len+hilt_len);
    pos->y = hand->y - c1 * (sword_len+hilt_len);
    hilt->x = hand->x + s1 * hilt_len;
    hilt->y = hand->y + c1 * hilt_len;
return( pos );
}

static Position *dancerLeftHandPos(DanceView *dv, Person *dancer, Position *shoulder, Position *pos) {
    if ( dancer->now_left_hand_point!=-1 ) {
	Person *other = &dv->dance->dancers[dancer->now_left_hand_point];
	Position oshoulder, ohand, ohilt;

	dancerRightHandPos(dv,other,dancerRightShoulderPos(dv,other,&oshoulder),&ohand);
	dancerSwordpointPos(dv,other,&ohand,pos,&ohilt);
    } else {
	double theta = dancer->leftHand.now.theta + dancer->now_orientation;
	double c1 = -cos(theta*PI/180.0);
	double s1 = sin(theta*PI/180.0);
	double arm_len = dancePersonWidth(dv) * 1.5 * dancer->leftHand.now.r;

	pos->x = shoulder->x - s1 * arm_len;
	pos->y = shoulder->y - c1 * arm_len;
    }
return( pos );
}

static int swordPointHeld(Dance *dance,int dancer_num) {
    int i;

    for ( i=0; i<dance->people; ++i ) {
	Person *p = &dance->dancers[i];
	if ( p->now_left_hand_point==dancer_num )
return( 1 );
    }
return( 0 );
}

static void danceDrawSword(DanceView *dv, Person *dancer) {
    Position shoulder, hand, swordpoint, hilt;
    int handClaspDiam = dancePersonWidth(dv)*HAND_FRAC;

    if ( dancer->rightHand.now.r==0 && dancer->swordPoint.now.r==0 )
return;

    dancerRightHandPos(dv,dancer,dancerRightShoulderPos(dv,dancer,&shoulder),&hand);
    dancerSwordpointPos(dv,dancer,&hand,&swordpoint,&hilt);

/* Draw arm first (below swords) */
    cairo_move_to(dv->cr,hand.x,dv->height-hand.y);
    cairo_line_to(dv->cr,shoulder.x,dv->height-shoulder.y);
    cairo_stroke(dv->cr);

    cairo_save(dv->cr);

    cairo_set_line_width(dv->cr,cairo_get_line_width(dv->cr)/2.0 );
    cairo_set_source_rgb(dv->cr,0.9,0.9,0.9);	/* silver blade */
    cairo_move_to(dv->cr,hand.x,dv->height-hand.y);
    cairo_line_to(dv->cr,swordpoint.x,dv->height-swordpoint.y);
    cairo_stroke(dv->cr);

    cairo_set_source_rgb(dv->cr,.65,.16,.16);	/* brown hilt */
    cairo_move_to(dv->cr,hilt.x,dv->height-hilt.y);
    cairo_line_to(dv->cr,hand.x-(hilt.x-hand.x),dv->height-(hand.y-(hilt.y-hand.y)));
    cairo_stroke(dv->cr);

    cairo_restore(dv->cr);

/* Draw the hand on top of hilt */
    cairo_arc(dv->cr, hand.x,dv->height-hand.y,handClaspDiam/2,0,2*PI);
    cairo_close_path(dv->cr);
    cairo_fill(dv->cr);

/* Draw left hand on top of point (if applicable) */
    if ( swordPointHeld(dv->dance,dancer-dv->dance->dancers)) {
	cairo_arc(dv->cr, swordpoint.x,dv->height-swordpoint.y,handClaspDiam/2,0,2*PI);
	cairo_close_path(dv->cr);
	cairo_fill(dv->cr);
    }
}

#if 0
static void danceDrawLSRightHand(DanceView *dv, Person *dancer) {
    Position shoulder, hand;
    int handClaspDiam = dancePersonWidth(dv)*HAND_FRAC;

    dancerRightHandPos(dv,dancer,dancerRightShoulderPos(dv,dancer,&shoulder),&hand);

    cairo_move_to(dv->cr,hand.x,dv->height-hand.y);
    cairo_line_to(dv->cr,shoulder.x,dv->height-shoulder.y);
    cairo_stroke(dv->cr);
    cairo_arc(dv->cr, hand.x,dv->height-hand.y,handClaspDiam/2,0,2*PI);
    cairo_close_path(dv->cr);
    cairo_fill(dv->cr);
}
#endif

static void danceDrawLSLeftHand(DanceView *dv, Person *dancer) {
    Position shoulder, hand;
    int handClaspDiam = dancePersonWidth(dv)*HAND_FRAC;

    if ( dancer->leftHand.now.r==0 && dancer->now_left_hand_point==-1 )
return;

    dancerLeftHandPos(dv,dancer,dancerLeftShoulderPos(dv,dancer,&shoulder),&hand);

    cairo_move_to(dv->cr,hand.x,dv->height-hand.y);
    cairo_line_to(dv->cr,shoulder.x,dv->height-shoulder.y);
    cairo_stroke(dv->cr);
    cairo_arc(dv->cr, hand.x,dv->height-hand.y,handClaspDiam/2,0,2*PI);
    cairo_close_path(dv->cr);
    cairo_fill(dv->cr);
}

static void danceDrawGrid(DanceView *dv) {
    int i;

    cairo_save(dv->cr);
    cairo_set_source_rgb(dv->cr,0,0,0);
    cairo_set_line_width(dv->cr,1.0);
    for ( i=0; i<dv->dance->people; ++i ) {
	Person *p = &dv->dance->dancers[i];
	FigureSegment *cs = &p->figures.moves[0];
	double x,y, len;
	x = dv->xorigin + cs->endPoint.x*dv->scale;
	y = dv->height - (dv->yorigin + cs->endPoint.y*dv->scale);
	len = 20*dv->scale;
	cairo_move_to(dv->cr,x-len,y);
	cairo_line_to(dv->cr,x+len,y);
	cairo_move_to(dv->cr,x,y-len);
	cairo_line_to(dv->cr,x,y+len);
    }
    if ( dv->dance->grid_pos!=NULL ) {
	for ( i=0; dv->dance->grid_pos[i]!=NULL; ++i ) {
	    Position *p = dv->dance->grid_pos[i];
	    double x,y, len;
	    x = dv->xorigin + p->x*dv->scale;
	    y = dv->height - (dv->yorigin + p->y*dv->scale);
	    len = 20*dv->scale;
	    cairo_move_to(dv->cr,x-len,y);
	    cairo_line_to(dv->cr,x+len,y);
	    cairo_move_to(dv->cr,x,y-len);
	    cairo_line_to(dv->cr,x,y+len);
	}
    }
    cairo_stroke(dv->cr);
    cairo_restore(dv->cr);
}

static void ButterflyWhirlBackground(DanceView *dv,int frame_no) {
    static GdkPixbuf *back = NULL;
    GError *error=NULL;
    double angle = frame_no*PI/dv->frames_per_bar;

    if ( back==NULL ) {
	back = gdk_pixbuf_new_from_file("Images/ButterflyWhirlSquare1280.png",&error);
	if ( back==NULL ) {
	    fprintf( stderr, "Failed to open background.\n" );
return;
	}
    }
    cairo_save(dv->cr);
    cairo_translate(dv->cr,0,-(1280-768)/2);
    cairo_translate(dv->cr,1280/2,1280/2);
    cairo_rotate(dv->cr,-angle);
    cairo_translate(dv->cr,-1280/2,-1280/2);
    gdk_cairo_set_source_pixbuf(dv->cr,back, 0,0 );
    cairo_rectangle(dv->cr, 0,0, 1280, 1280 );
    cairo_fill(dv->cr);
    cairo_restore(dv->cr);
}

static GdkPixbuf *danceLoadScaleImage(DanceView *dv,char *background_file,int complain) {
    GdkPixbuf *pix;
    GError *error=NULL;

    if ( background_file==NULL )
return( NULL );

    pix = gdk_pixbuf_new_from_file(background_file,&error);
    if ( pix==NULL ) {
	if ( complain )
	    fprintf( stderr, "Failed to load background image: %s\n", background_file );
    } else {
	int width, height;
	GdkPixbuf *temp;
	width = gdk_pixbuf_get_width(pix);
	height = gdk_pixbuf_get_width(pix);
	if ( width>dv->width && height>dv->height ) {
	    temp = gdk_pixbuf_scale_simple(pix,dv->width,dv->height,GDK_INTERP_BILINEAR);
	    g_object_unref(G_OBJECT(pix));
	    pix = temp;
	}
    }
return( pix );
}

static void danceSetBackgroundSource(DanceView *dv,int frame_no) {

    if ( dv->has_back_frames && frame_no!=dv->dance_frame_count_of_last_back_frame ) {
	char buffer[400]; int f;
	g_object_unref(G_OBJECT(dv->back_img));
	dv->back_img = NULL;
	snprintf( buffer, sizeof(buffer), dv->dance->background_file, ++dv->last_back_frame_loaded );
	dv->back_img = danceLoadScaleImage(dv,buffer,false);
	if ( dv->back_img==NULL ) {
	    for ( f=0; f<101; ++f ) {
		snprintf( buffer, sizeof(buffer), dv->dance->background_file, f );
		dv->back_img = danceLoadScaleImage(dv,buffer,false);
		if ( dv->back_img!=NULL )
	    break;
	    }
	    if ( dv->back_img==NULL )
		dv->has_back_frames = false;
	    else
		dv->last_back_frame_loaded = f;
	}
    }

    if ( dv->title_back_img!=NULL && dv->titling )
	gdk_cairo_set_source_pixbuf(dv->cr,dv->title_back_img,
		0,0 );
    else if ( dv->back_img!=NULL )
	gdk_cairo_set_source_pixbuf(dv->cr,dv->back_img,
		0,0 );
    else if ( dv->back_col!=-1 && dv->fade_col!=-1 && dv->total_dance_frames>0 ) {
	float r, g, b;
	double frac = frame_no/(double) dv->total_dance_frames;
	if ( dv->fade_delay ) {
	    if ( frame_no<dv->total_dance_frames/2 )
		frac = 0;
	    else
		frac = 2.0 * (frame_no-dv->total_dance_frames/2) / (double)dv->total_dance_frames;
	}
	if ( frac<0 ) frac = 0; else if ( frac>1 ) frac = 1;
	r = ((dv->back_col>>16)&0xff)*(1-frac) + ((dv->fade_col>>16)&0xff)*frac;
	g = ((dv->back_col>>8 )&0xff)*(1-frac) + ((dv->fade_col>>8 )&0xff)*frac;
	b = ((dv->back_col    )&0xff)*(1-frac) + ((dv->fade_col    )&0xff)*frac;
	cairo_set_source_rgb(dv->cr, r/255.0, g/255.0, b/255.0);
    } else if ( dv->back_col!=-1 )
	cairo_set_source_rgb(dv->cr, (dv->back_col>>16)/255.0,
		((dv->back_col>>8)&0xff)/255.0,
		(dv->back_col&0xff)/255.0);
    else if ( dv->dance->style == ds_longsword )
	cairo_set_source_rgb(dv->cr, .94,.78,.72 );
    else
	cairo_set_source_rgb(dv->cr, 1.0,.94,.87 );
}

static void danceOrientForWomen(DanceView *dv) {
    cairo_translate(dv->cr,dv->width/2,dv->height/2);
    cairo_rotate(dv->cr,PI);
    cairo_translate(dv->cr,-dv->width/2,-dv->height/2);
}

static int band_loaded=0;
static cairo_surface_t *bandSurface;

static void my_pango_cairo_show_stroked_layout(DanceView *dv,PangoLayout *layout,int x, int y, double alpha) {
    int stroke_color= -1;
    int stroke_size= dv->width<1000? 3 : 5;
    int text_color = dv->dance->text_color==-1 ? 0 : dv->dance->text_color;

    if ( dv->fade_col!=-1 ) {
	stroke_color = dv->back_col;
	cairo_set_line_width(dv->cr,stroke_size);
    } else if (( dv->title_back_img!=NULL || dv->back_img!=NULL ) && dv->dance->stroke_text_on_background ) {
	stroke_color = ( ((text_color>>16)&0xff)>0x80 ? 0 : 0xff0000 ) |
			( ((text_color>>8)&0xff)>0x80 ? 0 : 0xff00 ) |
			( ((text_color)&0xff)>0x80 ? 0 : 0xff );
	cairo_set_line_width(dv->cr,stroke_size);
    }
    if ( stroke_color!=-1 ) {
	cairo_save(dv->cr);	
	cairo_new_path(dv->cr);
	cairo_move_to(dv->cr,x,y);
	pango_cairo_layout_path(dv->cr,layout);
	cairo_set_source_rgba(dv->cr,(stroke_color>>16)/256.0,((stroke_color>>8)&0xff)/256.0,(stroke_color&0xff)/256.0,alpha);
	cairo_stroke_preserve(dv->cr);
	cairo_set_source_rgba(dv->cr,(text_color>>16)/256.0,((text_color>>8)&0xff)/256.0,(text_color&0xff)/256.0,alpha);
	cairo_fill(dv->cr);
	cairo_restore(dv->cr);
    } else {
	cairo_move_to(dv->cr,x,y);
	pango_cairo_show_layout(dv->cr,layout);
    }
}

static double danceActualBarPos(DanceView *dv, int frame_from_curstart) {
    /* Normally this is a simple calculation, just divide frame_from_curstart
	by (double) dv->frames_per_bar. But sometimes we have complicated music
	which changes the number of counts per bar
     */
    CallList *here;
    int bars_sofar = 0;
    double frames = frame_from_curstart;

    if ( dv->currentMusicCallStart==NULL )
return( frames/(double) dv->frames_per_bar/MILLIBARS_MUL );

    for ( here = dv->currentMusicCallStart; here->callName!=NULL ; ++here ) {
	int myframes = (here->millibars * dv->frames_per_bar) / MILLIBARS_MUL;
	if ( myframes>frames ) {
	    bars_sofar += (frames/myframes) * here->actual_millibars;
return( bars_sofar/MILLIBARS_MUL );
	}
	frames -= myframes;
	bars_sofar += here->actual_millibars;
    }
    bars_sofar += frames/(double) dv->frames_per_bar;
return( bars_sofar/MILLIBARS_MUL );
}

void danceRender(DanceView *dv,int frame_no) {
    int i;
    int w;
    PangoRectangle r, r2;
    GError *error=NULL;

    if ( dv->output_svg ) {
	danceSVGRenderFrame(dv, frame_no);
return;
    }
    danceSetBackgroundSource(dv,frame_no);
    cairo_rectangle(dv->cr, 0,0, dv->width, dv->height );
    cairo_fill(dv->cr);
    if ( dv->titling && dv->title_back_img!=NULL && dv->back_img==NULL && dv->back_col==-1 && dv->title_fadeout_frac!=0 ) {
	cairo_set_source_rgba(dv->cr, 1.0,.94,.87, dv->title_fadeout_frac );
	cairo_paint(dv->cr);
    }
    if ( frame_no>=dv->dance->whirl_bar*dv->frames_per_bar &&
	    frame_no<dv->dance->whirl_end_bar*dv->frames_per_bar )
	ButterflyWhirlBackground(dv,frame_no);

    if ( /* frame_no!=0 || !dv->show_title*/ !dv->dance->sicilian ) { /* I think I now prefer to have the band always present */
	cairo_set_source_rgb(dv->cr,0.7,0.7,0.7);
	enum band bandPos = dv->dance->bandPos;
	if ( bandPos==band_unspecified ) {
/*
	    if ( dv->dance->style==ds_square || dv->dance->formation==sp_squaredSet )
		bandPos = band_bottom;
	    else
*/
		bandPos = band_left;
	    /* If enum band were zero based this would just be (bandPos+2)%4, but it's 1 based */
	    if ( dv->womensOrientation )
		bandPos = (bandPos+1)%4 +1;
	}
	if ( !band_loaded && !dv->test ) {		/* in test mode be as fast as possible so no images */
	    double scale;
	    GdkPixbuf *bandImg;
	    if ( bandPos==band_bottom || bandPos==band_top )
		bandImg = gdk_pixbuf_new_from_file("Images/band-h.png",&error);
	    else
		bandImg = gdk_pixbuf_new_from_file("Images/band-v.png",&error);
	    band_loaded = true;
	    if ( bandImg!=NULL ) {
		cairo_t *bcr;
		int w,h;
		if ( bandPos==band_bottom || bandPos==band_top )
		    scale = dv->width/10 / (double) gdk_pixbuf_get_width(bandImg);
		else
		    scale = dv->height/4 / (double) gdk_pixbuf_get_height(bandImg);
		w = scale * gdk_pixbuf_get_width(bandImg);
		h = scale * gdk_pixbuf_get_height(bandImg);
		bandSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,w,h);
		bcr = cairo_create(bandSurface);
		cairo_rectangle( bcr, 0,0, w,h);
		cairo_scale( bcr, scale, scale );
		gdk_cairo_set_source_pixbuf(bcr,bandImg, 0,0 );
		cairo_fill(bcr);
		g_object_unref(G_OBJECT(bandImg));
	    }
	} else
	    band_loaded = true;

	switch ( bandPos ) {
	  case band_bottom:
	    if ( bandSurface!=NULL ) {
		cairo_save(dv->cr);
		cairo_translate( dv->cr, (dv->width-cairo_image_surface_get_width(bandSurface))/2,dv->height-cairo_image_surface_get_height(bandSurface) );
		cairo_rectangle( dv->cr, 0,0,
			cairo_image_surface_get_width(bandSurface),cairo_image_surface_get_height(bandSurface));
		cairo_set_source_surface(dv->cr,bandSurface, 0,0 );
		cairo_fill(dv->cr);
		cairo_restore(dv->cr);
	    } else {
		pango_layout_set_text(dv->layout,"Band",-1);
		pango_layout_get_pixel_extents(dv->layout,NULL,&r);
		cairo_move_to(dv->cr,(dv->width-r.width)/2,dv->height-r.height-dv->height/200);
		pango_cairo_show_layout(dv->cr,dv->layout);
	    }
	  break;

	  case band_left:
	  default:
	    if ( bandSurface!=NULL ) {
		cairo_rectangle( dv->cr, 0,(dv->height-cairo_image_surface_get_height(bandSurface))/2,
			cairo_image_surface_get_width(bandSurface),cairo_image_surface_get_height(bandSurface));
		cairo_set_source_surface(dv->cr,bandSurface, 0,(dv->height-cairo_image_surface_get_height(bandSurface))/2 );
		cairo_fill(dv->cr);
	    } else {
		pango_layout_set_text(dv->layout,"B\na\nn\nd",-1);
		pango_layout_get_pixel_extents(dv->layout,NULL,&r);
		cairo_move_to(dv->cr,dv->width/200,(dv->height-r.height)/2);
		pango_cairo_show_layout(dv->cr,dv->layout);
	    }
	  break;

	  case band_top:
	    pango_layout_set_text(dv->layout,"Band",-1);
	    pango_layout_get_pixel_extents(dv->layout,NULL,&r);
	    cairo_move_to(dv->cr,(dv->width-r.width)/2,dv->height/200);
	    pango_cairo_show_layout(dv->cr,dv->layout);
	  break;

	  case band_right:
	    pango_layout_set_text(dv->layout,"B\na\nn\nd",-1);
	    pango_layout_get_pixel_extents(dv->layout,NULL,&r);
	    cairo_move_to(dv->cr,dv->width-dv->width/200-r.width,(dv->height-r.height)/2);
	    pango_cairo_show_layout(dv->cr,dv->layout);
	  break;
	}
    }
    cairo_new_path(dv->cr);

    if ( dv->dance->text_color==-1 )
	cairo_set_source_rgb(dv->cr,0,0,0);
    else
	cairo_set_source_rgb(dv->cr,(dv->dance->text_color>>16)/256.0,((dv->dance->text_color>>8)&0xff)/256.0,(dv->dance->text_color&0xff)/256.0);
    if ( !dv->titling && dv->show_frameTitle ) {
	pango_layout_get_pixel_extents(dv->frametitle_layout,NULL,&r);
	my_pango_cairo_show_stroked_layout(dv,dv->frametitle_layout,
		(dv->width-r.width)/2,r.height, 1.0);
	if ( dv->dance->show_figure_titles && dv->dance->choreo_pos <= dv->dance->choreo_cnt && dv->dance->choreo_pos>0 ) {
	    Choreography *c = &dv->dance->sequence[dv->dance->choreo_pos-1];
	    pango_layout_set_text(dv->framefiguretitle_layout, c->figure->name,-1);
	    pango_layout_get_pixel_extents(dv->framefiguretitle_layout,NULL,&r2);
	    my_pango_cairo_show_stroked_layout(dv,dv->framefiguretitle_layout,
		    (dv->width-r2.width)/2,r.height+r2.height*1.3, 1.0);
	}
    }
    if ( dv->show_frame && frame_no!=-1 ) {
	char buffer[40];
	sprintf(buffer, "%d %.2f", frame_no, frame_no/(double) dv->frames_per_bar );
	pango_layout_set_text(dv->layout,buffer,-1);
	cairo_move_to(dv->cr,4,dv->height-54);
	pango_cairo_show_layout(dv->cr,dv->layout);
    }
    if ( dv->show_bar && frame_no!=-1 && !dv->titling ) {
	char buffer[80], *bpt;
	if ( dv->dance->current!=NULL && dv->dance->current->musicName[0]!='\0' &&
		strcmp(dv->dance->current->musicName,dv->currentMusicName)!=0 ) {
	    strcpy(dv->currentMusicName,dv->dance->current->musicName);
	    dv->currentMusicFrameStart = frame_no;
	    dv->currentMusicCallStart = dv->dance->current;
	}
	if ( strchr(dv->currentMusicName,'.')!=NULL ) {
	    strcpy(buffer,dv->currentMusicName);
	    bpt = buffer+strlen(buffer);
	} else {
	    romanNumeral(buffer,sizeof(buffer), dv->dance->current_minorset+1, false);
	    bpt = buffer+strlen(buffer);
	    if ( dv->currentMusicName[0]!='\0' ) {
		*bpt++ = '.';
		strcpy(bpt,dv->currentMusicName);
		bpt = bpt+strlen(bpt);
	    }
	}
	*bpt++ = ':';
	sprintf( bpt, "%.1f", 1+danceActualBarPos(dv,frame_no-dv->currentMusicFrameStart) );
	pango_layout_set_text(dv->music_layout,buffer,-1);
	pango_layout_get_pixel_extents(dv->music_layout,NULL,&r2);
	my_pango_cairo_show_stroked_layout(dv,dv->music_layout,
		4,dv->height-r2.height-5, 1.0);
    }

    if ( dv->dance->current!=NULL ) {
	int x,y;
	/*CallList *cname = dv->dance->current;*/
	PangoRectangle r;
	pango_layout_set_text(dv->call_layout,dv->dance->current->callName,-1);
	pango_layout_get_pixel_extents(dv->call_layout,NULL,&r);
	if ( dv->place_calls_underneath ) {
	    if ( bandSurface==NULL || dv->dance->bandPos!=band_bottom ) {
		x = (dv->width-r.width)/2; y = dv->height-r.height-dv->height/20 ;
	    } else {
		x = (dv->width-r.width)/2; y = dv->height-r.height-cairo_image_surface_get_height(bandSurface);
	    }
	} else {
	    if ( r.height+dv->yorigin+dv->height/100<dv->height ) {
		x = dv->height*60/1080; y = dv->yorigin+dv->height/100;
	    } else {
		x = dv->height*48/1080; y = dv->height/30;
	    }
	}
	my_pango_cairo_show_stroked_layout(dv,dv->call_layout,
		x,y, 1.0);
	if ( dv->dance->current->musicName[0]!='\0' ) {
	    PangoRectangle r2;
	    char buf[100];
	    
	    pango_layout_set_text(dv->music_layout,partAndMusic(buf,sizeof(buf),dv->dance->current),-1);
	    pango_layout_get_pixel_extents(dv->music_layout,NULL,&r2);
	    if ( dv->place_calls_underneath ) {
		my_pango_cairo_show_stroked_layout(dv,dv->music_layout,
			(dv->width-r.width)/2-r2.width-r2.height/2,dv->height-r.height-dv->height/20 + (dv->height*32/1080), 1.0);
	    }
	}
    }
    cairo_new_path(dv->cr);

    cairo_save(dv->cr);
    if ( dv->womensOrientation )
	danceOrientForWomen(dv);

    if ( dv->show_grid )
	danceDrawGrid(dv);
    if ( dv->show_path ) {
	cairo_set_line_width(dv->cr,1.0 );
	for ( i=0; i<dv->dance->people; ++i )
	    danceStrokeSplinePath(dv,&dv->dance->dancers[i], frame_no*1000/dv->frames_per_bar );
    }

    w = 2*round(dv->dance->units/200*dv->scale);
    if ( w==0 ) w=2;
    cairo_set_line_width(dv->cr,w );

    if ( dv->dance->style != ds_longsword ) for ( i=0; i<dv->dance->people; ++i ) {
	Person *dancer = &dv->dance->dancers[i];
	if ( dancer->now_hand_right!=0 && (dancer->now_hand_right&HAND_INDIVIDUAL_MASK)>=i )
	    danceDrawClasp(dv,(i|HAND_RIGHT_MASK),dancer->now_hand_right);
	if ( dancer->now_hand_left!=0 && (dancer->now_hand_left&HAND_INDIVIDUAL_MASK)>=i )
	    danceDrawClasp(dv,(i|HAND_LEFT_MASK),dancer->now_hand_left);
    }
    for ( i=0; i<dv->dance->people; ++i )
	danceDrawDancer(dv,&dv->dance->dancers[i]);
    cairo_set_line_width(dv->cr,w );
    if ( dv->dance->style != ds_longsword ) {
	for ( i=0; i<dv->dance->people; ++i ) {
	    Person *dancer = &dv->dance->dancers[i];
	    if ( ((dancer->now_hand_right&HAND_ABOVE_MASK)!=0 && (dancer->now_hand_right&HAND_INDIVIDUAL_MASK)>=i ) ||
		    (dancer->now_hand_right!=0 && dv->debugClasp ))
		danceDrawClasp(dv,(i|HAND_RIGHT_MASK),dancer->now_hand_right);
	    if ( ((dancer->now_hand_left&HAND_ABOVE_MASK)!=0 && (dancer->now_hand_left&HAND_INDIVIDUAL_MASK)>=i ) ||
		    (dancer->now_hand_left!=0 && dv->debugClasp ))
		danceDrawClasp(dv,(i|HAND_LEFT_MASK),dancer->now_hand_left);
	}
    } else {
	cairo_set_line_width(dv->cr,w );
	cairo_set_source_rgb(dv->cr,0.9,.7,0);
	for ( i=0; i<dv->dance->people; ++i ) {
	    Person *dancer = &dv->dance->dancers[i];
	    /*danceDrawLSRightHand(dv,dancer);*/
	    danceDrawLSLeftHand(dv,dancer);
	}
	for ( i=0; i<dv->dance->people; ++i ) {
	    danceDrawSword(dv,&dv->dance->dancers[i]);
	}
    }
    cairo_restore(dv->cr);
}

static void danceScottishHonour(DanceView *dv) {
    CallList *cname = dv->dance->current;
    static CallList bows = { "Bows and curtsies", NULL, NULL, { '\0' }, 2000, 2000, 2000, 0, NULL, -1 };
    static CallList honour = { "Honour Partner", NULL, NULL, { '\0' }, 2000, 2000, 2000, 0, NULL, -1 };
    static CallList honor = { "Honor Partner", NULL, NULL, { '\0' }, 2000, 2000, 2000, 0, NULL, -1 };
    int i;
    int frames = dv->active->end_frame - dv->active->start_frame;
    int halfway = frames/2;

    if ( dv->dance->style==ds_square || dv->dance->style==ds_contra )
	dv->dance->current = &honor;
    else if ( dv->dance->style==ds_scottish )
	dv->dance->current = &bows;
    else
	dv->dance->current = &honour;
    for ( i=0; i<frames; ++i ) {
	if ( i<halfway )
	    dv->dance->honour = (i+1) / (float) halfway;
	else
	    dv->dance->honour = (frames - i) / (float) (frames-halfway);
	danceRender(dv,0);
	++dv->title_frame_offset;
	danceAVAddVideoFrame(dv,0);
    }
    dv->dance->honour = 0;
    dv->dance->current = cname;
    dv->active = dv->active->next;
}

static void danceScottishImproper(DanceView *dv) {
    Dance *dance = dv->dance;
    CallList *cname = dance->current;
    static CallList improper = { "Improper crossover", NULL, NULL, { '\0' }, 2000, 2000, 2000, 0, NULL, -1 };
    static CallList becket = { "Becket crossover", NULL, NULL, { '\0' }, 2000, 2000, 2000, 0, NULL, -1 };
    int i,j;
    int frames = dv->active->end_frame - dv->active->start_frame;
    int halfway = frames/2;
    int threequarters = 3*frames/4;
    int onequarter = frames-threequarters;

    dance->current = &improper;
    if ( dance->becket_style!=bs_none )
	dance->current = &becket;
    for ( i=0; i<frames; ++i ) {
	if ( dance->becket_style==bs_ccw ) {
	    Person *p;
	    assert(dance->formation=sp_scottishDupleMinor);
	    int yrange = dance->dancers[1].figures.moves[0].endPoint.y -
		    dance->dancers[2].figures.moves[0].endPoint.y;
	    int xrange = dance->dancers[2].figures.moves[0].endPoint.x -
		    dance->dancers[1].figures.moves[0].endPoint.x;
	    if ( yrange==0 ) yrange = 256;
	    if ( xrange==0 ) xrange = 256;
	    /* M1 is in the right place */
	    p = &dance->dancers[1];	/* L1 goes to M2's place */
	    p->now.x = p->figures.moves[0].endPoint.x +
		xrange*(i+1)/frames;
	    p->now.y = p->figures.moves[0].endPoint.y -
		yrange*(i+1)/frames;
	    p->now_orientation= -180* (frames-i-1)/(double) frames;

	    p = &dance->dancers[2];	/* M2 goes to L2's place */
	    p->now.x = p->figures.moves[0].endPoint.x;
	    p->now.y = p->figures.moves[0].endPoint.y +
		yrange*(i+1)/frames;
	    p->now_orientation= 180* (i+1)/(double) frames;

	    p = &dance->dancers[3];	/* L2 goes to L1's place */
	    p->now.x = p->figures.moves[0].endPoint.x -
		xrange*(i+1)/frames;
	    p->now.y = p->figures.moves[0].endPoint.y;
	} else if ( dance->becket_style==bs_cw ) {
	    /* L1 is in the right place */
	    fprintf( stderr, "Clockwise becket not supported in Scottish yet because no dances need it.\n" );
	    abort();
	} else {
	for ( j=0; j<dance->people;  ++j ) {
	    Person *p = &dance->dancers[j];
	    if ( p->effective_role <= r_30s && (dance->improper_mask&(1<<(int) p->effective_role))) {
		if ( p->sex==s_male ) {
		    if ( i<=halfway ) {
			p->now.x = p->figures.moves[0].endPoint.x -
			    64*i/halfway;
			p->now.y = p->figures.moves[0].endPoint.y +
			    128*i/halfway;
		    } else {
			p->now.x = p->figures.moves[0].endPoint.x -
			    64*(frames-i)/(frames-halfway);
			p->now.y = p->figures.moves[0].endPoint.y +
			    128*i/(frames-halfway);
			if ( i>=threequarters )
			    p->now_orientation= -180* (i-threequarters)/onequarter;
		    }
		} else {
		    if ( i<=dv->frames_per_sec ) {
			p->now.x = p->figures.moves[0].endPoint.x +
			    64*i/halfway;
			p->now.y = p->figures.moves[0].endPoint.y -
			    128*i/halfway;
		    } else {
			p->now.x = p->figures.moves[0].endPoint.x +
			    64*(frames-i)/(frames-halfway);
			p->now.y = p->figures.moves[0].endPoint.y -
			    128*i/(frames-halfway);
			if ( i>=threequarters )
			    p->now_orientation= 180 -180* (i-threequarters)/onequarter;
		    }
		}
	    }
	}}
	danceRender(dv,0);
	++dv->title_frame_offset;
	danceAVAddVideoFrame(dv,0);
	if ( dv->title_frame_offset==1 ) {	/* We seem to lose the first frame???? */
	    ++dv->title_frame_offset;
	    danceAVAddVideoFrame(dv,0);
	}
    }

    if ( dance->becket_style==bs_ccw ) {
	for ( j=1; j<4; ++j ) {
	    Person *p = &dance->dancers[j];
	    p->orientation_start_of_current = p->now_orientation;
	    if ( p->figures.move_cnt>1 &&
		    (p->figures.moves[1].cmd==c_lineto || p->figures.moves[1].cmd==c_curveto ))
		splineMake(&p->figures.moves[1],&p->now);
	}
    } else
    for ( j=0; j<dance->people;  ++j ) {
	Person *p = &dance->dancers[j];
	if ( p->effective_role <= r_30s && (dance->improper_mask&(1<<(int) p->effective_role))) {
	    p->now.x = p->figures.moves[0].endPoint.x;
	    if ( p->sex==s_male ) {
		p->now.y = 320;
		p->now_orientation=180;
	    } else {
		p->now.y = 64;
		p->now_orientation=0;
	    }
	    p->orientation_start_of_current = p->now_orientation;
	    if ( p->figures.move_cnt>1 &&
		    (p->figures.moves[1].cmd==c_lineto || p->figures.moves[1].cmd==c_curveto ))
		splineMake(&p->figures.moves[1],&p->now);
	}
    }

    dv->dance->current = cname;
    dv->active = dv->active->next;
}

char *quoteCvt(char *name, int ismarkup) {
    char *pt1, *pt2, *ret;
    int len;

    for ( pt1=name, len=0; *pt1; ++pt1 )
	if ( *pt1=='\'' )
	    len += 3;
	else if ( *pt1=='&' && ismarkup )
	    len += 5;
	else
	    ++len;
    ret = malloc(len+1);
    for ( pt1=name, pt2=ret; *pt1; ++pt1 )
	if ( *pt1=='\'' ) {
	    *pt2++ = '\xE2';
	    *pt2++ = '\x80';
	    *pt2++ = '\x99';
	} else if ( *pt1=='&' && ismarkup ) {
	    *pt2++ = '&';
	    *pt2++ = 'a';
	    *pt2++ = 'm';
	    *pt2++ = 'p';
	    *pt2++ = ';';
	} else
	    *pt2++ = *pt1;
    *pt2 = '\0';
return( ret );
}

static void danceDrawTitle(DanceView *dv, int name_size, int author_size, char *authorText,double alpha) {
    PangoRectangle r;
    char *temp;
    int style_size;
    int text_color = dv->dance->text_color==-1 ? 0 : dv->dance->text_color;
    int use_long_s;

    cairo_set_source_rgba(dv->cr,(text_color>>16)/256.0,((text_color>>8)&0xff)/256.0,(text_color&0xff)/256.0,alpha);
    cairo_save(dv->cr);
    

    pango_font_description_set_size(dv->title_font,name_size);
    pango_layout_set_font_description(dv->title_layout,dv->title_font);
    temp = quoteCvt(dv->dance->name,true);
    pango_layout_set_markup(dv->title_layout,temp,-1);
    pango_layout_set_alignment(dv->title_layout,PANGO_ALIGN_CENTER);
    free( temp );
    pango_layout_get_pixel_extents(dv->title_layout,NULL,&r);
    my_pango_cairo_show_stroked_layout(dv,dv->title_layout,
	    (dv->width-r.width)/2,dv->height/10, alpha);

    pango_layout_set_alignment(dv->title_layout,PANGO_ALIGN_LEFT);
    pango_layout_set_attributes(dv->title_layout,NULL);

/* The times (London) stopped using the long s on 10 Sept 1803 */
/* US Congress stopped using it on 1 Jan 1804 */
    if ( dv->dance->use_long_s!=2 )
	use_long_s = dv->dance->use_long_s;
    else if (( dv->dance->year_written<1804 && dv->dance->year_written>0 ) ||
	     (dv->dance->year_written>-1804 && dv->dance->year_written<0 ))
	use_long_s = 1;
    else
	use_long_s = 0;
    if ( dv->dance->style==ds_english && use_long_s )
	pango_layout_set_text(dv->title_layout,"Engliſh Country Dance",-1);
    else if ( dv->dance->style==ds_contredanse && use_long_s )
	pango_layout_set_text(dv->title_layout,"Contredanſe",-1);
#if 0		/* Not with the current uncial font */
    else if ( dv->dance->style==ds_scottish && ((dv->dance->year_written<1804 && dv->dance->year_written>0 ) ||
					    (dv->dance->year_written>-1804 && dv->dance->year_written<0 )))
	pango_layout_set_text(dv->title_layout,"Scottiſh Country Dance",-1);
#endif
    else
	pango_layout_set_text(dv->title_layout,styleNames[dv->dance->style],-1);

    style_size = dv->height*145*PANGO_SCALE/1080;
    pango_font_description_set_size(dv->title_font,style_size);
    pango_layout_set_font_description(dv->title_layout,dv->title_font);
    pango_layout_get_pixel_extents(dv->title_layout,NULL,&r);
    if ( r.width > dv->width ) {
	long size = 140*PANGO_SCALE;
	size *= dv->height*dv->width;
	size /= (r.width*1080);
	style_size = size;
	pango_font_description_set_size(dv->title_font,style_size);
	pango_layout_set_font_description(dv->title_layout,dv->title_font);
    }
    pango_layout_get_pixel_extents(dv->title_layout,NULL,&r);
    my_pango_cairo_show_stroked_layout(dv,dv->title_layout,
	    (dv->width-r.width)/2,dv->height/2, alpha);

    if ( dv->dance->style == ds_scottish ) {
	char buffer[80];
	char *precis = danceScottishPrecis(buffer,sizeof(buffer),dv->dance);
	int ypos = dv->height/2 + r.height*.8;		/* I just want the ascent here, pango doesn't give me that */

	if ( precis!=NULL ) {
	    pango_font_description_set_size(dv->title_font,dv->height*30*PANGO_SCALE/1080);
	    pango_layout_set_font_description(dv->title_layout,dv->title_font);
	    pango_layout_set_text(dv->title_layout,precis,-1);
	    pango_layout_get_pixel_extents(dv->title_layout,NULL,&r);
	    my_pango_cairo_show_stroked_layout(dv,dv->title_layout,
		    dv->width-r.width-10,ypos, alpha);
	}
    }

    if ( authorText!=NULL ) {
	pango_font_description_set_size(dv->title_font,author_size);
	pango_layout_set_font_description(dv->title_layout,dv->title_font);
	pango_layout_set_text(dv->title_layout,authorText,-1);
	pango_layout_get_pixel_extents(dv->title_layout,NULL,&r);
	my_pango_cairo_show_stroked_layout(dv,dv->title_layout,
		(dv->width-r.width)/2,7*dv->height/10, alpha);
    }

    if ( dv->dance->animator!=NULL ) {
	char buffer[400];
	/* fecit=he made. facit=he makes */
	snprintf(buffer,sizeof(buffer),"%s fecit", dv->dance->animator );
	pango_font_description_set_size(dv->title_font,dv->height*11*PANGO_SCALE/1080);
	pango_layout_set_font_description(dv->title_layout,dv->title_font);
	pango_layout_set_text(dv->title_layout,buffer,-1);
	pango_layout_get_pixel_extents(dv->title_layout,NULL,&r);
	my_pango_cairo_show_stroked_layout(dv,dv->title_layout,
		dv->width-r.width-2,dv->height-r.height-2, alpha);
	if ( dv->dance->author!=NULL && strcasecmp(dv->dance->author,"George Williams")==0 ) {
	    GError *error=NULL;
	    GdkPixbuf *ccby = gdk_pixbuf_new_from_file("Images/CC-BY-88x31.png",&error);
	    if ( ccby!=NULL ) {
		gdk_cairo_set_source_pixbuf(dv->cr,ccby, dv->width-r.width-2-4-gdk_pixbuf_get_width(ccby),dv->height-gdk_pixbuf_get_height(ccby)-2 );
		cairo_rectangle(dv->cr,dv->width-r.width-2-4-gdk_pixbuf_get_width(ccby),dv->height-gdk_pixbuf_get_height(ccby)-2,
			gdk_pixbuf_get_width(ccby),gdk_pixbuf_get_height(ccby));
		cairo_fill(dv->cr);
		g_object_unref(G_OBJECT(ccby));
	    }
	}
    }
    cairo_restore(dv->cr);
}

void danceMinorsetSlowdown(DanceView *dv) {
    if ( dv->dance->counts_per_bar==4 && danceHasTag(dv->dance,"strathspey"))
	dv->frames_per_bar = dv->original_frames_per_bar * FIRST_MINORSET_STRATHSPEY_SLOWDOWN;
    else
	dv->frames_per_bar = dv->original_frames_per_bar * FIRST_MINORSET_SLOWDOWN;	/* Slow down the first minor set */
}

static void danceTitle(DanceView *dv,int minorSetCallsUnderneath) {
    CallList *cname = dv->dance->current;
    PangoRectangle r;
    char authorBuf[400];
    char buffer[400];
    int i;
    int name_size, author_size;
    char *authorText = NULL;
    int title_frames;
    char *temp;

    /* draw the first frame of the dance (without the call name, if any) */
    dv->dance->current = NULL;
    dv->titling = true;
    dv->title_fadeout_frac = 0;
    danceRender(dv,0);

    /* fade it out a bit by drawing the background on top of it */
    danceSetBackgroundSource(dv,0);
    cairo_paint_with_alpha(dv->cr,.5);

    name_size = dv->height*200*PANGO_SCALE/1080;
    pango_font_description_set_size(dv->title_font,name_size);
    pango_layout_set_font_description(dv->title_layout,dv->title_font);
    temp = quoteCvt(dv->dance->name,true);
    pango_layout_set_markup(dv->title_layout,temp,-1);
    free(temp);
    pango_layout_get_pixel_extents(dv->title_layout,NULL,&r);
    if ( r.width > dv->width ) {
	long size = 195*PANGO_SCALE;
	size *= dv->height*dv->width;
	size /= (r.width*1080);
	name_size = size;
    }
    pango_layout_set_attributes(dv->title_layout,NULL);

    author_size = dv->height*120*PANGO_SCALE/1080;
    if ( dv->dance->year_written!=0 || (dv->dance->author!=NULL && *dv->dance->author!='\0') ) {
	pango_font_description_set_size(dv->title_font,author_size);
	pango_layout_set_font_description(dv->title_layout,dv->title_font);
	authorText = buffer;
	if ( dv->dance->author!=NULL && *dv->dance->author!='\0' ) {
	    if ( dv->dance->style==ds_contra && dv->dance->interpreter!=NULL && strcmp(dv->dance->author,"Traditional")!=0 && strcmp(dv->dance->author,"Elias Howe")!=0 )
		snprintf( authorBuf, sizeof(authorBuf), "%s & %s", dv->dance->author, dv->dance->interpreter );
	    else
		snprintf( authorBuf, sizeof(authorBuf), "%s", dv->dance->author );
	} else
	    authorBuf[0] = '\0';
	if ( dv->dance->year_written>0 && authorBuf[0]!='\0' ) {
	    sprintf( buffer, "%s, %d", authorBuf, dv->dance->year_written );
	    pango_layout_set_text(dv->title_layout,buffer,-1);
	} else if ( dv->dance->year_written<0 && authorBuf[0]!='\0' ) {
	    sprintf( buffer, "%s, ~%d", authorBuf, -dv->dance->year_written );
	    pango_layout_set_text(dv->title_layout,buffer,-1);
	} else if ( dv->dance->year_written>0 ) {
	    sprintf( buffer, "%d", dv->dance->year_written );
	    pango_layout_set_text(dv->title_layout,buffer,-1);
	} else if ( dv->dance->year_written<0 ) {
	    sprintf( buffer, "~%d", -dv->dance->year_written );
	    pango_layout_set_text(dv->title_layout,buffer,-1);
	} else {
	    pango_layout_set_text(dv->title_layout,authorBuf,-1);
	    authorText = authorBuf;
	}
	authorText = quoteCvt(authorText,false);
	pango_layout_get_pixel_extents(dv->title_layout,NULL,&r);
	if ( r.width>dv->width ) {
	    long size = 118*PANGO_SCALE;
	    size *= dv->height*dv->width;
	    size /= (r.width*1080);
	    author_size = size;
	}
    }
    dv->titling = true;
    danceDrawTitle(dv,name_size,author_size,authorText,1.0);
    title_frames = dv->active->end_frame - dv->active->start_frame;
    for ( i=0; i<title_frames; ++i ) {
	dv->title_frame_offset=i;
	danceAVAddVideoFrame(dv,0);
    }
    dv->active = dv->active->next;

/* Code to show the CD Image */
    if ( dv->active->timeline_type==tl_transition_title_album ) {
	GError *error=NULL;
	GdkPixbuf *album = NULL;
	cairo_surface_t *temp;	/* Contains a temporary copy of the title page image */
	cairo_t *tempCr;
	int frame_bytes;
	int frames;
	PangoLayout *layout;
	PangoRectangle r;

	if ( dv->dance->music.cdNotes!=NULL ) {
	    PangoFontDescription *font = pango_font_description_new();

	    layout = pango_cairo_create_layout(dv->cr);
	    pango_font_description_set_family(font,"Helvetica,sans");
	    pango_font_description_set_size(font,dv->height*36*PANGO_SCALE/1080);
	    pango_layout_set_font_description(layout,font);
	    pango_layout_set_alignment(layout,PANGO_ALIGN_CENTER);
	    pango_font_description_free(font);
	} else
	    layout = NULL;

	temp = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
		cairo_image_surface_get_width(dv->frame),
		cairo_image_surface_get_height(dv->frame));
	/* Set this temp surface to contain the title images */
	tempCr = cairo_create(temp);
	cairo_set_source_surface(tempCr,dv->frame,0,0);
	cairo_rectangle(tempCr, 0,0, dv->width, dv->height );
	cairo_fill(tempCr);
	cairo_destroy(tempCr);

	frame_bytes = cairo_image_surface_get_stride(temp)*cairo_image_surface_get_height(temp);
	memcpy(cairo_image_surface_get_data(temp),cairo_image_surface_get_data(dv->frame),
		frame_bytes);

	if ( dv->dance->music.cdImage!=NULL )
	    album = gdk_pixbuf_new_from_file(dv->dance->music.cdImage,&error);
	if ( album==NULL && dv->dance->music.cdNotes==NULL ) {
	    /* Could not open the album? Just display the title frame for as long as we need */
	    if ( dv->dance->music.cdImage!=NULL )
		fprintf( stderr, "Failed to open %s (album image)\n", dv->dance->music.cdImage);
	    frames = dv->active->end_frame - dv->active->start_frame;
	    for ( i=0; i<frames; ++i ) {
		++dv->title_frame_offset;
		danceAVAddVideoFrame(dv,0);
	    }
	    dv->active = dv->active->next;
	    if ( dv->active->timeline_type==tl_album ) {
		frames = dv->active->end_frame - dv->active->start_frame;
		for ( i=0; i<frames; ++i ) {
		    ++dv->title_frame_offset;
		    danceAVAddVideoFrame(dv,0);
		}
		dv->active = dv->active->next;
	    }
	    if ( dv->active->timeline_type==tl_transition_album_title ) {
		frames = dv->active->end_frame - dv->active->start_frame;
		for ( i=0; i<frames; ++i ) {
		    ++dv->title_frame_offset;
		    danceAVAddVideoFrame(dv,0);
		}
		dv->active = dv->active->next;
	    }
	} else if ( album==NULL ) {
	    /* Just display the cdNotes (and permission) in a black rectangle */
	    int width = dv->width*2/3.0;
	    int height;
	    int x,y, fw,fh;
	    int len;
	    char *space;

	    len = strlen(dv->dance->music.cdNotes);
	    if ( dv->dance->music.permission!=NULL ) {
		snprintf( buffer,sizeof(buffer), "<small><small><small>Music used with permission of\n</small>%s</small></small>", dv->dance->music.permission );
		len += strlen(buffer)+1;
	    }
	    space = malloc(len+4);
	    strcpy(space,dv->dance->music.cdNotes);
	    if ( dv->dance->music.permission!=NULL ) {
		strcat(space,"\n");
		strcat(space,buffer);
	    }	    

	    pango_layout_set_width(layout,width*PANGO_SCALE);
	    pango_layout_set_markup(layout,space,-1);
	    free(space);
	    pango_layout_get_pixel_extents(layout,NULL,&r);

	    height = r.height+3;
	    frames = dv->active->end_frame - dv->active->start_frame;
	    for ( i=0; i<frames; ++i ) {
		if ( frames==1 ) {
		    fw = width;
		    fh = height;
		} else {
		    fw = width * (i+1) / (frames-1);
		    fh = height * (i+1) / (frames-1);
		}
		x = (dv->width-fw)/2;
		y = (dv->height-fh)/2;
		cairo_rectangle(dv->cr, x,y, fw,fh );
		cairo_set_source_rgb(dv->cr,0,0,0);
		cairo_fill(dv->cr);
		++dv->title_frame_offset;
		danceAVAddVideoFrame(dv,0);
	    }
	    dv->active = dv->active->next;
	    if ( dv->active->timeline_type==tl_album ) {
		cairo_move_to(dv->cr,x,y+1);
		cairo_set_source_rgb(dv->cr,1,1,1);
		pango_cairo_show_layout(dv->cr,layout);
		frames = dv->active->end_frame - dv->active->start_frame;
		for ( i=0; i<frames; ++i ) {
		    ++dv->title_frame_offset;
		    danceAVAddVideoFrame(dv,0);
		}
		dv->active = dv->active->next;
	    }
	    g_object_unref(G_OBJECT(layout));
	    if ( dv->active->timeline_type==tl_transition_album_title ) {
		frames = dv->active->end_frame - dv->active->start_frame;
		for ( i=0; i<frames; ++i ) {
		    ++dv->title_frame_offset;
		    cairo_set_source_surface(dv->cr,temp,0,0);
		    cairo_rectangle(dv->cr, 0,0, dv->width, dv->height );
		    cairo_fill(dv->cr);
		    fw = width * (frames-i-1) / frames;
		    fh = height * (frames-i-1) / frames;
		    x = (dv->width-fw)/2;
		    y = (dv->height-fh)/2;
		    cairo_rectangle(dv->cr, x,y, fw,fh );
		    cairo_set_source_rgb(dv->cr,0,0,0);
		    cairo_fill(dv->cr);
		    danceAVAddVideoFrame(dv,0);
		}
		dv->active = dv->active->next;
	    }
	} else {
	    double hscale, vscale;
	    double x,y;
	    /* album cover should not take up more than 2/3 of screen */
	    hscale = dv->width*2/3.0 / (double) gdk_pixbuf_get_width(album);
	    vscale = dv->height*2/3.0 / (double) gdk_pixbuf_get_width(album);
	    if ( vscale>hscale )
		vscale = hscale;
	    if ( vscale<1 ) {
		GdkPixbuf *temp_album = gdk_pixbuf_scale_simple(album,
			vscale*gdk_pixbuf_get_width(album),
			vscale*gdk_pixbuf_get_height(album),
			GDK_INTERP_BILINEAR);
		if ( temp_album!=NULL ) {
		    g_object_unref(G_OBJECT(album));
		    album = temp_album;
		}
	    }

	    frames = dv->active->end_frame - dv->active->start_frame;
	    for ( i=0; i<frames; ++i ) {
		++dv->title_frame_offset;
		cairo_set_source_surface(dv->cr,temp,0,0);
		cairo_rectangle(dv->cr, 0,0, dv->width, dv->height );
		cairo_fill(dv->cr);
		hscale = (i+1) / (double) frames;
		cairo_save(dv->cr);
		if ( frames>2 ) {
		    cairo_translate( dv->cr, (i/(frames-1.0))*dv->width/2,(i/(frames-1.0))*dv->height/2 );
		    cairo_rotate( dv->cr, 2*PI*i/(frames-1) );
		} else
		    cairo_translate( dv->cr, dv->width/2,dv->height/2 );
		x = -hscale*gdk_pixbuf_get_width(album)/2;
		y = -hscale*gdk_pixbuf_get_height(album)/2;
		cairo_translate( dv->cr, x,y );
		cairo_rectangle( dv->cr, 0,0,
			hscale*gdk_pixbuf_get_width(album),hscale*gdk_pixbuf_get_height(album));
		cairo_scale(dv->cr,hscale,hscale);
		gdk_cairo_set_source_pixbuf(dv->cr,album, 0,0 );
		cairo_fill(dv->cr);
		cairo_restore(dv->cr);
		danceAVAddVideoFrame(dv,0);
	    }
	    dv->active = dv->active->next;
	    if ( dv->active->timeline_type==tl_album ) {
		int awidth=0;
		int ax, ay;
		int lheight=0;
		if ( dv->dance->music.cdNotes!=NULL ) {
		    PangoRectangle r;

		    pango_layout_set_width(layout,gdk_pixbuf_get_width(album)*PANGO_SCALE);
		    pango_layout_set_text(layout,dv->dance->music.cdNotes,-1);
		    pango_layout_get_pixel_extents(dv->frametitle_layout,NULL,&r);

		    awidth = gdk_pixbuf_get_width(album);
		    lheight = r.height+3;
		    ax = (dv->width-awidth)/2;
		    ay = (dv->height+gdk_pixbuf_get_height(album))/2;
		    cairo_rectangle(dv->cr, ax,ay, awidth,lheight );
		    cairo_set_source_rgb(dv->cr,0,0,0);
		    cairo_fill(dv->cr);
		    cairo_move_to(dv->cr,ax,ay+1);
		    cairo_set_source_rgb(dv->cr,1,1,1);
		    pango_cairo_show_layout(dv->cr,layout);

		} else
		    layout=NULL;
		frames = dv->active->end_frame - dv->active->start_frame;
		for ( i=0; i<frames; ++i ) {
		    if ( dv->dance->music.permission!=NULL && layout!=NULL ) {
			if ( i<dv->frames_per_sec ) {
			    cairo_rectangle(dv->cr, ax,ay+lheight, awidth,lheight*1.2 );
			    cairo_set_source_rgba(dv->cr,0,0,0,1.5/dv->frames_per_sec);
			    cairo_fill(dv->cr);
			} else if ( i==dv->frames_per_sec ) {
			    cairo_rectangle(dv->cr, ax,ay+lheight, awidth,lheight*1.2 );
			    cairo_set_source_rgb(dv->cr,0,0,0);
			    cairo_fill(dv->cr);
			    snprintf( buffer,sizeof(buffer), "<small><small><small>Music used with permission of\n</small>%s</small></small>", dv->dance->music.permission );
			    pango_layout_set_markup(layout,buffer,-1);
			    cairo_move_to(dv->cr,ax,ay+lheight-1);
			    cairo_set_source_rgb(dv->cr,1,1,1);
			    pango_cairo_show_layout(dv->cr,layout);
			}
		    }
		    ++dv->title_frame_offset;
		    danceAVAddVideoFrame(dv,0);
		}
		dv->active = dv->active->next;
		if ( layout!=NULL )
		    g_object_unref(G_OBJECT(layout));
	    }
	    if ( dv->active->timeline_type==tl_transition_album_title ) {
		frames = dv->active->end_frame - dv->active->start_frame;
		for ( i=0; i<frames; ++i ) {
		    ++dv->title_frame_offset;
		    cairo_set_source_surface(dv->cr,temp,0,0);
		    cairo_rectangle(dv->cr, 0,0, dv->width, dv->height );
		    cairo_fill(dv->cr);

		    hscale = (frames-i-1) / (double) frames;
		    if ( hscale!=0 ) {
			x = (dv->width-hscale*gdk_pixbuf_get_width(album))/2;
			y = (dv->height-hscale*gdk_pixbuf_get_height(album))/2;
			cairo_save(dv->cr);
			cairo_translate( dv->cr, x,y );
			cairo_rectangle( dv->cr, 0,0,
				hscale*gdk_pixbuf_get_width(album),hscale*gdk_pixbuf_get_height(album));
			cairo_scale(dv->cr,hscale,hscale);
			gdk_cairo_set_source_pixbuf(dv->cr,album, 0,0 );
			cairo_fill(dv->cr);
			cairo_restore(dv->cr);
		    }

		    danceAVAddVideoFrame(dv,0);
		}
		dv->active = dv->active->next;
	    }

	    g_object_unref(G_OBJECT(album));
	}
	cairo_surface_destroy(temp);
    }
    
/* Code to show the transition to the minor set */
    if ( dv->active->timeline_type==tl_transition_title_set ||
	    dv->active->timeline_type==tl_transition_title_fadeout ) {
	int transition_frames = dv->active->end_frame - dv->active->start_frame;
	for ( i=0; i<transition_frames; ++i ) {
	    double frac = (i+1)/(double) (transition_frames);
	    dv->title_fadeout_frac = frac;
	    if ( dv->active->timeline_type==tl_transition_title_set ) {
		dv->scale   = (1-frac)*dv->full_scale   + frac*dv->ms_scale;
		dv->xorigin = round( (1-frac)*dv->full_xorigin + frac*dv->ms_xorigin );
		dv->yorigin = round( (1-frac)*dv->full_yorigin + frac*dv->ms_yorigin );
	    }
	    danceRender(dv,0);
	    danceSetBackgroundSource(dv,0);
	    cairo_paint_with_alpha(dv->cr,.5*(1.0-frac));

	    danceDrawTitle(dv,name_size,author_size,authorText,1.0-frac);
	    ++dv->title_frame_offset;
	    danceAVAddVideoFrame(dv,0);
	}
	dv->active = dv->active->next;
    }
    dv->titling = false;
    free(authorText); authorText=NULL;

    if ( dv->show_minorset_first ) {
	if ( dv->dance->formation!=sp_circularMonople && dv->dance->formation!=sp_trioCircularMonople ) {
	    if ( dv->dance->formation==sp_threeFaceThree )
		dv->dont_show_couples_above_me = 2;
	    else
		dv->dont_show_couples_above_me = danceMinorSetSize(dv->dance)/2 + dv->initial_out_at_top;
	}
	danceMinorsetSlowdown(dv);
	dv->place_calls_underneath = minorSetCallsUnderneath;
    } else
	minorSetCallsUnderneath = dv->place_calls_underneath;
    dv->dance->current = cname;
    if ( minorSetCallsUnderneath )
	pango_layout_set_width(dv->call_layout,dv->width*dv->usable_fraction*PANGO_SCALE);
    else
	pango_layout_set_width(dv->call_layout,(dv->xorigin+dv->minorSetBB.minx*dv->scale)*PANGO_SCALE-2*dv->height*48*PANGO_SCALE/1080);
}

static void danceContractToMinorSet(DanceView *dv,int minorSetCallsUnderneath) {
    CallList *cname = dv->dance->current;
    int i;
    int transition_frames = dv->active->end_frame - dv->active->start_frame;

    dv->dance->current = NULL;
    for ( i=0; i<transition_frames; ++i ) {
	double frac = (i+1)/(double) (dv->frames_per_sec-1);
	dv->scale   = (1-frac)*dv->full_scale   + frac*dv->ms_scale;
	dv->xorigin = round( (1-frac)*dv->full_xorigin + frac*dv->ms_xorigin );
	dv->yorigin = round( (1-frac)*dv->full_yorigin + frac*dv->ms_yorigin );
	danceRender(dv,0);
	++dv->title_frame_offset;
	danceAVAddVideoFrame(dv,0);
    }
    if ( dv->dance->formation==sp_threeFaceThree )
	dv->dont_show_couples_above_me = 2;
    else
	dv->dont_show_couples_above_me = danceMinorSetSize(dv->dance)/2;
    danceMinorsetSlowdown(dv);	/* Slow down the first minor set */
    dv->place_calls_underneath = minorSetCallsUnderneath;
    dv->dance->current = cname;
    if ( minorSetCallsUnderneath )
	pango_layout_set_width(dv->call_layout,dv->width*dv->usable_fraction*PANGO_SCALE);
    else
	pango_layout_set_width(dv->call_layout,dv->xorigin*PANGO_SCALE-2*dv->height*48*PANGO_SCALE/1080);
    dv->active = dv->active->next;
}

static void danceExpandToFullSet(DanceView *dv,int frame) {
    int i;
    int frames = dv->active->end_frame - dv->active->start_frame;

    dv->frames_per_bar = dv->original_frames_per_bar;		/* Speed up to normal */
    dv->dont_show_couples_above_me = 0;
    /* should be no call name, but if so do without */
    if ( /*dv->ms_scale!=dv->full_scale*/ true ) {
	CallList *cname = dv->dance->current;
	dv->dance->current = NULL;
	for ( i=0; i<frames; ++i ) {
	    double frac = (i+1)/(double) (frames);
	    dv->scale   = frac*dv->full_scale   + (1-frac)*dv->ms_scale;
	    dv->xorigin = round( frac*dv->full_xorigin + (1-frac)*dv->ms_xorigin );
	    dv->yorigin = round( frac*dv->full_yorigin + (1-frac)*dv->ms_yorigin );
	    danceRender(dv,frame);
	    ++dv->title_frame_offset;
	    danceAVAddVideoFrame(dv,frame);
	}
	dv->dance->current = cname;
    }

    dv->place_calls_underneath = dv->full_place_calls_underneath;
    if ( dv->full_place_calls_underneath )
	pango_layout_set_width(dv->call_layout,dv->width*dv->usable_fraction*PANGO_SCALE);
    else
	pango_layout_set_width(dv->call_layout,dv->xorigin*PANGO_SCALE-2*dv->height*48*PANGO_SCALE/1080);
    dv->active = dv->active->next ;
}

#if 0
static void fuf(void) {
    PangoFontMap *fm;
    PangoFontFamily **families;
    int i,n;

    fm = pango_cairo_font_map_get_default();
    pango_font_map_list_families(fm,&families,&n);
    for ( i=0; i<n; ++i )
	printf( "%s\n", pango_font_family_get_name(families[i]) );
exit(0);
}
#endif

static void danceLoadFaces(DanceView *dv) {
    Dance *d = dv->dance;
    int i, j;
    int w=-1, nw, nh;
    GError *error;

    if ( d->people < d->face_cnt )
	fprintf( stderr, "Warning: More faces than people. Ignoring extras.\n" );
    for ( i=0; i<d->face_cnt; ++i ) if ( d->faceImages[i].facename!=NULL ) {
	struct faceImage *fi = &d->faceImages[i];
	error = NULL;
	fi->image = gdk_pixbuf_new_from_file(fi->facename,&error);
	if ( fi->image==NULL ) {
	    if ( fi->person != -1 )
		fprintf( stderr, "Failed to load face %s for dancer %d from file\n", fi->facename, fi->person );
	    else
		fprintf( stderr, "Failed to load face %s for sex %c from file\n", fi->facename, sexChars[fi->sex] );
	} else {
	    nw = gdk_pixbuf_get_width( fi->image );
	    nh = gdk_pixbuf_get_height( fi->image );
	    if ( nw!=nh ) {
		fprintf( stderr, "Face %s for dancer %d is not square (ignoring it) (width=%d, height=%d)\n",
			fi->facename, fi->person, nw, nh );
		g_object_unref(G_OBJECT(fi->image ));
		fi->image = NULL;
	    } else if ( w==-1 )
		w = nw;
	    else if ( w!=nw ) {
		fprintf( stderr, "Face %s for dancer %d is not the same size as a previous face (%d!=%d)\n",
			fi->facename, i, nw, w );
		g_object_unref(G_OBJECT(fi->image ));
		fi->image = NULL;
	    }
	}
	if ( fi->person!=-1 && fi->person<d->people ) {
	    Person *p = &d->dancers[fi->person];
	    p->face = fi->image;
	    p->facescale = fi->facescale;
	    p->face_hide_normal = fi->hide_normal;
	} else {
	    for ( j=0; j<d->people; ++j ) if ( d->dancers[j].sex==fi->sex ) {
		Person *p = &d->dancers[j];
		p->face = fi->image;
		p->facescale = fi->facescale;
		p->face_hide_normal = fi->hide_normal;
	    }
	}
    }
    dv->face_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
	    w,w);
    dv->fcr = cairo_create(dv->face_surface);
    dv->face_size = w;
}

static void danceFlushFaces(DanceView *dv) {
    Dance *d = dv->dance;
    int i;

    for ( i=0; i<d->face_cnt; ++i ) if ( d->faceImages[i].image!=NULL ) {
	g_object_unref(G_OBJECT(d->faceImages[i].image ));
	d->faceImages[i].image = NULL;
    }
    for ( i=0; i<d->people; ++i )
	d->dancers[i].face = NULL;
    cairo_destroy(dv->fcr);
    cairo_surface_destroy(dv->face_surface);
    dv->face_surface = NULL;
}

static void danceAddStillFrames(DanceView *dv,int frame) {
    int extras = dv->active->end_frame - dv->active->start_frame;

    while ( extras > 0 ) {
	++dv->title_frame_offset;
	danceAVAddVideoFrame(dv,frame);
	--extras;
    }
    dv->active = dv->active->next;
}

static void danceVideoFigureScale(DanceView *dv, struct video_options *options) {
    int dw, dh;
    int vw, vh;
    float sx,sy;

    vw = round(dv->width *dv->usable_fraction);
    vh = round(dv->height*dv->usable_fraction);

    if ( options->callPos == cp_under )
	vh -= 2*dv->height*48/1080;		/* room for two lines of calls */
    else if ( options->callPos == cp_left )
	vw -= 10*dv->height*48/1080;;		/* room for 10 ems horizontal for the calls */

    vh -= dv->height*48/1080;			/* room for title at top */

    vh *= .95;					/* things that are bounded by height seem to walk on top of the calls, so give them some more room */

    dw = dv->bb.maxx - dv->bb.minx;
    dh = dv->bb.maxy - dv->bb.miny;
    sx = vw / (float) dw;
    sy = vh / (float) dh;
    if ( sx>sy )
	sx = sy;
    /*else*/
	/*dv->place_calls_underneath = true;*/
    if ( dv->dance->bandPos==band_bottom )
	sx *= .95;
    dv->scale = sx;
    dv->xorigin = (dv->width - (dv->bb.maxx-dv->bb.minx)*sx)/2 - dv->bb.minx*sx;
    dv->yorigin = (dv->height- (dv->bb.maxy-dv->bb.miny)*sx)/2 - dv->bb.miny*sx + dv->height/30;
}

static void danceVideoFigureCurrentBB(DanceView *dv,int frame) {
    Dance *dance = dv->dance;

    if ( dance->currentBB==NULL )
return;
    if ( !dance->currentBB->fixed ) {
	int off = (frame*MILLIBARS_MUL)/dv->frames_per_bar - dance->millibars_start_of_bbcurrent;

	if ( off<0 ) off = 0;
	if ( off>dance->currentBB->millibars ) off = dance->currentBB->millibars;

	if ( off==0 )
	    dv->bb = dance->bbStartOfCurrent;
	else if ( off==dance->currentBB->millibars )
	    dv->bb = dance->currentBB->bb;
	else {
	    double frac = off / (double) dance->currentBB->millibars;

	    dv->bb.minx = dance->bbStartOfCurrent.minx +
		    frac*(dance->currentBB->bb.minx - dance->bbStartOfCurrent.minx);
	    dv->bb.maxx = dance->bbStartOfCurrent.maxx +
		    frac*(dance->currentBB->bb.maxx - dance->bbStartOfCurrent.maxx);
	    dv->bb.miny = dance->bbStartOfCurrent.miny +
		    frac*(dance->currentBB->bb.miny - dance->bbStartOfCurrent.miny);
	    dv->bb.maxy = dance->bbStartOfCurrent.maxy +
		    frac*(dance->currentBB->bb.maxy - dance->bbStartOfCurrent.maxy);
	}
    }
}

static void dump_font_families() {
    PangoFontFamily **families;
    int n_families;
    int i;
    FILE *fontdump;

    pango_font_map_list_families(pango_cairo_font_map_get_default(),&families,&n_families);
    fontdump=fopen("fonts.dump","w");
    for ( i=0; i<n_families; ++i )
	fprintf( fontdump, "%s\n", pango_font_family_get_name(families[i]) );
    fclose(fontdump);
    g_free(families);
}

void danceMakeVideo(Dance *dance, struct video_options *options,
	char *filename, char *framedir) {
    DanceView *dv;
    int dw, dh;
    int vw, vh;
    float sx,sy;
    int i, f, cnt;
    char buffer[500];
    PangoFontDescription *font;
    float fontFactor;
    int minorSetCallsUnderneath;
    char *temp,*pt;
    enum dancecondition dc;
    PangoRectangle r;

    if ( dance->is_comparison ) {
	danceMakeComparison((Comparison *) dance,options,filename,framedir);
return;
    }

    dv = calloc(1,sizeof(DanceView));
    dv->back_col = -1;
    dv->fade_col = -1;
    dv->last_frame_cnt = -1;
    dv->dance = dance;
    dv->test = (options->flags&(1L<<df_test)) ? 1 : 0;

    if ( options->flags & (1L<<df_oldProgression) )
	dance->old_style_progression = true;
    else if ( options->flags & (1L<<df_noOldProgression) )
	dance->old_style_progression = false;

    if (( (dance->formation==sp_circularMonople || dance->formation==sp_trioCircularMonople ||
	    dance->formation==sp_rotatedTripleMinor ||
	    (dance->style==ds_appalachian && dance->formation==sp_dupleMinor)) &&
	    !(options->flags&(1<<df_noSicilianCircle)) ) ||
	    (options->flags&(1<<df_sicilianCircle)) ) {
	dance->sicilian = true;
	/* Sometimes this next line makes things look better (Or I used to think so) */
	/*  buf if there are stars or swings it looks bad */
	/*danceXScale(dance,1.5);*/
    }

    if ( (options->flags&(1<<df_noSicilianCircle)) )
	dance->sicilianScale = false;
    else if ( (options->flags&(1<<df_sicilianCircle)) )
	dance->sicilianScale = true;
    else
	dance->sicilianScale = dance->sicilian;

    if ( options->couples!=0 )
	dv->couples = options->couples;
    else if ( dance->desired_couple_cnt!=0 )
	dv->couples = dance->desired_couple_cnt;
    else if ( dance->formation==sp_tempest || dance->formation==sp_fourFaceFour ) {
	if ( dance->old_style_progression )
	    dv->couples = 8;
	else
	    dv->couples = 14;
    } else if ( dance->formation==sp_quadrupleMinorBecket )
	dv->couples = 10;
    else if ( dance->formation==sp_quadrupleMinor && dance->progression==4 )
	dv->couples = 8;
    else if ( dance->formation==sp_scottishDupleMinor )
	dv->couples = 4;
    else if ( dance->formation==sp_scottishTripleMinor )
	dv->couples = 4;	/* could also be 7, 10, 13, ... */
    else if ( dance->old_style_progression && (dance->formation==sp_dupleMinor || dance->formation==sp_threeFaceThree) )
	dv->couples = 5;
    else if ( dance->old_style_progression && dance->formation==sp_tripleMinor )
	dv->couples = 6;
    else if ( danceNeedsCoupleCount(dance) )
	dv->couples = 9;
    else
	dv->couples = dance->role_cnt/2;
    if ( options->couples==0 && dance->desired_couple_cnt==0 && dance->sicilian ) {
	if ( dance->old_style_progression && dance->formation==sp_dupleMinor )
	    dv->couples = 8;
	else if ( dance->formation==sp_fourFaceFour || dance->formation==sp_tempest )
	    dv->couples = 40;
	else
	    dv->couples = 24;
    }
    if ( options->couples==0 && dance->formation==sp_rotatedTripleMinor )
	dv->couples=10*3;
    dv->initial_out_at_top = options->out_at_top;
    /* Four face four dances must have an even number of couples */
    /*  and are better if the number of lines (couples/2) is odd */
    /* (except in sicilian cirles there must be an even number of lines) */
    if ( dance->formation==sp_fourFaceFour || dance->formation==sp_tempest || dance->formation==sp_quadrupleMinorBecket )
	dv->couples &= ~1;
    if ( (dance->formation==sp_squaredSet ||
		dance->formation==sp_facingCouples ||
		(options->flags&(1<<df_sicilianCircle)) ||
		danceIsCircle(dance)) &&
	    (options->flags&(1L<<df_svg)) ) {
	if ( options->width!=0 )
	    dv->width = options->width;
	else if ( options->height!=0 )
	    dv->width = dv->height;
	else
	    dv->width = 1280;
	if ( options->height==0 )
	    dv->height = dv->width;
	else
	    dv->height = options->height;
    } else {
	if ( options->width!=0 )
	    dv->width = options->width;
	else if ( options->height!=0 )
	    dv->width = (16*dv->height)/9;
	else
	    dv->width = 1280;
	if ( options->height==0 )
	    dv->height = (9*dv->width)/16;
	else
	    dv->height = options->height;
    }

    if ( options->music!=NULL )
	mdCopyContents(&dance->music,options->music);
    danceAVFigureFramesPerSecBar(dv, options);

    if ( options->usable_fraction==0 )
	dv->usable_fraction = .9;
    else
	dv->usable_fraction = options->usable_fraction;
    if ( options->flags&(1L<<df_svg) )
	dv->audio_sample_rate = 0;
    else if ( options->audio_sample_rate==0 )
	dv->audio_sample_rate = 44100;
    else if ( options->audio_sample_rate<0 )
	dv->audio_sample_rate = 0;
    else
	dv->audio_sample_rate = options->audio_sample_rate;
    dv->vbit_rate = options->vbit_rate;

    dv->filename = filename;
    dv->framedir = framedir;
    dv->show_title = true;
    dv->show_frameTitle = true;

    /* When we have a sequence, we don't want +test to only display the first */
    /*  figure of the sequence */
    if ( options->minor_set_count==1 && dance->choreo_cnt!=0 && (options->flags&(1L<<df_test)) )
	dv->minor_set_count = dance->choreo_cnt;
    else if ( options->minor_set_count!=0 )
	dv->minor_set_count = options->minor_set_count;
    else if ( dance->music.desired_repetitions!=0 )
	dv->minor_set_count = dance->music.desired_repetitions;
    else if ( dance->desired_repetitions!=0 )
	dv->minor_set_count = dance->desired_repetitions;
    else if ( dance->choreo_cnt!=0 )
	dv->minor_set_count = dance->choreo_cnt;
    else if ( dance->old_style_progression && dance->formation==sp_dupleMinor && dance->sicilian )
	dv->minor_set_count = 3*dv->couples/2 - 1;
    else if ( dance->old_style_progression && (dance->formation==sp_dupleMinor || dance->formation==sp_threeFaceThree) )
	dv->minor_set_count = 3*dv->couples - 3;
    /* In a triple minor set the 3s do not progress. So it takes twice as long*/
    /*  to move up the set as to move down it */
    else if ( dance->old_style_progression && dance->formation==sp_tripleMinor )
	dv->minor_set_count = 4*dv->couples - 4;
    else if ( dance->old_style_progression && (dance->formation==sp_tempest || dance->formation==sp_fourFaceFour ) )
	dv->minor_set_count = 3*dv->couples/2 - 3;
    else if ( dance->formation == sp_custom || (dance->formation>=sp_2Couple && dance->formation<=sp_7CplCircle))  {
	if ( danceHasReset(dance))
	    dv->minor_set_count = (dance->role_cnt==0 ? dance->people : dance->role_cnt)/2;
	else
	    dv->minor_set_count = 1;
    } else if ( dance->formation==sp_custom3 ) {
	if ( danceHasReset(dance))
	    dv->minor_set_count = (dance->role_cnt==0 ? dance->people : dance->role_cnt)/3;
	else
	    dv->minor_set_count = 1;
    } else if ( dance->formation == sp_facingCouples ) 
	dv->minor_set_count = (dance->part_cnt>1 ) ? dance->part_cnt : 2;
    else if ( dance->formation == sp_scottishTripleMinor ) {
	if ( danceHasLastCondition(dance) )
	    dv->minor_set_count = 8;
	else
	    dv->minor_set_count = 6;
    } else
	dv->minor_set_count = 4;
    if ( options->minor_set_count==0 && dv->minor_set_count<dance->part_cnt && dance->choreo_cnt==0 )
	dv->minor_set_count = dance->part_cnt;

    if ( dance->music.show_minorset!=MINORSET_UNSPECIFIED )
	dance->show_minorset = dance->music.show_minorset;
    if ( dance->choreo_cnt==0 && dance->show_minorset &&
	    (danceHasReset(dv->dance) ||
	     (danceNeedsCoupleCount(dv->dance) && dv->minor_set_count>1 ))) {
	dv->show_minorset_first = true;
#if 0
	if ( options->minor_set_count==0 && dance->music.filename!=NULL && dance->music.minorsets!=0 )
	    ++dv->minor_set_count;
#endif
    }

    if ( options->flags&(1<<df_showPerson) )
	dv->show_person = true;
    else if ( options->flags&(1<<df_noShowPerson) )
	dv->show_person = false;
    if ( options->flags&(1<<df_showGrid) )
	dv->show_grid = true;
    else if ( options->flags&(1<<df_noShowGrid) )
	dv->show_grid = false;
    if ( options->flags&(1<<df_showFrame) )
	dv->show_frame = true;
    else if ( options->flags&(1<<df_noShowFrame) )
	dv->show_frame = false;

    if ( dance->style != ds_square && dance->style!=ds_appalachian && dance->style!=ds_longsword )
	dv->show_bar = true; /* !danceHasActualBars(dance); */ /* Should handle actual bars now */
    if ( options->flags&(1L<<df_showBar) )
	dv->show_bar = true;
    else if ( options->flags&(1L<<df_noShowBar) )
	dv->show_bar = false;

    if ( options->flags&(1<<df_frameTitle) )
	dv->show_frameTitle = true;
    else if ( options->flags&(1<<df_noFrameTitle) )
	dv->show_frameTitle = false;
    if ( options->flags&(1<<df_title) )
	dv->show_title = true;
    else if ( options->flags&(1<<df_noTitle) )
	dv->show_title = false;
    if ( options->flags&(1<<df_minorSet) )
	dv->show_minorset_first = true;
    else if ( options->flags&(1<<df_noMinorSet) )
	dv->show_minorset_first = false;
    if ( options->flags&(1<<df_showPath) )
	dv->show_path = true;
    else if ( options->flags&(1<<df_noShowPath) )
	dv->show_path = false;
    if ( options->flags&(1<<df_debugClasp) )
	dv->debugClasp = true;
    else if ( options->flags&(1<<df_noDebugClasp) )
	dv->debugClasp = false;
    if ( options->flags&(1<<df_womensOrientation) )
	dv->womensOrientation = true;
    else if ( options->flags&(1<<df_mensOrientation) )
	dv->womensOrientation = false;

    if ( options->flags&(1<<df_honour))
	dv->do_honour = true;
    else if ( options->flags&(1<<df_noHonour))
	dv->do_honour = false;
    else if ( dance->do_honour==honour_doit )
	dv->do_honour = true;
    else if ( dance->do_honour==honour_dont )
	dv->do_honour = false;
    else if ( dance->style == ds_scottish )
	dv->do_honour = true;
    else
	dv->do_honour = false;

    if ( options->flags&(1L<<df_verbose))
	dv->verbose = true;
    else if ( options->flags&(1L<<df_noVerbose))
	dv->verbose = false;
    dance->verbose = dv->verbose;

    if ( options->flags&(1L<<df_png))
	dv->output_pngs = true;
    else if ( options->flags&(1L<<df_noPng))
	dv->output_pngs = false;

    if ( options->flags&(1L<<df_svg))
	dv->output_svg = true;
    else if ( options->flags&(1L<<df_noSvg))
	dv->output_svg = false;

    dv->add_counts_to_audio = false;
    dv->add_tones_to_audio = false;
    if (( dance->counts_per_bar==2 || dance->counts_per_bar==3 ) &&
	    danceMinorSetMillibarCount(dance)%4000==0 &&
	    dv->frames_per_bar / (double) dv->frames_per_sec /(double) dv->dance->counts_per_bar >= .5 &&
	    dv->audio_sample_rate == 44100 ) {
	if ( options->flags&(1<<df_addCounts) )
	    dv->add_counts_to_audio = true;
	else if ( options->flags&(1<<df_noAddCounts) )
	    dv->add_counts_to_audio = false;
    } else if ( options->flags&(1<<df_addCounts) ) {
	fprintf( stderr, "Cannot provide aural counts because: " );
	if ( dance->counts_per_bar!=2 && dance->counts_per_bar!=3 )
	    fprintf( stderr, "counts per bar is neither 2 nor 3, " );
	if ( danceMinorSetMillibarCount(dance)%4000!=0 )
	    fprintf( stderr, "the number of bars in the dance is not a multiple of 4, " );
	if ( dv->frames_per_bar / (double) dv->frames_per_sec /(double) dv->dance->counts_per_bar < .5 )
	    fprintf( stderr, "the time for each count must be at least half a second (but this dance is too fast), " );
	if ( dv->audio_sample_rate != 44100 )
	    fprintf( stderr, "output sample rate is wrong (must be 44100)." );
	fprintf( stderr, "\n" );
    }
    if ( dv->frames_per_bar / (double) dv->frames_per_sec /(double) dv->dance->counts_per_bar >= .25 &&
	    dv->audio_sample_rate != 0 && !dv->add_counts_to_audio ) {
	if ( options->flags&(1<<df_addTone) )
	    dv->add_tones_to_audio = true;
	else if ( options->flags&(1<<df_noAddTone) )
	    dv->add_tones_to_audio = false;
	else if ( dance->music.filename==NULL )
	    dv->add_tones_to_audio = true;
	else
	    dv->minorset_add_tones_to_audio = true;
    } else if ( options->flags&(1<<df_addTone) ) {
	fprintf( stderr, "Cannot provide aural tones because: " );
	if ( dv->frames_per_bar / (double) dv->frames_per_sec /(double) dv->dance->counts_per_bar < .25 )
	    fprintf( stderr, "the time for each count must be at least a quarter second (but this dance is too fast), " );
	if ( dv->audio_sample_rate != 44100 )
	    fprintf( stderr, "output sample rate is wrong (must be 44100)." );
	fprintf( stderr, "\n" );
    }
    if ( options->past_path_millibars<0 )
	dv->past_path_millibars = 1 * 1000;
    else
	dv->past_path_millibars = options->past_path_millibars;
    if ( options->future_path_millibars<0 )
	dv->future_path_millibars = 2 * 1000;
    else
	dv->future_path_millibars = options->past_path_millibars;

    if ( options->background!=NULL ) {
	if ( *options->background=='#' ) {
	    sscanf( options->background, "#%x,fade:#%x", &dv->back_col, &dv->fade_col );
	    if ( strstr(options->background,"delay")!=NULL ) dv->fade_delay = 1;
	} else {
	    dv->back_img = danceLoadScaleImage(dv,options->background,true);
	}
    } else if ( dance->background_file!=NULL ) {
	if ( *dance->background_file=='#' ) {
	    sscanf( dance->background_file, "#%x,fade:#%x", &dv->back_col, &dv->fade_col );
	    if ( strstr(dance->background_file,"delay")!=NULL ) dv->fade_delay = 1;
	} else if ( strchr(dance->background_file,'%')!=NULL ) {
	    int f;
	    for ( f=0; f<101; ++f ) {
		snprintf( buffer, sizeof(buffer), dance->background_file, f );
		dv->back_img = danceLoadScaleImage(dv,buffer,false);
		if ( dv->back_img!=NULL )
	    break;
	    }
	    if ( dv->back_img!=NULL ) {
		dv->has_back_frames = true;
		dv->last_back_frame_loaded = f;
	    } else
		fprintf( stderr, "Failed to find background frames: %s\n", dance->background_file );
	} else
	    dv->back_img = danceLoadScaleImage(dv,dance->background_file,true);
    }
    if ( dance->title_background_file!=NULL )
	dv->title_back_img = danceLoadScaleImage(dv,dance->title_background_file,true);

    if ( dance->minorset1_bar==-1 && danceClaspsOutsideMinorSet(dance) &&
/* A lot of Scottish dances end in a circle 6 which becomes a circle 8 on the */
/*  last set. The circle 8 is outside the minor set, but it's only at the end */
/*  so we don't need a minorset1 tag for it */
	    dance->formation!=sp_scottishTripleMinor )
	fprintf( stderr, "Warning: You probably want to add a <minorset1 tag to your dance.\n" );

    danceAVBuildTimelines(dv, options);
    danceAVStart(dv, options);
    dv->active = dv->timeline;

    dance->current_part = options->initial_part-1;
    if ( dance->current_part>=dance->part_cnt || dance->current_part<0 )
	dance->current_part = 0;
    if ( dance->roles!=NULL || dance->sequence!=NULL )
	danceStart(dance,dv->couples,options->out_at_top);

    if ( dance->face_cnt!=0 )
	danceLoadFaces(dv);
    danceFindBB(dance,&dv->bb);
    if ( dance->bbs!=NULL ) {
	dance->currentBB = dance->bbs;
	dance->bbStartOfCurrent = dance->bbs->bb;
	danceVideoFigureCurrentBB(dv,0);
    }
    if ( dv->show_minorset_first ) {
	if ( /* (dance->formation==sp_circularMonople && dance->sicilian) ||*/
		dance->minorset1_same_bb ||
		(dance->formation>=sp_2Couple && dance->formation<=sp_8Couple) ||
		dance->formation==sp_custom || dance->formation==sp_custom3 ||
		dance->formation==sp_facingCouples /*|| dance->formation==sp_facingCouplesProper*/ ) {
	    dv->minorSetBB = dv->bb;
/* These haven't been set yet, so no point copying them */
/*	    dv->ms_scale = dv->full_scale; */
/*	    dv->ms_xorigin = dv->full_xorigin; */
/*	    dv->ms_yorigin = dv->full_yorigin; */
	} else
	    danceFindMinorSetBB(dance,&dv->minorSetBB);
    }

    danceVideoFigureScale(dv,options);

    dv->place_calls_underneath = true;
    if ( options->callPos == cp_under )
	dv->place_calls_underneath = true;
    else if ( options->callPos == cp_left )
	dv->place_calls_underneath = false;

    if ( dv->show_minorset_first &&
	    (danceNeedsCoupleCount(dv->dance) || dance->formation==sp_scottishTripleMinor || dance->formation==sp_scottishDupleMinor) &&
	    /* dv->minor_set_count>1 && */
	    dv->dance->bbs == NULL) {
	dv->full_scale = dv->scale;
	dv->full_xorigin = dv->xorigin;
	dv->full_yorigin = dv->yorigin;
	dv->full_place_calls_underneath = dv->place_calls_underneath;

	vw = round(dv->width *dv->usable_fraction);
	vh = round(dv->height*dv->usable_fraction);

	if ( options->callPos == cp_under )
	    vh -= 2*dv->height*48/1080;		/* room for two lines of calls */
	else if ( options->callPos == cp_left )
	    vw -= 10*dv->height*48/1080;;		/* room for 10 ems horizontal for the calls */

	dw = dv->minorSetBB.maxx - dv->minorSetBB.minx;
	dh = dv->minorSetBB.maxy - dv->minorSetBB.miny;
	sx = vw / (float) dw;
	sy = vh / (float) dh;
	if ( sx>sy ) {
	    sx = sy;
	    /*minorSetCallsUnderneath = false;*/
	} /*else*/
	    minorSetCallsUnderneath = true;
	if ( options->callPos == cp_under )
	    minorSetCallsUnderneath = true;
	else if ( options->callPos == cp_left )
	    minorSetCallsUnderneath = false;
	dv->ms_scale = sx;
	dv->ms_xorigin = (dv->width - (dv->minorSetBB.maxx-dv->minorSetBB.minx)*sx)/2 - dv->minorSetBB.minx*sx;
	dv->ms_yorigin = (dv->height- (dv->minorSetBB.maxy-dv->minorSetBB.miny)*sx)/2 - dv->minorSetBB.miny*sx + dv->height/30;
    } else {
	dv->minorSetBB = dv->bb;
	dv->ms_scale   = dv->full_scale = dv->scale;
	dv->ms_xorigin = dv->full_xorigin = dv->xorigin;
	dv->ms_yorigin = dv->full_yorigin = dv->yorigin;
	minorSetCallsUnderneath = dv->full_place_calls_underneath = dv->place_calls_underneath;
    }
    if ( dv->show_minorset_first && !(options->flags&(1<<df_noShowGrid)))
	dv->show_grid = true;		/* Show grid during minor set unless specifically barred */

    dv->frame = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
	    dv->width, dv->height);
    dv->cr = cairo_create(dv->frame);

    dv->layout = pango_cairo_create_layout(dv->cr);
    dv->call_layout = pango_cairo_create_layout(dv->cr);
    dv->music_layout = pango_cairo_create_layout(dv->cr);
    dv->title_layout = pango_cairo_create_layout(dv->cr);
    dv->frametitle_layout = pango_cairo_create_layout(dv->cr);
    dv->framefiguretitle_layout = pango_cairo_create_layout(dv->cr);
    dv->person_layout = pango_cairo_create_layout(dv->cr);
    fontFactor = dv->width / (dv->height*16/9.0);
    if ( fontFactor>.9 ) fontFactor = 1;
    font = pango_font_description_new();
    pango_font_description_set_family(font,"Adobe Caslon Pro,times,serif");
    pango_font_description_set_size(font,dv->height*48*fontFactor*PANGO_SCALE/1080);
    pango_layout_set_font_description(dv->layout,font);
    pango_layout_set_font_description(dv->call_layout,font);
    if ( dv->full_place_calls_underneath )
	pango_layout_set_width(dv->call_layout,dv->width*dv->usable_fraction*PANGO_SCALE);
    else
	pango_layout_set_width(dv->call_layout,dv->xorigin*PANGO_SCALE-2*dv->height*48*PANGO_SCALE/1080);    
    pango_layout_set_wrap(dv->call_layout,PANGO_WRAP_WORD);
    pango_font_description_set_size(font,dv->height*16*fontFactor*PANGO_SCALE/1080);
    pango_layout_set_font_description(dv->music_layout,font);
    pango_layout_set_font_description(dv->person_layout,font);

    if ( dance->title_font!=NULL ) {
	char *others = ",Adobe Caslon Pro,times,serif";
	int len = strlen( dance->title_font ) + strlen(others)+4;
	char *space = malloc(len);
	strcpy(space,dance->title_font);
	strcat(space,others);
	pango_font_description_set_family(font,space);
	free(space);
    } else if ( dance->style==ds_scottish ) {
	pango_font_description_set_family(font,"Kells,Adobe Caslon Pro,times,serif");
	pango_font_description_set_weight(font,PANGO_WEIGHT_BOLD);
    } else {
	/* I only made the yogh font in italic, that's all I need it for */
	pango_font_description_set_family(font,"Adobe Caslon Yogh Pro,Adobe Caslon Pro,Times,serif");
	pango_font_description_set_style(font,PANGO_STYLE_ITALIC);
    }
/* Vic asked for the title size to be increased */
    pango_font_description_set_size(font,dv->height*40*fontFactor*PANGO_SCALE/1080);
    pango_layout_set_font_description(dv->frametitle_layout,font);
    temp = quoteCvt(dv->dance->name,true);
    if ( (pt=strchr(temp,'\n'))!=NULL )		/* This is for titles with a subtitle (Three's company): Don't show the subtitle here */
	*pt='\0';
    pango_layout_set_markup(dv->frametitle_layout,temp,-1);
    free(temp);
    pango_layout_get_pixel_extents(dv->frametitle_layout,NULL,&r);
    if ( r.width > dv->width ) {
	long size = 39*fontFactor*PANGO_SCALE;
	size *= dv->height*dv->width;
	size /= (r.width*1080);
	pango_font_description_set_size(font,size);
	pango_layout_set_font_description(dv->frametitle_layout,font);
    }
    pango_font_description_set_size(font,dv->height*30*fontFactor*PANGO_SCALE/1080);
    pango_layout_set_font_description(dv->framefiguretitle_layout,font);

    /*dump_font_families();*/
    pango_font_description_set_size(font,dv->height*200*PANGO_SCALE/1080);
    pango_layout_set_font_description(dv->title_layout,font);
    dv->title_font = font;

    if ( dv->output_svg )
	danceSVGInit(dv, options);

    cnt = 0;
    dance->current_minorset = 0;
    for ( f=0; cnt<dv->minor_set_count; ++f ) {

	dc = danceNextFrame(dance,f,dv->frames_per_bar);
	if ( dance->currentBB!=NULL ) {
	    danceVideoFigureCurrentBB(dv,f);
	    danceVideoFigureScale(dv,options);
	}
#if 0
 printf( "%d: %.1f,%.1f %s %.0f,%.0f part=0x%x\n",
  f,
  dance->dancers[0].now.x,
  dance->dancers[0].now.y,
  commandNames[dance->dancers[0].current->cmd],
  dance->dancers[0].current->endPoint.x, dance->dancers[0].current->endPoint.y,
  dance->dancers[0].current->part_mask );	/* Debug!!! */
#endif
	if ( f==0 ) {
	    if ( dv->active->timeline_type==tl_title )
		danceTitle(dv,minorSetCallsUnderneath);
	    else if ( dv->active->timeline_type==tl_transition_title_set ) /* Actuall just a transition to set */
		danceContractToMinorSet(dv,minorSetCallsUnderneath);
	    if ( dv->active->timeline_type==tl_honour )
		danceScottishHonour(dv);
	    if ( dv->active->timeline_type==tl_scottishImproper )
		danceScottishImproper(dv);
	    dv->currentMusicFrameStart = f;
	    dv->currentMusicCallStart = dv->dance->current;
	}
	/* Special hack for dances were the 1s interact with the 2s below. */
	/*  (like Three Coney Walk, Cockleshells-down, Duke of Kent) */
	/* When displaying only the minor set, it looks odd to have the 1s */
	/*  dancing with ghosts, so turn on display of the couple below when */
	/*  they become useful */
	if ( f==dance->minorset1_bar*dv->frames_per_bar && dv->dont_show_couples_above_me!=0 ) {
	    dv->old_dont_show_couples_above_me = dv->dont_show_couples_above_me;
	    dv->dont_show_couples_above_me = dance->minorset1_dont_show_couples_above_me;
	} else if ( f==dance->minorset1_end_bar*dv->frames_per_bar && dv->old_dont_show_couples_above_me!=0 ) {
	    dv->dont_show_couples_above_me = dv->old_dont_show_couples_above_me;
	}
	danceRender(dv,f);
	if ( f==0 && dv->active->timeline_type==tl_stillFrames ) {
	    danceAddStillFrames(dv,f);
	}
	danceAVAddVideoFrame(dv,f);

	if ( dc==dc_finished ) {
	    dv->active = dv->active->next;
    break;
	}
	else if ( dc==dc_minorSetEnded ) {
	    dv->active = dv->active->next;
	    /* Fiddle with show_grid during minor set */
	    dv->show_grid = (options->flags&(1<<df_showGrid)) ? 1 : 0;
	    if ( dv->active->timeline_type == tl_transition_minor_major )
		danceExpandToFullSet(dv,f);
	    ++cnt;
	    ++dance->current_minorset;
	    dv->currentMusicFrameStart = f;
	    dv->currentMusicCallStart = dv->dance->current;
	    if ( cnt==dv->minor_set_count-1 )
		dance->last_minor_set = 1;
	}
    }
    /* Write enough frames for the video to cover the music */
    if ( dv->active!=NULL && dv->active->timeline_type == tl_stillFrames ) {
	if ( dc==dc_minorSetEnded && !(options->flags&(1L<<df_test))) {
	    /* It looks odd at the end of 3/4 couple dances when everyone */
	    /* is back home but the borders are still colored with the */
	    /* last position they were in. */
	    danceReset(dance,f,dv->frames_per_bar);
	    dance->current = NULL;	/* But don't show the next call */
/*
	    dv->currentMusicFrameStart = f;
	    dv->currentMusicCallStart = &dv->dance->calls[0];
	    strcpy(dv->currentMusicName, dv->dance->calls[0].musicName);
*/
	    dv->show_bar=false;		/* the music is ending, can't really fit the final chord in A or B */
	    danceRender(dv,f);
	}
	danceAddStillFrames(dv,f);
/*	danceAVAddVideoFrame(dv,f);*/
    }
    if ( dv->active!=NULL && dv->active->timeline_type==tl_honour )
	danceScottishHonour(dv);
    if ( dv->active!=NULL && dv->active->timeline_type == tl_stillFrames )
	danceAddStillFrames(dv,f);

    danceAVFinish(dv,options);

    if ( dv->person_patterns!=NULL ) {
	int avail = sizeof(person_colors)/sizeof(person_colors[0]);
	int top = dance->people < avail*avail ? dance->people : avail*avail;
	for ( i=avail; i<top; ++i ) if ( dv->person_patterns[i]!=NULL )
	    cairo_pattern_destroy(dv->person_patterns[i]);
    }
    if ( dv->edge_patterns!=NULL ) {
	int avail = sizeof(edge_colors)/sizeof(edge_colors[0]);
	int top = dance->people < avail*avail ? dance->people : avail*avail;
	for ( i=avail; i<top; ++i ) if ( dv->edge_patterns[i]!=NULL )
	    cairo_pattern_destroy(dv->edge_patterns[i]);
    }
    if ( dv->tripleMinorOutAtBottom[0]!=NULL )
	cairo_pattern_destroy(dv->tripleMinorOutAtBottom[0]);
    if ( dv->tripleMinorOutAtBottom[1]!=NULL )
	cairo_pattern_destroy(dv->tripleMinorOutAtBottom[1]);
    if ( dance->face_cnt!=0 )
	danceFlushFaces(dv);
    pango_font_description_free(dv->title_font);
    g_object_unref(G_OBJECT(dv->person_layout));
    g_object_unref(G_OBJECT(dv->title_layout));
    g_object_unref(G_OBJECT(dv->frametitle_layout));
    g_object_unref(G_OBJECT(dv->framefiguretitle_layout));
    g_object_unref(G_OBJECT(dv->call_layout));
    g_object_unref(G_OBJECT(dv->music_layout));
    g_object_unref(G_OBJECT(dv->layout));
    cairo_destroy(dv->cr);
    cairo_surface_destroy(dv->frame);
    if ( dv->title_back_img!=NULL )
	g_object_unref(G_OBJECT(dv->title_back_img));
    if ( dv->back_img!=NULL )
	g_object_unref(G_OBJECT(dv->back_img));
    if ( bandSurface!=NULL )
	cairo_surface_destroy(bandSurface);
    band_loaded = false; bandSurface=NULL;
	
    free(dv);
    if ( dance->roles!=NULL )
	danceCleanup(dance);
	
}
