Skip to content

Commit c8b744d

Browse files
committed
Media: Fix admin image cropping calculations.
The admin image editor crop function introduced rounding errors by using a scaled image to calculate values. Fix uses the image at 100% scale for calculations. Also avoid recalculating selection when the selection position is changed, and prevent incorrect values after scaling or restoration. Previously committed in [58456] and reverted in [58571]. The revert was due to a misattributed test failure. Props Jossnaz, johnillo, shailu25, rachelbaker, sudipatel007, joedolson, kevin940726 , andrewserong, hmbashar. Fixes #32282. git-svn-id: https://develop.svn.wordpress.org/trunk@58915 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 913ef22 commit c8b744d

File tree

2 files changed

+83
-31
lines changed

2 files changed

+83
-31
lines changed

src/js/_enqueues/lib/image-edit.js

Lines changed: 81 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -143,17 +143,12 @@
143143
* @return {void}
144144
*/
145145
init : function(postid) {
146-
var t = this, old = $('#image-editor-' + t.postid),
147-
x = t.intval( $('#imgedit-x-' + postid).val() ),
148-
y = t.intval( $('#imgedit-y-' + postid).val() );
146+
var t = this, old = $('#image-editor-' + t.postid);
149147

150148
if ( t.postid !== postid && old.length ) {
151149
t.close(t.postid);
152150
}
153151

154-
t.hold.w = t.hold.ow = x;
155-
t.hold.h = t.hold.oh = y;
156-
t.hold.xy_ratio = x / y;
157152
t.hold.sizer = parseFloat( $('#imgedit-sizer-' + postid).val() );
158153
t.postid = postid;
159154
$('#imgedit-response-' + postid).empty();
@@ -188,6 +183,29 @@
188183
$( document ).on( 'image-editor-ui-ready', this.focusManager );
189184
},
190185

