/* copyright 2006 by jon snell free for all uses */ #define AAGD_LINE_PASSES_THROUGH_TOP 1 #define AAGD_LINE_PASSES_THROUGH_BOTTOM 2 #define AAGD_LINE_PASSES_THROUGH_LEFT 4 #define AAGD_LINE_PASSES_THROUGH_RIGHT 8 #include #include #include /* declaration of functions to be exported */ ZEND_FUNCTION(aa_filled_arc); //ZEND_MSHUTDOWN(end_import); /* compiled function list so Zend knows what's in this module */ zend_function_entry firstmod_functions[] = { ZEND_FE(aa_filled_arc, NULL) {NULL, NULL, NULL} }; /* compiled module information */ zend_module_entry firstmod_module_entry = { STANDARD_MODULE_HEADER, "First Module", firstmod_functions, /* function list */ NULL, /* process startup */ NULL, NULL, NULL, NO_VERSION_YET, STANDARD_MODULE_PROPERTIES }; /* implement standard "stub" routine to introduce ourselves to Zend */ #if COMPILE_DL ZEND_GET_MODULE(firstmod) #endif /* starts the file open */ double arc_area(double x1, double x2, double a, double b, double h) { double rval; double a_sq, b_sq, x1_sq, x2_sq; double dist; if (x2 == x1) return 0; a /= (double) 2; b /= (double) 2; a_sq = pow(a,(double)2); b_sq = pow(b,(double)2); x2_sq = pow(x2,(double)2); x1_sq = pow(x1,(double) 2); rval = 0.5*a*b*( ((x2/b)*(sqrt(b_sq - x2_sq))/b + asin (x2/b)) - ((x1/b)*(sqrt(b_sq - x1_sq))/b + asin (x1/b)) ); dist = x2 - x1; if (dist < 0) { dist *= -1.0; } rval -= dist * h; return rval; } /* ellipse alpha function */ /* starts the file open */ double ellipse_alpha(double fx, double fy, double h, double w) { double w2, h2, shade; double y_val_at_right, y_val_at_left, x_val_at_top, x_val_at_bottom; char enter_through_left = 0; char enter_through_top = 0; char leave_through_right = 0; char leave_through_bottom = 0; w2 = w / 2; h2 = h / 2; if ((((fx)*(fx))/(w2*w2) + ((fy)*(fy))/(h2*h2))>1) { // entirely outside of the ellipse return 0; } y_val_at_left = sqrt(pow(h2,2)*(1-(pow(fx,2) / pow(w2,2)))); y_val_at_right = sqrt(pow(h2,2)*(1-(pow((fx+1),2) / pow(w2,2)))); if (zend_isnan(y_val_at_right)) y_val_at_right = 0; if ((y_val_at_left >= fy) && (y_val_at_left <= (fy+1))) { enter_through_left = 1; } else { enter_through_top = 1; x_val_at_top = sqrt(pow(w2,2)*(1-(pow(fy+1,2) / pow(h2,2)))); } if ((y_val_at_right > fy) && (y_val_at_right < (fy+1))) { leave_through_right = 1; } else { x_val_at_bottom = sqrt(pow(w2,2)*(1 -(pow((fy),2) / pow(h2,2)))); leave_through_bottom = 1; } if (enter_through_left && leave_through_right) { shade = arc_area(fx,fx+1, h, w, fy); } else if (enter_through_left && leave_through_bottom) { shade = arc_area(fx,x_val_at_bottom, h, w, fy); } else if (enter_through_top && leave_through_bottom) { shade = arc_area(x_val_at_top,x_val_at_bottom, h, w, fy) + (( x_val_at_top- fx) * 1); } else if (enter_through_top && leave_through_right) { shade = arc_area(x_val_at_top,fx+1, h, w, fy) + ((x_val_at_top - fx) * 1); } return shade; } double pixel_angle(double oy, double ox) { double angle; angle = 180.0*atan2(ox,oy)/M_PI; if (angle < 0) { angle += 360; } angle = (angle+360); if (angle < 0) { angle += 360; } return fmod(angle, 360); } // assumes x/y is relative to the center pixel char point_in_arc(double ox, double oy, double start_angle, double end_angle, double *how_close) { double angle; char bounds_check; angle = pixel_angle(ox, oy); if (start_angle > end_angle) { bounds_check = ((angle <= start_angle) && (angle >= (end_angle))); *how_close = MIN(fabs(angle - start_angle), fabs(angle - end_angle)); return bounds_check; } else { bounds_check = ((angle <= end_angle) && (angle >= (start_angle))); *how_close = MIN(fabs(angle - start_angle), fabs(angle - end_angle)); return ! bounds_check; } } long alpha_alpha(gdImagePtr im, long color, int alpha) { int old_alpha, r, g, b; long cur_color; old_alpha = (color & 0xFF000000) >> 24; r = (color & 0x00FF0000) >> 16; g = (color & 0x0000FF00) >> 8; b = (color & 0x000000FF); cur_color = gdImageColorResolveAlpha(im, r, g, b, 127.0 - ((127.0 - old_alpha)* (alpha/127.0))); return cur_color; } void pixel_arc_mask(gdImagePtr im, double cx, double cy, double ox, double oy, double start_angle, double end_angle, long color) { double how_close; char pt_1_test; int corners; int sub_sample_points = 127; double step_size; double points_matched, steps_completed; double x_s, y_s; int pixel_shade = 0; if (start_angle != end_angle) { how_close = 0; pt_1_test = point_in_arc(oy, ox, start_angle, end_angle, &how_close); if (how_close > 5 && MIN(ox,oy) > 5) { pixel_shade = pt_1_test ? 127 : 0; } else { // now check all corners corners = ((pt_1_test ? 1 : 0) +(point_in_arc(oy-1.0, ox, start_angle, end_angle, &how_close) ? 1 : 0) + (point_in_arc(oy, ox-1.0, start_angle, end_angle, &how_close) ? 1 : 0) + (point_in_arc(oy-1.0, ox-1.0, start_angle, end_angle, &how_close) ? 1 : 0)); if (corners == 4) { pixel_shade = 127; } else if (corners > 0 ) { pixel_shade = (double) corners / 4.0 * 127.0; if (pixel_shade != 127) { step_size = 1 / floor(sqrt((double) sub_sample_points)); steps_completed = 0; points_matched = 0; for(x_s = ox; x_s >= ox-1.0; x_s-=step_size) { for(y_s = oy; y_s >= oy-1.0; y_s-=step_size) { if (point_in_arc(y_s, x_s, start_angle, end_angle, &how_close)) { points_matched++; } steps_completed++; } } pixel_shade = (points_matched / steps_completed) * 127.0; // echo("Shade: pixel_shade
"); } } } } if (pixel_shade != 0) { color = alpha_alpha(im, color, pixel_shade); gdImageSetPixel(im, cx+ox, cy+oy, color); // ImageSetPixel(im, cx+ox, cy+oy, color); } } ZEND_FUNCTION(aa_filled_arc) { gdImagePtr im; double cx, cy, w, h; double given_start_angle, given_end_angle; long color; // int corners; double x_at_start, y_at_start, start_angle, y_at_end, x_at_end; double end_angle, w2, h2, fy, fx; double shade; long s_color, cur_color; zval *param; zval *IM; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rddddddl", &IM, &cx, &cy, &w, &h, &given_start_angle, &given_end_angle, &color, ¶m) == FAILURE) { return; } ZEND_FETCH_RESOURCE(im, gdImagePtr, &IM, -1.0, "Image", phpi_get_le_gd()); /* first convert the angles given into real angles so we can easily mask them this is done by skewing them by the height and width of the ellipse */ x_at_start = h*sin(given_start_angle / 180.0 * M_PI); y_at_start = w*cos(given_start_angle / 180.0 * M_PI); start_angle = atan2(y_at_start, x_at_start) * 180.0 / M_PI; x_at_end = h*sin(given_end_angle / 180.0 * M_PI); y_at_end = w*cos(given_end_angle / 180.0 * M_PI); end_angle = atan2(y_at_end, x_at_end) * 180.0 / M_PI; /* normalize angles */ if (start_angle < 0) { start_angle += 360.0; } if (end_angle < 0) { end_angle += 360.0; } start_angle = fmod(start_angle, 360.0); end_angle = fmod(end_angle, 360.0); w2 = w / 2; h2 = h / 2; for(fy = 0; fy <= h2+1; fy++) { for(fx = 0; fx <= w2 + 1; fx++) { shade = 0; s_color = color; // is all of this pixel is within boundaries? if ((((fx+1)*(fx+1))/(w2*w2) + ((fy+1)*(fy+1))/(h2*h2))<=1) { // draw it at 100% shade = 1; } else { // pixel is on the edge, so calculate the alpha // now determine which way it's passing through so we know how to calculate area // yval at left side shade = ellipse_alpha(fx, fy, h, w); } if (shade > 0) { cur_color = alpha_alpha(im, s_color, shade * 127.0); if (fx > 0) { // when fx is 0, we only want to set the pixel once pixel_arc_mask(im, cx, cy, fx, fy, start_angle, end_angle, cur_color); } pixel_arc_mask(im, cx, cy, -1.0 * fx, fy, start_angle, end_angle, cur_color); if (fy > 0) { if (fx > 0) { pixel_arc_mask(im, cx, cy, fx, -1.0 * fy, start_angle, end_angle, cur_color); } pixel_arc_mask(im, cx, cy, -1.0 * fx, -1.0 * fy, start_angle, end_angle, cur_color); } } } } }