Added Area loading/saving from/to string
[Lilanci.git] / gr.c
blob59ccaf0233ca019b93fe1307887c36f77c939e7c
1 #include "config.h"
2 #include "gr.h"
3 #include <string.h>
4 #include <stdlib.h>
5 #include <limits.h>
7 #define TRECT(x,y,w,h, tw, th) glBegin(GL_TRIANGLE_STRIP);glTexCoord2f(0.0,h/th);glVertex2f(x, y+1*h);glTexCoord2f(0.0,0.0);glVertex2f(x, y);glTexCoord2f(w/tw,h/th);glVertex2f(x+1*w, y+1*h);glTexCoord2f(w/tw,0.0);glVertex2f(x+1*w, y);glEnd();
10 unsigned int occupied=0;
12 typedef struct{
13 GLuint *surfaces;
14 unsigned int allocated;
15 }SurfaceList;
17 SurfaceList gsprite_surfaces;
18 int AddSurface(GLuint s);
19 void RemoveSurface(GLuint s);
20 void RemoveSID(int SID);
21 void FlushSpriteSurfaces();
22 int SetVideoMode(int width, int height);
23 SDL_Surface *gscreen;
25 typedef struct{
26 Sprite **sprites;
27 unsigned int allocated;
28 }SpriteList;
30 SpriteList gsprites;
31 int AddSprite(Sprite *s);
32 void RemoveSprite(Sprite *s);
34 typedef struct{
35 Sprite *sprite;
36 double x;
37 double y;
38 int z; //z
39 }SpriteQueueItem;
41 typedef struct{
42 SpriteQueueItem *items;
43 unsigned int allocated;
44 unsigned int next_id;
45 }SpriteQueue;
46 SpriteQueue gsprite_queue;
47 SpriteQueue gsprite_queue_sorted;
48 void FlushSpriteQueue();
49 void RotateSurface(int SID, int rotation);
51 int ginvalidsprites;
53 GLuint load_svg (char *file, int width, int height, double *pdw, double *pdh);
55 int G2SX(double x);
56 int G2SY(double y);
58 int G2SX(double x){
59 return (x/(double)SCREEN_WIDTH)*GetConfig().screen_width;
61 int G2SY(double y){
62 return (y/(double)SCREEN_HEIGHT)*GetConfig().screen_height;
65 int SetVideoMode(int width, int height){
66 SDL_Rect **modes;
67 int ibest;
68 int bestdif;
69 int i;
70 Uint32 flags;
72 Config config=GetConfig();
74 SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
75 flags = SDL_OPENGL ;
76 if(config.fullscreen)
77 flags = flags |SDL_FULLSCREEN;
78 /* Get available fullscreen/hardware modes */
79 modes=SDL_ListModes(NULL, flags);
81 /* Check if there are any modes available */
82 if(modes == (SDL_Rect **)0){
83 printf("No modes available!\n");
84 exit(-1);
87 /* Check if our resolution is restricted */
88 if(modes == (SDL_Rect **)-1){
89 printf("All resolutions available.\n");
90 gscreen = SDL_SetVideoMode(config.screen_width, config.screen_height, 0, flags);
92 else{
93 /* Print valid modes */
94 printf("Available Modes\n");
95 bestdif = abs((modes[0]->w - config.screen_width) + (modes[0]->h - config.screen_height));
96 ibest = 0;
97 for(i=0;modes[i];++i){
98 printf(" %d x %d\n", modes[i]->w, modes[i]->h);
99 if(bestdif > abs((modes[i]->w - config.screen_width) + (modes[i]->h - config.screen_height))){
100 bestdif = abs((modes[i]->w - config.screen_width) + (modes[i]->h - config.screen_height));
101 ibest = i;
104 printf("Using: %d x %d\n", modes[ibest]->w, modes[ibest]->h);
105 gscreen = SDL_SetVideoMode(modes[ibest]->w, modes[ibest]->h, 0, flags);
106 config.screen_height = modes[ibest]->h;
107 config.screen_width = modes[ibest]->w;
108 SetConfig(config);
112 int AddSurface(GLuint s){ //returns -1 on error, SID if success
113 int i;
114 GLuint *tmp;
115 for(i=0;i<gsprite_surfaces.allocated;i++)
116 if( gsprite_surfaces.surfaces[i]==s ){
117 return i;
120 for(i=0;i<gsprite_surfaces.allocated;i++)
121 if( gsprite_surfaces.surfaces[i]==0 ){
122 gsprite_surfaces.surfaces[i]=s;
123 return i;
125 i=gsprite_surfaces.allocated;
126 tmp = (GLuint*) realloc(gsprite_surfaces.surfaces, sizeof(GLuint)*i*2); //if the allocated space is not wide enough, then allocate double space than previous
127 if(tmp==0)
128 return -1;
129 gsprite_surfaces.surfaces=tmp;
130 memset(&gsprite_surfaces.surfaces[i],0, sizeof(GLuint)*i); //zero newly allocated space
131 gsprite_surfaces.allocated=i*2;
132 gsprite_surfaces.surfaces[i]=s;
133 return i;
136 void RemoveSurface(GLuint s){
137 int i,iwannakillyoubitch;
139 iwannakillyoubitch=1;
140 for(i=0;i<gsprite_surfaces.allocated;i++){
141 if( gsprite_surfaces.surfaces[i]==s ){
142 gsprite_surfaces.surfaces[i]=0;
144 if( i>=gsprite_surfaces.allocated/2 && iwannakillyoubitch && gsprite_surfaces.surfaces[i]!=0 )
145 iwannakillyoubitch=0;
148 if(iwannakillyoubitch){
149 gsprite_surfaces.allocated /=2;
150 gsprite_surfaces.surfaces = (GLuint *)realloc(gsprite_surfaces.surfaces, sizeof(GLuint)*gsprite_surfaces.allocated);//TODO:check return value
154 void RemoveSID(int SID){
155 int i,iwannakillyoubitch;
157 iwannakillyoubitch=1;
158 for(i=0;i<gsprite_surfaces.allocated;i++){
159 if( i==SID ){
160 gsprite_surfaces.surfaces[i]=0;
162 if( i>=gsprite_surfaces.allocated/2 && iwannakillyoubitch && gsprite_surfaces.surfaces[i]!=0 )
163 iwannakillyoubitch=0;
166 if(iwannakillyoubitch){
167 gsprite_surfaces.allocated /=2;
168 gsprite_surfaces.surfaces = (GLuint *)realloc(gsprite_surfaces.surfaces, sizeof(GLuint)*gsprite_surfaces.allocated);//TODO:check return value
172 int AddSprite(Sprite *s){//returns 0 on error, Sprite ID on success
173 int i;
174 Sprite **tmp;
175 for(i=0;i<gsprites.allocated;i++)
176 if( gsprites.sprites[i]==s ){
177 return i;
180 for(i=0;i<gsprites.allocated;i++)
181 if( gsprites.sprites[i]==0 ){
182 gsprites.sprites[i]=s;
183 return i;
185 i=gsprites.allocated;
186 tmp=(Sprite **)realloc(gsprites.sprites, sizeof(Sprite *)*i*2); //if the allocated space is not wide enough, then allocate double space than previous
187 if(tmp==0)
188 return 0;
189 gsprites.sprites=tmp;
190 memset(&gsprites.sprites[i],0, sizeof(Sprite *)*i); //zero newly allocated space
191 gsprites.allocated=i*2;
192 gsprites.sprites[i]=s;
193 return i;
196 void RemoveSprite(Sprite *s){
197 int i,iwannakillyoubitch;
199 iwannakillyoubitch=1;
200 for(i=0;i<gsprites.allocated;i++){
201 if( gsprites.sprites[i]==s ){
202 gsprites.sprites[i]=0;
204 if( i>=gsprites.allocated/2 && iwannakillyoubitch && gsprites.sprites[i]!=0 )
205 iwannakillyoubitch=0;
208 if(iwannakillyoubitch){
209 gsprites.allocated /=2;
210 gsprites.sprites = (Sprite **) realloc(gsprites.sprites, sizeof(Sprite *)*gsprites.allocated);//TODO:check return value
214 int GrKill(){
215 Config config;
216 int i;
218 FlushSpriteQueue();
219 FlushSpriteSurfaces();
221 config=GetConfig();
222 free(gsprite_surfaces.surfaces);
223 gsprite_surfaces.surfaces=0;
226 for(i=0; i<gsprites.allocated; i++)
227 if(gsprites.sprites[i]){
228 free(gsprites.sprites[i]->fname);
229 free(gsprites.sprites[i]);
230 gsprites.sprites[i]=0;
233 gsprites.allocated = 0;
236 SetVideoMode(config.screen_width, config.screen_height);
238 return 0;
241 int GrInit(){
242 Config config;
244 config=GetConfig();
245 gsprite_surfaces.surfaces = (GLuint*) calloc(2, sizeof(GLuint));
246 if( gsprite_surfaces.surfaces == 0 )
247 return 1;
249 gsprite_surfaces.allocated = 2;
250 memset(gsprite_surfaces.surfaces, 0, gsprite_surfaces.allocated * sizeof(GLuint));
252 gsprites.sprites = (Sprite**) calloc(2,sizeof(Sprite*));
253 if( gsprites.sprites == 0 )
254 return 2;
255 memset(gsprites.sprites, 0, gsprites.allocated * sizeof(Sprite*));
256 gsprites.allocated = 2;
257 ginvalidsprites=1;
259 gsprite_queue.allocated=2;
260 gsprite_queue.items=(SpriteQueueItem*) malloc(sizeof(SpriteQueueItem)*2);
261 gsprite_queue.next_id=0;
263 SetVideoMode(config.screen_width, config.screen_height);
265 glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
266 glClearDepth(1.0);
267 glDisable(GL_DEPTH_TEST);
268 //glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
269 glEnable(GL_BLEND);
270 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
271 //glEnable(GL_ALPHA_TEST);
272 //glAlphaFunc(GL_GREATER, 0);
273 SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
275 glViewport(0, 0, config.screen_width, config.screen_height);
276 glMatrixMode(GL_PROJECTION);
277 glLoadIdentity();
279 glOrtho(0.0f,config.screen_width, config.screen_height,0.0f,-1.0f,1.0f);
281 glMatrixMode(GL_MODELVIEW);
282 glLoadIdentity();
284 return 0;
287 void FlushSpriteSurfaces(){
288 int i;
290 for(i=0;i<gsprite_surfaces.allocated;i++){
291 if(gsprite_surfaces.surfaces[i]){
292 glDeleteTextures(1, &gsprite_surfaces.surfaces[i]);
293 gsprite_surfaces.surfaces[i]=0;
296 occupied =0;
299 int RegenerateSprites(){
300 int i,SID;
301 Config config;
302 Sprite *tmpsprite;
303 GLuint tmpsurface;
304 int xx,yy;
306 config=GetConfig();
309 FlushSpriteSurfaces();
311 SetVideoMode(config.screen_width, config.screen_height);
314 for(i=0;i<gsprites.allocated;i++)
315 if(gsprites.sprites[i]){
316 tmpsprite=gsprites.sprites[i];
317 xx = (tmpsprite->width/SCREEN_WIDTH)*config.screen_width;
318 yy = (tmpsprite->height/SCREEN_HEIGHT)*config.screen_height;
320 tmpsurface=load_svg(tmpsprite->fname, xx, yy, &tmpsprite->twidth, &tmpsprite->theight);
322 SID=AddSurface(tmpsurface);
323 if(SID==-1){
324 return 1;
326 RotateSurface(SID,tmpsprite->rotation);
330 ginvalidsprites=0;
331 return 0;
333 void InvalidateSprites(){
334 printf("Invalid\n");
335 ginvalidsprites=1;
338 void FlushSpriteQueue(){
339 int i;
341 for(i=0;i<gsprite_queue.allocated;i++){
342 gsprite_queue.items[i].sprite = 0;
344 if(gsprite_queue.next_id < gsprite_queue.allocated/2){
345 //TODO:dopsat zmenseni fronty
347 gsprite_queue.next_id = 0;
351 void ZSortSpriteQueue(){
352 int minz, lastminz, found;
353 int i;
354 lastminz = INT_MIN;
357 found = 1;
358 gsprite_queue_sorted.next_id= 0;
359 while(found){
360 found = 0;
361 minz = INT_MAX;
362 //we seek for smallest z bigger than z in last iteration
363 for(i=0; i<gsprite_queue.next_id; i++){
364 if( gsprite_queue.items[i].z < minz && gsprite_queue.items[i].z > lastminz){
365 found = 1;
366 minz = gsprite_queue.items[i].z;
369 if(!found)
370 break;
371 //now we copy all items on z==minz
372 for(i=0; i < gsprite_queue.next_id; i++){
373 if(gsprite_queue.items[i].z == minz){
374 memcpy(&gsprite_queue_sorted.items[gsprite_queue_sorted.next_id], &gsprite_queue.items[i], sizeof(SpriteQueueItem));
375 gsprite_queue_sorted.next_id++;
378 lastminz=minz;
383 int DrawSprites(){
384 int i;
385 SDL_Rect dest;
386 SDL_Rect src;
387 GLuint ds;
388 Sprite *dspr;
390 if(ginvalidsprites){
391 printf("invalid\n");
392 RegenerateSprites();
395 gsprite_queue_sorted.allocated = gsprite_queue.allocated;
396 gsprite_queue_sorted.next_id = 0;
397 gsprite_queue_sorted.items = malloc(sizeof(SpriteQueueItem)* gsprite_queue_sorted.allocated);
398 ZSortSpriteQueue();
400 for(i=0;i<gsprite_queue_sorted.next_id;i++)
401 if(gsprite_queue_sorted.items[i].sprite){
402 dspr = gsprite_queue_sorted.items[i].sprite;
403 ds = gsprite_surfaces.surfaces[dspr->SID];
404 dest.x = G2SX(gsprite_queue_sorted.items[i].x);
405 dest.y = G2SY(gsprite_queue_sorted.items[i].y);
406 dest.w = G2SX(gsprite_queue_sorted.items[i].sprite->width);
407 dest.h = G2SX(gsprite_queue_sorted.items[i].sprite->height);
408 src.x = 0;
409 src.y = 0;
410 src.w = G2SX(gsprite_queue_sorted.items[i].sprite->width);
411 src.h = G2SX(gsprite_queue_sorted.items[i].sprite->height);
412 glBindTexture(GL_TEXTURE_2D, ds);
413 glEnable(GL_TEXTURE_2D);
414 TRECT(dest.x, dest.y, dest.w, dest.h, dspr->twidth, dspr->theight);
415 glDisable(GL_TEXTURE_2D);
418 gsprite_queue_sorted.allocated = 0;
419 gsprite_queue_sorted.next_id = 0;
420 free(gsprite_queue_sorted.items);
421 gsprite_queue_sorted.items = 0;
422 FlushSpriteQueue();
424 SDL_GL_SwapBuffers();
426 return 0;
429 int ClrScr(){
430 //SDL_FillRect(gscreen, 0, 0x0);
433 Sprite* LoadSpriteSVG(char *fname, double width, double height){
434 Sprite *sprite;
435 GLuint surface;
436 int xx,yy;
437 Config conf=GetConfig();
439 xx = width*(double)conf.screen_width/SCREEN_WIDTH;
440 yy = height*(double)conf.screen_height/SCREEN_HEIGHT;
443 sprite = (Sprite*)malloc(sizeof(Sprite));
444 surface = load_svg(fname, xx, yy, &sprite->twidth, &sprite->theight);
445 sprite->fname = malloc(sizeof(char)*(strlen(fname)+2));
446 strcpy(sprite->fname, fname);
447 sprite->width = width;
448 sprite->height = height;
449 sprite->rotation = 0;
450 sprite->SID = AddSurface(surface);
451 AddSprite(sprite);
453 return sprite;
455 void DestroySprite(Sprite *sprite){
456 int i;
458 RemoveSprite(sprite);
459 free(sprite->fname);
460 sprite->fname=0;
461 glDeleteTextures(1, &gsprite_surfaces.surfaces[sprite->SID]);
462 RemoveSID(sprite->SID);
463 free(sprite);
466 Sprite* CopySprite(Sprite *old){
467 printf("CopySprite not Implemented\n");
469 return 0;
473 void RotateSurface(int SID, int rotation){
474 int i,x,y;
475 int size;
477 fprintf(stderr, "Rotation of sprites not implemented :(\n");
480 void RotateClockwise(Sprite *sprite){
481 RotateSurface(sprite->SID,1);
482 sprite->rotation=1;
485 void QueueDrawSprite(Sprite *sprite, double x, double y, int z){
486 SpriteQueueItem *item;
487 if(gsprite_queue.next_id >=gsprite_queue.allocated){
488 item = (SpriteQueueItem *) realloc(gsprite_queue.items, sizeof(SpriteQueueItem)*gsprite_queue.allocated*2);
489 if(item == 0){
490 fprintf(stderr, "Can't allocate enough memory for sprite queue\n");
491 return;
493 gsprite_queue.allocated *= 2;
494 gsprite_queue.items = item;
497 item = &gsprite_queue.items[gsprite_queue.next_id];
498 item->sprite = sprite;
499 item->x = x;
500 item->y = y;
501 item->z = z;
503 gsprite_queue.next_id ++;
507 unsigned int closestpoweroftwo(unsigned int i){
508 int p;
509 p=0;
510 while(i){
511 i=i>>1;
512 p++;
514 return 1<<(p-0);
518 //load_svg borrowed from cairo demo
519 #include <stdio.h>
520 #include <stdlib.h>
521 #include <sys/types.h>
522 #include <cairo.h>
523 #include <svg.h>
524 #include <svg-cairo.h>
526 /* load_svg: This is what you're probably interested in!
527 * -----------------------------------------------------
528 * If width and height are greater than 0, the image will
529 * be scaled to that size. wscale and hscale would be ignored.
531 * If width and height are less than 1, wscale and hscale will
532 * resize the width and the height to the specified scales.
534 * If width and height are less than 1, and wscale and hscale
535 * are either 0 or 1, then the image will be loaded at it's
536 * natural size.
538 * See main() for examples.
540 GLuint load_svg (char *file, int width, int height, double *pdw, double *pdh) {
541 svg_cairo_t *scr;
542 int bpp;
543 int btpp;
544 unsigned int rwidth;
545 unsigned int rheight;
546 unsigned int pw,ph; //power of two dimensions
547 double wscale,hscale;
548 Config config=GetConfig();
550 // Create the SVG cairo stuff.
551 svg_cairo_create(&scr);
552 svg_cairo_parse (scr, file);
554 svg_cairo_get_size (scr, &rwidth, &rheight);
555 *pdw = closestpoweroftwo(width);
556 *pdh = closestpoweroftwo(height);
557 width = width>>config.texture_lod;
558 height = height>>config.texture_lod;
560 /*Calculate final width and height of our surface based on the parameters passed */
561 if (width > 0) {
562 wscale=(float)width/(float)rwidth;
563 } else {
564 width=(int)(rwidth*wscale);
566 if (height > 0) {
567 hscale=(float)height/(float)rheight;
568 } else {
569 height=(int)(rheight*hscale);
573 if(hscale>wscale){
574 hscale=wscale;
575 }else{
576 wscale=hscale;
579 /* We will create a CAIRO_FORMAT_ARGB32 surface. We don't need to match
580 the screen SDL format, but we are interested in the alpha bit */
581 bpp=32; /*bits per pixel*/
582 btpp=4; /*bytes per pixel*/
584 /* scanline width */
585 int stride=width * btpp;
587 /* Allocate an image */
588 unsigned char *image=calloc(stride*height, 1);
590 /* Create the cairo surface with the adjusted width and height */
591 cairo_surface_t *cairo_surface;
592 cairo_surface = cairo_image_surface_create_for_data (image,
593 CAIRO_FORMAT_ARGB32,
594 width, height, stride);
596 cairo_t *cr=cairo_create(cairo_surface);
597 cairo_scale (cr, wscale, hscale);
599 /* Render SVG to our surface */
600 svg_cairo_render (scr, cr);
602 /* Cleanup cairo */
603 cairo_surface_destroy (cairo_surface);
604 cairo_destroy (cr);
606 /*Destroy the svg_cairo structure */
607 svg_cairo_destroy (scr);
609 /*Adjust the SDL surface mask to ARGB, matching the cairo surface created.*/
610 Uint32 rmask, gmask, bmask, amask;
612 rmask = 0x00ff0000;
613 gmask = 0x0000ff00;
614 bmask = 0x000000ff;
615 amask = 0xff000000;
617 /* Create the SDL surface using the pixel data stored. It will automatically be set to use alpha using these mask values */
618 SDL_Surface *sdl_surface=SDL_CreateRGBSurfaceFrom( (void *) image, width, height, bpp, stride, rmask, gmask, bmask, amask);
619 SDL_SetAlpha(sdl_surface, 0, 255);
620 rmask = 0x000000ff;
621 gmask = 0x0000ff00;
622 bmask = 0x00ff0000;
623 amask = 0xff000000;
624 pw = closestpoweroftwo(width);
625 ph = closestpoweroftwo(height);
626 SDL_Surface *sdl_rgba_surface=SDL_CreateRGBSurface( SDL_SWSURFACE, pw, ph, bpp, rmask, gmask, bmask, amask);
627 SDL_BlitSurface(sdl_surface, 0, sdl_rgba_surface, 0);
628 SDL_FreeSurface(sdl_surface);
629 cfree(image);
631 SDL_LockSurface(sdl_rgba_surface);
633 GLuint tid;
634 glGenTextures(1, &tid);
635 glBindTexture(GL_TEXTURE_2D, tid);
636 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // scale linearly when image bigger than texture
637 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // scale linearly when image smalled than texture
638 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pw, ph, 0, GL_RGBA, GL_UNSIGNED_BYTE, sdl_rgba_surface->pixels);
639 SDL_UnlockSurface(sdl_rgba_surface);
640 SDL_FreeSurface(sdl_rgba_surface);
641 occupied += pw*ph;
642 printf("Occupied: %u\n", occupied);
644 return tid;