186+
/**
187+
* Calculate the image size and save it to memory.
188+
*
189+
* @since 6.7.0
190+
*
191+
* @memberof imageEdit
192+
*
193+
* @param {number} postid The post ID.
194+
*
195+
* @return {void}
196+
*/
197+
calculateImgSize: function( postid ) {
198+
var t = this,
199+
x = t.intval( $( '#imgedit-x-' + postid ).val() ),
200+
y = t.intval( $( '#imgedit-y-' + postid ).val() );
201+
202+
t.hold.w = t.hold.ow = x;
203+
t.hold.h = t.hold.oh = y;
204+
t.hold.xy_ratio = x / y;
205+
t.hold.sizer = parseFloat( $( '#imgedit-sizer-' + postid ).val() );
206+
t.currentCropSelection = null;
207+
},
208+
191209
/**
192210
* Toggles the wait/load icon in the editor.
193211
*
@@ -525,7 +543,7 @@
525543
for ( n in history ) {
526544
i = history[n];
527545
if ( i.hasOwnProperty('c') ) {
528-
op[n] = { 'c': { 'x': i.c.x, 'y': i.c.y, 'w': i.c.w, 'h': i.c.h } };
546+
op[n] = { 'c': { 'x': i.c.x, 'y': i.c.y, 'w': i.c.w, 'h': i.c.h, 'r': i.c.r } };
529547
} else if ( i.hasOwnProperty('r') ) {
530548
op[n] = { 'r': i.r.r };
531549
} else if ( i.hasOwnProperty('f') ) {
@@ -860,6 +878,7 @@
860878
if ( 'undefined' === typeof this.hold.sizer ) {
861879
this.init( postid );
862880
}
881+
this.calculateImgSize( postid );
863882

864883
this.initCrop(postid, img, parent);
865884
this.setCropSelection( postid, { 'x1': 0, 'y1': 0, 'x2': 0, 'y2': 0, 'width': img.innerWidth(), 'height': img.innerHeight() } );
@@ -909,8 +928,6 @@
909928
var t = this,
910929
selW = $('#imgedit-sel-width-' + postid),
911930
selH = $('#imgedit-sel-height-' + postid),
912-
selX = $('#imgedit-start-x-' + postid),
913-
selY = $('#imgedit-start-y-' + postid),
914931
$image = $( image ),
915932
$img;
916933

@@ -945,13 +962,16 @@
945962
*
946963
* @return {void}
947964
*/
948-
parent.children().on( 'mousedown, touchstart', function(e){
949-
var ratio = false, sel, defRatio;
950-
951-
if ( e.shiftKey ) {
952-
sel = t.iasapi.getSelection();
953-
defRatio = t.getSelRatio(postid);
954-
ratio = ( sel && sel.width && sel.height ) ? sel.width + ':' + sel.height : defRatio;
965+
parent.children().on( 'mousedown touchstart', function(e) {
966+
var ratio = false,
967+
sel = t.iasapi.getSelection(),
968+
cx = t.intval( $( '#imgedit-crop-width-' + postid ).val() ),
969+
cy = t.intval( $( '#imgedit-crop-height-' + postid ).val() );
970+
971+
if ( cx && cy ) {
972+
ratio = t.getSelRatio( postid );
973+
} else if ( e.shiftKey && sel && sel.width && sel.height ) {
974+
ratio = sel.width + ':' + sel.height;
955975
}
956976

957977
t.iasapi.setOptions({
@@ -1000,11 +1020,17 @@
10001020
* @return {void}
10011021
*/
10021022
onSelectChange: function(img, c) {
1003-
var sizer = imageEdit.hold.sizer;
1004-
selW.val( imageEdit.round(c.width / sizer) );
1005-
selH.val( imageEdit.round(c.height / sizer) );
1006-
selX.val( imageEdit.round(c.x1 / sizer) );
1007-
selY.val( imageEdit.round(c.y1 / sizer) );
1023+
var sizer = imageEdit.hold.sizer,
1024+
oldSel = imageEdit.currentCropSelection;
1025+
1026+
if ( oldSel != null && oldSel.width == c.width && oldSel.height == c.height ) {
1027+
return;
1028+
}
1029+
1030+
selW.val( Math.min( imageEdit.hold.w, imageEdit.round( c.width / sizer ) ) );
1031+
selH.val( Math.min( imageEdit.hold.h, imageEdit.round( c.height / sizer ) ) );
1032+
1033+
t.currentCropSelection = c;
10081034
}
10091035
});
10101036
},
@@ -1022,7 +1048,11 @@
10221048
* @return {boolean}
10231049
*/
10241050
setCropSelection : function(postid, c) {
1025-
var sel;
1051+
var sel,
1052+
selW = $( '#imgedit-sel-width-' + postid ),
1053+
selH = $( '#imgedit-sel-height-' + postid ),
1054+
sizer = this.hold.sizer,
1055+
hold = this.hold;
10261056

10271057
c = c || 0;
10281058

@@ -1037,7 +1067,15 @@
10371067
return false;
10381068
}
10391069

1040-
sel = { 'x': c.x1, 'y': c.y1, 'w': c.width, 'h': c.height };
1070+
// adjust the selection within the bounds of the image on 100% scale
1071+
var excessW = hold.w - ( Math.round( c.x1 / sizer ) + parseInt( selW.val() ) );
1072+
var excessH = hold.h - ( Math.round( c.y1 / sizer ) + parseInt( selH.val() ) );
1073+
var x = Math.round( c.x1 / sizer ) + Math.min( 0, excessW );
1074+
var y = Math.round( c.y1 / sizer ) + Math.min( 0, excessH );
1075+
1076+
// use 100% scaling to prevent rounding errors
1077+
sel = { 'r': 1, 'x': x, 'y': y, 'w': selW.val(), 'h': selH.val() };
1078+
10411079
this.setDisabled($('.imgedit-crop', '#imgedit-panel-' + postid), 1);
10421080
$('#imgedit-selection-' + postid).val( JSON.stringify(sel) );
10431081
},
@@ -1165,6 +1203,11 @@
11651203
}
11661204
this.closePopup(t);
11671205
this.addStep({ 'r': { 'r': angle, 'fw': this.hold.h, 'fh': this.hold.w }}, postid, nonce);
1206+
1207+
// Clear the selection fields after rotating.
1208+
$( '#imgedit-sel-width-' + postid ).val( '' );
1209+
$( '#imgedit-sel-height-' + postid ).val( '' );
1210+
this.currentCropSelection = null;
11681211
},
11691212

11701213
/**
@@ -1187,6 +1230,11 @@
11871230
}
11881231
this.closePopup(t);
11891232
this.addStep({ 'f': { 'f': axis, 'fw': this.hold.w, 'fh': this.hold.h }}, postid, nonce);
1233+
1234+
// Clear the selection fields after flipping.
1235+
$( '#imgedit-sel-width-' + postid ).val( '' );
1236+
$( '#imgedit-sel-height-' + postid ).val( '' );
1237+
this.currentCropSelection = null;
11901238
},
11911239

11921240
/**
@@ -1219,10 +1267,11 @@
12191267
}
12201268

12211269
// Clear the selection fields after cropping.
1222-
$('#imgedit-sel-width-' + postid).val('');
1223-
$('#imgedit-sel-height-' + postid).val('');
1224-
$('#imgedit-start-x-' + postid).val('0');
1225-
$('#imgedit-start-y-' + postid).val('0');
1270+
$( '#imgedit-sel-width-' + postid ).val( '' );
1271+
$( '#imgedit-sel-height-' + postid ).val( '' );
1272+
$( '#imgedit-start-x-' + postid ).val( '0' );
1273+
$( '#imgedit-start-y-' + postid ).val( '0' );
1274+
this.currentCropSelection = null;
12261275
},
12271276

12281277
/**
@@ -1312,6 +1361,8 @@
13121361
img = $('#image-preview-' + postid), imgh = img.height(), imgw = img.width(),
13131362
sizer = this.hold.sizer, x1, y1, x2, y2, ias = this.iasapi;
13141363

1364+
this.currentCropSelection = null;
1365+
13151366
if ( false === this.validateNumeric( el ) ) {
13161367
return;
13171368
}
@@ -1335,18 +1386,19 @@
13351386
if ( x2 > imgw ) {
13361387
x1 = 0;
13371388
x2 = imgw;
1338-
elX.val( Math.round( x2 / sizer ) );
1389+
elX.val( Math.min( this.hold.w, Math.round( x2 / sizer ) ) );
13391390
}
13401391

13411392
if ( y2 > imgh ) {
13421393
y1 = 0;
13431394
y2 = imgh;
1344-
elY.val( Math.round( y2 / sizer ) );
1395+
elY.val( Math.min( this.hold.h, Math.round( y2 / sizer ) ) );
13451396
}
13461397

13471398
ias.setSelection( x1, y1, x2, y2 );
13481399
ias.update();
13491400
this.setCropSelection(postid, ias.getSelection());
1401+
this.currentCropSelection = ias.getSelection();
13501402
}
13511403
},
13521404

src/wp-admin/includes/image-edit.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -735,10 +735,10 @@ function image_edit_apply_changes( $image, $changes ) {
735735
$w = $size['width'];
736736
$h = $size['height'];
737737

738-
$scale = 1 / _image_get_preview_ratio( $w, $h ); // Discard preview scaling.
738+
$scale = isset( $sel->r ) ? $sel->r : 1 / _image_get_preview_ratio( $w, $h ); // Discard preview scaling.
739739
$image->crop( (int) ( $sel->x * $scale ), (int) ( $sel->y * $scale ), (int) ( $sel->w * $scale ), (int) ( $sel->h * $scale ) );
740740
} else {
741-
$scale = 1 / _image_get_preview_ratio( imagesx( $image ), imagesy( $image ) ); // Discard preview scaling.
741+
$scale = isset( $sel->r ) ? $sel->r : 1 / _image_get_preview_ratio( imagesx( $image ), imagesy( $image ) ); // Discard preview scaling.
742742
$image = _crop_image_resource( $image, $sel->x * $scale, $sel->y * $scale, $sel->w * $scale, $sel->h * $scale );
743743
}
744744
break;

0 commit comments

Comments
 (0)