Merge branch 'feature/handle-quit-event'
[jumpnbump.git] / sdl / gfx.c
blob4cea7e5279f909332562d10b83b7f00f96a7db45
1 /*
2 * gfx.c
3 * Copyright (C) 1998 Brainchild Design - http://brainchilddesign.com/
4 *
5 * Copyright (C) 2001 Chuck Mason <cemason@users.sourceforge.net>
7 * Copyright (C) 2002 Florian Schulze <crow@icculus.org>
9 * This file is part of Jump'n'Bump.
11 * Jump'n'Bump is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * Jump'n'Bump is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "globals.h"
27 #include "SDL_endian.h"
28 #include "filter.h"
30 #ifdef _MSC_VER
31 #include "jumpnbump32.xpm"
32 #elif __APPLE__
33 #include "jumpnbump128.xpm"
34 #else
35 #include "jumpnbump64.xpm"
36 #endif
37 SDL_Surface *icon;
39 int screen_width=400;
40 int screen_height=256;
41 int screen_pitch=400;
42 int scale_up=0;
43 int dirty_block_shift=4;
45 static SDL_Surface *jnb_surface;
46 static int fullscreen = 0;
47 static int vinited = 0;
48 static void *screen_buffer[2];
49 static int drawing_enable = 0;
50 static void *background = NULL;
51 static int background_drawn;
52 static void *mask = NULL;
53 static int dirty_blocks[2][25*16*2];
55 static SDL_Surface *load_xpm_from_array(char **xpm)
57 #define NEXT_TOKEN { \
58 while ((*p != ' ') && (*p != '\t')) p++; \
59 while ((*p == ' ') || (*p == '\t')) p++; }
61 SDL_Surface *surface;
62 char *p;
63 int width;
64 int height;
65 int colors;
66 int images;
67 int color;
68 int pal[256];
69 int x,y;
71 p = *xpm++;
73 width = atoi(p);
74 if (width <= 0)
75 return NULL;
76 NEXT_TOKEN;
78 height = atoi(p);
79 if (height <= 0)
80 return NULL;
81 NEXT_TOKEN;
83 colors = atoi(p);
84 if (colors <= 0)
85 return NULL;
86 NEXT_TOKEN;
88 images = atoi(p);
89 if (images <= 0)
90 return NULL;
92 surface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
93 if (!surface)
94 return NULL;
96 SDL_SetColorKey(surface, SDL_SRCCOLORKEY, SDL_MapRGBA(surface->format, 0, 0, 0, 0));
97 while (colors--) {
98 p = *xpm++;
100 color = *p++;
101 NEXT_TOKEN;
103 if (*p++ != 'c') {
104 SDL_FreeSurface(surface);
105 return NULL;
107 NEXT_TOKEN;
109 if (*p == '#')
110 pal[color] = strtoul(++p, NULL, 16) | 0xff000000;
111 else
112 pal[color] = 0;
115 y = 0;
116 while (y < height) {
117 int *pixels;
119 p = *xpm++;
121 pixels = (int *)&((char *)surface->pixels)[y++ * surface->pitch];
122 x = 0;
123 while (x < width) {
124 Uint8 r,g,b,a;
126 if (*p == '\0') {
127 SDL_FreeSurface(surface);
128 return NULL;
130 r = (pal[(int)*p] >> 16) & 0xff;
131 b = (pal[(int)*p] & 0xff);
132 g = (pal[(int)*p] >> 8) & 0xff;
133 a = (pal[(int)*p] >> 24) & 0xff;
134 pixels[x] = SDL_MapRGBA(surface->format, r, g, b, a);
135 x++;
136 p++;
140 return surface;
143 unsigned char *get_vgaptr(int page, int x, int y)
145 assert(drawing_enable==1);
147 return (unsigned char *)screen_buffer[page] + (y*screen_pitch)+(x);
151 void set_scaling(int scale)
153 if (scale==1) {
154 screen_width=800;
155 screen_height=512;
156 scale_up=1;
157 dirty_block_shift=5;
158 screen_pitch=screen_width;
159 } else {
160 screen_width=400;
161 screen_height=256;
162 scale_up=0;
163 dirty_block_shift=4;
164 screen_pitch=screen_width;
168 void open_screen(void)
170 int lval = 0;
171 int flags;
173 lval = SDL_Init(SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_JOYSTICK);
174 if (lval < 0) {
175 fprintf(stderr, "SDL ERROR: %s\n", SDL_GetError());
176 exit(EXIT_FAILURE);
179 flags = SDL_SWSURFACE;
180 if (fullscreen)
181 flags |= SDL_FULLSCREEN;
182 jnb_surface = SDL_SetVideoMode(screen_width, screen_height, 8, flags);
184 if (!jnb_surface) {
185 fprintf(stderr, "SDL ERROR: %s\n", SDL_GetError());
186 exit(EXIT_FAILURE);
189 if(fullscreen)
190 SDL_ShowCursor(0);
191 else
192 SDL_ShowCursor(1);
194 SDL_WM_SetCaption("Jump'n'Bump","");
196 icon=load_xpm_from_array(jumpnbump_xpm);
197 if (icon==NULL) {
198 printf("Couldn't load icon\n");
199 } else {
200 SDL_WM_SetIcon(icon,NULL);
203 vinited = 1;
205 memset(dirty_blocks, 0, sizeof(dirty_blocks));
207 screen_buffer[0]=malloc(screen_width*screen_height);
208 screen_buffer[1]=malloc(screen_width*screen_height);
211 dirty_blocks[0]=malloc(sizeof(int)*25*16+1000);
212 dirty_blocks[1]=malloc(sizeof(int)*25*16+1000);
215 return;
219 void fs_toggle()
221 if (!vinited) {
222 fullscreen ^= 1;
223 return;
225 if (SDL_WM_ToggleFullScreen(jnb_surface))
226 fullscreen ^= 1;
229 void exit_fullscreen()
231 if (fullscreen)
232 fs_toggle();
235 void wait_vrt(int mix)
237 return;
241 void clear_page(int page, int color)
243 int i,j;
244 unsigned char *buf = get_vgaptr(page, 0, 0);
246 assert(drawing_enable==1);
248 for (i=0; i<(25*16); i++)
249 dirty_blocks[page][i] = 1;
251 for (i=0; i<screen_height; i++)
252 for (j=0; j<screen_width; j++)
253 *buf++ = color;
257 void clear_lines(int page, int y, int count, int color)
259 int i,j;
261 assert(drawing_enable==1);
263 if (scale_up) {
264 count *= 2;
265 y *= 2;
268 for (i=0; i<count; i++) {
269 if ((i+y)<screen_height) {
270 unsigned char *buf = get_vgaptr(page, 0, i+y);
271 for (j=0; j<screen_width; j++)
272 *buf++ = color;
275 count = ((y+count)>>dirty_block_shift) - (y>>dirty_block_shift) + 1;
276 y >>= dirty_block_shift;
277 for (i=0; i<count; i++)
278 for (j=0; j<25; j++)
279 dirty_blocks[page][(y+i)*25+j] = 1;
283 int get_color(int color, char pal[768])
285 assert(color<256);
286 assert(pal);
287 return SDL_MapRGB(jnb_surface->format, (Uint8)(pal[color*3+0]<<2), (Uint8)(pal[color*3+1]<<2), (Uint8)(pal[color*3+2]<<2));
291 int get_pixel(int page, int x, int y)
293 assert(drawing_enable==1);
295 if (scale_up) {
296 x *= 2;
297 y *= 2;
300 assert(x<screen_width);
301 assert(y<screen_height);
303 return *(unsigned char *)get_vgaptr(page, x, y);
307 void set_pixel(int page, int x, int y, int color)
309 assert(drawing_enable==1);
311 if (scale_up) {
312 x *= 2;
313 y *= 2;
316 assert(x<screen_width);
317 assert(y<screen_height);
319 dirty_blocks[page][(y>>dirty_block_shift)*25+(x>>dirty_block_shift)] = 1;
321 *(unsigned char *)get_vgaptr(page, x, y) = color;
325 void flippage(int page)
327 int x,y;
328 unsigned char *src;
329 unsigned char *dest;
331 assert(drawing_enable==0);
333 SDL_LockSurface(jnb_surface);
334 if (!jnb_surface->pixels) {
336 for (x=0; x<(25*16); x++) {
337 dirty_blocks[0][x] = 1;
338 dirty_blocks[1][x] = 1;
341 return;
343 dest=(unsigned char *)jnb_surface->pixels;
344 src=screen_buffer[page];
345 for (y=0; y<screen_height; y++) {
346 for (x=0; x<25; x++) {
347 int count;
348 int test_x;
350 count=0;
351 test_x=x;
352 while ( (test_x<25) && (dirty_blocks[page][(y>>dirty_block_shift)*25+test_x]) ) {
353 count++;
354 test_x++;
356 if (count) {
357 memcpy( &dest[y*jnb_surface->pitch+(x<<dirty_block_shift)],
358 &src[y*screen_pitch+((x<<dirty_block_shift))],
359 ((16<<dirty_block_shift)>>4)*count);
361 x = test_x;
364 memset(&dirty_blocks[page], 0, sizeof(int)*25*16);
365 SDL_UnlockSurface(jnb_surface);
366 SDL_Flip(jnb_surface);
370 void draw_begin(void)
372 assert(drawing_enable==0);
374 drawing_enable = 1;
375 if (background_drawn == 0) {
376 if (background) {
377 put_block(0, 0, 0, JNB_WIDTH, JNB_HEIGHT, background);
378 put_block(1, 0, 0, JNB_WIDTH, JNB_HEIGHT, background);
379 } else {
380 clear_page(0, 0);
381 clear_page(1, 0);
383 background_drawn = 1;
388 void draw_end(void)
390 assert(drawing_enable==1);
392 drawing_enable = 0;
396 void setpalette(int index, int count, char *palette)
398 SDL_Color colors[256];
399 int i;
401 assert(drawing_enable==0);
403 for (i = 0; i < count; i++) {
404 colors[i+index].r = palette[i * 3 + 0] << 2;
405 colors[i+index].g = palette[i * 3 + 1] << 2;
406 colors[i+index].b = palette[i * 3 + 2] << 2;
408 SDL_SetColors(jnb_surface, &colors[index], index, count);
412 void fillpalette(int red, int green, int blue)
414 SDL_Color colors[256];
415 int i;
417 assert(drawing_enable==0);
419 for (i = 0; i < 256; i++) {
420 colors[i].r = red << 2;
421 colors[i].g = green << 2;
422 colors[i].b = blue << 2;
424 SDL_SetColors(jnb_surface, colors, 0, 256);
428 void get_block(int page, int x, int y, int width, int height, void *buffer)
430 unsigned char *buffer_ptr, *vga_ptr;
431 int h;
433 assert(drawing_enable==1);
435 if (scale_up) {
436 x *= 2;
437 y *= 2;
438 width *= 2;
439 height *= 2;
442 if (x < 0)
443 x = 0;
444 if (y < 0)
445 y = 0;
446 if (y + height >= screen_height)
447 height = screen_height - y;
448 if (x + width >= screen_width)
449 width = screen_width - x;
450 if (width<=0)
451 return;
452 if(height<=0)
453 return;
455 vga_ptr = get_vgaptr(page, x, y);
456 buffer_ptr = buffer;
457 for (h = 0; h < height; h++) {
458 memcpy(buffer_ptr, vga_ptr, width);
459 vga_ptr += screen_pitch;
460 buffer_ptr += width;
466 void put_block(int page, int x, int y, int width, int height, void *buffer)
468 int h;
469 unsigned char *vga_ptr, *buffer_ptr;
471 assert(drawing_enable==1);
473 if (scale_up) {
474 x *= 2;
475 y *= 2;
476 width *= 2;
477 height *= 2;
480 if (x < 0)
481 x = 0;
482 if (y < 0)
483 y = 0;
484 if (y + height >= screen_height)
485 height = screen_height - y;
486 if (x + width >= screen_width)
487 width = screen_width - x;
488 if (width<=0)
489 return;
490 if(height<=0)
491 return;
493 vga_ptr = get_vgaptr(page, x, y);
494 buffer_ptr = buffer;
495 for (h = 0; h < height; h++) {
496 memcpy(vga_ptr, buffer_ptr, width);
497 vga_ptr += screen_pitch;
498 buffer_ptr += width;
500 width = ((x+width)>>dirty_block_shift) - (x>>dirty_block_shift) + 1;
501 height = ((y+height)>>dirty_block_shift) - (y>>dirty_block_shift) + 1;
502 x >>= dirty_block_shift;
503 y >>= dirty_block_shift;
504 while (width--)
505 for (h=0; h<height; h++)
506 dirty_blocks[page][(y+h)*25+(x+width)] = 1;
510 void put_text(int page, int x, int y, char *text, int align)
512 int c1;
513 int t1;
514 int width;
515 int cur_x;
516 int image;
518 assert(drawing_enable==1);
520 if (text == NULL || strlen(text) == 0)
521 return;
522 if (font_gobs.num_images == 0)
523 return;
525 width = 0;
526 c1 = 0;
527 while (text[c1] != 0) {
528 t1 = text[c1];
529 c1++;
530 if (t1 == ' ') {
531 width += 5;
532 continue;
534 if (t1 >= 33 && t1 <= 34)
535 image = t1 - 33;
537 else if (t1 >= 39 && t1 <= 41)
538 image = t1 - 37;
540 else if (t1 >= 44 && t1 <= 59)
541 image = t1 - 39;
543 else if (t1 >= 64 && t1 <= 90)
544 image = t1 - 43;
546 else if (t1 >= 97 && t1 <= 122)
547 image = t1 - 49;
549 else if (t1 == '~')
550 image = 74;
552 else if (t1 == 0x84)
553 image = 75;
555 else if (t1 == 0x86)
556 image = 76;
558 else if (t1 == 0x8e)
559 image = 77;
561 else if (t1 == 0x8f)
562 image = 78;
564 else if (t1 == 0x94)
565 image = 79;
567 else if (t1 == 0x99)
568 image = 80;
570 else
571 continue;
572 width += pob_width(image, &font_gobs) + 1;
575 switch (align) {
576 case 0:
577 cur_x = x;
578 break;
579 case 1:
580 cur_x = x - width;
581 break;
582 case 2:
583 cur_x = x - width / 2;
584 break;
585 default:
586 cur_x = 0; /* this should cause error? -Chuck */
587 break;
589 c1 = 0;
591 while (text[c1] != 0) {
592 t1 = text[c1];
593 c1++;
594 if (t1 == ' ') {
595 cur_x += 5;
596 continue;
598 if (t1 >= 33 && t1 <= 34)
599 image = t1 - 33;
601 else if (t1 >= 39 && t1 <= 41)
602 image = t1 - 37;
604 else if (t1 >= 44 && t1 <= 59)
605 image = t1 - 39;
607 else if (t1 >= 64 && t1 <= 90)
608 image = t1 - 43;
610 else if (t1 >= 97 && t1 <= 122)
611 image = t1 - 49;
613 else if (t1 == '~')
614 image = 74;
616 else if (t1 == 0x84)
617 image = 75;
619 else if (t1 == 0x86)
620 image = 76;
622 else if (t1 == 0x8e)
623 image = 77;
625 else if (t1 == 0x8f)
626 image = 78;
628 else if (t1 == 0x94)
629 image = 79;
631 else if (t1 == 0x99)
632 image = 80;
634 else
635 continue;
636 put_pob(page, cur_x, y, image, &font_gobs, 1, mask_pic);
637 cur_x += pob_width(image, &font_gobs) + 1;
642 void put_pob(int page, int x, int y, int image, gob_t *gob, int use_mask, void *mask_pic)
644 int c1, c2;
645 int pob_x, pob_y;
646 int width, height;
647 int draw_width, draw_height;
648 int colour;
649 unsigned char *vga_ptr;
650 unsigned char *pob_ptr;
651 unsigned char *mask_ptr;
653 assert(drawing_enable==1);
654 assert(gob);
655 assert(image>=0);
656 assert(image<gob->num_images);
658 if (scale_up) {
659 x *= 2;
660 y *= 2;
661 width = draw_width = gob->width[image]*2;
662 height = draw_height = gob->height[image]*2;
663 x -= gob->hs_x[image]*2;
664 y -= gob->hs_y[image]*2;
665 } else {
666 width = draw_width = gob->width[image];
667 height = draw_height = gob->height[image];
668 x -= gob->hs_x[image];
669 y -= gob->hs_y[image];
672 if ((x + width) <= 0 || x >= screen_width)
673 return;
674 if ((y + height) <= 0 || y >= screen_height)
675 return;
677 pob_x = 0;
678 pob_y = 0;
679 if (x < 0) {
680 pob_x -= x;
681 draw_width += x;
682 x = 0;
684 if ((x + width) > screen_width)
685 draw_width -= x + width - screen_width;
686 if (y < 0) {
687 pob_y -= y;
688 draw_height += y;
689 y = 0;
691 if ((y + height) > screen_height)
692 draw_height -= y + height - screen_height;
694 vga_ptr = get_vgaptr(page, x, y);
695 pob_ptr = ((unsigned char *)gob->data[image]) + ((pob_y * width) + pob_x);
696 mask_ptr = ((unsigned char *)mask) + ((y * screen_pitch) + (x));
697 for (c1 = 0; c1 < draw_height; c1++) {
698 for (c2 = 0; c2 < draw_width; c2++) {
699 colour = *mask_ptr;
700 if (use_mask == 0 || (use_mask == 1 && colour == 0)) {
701 colour = *pob_ptr;
702 if (colour != 0) {
703 *vga_ptr = colour;
706 vga_ptr++;
707 pob_ptr++;
708 mask_ptr++;
710 pob_ptr += width - c2;
711 vga_ptr += (screen_width - c2);
712 mask_ptr += (screen_width - c2);
714 draw_width = ((x+draw_width)>>dirty_block_shift) - (x>>dirty_block_shift) + 1;
715 draw_height = ((y+draw_height)>>dirty_block_shift) - (y>>dirty_block_shift) + 1;
716 x >>= dirty_block_shift;
717 y >>= dirty_block_shift;
718 while (draw_width--)
719 for (c1=0; c1<draw_height; c1++)
720 dirty_blocks[page][(y+c1)*25+(x+draw_width)] = 1;
724 int pob_width(int image, gob_t *gob)
726 assert(gob);
727 assert(image>=0);
728 assert(image<gob->num_images);
729 return gob->width[image];
733 int pob_height(int image, gob_t *gob)
735 assert(gob);
736 assert(image>=0);
737 assert(image<gob->num_images);
738 return gob->height[image];
742 int pob_hs_x(int image, gob_t *gob)
744 assert(gob);
745 assert(image>=0);
746 assert(image<gob->num_images);
747 return gob->hs_x[image];
751 int pob_hs_y(int image, gob_t *gob)
753 assert(gob);
754 assert(image>=0);
755 assert(image<gob->num_images);
756 return gob->hs_y[image];
760 int read_pcx(unsigned char * handle, void *buf, int buf_len, char *pal)
762 unsigned char *buffer=buf;
763 short c1;
764 short a, b;
765 long ofs1;
766 if (buffer != 0) {
767 handle += 128;
768 ofs1 = 0;
769 while (ofs1 < buf_len) {
770 a = *(handle++);
771 if ((a & 0xc0) == 0xc0) {
772 b = *(handle++);
773 a &= 0x3f;
774 for (c1 = 0; c1 < a && ofs1 < buf_len; c1++)
775 buffer[ofs1++] = (char) b;
776 } else
777 buffer[ofs1++] = (char) a;
779 if (pal != 0) {
780 handle++;
781 for (c1 = 0; c1 < 768; c1++)
782 pal[c1] = *(handle++) /*fgetc(handle)*/ >> 2;
785 return 0;
789 void register_background(char *pixels, char pal[768])
791 if (background) {
792 free(background);
793 background = NULL;
795 background_drawn = 0;
796 if (!pixels)
797 return;
798 assert(pal);
799 if (scale_up) {
800 background = malloc(screen_pitch*screen_height);
801 assert(background);
802 do_scale2x((unsigned char *)pixels, JNB_WIDTH, JNB_HEIGHT, (unsigned char *)background);
803 } else {
804 background = malloc(JNB_WIDTH*JNB_HEIGHT);
805 assert(background);
806 memcpy(background, pixels, JNB_WIDTH*JNB_HEIGHT);
810 int register_gob(unsigned char *handle, gob_t *gob, int len)
812 unsigned char *gob_data;
813 int i;
815 gob_data = malloc(len);
816 memcpy(gob_data, handle, len);
818 gob->num_images = (short)((gob_data[0]) + (gob_data[1] << 8));
820 gob->width = malloc(gob->num_images*sizeof(int));
821 gob->height = malloc(gob->num_images*sizeof(int));
822 gob->hs_x = malloc(gob->num_images*sizeof(int));
823 gob->hs_y = malloc(gob->num_images*sizeof(int));
824 gob->data = malloc(gob->num_images*sizeof(void *));
825 gob->orig_data = malloc(gob->num_images*sizeof(void *));
826 for (i=0; i<gob->num_images; i++) {
827 int image_size;
828 int offset;
830 offset = (gob_data[i*4+2]) + (gob_data[i*4+3] << 8) + (gob_data[i*4+4] << 16) + (gob_data[i*4+5] << 24);
832 gob->width[i] = (short)((gob_data[offset]) + (gob_data[offset+1] << 8)); offset += 2;
833 gob->height[i] = (short)((gob_data[offset]) + (gob_data[offset+1] << 8)); offset += 2;
834 gob->hs_x[i] = (short)((gob_data[offset]) + (gob_data[offset+1] << 8)); offset += 2;
835 gob->hs_y[i] = (short)((gob_data[offset]) + (gob_data[offset+1] << 8)); offset += 2;
837 image_size = gob->width[i] * gob->height[i];
838 gob->orig_data[i] = malloc(image_size);
839 memcpy(gob->orig_data[i], &gob_data[offset], image_size);
840 if (scale_up) {
841 image_size = gob->width[i] * gob->height[i] * 4;
842 gob->data[i] = malloc(image_size);
843 do_scale2x((unsigned char *)gob->orig_data[i], gob->width[i], gob->height[i], (unsigned char *)gob->data[i]);
844 } else {
845 gob->data[i] = (unsigned short *)gob->orig_data[i];
848 free(gob_data);
849 return 0;
853 void recalculate_gob(gob_t *gob, char pal[768])
857 void register_mask(void *pixels)
859 if (mask) {
860 free(mask);
861 mask = NULL;
863 assert(pixels);
864 if (scale_up) {
865 mask = malloc(screen_pitch*screen_height);
866 assert(mask);
867 do_scale2x((unsigned char *)pixels, JNB_WIDTH, JNB_HEIGHT, (unsigned char *)mask);
868 } else {
869 mask = malloc(JNB_WIDTH*JNB_HEIGHT);
870 assert(mask);
871 memcpy(mask, pixels, JNB_WIDTH*JNB_HEIGHT);