Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





¿cómo Escalar Sin Librerias? (sdl)

Iniciado por J_F_NASH, 21 de Octubre de 2005, 12:11:12 AM

« anterior - próximo »

J_F_NASH

 Necesito que alguien me explique cómo escalar una superficie en SDL sin utilizar librerias.
¿Cual es la manera mas sencilla de hacerlo?

S2.

TheAzazel

 Porque no quieres utilzar SDL_gfx que trae una rutina de escalado bastante buena?

J_F_NASH

Cita de: "TheAzazel"Porque no quieres utilzar SDL_gfx que trae una rutina de escalado bastante buena?
Preferiría saber hacerlo a pelo, la verdad. No creo que sea muy complicado.
De todas formas no me entero con la librería. Metí los archivos .h en su lugar y probé pero me daba un error de enlaces o algo así, terminé por dejarlo. Debe estar tirao pero no hay paciencia. Si hay por ahí alguna explicación en castellano u otro día tengo la paciencia necesaria...

Ya tengo bastante con aprender C y SDL como para meterme en mas berenjenales. No se como vosotros teneis cabeza para tanto C, C++, SDL, glSDL, DX, OGL,... buf yo no puedo. A veces me gustaría empezar con código máquina; te aprendes 4 instrucciones y con eso lo haces TODO. Claro que tambien tiene sus contras.

Bueno a ver si me decís algo y el sabado estoy "escalando" :D

S2.  

HgH

 ¿te refieres a una función de escalado de bitmaps normal y corriente?

por otra parte.... ¿ensamblador..  4 instrucciones ?  :blink:

tendrias que aprender:

ASM x86 (instrucciones mínimo de 386)
FPU
MMX
3dNow
SSE
SSE2

etc,etc    B)  
gH _ TLOTB
..................
"No queremos estar obligados, por nada ni nadie, a pensar sólo en lo que es más comercial. Siempre seremos, antes que nada, programadores"

- Equipo de Zigurat -

J_F_NASH

Cita de: "HgH"¿te refieres a una función de escalado de bitmaps normal y corriente?
Pues no se si es lo mismo.
Yo tengo la superficie principal y "dentro" 2 superficies mas. En cada una de estas dos ultimas tengo un bitmap.
Lo que quiero hacer es escalar la superficie principal de manera que se escale todo.

Aquí la superficie principal:
SDL_Surface *pantalla
pantalla = SDL_SetVideoMode(ancho, alto, 8, SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_FULLSCREEN);


FPU, MMX, 3dNow, SSE, SSE2... eso es para los que apuntan alto. Yo me conformo con hacer un juego del tipo AMSTRAD & SPECTRUM


S2.

HgH

 A ver si me acuerdo de mis dias de modo 13:

Suponiendo que el bitmap es 32 bpp

void EscalaBitmap (DWORD *origen, DWORD *destino, int ancho_origen, int alto_origen,
       int ancho_destino, int alto_destino)
{
 float factor_escala_x = ancho_origen / ancho_destino;
 float factor_escala_y = alto_origen / alto_destino;
 
 float xx_origen;
 float yy_origen;
 
 for(int yy = 0;yy<alto_destino;yy++)
 {
  yy_origen = yy * factor_escala_y;
  xx_origen = 0.0f;
  for(int xx = 0;xx<ancho_destino;xx++)
  {
   destino[(int)(ancho_destino*yy + xx)] = origen[(int)(yy_origen + xx_origen)];
   xx_origen+=factor_escala_x;
  }
 }
}


No se si esto es lo que estabas buscando. Por supuesto se puede optimizar, por ejemplo usando coma fija y algun truquillo más. Eso te lo dejo para nota :P

gH _ TLOTB
..................
"No queremos estar obligados, por nada ni nadie, a pensar sólo en lo que es más comercial. Siempre seremos, antes que nada, programadores"

- Equipo de Zigurat -

J_F_NASH

Cita de: "HgH"
void EscalaBitmap (DWORD *origen, DWORD *destino, int ancho_origen, int alto_origen,
       int ancho_destino, int alto_destino)
{
 float factor_escala_x = ancho_origen / ancho_destino;
 float factor_escala_y = alto_origen / alto_destino;
 
 float xx_origen;
 float yy_origen;
 
 for(int yy = 0;yy<alto_destino;yy++)
 {
  yy_origen = yy * factor_escala_y;
  xx_origen = 0.0f;
  for(int xx = 0;xx<ancho_destino;xx++)
  {
   destino[(int)(ancho_destino*yy + xx)] = origen[(int)(yy_origen + xx_origen)];
   xx_origen+=factor_escala_x;
  }
 }
}
1) ¿DWORD?... ¿qué tipo de dato es eso?
2) ¿Se supone que tengo que crear una nueva superficie (destino)?.

