diff options
| author | Alan Third | 2021-03-09 18:05:10 +0000 |
|---|---|---|
| committer | Alan Third | 2021-03-10 21:40:42 +0000 |
| commit | c93447eac6f801d7ff97ed6dad368dc49d55cc46 (patch) | |
| tree | 4643e59abe2e6ca836250f65b9dd5dcc75058f8e | |
| parent | d07ed6dfee9338b0d715f8181703252c99e5133a (diff) | |
| download | emacs-c93447eac6f801d7ff97ed6dad368dc49d55cc46.tar.gz emacs-c93447eac6f801d7ff97ed6dad368dc49d55cc46.zip | |
Enable selectable image smoothing (bug#38394)
* lisp/doc-view.el (doc-view-insert-image): Always use smoothing in
docview.
* lisp/image-mode.el (image-transform-smoothing): New variable.
(image-mode-map): Add smoothing binding.
(image-transform-properties): Apply smoothing when requested.
(image-transform-set-smoothing): New function.
(image-transform-reset): Reset smoothing.
* src/image.c (image_set_transform): Use new :transform-smoothing
attribute.
(syms_of_image): Add :transform-smoothing attribute.
* doc/lispref/display.texi (Image Descriptors): Document new
:transform-smoothing property.
| -rw-r--r-- | doc/lispref/display.texi | 11 | ||||
| -rw-r--r-- | etc/NEWS | 10 | ||||
| -rw-r--r-- | lisp/doc-view.el | 2 | ||||
| -rw-r--r-- | lisp/image-mode.el | 20 | ||||
| -rw-r--r-- | src/image.c | 16 |
5 files changed, 52 insertions, 7 deletions
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index 131ad2d9c87..3d91ed27642 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi | |||
| @@ -5392,6 +5392,17 @@ are supported, unless the image type is @code{imagemagick}. Positive | |||
| 5392 | values rotate clockwise, negative values counter-clockwise. Rotation | 5392 | values rotate clockwise, negative values counter-clockwise. Rotation |
| 5393 | is performed after scaling and cropping. | 5393 | is performed after scaling and cropping. |
| 5394 | 5394 | ||
| 5395 | @item :transform-smoothing @var{smooth} | ||
| 5396 | When @code{t} any image transform will have smoothing applied, and if | ||
| 5397 | @code{nil} no smoothing will be applied. The exact algorithm used | ||
| 5398 | will be platform dependent, but should be equivalent to bilinear | ||
| 5399 | filtering. Disabling smoothing will use a nearest neighbour | ||
| 5400 | algorithm. | ||
| 5401 | |||
| 5402 | The default, if this property is not specified, will be for | ||
| 5403 | down-scaling to apply smoothing, and up-scaling to not apply | ||
| 5404 | smoothing. | ||
| 5405 | |||
| 5395 | @item :index @var{frame} | 5406 | @item :index @var{frame} |
| 5396 | @xref{Multi-Frame Images}. | 5407 | @xref{Multi-Frame Images}. |
| 5397 | 5408 | ||
| @@ -1473,6 +1473,16 @@ To load images with the default frame colors use the ':foreground' and | |||
| 1473 | This change only affects image types that support foreground and | 1473 | This change only affects image types that support foreground and |
| 1474 | background colors or transparency, such as xbm, pbm, svg, png and gif. | 1474 | background colors or transparency, such as xbm, pbm, svg, png and gif. |
| 1475 | 1475 | ||
| 1476 | +++ | ||
| 1477 | *** Image smoothing can now be explicitly enabled or disabled. | ||
| 1478 | Smoothing applies a bilinear filter while scaling or rotating an image | ||
| 1479 | to prevent aliasing and other unwanted effects. The new image | ||
| 1480 | property ':transform-smoothing' can be set to t to enable smoothing | ||
| 1481 | and nil to disable smoothing. | ||
| 1482 | |||
| 1483 | The default behaviour of smoothing on down-scaling and not smoothing | ||
| 1484 | on up-scaling remains unchanged. | ||
| 1485 | |||
| 1476 | ** EWW | 1486 | ** EWW |
| 1477 | 1487 | ||
| 1478 | +++ | 1488 | +++ |
diff --git a/lisp/doc-view.el b/lisp/doc-view.el index f6fcfae453e..cef09009d95 100644 --- a/lisp/doc-view.el +++ b/lisp/doc-view.el | |||
| @@ -1439,6 +1439,8 @@ ARGS is a list of image descriptors." | |||
| 1439 | (apply #'create-image file doc-view--image-type nil args) | 1439 | (apply #'create-image file doc-view--image-type nil args) |
| 1440 | (unless (member :width args) | 1440 | (unless (member :width args) |
| 1441 | (setq args `(,@args :width ,doc-view-image-width))) | 1441 | (setq args `(,@args :width ,doc-view-image-width))) |
| 1442 | (unless (member :transform-smoothing args) | ||
| 1443 | (setq args `(,@args :transform-smoothing t))) | ||
| 1442 | (apply #'create-image file doc-view--image-type nil args)))) | 1444 | (apply #'create-image file doc-view--image-type nil args)))) |
| 1443 | (slice (doc-view-current-slice)) | 1445 | (slice (doc-view-current-slice)) |
| 1444 | (img-width (and image (car (image-size image)))) | 1446 | (img-width (and image (car (image-size image)))) |
diff --git a/lisp/image-mode.el b/lisp/image-mode.el index 7384abf3b23..8b61aa7e73f 100644 --- a/lisp/image-mode.el +++ b/lisp/image-mode.el | |||
| @@ -95,6 +95,9 @@ Its value should be one of the following: | |||
| 95 | (defvar-local image-transform-rotation 0.0 | 95 | (defvar-local image-transform-rotation 0.0 |
| 96 | "Rotation angle for the image in the current Image mode buffer.") | 96 | "Rotation angle for the image in the current Image mode buffer.") |
| 97 | 97 | ||
| 98 | (defvar-local image-transform-smoothing nil | ||
| 99 | "Whether to use transform smoothing.") | ||
| 100 | |||
| 98 | (defvar image-transform-right-angle-fudge 0.0001 | 101 | (defvar image-transform-right-angle-fudge 0.0001 |
| 99 | "Snap distance to a multiple of a right angle. | 102 | "Snap distance to a multiple of a right angle. |
| 100 | There's no deep theory behind the default value, it should just | 103 | There's no deep theory behind the default value, it should just |
| @@ -457,6 +460,7 @@ call." | |||
| 457 | (define-key map "sb" 'image-transform-fit-both) | 460 | (define-key map "sb" 'image-transform-fit-both) |
| 458 | (define-key map "ss" 'image-transform-set-scale) | 461 | (define-key map "ss" 'image-transform-set-scale) |
| 459 | (define-key map "sr" 'image-transform-set-rotation) | 462 | (define-key map "sr" 'image-transform-set-rotation) |
| 463 | (define-key map "sm" 'image-transform-set-smoothing) | ||
| 460 | (define-key map "so" 'image-transform-original) | 464 | (define-key map "so" 'image-transform-original) |
| 461 | (define-key map "s0" 'image-transform-reset) | 465 | (define-key map "s0" 'image-transform-reset) |
| 462 | 466 | ||
| @@ -523,6 +527,8 @@ call." | |||
| 523 | :help "Rotate the image"] | 527 | :help "Rotate the image"] |
| 524 | ["Set Rotation..." image-transform-set-rotation | 528 | ["Set Rotation..." image-transform-set-rotation |
| 525 | :help "Set rotation angle of the image"] | 529 | :help "Set rotation angle of the image"] |
| 530 | ["Set Smoothing..." image-transform-set-smoothing | ||
| 531 | :help "Toggle smoothing"] | ||
| 526 | ["Original Size" image-transform-original | 532 | ["Original Size" image-transform-original |
| 527 | :help "Reset image to actual size"] | 533 | :help "Reset image to actual size"] |
| 528 | ["Reset to Default Size" image-transform-reset | 534 | ["Reset to Default Size" image-transform-reset |
| @@ -1474,7 +1480,10 @@ return value is suitable for appending to an image spec." | |||
| 1474 | ,@(when (cdr resized) | 1480 | ,@(when (cdr resized) |
| 1475 | (list :height (cdr resized))) | 1481 | (list :height (cdr resized))) |
| 1476 | ,@(unless (= 0.0 image-transform-rotation) | 1482 | ,@(unless (= 0.0 image-transform-rotation) |
| 1477 | (list :rotation image-transform-rotation)))))) | 1483 | (list :rotation image-transform-rotation)) |
| 1484 | ,@(when image-transform-smoothing | ||
| 1485 | (list :transform-smoothing | ||
| 1486 | (string= image-transform-smoothing "smooth"))))))) | ||
| 1478 | 1487 | ||
| 1479 | (defun image-transform-set-scale (scale) | 1488 | (defun image-transform-set-scale (scale) |
| 1480 | "Prompt for a number, and resize the current image by that amount." | 1489 | "Prompt for a number, and resize the current image by that amount." |
| @@ -1507,6 +1516,12 @@ ROTATION should be in degrees." | |||
| 1507 | (setq image-transform-rotation (float (mod rotation 360))) | 1516 | (setq image-transform-rotation (float (mod rotation 360))) |
| 1508 | (image-toggle-display-image)) | 1517 | (image-toggle-display-image)) |
| 1509 | 1518 | ||
| 1519 | (defun image-transform-set-smoothing (smoothing) | ||
| 1520 | (interactive (list (completing-read "Smoothing: " | ||
| 1521 | '("none" "smooth") nil t))) | ||
| 1522 | (setq image-transform-smoothing smoothing) | ||
| 1523 | (image-toggle-display-image)) | ||
| 1524 | |||
| 1510 | (defun image-transform-original () | 1525 | (defun image-transform-original () |
| 1511 | "Display the current image with the original (actual) size and rotation." | 1526 | "Display the current image with the original (actual) size and rotation." |
| 1512 | (interactive) | 1527 | (interactive) |
| @@ -1519,7 +1534,8 @@ ROTATION should be in degrees." | |||
| 1519 | (interactive) | 1534 | (interactive) |
| 1520 | (setq image-transform-resize image-auto-resize | 1535 | (setq image-transform-resize image-auto-resize |
| 1521 | image-transform-rotation 0.0 | 1536 | image-transform-rotation 0.0 |
| 1522 | image-transform-scale 1) | 1537 | image-transform-scale 1 |
| 1538 | image-transform-smoothing nil) | ||
| 1523 | (image-toggle-display-image)) | 1539 | (image-toggle-display-image)) |
| 1524 | 1540 | ||
| 1525 | (provide 'image-mode) | 1541 | (provide 'image-mode) |
diff --git a/src/image.c b/src/image.c index 8137dbea8d7..95ae573354d 100644 --- a/src/image.c +++ b/src/image.c | |||
| @@ -2230,7 +2230,12 @@ image_set_transform (struct frame *f, struct image *img) | |||
| 2230 | operations to use a blended filter, to avoid aliasing and the like. | 2230 | operations to use a blended filter, to avoid aliasing and the like. |
| 2231 | 2231 | ||
| 2232 | TODO: implement for Windows. */ | 2232 | TODO: implement for Windows. */ |
| 2233 | bool scale_down = (width < img->width) || (height < img->height); | 2233 | bool smoothing; |
| 2234 | Lisp_Object s = image_spec_value (img->spec, QCtransform_smoothing, NULL); | ||
| 2235 | if (!s) | ||
| 2236 | smoothing = (width < img->width) || (height < img->height); | ||
| 2237 | else | ||
| 2238 | smoothing = !NILP (s); | ||
| 2234 | # endif | 2239 | # endif |
| 2235 | 2240 | ||
| 2236 | /* Perform scale transformation. */ | 2241 | /* Perform scale transformation. */ |
| @@ -2344,13 +2349,13 @@ image_set_transform (struct frame *f, struct image *img) | |||
| 2344 | /* Under NS the transform is applied to the drawing surface at | 2349 | /* Under NS the transform is applied to the drawing surface at |
| 2345 | drawing time, so store it for later. */ | 2350 | drawing time, so store it for later. */ |
| 2346 | ns_image_set_transform (img->pixmap, matrix); | 2351 | ns_image_set_transform (img->pixmap, matrix); |
| 2347 | ns_image_set_smoothing (img->pixmap, scale_down); | 2352 | ns_image_set_smoothing (img->pixmap, smoothing); |
| 2348 | # elif defined USE_CAIRO | 2353 | # elif defined USE_CAIRO |
| 2349 | cairo_matrix_t cr_matrix = {matrix[0][0], matrix[0][1], matrix[1][0], | 2354 | cairo_matrix_t cr_matrix = {matrix[0][0], matrix[0][1], matrix[1][0], |
| 2350 | matrix[1][1], matrix[2][0], matrix[2][1]}; | 2355 | matrix[1][1], matrix[2][0], matrix[2][1]}; |
| 2351 | cairo_pattern_t *pattern = cairo_pattern_create_rgb (0, 0, 0); | 2356 | cairo_pattern_t *pattern = cairo_pattern_create_rgb (0, 0, 0); |
| 2352 | cairo_pattern_set_matrix (pattern, &cr_matrix); | 2357 | cairo_pattern_set_matrix (pattern, &cr_matrix); |
| 2353 | cairo_pattern_set_filter (pattern, scale_down | 2358 | cairo_pattern_set_filter (pattern, smoothing |
| 2354 | ? CAIRO_FILTER_BEST : CAIRO_FILTER_NEAREST); | 2359 | ? CAIRO_FILTER_BEST : CAIRO_FILTER_NEAREST); |
| 2355 | /* Dummy solid color pattern just to record pattern matrix. */ | 2360 | /* Dummy solid color pattern just to record pattern matrix. */ |
| 2356 | img->cr_data = pattern; | 2361 | img->cr_data = pattern; |
| @@ -2369,13 +2374,13 @@ image_set_transform (struct frame *f, struct image *img) | |||
| 2369 | XDoubleToFixed (matrix[2][2])}}}; | 2374 | XDoubleToFixed (matrix[2][2])}}}; |
| 2370 | 2375 | ||
| 2371 | XRenderSetPictureFilter (FRAME_X_DISPLAY (f), img->picture, | 2376 | XRenderSetPictureFilter (FRAME_X_DISPLAY (f), img->picture, |
| 2372 | scale_down ? FilterBest : FilterNearest, 0, 0); | 2377 | smoothing ? FilterBest : FilterNearest, 0, 0); |
| 2373 | XRenderSetPictureTransform (FRAME_X_DISPLAY (f), img->picture, &tmat); | 2378 | XRenderSetPictureTransform (FRAME_X_DISPLAY (f), img->picture, &tmat); |
| 2374 | 2379 | ||
| 2375 | if (img->mask_picture) | 2380 | if (img->mask_picture) |
| 2376 | { | 2381 | { |
| 2377 | XRenderSetPictureFilter (FRAME_X_DISPLAY (f), img->mask_picture, | 2382 | XRenderSetPictureFilter (FRAME_X_DISPLAY (f), img->mask_picture, |
| 2378 | scale_down ? FilterBest : FilterNearest, 0, 0); | 2383 | smoothing ? FilterBest : FilterNearest, 0, 0); |
| 2379 | XRenderSetPictureTransform (FRAME_X_DISPLAY (f), img->mask_picture, | 2384 | XRenderSetPictureTransform (FRAME_X_DISPLAY (f), img->mask_picture, |
| 2380 | &tmat); | 2385 | &tmat); |
| 2381 | } | 2386 | } |
| @@ -10693,6 +10698,7 @@ non-numeric, there is no explicit limit on the size of images. */); | |||
| 10693 | DEFSYM (QCrotation, ":rotation"); | 10698 | DEFSYM (QCrotation, ":rotation"); |
| 10694 | DEFSYM (QCmatrix, ":matrix"); | 10699 | DEFSYM (QCmatrix, ":matrix"); |
| 10695 | DEFSYM (QCscale, ":scale"); | 10700 | DEFSYM (QCscale, ":scale"); |
| 10701 | DEFSYM (QCtransform_smoothing, ":transform-smoothing"); | ||
| 10696 | DEFSYM (QCcolor_adjustment, ":color-adjustment"); | 10702 | DEFSYM (QCcolor_adjustment, ":color-adjustment"); |
| 10697 | DEFSYM (QCmask, ":mask"); | 10703 | DEFSYM (QCmask, ":mask"); |
| 10698 | 10704 | ||