/* -------------------------------------------- * p_attempt_locate_at_current_offset * -------------------------------------------- */ static void p_attempt_locate_at_current_offset(Context *context, gint32 px, gint32 py) { GimpPixelRgn refPR; GimpPixelRgn targetPR; gpointer pr; gint rx1, ry1, rWidth, rHeight; gint tx1, ty1, tWidth, tHeight; gint commonAreaWidth, commonAreaHeight; gint fullShapeWidth, fullShapeHeight; gint rWidthRequired, rHeightRequired; gboolean isIntersect; gint leftShapeRadius; gint upperShapeRadius; if (context->isFinishedFlag) { return; } fullShapeWidth = (2 * context->refShapeRadius); fullShapeHeight = (2 * context->refShapeRadius); /* calculate processing relevant intersecting reference / target rectangles */ isIntersect = gimp_rectangle_intersect((context->refX - context->refShapeRadius) /* origin1 */ , (context->refY - context->refShapeRadius) , fullShapeWidth /* width1 */ , fullShapeHeight /* height1 */ ,0 ,0 ,context->refDrawable->width ,context->refDrawable->height ,&rx1 ,&ry1 ,&rWidth ,&rHeight ); if (!isIntersect) { return; } leftShapeRadius = context->refX - rx1; upperShapeRadius = context->refY - ry1; isIntersect = gimp_rectangle_intersect((px - leftShapeRadius) /* origin1 */ , (py - upperShapeRadius) , rWidth /* width1 */ , rHeight /* height1 */ ,0 ,0 ,context->targetDrawable->width ,context->targetDrawable->height ,&tx1 ,&ty1 ,&tWidth ,&tHeight ); if (!isIntersect) { return; } commonAreaWidth = tWidth; commonAreaHeight = tHeight; // TODO test if 2/3 of the fullShapeWidth and fullShapeHeight is sufficient for usable results. // alternative1: maybe require rWidth and rHeight // alternative2: maybe require fullShapeWidth and fullShapeHeight rWidthRequired = (fullShapeWidth * 2) / 3; rHeightRequired = (fullShapeHeight * 2) / 3; if ((commonAreaWidth < rWidthRequired) || (commonAreaHeight < rHeightRequired)) { /* the common area is significant smaller than the reference shape * skip the compare attempt in this case to avoid unpredictable results (near borders) */ return; } // if(gap_debug) // { // printf("p_attempt_locate_at: px: %04d py:%04d\n" // " rx1:%04d ry1:%04d rWidth:%d rHeight:%d\n" // " tx1:%04d ty1:%04d tWidth:%d tHeight:%d\n" // " commonAreaWidth:%d commonAreaHeight:%d\n" // ,(int)px // ,(int)py // ,(int)rx1 // ,(int)ry1 // ,(int)rWidth // ,(int)rHeight // ,(int)tx1 // ,(int)ty1 // ,(int)tWidth // ,(int)tHeight // ,(int)commonAreaWidth // ,(int)commonAreaHeight // ); // } /* rest 'per offset' values in the context */ context->cancelAttemptFlag = FALSE; context->sumDiffValue = 0; context->involvedPixelCount = 0; context->currentDistance = p_calculate_distance_to_ref_coord(context, px, py); context->px = px; context->py = py; gimp_pixel_rgn_init (&refPR, context->refDrawable, rx1, ry1 , commonAreaWidth, commonAreaHeight , FALSE /* dirty */ , FALSE /* shadow */ ); gimp_pixel_rgn_init (&targetPR, context->targetDrawable, tx1, ty1 , commonAreaWidth, commonAreaHeight , FALSE /* dirty */ , FALSE /* shadow */ ); /* compare pixel areas in tiled portions via pixel region processing loops. */ for (pr = gimp_pixel_rgns_register (2, &refPR, &targetPR); pr != NULL; pr = gimp_pixel_rgns_process (pr)) { if (context->cancelAttemptFlag) { break; } else { p_compare_regions(&refPR, &targetPR, context); } } if (pr != NULL) { /* NOTE: * early escaping from the loop with pr != NULL * leads to memory leaks due to unbalanced tile ref/unref calls. * the call to gap_gimp_pixel_rgns_unref cals unref on the current tile * (in the same way as gimp_pixel_rgns_process does) * but does not ref another available tile. */ gap_gimp_pixel_rgns_unref (pr); } if ((context->involvedPixelCount >= context->requiredPixelCount) && (context->sumDiffValue <= context->bestMatchingSumDiffValue)) { if((context->sumDiffValue < context->bestMatchingSumDiffValue) || ( context->currentDistance < context->bestMatchingDistance)) { context->bestMatchingSumDiffValue = context->sumDiffValue; context->bestMatchingDistance = context->currentDistance; context->bestMatchingPixelCount = context->involvedPixelCount; context->bestX = px; context->bestY = py; if(gap_debug) { gdouble bestMatchingAvgColordiff; bestMatchingAvgColordiff = p_calculate_average_colordiff(context->bestMatchingSumDiffValue , context->bestMatchingPixelCount ); printf("FOUND: bestX:%d bestY:%d squareDist:%d\n" " sumDiffValues:%d pixelCount:%d bestMatchingAvgColordiff:%.5f\n" , (int)context->bestX , (int)context->bestY , (int)context->bestMatchingDistance , (int)context->bestMatchingSumDiffValue , (int)context->bestMatchingPixelCount , (float)bestMatchingAvgColordiff ); } if ((context->currentDistance <= context->veryNearDistance) && (context->sumDiffValue == 0)) { /* stop all further attempts on exact matching area when near reference origin */ context->isFinishedFlag = TRUE; } } } } /* end p_attempt_locate_at_current_offset */
/* -------------------------------------------- * gap_locateAreaWithinRadiusWithOffset * -------------------------------------------- * processing starts at reference coords + offest * and continues outwards upto targetMoveRadius for 4 quadrants. * * returns average color difference (0.0 upto 1.0) * where 0.0 indicates exact matching area * and 1.0 indicates all pixel have maximum color diff (when comaring full white agains full black area) */ gdouble gap_locateAreaWithinRadiusWithOffset(gint32 refDrawableId , gint32 refX , gint32 refY , gint32 refShapeRadius , gint32 targetDrawableId , gint32 targetMoveRadius , gint32 *targetX , gint32 *targetY , gint32 offsetX , gint32 offsetY ) { Context contextData; Context *context; gdouble averageColorDiff; gboolean isFinishedFlag; gdouble maxPixelCount; gint32 shapeDiameter; gint32 fullAreaPixelCount; gint32 dx; gint32 dy; *targetX = refX; *targetY = refY; shapeDiameter = 1 + (refShapeRadius + refShapeRadius); fullAreaPixelCount = (shapeDiameter) * (shapeDiameter); /* init Context */ context = &contextData; context->refShapeRadius = refShapeRadius; context->refX = refX; context->refY = refY; context->bestX = refX; context->bestY = refY; context->cancelAttemptCount = 0; context->rowsProcessedCount = 0; context->cancelAttemptFlag = FALSE; context->isFinishedFlag = FALSE; context->requiredPixelCount = (fullAreaPixelCount * 30) / 100; context->almostFullAreaPixelCount = (fullAreaPixelCount * 90) / 100; context->involvedPixelCount = 0; context->sumDiffValue = 0; context->currentDistance = 0; context->bestMatchingPixelCount = 0; context->veryNearDistance = (2 * 2); context->refDrawable = gimp_drawable_get(refDrawableId); context->targetDrawable = gimp_drawable_get(targetDrawableId); maxPixelCount = MAX(context->refDrawable->width, context->targetDrawable->width) * MAX(context->refDrawable->height, context->targetDrawable->height); context->bestMatchingSumDiffValue = maxPixelCount * MAX_DIFF_VALUE_PER_PIXEL; context->bestMatchingDistance = maxPixelCount; averageColorDiff = 1.0; if(gap_debug) { printf("gap_locateAreaWithinRadiusWithOffset START: refDrawableId:%d targetDrawableId:%d\n" " refX:%d refY:%d refShapeRadius:%d\n" " requiredPixelCount:%d almostFullAreaPixelCount:%d fullAreaPixelCount:%d\n" , (int)refDrawableId , (int)targetDrawableId , (int)context->refX , (int)context->refY , (int)refShapeRadius , (int)context->requiredPixelCount , (int)context->almostFullAreaPixelCount , (int)fullAreaPixelCount ); } for(dx = 0; dx <= targetMoveRadius; dx ++) { if (context->isFinishedFlag) { break; } for(dy = 0; dy <= targetMoveRadius; dy++) { p_attempt_locate_at_current_offset(context, (offsetX + refX) + dx, (offsetY + refY) + dy); if (isFinishedFlag) { break; } if (dx > 0) { p_attempt_locate_at_current_offset(context, (offsetX + refX) - dx, (offsetY + refY) + dy); if (context->isFinishedFlag) { break; } } if (dy > 0) { p_attempt_locate_at_current_offset(context, (offsetX + refX) + dx, (offsetY + refY) - dy); if (context->isFinishedFlag) { break; } } if ((dx > 0) && (dy > 0)) { p_attempt_locate_at_current_offset(context, (offsetX + refX) - dx, (offsetY + refY) - dy); if (context->isFinishedFlag) { break; } } } } if (context->bestMatchingPixelCount > 0) { *targetX = context->bestX; *targetY = context->bestY; averageColorDiff = p_calculate_average_colordiff(context->bestMatchingSumDiffValue , context->bestMatchingPixelCount ); if(gap_debug) { printf("gap_locateAreaWithinRadiusWithOffset Result: bestX:%d bestY:%d averageColorDiff:%.5f\n" " sumDiffValues:%d pixelCount:%d\n" " refX:%d refY:%d cancelAttemptCount:%d rowsProcessedCount:%d\n" " requiredPixelCount:%d almostFullAreaPixelCount:%d\n" , (int)context->bestX , (int)context->bestY , (float)averageColorDiff , (int)context->bestMatchingSumDiffValue , (int)context->bestMatchingPixelCount , (int)context->refX , (int)context->refY , (int)context->cancelAttemptCount , (int)context->rowsProcessedCount , (int)context->requiredPixelCount , (int)context->almostFullAreaPixelCount ); } } else { if(gap_debug) { printf("gap_locateAreaWithinRadiusWithOffset * NOTHING FOUND *\n"); } } if(context->refDrawable != NULL) { gimp_drawable_detach(context->refDrawable); } if(context->targetDrawable != NULL) { gimp_drawable_detach(context->targetDrawable); } return (averageColorDiff); } /* end gap_locateAreaWithinRadiusWithOffset */
/* --------------------------------- * p_compare_regions * --------------------------------- * calculate summary Colorvalues difference for all opaque pixels * in the compared area region. */ static void p_compare_regions (const GimpPixelRgn *refPR ,const GimpPixelRgn *targetPR ,Context *context) { guint row; guchar* ref = refPR->data; /* the reference drawable */ guchar* target = targetPR->data; /* the target drawable */ // if(gap_debug) // { // printf("region REF x:%d y:%d w:%d h:%d TARGET x:%d y:%d w:%d h:%d px:%d py:%d\n" // , (int)refPR->x // , (int)refPR->y // , (int)refPR->w // , (int)refPR->h // , (int)targetPR->x // , (int)targetPR->y // , (int)targetPR->w // , (int)targetPR->h // , (int)context->px // , (int)context->py // ); // } for (row = 0; row < targetPR->h; row++) { guint col; guint idxref; guint idxtarget; if (row >= refPR->h) { continue; } idxref = 0; idxtarget = 0; for(col = 0; col < targetPR->w; col++) { gboolean isCompareable; isCompareable = TRUE; if (col < refPR->w) { if(refPR->bpp > 3) { if(ref[idxref +3] < OPACITY_LEVEL_UCHAR) { /* transparent reference pixel is not compared */ isCompareable = FALSE; } } } else { isCompareable = FALSE; } if(targetPR->bpp > 3) { if(target[idxtarget +3] < OPACITY_LEVEL_UCHAR) { /* transparent target pixel is not compared */ isCompareable = FALSE; } } if (isCompareable == TRUE) { context->involvedPixelCount += 1; context->sumDiffValue += abs(ref[idxref] - target[idxtarget]); context->sumDiffValue += abs(ref[idxref +1] - target[idxtarget +1]); context->sumDiffValue += abs(ref[idxref +2] - target[idxtarget +2]); if (context->sumDiffValue > context->bestMatchingSumDiffValue) { gdouble avgColodiff; avgColodiff = p_calculate_average_colordiff(context->sumDiffValue , context->involvedPixelCount ); if(avgColodiff > context->bestMatchingAvgColordiff) { /* stop evaluating area at current offset on worse results */ context->cancelAttemptFlag = TRUE; context->cancelAttemptCount += 1; return; } } } idxref += refPR->bpp; idxtarget += targetPR->bpp; } ref += refPR->rowstride; target += targetPR->rowstride; } } /* end p_compare_regions */