Revert "Simplify image transforms"

This reverts commit cf3081d208.

; Pushed in error.
This commit is contained in:
Alan Third 2019-06-16 20:46:31 +01:00
parent cf3081d208
commit beb572a7dd
2 changed files with 249 additions and 107 deletions

View file

@ -1967,7 +1967,8 @@ compute_image_size (size_t width, size_t height,
}
#endif /* HAVE_IMAGEMAGICK || HAVE_NATIVE_TRANSFORMS */
/* image_set_transform uses affine transformation matrices to perform
/* image_set_rotation, image_set_crop, image_set_size and
image_set_transform use affine transformation matrices to perform
various transforms on the image. The matrix is a 2D array of
doubles. It is laid out like this:
@ -2038,6 +2039,10 @@ compute_image_size (size_t width, size_t height,
finally move the origin back to the top left of the image, which
may now be a different corner.
Cropping is easier as we just move the origin to the top left of
where we want to crop and set the width and height accordingly.
The matrices dont know anything about width and height.
It's possible to pre-calculate the matrix multiplications and just
generate one transform matrix that will do everything we need in a
single step, but the maths for each element is much more complex
@ -2065,7 +2070,7 @@ matrix3x3_mult (matrix3x3 a, matrix3x3 b, matrix3x3 result)
}
static void
image_set_transform (struct frame *f, struct image *img)
image_set_rotation (struct image *img, matrix3x3 tm)
{
#ifdef HAVE_NATIVE_TRANSFORMS
# ifdef HAVE_IMAGEMAGICK
@ -2079,112 +2084,217 @@ image_set_transform (struct frame *f, struct image *img)
return;
# endif
/* This is the transformation matrix we will use through all the
calculations. */
matrix3x3 tm = { [0][0] = 1, [1][1] = 1, [2][2] = 1 };
int rotation, cos_r, sin_r, width, height;
/* Calculate image rotation. */
Lisp_Object value = image_spec_value (img->spec, QCrotation, NULL);
if (NUMBERP (value))
if (! NUMBERP (value))
return;
Lisp_Object reduced_angle = Fmod (value, make_fixnum (360));
if (! FLOATP (reduced_angle))
rotation = XFIXNUM (reduced_angle);
else
{
int rotation, cos_r, sin_r, width, height;
Lisp_Object reduced_angle = Fmod (value, make_fixnum (360));
if (! FLOATP (reduced_angle))
rotation = XFIXNUM (reduced_angle);
else
{
rotation = XFLOAT_DATA (reduced_angle);
if (rotation != XFLOAT_DATA (reduced_angle))
goto not_a_multiple_of_90;
}
if (rotation != 0)
{
if (rotation == 90)
{
width = img->height;
height = img->width;
cos_r = 0;
sin_r = 1;
}
else if (rotation == 180)
{
width = img->width;
height = img->height;
cos_r = -1;
sin_r = 0;
}
else if (rotation == 270)
{
width = img->height;
height = img->width;
cos_r = 0;
sin_r = -1;
}
else
{
not_a_multiple_of_90:
image_error ("Native image rotation supports "
"only multiples of 90 degrees");
return;
}
/* Translate so (0, 0) is in the center of the image. */
matrix3x3 t
= { [0][0] = 1, [2][0] = img->width >> 1,
[1][1] = 1, [2][1] = img->height >> 1,
[2][2] = 1 };
matrix3x3 tmp;
matrix3x3_mult (t, tm, tmp);
/* Rotate. */
matrix3x3 rot = { [0][0] = cos_r, [1][0] = sin_r,
[0][1] = -sin_r, [1][1] = cos_r,
[2][2] = 1 };
matrix3x3 tmp2;
matrix3x3_mult (rot, tmp, tmp2);
/* Translate back. */
t[2][0] = - (width >> 1);
t[2][1] = - (height >> 1);
matrix3x3_mult (t, tmp2, tm);
img->width = width;
img->height = height;
}
rotation = XFLOAT_DATA (reduced_angle);
if (rotation != XFLOAT_DATA (reduced_angle))
goto not_a_multiple_of_90;
}
/* Calculate final image size. */
{
int width, height;
if (rotation == 0)
return;
compute_image_size (img->width, img->height, img->spec, &width, &height);
if (rotation == 90)
{
width = img->height;
height = img->width;
if (img->width != width || img->height != height)
{
double xscale = img->width / (double) width;
double yscale = img->height / (double) height;
cos_r = 0;
sin_r = 1;
}
else if (rotation == 180)
{
width = img->width;
height = img->height;
matrix3x3 tmp, rm = { [0][0] = xscale, [1][1] = yscale, [2][2] = 1 };
matrix3x3_mult (rm, tm, tmp);
matrix3x3_copy (tmp, tm);
cos_r = -1;
sin_r = 0;
}
else if (rotation == 270)
{
width = img->height;
height = img->width;
img->width = width;
img->height = height;
}
}
cos_r = 0;
sin_r = -1;
}
else
{
not_a_multiple_of_90:
image_error ("Native image rotation supports "
"only multiples of 90 degrees");
return;
}
/* Translate so (0, 0) is in the center of the image. */
matrix3x3 t
= { [0][0] = 1,
[1][1] = 1,
[2][0] = img->width >> 1, [2][1] = img->height >> 1, [2][2] = 1 };
matrix3x3 tmp;
matrix3x3_mult (t, tm, tmp);
/* Rotate. */
matrix3x3 rot = { [0][0] = cos_r, [0][1] = -sin_r,
[1][0] = sin_r, [1][1] = cos_r,
[2][2] = 1 };
matrix3x3 tmp2;
matrix3x3_mult (rot, tmp, tmp2);
/* Translate back. */
t[2][0] = - (width >> 1);
t[2][1] = - (height >> 1);
matrix3x3_mult (t, tmp2, tm);
img->width = width;
img->height = height;
#endif
}
static void
image_set_crop (struct image *img, matrix3x3 tm)
{
#ifdef HAVE_NATIVE_TRANSFORMS
# ifdef HAVE_IMAGEMAGICK
/* ImageMagick images are already cropped. */
if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick))
return;
# endif
# if !defined USE_CAIRO && defined HAVE_XRENDER
if (!img->picture)
return;
# endif
Lisp_Object crop = image_spec_value (img->spec, QCcrop, NULL);
if (!CONSP (crop))
return;
Lisp_Object w = XCAR (crop), h = Qnil, x = Qnil, y = Qnil;
crop = XCDR (crop);
if (CONSP (crop))
{
h = XCAR (crop);
crop = XCDR (crop);
if (CONSP (crop))
{
x = XCAR (crop);
crop = XCDR (crop);
if (CONSP (crop))
y = XCAR (crop);
}
}
int width = img->width;
if (FIXNATP (w) && XFIXNAT (w) < img->width)
width = XFIXNAT (w);
int left;
if (TYPE_RANGED_FIXNUMP (int, x))
{
left = XFIXNUM (x);
if (left < 0)
left = img->width - width + left;
}
else
left = (img->width - width) >> 1;
int height = img->height;
if (FIXNATP (h) && XFIXNAT (h) < img->height)
height = XFIXNAT (h);
int top;
if (TYPE_RANGED_FIXNUMP (int, y))
{
top = XFIXNUM (y);
if (top < 0)
top = img->height - height + top;
}
else
top = (img->height - height) >> 1;
/* Negative values operate from the right and bottom of the image
instead of the left and top. */
if (left < 0)
{
width = img->width + left;
left = 0;
}
if (width + left > img->width)
width = img->width - left;
if (top < 0)
{
height = img->height + top;
top = 0;
}
if (height + top > img->height)
height = img->height - top;
matrix3x3 tmp, m = { [0][0] = 1,
[1][1] = 1,
[2][0] = left, [2][1] = top, [2][2] = 1 };
matrix3x3_mult (m, tm, tmp);
matrix3x3_copy (tmp, tm);
img->width = width;
img->height = height;
#endif
}
static void
image_set_size (struct image *img, matrix3x3 tm)
{
#ifdef HAVE_NATIVE_TRANSFORMS
# ifdef HAVE_IMAGEMAGICK
/* ImageMagick images are already the correct size. */
if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick))
return;
# endif
# if !defined USE_CAIRO && defined HAVE_XRENDER
if (!img->picture)
return;
# endif
int width, height;
compute_image_size (img->width, img->height, img->spec, &width, &height);
double xscale = img->width / (double) width;
double yscale = img->height / (double) height;
matrix3x3 tmp, rm = { [0][0] = xscale, [1][1] = yscale, [2][2] = 1 };
matrix3x3_mult (rm, tm, tmp);
matrix3x3_copy (tmp, tm);
img->width = width;
img->height = height;
#endif
}
static void
image_set_transform (struct frame *f, struct image *img, matrix3x3 matrix)
{
/* TODO: Add MS Windows support. */
#ifdef HAVE_NATIVE_TRANSFORMS
# if defined (HAVE_NS)
/* Under NS the transform is applied to the drawing surface at
drawing time, so store it for later. */
ns_image_set_transform (img->pixmap, tm);
ns_image_set_transform (img->pixmap, matrix);
# elif defined USE_CAIRO
cairo_matrix_t cr_matrix = {tm[0][0], tm[0][1], tm[1][0],
tm[1][1], tm[2][0], tm[2][1]};
cairo_matrix_t cr_matrix = {matrix[0][0], matrix[0][1], matrix[1][0],
matrix[1][1], matrix[2][0], matrix[2][1]};
cairo_pattern_t *pattern = cairo_pattern_create_rgb (0, 0, 0);
cairo_pattern_set_matrix (pattern, &cr_matrix);
/* Dummy solid color pattern just to record pattern matrix. */
@ -2193,15 +2303,15 @@ image_set_transform (struct frame *f, struct image *img)
if (img->picture)
{
XTransform tmat
= {{{XDoubleToFixed (tm[0][0]),
XDoubleToFixed (tm[1][0]),
XDoubleToFixed (tm[2][0])},
{XDoubleToFixed (tm[0][1]),
XDoubleToFixed (tm[1][1]),
XDoubleToFixed (tm[2][1])},
{XDoubleToFixed (tm[0][2]),
XDoubleToFixed (tm[1][2]),
XDoubleToFixed (tm[2][2])}}};
= {{{XDoubleToFixed (matrix[0][0]),
XDoubleToFixed (matrix[1][0]),
XDoubleToFixed (matrix[2][0])},
{XDoubleToFixed (matrix[0][1]),
XDoubleToFixed (matrix[1][1]),
XDoubleToFixed (matrix[2][1])},
{XDoubleToFixed (matrix[0][2]),
XDoubleToFixed (matrix[1][2]),
XDoubleToFixed (matrix[2][2])}}};
XRenderSetPictureFilter (FRAME_X_DISPLAY (f), img->picture, FilterBest,
0, 0);
@ -2267,7 +2377,11 @@ lookup_image (struct frame *f, Lisp_Object spec)
int relief_bound;
#ifdef HAVE_NATIVE_TRANSFORMS
image_set_transform (f, img);
matrix3x3 transform_matrix = { [0][0] = 1, [1][1] = 1, [2][2] = 1 };
image_set_size (img, transform_matrix);
image_set_crop (img, transform_matrix);
image_set_rotation (img, transform_matrix);
image_set_transform (f, img, transform_matrix);
#endif
ascent = image_spec_value (spec, QCascent, NULL);

View file

@ -25,9 +25,6 @@
;; Type M-x test-transforms RET to generate the test buffer.
;; There is a difference in how librsvg and ImageMagick draw some of
;; the images. This results in what looks like a one pixel difference.
;;; Code:
(defun test-rotation ()
@ -47,6 +44,36 @@
(insert-test "45" up up '(:rotation 45)))
(insert "\n\n"))
(defun test-cropping ()
(let ((image "<svg height='30' width='30'>
<rect x='0' y='0' width='10' height='10'/>
<rect x='10' y='10' width='10' height='10'
style='fill:none;stroke-width:1;stroke:#000'/>
<line x1='10' y1='10' x2='20' y2='20' style='stroke:#000'/>
<line x1='20' y1='10' x2='10' y2='20' style='stroke:#000'/>
<rect x='20' y='20' width='10' height='10'
style='fill:none;stroke-width:1;stroke:#000'/>
</svg>")
(top-left "<svg height='10' width='10'>
<rect x='0' y='0' width='10' height='10'/>
</svg>")
(middle "<svg height='10' width='10'>
<rect x='0' y='0' width='10' height='10'
style='fill:none;stroke-width:1;stroke:#000'/>
<line x1='0' y1='0' x2='10' y2='10' style='stroke:#000'/>
<line x1='10' y1='0' x2='0' y2='10' style='stroke:#000'/>
</svg>")
(bottom-right "<svg height='10' width='10'>
<rect x='0' y='0' width='10' height='10'
style='fill:none;stroke-width:1;stroke:#000'/>
</svg>"))
(insert-header "Test Crop: cropping an image")
(insert-test "all params" top-left image '(:crop (10 10 0 0)))
(insert-test "width/height only" middle image '(:crop (10 10)))
(insert-test "negative x y" middle image '(:crop (10 10 -10 -10)))
(insert-test "all params" bottom-right image '(:crop (10 10 20 20))))
(insert "\n\n"))
(defun test-scaling ()
(let ((image "<svg height='10' width='10'>
<rect x='0' y='0' width='10' height='10'
@ -143,6 +170,7 @@
(unless #'imagemagick-types
(insert "ImageMagick not detected. ImageMagick tests will be skipped.\n\n"))
(test-rotation)
(test-cropping)
(test-scaling)
(test-scaling-rotation)
(goto-char (point-min))))