3) ¿Cual es la filosofia al escalar algo?, ¿en que consiste?, ¿multiplicar los 4 vertices de la superficie, hacerlo con arreglos,...?, ¿cómo se  hace entonces?... quizás e de hecharle mas imaginación.
No hace falta que metais un código y si lo haceis comentadlo porque ando muy perdido. No se ni por donde agarrar esto. Que no se moleste nadie pero estoy como al principio (nooo) .

Gracias en cualquier caso.

S2.

TheAzazel

 En realidad, lo que tu buscas son algoritmos de interpolacion (los hay super simples, bilineales, cubicos, etc...).
Busca con el google y te saldran mil cosas, y lee todo lo que puedas para acumular experiencia :)

suerte

PD: joer EX3, parecemos los buhos jeje, yo me voy a dormir YA je

zupervaca

 
Citar1) ¿DWORD?... ¿qué tipo de dato es eso?
son 4 bytes

Citar2) ¿Se supone que tengo que crear una nueva superficie (destino)?
no puedes escalar la superficie origen ya que es de donde estas leyendo datos y se pisarian
a = b
b = a <-- a = b (nooo) perdemos a!!

Citar3) ¿Cual es la filosofia al escalar algo?, ¿en que consiste?, ¿multiplicar los 4 vertices de la superficie, hacerlo con arreglos,...?, ¿cómo se hace entonces?
para escalar superficies debes repetir puntos o saltarlos, esto es lo que se hacia antiguamente, pero creaba pixelaciones muy grandes, segun ivan aumentando los mhz se fueron usando interpolaciones para crear escalados mas suaves, con la llegada de la aceleracion por hard con las tarjetas graficas el escalado mejoro mucho ya que se empezo a usar la filtracion anisotropica, esta a 16x casi hace un escalado perfecto, te recomiendo mirar lo que dice TheAzazel

Ray

 Antes de explicar como copiar surfaces me gustaría extenderme un poco en el tema del DWORD.

DWORD (unsigned int) son 4 bytes, o lo que es lo mismo un valor de 32 bits, cada byte sirve para guardar un componente de color diferente alfa, rojo, verde, y azul, por lo tanto un color ARGB puede ser guardado en un DWORD con lo que el valor de un pixel podria quedar asi.

0x00FFFFFF blanco
0x00FF0000 rojo
0x0000FF00 verde
0xFF0000FF azul con transparencia
0x00FF8000 naranja
0x80FFFF00 amarillo con media transparencia

si la imagen es de 16 bits se debera usar un WORD (unsigned short), de manera que podemos usar 1 bit para la componente alfa y 5 bits para cada color 1+5x3= 16 bits, tambien se podria usar un color que no tenga alfa usando 5 bits para el color rojo, 6 para el verde y 5 para el azul, el bit extra se le añade el verde porque es el color mas sensible al ojo humano.

Para acceder a las surfaces directamente hay que bloquearlas antes y desbloquearlas al final con SDL_LockSurface y SDL_UnlockSurface y a los memoria se accede mediante un puntero de la propia surface concretamente surface->pixels.

Entonces para copiar una imagen a otra pixel a pixel.

1- se crea la surface destino
2- se bloquean las surfaces origen y destino
3- se copian los pixels de una a otra
4- se desbloquean las surfaces

si queremos copiarla entera y suponiendo que son del mismo tamaño con memcpy copiando todos los bytes que serían el ancho por el alto por el numero de bytes de cada pixel, en este caso 4 por ser de tipo DWORD.

int numBytes=surfaceOrigen->w * surfaceOrigen->h * sizeof(DWORD);
memcpy(surfaceDestino->pixels, surfaceOrigen->pixels, numBytes);

para copiar pixels individuales no podemos usar el puntero surface->pixels directamente porque este es de tipo VOID por lo que mejor es crear un puntero que apunte a los pixels, de tipo DWORD si los pixels son de 32 bits y WORD si son de 16 bits.

