/***************************************************************************** * puzzle_auto_solve: solve the puzzle depending on auto_solve_speed parameter * = move one piece at the final location each time * auto_solve_countdown is < 0 *****************************************************************************/ void puzzle_auto_solve( filter_t *p_filter) { filter_sys_t *p_sys = p_filter->p_sys; if ( p_sys->s_current_param.i_auto_solve_speed < 500 ) return; if ( --p_sys->i_auto_solve_countdown_val > 0 ) return; /* delay reached, preset next delay and proceed with puzzle_auto_solve */ p_sys->i_auto_solve_countdown_val = init_countdown(p_sys->s_current_param.i_auto_solve_speed); /* random piece to be moved */ int32_t i_start = ((unsigned)vlc_mrand48()) % p_sys->s_allocated.i_pieces_nbr; /* here the computer will help player by placing the piece at the final location */ for (uint32_t i_l = 0; i_l < p_sys->s_allocated.i_pieces_nbr; i_l++) { int32_t i = ( i_l + i_start ) % p_sys->s_allocated.i_pieces_nbr; if ( !p_sys->ps_pieces[i].b_finished ) { for (uint32_t j = 0; j < p_sys->s_allocated.i_pieces_nbr; j++) { if ( p_sys->ps_pieces[j].i_group_ID == p_sys->ps_pieces[i].i_group_ID ) { p_sys->ps_pieces[j].i_actual_angle = 0; p_sys->ps_pieces[j].i_actual_mirror = +1; p_sys->ps_pieces[j].ps_piece_in_plane[0].i_actual_x = p_sys->ps_pieces[j].ps_piece_in_plane[0].i_original_x; p_sys->ps_pieces[j].ps_piece_in_plane[0].i_actual_y = p_sys->ps_pieces[j].ps_piece_in_plane[0].i_original_y; puzzle_calculate_corners( p_filter, j ); } } break; } } }
/***************************************************************************** * puzzle_auto_shuffle: shuffle the pieces on the desk depending on * auto_shuffle_speed parameter * = random move of one piece each time * auto_shuffle_countdown is < 0 *****************************************************************************/ void puzzle_auto_shuffle( filter_t *p_filter) { filter_sys_t *p_sys = p_filter->p_sys; if ( p_sys->s_current_param.i_auto_shuffle_speed < 500 ) return; if ( --p_sys->i_auto_shuffle_countdown_val > 0 ) return; /* delay reached, preset next delay and proceed with puzzle_auto_shuffle */ p_sys->i_auto_shuffle_countdown_val = init_countdown(p_sys->s_current_param.i_auto_shuffle_speed); /* random piece to be moved */ int32_t i_start = ((unsigned)vlc_mrand48()) % p_sys->s_allocated.i_pieces_nbr; for (uint32_t i_l = 0; i_l < p_sys->s_allocated.i_pieces_nbr; i_l++){ int32_t i = ( i_l + i_start ) % p_sys->s_allocated.i_pieces_nbr; /* find one piece which is part of one group */ if ( p_sys->pi_group_qty[p_sys->ps_pieces[i].i_group_ID] > 1 ) { /* find an empty group to be used by this dismantled piece */ uint32_t i_new_group; for ( i_new_group = 0 ; i_new_group < p_sys->s_allocated.i_pieces_nbr ; i_new_group ++ ) if ( p_sys->pi_group_qty[i_new_group] == 0 ) break; p_sys->ps_pieces[i].i_group_ID = i_new_group; p_sys->ps_pieces[i].b_finished = false; /* random rotate & mirror */ switch ( p_sys->s_current_param.i_rotate ) { case 1: puzzle_rotate_pce( p_filter, i, (( (unsigned) vlc_mrand48()) % ( 2 ) ) * 2, p_sys->ps_pieces[i].i_center_x, p_sys->ps_pieces[i].i_center_y, false ); break; case 2: puzzle_rotate_pce( p_filter, i, (( (unsigned) vlc_mrand48()) % ( 4 ) ), p_sys->ps_pieces[i].i_center_x, p_sys->ps_pieces[i].i_center_y, false ); break; case 3: puzzle_rotate_pce( p_filter, i, (( (unsigned) vlc_mrand48()) % ( 8 ) ), p_sys->ps_pieces[i].i_center_x, p_sys->ps_pieces[i].i_center_y, false ); break; } /* random mvt */ p_sys->ps_pieces[i].ps_piece_in_plane[0].i_actual_x = p_sys->ps_desk_planes[0].i_border_width + ( (unsigned) vlc_mrand48()) % ( p_sys->ps_desk_planes[0].i_width - 2*p_sys->ps_desk_planes[0].i_border_width - p_sys->ps_pieces[i].ps_piece_in_plane[0].i_width) + p_sys->ps_pieces[i].ps_piece_in_plane[0].i_width / 2 * ( 1 - p_sys->ps_pieces[i].i_step_x_x ) - (p_sys->ps_pieces[i].ps_piece_in_plane[0].i_lines / 2) * p_sys->ps_pieces[i].i_step_y_x; p_sys->ps_pieces[i].ps_piece_in_plane[0].i_actual_y = p_sys->ps_desk_planes[0].i_border_lines + ( (unsigned) vlc_mrand48()) % ( p_sys->ps_desk_planes[0].i_lines - 2*p_sys->ps_desk_planes[0].i_border_lines - p_sys->ps_pieces[i].ps_piece_in_plane[0].i_lines) + p_sys->ps_pieces[i].ps_piece_in_plane[0].i_lines / 2 * ( 1 - p_sys->ps_pieces[i].i_step_y_y ) - (p_sys->ps_pieces[i].ps_piece_in_plane[0].i_width / 2) * p_sys->ps_pieces[i].i_step_x_y; /* redefine shapes */ uint32_t i_left_pce = 0; uint32_t i_right_pce = 6; uint32_t i_top_pce = 2; uint32_t i_btm_pce = 4; uint32_t i_pce = 0; for (int32_t i_row = 0; i_row < p_sys->s_allocated.i_rows; i_row++) for (int32_t i_col = 0; i_col < p_sys->s_allocated.i_cols; i_col++) { if (p_sys->ps_pieces[i].i_original_row == p_sys->ps_pieces[i_pce].i_original_row) { if (p_sys->ps_pieces[i].i_original_col == p_sys->ps_pieces[i_pce].i_original_col - 1) i_right_pce = i_pce; else if (p_sys->ps_pieces[i].i_original_col == p_sys->ps_pieces[i_pce].i_original_col + 1) i_left_pce = i_pce; } else if (p_sys->ps_pieces[i].i_original_col == p_sys->ps_pieces[i_pce].i_original_col) { if (p_sys->ps_pieces[i].i_original_row == p_sys->ps_pieces[i_pce].i_original_row - 1) i_btm_pce = i_pce; else if (p_sys->ps_pieces[i].i_original_row == p_sys->ps_pieces[i_pce].i_original_row + 1) i_top_pce = i_pce; } i_pce++; } if ((p_sys->ps_pieces[i].i_left_shape == 0) && (p_sys->ps_pieces[i].i_original_col != 0)) { p_sys->ps_pieces[i_left_pce].i_right_shape = 6 + 8 + 8*(( (unsigned) vlc_mrand48()) % ( SHAPES_QTY ) ) + (vlc_mrand48() & 0x01); p_sys->ps_pieces[i].i_left_shape = (p_sys->ps_pieces[i_left_pce].i_right_shape - 6 ) ^ 0x01; } if ((p_sys->ps_pieces[i].i_right_shape == 6) && (p_sys->ps_pieces[i].i_original_col != p_sys->s_allocated.i_cols-1)) { p_sys->ps_pieces[i].i_right_shape = 6 + 8 + 8*(( (unsigned) vlc_mrand48()) % ( SHAPES_QTY ) ) + (vlc_mrand48() & 0x01); p_sys->ps_pieces[i_right_pce].i_left_shape = (p_sys->ps_pieces[i].i_right_shape - 6 ) ^ 0x01; } if ((p_sys->ps_pieces[i].i_top_shape == 2) && (p_sys->ps_pieces[i].i_original_row != 0)) { p_sys->ps_pieces[i_top_pce].i_btm_shape = 4 + 8 + 8*(( (unsigned) vlc_mrand48()) % ( SHAPES_QTY ) ) + (vlc_mrand48() & 0x01); p_sys->ps_pieces[i].i_top_shape = (p_sys->ps_pieces[i_top_pce].i_btm_shape - 2 ) ^ 0x01; } if ((p_sys->ps_pieces[i].i_btm_shape == 4) && (p_sys->ps_pieces[i].i_original_row != p_sys->s_allocated.i_rows-1)) { p_sys->ps_pieces[i].i_btm_shape = 4 + 8 + 8*(( (unsigned) vlc_mrand48()) % ( SHAPES_QTY ) ) + (vlc_mrand48() & 0x01); p_sys->ps_pieces[i_btm_pce].i_top_shape = (p_sys->ps_pieces[i].i_btm_shape - 2 ) ^ 0x01; } puzzle_calculate_corners( p_filter, i ); break; } } }
/** * Filter a picture */ picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) { if( !p_pic_in || !p_filter) return NULL; const video_format_t *p_fmt_in = &p_filter->fmt_in.video; filter_sys_t *p_sys = p_filter->p_sys; picture_t *p_pic_out = filter_NewPicture( p_filter ); if( !p_pic_out ) { picture_Release( p_pic_in ); return NULL; } int i_ret = 0; p_sys->b_bake_request = false; if ((p_sys->pi_order == NULL) || (p_sys->ps_desk_planes == NULL) || (p_sys->ps_pict_planes == NULL) || (p_sys->ps_puzzle_array == NULL) || (p_sys->ps_pieces == NULL)) p_sys->b_init = false; if ((p_sys->ps_pieces_shapes == NULL) && p_sys->s_current_param.b_advanced && (p_sys->s_current_param.i_shape_size != 0)) p_sys->b_init = false; /* assert initialized & allocated data match with current frame characteristics */ if ( p_sys->s_allocated.i_planes != p_pic_out->i_planes) p_sys->b_init = false; p_sys->s_current_param.i_planes = p_pic_out->i_planes; if (p_sys->ps_pict_planes != NULL) { for (uint8_t i_plane = 0; i_plane < p_sys->s_allocated.i_planes; i_plane++) { if ( (p_sys->ps_pict_planes[i_plane].i_lines != p_pic_in->p[i_plane].i_visible_lines) || (p_sys->ps_pict_planes[i_plane].i_width != p_pic_in->p[i_plane].i_visible_pitch / p_pic_in->p[i_plane].i_pixel_pitch) || (p_sys->ps_desk_planes[i_plane].i_lines != p_pic_out->p[i_plane].i_visible_lines) || (p_sys->ps_desk_planes[i_plane].i_width != p_pic_out->p[i_plane].i_visible_pitch / p_pic_out->p[i_plane].i_pixel_pitch) ) p_sys->b_init = false; } } p_sys->s_current_param.i_pict_width = (int) p_pic_in->p[0].i_visible_pitch / p_pic_in->p[0].i_pixel_pitch; p_sys->s_current_param.i_pict_height = (int) p_pic_in->p[0].i_visible_lines; p_sys->s_current_param.i_desk_width = (int) p_pic_out->p[0].i_visible_pitch / p_pic_out->p[0].i_pixel_pitch; p_sys->s_current_param.i_desk_height = (int) p_pic_out->p[0].i_visible_lines; /* assert no mismatch between sizes */ if ( p_sys->s_current_param.i_pict_width != p_sys->s_current_param.i_desk_width || p_sys->s_current_param.i_pict_height != p_sys->s_current_param.i_desk_height || p_sys->s_current_param.i_pict_width != (int) p_fmt_in->i_visible_width || p_sys->s_current_param.i_pict_height != (int) p_fmt_in->i_visible_height ) { picture_Release(p_pic_in); picture_Release(p_pic_out); return NULL; } vlc_mutex_lock( &p_sys->lock ); /* check if we have to compute initial data */ if ( p_sys->b_change_param || p_sys->b_bake_request || !p_sys->b_init ) { if ( p_sys->s_allocated.i_rows != p_sys->s_new_param.i_rows || p_sys->s_allocated.i_cols != p_sys->s_new_param.i_cols || p_sys->s_allocated.i_rotate != p_sys->s_new_param.i_rotate || p_sys->s_allocated.i_mode != p_sys->s_new_param.i_mode || p_sys->b_bake_request || !p_sys->b_init ) { p_sys->b_bake_request = true; p_sys->b_init = false; p_sys->b_shuffle_rqst = true; p_sys->b_shape_init = false; } if ( p_sys->s_current_param.i_border != p_sys->s_new_param.i_border || p_sys->s_current_param.i_shape_size != p_sys->s_new_param.i_shape_size ) { p_sys->b_bake_request = true; p_sys->b_shape_init = false; } /* depending on the game selected, set associated internal flags */ switch ( p_sys->s_new_param.i_mode ) { case 0: /* jigsaw puzzle */ p_sys->s_new_param.b_advanced = true; p_sys->s_new_param.b_blackslot = false; p_sys->s_new_param.b_near = false; break; case 1: /* sliding puzzle */ p_sys->s_new_param.b_advanced = false; p_sys->s_new_param.b_blackslot = true; p_sys->s_new_param.b_near = true; break; case 2: /* swap puzzle */ p_sys->s_new_param.b_advanced = false; p_sys->s_new_param.b_blackslot = false; p_sys->s_new_param.b_near = true; break; case 3: /* exchange puzzle */ p_sys->s_new_param.b_advanced = false; p_sys->s_new_param.b_blackslot = false; p_sys->s_new_param.b_near = false; break; } p_sys->s_current_param.i_mode = p_sys->s_new_param.i_mode; if ( p_sys->s_current_param.b_blackslot != p_sys->s_new_param.b_blackslot && p_sys->i_selected == NO_PCE && p_sys->s_current_param.b_blackslot ) p_sys->i_selected = 0; if ( p_sys->s_current_param.i_auto_shuffle_speed != p_sys->s_new_param.i_auto_shuffle_speed ) p_sys->i_auto_shuffle_countdown_val = init_countdown(p_sys->s_new_param.i_auto_shuffle_speed); if ( p_sys->s_current_param.i_auto_solve_speed != p_sys->s_new_param.i_auto_solve_speed ) p_sys->i_auto_solve_countdown_val = init_countdown(p_sys->s_current_param.i_auto_solve_speed); p_sys->s_current_param.i_rows = p_sys->s_new_param.i_rows; p_sys->s_current_param.i_cols = p_sys->s_new_param.i_cols; p_sys->s_current_param.i_pieces_nbr = p_sys->s_current_param.i_rows * p_sys->s_current_param.i_cols; p_sys->s_current_param.b_advanced = p_sys->s_new_param.b_advanced; if (!p_sys->s_new_param.b_advanced) { p_sys->s_current_param.b_blackslot = p_sys->s_new_param.b_blackslot; p_sys->s_current_param.b_near = p_sys->s_new_param.b_near || p_sys->s_new_param.b_blackslot; p_sys->s_current_param.i_border = 0; p_sys->s_current_param.b_preview = false; p_sys->s_current_param.i_preview_size= 0; p_sys->s_current_param.i_shape_size = 0; p_sys->s_current_param.i_auto_shuffle_speed = 0; p_sys->s_current_param.i_auto_solve_speed = 0; p_sys->s_current_param.i_rotate = 0; } else { p_sys->s_current_param.b_blackslot = false; p_sys->s_current_param.b_near = false; p_sys->s_current_param.i_border = p_sys->s_new_param.i_border; p_sys->s_current_param.b_preview = p_sys->s_new_param.b_preview; p_sys->s_current_param.i_preview_size = p_sys->s_new_param.i_preview_size; p_sys->s_current_param.i_shape_size = p_sys->s_new_param.i_shape_size; p_sys->s_current_param.i_auto_shuffle_speed = p_sys->s_new_param.i_auto_shuffle_speed; p_sys->s_current_param.i_auto_solve_speed = p_sys->s_new_param.i_auto_solve_speed; p_sys->s_current_param.i_rotate = p_sys->s_new_param.i_rotate; } p_sys->b_change_param = false; } vlc_mutex_unlock( &p_sys->lock ); /* generate initial puzzle data when needed */ if ( p_sys->b_bake_request ) { if (!p_sys->b_shuffle_rqst) { /* here we have to keep the same position * we have to save locations before generating new data */ save_game_t *ps_save_game = puzzle_save(p_filter); if (!ps_save_game) return CopyInfoAndRelease( p_pic_out, p_pic_in ); i_ret = puzzle_bake( p_filter, p_pic_out, p_pic_in ); if ( i_ret != VLC_SUCCESS ) { free(ps_save_game->ps_pieces); free(ps_save_game); return CopyInfoAndRelease( p_pic_out, p_pic_in ); } puzzle_load( p_filter, ps_save_game); free(ps_save_game->ps_pieces); free(ps_save_game); } else { i_ret = puzzle_bake( p_filter, p_pic_out, p_pic_in ); if ( i_ret != VLC_SUCCESS ) return CopyInfoAndRelease( p_pic_out, p_pic_in ); } } /* shuffle the desk and generate pieces data */ if ( p_sys->b_shuffle_rqst && p_sys->b_init ) { i_ret = puzzle_bake_piece ( p_filter ); if (i_ret != VLC_SUCCESS) return CopyInfoAndRelease( p_pic_out, p_pic_in ); } /* preset output pic */ if ( !p_sys->b_bake_request && !p_sys->b_shuffle_rqst && p_sys->b_init && !p_sys->b_finished ) puzzle_preset_desk_background(p_pic_out, 0, 127, 127); else { /* copy src to dst during init & bake process */ for( uint8_t i_plane = 0; i_plane < p_pic_out->i_planes; i_plane++ ) memcpy( p_pic_out->p[i_plane].p_pixels, p_pic_in->p[i_plane].p_pixels, p_pic_in->p[i_plane].i_pitch * (int32_t) p_pic_in->p[i_plane].i_visible_lines ); } vlc_mutex_lock( &p_sys->pce_lock ); /* manage the game, adjust locations, groups and regenerate some corrupted data if any */ for (uint32_t i = 0; i < __MAX( 4, p_sys->s_allocated.i_pieces_nbr / 4 ) && ( !p_sys->b_bake_request && !p_sys->b_mouse_drag && p_sys->b_init && p_sys->s_current_param.b_advanced ); i++) { puzzle_solve_pces_accuracy( p_filter ); } for (uint32_t i = 0; i < __MAX( 4, p_sys->s_allocated.i_pieces_nbr / 4 ) && ( !p_sys->b_bake_request && !p_sys->b_mouse_drag && p_sys->b_init && p_sys->s_current_param.b_advanced ); i++) { puzzle_solve_pces_group( p_filter ); } if ( !p_sys->b_bake_request && !p_sys->b_mouse_drag && p_sys->b_init && p_sys->s_current_param.b_advanced ) puzzle_count_pce_group( p_filter); if ( !p_sys->b_bake_request && !p_sys->b_mouse_drag && p_sys->b_init && p_sys->s_current_param.b_advanced ) { i_ret = puzzle_sort_layers( p_filter); if (i_ret != VLC_SUCCESS) { vlc_mutex_unlock( &p_sys->pce_lock ); return CopyInfoAndRelease( p_pic_out, p_pic_in ); } } for (uint32_t i = 0; i < __MAX( 4, p_sys->s_allocated.i_pieces_nbr / 24 ) && ( !p_sys->b_bake_request && !p_sys->b_mouse_drag && p_sys->b_init && p_sys->s_current_param.b_advanced ); i++) { p_sys->i_calc_corn_loop++; p_sys->i_calc_corn_loop %= p_sys->s_allocated.i_pieces_nbr; puzzle_calculate_corners( p_filter, p_sys->i_calc_corn_loop ); } /* computer moves some piece depending on auto_solve and auto_shuffle param */ if ( !p_sys->b_bake_request && !p_sys->b_mouse_drag && p_sys->b_init && p_sys->ps_puzzle_array != NULL && p_sys->s_current_param.b_advanced ) { puzzle_auto_shuffle( p_filter ); puzzle_auto_solve( p_filter ); } vlc_mutex_unlock( &p_sys->pce_lock ); /* draw output pic */ if ( !p_sys->b_bake_request && p_sys->b_init && p_sys->ps_puzzle_array != NULL ) { puzzle_draw_borders(p_filter, p_pic_in, p_pic_out); p_sys->i_pointed_pce = NO_PCE; puzzle_draw_pieces(p_filter, p_pic_in, p_pic_out); /* when puzzle_draw_pieces() has not updated p_sys->i_pointed_pce, * use puzzle_find_piece to define the piece pointed by the mouse */ if (p_sys->i_pointed_pce == NO_PCE) p_sys->i_mouse_drag_pce = puzzle_find_piece( p_filter, p_sys->i_mouse_x, p_sys->i_mouse_y, -1); else p_sys->i_mouse_drag_pce = p_sys->i_pointed_pce; if (p_sys->s_current_param.b_preview ) puzzle_draw_preview(p_filter, p_pic_in, p_pic_out); /* highlight the selected piece when not playing jigsaw mode */ if ( p_sys->i_selected != NO_PCE && !p_sys->s_current_param.b_blackslot && !p_sys->s_current_param.b_advanced ) { int32_t c = (p_sys->i_selected % p_sys->s_allocated.i_cols); int32_t r = (p_sys->i_selected / p_sys->s_allocated.i_cols); puzzle_draw_rectangle(p_pic_out, p_sys->ps_puzzle_array[r][c][0].i_x, p_sys->ps_puzzle_array[r][c][0].i_y, p_sys->ps_puzzle_array[r][c][0].i_width, p_sys->ps_puzzle_array[r][c][0].i_lines, 255, 127, 127); } /* draw the blackslot when playing sliding puzzle mode */ if ( p_sys->i_selected != NO_PCE && p_sys->s_current_param.b_blackslot && !p_sys->s_current_param.b_advanced ) { int32_t c = (p_sys->i_selected % p_sys->s_allocated.i_cols); int32_t r = (p_sys->i_selected / p_sys->s_allocated.i_cols); puzzle_fill_rectangle(p_pic_out, p_sys->ps_puzzle_array[r][c][0].i_x, p_sys->ps_puzzle_array[r][c][0].i_y, p_sys->ps_puzzle_array[r][c][0].i_width, p_sys->ps_puzzle_array[r][c][0].i_lines, 0, 127, 127); } /* Draw the 'puzzle_shuffle' button if the puzzle is finished */ if ( p_sys->b_finished ) puzzle_draw_sign(p_pic_out, 0, 0, SHUFFLE_WIDTH, SHUFFLE_LINES, ppsz_shuffle_button, false); /* draw an arrow at mouse pointer to indicate current action (rotation...) */ if ((p_sys->i_mouse_drag_pce != NO_PCE) && !p_sys->b_mouse_drag && !p_sys->b_finished && p_sys->s_current_param.b_advanced ) { vlc_mutex_lock( &p_sys->pce_lock ); int32_t i_delta_x; if (p_sys->s_current_param.i_rotate != 3) i_delta_x = 0; else if ( (p_sys->ps_pieces[p_sys->i_mouse_drag_pce].i_actual_angle & 1) == 0) i_delta_x = p_sys->ps_desk_planes[0].i_pce_max_width / 6; else i_delta_x = p_sys->ps_desk_planes[0].i_pce_max_lines / 6; if (p_sys->s_current_param.i_rotate == 0) p_sys->i_mouse_action = 0; else if (p_sys->s_current_param.i_rotate == 1) p_sys->i_mouse_action = 2; else if ( p_sys->i_mouse_x >= ( p_sys->ps_pieces[p_sys->i_mouse_drag_pce].i_center_x + i_delta_x) ) p_sys->i_mouse_action = -1; /* rotate counterclockwise */ else if ( p_sys->i_mouse_x <= ( p_sys->ps_pieces[p_sys->i_mouse_drag_pce].i_center_x - i_delta_x) ) p_sys->i_mouse_action = +1; else p_sys->i_mouse_action = 4; /* center click: only mirror */ if ( p_sys->i_mouse_action == +1 ) puzzle_draw_sign(p_pic_out, p_sys->i_mouse_x - ARROW_WIDTH, p_sys->i_mouse_y, ARROW_WIDTH, ARROW_LINES, ppsz_rot_arrow_sign, false); else if ( p_sys->i_mouse_action == -1 ) puzzle_draw_sign(p_pic_out, p_sys->i_mouse_x - ARROW_WIDTH, p_sys->i_mouse_y, ARROW_WIDTH, ARROW_LINES, ppsz_rot_arrow_sign, true); else if ( p_sys->i_mouse_action == 4 ) puzzle_draw_sign(p_pic_out, p_sys->i_mouse_x - ARROW_WIDTH, p_sys->i_mouse_y, ARROW_WIDTH, ARROW_LINES, ppsz_mir_arrow_sign, false); vlc_mutex_unlock( &p_sys->pce_lock ); } } return CopyInfoAndRelease( p_pic_out, p_pic_in ); }