+/*****************************************************************************
+ * Print: print simple text on a picture
+ *****************************************************************************
+ * This function will print a simple text on the picture. It is designed to
+ * print debugging or general informations.
+ *****************************************************************************/
+void Print( vout_thread_t *p_vout, int i_x, int i_y, int i_h_align, int i_v_align, unsigned char *psz_text )
+{
+ int i_text_height; /* total text height */
+ int i_text_width; /* total text width */
+
+ /* Update upper left coordinates according to alignment */
+ vout_TextSize( p_vout->p_default_font, 0, psz_text, &i_text_width, &i_text_height );
+ if( !Align( p_vout, &i_x, &i_y, i_text_width, i_text_height, i_h_align, i_v_align ) )
+ {
+ /* Set area and print text */
+ SetBufferArea( p_vout, i_x, i_y, i_text_width, i_text_height );
+ vout_Print( p_vout->p_default_font, p_vout->p_buffer[ p_vout->i_buffer_index ].p_data +
+ i_y * p_vout->i_bytes_per_line + i_x * p_vout->i_bytes_per_pixel,
+ p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,
+ p_vout->i_white_pixel, 0, 0,
+ 0, psz_text );
+ }
+}
+
+/*****************************************************************************
+ * SetBufferArea: activate an area in current buffer
+ *****************************************************************************
+ * This function is called when something is rendered on the current buffer.
+ * It set the area as active and prepare it to be cleared on next rendering.
+ * Pay attention to the fact that in this functions, i_h is in fact the end y
+ * coordinate of the new area.
+ *****************************************************************************/
+static void SetBufferArea( vout_thread_t *p_vout, int i_x, int i_y, int i_w, int i_h )
+{
+ vout_buffer_t * p_buffer; /* current buffer */
+ int i_area_begin, i_area_end; /* area vertical extension */
+ int i_area, i_area_copy; /* area index */
+ int i_area_shift; /* shift distance for areas */
+
+ /* Choose buffer and modify h to end of area position */
+ p_buffer = &p_vout->p_buffer[ p_vout->i_buffer_index ];
+ i_h += i_y - 1;
+
+ /*
+ * Remove part of the area which is inside the picture - this is done
+ * by calling again SetBufferArea with the correct areas dimensions.
+ */
+ if( (i_x >= p_buffer->i_pic_x) && (i_x + i_w <= p_buffer->i_pic_x + p_buffer->i_pic_width) )
+ {
+ i_area_begin = p_buffer->i_pic_y;
+ i_area_end = i_area_begin + p_buffer->i_pic_height - 1;
+
+ if( ((i_y >= i_area_begin) && (i_y <= i_area_end)) ||
+ ((i_h >= i_area_begin) && (i_h <= i_area_end)) ||
+ ((i_y < i_area_begin) && (i_h > i_area_end)) )
+ {
+ /* Keep the stripe above the picture, if any */
+ if( i_y < i_area_begin )
+ {
+ SetBufferArea( p_vout, i_x, i_y, i_w, i_area_begin - i_y );
+ }
+ /* Keep the stripe below the picture, if any */
+ if( i_h > i_area_end )
+ {
+ SetBufferArea( p_vout, i_x, i_area_end, i_w, i_h - i_area_end );
+ }
+ return;
+ }
+ }
+
+ /* Skip some extensions until interesting areas */
+ for( i_area = 0;
+ (i_area < p_buffer->i_areas) &&
+ (p_buffer->pi_area_end[i_area] + 1 <= i_y);
+ i_area++ )
+ {
+ ;
+ }
+
+ if( i_area == p_buffer->i_areas )
+ {
+ /* New area is below all existing ones: just add it at the end of the
+ * array, if possible - else, append it to the last one */
+ if( i_area < VOUT_MAX_AREAS )
+ {
+ p_buffer->pi_area_begin[i_area] = i_y;
+ p_buffer->pi_area_end[i_area] = i_h;
+ p_buffer->i_areas++;
+ }
+ else
+ {
+#ifdef DEBUG_VIDEO
+ intf_DbgMsg("areas overflow\n");
+#endif
+ p_buffer->pi_area_end[VOUT_MAX_AREAS - 1] = i_h;
+ }
+ }
+ else
+ {
+ i_area_begin = p_buffer->pi_area_begin[i_area];
+ i_area_end = p_buffer->pi_area_end[i_area];
+
+ if( i_y < i_area_begin )
+ {
+ if( i_h >= i_area_begin - 1 )
+ {
+ /* Extend area above */
+ p_buffer->pi_area_begin[i_area] = i_y;
+ }
+ else
+ {
+ /* Create a new area above : merge last area if overflow, then
+ * move all old areas down */
+ if( p_buffer->i_areas == VOUT_MAX_AREAS )
+ {
+#ifdef DEBUG_VIDEO
+ intf_DbgMsg("areas overflow\n");
+#endif
+ p_buffer->pi_area_end[VOUT_MAX_AREAS - 2] = p_buffer->pi_area_end[VOUT_MAX_AREAS - 1];
+ }
+ else
+ {
+ p_buffer->i_areas++;
+ }
+ for( i_area_copy = p_buffer->i_areas - 1; i_area_copy > i_area; i_area_copy-- )
+ {
+ p_buffer->pi_area_begin[i_area_copy] = p_buffer->pi_area_begin[i_area_copy - 1];
+ p_buffer->pi_area_end[i_area_copy] = p_buffer->pi_area_end[i_area_copy - 1];
+ }
+ p_buffer->pi_area_begin[i_area] = i_y;
+ p_buffer->pi_area_end[i_area] = i_h;
+ return;
+ }
+ }
+ if( i_h > i_area_end )
+ {
+ /* Find further areas which can be merged with the new one */
+ for( i_area_copy = i_area + 1;
+ (i_area_copy < p_buffer->i_areas) &&
+ (p_buffer->pi_area_begin[i_area] <= i_h);
+ i_area_copy++ )
+ {
+ ;
+ }
+ i_area_copy--;
+
+ if( i_area_copy != i_area )
+ {
+ /* Merge with last possible areas */
+ p_buffer->pi_area_end[i_area] = MAX( i_h, p_buffer->pi_area_end[i_area_copy] );
+
+ /* Shift lower areas upward */
+ i_area_shift = i_area_copy - i_area;
+ p_buffer->i_areas -= i_area_shift;
+ for( i_area_copy = i_area + 1; i_area_copy < p_buffer->i_areas; i_area_copy++ )
+ {
+ p_buffer->pi_area_begin[i_area_copy] = p_buffer->pi_area_begin[i_area_copy + i_area_shift];
+ p_buffer->pi_area_end[i_area_copy] = p_buffer->pi_area_end[i_area_copy + i_area_shift];
+ }
+ }
+ else
+ {
+ /* Extend area below */
+ p_buffer->pi_area_end[i_area] = i_h;
+ }
+ }
+ }