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