static SDL_Surface *DISPMANX_SetVideoMode(_THIS, SDL_Surface *current,
				int width, int height, int bpp, Uint32 flags)
{
//MAC Recuerda que aqui­,originalmente, nos llegaban las dimensiones de un modo de video
// aproximado en SDL_Video.c de entre los modos de video disponibles. AHORA YA NO.
//Ahora lo que hacemos es que nos lleguen directamente la altura y anchura del modo 
//en el que quiere correr la aplicacion, 
//Luego se escala ese modo, de cuanta menos resolucion mejor, (ya que hay
//que hacer una escritura de ram a grafica en la funcion FlipHWScreen), al modo fisico, que
//es en realidad el unico modo grafico que existe, el modo en que hemos arrancado.
//Esto explica por que creamos el plano de overlay a parte, 
//ya que cuando SDL_Video.c llama a SetVideoMode aun no se tienen listos los 
//offsets horizontal y vertical donde empieza el modo de video pequenio 
//(el modo en que corre la app internamente) sobre el grande (el modo fisico).
	
	//Si nos pasan width=0 y height=0, interpreto que el programa no quiere video sino
	//que solo necesita entrar en modo grafico, asi que salto alli:
	if ((width == 0) | (height == 0)) goto go_video_console;	

	//MAC Inicializamos el SOC (bcm_host_init) SOLO si no hemos pasado antes por aqui­. Lo mismo con el fondo.
	//Si ya hemos pasado antes, hacemos limpieza, pero dejamos el fondo sin tocar.
	if (dispvars->pixmem != NULL){
		//Hacemos limpieza de resources, pero dejamos el fondo. No hay problema porque solo lo ponemos
		//si no hemos pasado por aqui antes.
		DISPMANX_FreeResources();	
	}
	else {
    		uint32_t screen = 0;
		
		bcm_host_init();
		
		//MAC Abrimos el display dispmanx
		printf("Dispmanx: Opening display %i\n", screen );
        	dispvars->display = vc_dispmanx_display_open( screen );

		//MAC Recuperamos algunos datos de la configuracion del buffer actual
		vc_dispmanx_display_get_info( dispvars->display, &(dispvars->amode));
		printf( "Dispmanx: Physical video mode is %d x %d\n", 
		dispvars->amode.width, dispvars->amode.height );
		
		//Ponemos el element de fondo negro tanto si se respeta el ratio como si no, 
		//porque si no, se nos vera­a la consola al cambiar de resolucion durante el programa.
		DISPMANX_BlankBackground();
	}	
	


	//-------Bloque de lista de resoluciones, originalmente en VideoInit--------------
	//Para la aplicacion SDL, el unico modo de video disponible va a ser siempre el que pida. 
	
	DISPMANX_AddMode(this, width, height, (((bpp+7)/8)-1));

	//---------------------------------------------------------------------------------	
	
	Uint32 Rmask;
	Uint32 Gmask;
	Uint32 Bmask;
	
	//dispvars->pitch = width * ((bpp+7) /8);

	//MAC Establecemos el pitch en funcion del bpp deseado
        //Lo alineamos a 16 porque es el aligment interno de dispmanx(en ejemp)
	dispvars->bits_per_pixel = bpp;	
        dispvars->pitch = ( ALIGN_UP( width, 16 ) * (bpp/8) );
        //Alineamos la atura a 16 por el mismo motivo (ver ejemplo hello_disp)
        height = ALIGN_UP( height, 16);

	switch (bpp){
	   case 8:
		dispvars->pix_format = VC_IMAGE_8BPP;	       
		break;
	   
	   case 16:
		dispvars->pix_format = VC_IMAGE_RGB565;	       
		break;

	   case 32:
		dispvars->pix_format = VC_IMAGE_XRGB8888;	       
	        break;
           
           default:
	      printf ("\n[ERROR] - wrong bpp: %d\n",bpp);
	      return (NULL);
	}	
	    	
	//MAC blah 
	this->UpdateRects = DISPMANX_DirectUpdate;

	printf ("\nUsing internal program mode: %d x %d %d bpp", 
		width, height, dispvars->bits_per_pixel);	

	//MAC Por ahora en DISPMANX usamos el mismo modo q ya esta establecido
	printf ("\nUsing physical mode: %d x %d %d bpp",
		dispvars->amode.width, dispvars->amode.height,
		dispvars->bits_per_pixel);
	
	//-----------------------------------------------------------------------------
	//Esta parte no es fundamental, solo sirve para conservar el ratio del juego.
	//Si no se hace y simplemente quitas estas lineas, se estira al modo fisico y ya, 
	//quedando la imagen deformada si es de 4:3 en una tele de 16:9, que es lo que pasaba antes.	
	//Simplemente hallamos ese ratio y con el hallamos la nueva anchura, considerando
	//como altura la maxima fisica que tenemos establecida, o sea, la altura del modo fisico establecido. 
	//Tambien se calcula la posicion horizontal en que debe empezar el rect de destino (dst_ypos), 
	//para que no quede pegado a la parte izquierda de la pantalla al ser menor que la resolucion fisica, que
	//obviamente no cambia. 
	//Queda obsoleto si cambiamos la resolucion a una que tenga el mismo ratio que el modo original del juego.
	
	dispvars->ignore_ratio = (int) SDL_getenv("SDL_DISPMANX_IGNORE_RATIO");

	if (dispvars->ignore_ratio)
		vc_dispmanx_rect_set( &(dispvars->dst_rect), 0, 0, 
	   		dispvars->amode.width , dispvars->amode.height );
	else {
		float orig_ratio = ((float)width / (float)height); 
		int dst_width = dispvars->amode.height * orig_ratio;	
		
		//Si la anchura de la imagen escalada nos sale mayor que el ancho fisico de pantalla,
		//mantenemos el ancho fisico de pantalla como anchura maxima.
		if (dst_width > dispvars->amode.width) dst_width = dispvars->amode.width;

		int dst_ypos  = (dispvars->amode.width - dst_width) / 2; 
		printf ("\nUsing proportion ratio: %d / %d = %f", width, height, orig_ratio);
		printf ("\nProgram rect, respecting original ratio: %d x %d \n", 
		dst_width, dispvars->amode.height);

		vc_dispmanx_rect_set( &(dispvars->dst_rect), dst_ypos, 0, 
	   		dst_width , dispvars->amode.height );
			
	}

	//---------------------------Dejamos configurados los rects---------------------
	//Recuerda que los rects NO contienen ninguna informacion visual, solo son tamanio, rectangulos
	//descritos para que los entiendan las funciones vc, solo tamanios de areas.
	//
	//bmp_rect: se usa solo para el volcado del buffer en RAM al resource que toque. Define el tamanio
	//del area a copiar de RAM (pixmem) al resource (dispmam->resources[]) usando write_data(), por
	//eso, y para acabarlo de entender del todo, su altura y anchura son las internas del juego, width y height.
	//
	//src_rect y dst_rect: se usan porque un element necesita dos rects definidos: src_rect es el tamanio del area
	//de entrada,o sea, el tamanio con el que clipeamos la imagen de origen, y dst_rect es el tamanio del area de
	//salida, o sea, el tamanio con que se vera, escalada por hardware, en el element.
	//
	//Por todo esto, src_rect tendra generalmente la altura y anchura de la imagen original, o dicho de otro
	//modo la altura y anchura que usa el juego internamente (width << 16 y height << 16 por algun rollo de
	//tamanio de variable), y dst_rect tendra las dimensiones del area de pantalla a la que queremos escalar
	//esa imagen: si le damos las dimensiones fisicas totales de la pantalla, escalara sin respetar el ratio.   
	//Asi­que lo he corregido manteniendo la altura maxima de la pantalla fisica, y calculando la anchura
	//a partir de dicha altura y el ratio de la imagen (de la resolucion del juego) original.
	//
	//Debes pensar siempre de la siguiente manera: un element, que es como un cristal-lupa, un resource 
	//(aunque tengas dos, en un momento dado el element solo tiene uno) que es como la imagen original,
	//muy pequenita al fondo, y un "embudo", cuyo tamanio del extremo inferior pegado a la imagen original 
	//es de tamanio src_rect, y cuyo tamanio del extremo superior, pegado al element, es de tamanio dst_rect.
	
	vc_dispmanx_rect_set (&(dispvars->bmp_rect), 0, 0, 
	   width, height);	
	
	vc_dispmanx_rect_set (&(dispvars->src_rect), 0, 0, 
	   width << 16, height << 16);	

	//------------------------------------------------------------------------------
	
	//MAC Establecemos alpha. Para transparencia descomentar flags con or.
	VC_DISPMANX_ALPHA_T layerAlpha;
	/*layerAlpha.flags = (DISPMANX_FLAGS_ALPHA_FROM_SOURCE | 
           DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS);*/
	layerAlpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS;
	layerAlpha.opacity = 255;
	layerAlpha.mask	   = 0;
	dispvars->alpha = &layerAlpha;
	
	//MAC Creo los resources. Me hacen falta dos para el double buffering
	dispvars->resources[0] = vc_dispmanx_resource_create( 
	   dispvars->pix_format, width, height, 
	   &(dispvars->vc_image_ptr) );
	
	dispvars->resources[1] = vc_dispmanx_resource_create( 
	   dispvars->pix_format, width, height,
	   &(dispvars->vc_image_ptr) );
	
	//Reservo memoria para el array de pixles en RAM 
    	dispvars->pixmem = calloc( 1, dispvars->pitch * height);
    	//dispvars->pixmem=malloc ( dispvars->pitch * dispvars->amode.height );

	//MAC Esta llamada a ReallocFormat es lo que impedia­a ver algo...
	Rmask = 0;
	Gmask = 0;
	Bmask = 0;
	if ( ! SDL_ReallocFormat(current, bpp, Rmask, Gmask, Bmask, 0) ) {
		return(NULL);
	}
	
	//Preparamos SDL para trabajar sobre el nuevo framebuffer

	//No queremos HWSURFACEs por la manera en que funciona nuestro backend, ya que la app solo
	//debe conocer el buffer en RAM para que las actualizaciones no sean bloqueantes.
	//TAMPOCO queremos DOUBLEBUFFER: realmente piensa lo que estas haciendo: actualizas la 
	//superficie de video, que esta en la RAM, copias a VRAM y, saltandote las normas del API,
	//esperas a evento de vsync para hacer el buffer swapping. Asi­ que la app NO SABE NADA de 
	//double buffering ni debe saberlo. UpdateRect() debe hacer lo que antes hacia­a FlipHWSurface,
	//ya que de cara a la APP, solo hay una actualizacion del buffer de dibujado, NO de pantalla,
	//ya que carecemos de acceso directo a la VRAM.
	//Permitimos HWPALETTEs, cosa que solo se activa si el juego pide un modo de 8bpp porque,
	//tanto si conseguimos modificar la paleta por hard como si tenemos que indexar los valores
	//como estamos haciendo hasta ahora emulando asi la paleta, nos interesa que los juegos
	//entren en SetColors(), y sin paleta por hardware no entran.
	
	current->flags |= SDL_FULLSCREEN;	
	if (flags & SDL_DOUBLEBUF){
	   current->flags &= ~SDL_DOUBLEBUF;	
	}
	if (flags & SDL_HWSURFACE){
	   current->flags &= ~SDL_HWSURFACE;
	   current->flags |= SDL_SWSURFACE;
	}	
	if (flags & SDL_HWPALETTE)
	   current->flags |= SDL_HWPALETTE;	
	
	current->w = width;
	current->h = height;

	current->pitch  = dispvars->pitch;
	current->pixels = dispvars->pixmem;
	
	//DISPMANX_FreeHWSurfaces(this);
	//DISPMANX_InitHWSurfaces(this, current, surfaces_mem, surfaces_len);
	
	//this->screen = current;
	//this->screen = NULL;

	//Aniadimos el element.
	dispvars->update = vc_dispmanx_update_start( 0 );
	
	dispvars->element = vc_dispmanx_element_add( dispvars->update, 
	   dispvars->display, 0 /*layer*/, &(dispvars->dst_rect), 	   
	   dispvars->resources[flip_page], &(dispvars->src_rect), 
	   DISPMANX_PROTECTION_NONE, dispvars->alpha, 0 /*clamp*/, 
	   /*VC_IMAGE_ROT0*/ 0 );
	
	vc_dispmanx_update_submit_sync( dispvars->update );		
	
	/* We're done */
	//MAC Disable graphics 1
	//Aqui ponemos la terminal en modo grafico. Ya no se imprimiran mas mensajes en la consola a partir de aqui. 
	go_video_console:
	if ( DISPMANX_EnterGraphicsMode(this) < 0 )
        	return(NULL);

	
	return(current);
}
예제 #2
0
static SDL_Surface *DISPMANX_SetVideoMode(_THIS, SDL_Surface *current,
				int width, int height, int bpp, Uint32 flags)
{
//MAC Recuerda que aquí ya nos llegan las dimensiones de un modo de vídeo
// aproximado en SDL_Video.c de entre los modos de vídeo disponibles. 
//La jugada es dejar que un modo de 320x200 se vea en el modo que se nos ha 
//aproximado, seguramente mucho mayor, y luego escalar por hardware el área 
//visible al modo aproximado (de ahí que creemos el plano de overlay a parte, 
//ya que cuando SDL_Video.c llama a SetVideoMode aún no se tienen listos los 
//offsets horizontal y vertical donde empieza el modo de vídeo pequeño 
//(el original, ideal) sobre el grande (que sería el aproximado).
//Otra cosa es la tasa de refresco. Tendrás que usar modos físicos 
//concretos (config.txt) para ajustarte a 50, 60 o 70 Hz.
	
	Uint32 Rmask;
	Uint32 Gmask;
	Uint32 Bmask;
	char *surfaces_mem; 
	int surfaces_len;
		
	dispvars->bits_per_pixel = bpp;	
	
	switch (bpp){
	   case 8:
		dispvars->pix_format = VC_IMAGE_8BPP;	       
		break;

	   case 16:
		dispvars->pix_format = VC_IMAGE_RGB565;	       
		break;

	   case 32:
		dispvars->pix_format = VC_IMAGE_XRGB8888;	       
	        break;
           
           default:
	      printf ("\nERR - wrong bpp: %d\n",bpp);
	      return (NULL);
	}	
    	
	//MAC blah 
	this->UpdateRects = DISPMANX_DirectUpdate;

	//MAC Establecemos los rects para el escalado
	//this->offset_x = (dispvars->amode.width - width)/2;
	//this->offset_y = (dispvars->amode.height - height)/2;
	
	printf ("\nUsing internal program mode: width=%d height=%d\n", 
		width, height);	

	//MAC Por ahora en DISPMANX usamos el mismo modo q ya está establecido
	printf ("\nUsing physical mode: %d x %d %d bpp\n",
		dispvars->amode.width, dispvars->amode.height,
		dispvars->bits_per_pixel);
	
	//MAC Establecemos el pitch en fn del bpp deseado	
    	//Lo alineamos a 32 porque es el aligment interno de dispmanx(en ejemp)
	dispvars->pitch = ( ALIGN_UP( width, 16 ) * (bpp/8) );
	//Alineamos la atura a 16 por el mismo motivo (ver ejemplo hello_disp)
	height = ALIGN_UP( height, 16);
	
	//Dejamos configurados los rects
	vc_dispmanx_rect_set (&(dispvars->bmp_rect), 0, 0, 
	   width, height);	
	
	vc_dispmanx_rect_set (&(dispvars->src_rect), 0, 0, 
	   width << 16, height << 16);	

	vc_dispmanx_rect_set( &(dispvars->dst_rect), 0, 0, 
	   dispvars->amode.width , dispvars->amode.height );
	
	//MAC Establecemos alpha. Para transparencia descomentar flags con or.
	VC_DISPMANX_ALPHA_T layerAlpha;
	layerAlpha.flags = (DISPMANX_FLAGS_ALPHA_FROM_SOURCE | 
           DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS);
	/*layerAlpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS;*/
	layerAlpha.opacity = 255;
	layerAlpha.mask	   = 0;
	dispvars->alpha = &layerAlpha;
	
	//MAC Creo los resources. Me hacen falta dos para el double buffering
	dispvars->resources[0] = vc_dispmanx_resource_create( 
	   dispvars->pix_format, width, height, 
	   &(dispvars->vc_image_ptr) );
    	assert (dispvars->resources[0]);
    	
	dispvars->resources[1] = vc_dispmanx_resource_create( 
	   dispvars->pix_format, width, height,
	   &(dispvars->vc_image_ptr) );
    	assert (dispvars->resources[1]);
	
	//Reservo memoria para el array de pixles en RAM 
    	dispvars->pixmem=calloc( 1, dispvars->pitch * height);
    	//dispvars->pixmem=malloc ( dispvars->pitch * dispvars->amode.height );
	
	//MAC Esto se usa, como mínimo y que yo sepa, para DirectUpdate
	//cache_modinfo = *modinfo;	
	//cache_fbinfo  = *(drmModeGetFB (fd, fb_id));	
	
	//MAC Esta llamada a ReallocFormat es lo que impedía ver algo...
	Rmask = 0;
	Gmask = 0;
	Bmask = 0;
	if ( ! SDL_ReallocFormat(current, bpp, Rmask, Gmask, Bmask, 0) ) {
		return(NULL);
	}
	
	//Preparamos SDL para trabajar sobre el nuevo framebuffer
	shadow_fb = 0;
	current->flags |= SDL_HWSURFACE;
	current->flags |= SDL_FULLSCREEN;
	current->flags |= SDL_HWPALETTE;
	if (flags & SDL_DOUBLEBUF)
	   current->flags |= SDL_DOUBLEBUF;	

	current->w = width;
	current->h = height;

	mapped_mem    = dispvars->pixmem;
	mapped_memlen =  (dispvars->pitch * height); 
	current->pitch  = dispvars->pitch;
	current->pixels = mapped_mem;
	
	/* Set up the information for hardware surfaces */
	surfaces_mem = (char *)current->pixels +
		(dispvars->pitch * height);
	surfaces_len = (mapped_memlen-(surfaces_mem-mapped_mem));
		
	DISPMANX_FreeHWSurfaces(this);
	DISPMANX_InitHWSurfaces(this, current, surfaces_mem, surfaces_len);
	
	/* Update for double-buffering, if we can */
	//Recuerda que necesitamos dos buffers porque hay que mantener uno
	//sin tocar durante todo el ciclo de fotograma (16ms) ya que es el que
	//el monitor lee como scanout durante ese tiempo.
	
	this->screen = current;
	this->screen = NULL;

	//Colocamos el element. Alpha 0 estabiliza %uso de cpu
	dispvars->update = vc_dispmanx_update_start( 0 );
	
	dispvars->element = vc_dispmanx_element_add( dispvars->update, 
	   dispvars->display, 2000 /*layer*/, &(dispvars->dst_rect), 	   
	   dispvars->resources[flip_page], &(dispvars->src_rect), 
	   DISPMANX_PROTECTION_NONE, dispvars->alpha, 0 /*clamp*/, 
	   /*VC_IMAGE_ROT0*/ 0 );
	
	vc_dispmanx_update_submit_sync( dispvars->update );		
	
	/* We're done */
	//MAC Disable graphics 1
	// Set the terminal into graphics mode 
        printf ("\nDISPMANX_SetVideoMode activating keyboard and exiting");
	if ( DISPMANX_EnterGraphicsMode(this) < 0 )
        	return(NULL);
	
	return(current);
}