diff options
| author | Alan Third | 2019-06-16 20:10:20 +0100 |
|---|---|---|
| committer | Alan Third | 2019-06-16 20:24:53 +0100 |
| commit | cf3081d208814ea02fce33aac645abfc24f880a6 (patch) | |
| tree | 7c840344a0a6edbf6bc9ee9debcb690877e417d8 /src/image.c | |
| parent | 11b0e33462fa7ebef142953010e25728543d1be8 (diff) | |
| download | emacs-cf3081d208814ea02fce33aac645abfc24f880a6.tar.gz emacs-cf3081d208814ea02fce33aac645abfc24f880a6.zip | |
Simplify image transforms
* src/image.c: (image_set_rotation, image_set_size,
image_set_transform): Combine into image_set_transform.
(image_set_crop): Remove function.
(lookup_image): Remove calls to removed functions and remove
transform_matrix.
* test/manual/image-transforms-tests.el (test-cropping): Remove
function.
(test-transforms): Remove reference to test-cropping.
Diffstat (limited to 'src/image.c')
| -rw-r--r-- | src/image.c | 310 |
1 files changed, 98 insertions, 212 deletions
diff --git a/src/image.c b/src/image.c index a3747cfa6b7..8b571dd72d6 100644 --- a/src/image.c +++ b/src/image.c | |||
| @@ -1967,8 +1967,7 @@ compute_image_size (size_t width, size_t height, | |||
| 1967 | } | 1967 | } |
| 1968 | #endif /* HAVE_IMAGEMAGICK || HAVE_NATIVE_TRANSFORMS */ | 1968 | #endif /* HAVE_IMAGEMAGICK || HAVE_NATIVE_TRANSFORMS */ |
| 1969 | 1969 | ||
| 1970 | /* image_set_rotation, image_set_crop, image_set_size and | 1970 | /* image_set_transform uses affine transformation matrices to perform |
| 1971 | image_set_transform use affine transformation matrices to perform | ||
| 1972 | various transforms on the image. The matrix is a 2D array of | 1971 | various transforms on the image. The matrix is a 2D array of |
| 1973 | doubles. It is laid out like this: | 1972 | doubles. It is laid out like this: |
| 1974 | 1973 | ||
| @@ -2039,10 +2038,6 @@ compute_image_size (size_t width, size_t height, | |||
| 2039 | finally move the origin back to the top left of the image, which | 2038 | finally move the origin back to the top left of the image, which |
| 2040 | may now be a different corner. | 2039 | may now be a different corner. |
| 2041 | 2040 | ||
| 2042 | Cropping is easier as we just move the origin to the top left of | ||
| 2043 | where we want to crop and set the width and height accordingly. | ||
| 2044 | The matrices don’t know anything about width and height. | ||
| 2045 | |||
| 2046 | It's possible to pre-calculate the matrix multiplications and just | 2041 | It's possible to pre-calculate the matrix multiplications and just |
| 2047 | generate one transform matrix that will do everything we need in a | 2042 | 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 | 2043 | single step, but the maths for each element is much more complex |
| @@ -2070,7 +2065,7 @@ matrix3x3_mult (matrix3x3 a, matrix3x3 b, matrix3x3 result) | |||
| 2070 | } | 2065 | } |
| 2071 | 2066 | ||
| 2072 | static void | 2067 | static void |
| 2073 | image_set_rotation (struct image *img, matrix3x3 tm) | 2068 | image_set_transform (struct frame *f, struct image *img) |
| 2074 | { | 2069 | { |
| 2075 | #ifdef HAVE_NATIVE_TRANSFORMS | 2070 | #ifdef HAVE_NATIVE_TRANSFORMS |
| 2076 | # ifdef HAVE_IMAGEMAGICK | 2071 | # ifdef HAVE_IMAGEMAGICK |
| @@ -2084,217 +2079,112 @@ image_set_rotation (struct image *img, matrix3x3 tm) | |||
| 2084 | return; | 2079 | return; |
| 2085 | # endif | 2080 | # endif |
| 2086 | 2081 | ||
| 2087 | int rotation, cos_r, sin_r, width, height; | 2082 | /* This is the transformation matrix we will use through all the |
| 2083 | calculations. */ | ||
| 2084 | matrix3x3 tm = { [0][0] = 1, [1][1] = 1, [2][2] = 1 }; | ||
| 2088 | 2085 | ||
| 2086 | /* Calculate image rotation. */ | ||
| 2089 | Lisp_Object value = image_spec_value (img->spec, QCrotation, NULL); | 2087 | Lisp_Object value = image_spec_value (img->spec, QCrotation, NULL); |
| 2090 | if (! NUMBERP (value)) | 2088 | 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 | { | ||
| 2098 | rotation = XFLOAT_DATA (reduced_angle); | ||
| 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 | { | 2089 | { |
| 2124 | width = img->height; | 2090 | int rotation, cos_r, sin_r, width, height; |
| 2125 | height = img->width; | ||
| 2126 | 2091 | ||
| 2127 | cos_r = 0; | 2092 | Lisp_Object reduced_angle = Fmod (value, make_fixnum (360)); |
| 2128 | sin_r = -1; | 2093 | if (! FLOATP (reduced_angle)) |
| 2129 | } | 2094 | rotation = XFIXNUM (reduced_angle); |
| 2130 | else | 2095 | else |
| 2131 | { | 2096 | { |
| 2132 | not_a_multiple_of_90: | 2097 | rotation = XFLOAT_DATA (reduced_angle); |
| 2133 | image_error ("Native image rotation supports " | 2098 | if (rotation != XFLOAT_DATA (reduced_angle)) |
| 2134 | "only multiples of 90 degrees"); | 2099 | goto not_a_multiple_of_90; |
| 2135 | return; | 2100 | } |
| 2136 | } | ||
| 2137 | |||
| 2138 | /* Translate so (0, 0) is in the center of the image. */ | ||
| 2139 | matrix3x3 t | ||
| 2140 | = { [0][0] = 1, | ||
| 2141 | [1][1] = 1, | ||
| 2142 | [2][0] = img->width >> 1, [2][1] = img->height >> 1, [2][2] = 1 }; | ||
| 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 >> 1); | ||
| 2155 | t[2][1] = - (height >> 1); | ||
| 2156 | matrix3x3_mult (t, tmp2, tm); | ||
| 2157 | |||
| 2158 | img->width = width; | ||
| 2159 | img->height = height; | ||
| 2160 | #endif | ||
| 2161 | } | ||
| 2162 | |||
| 2163 | static void | ||
| 2164 | image_set_crop (struct image *img, matrix3x3 tm) | ||
| 2165 | { | ||
| 2166 | #ifdef HAVE_NATIVE_TRANSFORMS | ||
| 2167 | # ifdef HAVE_IMAGEMAGICK | ||
| 2168 | /* ImageMagick images are already cropped. */ | ||
| 2169 | if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick)) | ||
| 2170 | return; | ||
| 2171 | # endif | ||
| 2172 | |||
| 2173 | # if !defined USE_CAIRO && defined HAVE_XRENDER | ||
| 2174 | if (!img->picture) | ||
| 2175 | return; | ||
| 2176 | # endif | ||
| 2177 | |||
| 2178 | Lisp_Object crop = image_spec_value (img->spec, QCcrop, NULL); | ||
| 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 | 2101 | ||
| 2198 | int width = img->width; | 2102 | if (rotation != 0) |
| 2199 | if (FIXNATP (w) && XFIXNAT (w) < img->width) | 2103 | { |
| 2200 | width = XFIXNAT (w); | 2104 | if (rotation == 90) |
| 2201 | int left; | 2105 | { |
| 2202 | if (TYPE_RANGED_FIXNUMP (int, x)) | 2106 | width = img->height; |
| 2203 | { | 2107 | height = img->width; |
| 2204 | left = XFIXNUM (x); | ||
| 2205 | if (left < 0) | ||
| 2206 | left = img->width - width + left; | ||
| 2207 | } | ||
| 2208 | else | ||
| 2209 | left = (img->width - width) >> 1; | ||
| 2210 | 2108 | ||
| 2211 | int height = img->height; | 2109 | cos_r = 0; |
| 2212 | if (FIXNATP (h) && XFIXNAT (h) < img->height) | 2110 | sin_r = 1; |
| 2213 | height = XFIXNAT (h); | 2111 | } |
| 2214 | int top; | 2112 | else if (rotation == 180) |
| 2215 | if (TYPE_RANGED_FIXNUMP (int, y)) | 2113 | { |
| 2216 | { | 2114 | width = img->width; |
| 2217 | top = XFIXNUM (y); | 2115 | height = img->height; |
| 2218 | if (top < 0) | ||
| 2219 | top = img->height - height + top; | ||
| 2220 | } | ||
| 2221 | else | ||
| 2222 | top = (img->height - height) >> 1; | ||
| 2223 | 2116 | ||
| 2224 | /* Negative values operate from the right and bottom of the image | 2117 | cos_r = -1; |
| 2225 | instead of the left and top. */ | 2118 | sin_r = 0; |
| 2226 | if (left < 0) | 2119 | } |
| 2227 | { | 2120 | else if (rotation == 270) |
| 2228 | width = img->width + left; | 2121 | { |
| 2229 | left = 0; | 2122 | width = img->height; |
| 2230 | } | 2123 | height = img->width; |
| 2231 | 2124 | ||
| 2232 | if (width + left > img->width) | 2125 | cos_r = 0; |
| 2233 | width = img->width - left; | 2126 | sin_r = -1; |
| 2127 | } | ||
| 2128 | else | ||
| 2129 | { | ||
| 2130 | not_a_multiple_of_90: | ||
| 2131 | image_error ("Native image rotation supports " | ||
| 2132 | "only multiples of 90 degrees"); | ||
| 2133 | return; | ||
| 2134 | } | ||
| 2234 | 2135 | ||
| 2235 | if (top < 0) | 2136 | /* Translate so (0, 0) is in the center of the image. */ |
| 2236 | { | 2137 | matrix3x3 t |
| 2237 | height = img->height + top; | 2138 | = { [0][0] = 1, [2][0] = img->width >> 1, |
| 2238 | top = 0; | 2139 | [1][1] = 1, [2][1] = img->height >> 1, |
| 2140 | [2][2] = 1 }; | ||
| 2141 | matrix3x3 tmp; | ||
| 2142 | matrix3x3_mult (t, tm, tmp); | ||
| 2143 | |||
| 2144 | /* Rotate. */ | ||
| 2145 | matrix3x3 rot = { [0][0] = cos_r, [1][0] = sin_r, | ||
| 2146 | [0][1] = -sin_r, [1][1] = cos_r, | ||
| 2147 | [2][2] = 1 }; | ||
| 2148 | matrix3x3 tmp2; | ||
| 2149 | matrix3x3_mult (rot, tmp, tmp2); | ||
| 2150 | |||
| 2151 | /* Translate back. */ | ||
| 2152 | t[2][0] = - (width >> 1); | ||
| 2153 | t[2][1] = - (height >> 1); | ||
| 2154 | matrix3x3_mult (t, tmp2, tm); | ||
| 2155 | |||
| 2156 | img->width = width; | ||
| 2157 | img->height = height; | ||
| 2158 | } | ||
| 2239 | } | 2159 | } |
| 2240 | 2160 | ||
| 2241 | if (height + top > img->height) | 2161 | /* Calculate final image size. */ |
| 2242 | height = img->height - top; | 2162 | { |
| 2243 | 2163 | int width, height; | |
| 2244 | matrix3x3 tmp, m = { [0][0] = 1, | ||
| 2245 | [1][1] = 1, | ||
| 2246 | [2][0] = left, [2][1] = top, [2][2] = 1 }; | ||
| 2247 | matrix3x3_mult (m, tm, tmp); | ||
| 2248 | matrix3x3_copy (tmp, tm); | ||
| 2249 | |||
| 2250 | img->width = width; | ||
| 2251 | img->height = height; | ||
| 2252 | #endif | ||
| 2253 | } | ||
| 2254 | |||
| 2255 | static void | ||
| 2256 | image_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 | |||
| 2265 | # if !defined USE_CAIRO && defined HAVE_XRENDER | ||
| 2266 | if (!img->picture) | ||
| 2267 | return; | ||
| 2268 | # endif | ||
| 2269 | |||
| 2270 | int width, height; | ||
| 2271 | 2164 | ||
| 2272 | compute_image_size (img->width, img->height, img->spec, &width, &height); | 2165 | compute_image_size (img->width, img->height, img->spec, &width, &height); |
| 2273 | 2166 | ||
| 2274 | double xscale = img->width / (double) width; | 2167 | if (img->width != width || img->height != height) |
| 2275 | double yscale = img->height / (double) height; | 2168 | { |
| 2169 | double xscale = img->width / (double) width; | ||
| 2170 | double yscale = img->height / (double) height; | ||
| 2276 | 2171 | ||
| 2277 | matrix3x3 tmp, rm = { [0][0] = xscale, [1][1] = yscale, [2][2] = 1 }; | 2172 | matrix3x3 tmp, rm = { [0][0] = xscale, [1][1] = yscale, [2][2] = 1 }; |
| 2278 | matrix3x3_mult (rm, tm, tmp); | 2173 | matrix3x3_mult (rm, tm, tmp); |
| 2279 | matrix3x3_copy (tmp, tm); | 2174 | matrix3x3_copy (tmp, tm); |
| 2280 | 2175 | ||
| 2281 | img->width = width; | 2176 | img->width = width; |
| 2282 | img->height = height; | 2177 | img->height = height; |
| 2283 | #endif | 2178 | } |
| 2284 | } | 2179 | } |
| 2285 | 2180 | ||
| 2286 | static void | ||
| 2287 | image_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) | 2181 | # if defined (HAVE_NS) |
| 2292 | /* Under NS the transform is applied to the drawing surface at | 2182 | /* Under NS the transform is applied to the drawing surface at |
| 2293 | drawing time, so store it for later. */ | 2183 | drawing time, so store it for later. */ |
| 2294 | ns_image_set_transform (img->pixmap, matrix); | 2184 | ns_image_set_transform (img->pixmap, tm); |
| 2295 | # elif defined USE_CAIRO | 2185 | # elif defined USE_CAIRO |
| 2296 | cairo_matrix_t cr_matrix = {matrix[0][0], matrix[0][1], matrix[1][0], | 2186 | cairo_matrix_t cr_matrix = {tm[0][0], tm[0][1], tm[1][0], |
| 2297 | matrix[1][1], matrix[2][0], matrix[2][1]}; | 2187 | tm[1][1], tm[2][0], tm[2][1]}; |
| 2298 | cairo_pattern_t *pattern = cairo_pattern_create_rgb (0, 0, 0); | 2188 | cairo_pattern_t *pattern = cairo_pattern_create_rgb (0, 0, 0); |
| 2299 | cairo_pattern_set_matrix (pattern, &cr_matrix); | 2189 | cairo_pattern_set_matrix (pattern, &cr_matrix); |
| 2300 | /* Dummy solid color pattern just to record pattern matrix. */ | 2190 | /* Dummy solid color pattern just to record pattern matrix. */ |
| @@ -2303,15 +2193,15 @@ image_set_transform (struct frame *f, struct image *img, matrix3x3 matrix) | |||
| 2303 | if (img->picture) | 2193 | if (img->picture) |
| 2304 | { | 2194 | { |
| 2305 | XTransform tmat | 2195 | XTransform tmat |
| 2306 | = {{{XDoubleToFixed (matrix[0][0]), | 2196 | = {{{XDoubleToFixed (tm[0][0]), |
| 2307 | XDoubleToFixed (matrix[1][0]), | 2197 | XDoubleToFixed (tm[1][0]), |
| 2308 | XDoubleToFixed (matrix[2][0])}, | 2198 | XDoubleToFixed (tm[2][0])}, |
| 2309 | {XDoubleToFixed (matrix[0][1]), | 2199 | {XDoubleToFixed (tm[0][1]), |
| 2310 | XDoubleToFixed (matrix[1][1]), | 2200 | XDoubleToFixed (tm[1][1]), |
| 2311 | XDoubleToFixed (matrix[2][1])}, | 2201 | XDoubleToFixed (tm[2][1])}, |
| 2312 | {XDoubleToFixed (matrix[0][2]), | 2202 | {XDoubleToFixed (tm[0][2]), |
| 2313 | XDoubleToFixed (matrix[1][2]), | 2203 | XDoubleToFixed (tm[1][2]), |
| 2314 | XDoubleToFixed (matrix[2][2])}}}; | 2204 | XDoubleToFixed (tm[2][2])}}}; |
| 2315 | 2205 | ||
| 2316 | XRenderSetPictureFilter (FRAME_X_DISPLAY (f), img->picture, FilterBest, | 2206 | XRenderSetPictureFilter (FRAME_X_DISPLAY (f), img->picture, FilterBest, |
| 2317 | 0, 0); | 2207 | 0, 0); |
| @@ -2377,11 +2267,7 @@ lookup_image (struct frame *f, Lisp_Object spec) | |||
| 2377 | int relief_bound; | 2267 | int relief_bound; |
| 2378 | 2268 | ||
| 2379 | #ifdef HAVE_NATIVE_TRANSFORMS | 2269 | #ifdef HAVE_NATIVE_TRANSFORMS |
| 2380 | matrix3x3 transform_matrix = { [0][0] = 1, [1][1] = 1, [2][2] = 1 }; | 2270 | 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 | 2271 | #endif |
| 2386 | 2272 | ||
| 2387 | ascent = image_spec_value (spec, QCascent, NULL); | 2273 | ascent = image_spec_value (spec, QCascent, NULL); |