/* Hovertank 3-D Source Code * Copyright (C) 1993-2014 Flat Rock Software * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "HOVERDEF.H" /* ============================================================================= Id Software EGA scaling routines by John Carmack, 4-16-91 ------------------------ These routines implement SPARSE SCALING, a very fast way to draw a given shape at various sizes. SC_Setup -------- Builds tables used by the drawing routines to select which pixels to draw at different scales. A scale of 256 is regular size, 128 half size, 512 double size, etc. Usually you won't need exact precision in scaling, so some tables are used for a few scales. Basetables convert from a given pixel offset on the original shape to pixels on the scaled shape, screentables convert from a scaled offset to the original offset. SC_MakeShape ------------ Converts a standard four plane pic or sprite of the given width/height to sparse scaling format. The pixels are converted from four individual bits to segments of byte values (0-15). Pixels of BACKGROUND color are considered masks, and will not apear in the scaled shape. By considering the shape as a list of vertical line segments no masking is needed, as only visable pixels are scaled. SC_ScaleShape ------------- Draws the shape CENTERED at the given x,y at the given scale, clipped to scalexl,scalexh,scaleyl,scaleyh (inclusive). The drawing is done vertically so the bit mask register need only be set once for each line. SC_ScaleLine (ASM) ------------ Low level scaling routine to scale a given source of byte values (0-15) to a given point on the screen. No bounds checking. This is self modifying unwound code (coding purists go hide), with a string of 200 scaling operations that get a RET stuck in the code after the number of pixels that need to be scaled have been. DEPENDENCIES ------------ LIMITATIONS ----------- Shapes must never scale over MAXHEIGHT pixels high, or the scale tables are invalid. Greater than 256 character height is impossible without major changes in any case. The clipping bounds must be between 0 and 320 horizontally or the byte/mask tables are invalid. The segmented scaling isn't perfect POSSIBLE IMPROVEMENTS --------------------- 256 color VGA scaling can be done over twice as fast as 16 color EGA because the pixels can be written out without having to load the latches! Sparsing the shape with horizontal scans would also make an improvement. SCALESTEP probably shouldn't be linear, as size change perception varies with the original size Seperate horizontal/vertical scaling for stretching effects vertical clipping Bitmap holography... (coming soon!) ============================================================================= */ #define MAXPICHEIGHT 256 // tallest pic to be scaled #define BACKGROUND 5 // background pixel for make shape #define BASESCALE 64 // normal size #define MAXSCALE 256 // largest scale possible #define SCALESTEP 3 #define DISCREETSCALES (MAXSCALE/SCALESTEP) typedef struct scseg { int start; // relative to top of shape int length; // pixels in this segment unsigned next; // offset from segment, NULL if last segment char data[]; // pixel values } scaleseg; typedef struct { int width; // number of vertical lines int height; // only used for centering unsigned first[]; // offsets from segment to topmost segment on // each line, NULL if no pixels on line } scaleshape; #define SCREENPIXELS 320 unsigned char bytetable[SCREENPIXELS],masktable[SCREENPIXELS]; memptr basetableseg,screentableseg; // segment of basetables and screentables unsigned basetables[DISCREETSCALES], // offsets in basetableseg screentables[DISCREETSCALES]; // offsets in screentableseg int scalexl = 0, scalexh = 319, scaleyl = 0, scaleyh = 144; unsigned scaleblockwidth, scaleblockheight, scaleblockdest; //========================================================================== /* =========================== = = SC_Setup = =========================== */ void SC_Setup (void) { unsigned mask,i,step,scale,space; unsigned char far *baseptr, far *screenptr; unsigned offset1,offset2,size; // // fast ploting tables // mask = 128; for (i=0;i<320;i++) { bytetable[i]=i/8; masktable[i]=mask; if (!(mask>>=1)) mask = 128; } // // fast scaling tables // offset1 = offset2 = 0; for (step=0;stepwidth = pixwidth; // pixel dimensions tempseg->height = height; // // convert ega pixels to byte color values in a temp buffer // // Stored in a collumn format, not rows! // MMGetPtr(&byteseg,pixwidth*height); byteptr = (char far *)byteseg; plane0 = src; plane1 = plane0 + width*height; plane2 = plane1 + width*height; plane3 = plane2 + width*height; for (x=0;xfirst[pixwidth]; // start filling in data after all pointers to line segments byteptr = byteseg; // first pixel in byte array for (x=0;xfirst[x]; *segptr = 0; // in case there are no segments on line do { // scan for first pixel to be scaled while (*byteptr == BACKGROUND && ydata[0]; // // copy bytes in the segment to the shape // while (*byteptr != BACKGROUND && ystart = start; ((scaleseg far *)saveptr)->length = length; ((scaleseg far *)saveptr)->next = 0; // get ready for next segment segptr = (unsigned far *)&((scaleseg far *)saveptr)->next; saveptr = dataptr; // next free byte to be used } while (y=DISCREETSCALES) scalechop = DISCREETSCALES-1; basetoscreenptr = (unsigned char _seg *)basetableseg + basetables[scalechop]; screentobaseptr = (unsigned char _seg *)screentableseg + screentables[scalechop]; // // figure bounding rectangle for scaled image // fullwidth = ((scaleshape _seg *)shape)->width; fullheight = ((scaleshape _seg *)shape)->height; scalewidth = fullwidth*((scalechop+1)*SCALESTEP)/BASESCALE; //basetoscreenptr[fullwidth-1]; scaleheight = basetoscreenptr[fullheight-1]; xl=x-scalewidth/2; xh=xl+scalewidth-1; yl=y-scaleheight/2; yh=yl+scaleheight-1; // off screen? if (xl>scalexh || xhscaleyh || yhscalexh) sxh=scalexh; else sxh=xh; // // clip both sides to zbuffer // sx=sxl; while (zbuffer[sx]>scale && sx<=sxh) sx++; sxl=sx; sx=sxh; while (zbuffer[sx]>scale && sx>sxl) sx--; sxh=sx; if (sxl>sxh) return 0; // behind a wall // // save block info for background erasing // screencorner = screenofs+yl*linewidth; scaleblockdest = screencorner + sxl/8; scaleblockwidth = sxh/8-sxl/8+1; scaleblockheight = yh-yl+1; // // start drawing // for (sx=sxl;sx<=sxh;sx++) { shapex=screentobaseptr[sx-xl]; if ( (shapeofs = ((scaleshape _seg *)shape)->first[shapex]) != 0) { xbyte = bytetable[sx]; mask = masktable[sx]; if (scale>BASESCALE) { // // make a multiple pixel scale pass if possible // while ( ( (sx&7) != 7) && (screentobaseptr[sx+1-xl] == shapex) ) { sx++; mask |= masktable[sx]; } } // // set bit mask // asm mov ah,[BYTE PTR mask] asm mov al,GC_BITMASK asm mov dx,GC_INDEX asm out dx,ax do { shapeptr=MK_FP(FP_SEG(shape),shapeofs); yoffset=basetoscreenptr[shapeptr->start];// pixels on screen to be skipped screen=screencorner+ylookup[yoffset]+xbyte; ScaleLine (basetoscreenptr[shapeptr->length], screentobaseptr, &shapeptr->data[0], screen); shapeofs = shapeptr->next; } while (shapeofs); } } return 1; }