diff options
| author | Paul Eggert | 2019-06-06 08:09:58 -0700 |
|---|---|---|
| committer | Paul Eggert | 2019-06-06 08:10:31 -0700 |
| commit | 7c26e0b18d0cdf656778d6dd332972b538491d95 (patch) | |
| tree | 48674c6c1658fb5e45d69ca99285480b8655fe82 /src | |
| parent | e29fb7dfba96e63cb7ba2d2a68f91f8dec336100 (diff) | |
| download | emacs-7c26e0b18d0cdf656778d6dd332972b538491d95.tar.gz emacs-7c26e0b18d0cdf656778d6dd332972b538491d95.zip | |
Fix overflow issues in image rotation
Also, do some refactoring to simplify code.
* src/dispextern.h (INIT_MATRIX, COPY_MATRIX, MULT_MATRICES): Remove.
* src/image.c (matrix3x3): New type, replacing all uses of 3x3 double.
(matrix3x3_copy, matrix3x3_mult): New functions, replacing
COPY_MATRIX, MULT_MATRICES. Replace INIT_MATRIX by C initializers.
(image_set_rotation): Use Fmod to avoid undefined behavior on
double-to-int conversion and to reduce bignum rotations correctly.
(image_set_crop): Finish up previous correction, by not re-setting
width and height if compute_image_size has set them.
Prefer shifting right by 1 to dividing by 2 if either will do.
Diffstat (limited to 'src')
| -rw-r--r-- | src/dispextern.h | 21 | ||||
| -rw-r--r-- | src/image.c | 149 |
2 files changed, 77 insertions, 93 deletions
diff --git a/src/dispextern.h b/src/dispextern.h index a21edd818f0..cc15950d5df 100644 --- a/src/dispextern.h +++ b/src/dispextern.h | |||
| @@ -2988,26 +2988,9 @@ struct redisplay_interface | |||
| 2988 | 2988 | ||
| 2989 | #ifdef HAVE_WINDOW_SYSTEM | 2989 | #ifdef HAVE_WINDOW_SYSTEM |
| 2990 | 2990 | ||
| 2991 | # if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_NS || defined HAVE_NTGUI | 2991 | # if (defined USE_CAIRO || defined HAVE_XRENDER \ |
| 2992 | || defined HAVE_NS || defined HAVE_NTGUI) | ||
| 2992 | # define HAVE_NATIVE_TRANSFORMS | 2993 | # define HAVE_NATIVE_TRANSFORMS |
| 2993 | |||
| 2994 | # define INIT_MATRIX(m) \ | ||
| 2995 | for (int i = 0 ; i < 3 ; i++) \ | ||
| 2996 | for (int j = 0 ; j < 3 ; j++) \ | ||
| 2997 | m[i][j] = (i == j) ? 1 : 0; | ||
| 2998 | |||
| 2999 | # define COPY_MATRIX(a, b) \ | ||
| 3000 | for (int i = 0 ; i < 3 ; i++) \ | ||
| 3001 | for (int j = 0 ; j < 3 ; j++) \ | ||
| 3002 | b[i][j] = a[i][j]; | ||
| 3003 | |||
| 3004 | # define MULT_MATRICES(a, b, result) \ | ||
| 3005 | for (int i = 0 ; i < 3 ; i++) \ | ||
| 3006 | for (int j = 0 ; j < 3 ; j++) { \ | ||
| 3007 | double sum = 0; \ | ||
| 3008 | for (int k = 0 ; k < 3 ; k++) \ | ||
| 3009 | sum += a[k][j] * b[i][k]; \ | ||
| 3010 | result[i][j] = sum;} | ||
| 3011 | # endif | 2994 | # endif |
| 3012 | 2995 | ||
| 3013 | /* Structure describing an image. Specific image formats like XBM are | 2996 | /* Structure describing an image. Specific image formats like XBM are |
diff --git a/src/image.c b/src/image.c index 29cb2fc10b9..2a980135a22 100644 --- a/src/image.c +++ b/src/image.c | |||
| @@ -1942,8 +1942,29 @@ compute_image_size (size_t width, size_t height, | |||
| 1942 | } | 1942 | } |
| 1943 | #endif /* HAVE_IMAGEMAGICK || HAVE_NATIVE_TRANSFORMS */ | 1943 | #endif /* HAVE_IMAGEMAGICK || HAVE_NATIVE_TRANSFORMS */ |
| 1944 | 1944 | ||
| 1945 | typedef double matrix3x3[3][3]; | ||
| 1946 | |||
| 1947 | static void | ||
| 1948 | matrix3x3_copy (matrix3x3 a, matrix3x3 b) | ||
| 1949 | { | ||
| 1950 | memcpy (b, a, sizeof (matrix3x3)); | ||
| 1951 | } | ||
| 1952 | |||
| 1945 | static void | 1953 | static void |
| 1946 | image_set_rotation (struct image *img, double tm[3][3]) | 1954 | matrix3x3_mult (matrix3x3 a, matrix3x3 b, matrix3x3 result) |
| 1955 | { | ||
| 1956 | for (int i = 0; i < 3; i++) | ||
| 1957 | for (int j = 0; j < 3; j++) | ||
| 1958 | { | ||
| 1959 | double sum = 0; | ||
| 1960 | for (int k = 0; k < 3; k++) | ||
| 1961 | sum += a[i][k] * b[k][j]; | ||
| 1962 | result[i][j] = sum; | ||
| 1963 | } | ||
| 1964 | } | ||
| 1965 | |||
| 1966 | static void | ||
| 1967 | image_set_rotation (struct image *img, matrix3x3 tm) | ||
| 1947 | { | 1968 | { |
| 1948 | #ifdef HAVE_NATIVE_TRANSFORMS | 1969 | #ifdef HAVE_NATIVE_TRANSFORMS |
| 1949 | # ifdef HAVE_IMAGEMAGICK | 1970 | # ifdef HAVE_IMAGEMAGICK |
| @@ -1957,19 +1978,21 @@ image_set_rotation (struct image *img, double tm[3][3]) | |||
| 1957 | return; | 1978 | return; |
| 1958 | # endif | 1979 | # endif |
| 1959 | 1980 | ||
| 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; | 1981 | int rotation, cos_r, sin_r, width, height; |
| 1963 | 1982 | ||
| 1964 | value = image_spec_value (img->spec, QCrotation, NULL); | 1983 | Lisp_Object value = image_spec_value (img->spec, QCrotation, NULL); |
| 1965 | if (! NUMBERP (value)) | 1984 | if (! NUMBERP (value)) |
| 1966 | return; | 1985 | return; |
| 1967 | 1986 | ||
| 1968 | rotation = XFLOATINT (value); | 1987 | Lisp_Object reduced_angle = Fmod (value, make_fixnum (360)); |
| 1969 | rotation = rotation % 360; | 1988 | if (! FLOATP (reduced_angle)) |
| 1970 | 1989 | rotation = XFIXNUM (reduced_angle); | |
| 1971 | if (rotation < 0) | 1990 | else |
| 1972 | rotation += 360; | 1991 | { |
| 1992 | rotation = XFLOAT_DATA (reduced_angle); | ||
| 1993 | if (rotation != XFLOAT_DATA (reduced_angle)) | ||
| 1994 | goto not_a_multiple_of_90; | ||
| 1995 | } | ||
| 1973 | 1996 | ||
| 1974 | if (rotation == 0) | 1997 | if (rotation == 0) |
| 1975 | return; | 1998 | return; |
| @@ -2000,32 +2023,31 @@ image_set_rotation (struct image *img, double tm[3][3]) | |||
| 2000 | } | 2023 | } |
| 2001 | else | 2024 | else |
| 2002 | { | 2025 | { |
| 2003 | image_error ("Native image rotation only supports multiples of 90 degrees"); | 2026 | not_a_multiple_of_90: |
| 2027 | image_error ("Native image rotation supports " | ||
| 2028 | "only multiples of 90 degrees"); | ||
| 2004 | return; | 2029 | return; |
| 2005 | } | 2030 | } |
| 2006 | 2031 | ||
| 2007 | /* Translate so (0, 0) is in the centre of the image. */ | 2032 | /* Translate so (0, 0) is in the center of the image. */ |
| 2008 | INIT_MATRIX (t); | 2033 | matrix3x3 t |
| 2009 | t[2][0] = img->width/2; | 2034 | = { [0][0] = 1, |
| 2010 | t[2][1] = img->height/2; | 2035 | [1][1] = 1, |
| 2011 | 2036 | [2][0] = img->width >> 1, [2][1] = img->height >> 1, [2][2] = 1 }; | |
| 2012 | MULT_MATRICES (tm, t, tmp); | 2037 | matrix3x3 tmp; |
| 2038 | matrix3x3_mult (t, tm, tmp); | ||
| 2013 | 2039 | ||
| 2014 | /* Rotate. */ | 2040 | /* Rotate. */ |
| 2015 | INIT_MATRIX (rot); | 2041 | matrix3x3 rot = { [0][0] = cos_r, [0][1] = -sin_r, |
| 2016 | rot[0][0] = cos_r; | 2042 | [1][0] = sin_r, [1][1] = cos_r, |
| 2017 | rot[1][0] = sin_r; | 2043 | [2][2] = 1 }; |
| 2018 | rot[0][1] = - sin_r; | 2044 | matrix3x3 tmp2; |
| 2019 | rot[1][1] = cos_r; | 2045 | matrix3x3_mult (rot, tmp, tmp2); |
| 2020 | |||
| 2021 | MULT_MATRICES (tmp, rot, tmp2); | ||
| 2022 | 2046 | ||
| 2023 | /* Translate back. */ | 2047 | /* Translate back. */ |
| 2024 | INIT_MATRIX (t); | 2048 | t[2][0] = - (width >> 1); |
| 2025 | t[2][0] = - width/2; | 2049 | t[2][1] = - (height >> 1); |
| 2026 | t[2][1] = - height/2; | 2050 | matrix3x3_mult (t, tmp2, tm); |
| 2027 | |||
| 2028 | MULT_MATRICES (tmp2, t, tm); | ||
| 2029 | 2051 | ||
| 2030 | img->width = width; | 2052 | img->width = width; |
| 2031 | img->height = height; | 2053 | img->height = height; |
| @@ -2033,7 +2055,7 @@ image_set_rotation (struct image *img, double tm[3][3]) | |||
| 2033 | } | 2055 | } |
| 2034 | 2056 | ||
| 2035 | static void | 2057 | static void |
| 2036 | image_set_crop (struct image *img, double tm[3][3]) | 2058 | image_set_crop (struct image *img, matrix3x3 tm) |
| 2037 | { | 2059 | { |
| 2038 | int width, height; | 2060 | int width, height; |
| 2039 | compute_image_size (img->width, img->height, img->spec, &width, &height); | 2061 | compute_image_size (img->width, img->height, img->spec, &width, &height); |
| @@ -2052,39 +2074,29 @@ image_set_crop (struct image *img, double tm[3][3]) | |||
| 2052 | return; | 2074 | return; |
| 2053 | # endif | 2075 | # endif |
| 2054 | 2076 | ||
| 2055 | double m[3][3], tmp[3][3]; | ||
| 2056 | int left, top; | ||
| 2057 | Lisp_Object x = Qnil; | ||
| 2058 | Lisp_Object y = Qnil; | ||
| 2059 | Lisp_Object w = Qnil; | ||
| 2060 | Lisp_Object h = Qnil; | ||
| 2061 | Lisp_Object crop = image_spec_value (img->spec, QCcrop, NULL); | 2077 | Lisp_Object crop = image_spec_value (img->spec, QCcrop, NULL); |
| 2062 | 2078 | ||
| 2063 | if (!CONSP (crop)) | 2079 | if (!CONSP (crop)) |
| 2064 | return; | 2080 | return; |
| 2065 | else | 2081 | |
| 2082 | Lisp_Object w = XCAR (crop), h = Qnil, x = Qnil, y = Qnil; | ||
| 2083 | crop = XCDR (crop); | ||
| 2084 | if (CONSP (crop)) | ||
| 2066 | { | 2085 | { |
| 2067 | w = XCAR (crop); | 2086 | h = XCAR (crop); |
| 2068 | crop = XCDR (crop); | 2087 | crop = XCDR (crop); |
| 2069 | if (CONSP (crop)) | 2088 | if (CONSP (crop)) |
| 2070 | { | 2089 | { |
| 2071 | h = XCAR (crop); | 2090 | x = XCAR (crop); |
| 2072 | crop = XCDR (crop); | 2091 | crop = XCDR (crop); |
| 2073 | if (CONSP (crop)) | 2092 | if (CONSP (crop)) |
| 2074 | { | 2093 | y = XCAR (crop); |
| 2075 | x = XCAR (crop); | ||
| 2076 | crop = XCDR (crop); | ||
| 2077 | if (CONSP (crop)) | ||
| 2078 | y = XCAR (crop); | ||
| 2079 | } | ||
| 2080 | } | 2094 | } |
| 2081 | } | 2095 | } |
| 2082 | 2096 | ||
| 2083 | if (FIXNATP (w) && XFIXNAT (w) < img->width) | 2097 | if (FIXNATP (w) && XFIXNAT (w) < img->width) |
| 2084 | width = XFIXNAT (w); | 2098 | width = XFIXNAT (w); |
| 2085 | else | 2099 | int left; |
| 2086 | width = img->width; | ||
| 2087 | |||
| 2088 | if (TYPE_RANGED_FIXNUMP (int, x)) | 2100 | if (TYPE_RANGED_FIXNUMP (int, x)) |
| 2089 | { | 2101 | { |
| 2090 | left = XFIXNUM (x); | 2102 | left = XFIXNUM (x); |
| @@ -2092,13 +2104,11 @@ image_set_crop (struct image *img, double tm[3][3]) | |||
| 2092 | left = img->width - width + left; | 2104 | left = img->width - width + left; |
| 2093 | } | 2105 | } |
| 2094 | else | 2106 | else |
| 2095 | left = (img->width - width)/2; | 2107 | left = (img->width - width) >> 1; |
| 2096 | 2108 | ||
| 2097 | if (FIXNATP (h) && XFIXNAT (h) < img->height) | 2109 | if (FIXNATP (h) && XFIXNAT (h) < img->height) |
| 2098 | height = XFIXNAT (h); | 2110 | height = XFIXNAT (h); |
| 2099 | else | 2111 | int top; |
| 2100 | height = img->height; | ||
| 2101 | |||
| 2102 | if (TYPE_RANGED_FIXNUMP (int, y)) | 2112 | if (TYPE_RANGED_FIXNUMP (int, y)) |
| 2103 | { | 2113 | { |
| 2104 | top = XFIXNUM (y); | 2114 | top = XFIXNUM (y); |
| @@ -2106,7 +2116,7 @@ image_set_crop (struct image *img, double tm[3][3]) | |||
| 2106 | top = img->height - height + top; | 2116 | top = img->height - height + top; |
| 2107 | } | 2117 | } |
| 2108 | else | 2118 | else |
| 2109 | top = (img->height - height)/2; | 2119 | top = (img->height - height) >> 1; |
| 2110 | 2120 | ||
| 2111 | /* Negative values operate from the right and bottom of the image | 2121 | /* Negative values operate from the right and bottom of the image |
| 2112 | instead of the left and top. */ | 2122 | instead of the left and top. */ |
| @@ -2128,12 +2138,11 @@ image_set_crop (struct image *img, double tm[3][3]) | |||
| 2128 | if (height + top > img->height) | 2138 | if (height + top > img->height) |
| 2129 | height = img->height - top; | 2139 | height = img->height - top; |
| 2130 | 2140 | ||
| 2131 | INIT_MATRIX (m); | 2141 | matrix3x3 tmp, m = { [0][0] = 1, |
| 2132 | m[2][0] = left; | 2142 | [1][1] = 1, |
| 2133 | m[2][1] = top; | 2143 | [2][0] = left, [2][1] = top, [2][2] = 1 }; |
| 2134 | 2144 | matrix3x3_mult (m, tm, tmp); | |
| 2135 | MULT_MATRICES (tm, m, tmp); | 2145 | matrix3x3_copy (tmp, tm); |
| 2136 | COPY_MATRIX (tmp, tm); | ||
| 2137 | 2146 | ||
| 2138 | img->width = width; | 2147 | img->width = width; |
| 2139 | img->height = height; | 2148 | img->height = height; |
| @@ -2141,7 +2150,7 @@ image_set_crop (struct image *img, double tm[3][3]) | |||
| 2141 | } | 2150 | } |
| 2142 | 2151 | ||
| 2143 | static void | 2152 | static void |
| 2144 | image_set_size (struct image *img, double tm[3][3]) | 2153 | image_set_size (struct image *img, matrix3x3 tm) |
| 2145 | { | 2154 | { |
| 2146 | #ifdef HAVE_NATIVE_TRANSFORMS | 2155 | #ifdef HAVE_NATIVE_TRANSFORMS |
| 2147 | # ifdef HAVE_IMAGEMAGICK | 2156 | # ifdef HAVE_IMAGEMAGICK |
| @@ -2160,18 +2169,12 @@ image_set_size (struct image *img, double tm[3][3]) | |||
| 2160 | compute_image_size (img->width, img->height, img->spec, &width, &height); | 2169 | compute_image_size (img->width, img->height, img->spec, &width, &height); |
| 2161 | 2170 | ||
| 2162 | # if defined (HAVE_NS) || defined (HAVE_XRENDER) | 2171 | # if defined (HAVE_NS) || defined (HAVE_XRENDER) |
| 2163 | double rm[3][3], tmp[3][3]; | 2172 | double xscale = img->width / (double) width; |
| 2164 | double xscale, yscale; | 2173 | double yscale = img->height / (double) height; |
| 2165 | |||
| 2166 | xscale = img->width / (double) width; | ||
| 2167 | yscale = img->height / (double) height; | ||
| 2168 | 2174 | ||
| 2169 | INIT_MATRIX (rm); | 2175 | matrix3x3 tmp, rm = { [0][0] = xscale, [1][1] = yscale, [2][2] = 1 }; |
| 2170 | rm[0][0] = xscale; | 2176 | matrix3x3_mult (rm, tm, tmp); |
| 2171 | rm[1][1] = yscale; | 2177 | matrix3x3_copy (tmp, tm); |
| 2172 | |||
| 2173 | MULT_MATRICES (tm, rm, tmp); | ||
| 2174 | COPY_MATRIX (tmp, tm); | ||
| 2175 | 2178 | ||
| 2176 | img->width = width; | 2179 | img->width = width; |
| 2177 | img->height = height; | 2180 | img->height = height; |
| @@ -2187,7 +2190,7 @@ image_set_size (struct image *img, double tm[3][3]) | |||
| 2187 | } | 2190 | } |
| 2188 | 2191 | ||
| 2189 | static void | 2192 | static void |
| 2190 | image_set_transform (struct frame *f, struct image *img, double matrix[3][3]) | 2193 | image_set_transform (struct frame *f, struct image *img, matrix3x3 matrix) |
| 2191 | { | 2194 | { |
| 2192 | /* TODO: Add MS Windows support. */ | 2195 | /* TODO: Add MS Windows support. */ |
| 2193 | #ifdef HAVE_NATIVE_TRANSFORMS | 2196 | #ifdef HAVE_NATIVE_TRANSFORMS |
| @@ -2273,9 +2276,7 @@ lookup_image (struct frame *f, Lisp_Object spec) | |||
| 2273 | int relief_bound; | 2276 | int relief_bound; |
| 2274 | 2277 | ||
| 2275 | #ifdef HAVE_NATIVE_TRANSFORMS | 2278 | #ifdef HAVE_NATIVE_TRANSFORMS |
| 2276 | double transform_matrix[3][3]; | 2279 | matrix3x3 transform_matrix = { [0][0] = 1, [1][1] = 1, [2][2] = 1 }; |
| 2277 | |||
| 2278 | INIT_MATRIX (transform_matrix); | ||
| 2279 | image_set_size (img, transform_matrix); | 2280 | image_set_size (img, transform_matrix); |
| 2280 | image_set_crop (img, transform_matrix); | 2281 | image_set_crop (img, transform_matrix); |
| 2281 | image_set_rotation (img, transform_matrix); | 2282 | image_set_rotation (img, transform_matrix); |