- s32 temp;
- s32 xmin, xmax;
- s32 ymin, ymax;
- s32 x0, x1, dx;
- s32 y0, y1, dy;
- s32 r0, r1;
- s32 g0, g1;
- s32 b0, b1;
-
- x0 = PacketBuffer.S2[2] + DrawingOffset[0]; GPU_TESTRANGE(x0);
- y0 = PacketBuffer.S2[3] + DrawingOffset[1]; GPU_TESTRANGE(y0);
- x1 = PacketBuffer.S2[6] + DrawingOffset[0]; GPU_TESTRANGE(x1);
- y1 = PacketBuffer.S2[7] + DrawingOffset[1]; GPU_TESTRANGE(y1);
-
- r0 = PacketBuffer.U1[0]; g0 = PacketBuffer.U1[1]; b0 = PacketBuffer.U1[2];
- r1 = PacketBuffer.U1[8]; g1 = PacketBuffer.U1[9]; b1 = PacketBuffer.U1[10];
-
- xmin = DrawingArea[0]; xmax = DrawingArea[2];
- ymin = DrawingArea[1]; ymax = DrawingArea[3];
-
- dy = (y1 - y0);
- if (dy < 0)
- dy = -dy;
- dx = (x1 - x0);
- if (dx < 0)
- dx = -dx;
- if (dx > dy) {
- if (x0 > x1) {
- GPU_SWAP(x0, x1, temp);
- GPU_SWAP(y0, y1, temp);
- GPU_SWAP(r0, r1, temp);
- GPU_SWAP(g0, g1, temp);
- GPU_SWAP(b0, b1, temp);
+ 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_unai.DrawingArea[0];
+ const int ymin = gpu_unai.DrawingArea[1];
+ const int xmax = gpu_unai.DrawingArea[2] - 1;
+ const int ymax = gpu_unai.DrawingArea[3] - 1;
+
+ x0 = GPU_EXPANDSIGN(le16_to_s16(packet.U2[2])) + gpu_unai.DrawingOffset[0];
+ y0 = GPU_EXPANDSIGN(le16_to_s16(packet.U2[3])) + gpu_unai.DrawingOffset[1];
+ x1 = GPU_EXPANDSIGN(le16_to_s16(packet.U2[6])) + gpu_unai.DrawingOffset[0];
+ y1 = GPU_EXPANDSIGN(le16_to_s16(packet.U2[7])) + gpu_unai.DrawingOffset[1];
+
+ u32 col0 = le32_to_u32(packet.U4[0]);
+ u32 col1 = le32_to_u32(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;