|
46 | 46 | */ |
47 | 47 | #define ZOOM_DURATION (0.5) |
48 | 48 |
|
| 49 | +/* Snap if closer than this. |
| 50 | + */ |
| 51 | +const int imageui_snap_threshold = 10; |
| 52 | + |
49 | 53 | /* Drag state machine. |
50 | 54 | */ |
51 | 55 | typedef enum { |
@@ -484,6 +488,145 @@ imageui_get_position(Imageui *imageui, |
484 | 488 | #endif /*DEBUG_VERBOSE*/ |
485 | 489 | } |
486 | 490 |
|
| 491 | +/* Track this during a snap. |
| 492 | + */ |
| 493 | +typedef struct { |
| 494 | + Imageui *imageui; |
| 495 | + |
| 496 | + int x; /* Start point */ |
| 497 | + int y; |
| 498 | + int off_x; /* Current snap offset */ |
| 499 | + int off_y; |
| 500 | + int best_x; /* 'Closeness' of best snap so far */ |
| 501 | + int best_y; |
| 502 | +} ImageuiSnap; |
| 503 | + |
| 504 | +static void * |
| 505 | +imageui_snap_sub(Regionview *regionview, ImageuiSnap *snap, gboolean *snapped) |
| 506 | +{ |
| 507 | + Imageui *imageui = snap->imageui; |
| 508 | + |
| 509 | + /* Only static h/v guides. |
| 510 | + */ |
| 511 | + if (regionview->type != REGIONVIEW_HGUIDE && |
| 512 | + regionview->type != REGIONVIEW_VGUIDE) |
| 513 | + return NULL; |
| 514 | + |
| 515 | + if (regionview->type == REGIONVIEW_HGUIDE) { |
| 516 | + int y = regionview->our_area.top; |
| 517 | + int score = abs(y - snap->y); |
| 518 | + |
| 519 | + if (score < snap->best_y) { |
| 520 | + snap->off_y = y - snap->y; |
| 521 | + snap->best_y = score; |
| 522 | + *snapped = TRUE; |
| 523 | + } |
| 524 | + } |
| 525 | + else { |
| 526 | + int x = regionview->our_area.left; |
| 527 | + int score = abs(x - snap->x); |
| 528 | + |
| 529 | + if (score < snap->best_x) { |
| 530 | + snap->off_x = x - snap->x; |
| 531 | + snap->best_x = score; |
| 532 | + *snapped = TRUE; |
| 533 | + } |
| 534 | + } |
| 535 | + |
| 536 | + return NULL; |
| 537 | +} |
| 538 | + |
| 539 | +static gboolean |
| 540 | +imageui_snap(Imageui *imageui, ImageuiSnap *snap) |
| 541 | +{ |
| 542 | + gboolean snapped; |
| 543 | + |
| 544 | + // scale the snap threshold by the zoom factor |
| 545 | + snap->imageui = imageui; |
| 546 | + snap->off_x = 0; |
| 547 | + snap->off_y = 0; |
| 548 | + snap->best_x = |
| 549 | + VIPS_MAX(1, imageui_snap_threshold / imageui_get_zoom(imageui)); |
| 550 | + snap->best_y = |
| 551 | + VIPS_MAX(1, imageui_snap_threshold / imageui_get_zoom(imageui)); |
| 552 | + |
| 553 | + snapped = FALSE; |
| 554 | + slist_map2(imageui->regionviews, |
| 555 | + (SListMap2Fn) imageui_snap_sub, snap, &snapped); |
| 556 | + |
| 557 | + return snapped; |
| 558 | +} |
| 559 | + |
| 560 | +gboolean |
| 561 | +imageui_snap_point(Imageui *imageui, int x, int y, int *sx, int *sy) |
| 562 | +{ |
| 563 | + ImageuiSnap snap; |
| 564 | + gboolean snapped; |
| 565 | + |
| 566 | + snap.x = x; |
| 567 | + snap.y = y; |
| 568 | + snapped = imageui_snap(imageui, &snap); |
| 569 | + |
| 570 | + *sx = x + snap.off_x; |
| 571 | + *sy = y + snap.off_y; |
| 572 | + |
| 573 | + return snapped; |
| 574 | +} |
| 575 | + |
| 576 | +gboolean |
| 577 | +imageui_snap_rect(Imageui *imageui, VipsRect *in, VipsRect *out) |
| 578 | +{ |
| 579 | + /* Snap the corners plus the edge centres, take the best score. |
| 580 | + */ |
| 581 | + ImageuiSnap snap[8]; |
| 582 | + snap[0].x = in->left; |
| 583 | + snap[0].y = in->top; |
| 584 | + snap[1].x = in->left + in->width; |
| 585 | + snap[1].y = in->top; |
| 586 | + snap[2].x = in->left + in->width; |
| 587 | + snap[2].y = in->top + in->height; |
| 588 | + snap[3].x = in->left; |
| 589 | + snap[3].y = in->top + in->height; |
| 590 | + snap[4].x = in->left + in->width / 2; |
| 591 | + snap[4].y = in->top; |
| 592 | + snap[5].x = in->left + in->width; |
| 593 | + snap[5].y = in->top + in->height / 2; |
| 594 | + snap[6].x = in->left + in->width / 2; |
| 595 | + snap[6].y = in->top + in->height; |
| 596 | + snap[7].x = in->left; |
| 597 | + snap[7].y = in->top + in->height / 2; |
| 598 | + |
| 599 | + gboolean snapped; |
| 600 | + snapped = FALSE; |
| 601 | + for (int i = 0; i < 8; i++) |
| 602 | + snapped |= imageui_snap(imageui, &snap[i]); |
| 603 | + |
| 604 | + int best; |
| 605 | + int best_score; |
| 606 | + best = 0; |
| 607 | + best_score = snap[0].best_x; |
| 608 | + for (int i = 1; i < 7; i++) |
| 609 | + if (snap[i].best_x < best_score) { |
| 610 | + best = i; |
| 611 | + best_score = snap[i].best_x; |
| 612 | + } |
| 613 | + out->left = in->left + snap[best].off_x; |
| 614 | + |
| 615 | + best = 0; |
| 616 | + best_score = snap[0].best_y; |
| 617 | + for (int i = 1; i < 7; i++) |
| 618 | + if (snap[i].best_y < best_score) { |
| 619 | + best = i; |
| 620 | + best_score = snap[i].best_y; |
| 621 | + } |
| 622 | + out->top = in->top + snap[best].off_y; |
| 623 | + |
| 624 | + out->width = in->width; |
| 625 | + out->height = in->height; |
| 626 | + |
| 627 | + return snapped; |
| 628 | +} |
| 629 | + |
487 | 630 | static void |
488 | 631 | imageui_set_position(Imageui *imageui, double x, double y) |
489 | 632 | { |
@@ -1300,8 +1443,8 @@ imageui_init(Imageui *imageui) |
1300 | 1443 | G_CALLBACK(imageui_overlay_snapshot), imageui, 0); |
1301 | 1444 |
|
1302 | 1445 | /* Uncomment to test our animation disable |
1303 | | - g_object_set( gtk_widget_get_settings( GTK_WIDGET( win ) ), |
1304 | | - "gtk-enable-animations", FALSE, NULL ); |
| 1446 | + g_object_set(gtk_widget_get_settings(GTK_WIDGET(win)), |
| 1447 | + "gtk-enable-animations", FALSE, NULL); |
1305 | 1448 | */ |
1306 | 1449 |
|
1307 | 1450 | // read the gtk animation setting preference |
@@ -1446,3 +1589,41 @@ imageui_gtk_to_image(Imageui *imageui, |
1446 | 1589 | imagedisplay_gtk_to_image(IMAGEDISPLAY(imageui->imagedisplay), |
1447 | 1590 | x_gtk, y_gtk, x_image, y_image); |
1448 | 1591 | } |
| 1592 | + |
| 1593 | +void |
| 1594 | +imageui_image_to_gtk_rect(Imageui *imageui, VipsRect *in, VipsRect *out) |
| 1595 | +{ |
| 1596 | + VipsRect rect; |
| 1597 | + double x_gtk; |
| 1598 | + double y_gtk; |
| 1599 | + |
| 1600 | + imageui_image_to_gtk(imageui, in->left, in->top, &x_gtk, &y_gtk); |
| 1601 | + rect.left = x_gtk; |
| 1602 | + rect.top = y_gtk; |
| 1603 | + |
| 1604 | + imageui_image_to_gtk(imageui, |
| 1605 | + VIPS_RECT_RIGHT(in), VIPS_RECT_BOTTOM(in), &x_gtk, &y_gtk); |
| 1606 | + rect.width = ceil(x_gtk) - rect.left; |
| 1607 | + rect.height = ceil(y_gtk) - rect.top; |
| 1608 | + |
| 1609 | + *out = rect; |
| 1610 | +} |
| 1611 | + |
| 1612 | +void |
| 1613 | +imageui_gtk_to_image_rect(Imageui *imageui, VipsRect *in, VipsRect *out) |
| 1614 | +{ |
| 1615 | + VipsRect rect; |
| 1616 | + double x_image; |
| 1617 | + double y_image; |
| 1618 | + |
| 1619 | + imageui_gtk_to_image(imageui, in->left, in->top, &x_image, &y_image); |
| 1620 | + rect.left = x_image; |
| 1621 | + rect.top = y_image; |
| 1622 | + |
| 1623 | + imageui_gtk_to_image(imageui, |
| 1624 | + VIPS_RECT_RIGHT(in), VIPS_RECT_BOTTOM(in), &x_image, &y_image); |
| 1625 | + rect.width = ceil(x_image) - rect.left; |
| 1626 | + rect.height = ceil(y_image) - rect.top; |
| 1627 | + |
| 1628 | + *out = rect; |
| 1629 | +} |
0 commit comments