gr_copy_palette no really a kludge, I think
[btb/d2x.git] / 2d / scale.c
1 /* $Id: scale.c,v 1.3 2002-07-17 21:55:19 bradleyb Exp $ */
2 /*
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
12 COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
13 */
14 /*
15  * 
16  * Routines for scaling a bitmap.
17  * 
18  * Old Log:
19  *
20  * Revision 1.12  1995/03/14  15:14:11  john
21  * Increased max scanline length to 640.
22  * ..
23  * 
24  * Revision 1.11  1994/11/27  12:56:39  matt
25  * Took out unneeded include of 3d.h
26  * 
27  * Revision 1.10  1994/11/18  22:50:25  john
28  * Changed shorts to ints in parameters.
29  * 
30  * Revision 1.9  1994/11/09  16:35:02  john
31  * First version with working RLE bitmaps.
32  * 
33  * Revision 1.8  1994/06/09  13:15:17  john
34  * *** empty log message ***
35  * 
36  * Revision 1.7  1994/06/07  11:47:02  john
37  * Added back in the fast code for scaling up bitmaps.
38  * 
39  * Revision 1.6  1994/02/18  15:32:36  john
40  * *** empty log message ***
41  * 
42  * Revision 1.5  1994/01/22  14:35:01  john
43  * Added transparency as color index 255.
44  * 
45  * Revision 1.4  1994/01/17  16:59:12  john
46  * once again...
47  * 
48  * Revision 1.3  1994/01/17  16:51:17  john
49  * Added check so we don't draw outsibe
50  * the source bitmap's v coordinate... kind
51  * of a hack, but works.
52  * 
53  * Revision 1.2  1994/01/12  18:03:26  john
54  * The first iteration of fast scaler..
55  * 
56  * Revision 1.1  1994/01/11  14:48:42  john
57  * Initial revision
58  * 
59  * 
60  */
61
62 #ifdef HAVE_CONFIG_H
63 #include <conf.h>
64 #endif
65
66 #ifdef RCS
67 static char rcsid[] = "$Id: scale.c,v 1.3 2002-07-17 21:55:19 bradleyb Exp $";
68 #endif
69
70 #include <math.h>
71 #include <limits.h>
72 #include <stdio.h>
73 #include <stdlib.h>
74
75 #include "mono.h"
76 #include "fix.h"
77 #include "gr.h"
78 #include "error.h"
79 #include "rle.h"
80
81 #if 0
82 #define TRANSPARENCY_COLOR 255;
83 #endif
84
85 static int Transparency_color = TRANSPARENCY_COLOR;
86
87 #include "scalea.h"
88
89 void rls_stretch_scanline( char * source, char * dest, int XDelta, int YDelta );
90 void rls_stretch_scanline_setup( int XDelta, int YDelta );
91 void scale_bitmap_asm(grs_bitmap *source_bmp, grs_bitmap *dest_bmp, int x0, int y0, int x1, int y1, fix u0, fix v0,  fix u1, fix v1  );
92 void scale_bitmap_asm_rle(grs_bitmap *source_bmp, grs_bitmap *dest_bmp, int x0, int y0, int x1, int y1, fix u0, fix v0,  fix u1, fix v1  );
93 void scale_bitmap_cc_asm(grs_bitmap *source_bmp, grs_bitmap *dest_bmp, int x0, int y0, int x1, int y1, fix u0, fix v0,  fix u1, fix v1  );
94 void scale_bitmap_cc_asm_rle(grs_bitmap *source_bmp, grs_bitmap *dest_bmp, int x0, int y0, int x1, int y1, fix u0, fix v0,  fix u1, fix v1  );
95
96 void scale_row_c( ubyte * sbits, ubyte * dbits, int width, fix u, fix du )
97 {
98         int i;
99         ubyte c;
100
101         for ( i=0; i<width; i++ )       {
102                 c = sbits[ f2i(u) ];
103
104                 if ( c != Transparency_color )
105                         *dbits = c;
106                         
107                 dbits++;
108                 u += du;
109         }
110 }
111
112 #define FIND_SCALED_NUM(x,x0,x1,y0,y1) (fixmuldiv((x)-(x0),(y1)-(y0),(x1)-(x0))+(y0))
113
114 // Scales bitmap, bp, into vertbuf[0] to vertbuf[1]
115 void scale_bitmap(grs_bitmap *bp, grs_point *vertbuf ,int orientation)
116 {
117         grs_bitmap * dbp = &grd_curcanv->cv_bitmap;
118         fix x0, y0, x1, y1;
119         fix u0, v0, u1, v1;
120         fix clipped_x0, clipped_y0, clipped_x1, clipped_y1;
121         fix clipped_u0, clipped_v0, clipped_u1, clipped_v1;
122         fix xmin, xmax, ymin, ymax;
123         int dx0, dy0, dx1, dy1;
124         int dtemp;
125         // Set initial variables....
126
127         x0 = vertbuf[0].x; y0 = vertbuf[0].y;
128         x1 = vertbuf[2].x; y1 = vertbuf[2].y;
129
130         xmin = 0; ymin = 0;
131         xmax = i2f(dbp->bm_w)-fl2f(.5); ymax = i2f(dbp->bm_h)-fl2f(.5);
132
133         u0 = i2f(0); v0 = i2f(0);
134         u1 = i2f(bp->bm_w-1); v1 = i2f(bp->bm_h-1);
135
136         // Check for obviously offscreen bitmaps...
137         if ( (y1<=y0) || (x1<=x0) ) return;
138         if ( (x1<0 ) || (x0>=xmax) ) return;
139         if ( (y1<0 ) || (y0>=ymax) ) return;
140
141         clipped_u0 = u0; clipped_v0 = v0;
142         clipped_u1 = u1; clipped_v1 = v1;
143
144         clipped_x0 = x0; clipped_y0 = y0;
145         clipped_x1 = x1; clipped_y1 = y1;
146
147         // Clip the left, moving u0 right as necessary
148         if ( x0 < xmin )        {
149                 clipped_u0 = FIND_SCALED_NUM(xmin,x0,x1,u0,u1);
150                 clipped_x0 = xmin;
151         }
152
153         // Clip the right, moving u1 left as necessary
154         if ( x1 > xmax )        {
155                 clipped_u1 = FIND_SCALED_NUM(xmax,x0,x1,u0,u1);
156                 clipped_x1 = xmax;
157         }
158
159         // Clip the top, moving v0 down as necessary
160         if ( y0 < ymin )        {
161                 clipped_v0 = FIND_SCALED_NUM(ymin,y0,y1,v0,v1);
162                 clipped_y0 = ymin;
163         }
164
165         // Clip the bottom, moving v1 up as necessary
166         if ( y1 > ymax )        {
167                 clipped_v1 = FIND_SCALED_NUM(ymax,y0,y1,v0,v1);
168                 clipped_y1 = ymax;
169         }
170         
171         dx0 = f2i(clipped_x0); dx1 = f2i(clipped_x1);
172         dy0 = f2i(clipped_y0); dy1 = f2i(clipped_y1);
173
174         if (dx1<=dx0) return;
175         if (dy1<=dy0) return;
176
177         Assert( dx0>=0 );
178         Assert( dy0>=0 );
179         Assert( dx1<dbp->bm_w );
180         Assert( dy1<dbp->bm_h );
181         Assert( f2i(u0)<=f2i(u1) );
182         Assert( f2i(v0)<=f2i(v1) );
183         Assert( f2i(u0)>=0 );
184         Assert( f2i(v0)>=0 );
185         Assert( u1<i2f(bp->bm_w) );
186         Assert( v1<i2f(bp->bm_h) );
187
188         //mprintf( 0, "(%.2f,%.2f) to (%.2f,%.2f) using (%.2f,%.2f) to (%.2f,%.2f)\n", f2fl(clipped_x0), f2fl(clipped_y0), f2fl(clipped_x1), f2fl(clipped_y1), f2fl(clipped_u0), f2fl(clipped_v0), f2fl(clipped_u1), f2fl(clipped_v1) );
189
190         dtemp = f2i(clipped_u1)-f2i(clipped_u0);
191
192         if ( bp->bm_flags & BM_FLAG_RLE )       {
193                 if ( (dtemp < (f2i(clipped_x1)-f2i(clipped_x0))) && (dtemp>0) )
194                         scale_bitmap_cc_asm_rle(bp, dbp, dx0, dy0, dx1, dy1, clipped_u0, clipped_v0, clipped_u1, clipped_v1  );
195                 else
196                         scale_bitmap_asm_rle(bp, dbp, dx0, dy0, dx1, dy1, clipped_u0, clipped_v0, clipped_u1, clipped_v1  );
197         } else {
198                 if ( (dtemp < (f2i(clipped_x1)-f2i(clipped_x0))) && (dtemp>0) )
199                         scale_bitmap_cc_asm(bp, dbp, dx0, dy0, dx1, dy1, clipped_u0, clipped_v0, clipped_u1, clipped_v1  );
200                 else
201                         scale_bitmap_asm(bp, dbp, dx0, dy0, dx1, dy1, clipped_u0, clipped_v0, clipped_u1, clipped_v1  );
202         }
203 }
204
205
206 void scale_bitmap_c(grs_bitmap *source_bmp, grs_bitmap *dest_bmp, int x0, int y0, int x1, int y1, fix u0, fix v0,  fix u1, fix v1  )
207 {
208         fix u, v, du, dv;
209         int x, y;
210         ubyte * sbits, * dbits;
211
212         du = (u1-u0) / (x1-x0);
213         dv = (v1-v0) / (y1-y0);
214
215         v = v0;
216
217         for (y=y0; y<=y1; y++ )                 {
218                 sbits = &source_bmp->bm_data[source_bmp->bm_rowsize*f2i(v)];
219                 dbits = &dest_bmp->bm_data[dest_bmp->bm_rowsize*y+x0];
220                 u = u0; 
221                 v += dv;
222                 for (x=x0; x<=x1; x++ )                 {
223                         *dbits++ = sbits[ u >> 16 ];
224                         u += du;
225                 }
226         }
227 }
228
229 void scale_bitmap_asm(grs_bitmap *source_bmp, grs_bitmap *dest_bmp, int x0, int y0, int x1, int y1, fix u0, fix v0,  fix u1, fix v1  )
230 {
231         fix du, dv, v;
232         int y;
233
234         du = (u1-u0) / (x1-x0);
235         dv = (v1-v0) / (y1-y0);
236
237         v = v0;
238
239         for (y=y0; y<=y1; y++ )                 {
240                 scale_row_asm_transparent( &source_bmp->bm_data[source_bmp->bm_rowsize*f2i(v)], &dest_bmp->bm_data[dest_bmp->bm_rowsize*y+x0], x1-x0+1, u0, du );
241                 v += dv;
242         }
243 }
244
245 ubyte scale_rle_data[640];
246
247 void decode_row( grs_bitmap * bmp, int y )
248 {
249         int i, offset=4+bmp->bm_h;
250         
251         for (i=0; i<y; i++ )
252                 offset += bmp->bm_data[4+i];
253         gr_rle_decode( &bmp->bm_data[offset], scale_rle_data );
254 }
255
256 void scale_bitmap_asm_rle(grs_bitmap *source_bmp, grs_bitmap *dest_bmp, int x0, int y0, int x1, int y1, fix u0, fix v0,  fix u1, fix v1  )
257 {
258         fix du, dv, v;
259         int y, last_row=-1;
260
261         du = (u1-u0) / (x1-x0);
262         dv = (v1-v0) / (y1-y0);
263
264         v = v0;
265
266         for (y=y0; y<=y1; y++ )                 {
267                 if ( f2i(v) != last_row )       {
268                         last_row = f2i(v);
269                         decode_row( source_bmp, last_row );
270                 }
271                 scale_row_asm_transparent( scale_rle_data, &dest_bmp->bm_data[dest_bmp->bm_rowsize*y+x0], x1-x0+1, u0, du );
272                 v += dv;
273         }
274 }
275
276
277 void scale_bitmap_cc_asm(grs_bitmap *source_bmp, grs_bitmap *dest_bmp, int x0, int y0, int x1, int y1, fix u0, fix v0,  fix u1, fix v1  )
278 {
279         fix dv, v;
280         int y;
281
282         dv = (v1-v0) / (y1-y0);
283                 
284         rls_stretch_scanline_setup( (int)(x1-x0), f2i(u1)-f2i(u0) );
285         if ( scale_ydelta_minus_1 < 1 ) return;
286         rls_do_cc_setup_asm();
287
288         v = v0;
289
290         for (y=y0; y<=y1; y++ )                 {
291                 scale_source_ptr = &source_bmp->bm_data[source_bmp->bm_rowsize*f2i(v)+f2i(u0)];
292                 scale_dest_ptr = &dest_bmp->bm_data[dest_bmp->bm_rowsize*y+x0];
293                 scale_do_cc_scanline();
294                 v += dv;
295         }
296 }
297
298 void scale_bitmap_cc_asm_rle(grs_bitmap *source_bmp, grs_bitmap *dest_bmp, int x0, int y0, int x1, int y1, fix u0, fix v0,  fix u1, fix v1  )
299 {
300         fix dv, v;
301         int y, last_row = -1;
302
303         dv = (v1-v0) / (y1-y0);
304                 
305         rls_stretch_scanline_setup( (int)(x1-x0), f2i(u1)-f2i(u0) );
306         if ( scale_ydelta_minus_1 < 1 ) return;
307         rls_do_cc_setup_asm();
308
309         v = v0;
310
311         for (y=y0; y<=y1; y++ )                 {
312                 if ( f2i(v) != last_row )       {
313                         last_row = f2i(v);
314                         decode_row( source_bmp, last_row );
315                 }
316                 //scale_source_ptr = &source_bmp->bm_data[source_bmp->bm_rowsize*f2i(v)+f2i(u0)];
317                 scale_source_ptr = &scale_rle_data[f2i(u0)];
318                 scale_dest_ptr = &dest_bmp->bm_data[dest_bmp->bm_rowsize*y+x0];
319                 scale_do_cc_scanline();
320                 v += dv;
321         }
322 }
323
324
325
326 // Run-length slice bitmap scan line stretcher 
327
328 void DrawHorizontalRun(char *ScreenPtr, int RunLength, int Color)
329 {
330    int i;
331
332    for (i=0; i<RunLength; i++)
333       *ScreenPtr++ = Color;
334 }
335
336 void rls_stretch_scanline( char * source, char * dest, int XDelta, int YDelta )
337 {
338            int AdjUp, AdjDown, ErrorTerm;
339         int WholeStep, InitialPixelCount, FinalPixelCount, i, RunLength;
340
341       /* X major line */
342       /* Minimum # of pixels in a run in this line */
343       WholeStep = XDelta / YDelta;
344
345       /* Error term adjust each time Y steps by 1; used to tell when one
346          extra pixel should be drawn as part of a run, to account for
347          fractional steps along the X axis per 1-pixel steps along Y */
348       AdjUp = (XDelta % YDelta) * 2;
349
350       /* Error term adjust when the error term turns over, used to factor
351          out the X step made at that time */
352       AdjDown = YDelta * 2;
353
354       /* Initial error term; reflects an initial step of 0.5 along the Y
355          axis */
356       ErrorTerm = (XDelta % YDelta) - (YDelta * 2);
357
358       /* The initial and last runs are partial, because Y advances only 0.5
359          for these runs, rather than 1. Divide one full run, plus the
360          initial pixel, between the initial and last runs */
361       InitialPixelCount = (WholeStep / 2) + 1;
362       FinalPixelCount = InitialPixelCount;
363
364       /* If the basic run length is even and there's no fractional
365          advance, we have one pixel that could go to either the initial
366          or last partial run, which we'll arbitrarily allocate to the
367          last run */
368       if ((AdjUp == 0) && ((WholeStep & 0x01) == 0))
369       {
370          InitialPixelCount--;
371       }
372      /* If there're an odd number of pixels per run, we have 1 pixel that can't
373      be allocated to either the initial or last partial run, so we'll add 0.5
374      to error term so this pixel will be handled by the normal full-run loop */
375       if ((WholeStep & 0x01) != 0)
376       {
377          ErrorTerm += YDelta;
378       }
379       /* Draw the first, partial run of pixels */
380                 //if ( *source != Transparency_color )
381         rep_stosb(dest, InitialPixelCount, *source );
382                 dest += InitialPixelCount;
383                 source++;
384
385       /* Draw all full runs */
386       for (i=0; i<(YDelta-1); i++)
387       {
388          RunLength = WholeStep;  /* run is at least this long */
389
390          /* Advance the error term and add an extra pixel if the error term so indicates */
391          if ((ErrorTerm += AdjUp) > 0)
392          {
393             RunLength++;
394             ErrorTerm -= AdjDown;   /* reset the error term */
395          }
396          /* Draw this scan line's run */
397
398                         //if ( *source != Transparency_color )
399                 rep_stosb(dest, RunLength, *source );
400                         dest += RunLength;
401                         source++;
402
403       }
404
405       /* Draw the final run of pixels */
406                 //if ( *source != Transparency_color )
407               rep_stosb(dest, FinalPixelCount, *source );
408
409       return;
410 }
411
412
413
414
415 void rls_stretch_scanline_setup( int XDelta, int YDelta )
416 {
417                 scale_trans_color = Transparency_color & 0xFF;
418                 scale_ydelta_minus_1 = YDelta - 1;
419
420       /* X major line */
421       /* Minimum # of pixels in a run in this line */
422       scale_whole_step = XDelta / YDelta;
423
424       /* Error term adjust each time Y steps by 1; used to tell when one
425          extra pixel should be drawn as part of a run, to account for
426          fractional steps along the X axis per 1-pixel steps along Y */
427       scale_adj_up = (XDelta % YDelta) * 2;
428
429       /* Error term adjust when the error term turns over, used to factor
430          out the X step made at that time */
431       scale_adj_down = YDelta * 2;
432
433       /* Initial error term; reflects an initial step of 0.5 along the Y
434          axis */
435       scale_error_term = (XDelta % YDelta) - (YDelta * 2);
436
437       /* The initial and last runs are partial, because Y advances only 0.5
438          for these runs, rather than 1. Divide one full run, plus the
439          initial pixel, between the initial and last runs */
440       scale_initial_pixel_count = (scale_whole_step / 2) + 1;
441       scale_final_pixel_count = scale_initial_pixel_count;
442
443       /* If the basic run length is even and there's no fractional
444          advance, we have one pixel that could go to either the initial
445          or last partial run, which we'll arbitrarily allocate to the
446          last run */
447       if ((scale_adj_up == 0) && ((scale_whole_step & 0x01) == 0))
448       {
449          scale_initial_pixel_count--;
450       }
451      /* If there're an odd number of pixels per run, we have 1 pixel that can't
452      be allocated to either the initial or last partial run, so we'll add 0.5
453      to error term so this pixel will be handled by the normal full-run loop */
454       if ((scale_whole_step & 0x01) != 0)
455       {
456          scale_error_term += YDelta;
457       }
458
459 }
460