X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=plugins%2Fgpu_senquack%2Fgpu_raster_line.h;fp=plugins%2Fgpu_senquack%2Fgpu_raster_line.h;h=0000000000000000000000000000000000000000;hb=4cfc568dce10c4ff9a476e229c56b17f8f1ed678;hp=4dd99a6dd7bf4ff2a7fe192d25aeaefdc8a8415c;hpb=5a920d326f0508f941d6cfc6c42500e961f82a72;p=pcsx_rearmed.git diff --git a/plugins/gpu_senquack/gpu_raster_line.h b/plugins/gpu_senquack/gpu_raster_line.h deleted file mode 100644 index 4dd99a6d..00000000 --- a/plugins/gpu_senquack/gpu_raster_line.h +++ /dev/null @@ -1,720 +0,0 @@ -/*************************************************************************** -* Copyright (C) 2010 PCSX4ALL Team * -* Copyright (C) 2010 Unai * -* Copyright (C) 2016 Senquack (dansilsby gmail 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__ */