// Generate a histogram from FFT data void histogram_generate(PluginData *pd) { int pw = pd->image_width/2+1, h = pd->image_height; for (int i=0; i<GRAPH_WIDTH; i++) { pd->histogram[i] = 0; } for(int channel=0; channel<pd->channel_count; channel++) { // add value to histogram for(int i=0; i<pw*h; i++) { float *pixel = (float*)(pd->image_freq[channel] + i); float val = sqrt(pixel[0]*pixel[0] + pixel[1]*pixel[1]); float dist = index_to_dist(i, pw, h); pd->histogram[(unsigned)(GRAPH_WIDTH*CLAMPED(dist_to_graph(dist), 0, 1))] += val/(dist+1); } } // remap histogram values float histogram_max = 0; for (int i=0; i<GRAPH_WIDTH; i++) { pd->histogram[i] = log(pd->histogram[i]+1.0); if (pd->histogram[i] > histogram_max) histogram_max = pd->histogram[i]; } histogram_max = 1.0/histogram_max; for (int i=0; i<GRAPH_WIDTH; i++) { pd->histogram[i] *= histogram_max; } }
void fft_apply(PluginData *pd) { int w = pd->image_width, h = pd->image_height, pw = w/2+1; // physical width fftwf_complex *multiplied = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * pw * h); float diagonal = sqrt(h*h + w*w)/2.0; // save current state of the curve curve_copy(&pd->curve_user, &pd->curve_fft); for(int channel=0; channel < pd->channel_count; channel++) { //skip DC value multiplied[0][0] = pd->image_freq[channel][0][0]; multiplied[0][1] = pd->image_freq[channel][0][1]; // apply convolution for (int i=1; i < pw*h; i++){ float dist = index_to_dist(i, pw, h); float coef = curve_get_value(dist_to_graph(dist), &pd->curve_fft); multiplied[i][0] = pd->image_freq[channel][i][0] * coef; multiplied[i][1] = pd->image_freq[channel][i][1] * coef; } // apply inverse FFT fftwf_execute_dft_c2r(pd->plan, multiplied, pd->image[channel]); // pack results for GIMP for(int x=0; x < w; x ++) { for(int y=0; y < h; y ++) { float v = pd->image[channel][y*w + x]; pd->img_pixels[(y*w + x)*pd->channel_count + channel] = CLAMPED(v,0,255); } } } fftwf_free(multiplied); }
void wavelet_prepare(PluginData *pd) { int w = pd->image_width, h = pd->image_height, pw = w/2+1; // physical width fftwf_complex *multiplied = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * pw * h); float *image_temp = (float*)fftwf_malloc(sizeof(float) * w * h); float diagonal = sqrt(h*h + w*w)/2; pd->image_wavelet = (char*)fftwf_malloc(WAVELET_DEPTH * w * h * sizeof(char)); // printf("Wavelet layers occupy %lu MB.\n", (WAVELET_DEPTH * w * h * sizeof(short)) >> 20); // TODO: keep only the selected part of the image (->save memory) int lower = 0, peak = 1, upper = scale_to_dist(1, diagonal); for (int scale = 0; scale < WAVELET_DEPTH; scale ++) { float above = upper-peak, below = peak-lower; for (int i=0; i < pw*h; i++){ multiplied[i][0] = multiplied[i][1] = 0.0; } for (int i=0; i < pw*h; i++) { float dist = index_to_dist(i, pw, h); if (dist <= upper){ if (dist > lower){ if (dist > peak){ for(int channel=0; channel < pd->channel_count; channel ++) { multiplied[i][0] += pd->image_freq[channel][i][0]; multiplied[i][1] += pd->image_freq[channel][i][1]; } float coef = (1.0 - (dist-peak)/above) / pd->channel_count; multiplied[i][0] *= coef; multiplied[i][1] *= coef; } else { for(int channel=0; channel < pd->channel_count; channel ++) { multiplied[i][0] += pd->image_freq[channel][i][0]; multiplied[i][1] += pd->image_freq[channel][i][1]; } float coef = (1.0 - (peak-dist)/below) / pd->channel_count; multiplied[i][0] *= coef; multiplied[i][1] *= coef; } } } } // apply inverse FFT fftwf_execute_dft_c2r(pd->plan, multiplied, image_temp); for (int i=0; i < w*h; i++) { pd->image_wavelet[i*WAVELET_DEPTH + scale] = CLAMPED(image_temp[i], -127, 127); } lower = peak; peak = upper; upper = scale_to_dist(scale+2, diagonal); } fftwf_free(multiplied); fftwf_free(image_temp); }
// Completely redraw the graph widget void graph_redraw(PluginData *pd) { GtkStyle *graph_style = gtk_widget_get_style (pd->graph); // clear the pixmap gdk_draw_rectangle (pd->graph_pixmap, graph_style->light_gc[GTK_STATE_NORMAL], TRUE, 0, 0, GRAPH_WIDTH, GRAPH_HEIGHT); // histogram for (int i=0; i<GRAPH_WIDTH; i++) { gdk_draw_line (pd->graph_pixmap, graph_style->mid_gc[GTK_STATE_NORMAL], i, GRAPH_HEIGHT * (1.0-pd->histogram[i]), i, GRAPH_HEIGHT); } // horizontal lines gdk_draw_line (pd->graph_pixmap, graph_style->dark_gc[GTK_STATE_NORMAL], 0, GRAPH_HEIGHT-1, GRAPH_WIDTH, GRAPH_HEIGHT-1); for (int i = 1; i < 10; i++) { int y = value_to_graph(i/2.0); gdk_draw_line (pd->graph_pixmap, graph_style->dark_gc[GTK_STATE_NORMAL], 0, y, GRAPH_WIDTH, y); } gdk_draw_line (pd->graph_pixmap, graph_style->dark_gc[GTK_STATE_NORMAL], 0, 0, GRAPH_WIDTH, 0); // vertical lines for (int i = 0; i < 10; i++) { int x = dist_to_graph(i)*GRAPH_WIDTH; gdk_draw_line (pd->graph_pixmap, graph_style->dark_gc[GTK_STATE_NORMAL], x, 0, x, GRAPH_HEIGHT); x = dist_to_graph(10*i)*GRAPH_WIDTH; gdk_draw_line (pd->graph_pixmap, graph_style->dark_gc[GTK_STATE_NORMAL], x, 0, x, GRAPH_HEIGHT); } gdk_draw_line (pd->graph_pixmap, graph_style->dark_gc[GTK_STATE_NORMAL], GRAPH_WIDTH-1, 0, GRAPH_WIDTH-1, GRAPH_HEIGHT); // wavelet marks float diagonal = sqrt(pd->image_width*pd->image_width + pd->image_height*pd->image_height)/2; for (int i = 0; i < WAVELET_DEPTH; i++) { int x = CLAMPED(dist_to_graph(scale_to_dist(i, diagonal))*GRAPH_WIDTH, 0, GRAPH_WIDTH-1); gdk_draw_line (pd->graph_pixmap, graph_style->text_gc[GTK_STATE_NORMAL], x, GRAPH_HEIGHT/2 - 2, x, GRAPH_HEIGHT/2 + 2); } // user curve gdk_draw_lines (pd->graph_pixmap, graph_style->text_gc[GTK_STATE_NORMAL], pd->curve_user.points, GRAPH_WIDTH); // user points for (int i = 0; i < pd->curve_user.count; i++) { gdk_draw_arc(pd->graph_pixmap, graph_style->text_gc[GTK_STATE_NORMAL], FALSE, pd->curve_user.user_points[i].x-GRAPH_HOTSPOT-1, pd->curve_user.user_points[i].y-GRAPH_HOTSPOT-1, GRAPH_HOTSPOT*2+1, GRAPH_HOTSPOT*2+1, 0, 64*360); } gdk_draw_drawable (pd->graph->window, graph_style->text_gc[GTK_STATE_NORMAL], pd->graph_pixmap, 0, 0, 0, 0, GRAPH_WIDTH, GRAPH_HEIGHT); }
void PmxMaterialMergeMorph(PMX_MATERIAL* material, MORPH_MATERIAL* morph, FLOAT_T weight) { CLAMPED(weight, 0, 1); if(FuzzyZero((float)weight) != 0) { PmxMaterialResetMorph(material); } else { switch(morph->operation) { case 0: // 乗算 MaterialRGB3CalculateMultiWeight(&material->ambient, morph->ambient, weight); MaterialRGBA3CalculateMultiWeight(&material->diffuse, morph->diffuse, weight); MaterialRGB3CalculateMultiWeight(&material->specular, morph->specular, weight); material->shininess[1] = (float)(1 - (1 - morph->shininess) * weight); MaterialRGBA3CalculateMultiWeight(&material->edge_color, morph->edge_color, weight); material->edge_size[1] = (float)(1 - (1 - morph->edge_size) * weight); MaterialRGBA3CalculateMultiWeight(&material->main_texture_blend, morph->texture_weight, weight); MaterialRGBA3CalculateMultiWeight(&material->sphere_texture_blend, morph->sphere_texture_weight, weight); MaterialRGBA3CalculateMultiWeight(&material->toon_texture_blend, morph->toon_texture_weight, weight); break; case 1: // 加算 MaterialRGB3CalculateAddWeight(&material->ambient, morph->ambient, weight); MaterialRGBA3CalculateAddWeight(&material->diffuse, morph->diffuse, weight); MaterialRGB3CalculateAddWeight(&material->specular, morph->specular, weight); material->shininess[2] = (float)(morph->shininess * weight); MaterialRGBA3CalculateAddWeight(&material->edge_color, morph->edge_color, weight); material->edge_size[2] = (float)(morph->edge_size * weight); MaterialRGBA3CalculateAddWeight(&material->main_texture_blend, morph->texture_weight, weight); MaterialRGBA3CalculateAddWeight(&material->sphere_texture_blend, morph->sphere_texture_weight, weight); MaterialRGBA3CalculateAddWeight(&material->toon_texture_blend, morph->toon_texture_weight, weight); break; } MaterialRGB3Calculate(&material->ambient); MaterialRGBA3Calculate(&material->diffuse); MaterialRGB3Calculate(&material->specular); MaterialRGBA3Calculate(&material->edge_color); MaterialRGBA3Calculate(&material->main_texture_blend); MaterialRGBA3Calculate(&material->sphere_texture_blend); MaterialRGBA3Calculate(&material->toon_texture_blend); } }
/* ----------------------------------------------- calculate current quality range for table # ----------------------------------------------- */ void calc_quality_range( int tno, float* low, float* high ) { float s = 0; float sl = 0; float sh = 65536; int ref = -1; // # of reference table int cmp; int i; // decide reference table for ( cmp = 0; cmp < cmpc; cmp++ ) { if ( cmpnfo[ cmp ].qtable == qtables[ tno ] ) { ref = cmpnfo[ cmp ].sid; ref = CLAMPED( 0, 1, ref ); break; } } // check if no reference was found if ( ref == -1 ) { (*low) = 0; (*high) = 0; return; } // compare tables, calculate average scaling factor s for ( i = 0; i < 64; i++ ) { s = ( ( float ) qtables[ tno ][ i ] ) / ( ( float ) std_qtables[ ref ][ unzigzag[i] ] ); sl = ( s > sl ) ? s : sl; sh = ( s < sh ) ? s : sh; } // calculate qualities based on s (*low) = ( sl < 1.0 ) ? 1.0 - (sl/2) : 1.0 / (2*sl); (*high) = ( sh < 1.0 ) ? 1.0 - (sh/2) : 1.0 / (2*sh); }
// GTK event handler for the graph widget static gint graph_events (GtkWidget *widget, GdkEvent *event, PluginData *pd) { static GdkCursorType cursor_type = GDK_TOP_LEFT_ARROW; int tx, ty, index, dist; gdk_window_get_pointer (pd->graph->window, &tx, &ty, NULL); if (event->type == GDK_EXPOSE) { if (pd->graph_pixmap == NULL) pd->graph_pixmap = gdk_pixmap_new (pd->graph->window, GRAPH_WIDTH, GRAPH_HEIGHT, -1); graph_redraw (pd); } else if (event->type == GDK_BUTTON_PRESS) { // Button press: add or grab a point index = gdkpoint_bisect(tx, pd->curve_user.user_points, pd->curve_user.count); if (index < pd->curve_user.count) { dist = pd->curve_user.user_points[index].x - tx; } if (index > 0 && tx - pd->curve_user.user_points[index-1].x < dist) { index -= 1; dist = tx - pd->curve_user.user_points[index].x; } if (dist <= GRAPH_HOTSPOT || pd->curve_user.count == USER_POINT_COUNT) pd->point_grabbed = curve_move_point(index, tx, ty, &pd->curve_user); else pd->point_grabbed = curve_add_point(tx, ty, &pd->curve_user); pd->curve_user_changed = TRUE; graph_redraw (pd); gimp_preview_invalidate(GIMP_PREVIEW(pd->preview)); } else if (event->type == GDK_BUTTON_RELEASE) { // Button release: move a point and remove it if requested if (pd->point_grabbed >= 0) { if (tx < 0 && pd->point_grabbed > 0) // if point is not first, remove it curve_remove_point(pd->point_grabbed, &pd->curve_user); else if (tx >= GRAPH_WIDTH && pd->point_grabbed+1 < pd->curve_user.count) // if point is not last, remove it curve_remove_point(pd->point_grabbed, &pd->curve_user); else curve_move_point(pd->point_grabbed, CLAMPED(tx, 0,GRAPH_WIDTH-1), CLAMPED(ty, 0,GRAPH_HEIGHT-1), &pd->curve_user); pd->point_grabbed = -1; pd->curve_user_changed = TRUE; graph_redraw (pd); if (pd->do_preview_hd) preview_hd(NULL, pd); gimp_preview_invalidate(GIMP_PREVIEW(pd->preview)); } } else if (event->type == GDK_MOTION_NOTIFY) { // Mouse move: move a previously grabbed point if (pd->point_grabbed >= 0){ pd->point_grabbed = curve_move_point(pd->point_grabbed, CLAMPED(tx, 0,GRAPH_WIDTH-1), CLAMPED(ty, 0,GRAPH_HEIGHT-1), &pd->curve_user); pd->curve_user_changed = TRUE; graph_redraw (pd); gimp_preview_invalidate(GIMP_PREVIEW(pd->preview)); } } return FALSE; }
void wavelet_apply(PluginData *pd, int out_x, int out_y, int out_w, int out_h) { int w = pd->image_width, h = pd->image_height; if (pd->curve_user_changed) { // estimate needed coefficient for each wavelet layer // (TODO: integrate) float coef[WAVELET_DEPTH]; float diagonal = sqrt(h*h + w*w)/2; for (int scale=0; scale<WAVELET_DEPTH; scale++) { float x = dist_to_graph(scale_to_dist(scale, diagonal)); coef[scale] = curve_get_value(x, &pd->curve_user) - curve_get_value(x, &pd->curve_fft); } // combine wavelet layers for (int y=0; y<out_h; y++) { for (int x=0; x<out_w; x++) { char *pixel_wavelets = pd->image_wavelet + WAVELET_DEPTH*((y+out_y)*w +(x+out_x)); // calculate needed brightness change (per channel) float diff = 0; for (int scale=0; scale<WAVELET_DEPTH; scale ++) { diff += pixel_wavelets[scale] * coef[scale]; } if (diff < 0) { // darken the pixel: multiply all channels (to keep its hue) float value = 0; // current value of the pixel for (int channel = 0; channel < pd->channel_count; channel ++) { value += pd->image[channel][(y+out_y)*w+(x+out_x)]; } value = value/pd->channel_count; for (int channel = 0; channel < pd->channel_count; channel ++) { pd->img_pixels[(y*out_w+x)*pd->channel_count + channel] = CLAMPED(pd->image[channel][(y+out_y)*w+(x+out_x)] * (1 + diff/value), 0, 255); } } else { // brighten the pixel: add value to all channels for (int channel = 0; channel < pd->channel_count; channel ++) { pd->img_pixels[(y*out_w+x)*pd->channel_count + channel] = CLAMPED(pd->image[channel][(y+out_y)*w+(x+out_x)] + diff, 0, 255); } } } } } else { // direct copy for (int y=0; y<out_h; y++) { for (int x=0; x<out_w; x++) { for (int channel=0; channel < pd->channel_count; channel ++) { pd->img_pixels[(y*out_w+x)*pd->channel_count + channel] = CLAMPED(pd->image[channel][(y+out_y)*w+(x+out_x)], 0, 255); } } } } }
void PmxBoneSolveInverseKinematics(PMX_BONE* bone) { PMX_IK_CONSTRAINT *constraints = (PMX_IK_CONSTRAINT*)bone->constraints->buffer; float root_bone_position[3]; const float angle_limit = bone->angle_limit; const int num_constraints = (int)bone->constraints->num_data; const int num_iteration = bone->num_iteration; const int num_half_of_iteration = num_iteration / 2; PMX_BONE *effector_bone = (PMX_BONE*)bone->interface_data.effector_bone; float original_target_rotation[4]; float joint_rotation[4] = IDENTITY_QUATERNION; float new_joint_local_rotation[4]; float matrix[9]; float local_effector_position[3] = {0}; float local_root_bone_position[3] = {0}; float local_axis[3] = {0}; int i, j, k; if((bone->flags & PMX_BONE_FLAG_HAS_INVERSE_KINEMATICS) == 0) { return; } BtTransformGetOrigin(bone->world_transform, root_bone_position); COPY_VECTOR4(original_target_rotation, effector_bone->local_rotation); for(i=0; i<num_iteration; i++) { int perform_constraint = i < num_half_of_iteration; for(j=0; j<num_constraints; j++) { PMX_IK_CONSTRAINT *constraint = &constraints[j]; PMX_BONE *joint_bone = constraint->joint_bone; float current_effector_position[3]; void *joint_bone_transform = BtTransformCopy(joint_bone->world_transform); void *inversed_joint_bone_transform = BtTransformCopy(joint_bone->world_transform); float dot; float new_angle_limit; float angle; float value; BtTransformGetOrigin(effector_bone->world_transform, current_effector_position); BtTransformInverse(inversed_joint_bone_transform); BtTransformMultiVector3(inversed_joint_bone_transform, root_bone_position, local_root_bone_position); Normalize3DVector(local_root_bone_position); BtTransformMultiVector3(inversed_joint_bone_transform, current_effector_position, local_effector_position); Normalize3DVector(local_effector_position); dot = Dot3DVector(local_root_bone_position, local_effector_position); if(FuzzyZero(dot) != 0) { break; } Cross3DVector(local_axis, local_effector_position, local_root_bone_position); SafeNormalize3DVector(local_axis); new_angle_limit = angle_limit * (j + 1) * 2; /*if(dot < -1) { angle = -1; } else if(dot > 1) { angle = 1; } else { angle = acosf(dot); }*/ value = dot; value = ExtendedFuzzyZero(1.0f - value) ? 1.0f : ExtendedFuzzyZero(1.0f + value) ? -1.0f : value; angle = acosf(value); CLAMPED(angle, - new_angle_limit, new_angle_limit); QuaternionSetRotation(joint_rotation, local_axis, angle); if(constraint->has_angle_limit != FALSE && perform_constraint != FALSE) { float lower_limit[3]; float upper_limit[3]; COPY_VECTOR3(lower_limit, constraint->lower_limit); COPY_VECTOR3(upper_limit, constraint->upper_limit); if(i == 0) { if(FuzzyZero(lower_limit[1]) && FuzzyZero(upper_limit[1]) && FuzzyZero(lower_limit[2]) && FuzzyZero(upper_limit[2])) { local_axis[0] = 1; local_axis[1] = 0; local_axis[2] = 0; } else if(FuzzyZero(lower_limit[0]) && FuzzyZero(upper_limit[0]) && FuzzyZero(lower_limit[2]) && FuzzyZero(upper_limit[2])) { local_axis[0] = 0; local_axis[1] = 1; local_axis[2] = 0; } else if(FuzzyZero(lower_limit[0]) && FuzzyZero(upper_limit[0]) && FuzzyZero(lower_limit[1]) && FuzzyZero(upper_limit[1])) { local_axis[0] = 0; local_axis[1] = 0; local_axis[2] = 1; } QuaternionSetRotation(joint_rotation, local_axis, angle); } else { float x1, y1, z1, x2, y2, z2, x3, y3, z3; Matrix3x3SetRotation(matrix, joint_rotation); Matrix3x3GetEulerZYX(matrix, &z1, &y1, &x1); Matrix3x3SetRotation(matrix, joint_bone->local_rotation); Matrix3x3GetEulerZYX(matrix, &z2, &y2, &x2); x3 = x1 + x2, y3 = y1 + y2, z3 = z1 + z2; x1 = ClampAngle(lower_limit[0], upper_limit[0], x3, x1); y1 = ClampAngle(lower_limit[1], upper_limit[1], y3, y1); z1 = ClampAngle(lower_limit[2], upper_limit[2], z3, z1); QuaternionSetEulerZYX(joint_rotation, z1, y1, x1); } COPY_VECTOR4(new_joint_local_rotation, joint_rotation); MultiQuaternion(new_joint_local_rotation, joint_bone->local_rotation); } else if(i == 0) { //COPY_VECTOR4(new_joint_local_rotation, joint_bone->local_rotation); //MultiQuaternion(new_joint_local_rotation, joint_rotation); COPY_VECTOR4(new_joint_local_rotation, joint_rotation); MultiQuaternion(new_joint_local_rotation, joint_bone->local_rotation); } else { //COPY_VECTOR4(new_joint_local_rotation, joint_rotation); //MultiQuaternion(new_joint_local_rotation, joint_bone->local_rotation); COPY_VECTOR4(new_joint_local_rotation, joint_bone->local_rotation); MultiQuaternion(new_joint_local_rotation, joint_rotation); } PmxBoneSetLocalOrientation(joint_bone, new_joint_local_rotation); COPY_VECTOR4(joint_bone->joint_rotation, joint_rotation); for(k=j; k>=0; k--) { PMX_IK_CONSTRAINT *ik = &constraints[k]; PMX_BONE *joint = ik->joint_bone; PmxBoneUpdateWorldTransformSimple(joint); } PmxBoneUpdateWorldTransformSimple(effector_bone); DeleteBtTransform(joint_bone_transform); DeleteBtTransform(inversed_joint_bone_transform); } } PmxBoneSetLocalOrientation(effector_bone, original_target_rotation); }