#include uint8_t AZRP_SHADER_LINE = -1; static void azrp_shader_line_configure(void) { azrp_set_uniforms(AZRP_SHADER_LINE, (void *)azrp_width); } __attribute__((constructor)) static void register_shader(void) { extern azrp_shader_t azrp_shader_line; AZRP_SHADER_LINE = azrp_register_shader(azrp_shader_line, azrp_shader_line_configure ); } //--- struct command { uint8_t shader_id; uint16_t color; uint16_t curr_y; uint16_t curr_x; int16_t dx, dy, sx, sy; int16_t cumul; int16_t i; }; int ABS( int x1 ) { if (x1 >= 0) return x1; else return (-1 * x1); } int SGN( int x1 ) { if (x1 > 0) return 1; else if (x1 == 0) return 0; else return -1; } void azrp_line(int xA, int yA, int xB, int yB, uint16_t color) { prof_enter(azrp_perf_cmdgen); //clipping algorithm as per "Another Simple but Faster Method for 2D Line Clipping" //from Dimitrios Matthes and Vasileios Drakopoulos //International Journal of Computer Graphics & Animation (IJCGA) Vol.9, No.1/2/3, July 2019 int xmin = 0; int xmax = azrp_width-1; int ymin = 0; int ymax = azrp_height-1; //step 1 line are fully out of the screen if ((xAxmax && xB>xmax) || (yAymax && yB>ymax)) { prof_leave(azrp_perf_cmdgen); return; } int x1, x2, y1, y2; // step 1.5 - specific to Azur fragment approach // we swap to always start with the point on top as the fragment are updated from top to bottom // (x1,y1) is the most top point and (x2,y2) is the most bottom point (rankig as per y values only if (yA <= yB) { x1 = xA; y1 = yA; x2 = xB; y2 = yB; } else { x1 = xB; y1 = yB; x2 = xA; y2 = yA; } //step 2 line clipping within the box (xmin,ymin) --> (xmax,ymax) int x[2]; int y[2]; x[0] = x1; x[1] = x2; y[0] = y1; y[1] = y2; for(int i=0; i<2; i++) { if (x[i] < xmin) { x[i] = xmin; y[i] = ((y2-y1) * (xmin-x1)) / (x2-x1) + y1; } else if (x[i] > xmax) { x[i] = xmax; y[i] = ((y2-y1) * (xmax-x1)) / (x2-x1) + y1; } if (y[i] < ymin) { x[i] = ((x2-x1) * (ymin-y1)) / (y2-y1) + x1; y[i] = ymin; } else if (y[i] > ymax) { x[i] = ((x2-x1) * (ymax-y1)) / (y2-y1) + x1; y[i] = ymax; } } if((x[0] < xmin && x[1] < xmin) || (x[0] > xmax && x[1] > xmax)) { prof_leave(azrp_perf_cmdgen); return; } x1 = x[0]; y1 = y[0]; x2 = x[1]; y2 = y[1]; int frag_first = y1 / azrp_frag_height; int frag_last = y2 / azrp_frag_height; int frag_count = frag_last - frag_first + 1; struct command *cmd = (struct command *) azrp_new_command(sizeof *cmd, frag_first, frag_count); if(!cmd) { prof_leave(azrp_perf_cmdgen); return; } cmd->shader_id = AZRP_SHADER_LINE; cmd->color = color; cmd->curr_x = x1; cmd->curr_y = y1 & 15; cmd->dx = ABS(x2-x1); cmd->sx = SGN(x2-x1); cmd->dy = ABS(y2-y1); cmd->sy = SGN(y2-y1); cmd->i = 0; cmd->cumul = (cmd->dx >= cmd->dy) ? cmd->dx/2 : cmd->dy/2; prof_leave(azrp_perf_cmdgen); } void azrp_shader_line( void *uniforms, void *comnd, void *fragment ) { struct command *cmd = (struct command *) comnd; uint16_t *frag = (uint16_t *) fragment; frag[ azrp_width * cmd->curr_y + cmd->curr_x ] = cmd->color; int i; if (cmd->dy == 0) { for( i = cmd->i; i < cmd->dx ; i++ ) { cmd->curr_x += cmd->sx; frag[ azrp_width * cmd->curr_y + cmd->curr_x ] = cmd->color; } } else if (cmd->dx == 0) { for( i = cmd->i; i < cmd->dy ; i++ ) { cmd->curr_y += cmd->sy; // if curr_y=16, this means we are changing to next fragment if (cmd->curr_y == azrp_frag_height) break; frag[ azrp_width * cmd->curr_y + cmd->curr_x ] = cmd->color; } } else if (cmd->dx >= cmd->dy) { for( i = cmd->i; i < cmd->dx; i++ ) { cmd->curr_x += cmd->sx; cmd->cumul += cmd->dy; if (cmd->cumul > cmd->dx) { cmd->cumul -= cmd->dx; cmd->curr_y += cmd->sy; } // if curr_y=16, this means we are changing to next fragment if (cmd->curr_y == azrp_frag_height ) break; frag[ azrp_width * cmd->curr_y + cmd->curr_x ] = cmd->color; } } else { for( i = cmd->i; i < cmd->dy; i++ ) { cmd->curr_y += cmd->sy; cmd->cumul += cmd->dx; if (cmd->cumul > cmd->dy) { cmd->cumul -= cmd->dy; cmd->curr_x += cmd->sx; } // if curr_y=16, this means we are changing to next fragment if (cmd->curr_y == azrp_frag_height) break; frag[ azrp_width * cmd->curr_y + cmd->curr_x ] = cmd->color; } } cmd->curr_y = cmd->curr_y & 15; cmd->i = i+1; }