1 /*
2  * OpenBOR - http://www.chronocrash.com
3  * -----------------------------------------------------------------------
4  * All rights reserved, see LICENSE in OpenBOR root for details.
5  *
6  * Copyright (c) 2004 - 2014 OpenBOR Team
7  */
8 
9 /*
10 	Code for handling 'screen' structures.
11 	(Memory allocation and copy functions)
12 	Last update: 10 feb 2003
13 */
14 #include <stdio.h>
15 #include <string.h>
16 #include "types.h"
17 #include "transform.h"
18 #include "screen.h"
19 
allocscreen(int width,int height,int pixelformat)20 s_screen *allocscreen(int width, int height, int pixelformat)
21 {
22     s_screen *screen;
23     int psize;
24     width &= (0xFFFFFFFF - 3);
25     psize = width * height * pixelbytes[pixelformat];
26     if(pixelformat == PIXEL_x8)
27     {
28         screen = (s_screen *)malloc(sizeof(s_screen) + psize + PAL_BYTES + ANYNUMBER);
29     }
30     else
31     {
32         screen = (s_screen *)malloc(sizeof(s_screen) + psize + ANYNUMBER);
33     }
34     if(screen == NULL)
35     {
36         return NULL;
37     }
38     screen->width = width;
39     screen->height = height;
40     screen->pixelformat = pixelformat;
41     screen->magic = screen_magic;
42     if(pixelformat == PIXEL_x8)
43     {
44         screen->palette = ((unsigned char *)screen->data) + width * height * pixelbytes[(int)pixelformat];
45     }
46     else
47     {
48         screen->palette = NULL;
49     }
50     return screen;
51 }
52 
freescreen(s_screen ** screen)53 void freescreen(s_screen **screen)
54 {
55     if((*screen) != NULL)
56     {
57         free((*screen));
58     }
59     (*screen) = NULL;
60 }
61 
62 // Screen copy func. Supports clipping.
copyscreen(s_screen * dest,s_screen * src)63 void copyscreen(s_screen *dest, s_screen *src)
64 {
65     unsigned char *sp, *dp;
66     int width = src->width;
67     int height = src->height;
68     int pixelformat = src->pixelformat;
69 
70     if(pixelformat != dest->pixelformat)
71     {
72         return;
73     }
74 
75     if(height > dest->height)
76     {
77         height = dest->height;
78     }
79     if(width > dest->width)
80     {
81         width = dest->width;
82     }
83 
84     dp = dest->data;
85     sp = src->data;
86     // Copy unclipped
87     if(dest->width == src->width)
88     {
89         memcpy(dest->data, src->data, width * height * pixelbytes[(int)pixelformat]);
90         return;
91     }
92 
93     // Copy clipped
94     do
95     {
96         memcpy(dp, sp, width * pixelbytes[(int)pixelformat]);
97         sp += src->width * pixelbytes[(int)pixelformat];
98         dp += dest->width * pixelbytes[(int)pixelformat];
99     }
100     while(--height);
101 }
102 
clearscreen(s_screen * s)103 void clearscreen(s_screen *s)
104 {
105     if(s == NULL)
106     {
107         return;
108     }
109     memset(s->data, 0, s->width * s->height * pixelbytes[(int)s->pixelformat]);
110 }
111 
112 // Screen copy function with offset options. Supports clipping.
copyscreen_o(s_screen * dest,s_screen * src,int x,int y)113 void copyscreen_o(s_screen *dest, s_screen *src, int x, int y)
114 {
115     unsigned char *sp, *dp;
116     int pixelformat = src->pixelformat;
117     int sw = src->width;
118     int sh = src->height;
119     int dw = dest->width;
120     int cw = sw, ch = sh;
121     int linew, slinew, dlinew;
122     int sox, soy;
123 
124     int xmin = useclip ? clipx1 : 0,
125         xmax = useclip ? clipx2 : dest->width,
126         ymin = useclip ? clipy1 : 0,
127         ymax = useclip ? clipy2 : dest->height;
128 
129     // Copy anything at all?
130     if(x >= xmax)
131     {
132         return;
133     }
134     if(sw + x <= xmin)
135     {
136         return;
137     }
138     if(y >= ymax)
139     {
140         return;
141     }
142     if(sh + y <= ymin)
143     {
144         return;
145     }
146 
147     sox = 0;
148     soy = 0;
149 
150     // Clip?
151     if(x < xmin)
152     {
153         sox = xmin - x;
154         cw -= sox;
155     }
156     if(y < ymin)
157     {
158         soy = ymin - y;
159         ch -= soy;
160     }
161 
162     if(x + sw > xmax)
163     {
164         cw -= (x + sw) - xmax;
165     }
166     if(y + sh > ymax)
167     {
168         ch -= (y + sh) - ymax;
169     }
170 
171     if(x < xmin)
172     {
173         x = xmin;
174     }
175     if(y < ymin)
176     {
177         y = ymin;
178     }
179 
180     if(dest->pixelformat != src->pixelformat)
181     {
182         return;
183     }
184 
185     sp = src->data + (soy * sw + sox) * pixelbytes[(int)pixelformat];
186     dp = dest->data + (y * dw + x) * pixelbytes[(int)pixelformat];
187     linew = cw * pixelbytes[(int)pixelformat];
188     slinew = sw * pixelbytes[(int)pixelformat];
189     dlinew = dw * pixelbytes[(int)pixelformat];
190     // Copy data
191     do
192     {
193         memcpy(dp, sp, linew);
194         sp += slinew;
195         dp += dlinew;
196     }
197     while(--ch);
198 }
199 
200 // same as above, with color key
copyscreen_trans(s_screen * dest,s_screen * src,int x,int y)201 void copyscreen_trans(s_screen *dest, s_screen *src, int x, int y)
202 {
203     unsigned char *sp, *dp;
204     int sw = src->width;
205     int sh = src->height;
206     int dw = dest->width;
207     int cw = sw, ch = sh;
208     int sox, soy;
209     int i;
210 
211     int xmin = useclip ? clipx1 : 0,
212         xmax = useclip ? clipx2 : dest->width,
213         ymin = useclip ? clipy1 : 0,
214         ymax = useclip ? clipy2 : dest->height;
215 
216     // Copy anything at all?
217     if(x >= xmax)
218     {
219         return;
220     }
221     if(sw + x <= xmin)
222     {
223         return;
224     }
225     if(y >= ymax)
226     {
227         return;
228     }
229     if(sh + y <= ymin)
230     {
231         return;
232     }
233 
234     sox = 0;
235     soy = 0;
236 
237     // Clip?
238     if(x < xmin)
239     {
240         sox = xmin - x;
241         cw -= sox;
242     }
243     if(y < ymin)
244     {
245         soy = ymin - y;
246         ch -= soy;
247     }
248 
249     if(x + sw > xmax)
250     {
251         cw -= (x + sw) - xmax;
252     }
253     if(y + sh > ymax)
254     {
255         ch -= (y + sh) - ymax;
256     }
257 
258     if(x < xmin)
259     {
260         x = xmin;
261     }
262     if(y < ymin)
263     {
264         y = ymin;
265     }
266 
267     sp = src->data + (soy * sw + sox);
268     dp = dest->data + (y * dw + x);
269     // Copy data
270     do
271     {
272         i = cw - 1;
273         do
274         {
275             if(sp[i] == 0)
276             {
277                 continue;
278             }
279             dp[i] = sp[i];
280         }
281         while(i--);
282         sp += sw;
283         dp += dw;
284     }
285     while(--ch);
286 }
287 
288 //same as above, with remap, work only under 8bit pixel format
copyscreen_remap(s_screen * dest,s_screen * src,int x,int y,unsigned char * remap)289 void copyscreen_remap(s_screen *dest, s_screen *src, int x, int y, unsigned char *remap)
290 {
291     unsigned char *sp = src->data;
292     unsigned char *dp = dest->data;
293     int i;
294     int sw = src->width;
295     int sh = src->height;
296     int dw = dest->width;
297     int cw = sw, ch = sh;
298     int sox, soy;
299 
300     int xmin = useclip ? clipx1 : 0,
301         xmax = useclip ? clipx2 : dest->width,
302         ymin = useclip ? clipy1 : 0,
303         ymax = useclip ? clipy2 : dest->height;
304 
305     // Copy anything at all?
306     if(x >= xmax)
307     {
308         return;
309     }
310     if(sw + x <= xmin)
311     {
312         return;
313     }
314     if(y >= ymax)
315     {
316         return;
317     }
318     if(sh + y <= ymin)
319     {
320         return;
321     }
322 
323     sox = 0;
324     soy = 0;
325 
326     // Clip?
327     if(x < xmin)
328     {
329         sox = xmin - x;
330         cw -= sox;
331     }
332     if(y < ymin)
333     {
334         soy = ymin - y;
335         ch -= soy;
336     }
337 
338     if(x + sw > xmax)
339     {
340         cw -= (x + sw) - xmax;
341     }
342     if(y + sh > ymax)
343     {
344         ch -= (y + sh) - ymax;
345     }
346 
347     if(x < xmin)
348     {
349         x = xmin;
350     }
351     if(y < ymin)
352     {
353         y = ymin;
354     }
355 
356     sp += (soy * sw + sox);
357     dp += (y * dw + x);
358 
359     // Copy data
360     do
361     {
362         i = cw - 1;
363         do
364         {
365             if(!sp[i])
366             {
367                 continue;
368             }
369             dp[i] = remap[sp[i]];
370         }
371         while(i--);
372         sp += sw;
373         dp += dw;
374     }
375     while(--ch);
376 }
377 
378 //same as above, with alpha blend
blendscreen(s_screen * dest,s_screen * src,int x,int y,unsigned char * lut)379 void blendscreen(s_screen *dest, s_screen *src, int x, int y, unsigned char *lut)
380 {
381     unsigned char *sp = src->data;
382     unsigned char *dp = dest->data;
383     int i;
384     int sw = src->width;
385     int sh = src->height;
386     int dw = dest->width;
387     int cw = sw, ch = sh;
388     int sox, soy;
389     unsigned char *d, *s;
390 
391     int xmin = useclip ? clipx1 : 0,
392         xmax = useclip ? clipx2 : dest->width,
393         ymin = useclip ? clipy1 : 0,
394         ymax = useclip ? clipy2 : dest->height;
395 
396     // Copy anything at all?
397     if(x >= xmax)
398     {
399         return;
400     }
401     if(sw + x <= xmin)
402     {
403         return;
404     }
405     if(y >= ymax)
406     {
407         return;
408     }
409     if(sh + y <= ymin)
410     {
411         return;
412     }
413 
414     sox = 0;
415     soy = 0;
416 
417     // Clip?
418     if(x < xmin)
419     {
420         sox = xmin - x;
421         cw -= sox;
422     }
423     if(y < ymin)
424     {
425         soy = ymin - y;
426         ch -= soy;
427     }
428 
429     if(x + sw > xmax)
430     {
431         cw -= (x + sw) - xmax;
432     }
433     if(y + sh > ymax)
434     {
435         ch -= (y + sh) - ymax;
436     }
437 
438     if(x < xmin)
439     {
440         x = xmin;
441     }
442     if(y < ymin)
443     {
444         y = ymin;
445     }
446 
447     sp += soy * sw + sox;
448     dp += y * dw + x;
449 
450     // Copy data
451     do
452     {
453         i = cw;
454         do
455         {
456             d = dp + i - 1;
457             s = sp + i - 1;
458             if(!(*s))
459             {
460                 continue;
461             }
462             *d = lut[(*s) << 8 | (*d)];
463         }
464         while(--i);
465         sp += sw;
466         dp += dw;
467     }
468     while(--ch);
469 }
470 
471 
_putscreen(s_screen * dest,s_screen * src,int x,int y,s_drawmethod * drawmethod)472 static void _putscreen(s_screen *dest, s_screen *src, int x, int y, s_drawmethod *drawmethod)
473 {
474     unsigned char *table;
475     int alpha, transbg;
476     gfx_entry gfx;
477 
478     if(!drawmethod || drawmethod->flag == 0)
479     {
480         table = NULL;
481         alpha = 0;
482         transbg = 0;
483     }
484     else if(drawmethod->water.watermode && drawmethod->water.amplitude)
485     {
486         gfx.screen = src;
487         if(drawmethod->water.watermode == 3)
488         {
489             gfx_draw_plane(dest, &gfx, x, y, 0, 0, drawmethod);
490         }
491         else
492         {
493             gfx_draw_water(dest, &gfx, x, y, 0, 0, drawmethod);
494         }
495         return ;
496     }
497     else if(drawmethod->rotate)
498     {
499         gfx.screen = src;
500         gfx_draw_rotate(dest, &gfx, x, y, 0, 0, drawmethod);
501         return;
502     }
503     else if(drawmethod->scalex != 256 || drawmethod->scaley != 256 || drawmethod->shiftx)
504     {
505         gfx.screen = src;
506         gfx_draw_scale(dest, &gfx, x, y, 0, 0, drawmethod);
507         return;
508     }
509     else
510     {
511         table = drawmethod->table;
512         alpha = drawmethod->alpha;
513         transbg = drawmethod->transbg;
514         x -= drawmethod->centerx;
515         y -= drawmethod->centery;
516     }
517 
518     if(!table && alpha <= 0 && !transbg && !usechannel)
519     {
520         if(dest->pixelformat == src->pixelformat && dest->width == src->width && dest->height == src->height && !x && !y)
521         {
522             copyscreen(dest, src);
523             return;
524         }
525     }
526 
527     if(dest->pixelformat == PIXEL_8)
528     {
529         if(table)
530         {
531             copyscreen_remap(dest, src, x, y, drawmethod->table);
532         }
533         else if(alpha > 0)
534         {
535             blendscreen(dest, src, x, y, blendtables[drawmethod->alpha - 1]);
536         }
537         else if(transbg)
538         {
539             copyscreen_trans(dest, src, x, y);
540         }
541         else
542         {
543             copyscreen_o(dest, src, x, y);
544         }
545     }
546     else if(dest->pixelformat == PIXEL_16)
547     {
548         if(src->pixelformat == PIXEL_x8)
549         {
550             putscreenx8p16(dest, src, x, y, transbg, (unsigned short *)table, getblendfunction16(alpha));
551         }
552         else if(src->pixelformat == PIXEL_16)
553         {
554             blendscreen16(dest, src, x, y, transbg, getblendfunction16(alpha));
555         }
556     }
557     else if(dest->pixelformat == PIXEL_32)
558     {
559         if(src->pixelformat == PIXEL_x8)
560         {
561             putscreenx8p32(dest, src, x, y, transbg, (unsigned *)table, getblendfunction32(alpha));
562         }
563         else if(src->pixelformat == PIXEL_32)
564         {
565             blendscreen32(dest, src, x, y, transbg, getblendfunction32(alpha));
566         }
567     }
568 }
569 
putscreen(s_screen * dest,s_screen * src,int x,int y,s_drawmethod * drawmethod)570 void putscreen(s_screen *dest, s_screen *src, int x, int y, s_drawmethod *drawmethod)
571 {
572     int xrepeat, yrepeat, xspan, yspan, i, j, dx, dy;
573 
574     drawmethod_global_init(drawmethod);
575 
576     if(drawmethod && drawmethod->flag)
577     {
578         xrepeat = drawmethod->xrepeat;
579         yrepeat = drawmethod->yrepeat;
580         xspan = drawmethod->xspan;
581         yspan = drawmethod->yspan;
582     }
583     else
584     {
585         xrepeat = yrepeat = 1;
586         xspan = yspan = 0;
587     }
588 
589     for(j = 0, dy = y; j < yrepeat; j++, dy += yspan)
590     {
591         for(i = 0, dx = x; i < xrepeat; i++, dx += xspan)
592         {
593             _putscreen(dest, src, dx, dy, drawmethod);
594         }
595     }
596 
597 }
598 
599 // Scale screen
scalescreen(s_screen * dest,s_screen * src)600 void scalescreen(s_screen *dest, s_screen *src)
601 {
602     int sw, sh;
603     int dw, dh;
604     int dx, dy;
605     unsigned char *sp;
606     unsigned char *dp;
607     unsigned char *lineptr;
608     unsigned int xstep, ystep, xpos, ypos;
609     int pixelformat = src->pixelformat;
610 
611     if(dest->pixelformat != pixelformat)
612     {
613         return;
614     }
615 
616     if(src == NULL || dest == NULL)
617     {
618         return;
619     }
620     sp = src->data;
621     dp = dest->data;
622 
623     sw = src->width * pixelbytes[(int)pixelformat];
624     sh = src->height;
625     dw = dest->width * pixelbytes[(int)pixelformat];
626     dh = dest->height;
627 
628     xstep = (sw << 16) / dw;
629     ystep = (sh << 16) / dh;
630 
631     ypos = 0;
632     for(dy = 0; dy < dh; dy++)
633     {
634         lineptr = sp + ((ypos >> 16) * sw);
635         ypos += ystep;
636         xpos = 0;
637         for(dx = 0; dx < dw; dx++)
638         {
639             *dp = lineptr[xpos >> 16];
640             ++dp;
641             xpos += xstep;
642         }
643     }
644 }
645 
646 /*
647  * Zooms in or out on the screen.
648  * Parameters:
649  *     centerx - x coord of zoom center on unclipped, unscaled screen
650  *     centery - y coord of zoom center on unclipped, unscaled screen
651  *     scalex - x scale factor
652  *     scaley - y scale factor
653  */
zoomscreen(s_screen * dest,s_screen * src,int centerx,int centery,int scalex,int scaley)654 void zoomscreen(s_screen *dest, s_screen *src, int centerx, int centery, int scalex, int scaley)
655 {
656     s_screen *frame;
657     int screenwidth = src->width;
658     int screenheight = src->height;
659     int width = (screenwidth << 8) / scalex; // width of clipped, unscaled screen
660     int height = (screenheight << 8) / scaley; // height of clipped, unscaled screen
661     int xmin = (width >> 1) - centerx; // x coord before clipping corresponding to x=0 after clipping
662     int ymin = (height >> 1) - centery; // y coord before clipping corresponding to y=0 after clipping
663     int pixelformat = dest->pixelformat;
664 
665     if(src->pixelformat != pixelformat)
666     {
667         return;
668     }
669     if(xmin >= screenwidth || xmin + ((width * scalex) >> 8) < 0)
670     {
671         return;    // out of left or right border
672     }
673     if(ymin >= screenheight)
674     {
675         return;
676     }
677 
678     frame = allocscreen(width, height, pixelformat); // the part of the screen that will be zoomed
679     copyscreen_o(frame, src, xmin, ymin);
680 
681     if(pixelbytes[pixelformat] == 1)
682     {
683         scalescreen(dest, frame);
684     }
685     else if(pixelbytes[pixelformat] == 2)
686     {
687         scalescreen16(dest, frame);
688     }
689     else if(pixelbytes[pixelformat] == 4)
690     {
691         scalescreen32(dest, frame);
692     }
693 
694     freescreen(&frame);
695 }
696