/* * Copyright (C) 2000-2003 the xine project * * This file is part of xine, a free video player. * * xine is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * xine is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * Contains common code to calculate video scaling parameters. * In short, it will map frame dimensions to screen/window size. * Takes into account aspect ratio correction and zooming. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #define LOG_MODULE "vo_scale" #define LOG_VERBOSE /* #define LOG */ #include "xine_internal.h" #include "vo_scale.h" /* * convert delivered height/width to ideal width/height * taking into account aspect ratio and zoom factor */ void _x_vo_scale_compute_ideal_size (vo_scale_t *this) { double image_ratio, desired_ratio; if (this->scaling_disabled) { this->video_pixel_aspect = this->gui_pixel_aspect; } else { /* * aspect ratio */ image_ratio = (double) (this->delivered_width - (this->crop_left + this->crop_right)) / (double) (this->delivered_height - (this->crop_top + this->crop_bottom)); switch (this->user_ratio) { case XINE_VO_ASPECT_AUTO: desired_ratio = this->delivered_ratio; break; case XINE_VO_ASPECT_ANAMORPHIC: desired_ratio = 16.0 / 9.0; break; case XINE_VO_ASPECT_DVB: desired_ratio = 2.0 / 1.0; break; case XINE_VO_ASPECT_SQUARE: desired_ratio = image_ratio; break; case XINE_VO_ASPECT_4_3: default: desired_ratio = 4.0 / 3.0; } this->video_pixel_aspect = desired_ratio / image_ratio; _x_assert(this->gui_pixel_aspect != 0.0); if (fabs (this->video_pixel_aspect / this->gui_pixel_aspect - 1.0) < 0.01) { this->video_pixel_aspect = this->gui_pixel_aspect; } #if 0 /* onefield_xv divide by 2 the number of lines */ if (this->deinterlace_enabled && (this->deinterlace_method == DEINTERLACE_ONEFIELDXV) && (this->cur_frame->format == XINE_IMGFMT_YV12)) { this->displayed_height = this->displayed_height / 2; this->displayed_yoffset = this->displayed_yoffset / 2; } #endif } } /* * make ideal width/height "fit" into the gui */ void _x_vo_scale_compute_output_size (vo_scale_t *this) { int cropped_width, cropped_height; double x_factor, y_factor, aspect; cropped_width = this->delivered_width - (this->crop_left + this->crop_right); cropped_height = this->delivered_height - (this->crop_top + this->crop_bottom); aspect = this->video_pixel_aspect / this->gui_pixel_aspect; x_factor = (double) this->gui_width / (double) (cropped_width * aspect); y_factor = (double) (this->gui_height * aspect) / (double) cropped_height; if (this->scaling_disabled) { this->output_width = cropped_width; this->output_height = cropped_height; this->displayed_width = cropped_width; this->displayed_height = cropped_height; } else { if ( this->support_zoom ) { /* zoom behaviour: * - window size never changes due zooming * - output image shall be increased whenever there are * black borders to use. * - exceding zoom shall be accounted by reducing displayed image. */ if (((double)this->gui_width - (double)cropped_width * y_factor) < ((double)this->gui_height - (double)cropped_height * x_factor)) { this->output_width = this->gui_width; this->displayed_width = (double)cropped_width / this->zoom_factor_x + 0.5; this->output_height = (double)cropped_height * x_factor + 0.5; if( this->output_height * this->zoom_factor_y <= this->gui_height ) { this->displayed_height = cropped_height; this->output_height = (double)this->output_height * this->zoom_factor_y + 0.5; } else { this->displayed_height = (double)cropped_height * this->gui_height / this->output_height / this->zoom_factor_y + 0.5; this->output_height = this->gui_height; } } else { this->output_height = this->gui_height; this->displayed_height = (double)cropped_height / this->zoom_factor_y + 0.5; this->output_width = (double)cropped_width * y_factor + 0.5; if( this->output_width * this->zoom_factor_x <= this->gui_width ) { this->displayed_width = cropped_width; this->output_width = (double)this->output_width * this->zoom_factor_x + 0.5; } else { this->displayed_width = (double)cropped_width * this->gui_width / this->output_width / this->zoom_factor_x + 0.5; this->output_width = this->gui_width; } } } else { if (((double)this->gui_width - (double)cropped_width * y_factor) < ((double)this->gui_height - (double)cropped_height * x_factor)) { this->output_width = (double) this->gui_width; this->output_height = (double) cropped_height * x_factor + 0.5; } else { this->output_width = (double) cropped_width * y_factor + 0.5; this->output_height = (double) this->gui_height; } this->displayed_width = cropped_width; this->displayed_height = cropped_height; } } /* make sure displayed values are sane, that is, we are not trying to display * something outside the delivered image. may happen when zoom < 100%. */ if( this->displayed_width > this->delivered_width ) { this->output_width = (double) this->output_width * this->delivered_width / this->displayed_width + 0.5; this->displayed_width = this->delivered_width; } if( this->displayed_height > this->delivered_height ) { this->output_height = (double) this->output_height * this->delivered_height / this->displayed_height + 0.5; this->displayed_height = this->delivered_height; } this->output_xoffset = (this->gui_width - this->output_width) * this->output_horizontal_position + this->gui_x; this->output_yoffset = (this->gui_height - this->output_height) * this->output_vertical_position + this->gui_y; this->displayed_xoffset = ((cropped_width - this->displayed_width) / 2) + this->crop_left; this->displayed_yoffset = ((cropped_height - this->displayed_height) / 2) + this->crop_top; lprintf ("frame source %d x %d (%d x %d) => screen output %d x %d\n", this->delivered_width, this->delivered_height, this->displayed_width, this->displayed_height, this->output_width, this->output_height); /* calculate borders */ if (this->output_height < this->gui_height) { /* top */ this->border[0].x = 0; this->border[0].y = 0; this->border[0].w = this->gui_width; this->border[0].h = this->output_yoffset; /* bottom */ this->border[1].x = 0; this->border[1].y = this->output_yoffset + this->output_height; this->border[1].w = this->gui_width; this->border[1].h = this->gui_height - this->border[1].y; } else { /* no top/bottom borders */ this->border[0].w = this->border[0].h = 0; this->border[1].w = this->border[1].h = 0; } if (this->output_width < this->gui_width) { /* left */ this->border[2].x = 0; this->border[2].y = 0; this->border[2].w = this->output_xoffset; this->border[2].h = this->gui_height; /* right */ this->border[3].x = this->output_xoffset + this->output_width; this->border[3].y = 0; this->border[3].w = this->gui_width - this->border[3].x; this->border[3].h = this->gui_height; } else { /* no left/right borders */ this->border[2].w = this->border[2].h = 0; this->border[3].w = this->border[3].h = 0; } } /* * return true if a redraw is needed due resizing, zooming, * aspect ratio changing, etc. */ int _x_vo_scale_redraw_needed (vo_scale_t *this) { int gui_x, gui_y, gui_width, gui_height, gui_win_x, gui_win_y; double gui_pixel_aspect; int ret = 0; _x_assert(this->frame_output_cb); if ( ! this->frame_output_cb ) return 0; this->frame_output_cb (this->user_data, this->delivered_width - (this->crop_left + this->crop_right), this->delivered_height - (this->crop_top + this->crop_bottom), this->video_pixel_aspect, &gui_x, &gui_y, &gui_width, &gui_height, &gui_pixel_aspect, &gui_win_x, &gui_win_y ); if ( (gui_x != this->gui_x) || (gui_y != this->gui_y) || (gui_width != this->gui_width) || (gui_height != this->gui_height) || (gui_pixel_aspect != this->gui_pixel_aspect) || (gui_win_x != this->gui_win_x) || (gui_win_y != this->gui_win_y) ) { this->gui_x = gui_x; this->gui_y = gui_y; this->gui_width = gui_width; this->gui_height = gui_height; this->gui_win_x = gui_win_x; this->gui_win_y = gui_win_y; this->gui_pixel_aspect = gui_pixel_aspect; ret = 1; } else ret = this->force_redraw; this->force_redraw = 0; return ret; } /* * */ void _x_vo_scale_translate_gui2video(vo_scale_t *this, int x, int y, int *vid_x, int *vid_y) { if (this->output_width > 0 && this->output_height > 0) { /* * 1. * the driver may center a small output area inside a larger * gui area. This is the case in fullscreen mode, where we often * have black borders on the top/bottom/left/right side. */ x -= this->output_xoffset; y -= this->output_yoffset; /* * 2. * the driver scales the delivered area into an output area. * translate output area coordianates into the delivered area * coordiantes. */ x = x * this->displayed_width / this->output_width + this->displayed_xoffset; y = y * this->displayed_height / this->output_height + this->displayed_yoffset; } *vid_x = x; *vid_y = y; } /* * Returns description of a given ratio code */ char *_x_vo_scale_aspect_ratio_name(int a) { switch (a) { case XINE_VO_ASPECT_AUTO: return "auto"; case XINE_VO_ASPECT_SQUARE: return "square"; case XINE_VO_ASPECT_4_3: return "4:3"; case XINE_VO_ASPECT_ANAMORPHIC: return "16:9"; case XINE_VO_ASPECT_DVB: return "2:1"; default: return "unknown"; } } /* * config callbacks */ static void vo_scale_horizontal_pos_changed(void *data, xine_cfg_entry_t *entry) { vo_scale_t *this = (vo_scale_t *)data; this->output_horizontal_position = entry->num_value / 100.0; this->force_redraw = 1; } static void vo_scale_vertical_pos_changed(void *data, xine_cfg_entry_t *entry) { vo_scale_t *this = (vo_scale_t *)data; this->output_vertical_position = entry->num_value / 100.0; this->force_redraw = 1; } static void vo_scale_disable_scaling_changed(void *data, xine_cfg_entry_t *entry) { vo_scale_t *this = (vo_scale_t *)data; if (this->scaling_disabled < 2) { this->scaling_disabled = entry->num_value; this->force_redraw = 1; } } /* * initialize rescaling struct */ void _x_vo_scale_init(vo_scale_t *this, int support_zoom, int scaling_disabled, config_values_t *config ) { memset( this, 0, sizeof(vo_scale_t) ); this->support_zoom = support_zoom; this->force_redraw = 1; this->zoom_factor_x = 1.0; this->zoom_factor_y = 1.0; this->gui_pixel_aspect = 1.0; this->user_ratio = XINE_VO_ASPECT_AUTO; this->delivered_ratio = 0.0; this->crop_left = 0; this->crop_right = 0; this->crop_top = 0; this->crop_bottom = 0; this->output_horizontal_position = config->register_range(config, "video.output.horizontal_position", 50, 0, 100, _("horizontal image position in the output window"), _("If the video window's horizontal size is bigger than the actual image " "to show, you can adjust the position where the image will be placed.\n" "The position is given as a percentage, so a value of 50 means \"in the " "middle\", while 0 means \"at the very left\" and 100 \"at the very right\"."), 10, vo_scale_horizontal_pos_changed, this) / 100.0; this->output_vertical_position = config->register_range(config, "video.output.vertical_position", 50, 0, 100, _("vertical image position in the output window"), _("If the video window's vertical size is bigger than the actual image " "to show, you can adjust the position where the image will be placed.\n" "The position is given as a percentage, so a value of 50 means \"in the " "middle\", while 0 means \"at the top\" and 100 \"at the bottom\"."), 10, vo_scale_vertical_pos_changed, this) / 100.0; this->scaling_disabled = (scaling_disabled << 1) | config->register_bool(config, "video.output.disable_scaling", 0, _("disable all video scaling"), _("If you want the video image to be always shown at its original resolution, " "you can disable all image scaling here.\n" "This of course means that the image will no longer adapt to the size of the " "video window and that videos with a pixel aspect ratio other than 1:1, like " "anamorphic DVDs, will be shown distorted. But on the other hand, with some " "video output drivers like XShm, where the image scaling is not hardware " "accelerated, this can dramatically reduce CPU usage."), 10, vo_scale_disable_scaling_changed, this); }