-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Image Editor Batch API #23369
Image Editor Batch API #23369
Conversation
ffc7e2e
to
8f26876
Compare
c444b67
to
d0ee6e7
Compare
Why is that needed? |
It's a more flexible API. Edits can happen in whatever order is required. The order of edits with react-easy-crop are rotate, flip, [scale,] crop, but if that was to be switched out the order may be different. Crop will almost certainly happen last, but I've seen rotating and flipping happen in different orders on other online editors. |
As far as I see this is (mostly) a replacement for the current |
I don't see this as replacing |
I'm nervous about introducing such a large abstraction if we're targeting this for 5.5. |
Thinking more about it, seems good to hash how this works in PHP (although that may be repeating stuff posted elsewhere). The image editor "back-end" needs to receive a set of instructions on how the image file has to be edited. This is not necessarily all of the "editing actions" the user performed. The actions can be combined to reduce the number of "edit operations" that have to be performed on the image file. For example: However it is not possible to combine rotate/flip when there is a crop in between. For example: As the order of these instructions is important, don't think it is a good idea to separate them. As far as I see a good format for submitting the instructions to the PHP back-end would either be an array, or even better a CSV? Then the instructions would look like: Once the image file is edited on the server, a new attachment is created and the data for it will be returned through the API (with all the standard info like any other image attachment), and the image will be added to the Image Library (like after uploading a new image) and replaced in the image block in the editor. |
The endpoint currently passes an array of instructions like this. [
{
"modifier": "crop",
"left": 0,
"top": 10,
"width": 100,
"height": 50
},
{
"modifier": "rotate",
"angle": 90
}
]
I'd prefer this stay as an array, we lose the ability to validate with a schema if we add a custom serialization mechanism. What would be better about using this format |
Yeah, that works too but has somewhat... redundant data structure?
Why would it need a Anyway, the current format works too, just seems too "wordy" perhaps? :) |
File naming is managed by As an edited image is saved as a new attachment, these would be the functions to use (this would also maintain full backwards compatibility)? Then the existing image in the image block in the editor will be replaced with the new attachment. The new attachment would (should) also be added to the Media Library modal. At this point it is a standard attachment, same as newly uploaded image. Then if the user decides to edit it again, they will be editing the new attachment, etc. |
It is possible with a little math 🙂 I didn't get around to finishing that part in #22959. Your specific example
could be simplified to My point about order of operations was more to match up with whatever was more convenient for the front-end if, for example, someone else wanted to use this API it with a different cropping library that assumed a different order of operations. One example in the wild that actually uses both orders in different contexts is BeFunky. Try one of their demo images with a horizon in it so you have a line that you can pay attention to. Then use the 'straighten' option followed by a 'horizontal flip' to see that the angle of the horizon hasn't changed even though you flipped the image. This means that the flip happens before the rotation. If you use the 'rotate' option instead of 'straighten, the rotation happens before the flip. Those changes could be simplified down too, but it would be more convenient to give the option to change the order and not have to manually convert to the way that the API handles it. |
You're right, it does seem somewhat redundant in this case, but I think it makes the request and the code a little more readable. And, when building a new REST API endpoint, I think backwards compatibility (even though we're not considering other modifications at this time) should be a consideration because changing APIs are not fun for the developers who rely on them. Filters, which we've discussed in earlier image editor PRs could be an example of a modification which might get added with keys which are much less obvious and could benefit from the readability of a {
"dark": "#111",
"light": "#EEE",
"angle": 22
} Maybe this modification is a gradient overlay that could replace the CSS version from the cover block. It includes the Then maybe you add another filter that rotates the colors like you can do in Gimp or Photoshop. That modification also takes an {
"angle": 22
} This is where pattern matching would break down. You could change the key to I think having the |
I didn't realize that. Your earlier comments about using So you think it would be better to update those methods along with |
I wouldn't say that it has to make it into 5.5 since the other API is still marked as experimental. These are my thoughts on what a stable API could look like. |
c4a6e52
to
d667c51
Compare
@ajlende what does the generated args look like at this point? |
The top-level schema only checks for an array of objects containing any of the properties from any of the modifiers. And the array(
'modifiers' => array(
'type' => 'array',
'required' => true,
'items' => array(
'type' => 'object',
'properties' => array(
'type' => array(
'type' => 'string',
'enum' => array( 'crop', 'rotate' ),
'required' => true,
),
'left' => array(
'type' => 'number',
'minimum' => 0,
'maximum' => 100,
),
'top' => array(
'type' => 'number',
'minimum' => 0,
'maximum' => 100,
),
'width' => array(
'type' => 'number',
'minimum' => 1,
'maximum' => 100,
),
'height' => array(
'type' => 'number',
'minimum' => 1,
'maximum' => 100,
),
'angle' => array(
'type' => 'integer',
),
),
),
),
) That's why I do a check later after I know if the |
Thanks! Do you want to take a crack at supporting |
I thought about it when making this change, but it seemed kind of intimidating which is why I took this approach. It would be helpful, though, so I can give it a shot if we use what I have as in interim solution. |
Are we trying to get this version of the endpoint for 5.5? |
Originally I was thinking no, but with #23536 moving the endpoint out of experimental, I'd prefer to not have to break backwards compatibility with an API that's going to be in core. Not sure how far along moving the PHP over is going at this point, but we can discuss more at the #core-editor meeting if you're available for that. |
I'm not opposed to allowing a different editing order, but I don't see value in allowing multiple edits of the same kind? That would create extra work on the server, which is maybe not obvious. |
The one case that might make sense for multiple edits of the same kind is rotation and straighten as I mentioned in the description about BeFunky. They could be flattened down into one operation, but it would be more natural to keep them as separate. As @azaozz pointed out, the math to flatten down these operations can be quite complicated, especially around multiple crop operations. Let the API be more generic, and if needed, those optimizations can be added on the API side without breaking backwards compatibility. Yes, the request body may grow large if you wanted to just send every operation in order without flattening first, but it'll make the API easier to consume. |
My plan is to transform the old request format to the new format when we introduce these changes. But if we can stabilize the changes in time, that'd of course be preferable. |
11f389a
to
ab22cb1
Compare
Thoughts on adjusting the API a bit from here? Having the {
"modifiers": [
{
"type": "crop",
"arguments": {
"left": 12.875,
"top": 0.125,
"width": 50,
"height": 50
}
},
{
"type": "rotate",
"arguments": {
"angle": 90
}
}
]
} |
|
4185d8c
to
30fd274
Compare
30fd274
to
c901297
Compare
@TimothyBJacobs I saw that |
Great! We should make sure to add a
At the beginning of the request it should check for the old request parameters, and then transform them to the new syntax as an array.
We don't have a mechanism to deprecate parameters in the REST API at the moment. We could potentially add a |
Moved to WordPress/wordpress-develop#845 |
Description
This proposes a new API for image editing that should be more flexible and extensible than the existing API. The new API takes an array of modifiers that will be applied in the order they appear.
An example request body looks like this.
Crop values are as a percentage of the original image and rotation angle is in degrees clockwise.
The crop values need to be percentages because the image loaded on the frontend may not be a scaled version of the original image, so
naturalWidth
andnaturalHeight
cannot be trusted as the actual width and height of the original image.The rotation value is in degrees to avoid rounding errors for the types of rotations that are most common like 90 degrees. Clockwise versus anticlockwise may be up for debate. When using degrees it seems more natural to me to rotate clockwise, but mathematically, when you've converted to radians, a positive value indicates an anticlockwise rotation.
Different front-end implementations may apply edits in a different order, so passing an array will allow them to use their edits as-is without converting to a fixed order.
One example in the wild that actually uses both orders in different contexts is BeFunky. To see the difference, try one of their demo images with a horizon in it so you have a line that you can pay attention to. Then use the 'straighten' option followed by a 'horizontal flip' to see that the angle of the horizon hasn't changed even though you flipped the image. This means that the flip happens before the rotation. If you use the 'rotate' option instead of 'straighten, the rotation happens before the flip.
Original description
Applies just the REST API changes from #22959 and continues where the old PR left off for REST API changes. This PR reverts the API changes from 5886225 (#23284), so the diff may read better on ajlende#484 which includes only the API changes compared to the commit prior to 5886225.
richimage
route toimage-editor
/apply
to/
since there is only one image editor endpoint nowHow has this been tested?
Screenshots
Types of changes
Checklist: