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 /*#include <stdio.h>
11 #include <string.h>*/
12 #include "globals.h"
13 #include "types.h"
14 #include "sprite.h"
15 #include "transform.h"
16 /////////////////////////////////////////////////////////////////////////////
17 
18 
putsprite_(unsigned char * dest,int x,int xmin,int xmax,int * linetab,int h,int screenwidth)19 static void putsprite_(
20     unsigned char *dest, int x, int xmin, int xmax, int *linetab, int h, int screenwidth
21 )
22 {
23     for(; h > 0; h--, dest += screenwidth)
24     {
25         register int lx = x;
26         unsigned char *data = ((unsigned char *)linetab) + (*linetab);
27         linetab++;
28         while(lx < xmax)
29         {
30             register int count = *data++;
31             if(count == 0xFF)
32             {
33                 break;
34             }
35             lx += count;
36             if(lx >= xmax)
37             {
38                 break;
39             }
40             count = *data++;
41             if(!count)
42             {
43                 continue;
44             }
45             if((lx + count) <= xmin)
46             {
47                 lx += count;
48                 data += count;
49                 continue;
50             }
51             if(lx < xmin)
52             {
53                 int diff = lx - xmin;
54                 count += diff;
55                 data -= diff;
56                 lx = xmin;
57             }
58             if((lx + count) > xmax)
59             {
60                 count = xmax - lx;
61             }
62             memcpy(dest + lx, data, count);
63             data += count;
64             lx += count;
65         }
66     }
67 }
68 
putsprite_flip_(unsigned char * dest,int x,int xmin,int xmax,int * linetab,int h,int screenwidth)69 static void putsprite_flip_(
70     unsigned char *dest, int x, int xmin, int xmax, int *linetab, int h, int screenwidth
71 )
72 {
73     for(; h > 0; h--, dest += screenwidth)
74     {
75         register int lx = x;
76         unsigned char *data = ((unsigned char *)linetab) + (*linetab);
77         linetab++;
78         while(lx > xmin)
79         {
80             register int count = *data++;
81             if(count == 0xFF)
82             {
83                 break;
84             }
85             lx -= count;
86             if(lx <= xmin)
87             {
88                 break;
89             }
90             count = *data++;
91             if(!count)
92             {
93                 continue;
94             }
95             if((lx - count) >= xmax)
96             {
97                 lx -= count;
98                 data += count;
99                 continue;
100             }
101             if(lx > xmax)
102             {
103                 int diff = (lx - xmax);
104                 count -= diff;
105                 data += diff;
106                 lx = xmax;
107             }
108             if((lx - count) < xmin)
109             {
110                 count = lx - xmin;
111             }
112             for(; count > 0; count--)
113             {
114                 dest[--lx] = *data++;
115             }
116             //lx--;
117             //u8revcpy(dest+lx, data, count);
118             //lx-=count-1;
119             //data+=count;
120         }
121     }
122 }
123 
putsprite_remap_(unsigned char * dest,int x,int xmin,int xmax,int * linetab,int h,int screenwidth,unsigned char * remap)124 static void putsprite_remap_(
125     unsigned char *dest, int x, int xmin, int xmax, int *linetab, int h, int screenwidth,
126     unsigned char *remap
127 )
128 {
129     for(; h > 0; h--, dest += screenwidth)
130     {
131         register int lx = x;
132         unsigned char *data = ((unsigned char *)linetab) + (*linetab);
133         linetab++;
134         while(lx < xmax)
135         {
136             register int count = *data++;
137             if(count == 0xFF)
138             {
139                 break;
140             }
141             lx += count;
142             if(lx >= xmax)
143             {
144                 break;
145             }
146             count = *data++;
147             if(!count)
148             {
149                 continue;
150             }
151             if((lx + count) <= xmin)
152             {
153                 lx += count;
154                 data += count;
155                 continue;
156             }
157             if(lx < xmin)
158             {
159                 int diff = lx - xmin;
160                 count += diff;
161                 data -= diff;
162                 lx = xmin;
163             }
164             if((lx + count) > xmax)
165             {
166                 count = xmax - lx;
167             }
168             for(; count > 0; count--)
169             {
170                 dest[lx++] = remap[((int)(*data++)) & 0xFF];
171             }
172             //u8pcpy(dest+lx, data, remap, count);
173             //lx+=count;
174             //data+=count;
175         }
176     }
177 }
178 
putsprite_remap_flip_(unsigned char * dest,int x,int xmin,int xmax,int * linetab,int h,int screenwidth,unsigned char * remap)179 static void putsprite_remap_flip_(
180     unsigned char *dest, int x, int xmin, int xmax, int *linetab, int h, int screenwidth,
181     unsigned char *remap
182 )
183 {
184     for(; h > 0; h--, dest += screenwidth)
185     {
186         register int lx = x;
187         unsigned char *data = ((unsigned char *)linetab) + (*linetab);
188         linetab++;
189         while(lx > xmin)
190         {
191             register int count = *data++;
192             if(count == 0xFF)
193             {
194                 break;
195             }
196             lx -= count;
197             if(lx <= xmin)
198             {
199                 break;
200             }
201             count = *data++;
202             if(!count)
203             {
204                 continue;
205             }
206             if((lx - count) >= xmax)
207             {
208                 lx -= count;
209                 data += count;
210                 continue;
211             }
212             if(lx > xmax)
213             {
214                 int diff = (lx - xmax);
215                 count -= diff;
216                 data += diff;
217                 lx = xmax;
218             }
219             if((lx - count) < xmin)
220             {
221                 count = lx - xmin;
222             }
223             for(; count > 0; count--)
224             {
225                 dest[--lx] = remap[((int)(*data++)) & 0xFF];
226             }
227             //lx--;
228             //u8revpcpy(dest+lx, data, remap, count);
229             //lx-=count-1;
230             //data+=count;
231         }
232     }
233 }
234 
235 //src high dest low
putsprite_remapblend_(unsigned char * dest,int x,int xmin,int xmax,int * linetab,int h,int screenwidth,unsigned char * remap,unsigned char * blend)236 static void putsprite_remapblend_(
237     unsigned char *dest, int x, int xmin, int xmax, int *linetab, int h, int screenwidth,
238     unsigned char *remap, unsigned char *blend
239 )
240 {
241     for(; h > 0; h--, dest += screenwidth)
242     {
243         register int lx = x;
244         unsigned char *data = ((unsigned char *)linetab) + (*linetab);
245         linetab++;
246         while(lx < xmax)
247         {
248             register int count = *data++;
249             if(count == 0xFF)
250             {
251                 break;
252             }
253             lx += count;
254             if(lx >= xmax)
255             {
256                 break;
257             }
258             count = *data++;
259             if(!count)
260             {
261                 continue;
262             }
263             if((lx + count) <= xmin)
264             {
265                 lx += count;
266                 data += count;
267                 continue;
268             }
269             if(lx < xmin)
270             {
271                 int diff = lx - xmin;
272                 count += diff;
273                 data -= diff;
274                 lx = xmin;
275             }
276             if((lx + count) > xmax)
277             {
278                 count = xmax - lx;
279             }
280             for(; count > 0; count--)
281             {
282                 dest[lx] = blend[(remap[(((int)(*data++)) & 0xFF)] << 8) | dest[lx]];
283                 lx++;
284             }
285         }
286     }
287 }
288 
putsprite_remapblend_flip_(unsigned char * dest,int x,int xmin,int xmax,int * linetab,int h,int screenwidth,unsigned char * remap,unsigned char * blend)289 static void putsprite_remapblend_flip_(
290     unsigned char *dest, int x, int xmin, int xmax, int *linetab, int h, int screenwidth,
291     unsigned char *remap, unsigned char *blend
292 )
293 {
294     for(; h > 0; h--, dest += screenwidth)
295     {
296         register int lx = x;
297         unsigned char *data = ((unsigned char *)linetab) + (*linetab);
298         linetab++;
299         while(lx > xmin)
300         {
301             register int count = *data++;
302             if(count == 0xFF)
303             {
304                 break;
305             }
306             lx -= count;
307             if(lx <= xmin)
308             {
309                 break;
310             }
311             count = *data++;
312             if(!count)
313             {
314                 continue;
315             }
316             if((lx - count) >= xmax)
317             {
318                 lx -= count;
319                 data += count;
320                 continue;
321             }
322             if(lx > xmax)
323             {
324                 int diff = (lx - xmax);
325                 count -= diff;
326                 data += diff;
327                 lx = xmax;
328             }
329             if((lx - count) < xmin)
330             {
331                 count = lx - xmin;
332             }
333             for(; count > 0; count--)
334             {
335                 --lx;
336                 dest[lx] = blend[(remap[(((int)(*data++)) & 0xFF)] << 8) | dest[lx]];
337             }
338         }
339     }
340 }
341 
putsprite_blend_(unsigned char * dest,int x,int xmin,int xmax,int * linetab,int h,int screenwidth,unsigned char * blend)342 static void putsprite_blend_(
343     unsigned char *dest, int x, int xmin, int xmax, int *linetab, int h, int screenwidth,
344     unsigned char *blend
345 )
346 {
347     for(; h > 0; h--, dest += screenwidth)
348     {
349         register int lx = x;
350         unsigned char *data = ((unsigned char *)linetab) + (*linetab);
351         linetab++;
352         while(lx < xmax)
353         {
354             register int count = *data++;
355             if(count == 0xFF)
356             {
357                 break;
358             }
359             lx += count;
360             if(lx >= xmax)
361             {
362                 break;
363             }
364             count = *data++;
365             if(!count)
366             {
367                 continue;
368             }
369             if((lx + count) <= xmin)
370             {
371                 lx += count;
372                 data += count;
373                 continue;
374             }
375             if(lx < xmin)
376             {
377                 int diff = lx - xmin;
378                 count += diff;
379                 data -= diff;
380                 lx = xmin;
381             }
382             if((lx + count) > xmax)
383             {
384                 count = xmax - lx;
385             }
386             for(; count > 0; count--)
387             {
388                 dest[lx] = blend[((((int)(*data++)) & 0xFF) << 8) | dest[lx]];
389                 lx++;
390             }
391         }
392     }
393 }
394 
putsprite_blend_flip_(unsigned char * dest,int x,int xmin,int xmax,int * linetab,int h,int screenwidth,unsigned char * blend)395 static void putsprite_blend_flip_(
396     unsigned char *dest, int x, int xmin, int xmax, int *linetab, int h, int screenwidth,
397     unsigned char *blend
398 )
399 {
400     for(; h > 0; h--, dest += screenwidth)
401     {
402         register int lx = x;
403         unsigned char *data = ((unsigned char *)linetab) + (*linetab);
404         linetab++;
405         while(lx > xmin)
406         {
407             register int count = *data++;
408             if(count == 0xFF)
409             {
410                 break;
411             }
412             lx -= count;
413             if(lx <= xmin)
414             {
415                 break;
416             }
417             count = *data++;
418             if(!count)
419             {
420                 continue;
421             }
422             if((lx - count) >= xmax)
423             {
424                 lx -= count;
425                 data += count;
426                 continue;
427             }
428             if(lx > xmax)
429             {
430                 int diff = (lx - screenwidth);
431                 count -= diff;
432                 data += diff;
433                 lx = screenwidth;
434             }
435             if((lx - count) < xmin)
436             {
437                 count = lx - xmin;
438             }
439             for(; count > 0; count--)
440             {
441                 --lx;
442                 dest[lx] = blend[((((int)(*data++)) & 0xFF) << 8) | dest[lx]];
443             }
444         }
445     }
446 }
447 
448 /////////////////////////////////////////////////////////////////////////////
449 
putsprite_8(int x,int y,int is_flip,s_sprite * sprite,s_screen * screen,unsigned char * remap,unsigned char * blend)450 void putsprite_8(
451     int x, int y, int is_flip, s_sprite *sprite, s_screen *screen,
452     unsigned char *remap, unsigned char *blend
453 )
454 {
455     int *linetab;
456     int w, h;
457     unsigned char *dest;
458     // Get screen size
459     int screenwidth = screen->width;
460     int xmin = useclip ? MAX(clipx1, 0) : 0,
461         xmax = useclip ? MIN(clipx2, screen->width) : screen->width,
462         ymin = useclip ? MAX(clipy1, 0) : 0,
463         ymax = useclip ? MIN(clipy2, screen->height) : screen->height;
464     // Adjust coords for centering
465     if(is_flip)
466     {
467         x += sprite->centerx;
468     }
469     else
470     {
471         x -= sprite->centerx;
472     }
473     y -= sprite->centery;
474     // Get sprite dimensions
475     w = sprite->width;
476     h = sprite->height;
477     // trivial clip all directions
478     if(is_flip)
479     {
480         if(x - w >= xmax)
481         {
482             return;
483         }
484         if(x <= xmin)
485         {
486             return;
487         }
488     }
489     else
490     {
491         if(x >= xmax)
492         {
493             return;
494         }
495         if((x + w) <= xmin)
496         {
497             return;
498         }
499     }
500     if(y >= ymax)
501     {
502         return;
503     }
504     if((y + h) <= ymin)
505     {
506         return;
507     }
508     // Init line table pointer
509     linetab = (int *)(sprite->data);
510     // clip top
511     if(y < ymin)
512     {
513         h += y - ymin; // subtract from height
514         linetab -= y - ymin; // add to linetab
515         y = ymin; // add to y
516     }
517     // clip bottom
518     if((y + h) > ymax)
519     {
520         h = ymax - y;
521     }
522     // calculate destination pointer
523     dest = ((unsigned char *)(screen->data)) + y * screenwidth;
524 
525     if(blend && remap)
526     {
527         if(is_flip)
528         {
529             putsprite_remapblend_flip_(dest, x, xmin, xmax, linetab, h, screenwidth, remap, blend);
530         }
531         else
532         {
533             putsprite_remapblend_     (dest, x, xmin, xmax  , linetab, h, screenwidth, remap, blend);
534         }
535     }
536     else if(blend)
537     {
538         if(is_flip)
539         {
540             putsprite_blend_flip_(dest, x, xmin, xmax, linetab, h, screenwidth, blend);
541         }
542         else
543         {
544             putsprite_blend_     (dest, x, xmin, xmax  , linetab, h, screenwidth, blend);
545         }
546     }
547     else if(remap)
548     {
549         if(is_flip)
550         {
551             putsprite_remap_flip_(dest, x, xmin, xmax, linetab, h, screenwidth, remap);
552         }
553         else
554         {
555             putsprite_remap_     (dest, x, xmin, xmax  , linetab, h, screenwidth, remap);
556         }
557     }
558     else
559     {
560         if(is_flip)
561         {
562             putsprite_flip_      (dest, x, xmin, xmax, linetab, h, screenwidth);
563         }
564         else
565         {
566             putsprite_           (dest, x, xmin, xmax  , linetab, h, screenwidth);
567         }
568     }
569 }
570 
571 /////////////////////////////////////////////////////////////////////////////
572 
573 // scalex scaley flipy ...
putsprite_ex(int x,int y,s_sprite * frame,s_screen * screen,s_drawmethod * drawmethod)574 void putsprite_ex(int x, int y, s_sprite *frame, s_screen *screen, s_drawmethod *drawmethod)
575 {
576     gfx_entry gfx;
577 
578     if(!drawmethod->scalex || !drawmethod->scaley)
579     {
580         return;    // zero size
581     }
582 
583     // no scale, no shift, no flip, no fill, so use common method
584     if(!drawmethod->water.watermode && drawmethod->scalex == 256 && drawmethod->scaley == 256 && !drawmethod->flipy && !drawmethod->shiftx && drawmethod->fillcolor == TRANSPARENT_IDX && !drawmethod->rotate)
585     {
586         if(drawmethod->flipx)
587         {
588             x += drawmethod->centerx;
589         }
590         else
591         {
592             x -= drawmethod->centerx;
593         }
594         y -= drawmethod->centery;
595         switch(screen->pixelformat)
596         {
597         case PIXEL_8:
598             putsprite_8(x, y, drawmethod->flipx, frame, screen, drawmethod->table, drawmethod->alpha > 0 ? blendtables[drawmethod->alpha - 1] : NULL);
599             break;
600         case PIXEL_16:
601             putsprite_x8p16(x, y, drawmethod->flipx, frame, screen, (unsigned short *)drawmethod->table, getblendfunction16(drawmethod->alpha));
602             break;
603         case PIXEL_32:
604             putsprite_x8p32(x, y, drawmethod->flipx, frame, screen, (unsigned *)drawmethod->table, getblendfunction32(drawmethod->alpha));
605             break;
606         }
607         return;
608     }
609 
610     gfx.sprite = frame;
611 
612     if(drawmethod->water.watermode == 3 && drawmethod->water.beginsize > 0)
613     {
614         gfx_draw_plane(screen, &gfx, x, y, frame->centerx, frame->centery, drawmethod);
615     }
616     else if(drawmethod->water.watermode && drawmethod->water.amplitude)
617     {
618         gfx_draw_water(screen, &gfx, x, y, frame->centerx, frame->centery, drawmethod);
619     }
620     else if(drawmethod->rotate)
621     {
622         gfx_draw_rotate(screen, &gfx, x, y, frame->centerx, frame->centery, drawmethod);
623     }
624     else
625     {
626         gfx_draw_scale(screen, &gfx, x, y, frame->centerx, frame->centery, drawmethod);
627     }
628 }
629 
_putsprite(int x,int y,s_sprite * sprite,s_screen * screen,s_drawmethod * drawmethod)630 static void _putsprite(int x, int y, s_sprite *sprite, s_screen *screen, s_drawmethod *drawmethod)
631 {
632     if(!drawmethod || drawmethod->flag == 0)
633     {
634         goto plainsprite;
635     }
636 
637     putsprite_ex(x, y, sprite, screen, drawmethod);
638     return;
639 plainsprite:
640     switch(screen->pixelformat)
641     {
642     case PIXEL_8:
643         putsprite_8(x, y, 0, sprite, screen, NULL, NULL);
644         break;
645     case PIXEL_16:
646         putsprite_x8p16(x, y, 0, sprite, screen, (unsigned short *)sprite->palette, NULL);
647         break;
648     case PIXEL_32:
649         putsprite_x8p32(x, y, 0, sprite, screen, (unsigned *)sprite->palette, NULL);
650         break;
651     }
652 }
653 
putsprite(int x,int y,s_sprite * sprite,s_screen * screen,s_drawmethod * drawmethod)654 void putsprite(int x, int y, s_sprite *sprite, s_screen *screen, s_drawmethod *drawmethod)
655 {
656     int xrepeat, yrepeat, xspan, yspan, i, j, dx, dy;
657 
658     drawmethod_global_init(drawmethod);
659 
660     if(drawmethod && drawmethod->flag)
661     {
662         xrepeat = drawmethod->xrepeat;
663         yrepeat = drawmethod->yrepeat;
664         xspan = drawmethod->xspan;
665         yspan = drawmethod->yspan;
666     }
667     else
668     {
669         xrepeat = yrepeat = 1;
670         xspan = yspan = 0;
671     }
672 
673     for(j = 0, dy = y; j < yrepeat; j++, dy += yspan)
674     {
675         for(i = 0, dx = x; i < xrepeat; i++, dx += xspan)
676         {
677             _putsprite(dx, dy, sprite, screen, drawmethod);
678         }
679     }
680 
681 }
682 
683 
684 
685 /////////////////////////////////////////////////////////////////////////////
686 //
687 // NULL for dest means do not actually encode
688 //
encodesprite(int centerx,int centery,s_bitmap * bitmap,s_sprite * dest)689 unsigned encodesprite(
690     int centerx, int centery,
691     s_bitmap *bitmap, s_sprite *dest
692 )
693 {
694     int x, x0, y, w, h, xoffset, origwidth;
695     unsigned char *data;
696     int *linetab;
697     unsigned char *src = bitmap->data;
698     int pb = PAL_BYTES, extrab;
699 
700     if(dest)
701     {
702         dest->magic = sprite_magic;
703     }
704 
705     if(bitmap->clipped_width <= 0 || bitmap->clipped_height <= 0)
706     {
707         // Image is empty (or bad), create an empty sprite
708         if(dest)
709         {
710             //dest->is_flip_of = NULL;
711             dest->centerx = 0;
712             dest->centery = 0;
713             dest->width = 0;
714             dest->height = 0;
715             dest->pixelformat = bitmap->pixelformat;
716             dest->mask = NULL;
717             dest->palette = NULL;
718         }
719         return sizeof(s_sprite);
720     }
721 
722     w = bitmap->clipped_width;
723     h = bitmap->clipped_height;
724     xoffset = bitmap->clipped_x_offset;
725     origwidth = bitmap->width;
726 
727     if(dest)
728     {
729         //dest->is_flip_of = NULL;
730         dest->centerx = centerx;
731         dest->centery = centery;
732         dest->width = w;
733         dest->height = h;
734         dest->pixelformat = bitmap->pixelformat;
735         dest->mask = NULL;
736     }
737     linetab = (int *)(dest->data);
738     data = (unsigned char *)(linetab + h);
739 
740     src += bitmap->clipped_y_offset * origwidth;
741     src += xoffset;
742 
743     for(y = 0; y < h; y++, src += origwidth)
744     {
745         if(dest)
746         {
747             linetab[y] = ((size_t)data) - ((size_t)(linetab + y));
748         }
749         x = 0;
750         for(;;)
751         {
752             // search for the first visible pixel
753             x0 = x;
754             for(; (x < w) && ((x - x0) < 0xFE); x++)
755             {
756                 if(src[x])
757                 {
758                     break;
759                 }
760             }
761             // handle EOL
762             if(x >= w)
763             {
764                 if(dest)
765                 {
766                     *data = 0xFF;
767                 }
768                 data++;
769                 break;
770             }
771             // encode clearcount
772             if(dest)
773             {
774                 *data = x - x0;
775             }
776             data++;
777             // if we're still not visible, encode a null visible count and continue
778             if(!src[x])
779             {
780                 if(dest)
781                 {
782                     *data = 0;
783                 }
784                 data++;
785                 continue;
786             }
787             // search for the first invisible pixel
788             x0 = x;
789             for(; (x < w) && ((x - x0) < 0xFF); x++)
790             {
791                 if(!src[x])
792                 {
793                     break;
794                 }
795             }
796             // encode viscount and visible pixels
797             if(dest)
798             {
799                 *data++ = x - x0;
800                 memcpy(data, src + x0, x - x0);
801                 data += x - x0;
802             }
803             else
804             {
805                 data += 1 + (x - x0);
806             }
807         }
808     }
809 
810     if(!bitmap->palette)
811     {
812         pb = extrab = 0;
813     }
814     else
815     {
816         extrab = ((size_t)data) - ((size_t)dest);
817         extrab %= 4;
818         extrab = 4 - extrab;
819         extrab %= 4;
820     }
821 
822     //point palette to the last byte of the pixel data
823     if(dest)
824     {
825         if(bitmap->palette) // if the bitmap contains palette, copy it
826         {
827             dest->palette = ((unsigned char *)data) + extrab ;
828             memcpy(dest->palette, bitmap->palette, pb);
829         }
830         else
831         {
832             dest->palette = NULL;
833         }
834     }
835     return ((size_t)data) - ((size_t)dest) + extrab + pb + ANYNUMBER;
836 }
837 
838 /////////////////////////////////////////////////////////////////////////////
839 
fakey_encodesprite(s_bitmap * bitmap)840 unsigned fakey_encodesprite(s_bitmap *bitmap)
841 {
842     return encodesprite(0, 0, bitmap, NULL);
843 }
844 
845 /////////////////////////////////////////////////////////////////////////////
846 
847