+++ /dev/null
-/***************************************************************************
-* Copyright (C) 2010 PCSX4ALL Team *
-* Copyright (C) 2010 Unai *
-* Copyright (C) 2016 Senquack (dansilsby <AT> gmail <DOT> com) *
-* *
-* 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 02111-1307 USA. *
-***************************************************************************/
-
-#ifndef __GPU_UNAI_GPU_RASTER_LINE_H__
-#define __GPU_UNAI_GPU_RASTER_LINE_H__
-
-///////////////////////////////////////////////////////////////////////////////
-// GPU internal line drawing functions
-//
-// Rewritten October 2016 by senquack:
-// Instead of one pixel at a time, lines are now drawn in runs of pixels,
-// whether vertical, horizontal, or diagonal. A new inner driver
-// 'gpuPixelSpanFn' is used, as well as an enhanced Bresenham run-slice
-// algorithm. For more information, see the following:
-//
-// Michael Abrash - Graphics Programming Black Book
-// Chapters 35 - 36 (does not implement diagonal runs)
-// http://www.drdobbs.com/parallel/graphics-programming-black-book/184404919
-// http://www.jagregory.com/abrash-black-book/
-//
-// Article by Andrew Delong (does not implement diagonal runs)
-// http://timetraces.ca/nw/drawline.htm
-//
-// 'Run-Based Multi-Point Line Drawing' by Eun Jae Lee & Larry F. Hodges
-// https://smartech.gatech.edu/bitstream/handle/1853/3632/93-22.pdf
-// Provided the idea of doing a half-octant transform allowing lines with
-// slopes between 0.5 and 2.0 (diagonal runs of pixels) to be handled
-// identically to the traditional horizontal/vertical run-slice method.
-
-// Use 16.16 fixed point precision for line math.
-// NOTE: Gouraud colors used by gpuPixelSpanFn can use a different precision.
-#define GPU_LINE_FIXED_BITS 16
-
-// If defined, Gouraud lines will use fixed-point multiply-by-inverse to
-// do most divisions. With enough accuracy, this should be OK.
-#define USE_LINES_ALL_FIXED_PT_MATH
-
-//////////////////////
-// Flat-shaded line //
-//////////////////////
-void gpuDrawLineF(PtrUnion packet, const PSD gpuPixelSpanDriver)
-{
- int x0, y0, x1, y1;
- int dx, dy;
-
- // All three of these variables should be signed (so multiplication works)
- ptrdiff_t sx; // Sign of x delta, positive when x0 < x1
- const ptrdiff_t dst_depth = FRAME_BYTES_PER_PIXEL; // PSX: 2 bytes per pixel
- const ptrdiff_t dst_stride = FRAME_BYTE_STRIDE; // PSX: 2048 bytes per framebuffer line
-
- // Clip region: xmax/ymax seem to normally be one *past* the rightmost/
- // bottommost pixels of the draw area. Since we render every pixel between
- // and including both line endpoints, subtract one from xmax/ymax.
- const int xmin = gpu_senquack.DrawingArea[0];
- const int ymin = gpu_senquack.DrawingArea[1];
- const int xmax = gpu_senquack.DrawingArea[2] - 1;
- const int ymax = gpu_senquack.DrawingArea[3] - 1;
-
- x0 = GPU_EXPANDSIGN(packet.S2[2]) + gpu_senquack.DrawingOffset[0];
- y0 = GPU_EXPANDSIGN(packet.S2[3]) + gpu_senquack.DrawingOffset[1];
- x1 = GPU_EXPANDSIGN(packet.S2[4]) + gpu_senquack.DrawingOffset[0];
- y1 = GPU_EXPANDSIGN(packet.S2[5]) + gpu_senquack.DrawingOffset[1];
-
- // Always draw top to bottom, so ensure y0 <= y1
- if (y0 > y1) {
- SwapValues(y0, y1);
- SwapValues(x0, x1);
- }
-
- // Is line totally outside Y clipping range?
- if (y0 > ymax || y1 < ymin) return;
-
- dx = x1 - x0;
- dy = y1 - y0;
-
- // X-axis range check : max distance between any two X coords is 1023
- // (PSX hardware will not render anything violating this rule)
- // NOTE: We'll check y coord range further below
- if (dx >= CHKMAX_X || dx <= -CHKMAX_X)
- return;
-
- // Y-axis range check and clipping
- if (dy) {
- // Y-axis range check : max distance between any two Y coords is 511
- // (PSX hardware will not render anything violating this rule)
- if (dy >= CHKMAX_Y)
- return;
-
- // We already know y0 < y1
- if (y0 < ymin) {
- x0 += GPU_FAST_DIV(((ymin - y0) * dx), dy);
- y0 = ymin;
- }
- if (y1 > ymax) {
- x1 += GPU_FAST_DIV(((ymax - y1) * dx), dy);
- y1 = ymax;
- }
-
- // Recompute in case clipping occurred:
- dx = x1 - x0;
- dy = y1 - y0;
- }
-
- // Check X clipping range, set 'sx' x-direction variable
- if (dx == 0) {
- // Is vertical line totally outside X clipping range?
- if (x0 < xmin || x0 > xmax)
- return;
- sx = 0;
- } else {
- if (dx > 0) {
- // x0 is leftmost coordinate
- if (x0 > xmax) return; // Both points outside X clip range
-
- if (x0 < xmin) {
- if (x1 < xmin) return; // Both points outside X clip range
- y0 += GPU_FAST_DIV(((xmin - x0) * dy), dx);
- x0 = xmin;
- }
-
- if (x1 > xmax) {
- y1 += GPU_FAST_DIV(((xmax - x1) * dy), dx);
- x1 = xmax;
- }
-
- sx = +1;
- dx = x1 - x0; // Get final value, which should also be absolute value
- } else {
- // x1 is leftmost coordinate
- if (x1 > xmax) return; // Both points outside X clip range
-
- if (x1 < xmin) {
- if (x0 < xmin) return; // Both points outside X clip range
-
- y1 += GPU_FAST_DIV(((xmin - x1) * dy), dx);
- x1 = xmin;
- }
-
- if (x0 > xmax) {
- y0 += GPU_FAST_DIV(((xmax - x0) * dy), dx);
- x0 = xmax;
- }
-
- sx = -1;
- dx = x0 - x1; // Get final value, which should also be absolute value
- }
-
- // Recompute in case clipping occurred:
- dy = y1 - y0;
- }
-
- // IMPORTANT: dx,dy should now contain their absolute values
-
- int min_length, // Minimum length of a pixel run
- start_length, // Length of first run
- end_length, // Length of last run
- err_term, // Cumulative error to determine when to draw longer run
- err_adjup, // Increment to err_term for each run drawn
- err_adjdown; // Subract this from err_term after drawing longer run
-
- // Color to draw with (16 bits, highest of which is unset mask bit)
- uintptr_t col16 = GPU_RGB16(packet.U4[0]);
-
- // We use u8 pointers even though PS1 has u16 framebuffer.
- // This allows pixel-drawing functions to increment dst pointer
- // directly by the passed 'incr' value, not having to shift it first.
- u8 *dst = (u8*)gpu_senquack.vram + y0 * dst_stride + x0 * dst_depth;
-
- // SPECIAL CASE: Vertical line
- if (dx == 0) {
- gpuPixelSpanDriver(dst, col16, dst_stride, dy+1);
- return;
- }
-
- // SPECIAL CASE: Horizontal line
- if (dy == 0) {
- gpuPixelSpanDriver(dst, col16, sx * dst_depth, dx+1);
- return;
- }
-
- // SPECIAL CASE: Diagonal line
- if (dx == dy) {
- gpuPixelSpanDriver(dst, col16, dst_stride + (sx * dst_depth), dy+1);
- return;
- }
-
- int major, minor; // Major axis, minor axis
- ptrdiff_t incr_major, incr_minor; // Ptr increment for each step along axis
-
- if (dx > dy) {
- major = dx;
- minor = dy;
- } else {
- major = dy;
- minor = dx;
- }
-
- // Determine if diagonal or horizontal runs
- if (major < (2 * minor)) {
- // Diagonal runs, so perform half-octant transformation
- minor = major - minor;
-
- // Advance diagonally when drawing runs
- incr_major = dst_stride + (sx * dst_depth);
-
- // After drawing each run, correct for over-advance along minor axis
- if (dx > dy)
- incr_minor = -dst_stride;
- else
- incr_minor = -sx * dst_depth;
- } else {
- // Horizontal or vertical runs
- if (dx > dy) {
- incr_major = sx * dst_depth;
- incr_minor = dst_stride;
- } else {
- incr_major = dst_stride;
- incr_minor = sx * dst_depth;
- }
- }
-
- if (minor > 1) {
- // Minimum number of pixels each run
- min_length = major / minor;
-
- // Initial error term; reflects an initial step of 0.5 along minor axis
- err_term = (major % minor) - (minor * 2);
-
- // Increment err_term this much each step along minor axis; when
- // err_term crosses zero, draw longer pixel run.
- err_adjup = (major % minor) * 2;
- } else {
- min_length = major;
- err_term = 0;
- err_adjup = 0;
- }
-
- // Error term adjustment when err_term turns over; used to factor
- // out the major-axis step made at that time
- err_adjdown = minor * 2;
-
- // The initial and last runs are partial, because minor axis advances
- // only 0.5 for these runs, rather than 1. Each is half a full run,
- // plus the initial pixel.
- start_length = end_length = (min_length / 2) + 1;
-
- if (min_length & 1) {
- // If there're an odd number of pixels per run, we have 1 pixel that
- // can't be allocated to either the initial or last partial run, so
- // we'll add 0.5 to err_term so that this pixel will be handled
- // by the normal full-run loop
- err_term += minor;
- } else {
- // If the minimum run length is even and there's no fractional advance,
- // we have one pixel that could go to either the initial or last
- // partial run, which we arbitrarily allocate to the last run
- if (err_adjup == 0)
- start_length--; // Leave out the extra pixel at the start
- }
-
- // First run of pixels
- dst = gpuPixelSpanDriver(dst, col16, incr_major, start_length);
- dst += incr_minor;
-
- // Middle runs of pixels
- while (--minor > 0) {
- int run_length = min_length;
- err_term += err_adjup;
-
- // If err_term passed 0, reset it and draw longer run
- if (err_term > 0) {
- err_term -= err_adjdown;
- run_length++;
- }
-
- dst = gpuPixelSpanDriver(dst, col16, incr_major, run_length);
- dst += incr_minor;
- }
-
- // Final run of pixels
- gpuPixelSpanDriver(dst, col16, incr_major, end_length);
-}
-
-/////////////////////////
-// Gouraud-shaded line //
-/////////////////////////
-void gpuDrawLineG(PtrUnion packet, const PSD gpuPixelSpanDriver)
-{
- int x0, y0, x1, y1;
- int dx, dy, dr, dg, db;
- u32 r0, g0, b0, r1, g1, b1;
-
- // All three of these variables should be signed (so multiplication works)
- ptrdiff_t sx; // Sign of x delta, positive when x0 < x1
- const ptrdiff_t dst_depth = FRAME_BYTES_PER_PIXEL; // PSX: 2 bytes per pixel
- const ptrdiff_t dst_stride = FRAME_BYTE_STRIDE; // PSX: 2048 bytes per framebuffer line
-
- // Clip region: xmax/ymax seem to normally be one *past* the rightmost/
- // bottommost pixels of the draw area. We'll render every pixel between
- // and including both line endpoints, so subtract one from xmax/ymax.
- const int xmin = gpu_senquack.DrawingArea[0];
- const int ymin = gpu_senquack.DrawingArea[1];
- const int xmax = gpu_senquack.DrawingArea[2] - 1;
- const int ymax = gpu_senquack.DrawingArea[3] - 1;
-
- x0 = GPU_EXPANDSIGN(packet.S2[2]) + gpu_senquack.DrawingOffset[0];
- y0 = GPU_EXPANDSIGN(packet.S2[3]) + gpu_senquack.DrawingOffset[1];
- x1 = GPU_EXPANDSIGN(packet.S2[6]) + gpu_senquack.DrawingOffset[0];
- y1 = GPU_EXPANDSIGN(packet.S2[7]) + gpu_senquack.DrawingOffset[1];
-
- u32 col0 = packet.U4[0];
- u32 col1 = packet.U4[2];
-
- // Always draw top to bottom, so ensure y0 <= y1
- if (y0 > y1) {
- SwapValues(y0, y1);
- SwapValues(x0, x1);
- SwapValues(col0, col1);
- }
-
- // Is line totally outside Y clipping range?
- if (y0 > ymax || y1 < ymin) return;
-
- // If defined, Gouraud colors are fixed-point 5.11, otherwise they are 8.16
- // (This is only beneficial if using SIMD-optimized pixel driver)
-#ifdef GPU_GOURAUD_LOW_PRECISION
- r0 = (col0 >> 3) & 0x1f; g0 = (col0 >> 11) & 0x1f; b0 = (col0 >> 19) & 0x1f;
- r1 = (col1 >> 3) & 0x1f; g1 = (col1 >> 11) & 0x1f; b1 = (col1 >> 19) & 0x1f;
-#else
- r0 = col0 & 0xff; g0 = (col0 >> 8) & 0xff; b0 = (col0 >> 16) & 0xff;
- r1 = col1 & 0xff; g1 = (col1 >> 8) & 0xff; b1 = (col1 >> 16) & 0xff;
-#endif
-
- dx = x1 - x0;
- dy = y1 - y0;
- dr = r1 - r0;
- dg = g1 - g0;
- db = b1 - b0;
-
- // X-axis range check : max distance between any two X coords is 1023
- // (PSX hardware will not render anything violating this rule)
- // NOTE: We'll check y coord range further below
- if (dx >= CHKMAX_X || dx <= -CHKMAX_X)
- return;
-
- // Y-axis range check and clipping
- if (dy) {
- // Y-axis range check : max distance between any two Y coords is 511
- // (PSX hardware will not render anything violating this rule)
- if (dy >= CHKMAX_Y)
- return;
-
- // We already know y0 < y1
- if (y0 < ymin) {
-#ifdef USE_LINES_ALL_FIXED_PT_MATH
- s32 factor = GPU_FAST_DIV(((ymin - y0) << GPU_LINE_FIXED_BITS), dy);
- x0 += (dx * factor) >> GPU_LINE_FIXED_BITS;
- r0 += (dr * factor) >> GPU_LINE_FIXED_BITS;
- g0 += (dg * factor) >> GPU_LINE_FIXED_BITS;
- b0 += (db * factor) >> GPU_LINE_FIXED_BITS;
-#else
- x0 += (ymin - y0) * dx / dy;
- r0 += (ymin - y0) * dr / dy;
- g0 += (ymin - y0) * dg / dy;
- b0 += (ymin - y0) * db / dy;
-#endif
- y0 = ymin;
- }
-
- if (y1 > ymax) {
-#ifdef USE_LINES_ALL_FIXED_PT_MATH
- s32 factor = GPU_FAST_DIV(((ymax - y1) << GPU_LINE_FIXED_BITS), dy);
- x1 += (dx * factor) >> GPU_LINE_FIXED_BITS;
- r1 += (dr * factor) >> GPU_LINE_FIXED_BITS;
- g1 += (dg * factor) >> GPU_LINE_FIXED_BITS;
- b1 += (db * factor) >> GPU_LINE_FIXED_BITS;
-#else
- x1 += (ymax - y1) * dx / dy;
- r1 += (ymax - y1) * dr / dy;
- g1 += (ymax - y1) * dg / dy;
- b1 += (ymax - y1) * db / dy;
-#endif
- y1 = ymax;
- }
-
- // Recompute in case clipping occurred:
- dx = x1 - x0;
- dy = y1 - y0;
- dr = r1 - r0;
- dg = g1 - g0;
- db = b1 - b0;
- }
-
- // Check X clipping range, set 'sx' x-direction variable
- if (dx == 0) {
- // Is vertical line totally outside X clipping range?
- if (x0 < xmin || x0 > xmax)
- return;
- sx = 0;
- } else {
- if (dx > 0) {
- // x0 is leftmost coordinate
- if (x0 > xmax) return; // Both points outside X clip range
-
- if (x0 < xmin) {
- if (x1 < xmin) return; // Both points outside X clip range
-
-#ifdef USE_LINES_ALL_FIXED_PT_MATH
- s32 factor = GPU_FAST_DIV(((xmin - x0) << GPU_LINE_FIXED_BITS), dx);
- y0 += (dy * factor) >> GPU_LINE_FIXED_BITS;
- r0 += (dr * factor) >> GPU_LINE_FIXED_BITS;
- g0 += (dg * factor) >> GPU_LINE_FIXED_BITS;
- b0 += (db * factor) >> GPU_LINE_FIXED_BITS;
-#else
- y0 += (xmin - x0) * dy / dx;
- r0 += (xmin - x0) * dr / dx;
- g0 += (xmin - x0) * dg / dx;
- b0 += (xmin - x0) * db / dx;
-#endif
- x0 = xmin;
- }
-
- if (x1 > xmax) {
-#ifdef USE_LINES_ALL_FIXED_PT_MATH
- s32 factor = GPU_FAST_DIV(((xmax - x1) << GPU_LINE_FIXED_BITS), dx);
- y1 += (dy * factor) >> GPU_LINE_FIXED_BITS;
- r1 += (dr * factor) >> GPU_LINE_FIXED_BITS;
- g1 += (dg * factor) >> GPU_LINE_FIXED_BITS;
- b1 += (db * factor) >> GPU_LINE_FIXED_BITS;
-#else
- y1 += (xmax - x1) * dy / dx;
- r1 += (xmax - x1) * dr / dx;
- g1 += (xmax - x1) * dg / dx;
- b1 += (xmax - x1) * db / dx;
-#endif
- x1 = xmax;
- }
-
- sx = +1;
- dx = x1 - x0; // Get final value, which should also be absolute value
- } else {
- // x1 is leftmost coordinate
- if (x1 > xmax) return; // Both points outside X clip range
-
- if (x1 < xmin) {
- if (x0 < xmin) return; // Both points outside X clip range
-
-#ifdef USE_LINES_ALL_FIXED_PT_MATH
- s32 factor = GPU_FAST_DIV(((xmin - x1) << GPU_LINE_FIXED_BITS), dx);
- y1 += (dy * factor) >> GPU_LINE_FIXED_BITS;
- r1 += (dr * factor) >> GPU_LINE_FIXED_BITS;
- g1 += (dg * factor) >> GPU_LINE_FIXED_BITS;
- b1 += (db * factor) >> GPU_LINE_FIXED_BITS;
-#else
- y1 += (xmin - x1) * dy / dx;
- r1 += (xmin - x1) * dr / dx;
- g1 += (xmin - x1) * dg / dx;
- b1 += (xmin - x1) * db / dx;
-#endif
- x1 = xmin;
- }
-
- if (x0 > xmax) {
-#ifdef USE_LINES_ALL_FIXED_PT_MATH
- s32 factor = GPU_FAST_DIV(((xmax - x0) << GPU_LINE_FIXED_BITS), dx);
- y0 += (dy * factor) >> GPU_LINE_FIXED_BITS;
- r0 += (dr * factor) >> GPU_LINE_FIXED_BITS;
- g0 += (dg * factor) >> GPU_LINE_FIXED_BITS;
- b0 += (db * factor) >> GPU_LINE_FIXED_BITS;
-#else
- y0 += (xmax - x0) * dy / dx;
- r0 += (xmax - x0) * dr / dx;
- g0 += (xmax - x0) * dg / dx;
- b0 += (xmax - x0) * db / dx;
-#endif
- x0 = xmax;
- }
-
- sx = -1;
- dx = x0 - x1; // Get final value, which should also be absolute value
- }
-
- // Recompute in case clipping occurred:
- dy = y1 - y0;
- dr = r1 - r0;
- dg = g1 - g0;
- db = b1 - b0;
- }
-
- // IMPORTANT: dx,dy should now contain their absolute values
-
- int min_length, // Minimum length of a pixel run
- start_length, // Length of first run
- end_length, // Length of last run
- err_term, // Cumulative error to determine when to draw longer run
- err_adjup, // Increment to err_term for each run drawn
- err_adjdown; // Subract this from err_term after drawing longer run
-
- GouraudColor gcol;
- gcol.r = r0 << GPU_GOURAUD_FIXED_BITS;
- gcol.g = g0 << GPU_GOURAUD_FIXED_BITS;
- gcol.b = b0 << GPU_GOURAUD_FIXED_BITS;
-
- // We use u8 pointers even though PS1 has u16 framebuffer.
- // This allows pixel-drawing functions to increment dst pointer
- // directly by the passed 'incr' value, not having to shift it first.
- u8 *dst = (u8*)gpu_senquack.vram + y0 * dst_stride + x0 * dst_depth;
-
- // SPECIAL CASE: Vertical line
- if (dx == 0) {
-#ifdef USE_LINES_ALL_FIXED_PT_MATH
- // Get dy fixed-point inverse
- s32 inv_factor = 1 << GPU_GOURAUD_FIXED_BITS;
- if (dy > 1) inv_factor = GPU_FAST_DIV(inv_factor, dy);
-
- // Simultaneously divide and convert integer to Gouraud fixed point:
- gcol.r_incr = dr * inv_factor;
- gcol.g_incr = dg * inv_factor;
- gcol.b_incr = db * inv_factor;
-#else
- // First, convert to Gouraud fixed point
- gcol.r_incr = dr << GPU_GOURAUD_FIXED_BITS;
- gcol.g_incr = dg << GPU_GOURAUD_FIXED_BITS;
- gcol.b_incr = db << GPU_GOURAUD_FIXED_BITS;
-
- if (dy > 1) {
- if (dr) gcol.r_incr /= dy;
- if (dg) gcol.g_incr /= dy;
- if (db) gcol.b_incr /= dy;
- }
-#endif
-
- gpuPixelSpanDriver(dst, (uintptr_t)&gcol, dst_stride, dy+1);
- return;
- }
-
- // SPECIAL CASE: Horizontal line
- if (dy == 0) {
-#ifdef USE_LINES_ALL_FIXED_PT_MATH
- // Get dx fixed-point inverse
- s32 inv_factor = (1 << GPU_GOURAUD_FIXED_BITS);
- if (dx > 1) inv_factor = GPU_FAST_DIV(inv_factor, dx);
-
- // Simultaneously divide and convert integer to Gouraud fixed point:
- gcol.r_incr = dr * inv_factor;
- gcol.g_incr = dg * inv_factor;
- gcol.b_incr = db * inv_factor;
-#else
- gcol.r_incr = dr << GPU_GOURAUD_FIXED_BITS;
- gcol.g_incr = dg << GPU_GOURAUD_FIXED_BITS;
- gcol.b_incr = db << GPU_GOURAUD_FIXED_BITS;
-
- if (dx > 1) {
- if (dr) gcol.r_incr /= dx;
- if (dg) gcol.g_incr /= dx;
- if (db) gcol.b_incr /= dx;
- }
-#endif
-
- gpuPixelSpanDriver(dst, (uintptr_t)&gcol, sx * dst_depth, dx+1);
- return;
- }
-
- // SPECIAL CASE: Diagonal line
- if (dx == dy) {
-#ifdef USE_LINES_ALL_FIXED_PT_MATH
- // Get dx fixed-point inverse
- s32 inv_factor = (1 << GPU_GOURAUD_FIXED_BITS);
- if (dx > 1) inv_factor = GPU_FAST_DIV(inv_factor, dx);
-
- // Simultaneously divide and convert integer to Gouraud fixed point:
- gcol.r_incr = dr * inv_factor;
- gcol.g_incr = dg * inv_factor;
- gcol.b_incr = db * inv_factor;
-#else
- // First, convert to Gouraud fixed point
- gcol.r_incr = dr << GPU_GOURAUD_FIXED_BITS;
- gcol.g_incr = dg << GPU_GOURAUD_FIXED_BITS;
- gcol.b_incr = db << GPU_GOURAUD_FIXED_BITS;
-
- if (dx > 1) {
- if (dr) gcol.r_incr /= dx;
- if (dg) gcol.g_incr /= dx;
- if (db) gcol.b_incr /= dx;
- }
-#endif
-
- gpuPixelSpanDriver(dst, (uintptr_t)&gcol, dst_stride + (sx * dst_depth), dy+1);
- return;
- }
-
- int major, minor; // Absolute val of major,minor axis delta
- ptrdiff_t incr_major, incr_minor; // Ptr increment for each step along axis
-
- if (dx > dy) {
- major = dx;
- minor = dy;
- } else {
- major = dy;
- minor = dx;
- }
-
- // Determine if diagonal or horizontal runs
- if (major < (2 * minor)) {
- // Diagonal runs, so perform half-octant transformation
- minor = major - minor;
-
- // Advance diagonally when drawing runs
- incr_major = dst_stride + (sx * dst_depth);
-
- // After drawing each run, correct for over-advance along minor axis
- if (dx > dy)
- incr_minor = -dst_stride;
- else
- incr_minor = -sx * dst_depth;
- } else {
- // Horizontal or vertical runs
- if (dx > dy) {
- incr_major = sx * dst_depth;
- incr_minor = dst_stride;
- } else {
- incr_major = dst_stride;
- incr_minor = sx * dst_depth;
- }
- }
-
-#ifdef USE_LINES_ALL_FIXED_PT_MATH
- s32 major_inv = GPU_FAST_DIV((1 << GPU_GOURAUD_FIXED_BITS), major);
-
- // Simultaneously divide and convert from integer to Gouraud fixed point:
- gcol.r_incr = dr * major_inv;
- gcol.g_incr = dg * major_inv;
- gcol.b_incr = db * major_inv;
-#else
- gcol.r_incr = dr ? ((dr << GPU_GOURAUD_FIXED_BITS) / major) : 0;
- gcol.g_incr = dg ? ((dg << GPU_GOURAUD_FIXED_BITS) / major) : 0;
- gcol.b_incr = db ? ((db << GPU_GOURAUD_FIXED_BITS) / major) : 0;
-#endif
-
- if (minor > 1) {
- // Minimum number of pixels each run
- min_length = major / minor;
-
- // Initial error term; reflects an initial step of 0.5 along minor axis
- err_term = (major % minor) - (minor * 2);
-
- // Increment err_term this much each step along minor axis; when
- // err_term crosses zero, draw longer pixel run.
- err_adjup = (major % minor) * 2;
- } else {
- min_length = major;
- err_term = 0;
- err_adjup = 0;
- }
-
- // Error term adjustment when err_term turns over; used to factor
- // out the major-axis step made at that time
- err_adjdown = minor * 2;
-
- // The initial and last runs are partial, because minor axis advances
- // only 0.5 for these runs, rather than 1. Each is half a full run,
- // plus the initial pixel.
- start_length = end_length = (min_length / 2) + 1;
-
- if (min_length & 1) {
- // If there're an odd number of pixels per run, we have 1 pixel that
- // can't be allocated to either the initial or last partial run, so
- // we'll add 0.5 to err_term so that this pixel will be handled
- // by the normal full-run loop
- err_term += minor;
- } else {
- // If the minimum run length is even and there's no fractional advance,
- // we have one pixel that could go to either the initial or last
- // partial run, which we'll arbitrarily allocate to the last run
- if (err_adjup == 0)
- start_length--; // Leave out the extra pixel at the start
- }
-
- // First run of pixels
- dst = gpuPixelSpanDriver(dst, (uintptr_t)&gcol, incr_major, start_length);
- dst += incr_minor;
-
- // Middle runs of pixels
- while (--minor > 0) {
- int run_length = min_length;
- err_term += err_adjup;
-
- // If err_term passed 0, reset it and draw longer run
- if (err_term > 0) {
- err_term -= err_adjdown;
- run_length++;
- }
-
- dst = gpuPixelSpanDriver(dst, (uintptr_t)&gcol, incr_major, run_length);
- dst += incr_minor;
- }
-
- // Final run of pixels
- gpuPixelSpanDriver(dst, (uintptr_t)&gcol, incr_major, end_length);
-}
-
-#endif /* __GPU_UNAI_GPU_RASTER_LINE_H__ */