diff options
| author | Alan Third | 2019-06-05 21:51:33 +0100 |
|---|---|---|
| committer | Alan Third | 2019-06-05 22:28:46 +0100 |
| commit | 610fb73ab6d7a22b722f523d6ebc4aa8fa1db7c9 (patch) | |
| tree | 8a3ee992ccc310071ac5a97ced8907652974c88e /src/image.c | |
| parent | 9201cf62ce9f17793bb6103050c9ba27eb942e57 (diff) | |
| download | emacs-610fb73ab6d7a22b722f523d6ebc4aa8fa1db7c9.tar.gz emacs-610fb73ab6d7a22b722f523d6ebc4aa8fa1db7c9.zip | |
Add native image rotation and cropping
* lisp/image.el (image--get-imagemagick-and-warn): Only fallback to
ImageMagick if native transforms aren't available.
* src/dispextern.h (INIT_MATRIX, COPY_MATRIX, MULT_MATRICES): New
macros for matrix manipulation.
(HAVE_NATIVE_SCALING, HAVE_NATIVE_TRANSFORMS): Rename and change all
relevant locations.
* src/image.c (x_set_image_rotation):
(x_set_transform): New functions.
(x_set_image_size): Use transform matrix for resizing under X and NS.
(x_set_image_crop): New function.
(lookup_image): Use the new transform functions.
(Fimage_scaling_p, Fimage_transforms_p): Rename and update all
callers.
* src/nsimage.m (ns_load_image): Remove rotation code.
(ns_image_set_transform): New function.
([EmacsImage dealloc]): Release the saved transform.
([EmacsImage rotate:]): Remove unneeded method.
([EmacsImage setTransform:]): New method.
* src/nsterm.h (EmacsImage): Add transform property and update method
definitions.
* src/nsterm.m (ns_dumpglyphs_image): Use the transform to draw the
image correctly.
* src/xterm.c (x_composite_image): Use PictOpSrc as we don't care
about alpha values here.
* doc/lispref/display.texi (Image Descriptors): Add :rotation.
(ImageMagick Images): Remove :rotation.
Diffstat (limited to 'src/image.c')
| -rw-r--r-- | src/image.c | 283 |
1 files changed, 257 insertions, 26 deletions
diff --git a/src/image.c b/src/image.c index 9b0080130bc..a95a7bf27f9 100644 --- a/src/image.c +++ b/src/image.c | |||
| @@ -1841,7 +1841,7 @@ postprocess_image (struct frame *f, struct image *img) | |||
| 1841 | } | 1841 | } |
| 1842 | } | 1842 | } |
| 1843 | 1843 | ||
| 1844 | #if defined (HAVE_IMAGEMAGICK) || defined (HAVE_NATIVE_SCALING) | 1844 | #if defined (HAVE_IMAGEMAGICK) || defined (HAVE_NATIVE_TRANSFORMS) |
| 1845 | /* Scale an image size by returning SIZE / DIVISOR * MULTIPLIER, | 1845 | /* Scale an image size by returning SIZE / DIVISOR * MULTIPLIER, |
| 1846 | safely rounded and clipped to int range. */ | 1846 | safely rounded and clipped to int range. */ |
| 1847 | 1847 | ||
| @@ -1940,49 +1940,241 @@ compute_image_size (size_t width, size_t height, | |||
| 1940 | *d_width = desired_width; | 1940 | *d_width = desired_width; |
| 1941 | *d_height = desired_height; | 1941 | *d_height = desired_height; |
| 1942 | } | 1942 | } |
| 1943 | #endif /* HAVE_IMAGEMAGICK || HAVE_NATIVE_SCALING */ | 1943 | #endif /* HAVE_IMAGEMAGICK || HAVE_NATIVE_TRANSFORMS */ |
| 1944 | 1944 | ||
| 1945 | static void | 1945 | static void |
| 1946 | image_set_image_size (struct frame *f, struct image *img) | 1946 | image_set_rotation (struct image *img, double tm[3][3]) |
| 1947 | { | 1947 | { |
| 1948 | #ifdef HAVE_NATIVE_SCALING | 1948 | #ifdef HAVE_NATIVE_TRANSFORMS |
| 1949 | # ifdef HAVE_IMAGEMAGICK | 1949 | # ifdef HAVE_IMAGEMAGICK |
| 1950 | /* ImageMagick images are already the correct size. */ | 1950 | /* ImageMagick images are already rotated. */ |
| 1951 | if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick)) | 1951 | if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick)) |
| 1952 | return; | 1952 | return; |
| 1953 | # endif | 1953 | # endif |
| 1954 | 1954 | ||
| 1955 | int width, height; | 1955 | # ifdef HAVE_XRENDER |
| 1956 | compute_image_size (img->width, img->height, img->spec, &width, &height); | 1956 | if (!img->picture) |
| 1957 | return; | ||
| 1958 | # endif | ||
| 1959 | |||
| 1960 | Lisp_Object value; | ||
| 1961 | double t[3][3], rot[3][3], tmp[3][3], tmp2[3][3]; | ||
| 1962 | int rotation, cos_r, sin_r, width, height; | ||
| 1963 | |||
| 1964 | value = image_spec_value (img->spec, QCrotation, NULL); | ||
| 1965 | if (! NUMBERP (value)) | ||
| 1966 | return; | ||
| 1967 | |||
| 1968 | rotation = XFLOATINT (value); | ||
| 1969 | rotation = rotation % 360; | ||
| 1970 | |||
| 1971 | if (rotation < 0) | ||
| 1972 | rotation += 360; | ||
| 1973 | |||
| 1974 | if (rotation == 0) | ||
| 1975 | return; | ||
| 1976 | |||
| 1977 | if (rotation == 90) | ||
| 1978 | { | ||
| 1979 | width = img->height; | ||
| 1980 | height = img->width; | ||
| 1981 | |||
| 1982 | cos_r = 0; | ||
| 1983 | sin_r = 1; | ||
| 1984 | } | ||
| 1985 | else if (rotation == 180) | ||
| 1986 | { | ||
| 1987 | width = img->width; | ||
| 1988 | height = img->height; | ||
| 1989 | |||
| 1990 | cos_r = -1; | ||
| 1991 | sin_r = 0; | ||
| 1992 | } | ||
| 1993 | else if (rotation == 270) | ||
| 1994 | { | ||
| 1995 | width = img->height; | ||
| 1996 | height = img->width; | ||
| 1997 | |||
| 1998 | cos_r = 0; | ||
| 1999 | sin_r = -1; | ||
| 2000 | } | ||
| 2001 | else | ||
| 2002 | { | ||
| 2003 | image_error ("Native image rotation only supports multiples of 90 degrees"); | ||
| 2004 | return; | ||
| 2005 | } | ||
| 2006 | |||
| 2007 | /* Translate so (0, 0) is in the centre of the image. */ | ||
| 2008 | INIT_MATRIX (t); | ||
| 2009 | t[2][0] = img->width/2; | ||
| 2010 | t[2][1] = img->height/2; | ||
| 2011 | |||
| 2012 | MULT_MATRICES (tm, t, tmp); | ||
| 2013 | |||
| 2014 | /* Rotate. */ | ||
| 2015 | INIT_MATRIX (rot); | ||
| 2016 | rot[0][0] = cos_r; | ||
| 2017 | rot[1][0] = sin_r; | ||
| 2018 | rot[0][1] = - sin_r; | ||
| 2019 | rot[1][1] = cos_r; | ||
| 2020 | |||
| 2021 | MULT_MATRICES (tmp, rot, tmp2); | ||
| 2022 | |||
| 2023 | /* Translate back. */ | ||
| 2024 | INIT_MATRIX (t); | ||
| 2025 | t[2][0] = - width/2; | ||
| 2026 | t[2][1] = - height/2; | ||
| 2027 | |||
| 2028 | MULT_MATRICES (tmp2, t, tm); | ||
| 1957 | 2029 | ||
| 1958 | # ifdef HAVE_NS | ||
| 1959 | ns_image_set_size (img->pixmap, width, height); | ||
| 1960 | img->width = width; | 2030 | img->width = width; |
| 1961 | img->height = height; | 2031 | img->height = height; |
| 2032 | #endif | ||
| 2033 | } | ||
| 2034 | |||
| 2035 | static void | ||
| 2036 | image_set_crop (struct image *img, double tm[3][3]) | ||
| 2037 | { | ||
| 2038 | #ifdef HAVE_NATIVE_TRANSFORMS | ||
| 2039 | # ifdef HAVE_IMAGEMAGICK | ||
| 2040 | /* ImageMagick images are already cropped. */ | ||
| 2041 | if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick)) | ||
| 2042 | return; | ||
| 1962 | # endif | 2043 | # endif |
| 1963 | 2044 | ||
| 1964 | # ifdef USE_CAIRO | 2045 | # ifdef USE_CAIRO |
| 1965 | img->width = width; | 2046 | img->width = width; |
| 1966 | img->height = height; | 2047 | img->height = height; |
| 1967 | # elif defined HAVE_XRENDER | 2048 | # elif defined HAVE_XRENDER |
| 1968 | if (img->picture) | 2049 | if (!img->picture) |
| 2050 | return; | ||
| 2051 | # endif | ||
| 2052 | |||
| 2053 | double m[3][3], tmp[3][3]; | ||
| 2054 | int left, top, width, height; | ||
| 2055 | Lisp_Object x = Qnil; | ||
| 2056 | Lisp_Object y = Qnil; | ||
| 2057 | Lisp_Object w = Qnil; | ||
| 2058 | Lisp_Object h = Qnil; | ||
| 2059 | Lisp_Object crop = image_spec_value (img->spec, QCcrop, NULL); | ||
| 2060 | |||
| 2061 | if (!CONSP (crop)) | ||
| 2062 | return; | ||
| 2063 | else | ||
| 1969 | { | 2064 | { |
| 1970 | double xscale = img->width / (double) width; | 2065 | w = XCAR (crop); |
| 1971 | double yscale = img->height / (double) height; | 2066 | crop = XCDR (crop); |
| 2067 | if (CONSP (crop)) | ||
| 2068 | { | ||
| 2069 | h = XCAR (crop); | ||
| 2070 | crop = XCDR (crop); | ||
| 2071 | if (CONSP (crop)) | ||
| 2072 | { | ||
| 2073 | x = XCAR (crop); | ||
| 2074 | crop = XCDR (crop); | ||
| 2075 | if (CONSP (crop)) | ||
| 2076 | y = XCAR (crop); | ||
| 2077 | } | ||
| 2078 | } | ||
| 2079 | } | ||
| 1972 | 2080 | ||
| 1973 | XTransform tmat | 2081 | if (FIXNATP (w) && XFIXNAT (w) < img->width) |
| 1974 | = {{{XDoubleToFixed (xscale), XDoubleToFixed (0), XDoubleToFixed (0)}, | 2082 | width = XFIXNAT (w); |
| 1975 | {XDoubleToFixed (0), XDoubleToFixed (yscale), XDoubleToFixed (0)}, | 2083 | else |
| 1976 | {XDoubleToFixed (0), XDoubleToFixed (0), XDoubleToFixed (1)}}}; | 2084 | width = img->width; |
| 1977 | 2085 | ||
| 1978 | XRenderSetPictureFilter (FRAME_X_DISPLAY (f), img->picture, FilterBest, | 2086 | if (TYPE_RANGED_FIXNUMP (int, x)) |
| 1979 | 0, 0); | 2087 | { |
| 1980 | XRenderSetPictureTransform (FRAME_X_DISPLAY (f), img->picture, &tmat); | 2088 | left = XFIXNUM (x); |
| 2089 | if (left < 0) | ||
| 2090 | left = img->width - width + left; | ||
| 2091 | } | ||
| 2092 | else | ||
| 2093 | left = (img->width - width)/2; | ||
| 2094 | |||
| 2095 | if (FIXNATP (h) && XFIXNAT (h) < img->height) | ||
| 2096 | height = XFIXNAT (h); | ||
| 2097 | else | ||
| 2098 | height = img->height; | ||
| 2099 | |||
| 2100 | if (TYPE_RANGED_FIXNUMP (int, y)) | ||
| 2101 | { | ||
| 2102 | top = XFIXNUM (y); | ||
| 2103 | if (top < 0) | ||
| 2104 | top = img->height - height + top; | ||
| 2105 | } | ||
| 2106 | else | ||
| 2107 | top = (img->height - height)/2; | ||
| 2108 | |||
| 2109 | /* Negative values operate from the right and bottom of the image | ||
| 2110 | instead of the left and top. */ | ||
| 2111 | if (left < 0) | ||
| 2112 | { | ||
| 2113 | width = img->width + left; | ||
| 2114 | left = 0; | ||
| 2115 | } | ||
| 2116 | |||
| 2117 | if (width + left > img->width) | ||
| 2118 | width = img->width - left; | ||
| 1981 | 2119 | ||
| 1982 | img->width = width; | 2120 | if (top < 0) |
| 1983 | img->height = height; | 2121 | { |
| 2122 | height = img->height + top; | ||
| 2123 | top = 0; | ||
| 1984 | } | 2124 | } |
| 2125 | |||
| 2126 | if (height + top > img->height) | ||
| 2127 | height = img->height - top; | ||
| 2128 | |||
| 2129 | INIT_MATRIX (m); | ||
| 2130 | m[2][0] = left; | ||
| 2131 | m[2][1] = top; | ||
| 2132 | |||
| 2133 | MULT_MATRICES (tm, m, tmp); | ||
| 2134 | COPY_MATRIX (tmp, tm); | ||
| 2135 | |||
| 2136 | img->width = width; | ||
| 2137 | img->height = height; | ||
| 2138 | #endif | ||
| 2139 | } | ||
| 2140 | |||
| 2141 | static void | ||
| 2142 | image_set_size (struct image *img, double tm[3][3]) | ||
| 2143 | { | ||
| 2144 | #ifdef HAVE_NATIVE_TRANSFORMS | ||
| 2145 | # ifdef HAVE_IMAGEMAGICK | ||
| 2146 | /* ImageMagick images are already the correct size. */ | ||
| 2147 | if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick)) | ||
| 2148 | return; | ||
| 2149 | # endif | ||
| 2150 | |||
| 2151 | # ifdef HAVE_XRENDER | ||
| 2152 | if (!img->picture) | ||
| 2153 | return; | ||
| 2154 | # endif | ||
| 2155 | |||
| 2156 | int width, height; | ||
| 2157 | |||
| 2158 | compute_image_size (img->width, img->height, img->spec, &width, &height); | ||
| 2159 | |||
| 2160 | # if defined (HAVE_NS) || defined (HAVE_XRENDER) | ||
| 2161 | double rm[3][3], tmp[3][3]; | ||
| 2162 | double xscale, yscale; | ||
| 2163 | |||
| 2164 | xscale = img->width / (double) width; | ||
| 2165 | yscale = img->height / (double) height; | ||
| 2166 | |||
| 2167 | INIT_MATRIX (rm); | ||
| 2168 | rm[0][0] = xscale; | ||
| 2169 | rm[1][1] = yscale; | ||
| 2170 | |||
| 2171 | MULT_MATRICES (tm, rm, tmp); | ||
| 2172 | COPY_MATRIX (tmp, tm); | ||
| 2173 | |||
| 2174 | img->width = width; | ||
| 2175 | img->height = height; | ||
| 1985 | # endif | 2176 | # endif |
| 2177 | |||
| 1986 | # ifdef HAVE_NTGUI | 2178 | # ifdef HAVE_NTGUI |
| 1987 | /* Under HAVE_NTGUI, we will scale the image on the fly, when we | 2179 | /* Under HAVE_NTGUI, we will scale the image on the fly, when we |
| 1988 | draw it. See w32term.c:x_draw_image_foreground. */ | 2180 | draw it. See w32term.c:x_draw_image_foreground. */ |
| @@ -1992,6 +2184,36 @@ image_set_image_size (struct frame *f, struct image *img) | |||
| 1992 | #endif | 2184 | #endif |
| 1993 | } | 2185 | } |
| 1994 | 2186 | ||
| 2187 | static void | ||
| 2188 | image_set_transform (struct frame *f, struct image *img, double matrix[3][3]) | ||
| 2189 | { | ||
| 2190 | /* TODO: Add MS Windows support. */ | ||
| 2191 | #ifdef HAVE_NATIVE_TRANSFORMS | ||
| 2192 | # if defined (HAVE_NS) | ||
| 2193 | /* Under NS the transform is applied to the drawing surface at | ||
| 2194 | drawing time, so store it for later. */ | ||
| 2195 | ns_image_set_transform (img->pixmap, matrix); | ||
| 2196 | # elif defined (HAVE_XRENDER) | ||
| 2197 | if (img->picture) | ||
| 2198 | { | ||
| 2199 | XTransform tmat | ||
| 2200 | = {{{XDoubleToFixed (matrix[0][0]), | ||
| 2201 | XDoubleToFixed (matrix[1][0]), | ||
| 2202 | XDoubleToFixed (matrix[2][0])}, | ||
| 2203 | {XDoubleToFixed (matrix[0][1]), | ||
| 2204 | XDoubleToFixed (matrix[1][1]), | ||
| 2205 | XDoubleToFixed (matrix[2][1])}, | ||
| 2206 | {XDoubleToFixed (matrix[0][2]), | ||
| 2207 | XDoubleToFixed (matrix[1][2]), | ||
| 2208 | XDoubleToFixed (matrix[2][2])}}}; | ||
| 2209 | |||
| 2210 | XRenderSetPictureFilter (FRAME_X_DISPLAY (f), img->picture, FilterBest, | ||
| 2211 | 0, 0); | ||
| 2212 | XRenderSetPictureTransform (FRAME_X_DISPLAY (f), img->picture, &tmat); | ||
| 2213 | } | ||
| 2214 | # endif | ||
| 2215 | #endif | ||
| 2216 | } | ||
| 1995 | 2217 | ||
| 1996 | /* Return the id of image with Lisp specification SPEC on frame F. | 2218 | /* Return the id of image with Lisp specification SPEC on frame F. |
| 1997 | SPEC must be a valid Lisp image specification (see valid_image_p). */ | 2219 | SPEC must be a valid Lisp image specification (see valid_image_p). */ |
| @@ -2047,7 +2269,16 @@ lookup_image (struct frame *f, Lisp_Object spec) | |||
| 2047 | `:background COLOR'. */ | 2269 | `:background COLOR'. */ |
| 2048 | Lisp_Object ascent, margin, relief, bg; | 2270 | Lisp_Object ascent, margin, relief, bg; |
| 2049 | int relief_bound; | 2271 | int relief_bound; |
| 2050 | image_set_image_size (f, img); | 2272 | |
| 2273 | #ifdef HAVE_NATIVE_TRANSFORMS | ||
| 2274 | double transform_matrix[3][3]; | ||
| 2275 | |||
| 2276 | INIT_MATRIX (transform_matrix); | ||
| 2277 | image_set_size (img, transform_matrix); | ||
| 2278 | image_set_crop (img, transform_matrix); | ||
| 2279 | image_set_rotation (img, transform_matrix); | ||
| 2280 | image_set_transform (f, img, transform_matrix); | ||
| 2281 | #endif | ||
| 2051 | 2282 | ||
| 2052 | ascent = image_spec_value (spec, QCascent, NULL); | 2283 | ascent = image_spec_value (spec, QCascent, NULL); |
| 2053 | if (FIXNUMP (ascent)) | 2284 | if (FIXNUMP (ascent)) |
| @@ -9673,9 +9904,9 @@ DEFUN ("lookup-image", Flookup_image, Slookup_image, 1, 1, 0, | |||
| 9673 | Initialization | 9904 | Initialization |
| 9674 | ***********************************************************************/ | 9905 | ***********************************************************************/ |
| 9675 | 9906 | ||
| 9676 | DEFUN ("image-scaling-p", Fimage_scaling_p, Simage_scaling_p, 0, 1, 0, | 9907 | DEFUN ("image-transforms-p", Fimage_transforms_p, Simage_transforms_p, 0, 1, 0, |
| 9677 | doc: /* Test whether FRAME supports resizing images. | 9908 | doc: /* Test whether FRAME supports image transformation. |
| 9678 | Return t if FRAME supports native scaling, nil otherwise. */) | 9909 | Return t if FRAME supports native transforms, nil otherwise. */) |
| 9679 | (Lisp_Object frame) | 9910 | (Lisp_Object frame) |
| 9680 | { | 9911 | { |
| 9681 | #if defined (USE_CAIRO) || defined (HAVE_NS) || defined (HAVE_NTGUI) | 9912 | #if defined (USE_CAIRO) || defined (HAVE_NS) || defined (HAVE_NTGUI) |
| @@ -9935,7 +10166,7 @@ non-numeric, there is no explicit limit on the size of images. */); | |||
| 9935 | defsubr (&Slookup_image); | 10166 | defsubr (&Slookup_image); |
| 9936 | #endif | 10167 | #endif |
| 9937 | 10168 | ||
| 9938 | defsubr (&Simage_scaling_p); | 10169 | defsubr (&Simage_transforms_p); |
| 9939 | 10170 | ||
| 9940 | DEFVAR_BOOL ("cross-disabled-images", cross_disabled_images, | 10171 | DEFVAR_BOOL ("cross-disabled-images", cross_disabled_images, |
| 9941 | doc: /* Non-nil means always draw a cross over disabled images. | 10172 | doc: /* Non-nil means always draw a cross over disabled images. |