aboutsummaryrefslogtreecommitdiffstats
path: root/src/image.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/image.c')
-rw-r--r--src/image.c371
1 files changed, 162 insertions, 209 deletions
diff --git a/src/image.c b/src/image.c
index f3d6508f460..02dd95caeda 100644
--- a/src/image.c
+++ b/src/image.c
@@ -57,6 +57,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
57#include <sys/types.h> 57#include <sys/types.h>
58#endif /* HAVE_SYS_TYPES_H */ 58#endif /* HAVE_SYS_TYPES_H */
59 59
60#ifdef HAVE_NATIVE_TRANSFORMS
61#include <float.h> /* for FLT_MIN */
62#endif
63
60#ifdef HAVE_WINDOW_SYSTEM 64#ifdef HAVE_WINDOW_SYSTEM
61#include TERM_HEADER 65#include TERM_HEADER
62#endif /* HAVE_WINDOW_SYSTEM */ 66#endif /* HAVE_WINDOW_SYSTEM */
@@ -1965,12 +1969,10 @@ compute_image_size (size_t width, size_t height,
1965 *d_width = desired_width; 1969 *d_width = desired_width;
1966 *d_height = desired_height; 1970 *d_height = desired_height;
1967} 1971}
1968#endif /* HAVE_IMAGEMAGICK || HAVE_NATIVE_TRANSFORMS */
1969 1972
1970/* image_set_rotation, image_set_crop, image_set_size and 1973/* image_set_rotation and image_set_transform use affine
1971 image_set_transform use affine transformation matrices to perform 1974 transformation matrices to perform various transforms on the image.
1972 various transforms on the image. The matrix is a 2D array of 1975 The matrix is a 2D array of doubles. It is laid out like this:
1973 doubles. It is laid out like this:
1974 1976
1975 m[0][0] = m11 | m[1][0] = m12 | m[2][0] = tx 1977 m[0][0] = m11 | m[1][0] = m12 | m[2][0] = tx
1976 --------------+---------------+------------- 1978 --------------+---------------+-------------
@@ -2039,14 +2041,15 @@ compute_image_size (size_t width, size_t height,
2039 finally move the origin back to the top left of the image, which 2041 finally move the origin back to the top left of the image, which
2040 may now be a different corner. 2042 may now be a different corner.
2041 2043
2042 Cropping is easier as we just move the origin to the top left of 2044 Note that different GUI backends (X, Cairo, w32, NS) want the
2043 where we want to crop and set the width and height accordingly. 2045 transform matrix defined as transform from the original image to
2044 The matrices don't know anything about width and height. 2046 the transformed image, while others want the matrix to describe the
2047 transform of the space, which boils down to inverting the matrix.
2045 2048
2046 It's possible to pre-calculate the matrix multiplications and just 2049 It's possible to pre-calculate the matrix multiplications and just
2047 generate one transform matrix that will do everything we need in a 2050 generate one transform matrix that will do everything we need in a
2048 single step, but the maths for each element is much more complex 2051 single step, but the maths for each element is much more complex
2049 and I thought it was better to perform the steps separately. */ 2052 and performing the steps separately makes for more readable code. */
2050 2053
2051typedef double matrix3x3[3][3]; 2054typedef double matrix3x3[3][3];
2052 2055
@@ -2070,102 +2073,27 @@ matrix3x3_mult (matrix3x3 a, matrix3x3 b, matrix3x3 result)
2070} 2073}
2071 2074
2072static void 2075static void
2073image_set_rotation (struct image *img, matrix3x3 tm) 2076compute_image_rotation (struct image *img, double *rotation)
2074{ 2077{
2075#ifdef HAVE_NATIVE_TRANSFORMS
2076# ifdef HAVE_IMAGEMAGICK
2077 /* ImageMagick images are already rotated. */
2078 if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick))
2079 return;
2080# endif
2081
2082# if !defined USE_CAIRO && defined HAVE_XRENDER
2083 if (!img->picture)
2084 return;
2085# endif
2086
2087 int rotation, cos_r, sin_r, width, height;
2088
2089 Lisp_Object value = image_spec_value (img->spec, QCrotation, NULL); 2078 Lisp_Object value = image_spec_value (img->spec, QCrotation, NULL);
2090 if (! NUMBERP (value)) 2079 if (! NUMBERP (value))
2091 return;
2092
2093 Lisp_Object reduced_angle = Fmod (value, make_fixnum (360));
2094 if (! FLOATP (reduced_angle))
2095 rotation = XFIXNUM (reduced_angle);
2096 else
2097 { 2080 {
2098 rotation = XFLOAT_DATA (reduced_angle); 2081 image_error ("Invalid image `:rotation' parameter");
2099 if (rotation != XFLOAT_DATA (reduced_angle))
2100 goto not_a_multiple_of_90;
2101 }
2102
2103 if (rotation == 0)
2104 return;
2105
2106 if (rotation == 90)
2107 {
2108 width = img->height;
2109 height = img->width;
2110
2111 cos_r = 0;
2112 sin_r = 1;
2113 }
2114 else if (rotation == 180)
2115 {
2116 width = img->width;
2117 height = img->height;
2118
2119 cos_r = -1;
2120 sin_r = 0;
2121 }
2122 else if (rotation == 270)
2123 {
2124 width = img->height;
2125 height = img->width;
2126
2127 cos_r = 0;
2128 sin_r = -1;
2129 }
2130 else
2131 {
2132 not_a_multiple_of_90:
2133 image_error ("Native image rotation supports "
2134 "only multiples of 90 degrees");
2135 return; 2082 return;
2136 } 2083 }
2137 2084
2138 /* Translate so (0, 0) is in the center of the image. */ 2085 Lisp_Object reduced_angle = Fmod (value, make_fixnum (360));
2139 matrix3x3 t 2086 if (FLOATP (reduced_angle))
2140 = { [0][0] = 1, 2087 *rotation = XFLOAT_DATA (reduced_angle);
2141 [1][1] = 1, 2088 else
2142 [2][0] = img->width * .5, [2][1] = img->height * .5, [2][2] = 1 }; 2089 *rotation = XFIXNUM (reduced_angle);
2143 matrix3x3 tmp;
2144 matrix3x3_mult (t, tm, tmp);
2145
2146 /* Rotate. */
2147 matrix3x3 rot = { [0][0] = cos_r, [0][1] = -sin_r,
2148 [1][0] = sin_r, [1][1] = cos_r,
2149 [2][2] = 1 };
2150 matrix3x3 tmp2;
2151 matrix3x3_mult (rot, tmp, tmp2);
2152
2153 /* Translate back. */
2154 t[2][0] = width * -.5;
2155 t[2][1] = height * -.5;
2156 matrix3x3_mult (t, tmp2, tm);
2157
2158 img->width = width;
2159 img->height = height;
2160#endif
2161} 2090}
2162 2091
2163static void 2092static void
2164image_set_crop (struct image *img, matrix3x3 tm) 2093image_set_transform (struct frame *f, struct image *img)
2165{ 2094{
2166#ifdef HAVE_NATIVE_TRANSFORMS
2167# ifdef HAVE_IMAGEMAGICK 2095# ifdef HAVE_IMAGEMAGICK
2168 /* ImageMagick images are already cropped. */ 2096 /* ImageMagick images already have the correct transform. */
2169 if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick)) 2097 if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick))
2170 return; 2098 return;
2171# endif 2099# endif
@@ -2175,119 +2103,112 @@ image_set_crop (struct image *img, matrix3x3 tm)
2175 return; 2103 return;
2176# endif 2104# endif
2177 2105
2178 Lisp_Object crop = image_spec_value (img->spec, QCcrop, NULL); 2106 matrix3x3 matrix = { [0][0] = 1, [1][1] = 1, [2][2] = 1 };
2179
2180 if (!CONSP (crop))
2181 return;
2182
2183 Lisp_Object w = XCAR (crop), h = Qnil, x = Qnil, y = Qnil;
2184 crop = XCDR (crop);
2185 if (CONSP (crop))
2186 {
2187 h = XCAR (crop);
2188 crop = XCDR (crop);
2189 if (CONSP (crop))
2190 {
2191 x = XCAR (crop);
2192 crop = XCDR (crop);
2193 if (CONSP (crop))
2194 y = XCAR (crop);
2195 }
2196 }
2197
2198 int width = img->width;
2199 if (FIXNATP (w) && XFIXNAT (w) < img->width)
2200 width = XFIXNAT (w);
2201 int left;
2202 if (TYPE_RANGED_FIXNUMP (int, x))
2203 {
2204 left = XFIXNUM (x);
2205 if (left < 0)
2206 left = img->width - width + left;
2207 }
2208 else
2209 left = (img->width - width) >> 1;
2210
2211 int height = img->height;
2212 if (FIXNATP (h) && XFIXNAT (h) < img->height)
2213 height = XFIXNAT (h);
2214 int top;
2215 if (TYPE_RANGED_FIXNUMP (int, y))
2216 {
2217 top = XFIXNUM (y);
2218 if (top < 0)
2219 top = img->height - height + top;
2220 }
2221 else
2222 top = (img->height - height) >> 1;
2223
2224 /* Negative values operate from the right and bottom of the image
2225 instead of the left and top. */
2226 if (left < 0)
2227 {
2228 width = img->width + left;
2229 left = 0;
2230 }
2231 2107
2232 if (width + left > img->width) 2108 /* Determine size. */
2233 width = img->width - left; 2109 int width, height;
2110 compute_image_size (img->width, img->height, img->spec, &width, &height);
2234 2111
2235 if (top < 0) 2112 /* Determine rotation. */
2236 { 2113 double rotation = 0.0;
2237 height = img->height + top; 2114 compute_image_rotation (img, &rotation);
2238 top = 0;
2239 }
2240 2115
2241 if (height + top > img->height) 2116 /* Perform scale transformation. */
2242 height = img->height - top;
2243 2117
2244 matrix3x3 tmp, m = { [0][0] = 1, 2118 double xscale = (double) width / img->width;
2245 [1][1] = 1, 2119 double yscale = (double) height / img->height;
2246 [2][0] = left, [2][1] = top, [2][2] = 1 };
2247 matrix3x3_mult (m, tm, tmp);
2248 matrix3x3_copy (tmp, tm);
2249 2120
2121# if defined USE_CAIRO || defined HAVE_XRENDER
2122 /* Avoid division by zero. */
2123 xscale = max (xscale, FLT_MIN);
2124 matrix3x3 tmp, rm =
2125 { [0][0] = 1.0 / xscale, [1][1] = 1.0 / yscale, [2][2] = 1 };
2126 matrix3x3_mult (rm, matrix, tmp);
2127# elif defined HAVE_NTGUI || defined HAVE_NS
2128 matrix3x3 tmp, rm = { [0][0] = xscale, [1][1] = yscale, [2][2] = 1 };
2129 matrix3x3_mult (matrix, rm, tmp);
2130# endif
2250 img->width = width; 2131 img->width = width;
2251 img->height = height; 2132 img->height = height;
2252#endif
2253}
2254 2133
2255static void 2134 /* Perform rotation transformation. */
2256image_set_size (struct image *img, matrix3x3 tm)
2257{
2258#ifdef HAVE_NATIVE_TRANSFORMS
2259# ifdef HAVE_IMAGEMAGICK
2260 /* ImageMagick images are already the correct size. */
2261 if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick))
2262 return;
2263# endif
2264 2135
2265# if !defined USE_CAIRO && defined HAVE_XRENDER 2136 int cos_r, sin_r;
2266 if (!img->picture) 2137 if (rotation == 0)
2267 return; 2138 matrix3x3_copy (tmp, matrix);
2268# endif 2139 else if (rotation == 90 || rotation == 180 || rotation == 270)
2269 2140 {
2270 int width, height; 2141 if (rotation == 90)
2271 2142 {
2272 compute_image_size (img->width, img->height, img->spec, &width, &height); 2143 width = img->height;
2144 height = img->width;
2273 2145
2274 double xscale = img->width / (double) width; 2146 cos_r = 0;
2275 double yscale = img->height / (double) height; 2147 sin_r = 1;
2148 }
2149 else if (rotation == 180)
2150 {
2151 width = img->width;
2152 height = img->height;
2276 2153
2277 matrix3x3 tmp, rm = { [0][0] = xscale, [1][1] = yscale, [2][2] = 1 }; 2154 cos_r = -1;
2278 matrix3x3_mult (rm, tm, tmp); 2155 sin_r = 0;
2279 matrix3x3_copy (tmp, tm); 2156 }
2157 else if (rotation == 270)
2158 {
2159 width = img->height;
2160 height = img->width;
2280 2161
2281 img->width = width; 2162 cos_r = 0;
2282 img->height = height; 2163 sin_r = -1;
2283#endif 2164 }
2284} 2165# if defined USE_CAIRO || defined HAVE_XRENDER
2166 /* 1. Translate so (0, 0) is in the center of the image. */
2167 matrix3x3 t
2168 = { [0][0] = 1,
2169 [1][1] = 1,
2170 [2][0] = img->width * .5, [2][1] = img->height * .5, [2][2] = 1 };
2171 matrix3x3 tmp2;
2172 matrix3x3_mult (t, tmp, tmp2);
2173
2174 /* 2. Rotate. */
2175 matrix3x3 rot = { [0][0] = cos_r, [0][1] = -sin_r,
2176 [1][0] = sin_r, [1][1] = cos_r,
2177 [2][2] = 1 };
2178 matrix3x3 tmp3;
2179 matrix3x3_mult (rot, tmp2, tmp3);
2180
2181 /* 3. Translate back. */
2182 t[2][0] = width * -.5;
2183 t[2][1] = height * -.5;
2184 matrix3x3_mult (t, tmp3, matrix);
2185# elif defined HAVE_NTGUI || defined HAVE_NS
2186 /* 1. Translate so (0, 0) is in the center of the image. */
2187 matrix3x3 t
2188 = { [0][0] = 1,
2189 [1][1] = 1,
2190 [2][0] = -img->width * .5, [2][1] = -img->height * .5, [2][2] = 1 };
2191 matrix3x3 tmp2;
2192 matrix3x3_mult (tmp, t, tmp2);
2193
2194 /* 2. Rotate. */
2195 matrix3x3 rot = { [0][0] = cos_r, [0][1] = sin_r,
2196 [1][0] = -sin_r, [1][1] = cos_r,
2197 [2][2] = 1 };
2198 matrix3x3 tmp3;
2199 matrix3x3_mult (tmp2, rot, tmp3);
2200
2201 /* 3. Translate back. */
2202 t[2][0] = width * .5;
2203 t[2][1] = height * .5;
2204 matrix3x3_mult (tmp3, t, matrix);
2205# endif
2206 img->width = width;
2207 img->height = height;
2208 }
2209 else
2210 image_error ("Native image rotation supports only multiples of 90 degrees");
2285 2211
2286static void
2287image_set_transform (struct frame *f, struct image *img, matrix3x3 matrix)
2288{
2289 /* TODO: Add MS Windows support. */
2290#ifdef HAVE_NATIVE_TRANSFORMS
2291# if defined (HAVE_NS) 2212# if defined (HAVE_NS)
2292 /* Under NS the transform is applied to the drawing surface at 2213 /* Under NS the transform is applied to the drawing surface at
2293 drawing time, so store it for later. */ 2214 drawing time, so store it for later. */
@@ -2317,10 +2238,19 @@ image_set_transform (struct frame *f, struct image *img, matrix3x3 matrix)
2317 0, 0); 2238 0, 0);
2318 XRenderSetPictureTransform (FRAME_X_DISPLAY (f), img->picture, &tmat); 2239 XRenderSetPictureTransform (FRAME_X_DISPLAY (f), img->picture, &tmat);
2319 } 2240 }
2241# elif defined HAVE_NTGUI
2242 /* Store the transform matrix for application at draw time. */
2243 img->xform.eM11 = matrix[0][0];
2244 img->xform.eM12 = matrix[0][1];
2245 img->xform.eM21 = matrix[1][0];
2246 img->xform.eM22 = matrix[1][1];
2247 img->xform.eDx = matrix[2][0];
2248 img->xform.eDy = matrix[2][1];
2320# endif 2249# endif
2321#endif
2322} 2250}
2323 2251
2252#endif /* HAVE_IMAGEMAGICK || HAVE_NATIVE_TRANSFORMS */
2253
2324/* Return the id of image with Lisp specification SPEC on frame F. 2254/* Return the id of image with Lisp specification SPEC on frame F.
2325 SPEC must be a valid Lisp image specification (see valid_image_p). */ 2255 SPEC must be a valid Lisp image specification (see valid_image_p). */
2326 2256
@@ -2377,11 +2307,7 @@ lookup_image (struct frame *f, Lisp_Object spec)
2377 int relief_bound; 2307 int relief_bound;
2378 2308
2379#ifdef HAVE_NATIVE_TRANSFORMS 2309#ifdef HAVE_NATIVE_TRANSFORMS
2380 matrix3x3 transform_matrix = { [0][0] = 1, [1][1] = 1, [2][2] = 1 }; 2310 image_set_transform (f, img);
2381 image_set_size (img, transform_matrix);
2382 image_set_crop (img, transform_matrix);
2383 image_set_rotation (img, transform_matrix);
2384 image_set_transform (f, img, transform_matrix);
2385#endif 2311#endif
2386 2312
2387 ascent = image_spec_value (spec, QCascent, NULL); 2313 ascent = image_spec_value (spec, QCascent, NULL);
@@ -10010,19 +9936,38 @@ DEFUN ("lookup-image", Flookup_image, Slookup_image, 1, 1, 0,
10010 9936
10011DEFUN ("image-transforms-p", Fimage_transforms_p, Simage_transforms_p, 0, 1, 0, 9937DEFUN ("image-transforms-p", Fimage_transforms_p, Simage_transforms_p, 0, 1, 0,
10012 doc: /* Test whether FRAME supports image transformation. 9938 doc: /* Test whether FRAME supports image transformation.
10013Return t if FRAME supports native transforms, nil otherwise. */) 9939Return list of capabilities if FRAME supports native transforms, nil otherwise.
9940FRAME defaults to the selected frame.
9941The list of capabilities can include one or more of the following:
9942
9943 - the symbol `scale' if FRAME supports image scaling
9944 - the symbol `rotate' if FRAME supports image rotation by arbitrary angles
9945 - the symbol `rotate90' if FRAME supports image rotation only by angles
9946 that are integral multiples of 90 degrees
9947 - the symbol `crop' if FRAME supports image cropping. */)
10014 (Lisp_Object frame) 9948 (Lisp_Object frame)
10015{ 9949{
10016#if defined (USE_CAIRO) || defined (HAVE_NS) || defined (HAVE_NTGUI) 9950 struct frame *f = decode_live_frame (frame);
10017 return Qt; 9951 if (FRAME_WINDOW_P (f))
10018#elif defined (HAVE_X_WINDOWS) && defined (HAVE_XRENDER) 9952 {
10019 int event_basep, error_basep; 9953#ifdef HAVE_NATIVE_TRANSFORMS
10020 9954# if defined HAVE_IMAGEMAGICK
10021 if (XRenderQueryExtension 9955 return list4 (Qscale, Qrotate90, Qrotate, Qcrop);
10022 (FRAME_X_DISPLAY (decode_window_system_frame (frame)), 9956# elif defined (USE_CAIRO) || defined (HAVE_NS)
10023 &event_basep, &error_basep)) 9957 return list2 (Qscale, Qrotate90);
10024 return Qt; 9958# elif defined (HAVE_NTGUI)
9959 return (w32_image_rotations_p ()
9960 ? list2 (Qscale, Qrotate90)
9961 : list1 (Qscale));
9962# elif defined (HAVE_X_WINDOWS) && defined (HAVE_XRENDER)
9963 int event_basep, error_basep;
9964
9965 if (XRenderQueryExtension (FRAME_X_DISPLAY (f),
9966 &event_basep, &error_basep))
9967 return list2 (Qscale, Qrotate90);
9968# endif
10025#endif 9969#endif
9970 }
10026 9971
10027 return Qnil; 9972 return Qnil;
10028} 9973}
@@ -10168,6 +10113,14 @@ non-numeric, there is no explicit limit on the size of images. */);
10168 DEFSYM (Qpostscript, "postscript"); 10113 DEFSYM (Qpostscript, "postscript");
10169 DEFSYM (QCmax_width, ":max-width"); 10114 DEFSYM (QCmax_width, ":max-width");
10170 DEFSYM (QCmax_height, ":max-height"); 10115 DEFSYM (QCmax_height, ":max-height");
10116
10117#ifdef HAVE_NATIVE_TRANSFORMS
10118 DEFSYM (Qscale, "scale");
10119 DEFSYM (Qrotate, "rotate");
10120 DEFSYM (Qrotate90, "rotate90");
10121 DEFSYM (Qcrop, "crop");
10122#endif
10123
10171#ifdef HAVE_GHOSTSCRIPT 10124#ifdef HAVE_GHOSTSCRIPT
10172 add_image_type (Qpostscript); 10125 add_image_type (Qpostscript);
10173 DEFSYM (QCloader, ":loader"); 10126 DEFSYM (QCloader, ":loader");