@monte: There be dragons lurking here
The current behavior of the image object depends on a number of properties:
- rect
- formattedWidth / formattedHeight (let's call this the 'intrinsic size').
- angle
- lockLocation
If angle and lockLocation are not set, then on open the image resets itself to be the intrinsic size. Subsequent changes to the rect cause scaling.
If angle is not set, and lockLocation is set, then on open the image retains its rect's size (as specified in the rect), and any changes to the width/height change the scale.
If angle is set, then on open the image resets itself to be the bounds of the transformed image at intrinsic size (i.e. the bounds of a rotated rectangle of intrinsic size around the origin). You cannot subsequently set its width / height, just its location.
If angle is set, and lockLocation is set, then a rect of intrinsic size is rotated, and then scaled to fit within the rect. You can subsequently change the width/height, and changing the angle will cause it to still have the same rect, but you'll get skew.
So, basically, at the moment you can actually get any affine transform out of the image object if you know how the maths works - the only thing you cannot do is get a sub-pixel translation which is actually quite important for positioning transformed images and ensuring you don't get 'jitter' when you want to smoothly animate the transformation (and can make them sit side by side without gaps appropriately).
I think it would be quite hard to retain backwards-compatibility (which we have to do) *and* find a way to more easily specify skew (especially as you can, actually already skew images - it would be possible to write a script handler which takes angle, scale, skew and applies the appropriate properties to the image to do it).
The internals of the image object are fully setup to handle a transform property - I think it would be a great deal less work to implement direct (and easy!) access to this and put the image object in a 'new mode' making it crystal clear and easy to transform than try and 'fit in' more properties to manipulate the rather muddy transform calculations which exist.
So - here is how I think it should work:
If transform (or one of its synthetic friends e.g. xScale, yScale etc.) are set then the image object enters the 'new mode'. In the new mode, the rect of the image object is determined by the transform of its 'intrinsic rect' (which is a rect of formattedWidth by formattedHeight sitting with its center on the origin) - this is always the case. To be exact - the rect of an image is always the integer bounding box of the transform(rect(-formattedWidth/2,-formattedHeight/2,formattedWidth/2,formattedHeight/2)).
Changing any of the transform parameters changes the transform and then the rect (obviously).
Now, the more difficult part of this is what to do when the rect is set as this does not contain enough information to have a single solution to the transform - each rect can give rise to a whole family of possible transforms which would result in the requested rect. However, if we make a decision then changing the rect is equivalent to a translation and scale relative to the current transform then that gives a reasonable behavior. Essentially, a transform is calculated which takes things from old-rect to new-rect, concatenated with the current transform, and then sets this as the new transform. I'm pretty confident that the maths would work out okay here - i.e. that this would result in the newly calculated rect being what was asked for.
This probably sounds a lot more complicated than it actually is to implement - it is mainly just a bit of geometry (which I think we already have the code to do).
Indeed, a similar mechanism could be applied to the current graphic object to clean that up - and in so doing fix a number of issues which have plagued it for years in terms of rect's being too big, jitter when setting the angle of certain shapes, the rect of a 1 pixel horizontal line etc.