DWORD *pPixelsOrigen=(DWORD)surfaceOrigen->pixels;
DWORD *pPixelsDestino=(DWORD)surfaceDestino->pixels;

ahora si quiero copiar el pixel de la columna 30 y la fila 20 de una superficie a otra:

*(pPixelsDestino + 20 * surfaceDestino->w + 30) = *(pPixelsOrigen + 20 * surfaceOrigen->w + 30)

también se podría copiar entera mediante un bucle anidado

for (int y=0;y    for (int x=0;x         *(pPixelsDestino + y * surfaceDestino->w + x) = *(pPixelsOrigen + y * surfaceOrigen->w + x)
        }
   }    

aunque para no tener que calcular la posición del puntero en cada momento es mejor irlos incrementando

for (int y=0;y    for (int x=0;x         *(pPixelsDestino++) = *(pPixelsOrigen++);
        }
   }    

para escalar a la mitad habría que saltarse los pixels de origen de 2 en 2

for (int y=0;y    for (int x=0;x         *(pPixelsDestino++) = *(pPixelsOrigen+=2);  // salto de 2 en 2 columnas
        }
   pPixelsOrigen+=surfaceOrigen.w; // salto de 2 en 2 filas
   }    

para escalar a un valor mayor o a otro valor normalmente hay que usar floats como indice en lugar de incrementar los punteros, como en el ejemplo de HqH.

Y otra cosa importante es comprobar antes que el pitch (que tambien es una propiedad de la propia surface) es del mismo tamaño que el ancho, aunque mejor dejo la explicación para otro momento porque me estoy alargando mucho.

no se si he cometido algún error en estos ejemplos porque los estoy haciendo de carrerilla sin probarlos, pero yo creo sirven de ejemplo para que te puedas hacer una idea de como operar con surfaces manualmente.

De todas maneras lo mejor es aprender a programar la tarjeta gráfica con DirectX u OpenGL, cuando lo controlas todo es mas fácil porque se hace por hardware, mira que sencillo es toda esta movida con DirectX, a toda velocidad y con filtrado para que quede una imagen perfecta y suavizada sin bailoteo de pixels sea cual sea el factor de escala y sin preocuparse de nada.

StretchRect(pSurfaceOrigen, NULL, pSurfaceDestino, NULL, D3DTEXF_LINEAR);

El único inconveniente del DX es que hay que aprender primero una base muy solida y practicar y probar muchas cosas hasta que funciona, pero una vez que lo tienes todo es mucho más sencillo.

un saludo.

J_F_NASH

 ¡De veras que se agradece la detallada explicación Ray!. Bastantes cosas he sacado en claro.
Y a TheAzazel gracias por ponerle nombre a mi problema: "Algoritmos de interpolación".
Sabiendo lo que busco ya es otra cosa :rolleyes: .

He logrado hacer un escalado moviendo y duplicando píxeles de una superficie. El problema viene cuando lo hago con varias y en cada ciclo.
Os podeis imaginar lo lento que va todo.

Finalmente he logrado utilizar "rotozoom" para escalar. Pero con identico problema (tremenda lentitud en la animacion). "A bote pronto" no se como solucionar este problema.
No os pongo el código porque es un caos pero si como lo estoy haciendo.
Consiste en un juego de una pantalla. De momento solo hay un PLAYER (en una surface de 40x40) y un FONDO (en una surface que cubre toda la pantalla (320x240)).
AMBOS se estan dibujando en cada ciclo. Con una perfecta fluidez y sin parpadeo alguno en la pantalla.


.
.
.
pantalla = SDL_SetVideoMode(ancho, alto, 8, SDL_SWSURFACE | SDL_DOUBLEBUF | SDL_FULLSCREEN);
.
.
.
while(salir == false){
fondo(); //Dentro hay una llamada a la funcion dibujaGrafico
player();//Dentro estan los movimientos y varias llamadas a dibujaGrafico
       SDL_Flip(pantalla);
}
.
.
.
void dibujaGrafico(SDL_Surface *screen, SDL_Surface *sprite, int _x, int _y, int comienzoX, int comienzoY, int w, int h){
SDL_Rect rect, seccion;
rect.x = _x;
rect.y = _y;
seccion.x = comienzoX;
seccion.y = comienzoY;
sprite->w = w;
sprite->h = h;
SDL_BlitSurface(sprite, &seccion, screen, &rect);
}


Total, si llamo a la funcion zoomSurface() dentro de dibujaGrafico() me escalará todos los gráficos que vaya colocando en pantalla. Uno a uno Y EN CADA CICLO. He aquí el problema de lentitud.
Pero es que si lo llamo fuera del bucle principal ¡solo me escalará en el primer ciclo!.

Tambien probé esto otro. Pienso que es lo más lógico:


panScaled = zoomSurface(pantalla,2,2,0);
while(salir == false){
 fondo();
 player();
       SDL_Flip(panScaled);
   }


Pero no parece funcionar. Digo parece porque solo obtengo un pantallazo negro, aunque no peta.

Bueno no se si me he explicado bien. Seguiré probando.


S2.

TheAzazel

 Escalar por software todos los sprites cada frame en un juego es una locura..normal que te vaya lento :P

Puedes hacerte un array de punteros a surfaces previamente escaladas que generas justo despues de cargar la imagen original... y no olvides liberarlos todos la final :P. De este modo, tendras lo que buscas a costa de "gastar" memoria en ello... cosa poco preocupante hoy en dia...

Saludos

Ray

 me gustaría corregir un fallo en uno de los ejemplos que he puesto y que puede dar lugar a confusión a alguien que lo lea, concretamente al convertir punteros VOID a DWORD:

Citar
DWORD *pPixelsOrigen=(DWORD)surfaceOrigen->pixels;
DWORD *pPixelsDestino=(DWORD)surfaceDestino->pixels;

así sería lo correcto (con el asterisco):

Citar
DWORD *pPixelsOrigen=(DWORD *)surfaceOrigen->pixels;
DWORD *pPixelsDestino=(DWORD *)surfaceDestino->pixels;

Ray

 
Citar
Total, si llamo a la funcion zoomSurface() dentro de dibujaGrafico() me escalará todos los gráficos que vaya colocando en pantalla. Uno a uno Y EN CADA CICLO. He aquí el problema de lentitud.

Pero es que si lo llamo fuera del bucle principal ¡solo me escalará en el primer ciclo!.

Si vas a escalar las surfaces al comienzo no habrá mucho problema de velocidad, aunque si de calidad como no uses algun algoritmo de interpolación o filtrado. Pero si vas a escalar por software antes de dibujar cada objeto casi te puedo asegurar que es imposible a efectos prácticos.

Citar
Tambien probé esto otro. Pienso que es lo más lógico:

CODE

panScaled = zoomSurface(pantalla,2,2,0);
while(salir == false){
fondo();
player();
       SDL_Flip(panScaled);
   }


Pero no parece funcionar. Digo parece porque solo obtengo un pantallazo negro, aunque no peta.

Se supone que quieres escalar el fondo de pantalla al principio ya que este solo es de 320x200 ¿no?, es perfectamente posible, debería funcionar. en este caso lo mejor es cargar la imagen en una surface "pantallaOriginal" de 320x240 y copiarla escalando a la surface "pantalla" (de 640x480 por ejemplo) que vas a usar, aunque van a aparer puntos gordos si no promedias los pixels cercanos.

J_F_NASH

 
Cita de: "Ray"
Citar
Tambien probé esto otro. Pienso que es lo más lógico:

CODE

panScaled = zoomSurface(pantalla,2,2,0);
while(salir == false){
fondo();
player();
       SDL_Flip(panScaled);
   }

Pero no parece funcionar. Digo parece porque solo obtengo un pantallazo negro, aunque no peta.

Se supone que quieres escalar el fondo de pantalla al principio ya que este solo es de 320x200 ¿no?, es perfectamente posible, debería funcionar. en este caso lo mejor es cargar la imagen en una surface "pantallaOriginal" de 320x240 y copiarla escalando a la surface "pantalla" (de 640x480 por ejemplo) que vas a usar, aunque van a aparer puntos gordos si no promedias los pixels cercanos.
Los puntos gordos no me importan en absoluto, de hecho, es lo que, al menos en parte, trato de conseguir (juego tipo amstrad & spectrum).
El código anterior ¿estas seguro de que debería funcionar?. Me refiero a:

panScaled = zoomSurface(pantalla,2,2,0);

while(salir == false)
{
fondo();
player();
SDL_Flip(panScaled);
}


Primero escalar la superficie original (donde no hay nada de nada) y luego dibujar ahí. ¿Lo que voy dibujando ahí será escalado?. Es que ya te digo, no hay mensaje de error pero no aparece nada en pantalla.


S2






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.