// Copyright (C) 1996 Keith Whitwell. // This file may only be copied under the terms of the GNU Library General // Public License - see the file COPYING in the lib3d distribution. #include #include #include #include class Viewport16Bpp : public Viewport { public: void line(const DeviceVector &from, const DeviceVector &to, DeviceColour colour); void flatTriangleZb(PipelineData *const[], Colour colour ); void flatPolygonZb(uint nr, PipelineData *const[], Colour colour ); Colour getColour( uint r, uint g, uint b ); void smoothPolygonZb(uint, SmoothPipelineData *const[], const ColourRamp& ); void smoothTriangleZb(SmoothPipelineData *const[], const ColourRamp& ); void drawColourSpace(); const char *getName() const { return "Viewport16Bpp"; } protected: Viewport *clone( Device *device ); ~Viewport16Bpp(); Viewport16Bpp( Exemplar e ) : Viewport( e, 50 ) {} Viewport16Bpp( Device * ); protected: static Viewport16Bpp *exemplar; }; Viewport16Bpp *Viewport16Bpp::exemplar = new Viewport16Bpp( Exemplar() ); Viewport16Bpp::Viewport16Bpp( Device *device ) : Viewport( device, 1, 1 ) { } Viewport16Bpp::~Viewport16Bpp() { } Viewport * Viewport16Bpp::clone( Device *device ) { uint depth = device->getDepth(); if (depth == 16) { // too strict? debug() << "Creating new viewport for 16 bit device." << endlog; return new Viewport16Bpp( device ); } else { debug() << "Viewport16Bpp not suitable for " << depth << " bit device." << endlog; return 0; } } Colour Viewport16Bpp::getColour( uint r, uint g, uint b ) { // For a 565 bit layout. Other 16bpp viewports need only override // this function. return ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | ((b & 0xf8) >> 3); } #define EDGEMAX 1024 extern int ex[2*EDGEMAX]; // danger! extern uint ez[EDGEMAX]; void Viewport16Bpp::flatPolygonZb(uint nr, PipelineData * const vertex[], Colour colour) { if (nr == 3) { Viewport16Bpp::flatTriangleZb( vertex, colour ); return; } uint i; int x1 = vertex[nr-1]->device.v[X]; int y1 = vertex[nr-1]->device.v[Y]; uint z1 = uint(vertex[nr-1]->device.v[Z]); int ymin = y1; int ymax = y1; // Need to find ymid in advance for ( int k = nr-2 ; k >= 0; k-- ) { int y = int(vertex[k]->device.v[Y]); if (y < ymin) ymin = y; if (y > ymax) ymax = y; } int ymid = (ymin + ymax) >> 1; int zmid = 0; // z value of rhs of polygon at ymid. for ( i = 0 ; i < nr; i++ ) { int x0 = x1; int y0 = y1; uint z0 = z1; y1 = int(vertex[i]->device.v[Y]); x1 = int(vertex[i]->device.v[X]); z1 = uint(vertex[i]->device.v[Z]); int dy = y1 - y0; if (dy == 0) continue; int dx; int x; // Start point for edge rasterization. int *ptr; // Into edge rasterization buffer. if (dy < 0) { if ( y0 == ymid ) { zmid = z0; } else if ( y0 >= ymid && y1 <= ymid ) { // Includes the middle scanline of the polygon. zmid = int(z0) + ((ymid - y0)*(int(z1) - int(z0))) / dy; } // We always want to work down the screen, so reverse the // direction of upwards edges. These should always be // right-hand edges, because we have forced a clockwise // traversal of the vertices. dy = -dy; ptr = ex + y1*2 + 1; x = x1 * 256; dx = (x0 * 256) - x; } else { // Left edge. We also want to interpolate the z coordinates. uint *zptr = ez + y0; uint z = z0 + zBuffer->getGenerationMask(); int dz = z1 - z0; int zSlope = dz / dy; int k = dy+1; do { *zptr++ = z; z += zSlope; } while (--k); // Set up for x interpolation. ptr = ex + y0*2; x = x0 * 256; dx = (x1 * 256) - x; } int xSlope = dx / dy; for (int k = 0; k++ <= dy ; ) { *ptr = x>>8; ptr += 2; x += xSlope; } } zmid += zBuffer->getGenerationMask(); int xspan = (ex[(ymid<<1)+1] - ex[ymid<<1]); if (xspan <= 0) return; int zSlope = (zmid - int(ez[ymid])) / xspan; ushort *cptr = ((ushort *)device->getBuffer()) + ymin * device->getWidth(); uint *zptr = zBuffer->getBuffer() + ymin * zBuffer->getWidth(); int *exptr = ex + ymin * 2; for ( int y = ymin ; y < ymax ; y++ ) { int xmin = *exptr++; int i = (*exptr++ - xmin) - 1; uint z; if (i >= 0) { ushort *cp = cptr + xmin; uint *zp = zptr + xmin; z = ez[y]; while ( i > 0 ) { if (z < zp[0]) { zp[0] = uint(z); cp[0] = colour; } z += zSlope; if (z < zp[1]) { zp[1] = uint(z); cp[1] = colour; } z += zSlope; zp+=2; cp+=2; i -= 2; } if (i == 0) { if (z < *zp) { *zp = uint(z); cp[0] = colour; } } } cptr += device->getWidth(); zptr += zBuffer->getWidth(); } } void Viewport16Bpp::line(const DeviceVector &from, const DeviceVector &to, DeviceColour colour) { int dx = to.v[X] - from.v[X]; int dy = to.v[Y] - from.v[Y]; if (dx == 0 && dy == 0) { return; } int width = device->getWidth(); int ipy; int ymin, xmin; if (dy < 0) { ymin = to.v[Y]; ipy = - width; dy = -dy; } else { ymin = from.v[Y]; ipy = width; } int ipx; if (dx < 0) { xmin = to.v[X]; ipx = -1; dx = -dx; } else { xmin = from.v[X]; ipx = 1; } zBuffer->setDirty( xmin, ymin, xmin+dx, ymin+dy ); device->setDirty( xmin, ymin, xmin+dx, ymin+dy ); ushort *cptr = ((ushort *)device->getBuffer() + from.v[X] + from.v[Y] * width); uint *zptr = (zBuffer->getBuffer() + from.v[X] + from.v[Y] * width); uint z = zBuffer->getGenerationMask(); int ipxy = ipx + ipy; if (dx > dy) { int iE = dy * 2; int d = iE - dx; // (dy * 2) - dx int iNE = d - dx; // (dy - dx) * 2 while (--dx) { *cptr = colour; *zptr = z; if (d <= 0) { d += iE; cptr += ipx; zptr += ipx; } else { d += iNE; cptr += ipxy; zptr += ipxy; } } } else { int iN = dx * 2; int d = iN - dy; // (dx * 2) - dy int iNE = d - dy; // (dx - dy) * 2 while (--dy) { *cptr = colour; *zptr = z; if (d <= 0) { d += iN; cptr += ipy; zptr += ipy; } else { d += iNE; cptr += ipxy; zptr += ipxy; } } } } void Viewport16Bpp::flatTriangleZb(PipelineData *const vertex[], Colour colour ) { int tx[3]; // type = 0 -- elbow is on lhs. // = 1 -- elbow is on rhs. // tx[0] = topmost vertex // tx[1] = elbow // tx[2] = bottommost vertex int y0 = vertex[0]->device.v[Y]; int y1 = vertex[1]->device.v[Y]; int y2 = vertex[2]->device.v[Y]; int type; if (y0 > y1) { if (y1 > y2) { type = 1; tx[0] = 2; tx[1] = 1; tx[2] = 0; } else { if (y0 > y2) { type = 0; tx[0] = 1; tx[1] = 2; tx[2] = 0; } else { type = 1; tx[0] = 1; tx[1] = 0; tx[2] = 2; } } } else { if (y2 > y1) { type = 0; tx[0] = 0; tx[1] = 1; tx[2] = 2; } else { if (y0 > y2) { type = 0; tx[0] = 2; tx[1] = 0; tx[2] = 1; } else { type = 1; tx[0] = 0; tx[1] = 2; tx[2] = 1; } } } if_debug { debug() << "In y order: " << endlog; debug() << "\t" << vertex[tx[0]]->device << endlog; debug() << "\t" << vertex[tx[1]]->device << endlog; debug() << "\t" << vertex[tx[2]]->device << endlog; debug() << "type: " << type << endlog; } int dy = vertex[tx[2]]->device.v[Y] - vertex[tx[0]]->device.v[Y]; if (dy == 0) { if_debug { debug() << "Zero height triangle" << endlog; } return; } uint *zptr = ez + vertex[tx[0]]->device.v[Y]; uint z = vertex[tx[0]]->device.v[Z] + zBuffer->getGenerationMask(); int dz = vertex[tx[2]]->device.v[Z] - vertex[tx[0]]->device.v[Z]; int zSlope = dz / dy; int *xptr = ex + (vertex[tx[0]]->device.v[Y] * 2); int x = vertex[tx[0]]->device.v[X] * 256; int dx = vertex[tx[2]]->device.v[0] - vertex[tx[0]]->device.v[0]; int xSlope = (dx * 256) / dy; int k = dy+1; if (dx != 0) { do { *xptr = x >> 8; xptr += 2; x += xSlope; *zptr++ = z; z += zSlope; } while (--k); } else { int xx = x >> 8; do { *xptr = xx; xptr += 2; *zptr++ = z; z += zSlope; } while (--k); } xptr = ex + (vertex[tx[0]]->device.v[Y] * 2) + 1; dy = vertex[tx[1]]->device.v[Y] - vertex[tx[0]]->device.v[Y]; if (dy != 0) { x = vertex[tx[0]]->device.v[X] * 256; dx = vertex[tx[1]]->device.v[X] - vertex[tx[0]]->device.v[X]; xSlope = (dx * 256) / dy; if (dx != 0) { do { *xptr = x >> 8; xptr += 2; x += xSlope; } while (--dy); } else { int xx = x >> 8; do { *xptr = xx; xptr += 2; } while (--dy); } } dy = vertex[tx[2]]->device.v[Y] - vertex[tx[1]]->device.v[Y]; if (dy != 0) { x = vertex[tx[1]]->device.v[X] * 256; dx = vertex[tx[2]]->device.v[X] - vertex[tx[1]]->device.v[X]; xSlope = (dx * 256) / dy; if (dx != 0) { do { *xptr = x >> 8; xptr += 2; x += xSlope; } while (--dy); } else { int xx = x >> 8; do { *xptr = xx; xptr += 2; } while (--dy); } } else { *xptr = x >> 8; } uint zmid = uint(vertex[tx[1]]->device.v[Z]) + zBuffer->getGenerationMask(); int ymin = vertex[tx[0]]->device.v[Y]; int ymid = vertex[tx[1]]->device.v[Y]; int ymax = vertex[tx[2]]->device.v[Y]; ushort *cptr = ((ushort *)device->getBuffer()) + ymin * device->getWidth(); zptr = (uint *)(zBuffer->getBuffer()) + ymin * zBuffer->getWidth(); xptr = ex + ymin * 2; if (type == 1) { int xspan = (ex[(ymid<<1)+1] - ex[ymid<<1]); if (xspan <= 0) { if_debug { if (xspan != 0) debug() << "degenerate triangle, y:" <= 0) { ushort c = colour; ushort *cp = cptr + xmin; uint *zp = zptr + xmin; z = ez[y]; while ( i > 0 ) { if (z < zp[0]) { zp[0] = uint(z); cp[0] = c; } z += zSlope; if (z < zp[1]) { zp[1] = uint(z); cp[1] = c; } z += zSlope; zp+=2; cp+=2; i -= 2; } if (i == 0) { if (z < *zp) { *zp = uint(z); cp[0] = c; } } } cptr += device->getWidth(); zptr += zBuffer->getWidth(); } } else { int xspan = (ex[(ymid<<1)] - ex[(ymid<<1)+1]); if (xspan <= 0) { if_debug { if (xspan != 0) debug() << "degenerate triangle, y:" <= 0) { ushort *cp = cptr + xmax -1; uint *zp = zptr + xmax -1; z = ez[y]; while ( i > 0 ) { if (z < zp[0]) { zp[0] = uint(z); cp[0] = colour; } z += zSlope; if (z < *(zp-1)) { *(zp-1) = uint(z); *(cp-1) = colour; } z += zSlope; zp -=2; cp -=2; i -= 2; } if (i == 0) { if (z < *zp) { *zp = uint(z); cp[0] = colour; } } } cptr += device->getWidth(); zptr += zBuffer->getWidth(); } } return; } /* * */ void Viewport16Bpp::drawColourSpace() { uint i; ushort *dptr = (ushort *)(device->getBuffer()); int wid = device->getWidth(); for (i = 0 ; i < 65536; i++) { uint idx =( (((i)&31) ) // horizontal spans + (((i>>5)&31) * (wid)) // arranged vertically + (((i>>10)&3) * (wid * 40)) // in tiles + (((i>>12) * 40))); // in 4 columns dptr[idx] = i; } for (i = 0 ; i < min(uint(256),getHeight()) ; i+=1) { uint idx = getColour( i, i, i ); for (int y = 0 ; y < 1 ; y++) { for (int x = 0 ; x < 32 ; x++ ) { dptr[200+x+((i+y)*wid)] = ushort(idx); } } } device->setDirty(0,0,device->getWidth(),device->getHeight()); } extern int ei[]; void Viewport16Bpp::smoothPolygonZb(uint nr, SmoothPipelineData * const vertex[], const ColourRamp &ramp ) { if (nr==3) { Viewport16Bpp::smoothTriangleZb(vertex, ramp); return; } int x1 = vertex[nr-1]->device.v[X]; int y1 = vertex[nr-1]->device.v[Y]; uint z1 = uint(vertex[nr-1]->device.v[Z]); int i1 = vertex[nr-1]->intensity; int ymin = y1; int ymax = y1; // Need to find ymid in advance for ( int k = nr-2 ; k >= 0; k-- ) { int y = int(vertex[k]->device.v[Y]); if (y < ymin) ymin = y; if (y > ymax) ymax = y; } if (ymin == ymax) return; int ymid = (ymin + ymax) >> 1; int zmid = 0; // z value of rhs of polygon at ymid. int imid = 0; for (uint v = 0 ; v < nr; v++ ) { int x0 = x1; int y0 = y1; uint z0 = z1; int i0 = i1; y1 = vertex[v]->device.v[Y]; x1 = vertex[v]->device.v[X]; z1 = uint(vertex[v]->device.v[Z]); i1 = vertex[v]->intensity; if_debug { debug() << "vertex " << v << ":" << vertex[v]->device << " intensity: " << i1 << endlog; } int dy = y1 - y0; if (dy == 0) continue; if (dy < 0) { if ( y0 == ymid ) { zmid = z0; imid = i0; } else if ( y0 > ymid && y1 <= ymid ) { // Includes the middle scanline of the polygon. zmid = int(z0) + ((ymid - y0)*(int(z1) - int(z0))) / dy; imid = i0 + ((ymid - y0)*(i1 - i0)) / dy; } dy = -dy; int *xptr = ex + y1*2 + 1; int x = x1 * 256; int dx = (x0 * 256) - x; int xSlope = dx / dy; int k = dy+1; do { *xptr = x>>8; xptr += 2; x += xSlope; } while(--k); } else { // Left edge. We also want to interpolate z and intensity. uint *zptr = ez + y0; uint z = z0 + zBuffer->getGenerationMask(); int dz = z1 - z0; int zSlope = dz / dy; int k = dy+1; int *xptr = ex + y0*2 ; int x = x0 * 256; int dx = (x1 * 256) - x; int xSlope = dx / dy; int i = i0; int di = i1 - i0; int iSlope = di / dy; int *iptr = ei + y0; do { *xptr = x>>8; xptr += 2; x += xSlope; *zptr = z; zptr++; z += zSlope; *iptr = i; i += iSlope; iptr++; } while (--k); } } zmid += zBuffer->getGenerationMask(); int xspan = (ex[(ymid<<1)+1] - ex[ymid<<1]); if (xspan <= 0) return; int zSlope = (zmid - int(ez[ymid])) / xspan; int iSlope = (imid - ei[ymid]) / xspan; ushort *cptr = ((ushort *)device->getBuffer()) + ymin * device->getWidth(); uint *zptr = zBuffer->getBuffer() + ymin * zBuffer->getWidth(); int *xptr = ex + ymin * 2; const ushort *dptr = (ushort *)ramp.getRamp(); for ( int y = ymin ; y < ymax ; y++ ) { int xmin = *xptr++; int wid = (*xptr++ - xmin) - 1; if (wid >= 0) { int i = ei[y]; ushort *cp = cptr + xmin; uint *zp = zptr + xmin; uint z = ez[y]; if ((wid & 1) == 0) { zp--; cp--; goto mid100; } do { if (z < zp[0]) { zp[0] = uint(z); cp[0] = dptr[(i>>8)*2]; } z += zSlope; i += iSlope; mid100: if (z < zp[1]) { zp[1] = uint(z); cp[1] = dptr[(i>>8)*2]; } z += zSlope; i += iSlope; zp+=2; cp+=2; wid -= 2; } while (wid >= 0); } cptr += device->getWidth(); zptr += zBuffer->getWidth(); } } void Viewport16Bpp::smoothTriangleZb(SmoothPipelineData * const vertex[], const ColourRamp &ramp ) { int tx[3]; int y0 = vertex[0]->device.v[Y]; int y1 = vertex[1]->device.v[Y]; int y2 = vertex[2]->device.v[Y]; int type; if (y0 > y1) { if (y1 > y2) { type = 1; tx[0] = 2; tx[1] = 1; tx[2] = 0; } else { if (y0 > y2) { type = 0; tx[0] = 1; tx[1] = 2; tx[2] = 0; } else { type = 1; tx[0] = 1; tx[1] = 0; tx[2] = 2; } } } else { if (y2 > y1) { type = 0; tx[0] = 0; tx[1] = 1; tx[2] = 2; } else { if (y0 > y2) { type = 0; tx[0] = 2; tx[1] = 0; tx[2] = 1; } else { type = 1; tx[0] = 0; tx[1] = 2; tx[2] = 1; } } } int dy = vertex[tx[2]]->device.v[Y] - vertex[tx[0]]->device.v[Y]; if (dy == 0) return; uint *zptr = ez + vertex[tx[0]]->device.v[Y]; uint z = vertex[tx[0]]->device.v[Z] + zBuffer->getGenerationMask(); int dz = vertex[tx[2]]->device.v[Z] - vertex[tx[0]]->device.v[Z]; int zSlope = dz / dy; int *iptr = ei + vertex[tx[0]]->device.v[Y]; int i = vertex[tx[0]]->intensity; int di = vertex[tx[2]]->intensity - i; int iSlope = di / dy; int k = dy+1; int *xptr = ex + (vertex[tx[0]]->device.v[Y] * 2); int x = vertex[tx[0]]->device.v[X] * 256; int dx = vertex[tx[2]]->device.v[0] - vertex[tx[0]]->device.v[0]; if (dx != 0) { int xSlope = (dx * 256) / dy; do { *xptr = x >> 8; xptr += 2; x += xSlope; *zptr++ = z; z += zSlope; *iptr = i; i += iSlope; iptr++; } while (--k); } else { int xx = x >> 8; do { *xptr = xx; xptr += 2; *zptr++ = z; z += zSlope; *iptr = i; i += iSlope; iptr++; } while (--k); } xptr = ex + (vertex[tx[0]]->device.v[Y] * 2) + 1; dy = vertex[tx[1]]->device.v[Y] - vertex[tx[0]]->device.v[Y]; if (dy != 0) { x = vertex[tx[0]]->device.v[X] * 256; dx = vertex[tx[1]]->device.v[X] - vertex[tx[0]]->device.v[X]; if (dx != 0) { int xSlope = (dx * 256) / dy; do { *xptr = x >> 8; xptr += 2; x += xSlope; } while (--dy); } else { int xx = x >> 8; do { *xptr = xx; xptr += 2; } while (--dy); } } dy = vertex[tx[2]]->device.v[Y] - vertex[tx[1]]->device.v[Y]; if (dy != 0) { x = vertex[tx[1]]->device.v[X] * 256; dx = vertex[tx[2]]->device.v[X] - vertex[tx[1]]->device.v[X]; if (dx != 0) { int xSlope = (dx * 256) / dy; do { *xptr = x >> 8; xptr += 2; x += xSlope; } while (--dy); } else { int xx = x >> 8; do { *xptr = xx; xptr += 2; } while (--dy); } } else { *xptr = x >> 8; } uint zmid = uint(vertex[tx[1]]->device.v[Z]) + zBuffer->getGenerationMask(); int imid = vertex[tx[1]]->intensity; int ymin = vertex[tx[0]]->device.v[Y]; int ymid = vertex[tx[1]]->device.v[Y]; int ymax = vertex[tx[2]]->device.v[Y]; ushort *cptr = ((ushort *)device->getBuffer()) + ymin * device->getWidth(); const ushort *dptr = (ushort *)ramp.getRamp(); zptr = (uint *)(zBuffer->getBuffer()) + ymin * zBuffer->getWidth(); xptr = ex + ymin * 2; if (type == 1) { int xspan = (ex[(ymid<<1)+1] - ex[ymid<<1]); if (xspan <= 0) return; int zSlope = (int(zmid) - int(ez[ymid])) / xspan; int iSlope = (imid - ei[ymid]) / xspan; for ( int y = ymin ; y < ymax ; y++ ) { int xmin = *xptr++; int wid = (*xptr++ - xmin) - 1; if (wid >= 0) { int i = ei[y]; ushort *cp = cptr + xmin; uint *zp = zptr + xmin; uint z = ez[y]; while (wid > 0) { if (z < zp[0]) { zp[0] = ulong(z); cp[0] = dptr[(i>>8)*2]; } z += zSlope; i += iSlope; if (z < zp[1]) { zp[1] = ulong(z); cp[1] = dptr[(i>>8)*2]; } zp +=2; cp +=2; z += zSlope; i += iSlope; wid -= 2; } if (wid == 0) { if (z < *zp) { *zp = ulong(z); cp[0] = dptr[(i>>8)*2]; } } } cptr += device->getWidth(); zptr += zBuffer->getWidth(); } } else { int xspan = (ex[(ymid<<1)] - ex[(ymid<<1)+1]); if (xspan <= 0) return; int zSlope = (int(zmid) - int(ez[ymid])) / xspan; int iSlope = (imid - ei[ymid]) / xspan; for ( int y = ymin ; y < ymax ; y++ ) { int xmax = *xptr++; int xmin = *xptr++; --xmax; int wid = xmax - xmin; if (wid >= 0) { int i = ei[y]; ushort *cp = cptr + xmax; uint *zp = zptr + xmax; uint z = ez[y]; while ( wid > 0 ) { if (z < zp[0]) { zp[0] = ulong(z); cp[0] = dptr[(i>>8)*2]; } z += zSlope; i += iSlope; if (z < *(zp-1)) { *(zp-1) = ulong(z); *(cp-1) = dptr[(i>>8)*2]; } z += zSlope; i += iSlope; zp -=2; cp -=2; wid -= 2; } if (wid == 0) { if (z < *zp) { *zp = ulong(z); cp[0] = dptr[(i>>8)*2]; } } } cptr += device->getWidth(); zptr += zBuffer->getWidth(); } } return; }