Commit beb572a7 authored by Alan Third's avatar Alan Third

Revert "Simplify image transforms"

This reverts commit cf3081d2.

; Pushed in error.
parent cf3081d2
Pipeline #2110 failed with stage
in 56 minutes and 47 seconds
......@@ -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 don’t 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;
rotation = XFLOAT_DATA (reduced_angle);
if (rotation != XFLOAT_DATA (reduced_angle))
goto not_a_multiple_of_90;
}
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)
return;
if (rotation != 0)
{
if (rotation == 90)
{
width = img->height;
height = img->width;
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 = 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 = -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;
}
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;
}
/* 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);
}
}
/* Calculate final image size. */
{
int width, height;
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;
compute_image_size (img->width, img->height, img->spec, &width, &height);
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;
if (img->width != width || img->height != height)
{
double xscale = img->width / (double) width;
double yscale = img->height / (double) height;
/* 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;
}
matrix3x3 tmp, rm = { [0][0] = xscale, [1][1] = yscale, [2][2] = 1 };
matrix3x3_mult (rm, tm, tmp);
matrix3x3_copy (tmp, tm);
if (width + left > img->width)
width = img->width - left;
img->width = width;
img->height = height;
}
}
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);
......
......@@ -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))))
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment