diff options
| author | Eli Zaretskii | 2024-05-30 17:45:33 +0300 |
|---|---|---|
| committer | Eli Zaretskii | 2024-05-30 17:45:33 +0300 |
| commit | ae7d0e86b37eabc434c48f85f56df0a221e0e7c7 (patch) | |
| tree | 6b4c65c594e47ee09f6a0b1fc6dc3856802bfe81 /src | |
| parent | 1ebb9cb93b2fefa84f18a63fb24c1ed4fcf095a7 (diff) | |
| download | emacs-ae7d0e86b37eabc434c48f85f56df0a221e0e7c7.tar.gz emacs-ae7d0e86b37eabc434c48f85f56df0a221e0e7c7.zip | |
Support built-in thumbnail creation on MS-Windows
* src/w32image.c (get_encoder_clsid, Fw32image_create_thumbnail)
(globals_of_w32image, syms_of_w32image): New functions.
* src/emacs.c (main): Call 'syms_of_w32image' and
'globals_of_w32image'.
* src/w32term.h (syms_of_w32image, globals_of_w32image): Add
prototypes.
* lisp/image/image-dired.el
(image-dired-thumbnail-display-external): Add a fallback for
MS-Windows.
* lisp/image/image-dired-external.el
(image-dired--probe-thumbnail-cmd): New function.
(image-dired--check-executable-exists): Call it to verify that
"convert" is indeed an Imagemagick program. New argument FUNC
specifies a function that can be used as an alternative to running
EXECUTABLE.
(image-dired-create-thumb-1): Don't call
'image-dired--check-executable-exists' here, ...
(image-dired-thumb-queue-run): ...call it here, with
'w32image-create-thumbnail' as the alternative function. If on
MS-Windows and no "convert" command, call
'image-dired-create-thumb-2' instead.
(image-dired-create-thumb-2): New function.
* etc/NEWS: Announce the thumbnail support.
Diffstat (limited to 'src')
| -rw-r--r-- | src/emacs.c | 4 | ||||
| -rw-r--r-- | src/w32image.c | 201 | ||||
| -rw-r--r-- | src/w32term.h | 3 |
3 files changed, 208 insertions, 0 deletions
diff --git a/src/emacs.c b/src/emacs.c index f122955884e..036bc1864e6 100644 --- a/src/emacs.c +++ b/src/emacs.c | |||
| @@ -2358,6 +2358,9 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem | |||
| 2358 | #ifdef HAVE_WINDOW_SYSTEM | 2358 | #ifdef HAVE_WINDOW_SYSTEM |
| 2359 | syms_of_fringe (); | 2359 | syms_of_fringe (); |
| 2360 | syms_of_image (); | 2360 | syms_of_image (); |
| 2361 | #ifdef HAVE_NTGUI | ||
| 2362 | syms_of_w32image (); | ||
| 2363 | #endif /* HAVE_NTGUI */ | ||
| 2361 | #endif /* HAVE_WINDOW_SYSTEM */ | 2364 | #endif /* HAVE_WINDOW_SYSTEM */ |
| 2362 | #ifdef HAVE_X_WINDOWS | 2365 | #ifdef HAVE_X_WINDOWS |
| 2363 | syms_of_xterm (); | 2366 | syms_of_xterm (); |
| @@ -2495,6 +2498,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem | |||
| 2495 | globals_of_w32font (); | 2498 | globals_of_w32font (); |
| 2496 | globals_of_w32fns (); | 2499 | globals_of_w32fns (); |
| 2497 | globals_of_w32menu (); | 2500 | globals_of_w32menu (); |
| 2501 | globals_of_w32image (); | ||
| 2498 | #endif /* HAVE_NTGUI */ | 2502 | #endif /* HAVE_NTGUI */ |
| 2499 | 2503 | ||
| 2500 | #if defined WINDOWSNT || defined HAVE_NTGUI | 2504 | #if defined WINDOWSNT || defined HAVE_NTGUI |
diff --git a/src/w32image.c b/src/w32image.c index 9010338a267..c81c3f0d3d1 100644 --- a/src/w32image.c +++ b/src/w32image.c | |||
| @@ -65,6 +65,16 @@ typedef GpStatus (WINGDIPAPI *GdipCreateHBITMAPFromBitmap_Proc) | |||
| 65 | typedef GpStatus (WINGDIPAPI *GdipDisposeImage_Proc) (GpImage *); | 65 | typedef GpStatus (WINGDIPAPI *GdipDisposeImage_Proc) (GpImage *); |
| 66 | typedef GpStatus (WINGDIPAPI *GdipGetImageHeight_Proc) (GpImage *, UINT *); | 66 | typedef GpStatus (WINGDIPAPI *GdipGetImageHeight_Proc) (GpImage *, UINT *); |
| 67 | typedef GpStatus (WINGDIPAPI *GdipGetImageWidth_Proc) (GpImage *, UINT *); | 67 | typedef GpStatus (WINGDIPAPI *GdipGetImageWidth_Proc) (GpImage *, UINT *); |
| 68 | typedef GpStatus (WINGDIPAPI *GdipGetImageEncodersSize_Proc) (UINT *, UINT *); | ||
| 69 | typedef GpStatus (WINGDIPAPI *GdipGetImageEncoders_Proc) | ||
| 70 | (UINT, UINT, ImageCodecInfo *); | ||
| 71 | typedef GpStatus (WINGDIPAPI *GdipLoadImageFromFile_Proc) | ||
| 72 | (GDIPCONST WCHAR *,GpImage **); | ||
| 73 | typedef GpStatus (WINGDIPAPI *GdipGetImageThumbnail_Proc) | ||
| 74 | (GpImage *, UINT, UINT, GpImage**, GetThumbnailImageAbort, VOID *); | ||
| 75 | typedef GpStatus (WINGDIPAPI *GdipSaveImageToFile_Proc) | ||
| 76 | (GpImage *, GDIPCONST WCHAR *, GDIPCONST CLSID *, | ||
| 77 | GDIPCONST EncoderParameters *); | ||
| 68 | 78 | ||
| 69 | GdiplusStartup_Proc fn_GdiplusStartup; | 79 | GdiplusStartup_Proc fn_GdiplusStartup; |
| 70 | GdiplusShutdown_Proc fn_GdiplusShutdown; | 80 | GdiplusShutdown_Proc fn_GdiplusShutdown; |
| @@ -81,6 +91,11 @@ GdipCreateHBITMAPFromBitmap_Proc fn_GdipCreateHBITMAPFromBitmap; | |||
| 81 | GdipDisposeImage_Proc fn_GdipDisposeImage; | 91 | GdipDisposeImage_Proc fn_GdipDisposeImage; |
| 82 | GdipGetImageHeight_Proc fn_GdipGetImageHeight; | 92 | GdipGetImageHeight_Proc fn_GdipGetImageHeight; |
| 83 | GdipGetImageWidth_Proc fn_GdipGetImageWidth; | 93 | GdipGetImageWidth_Proc fn_GdipGetImageWidth; |
| 94 | GdipGetImageEncodersSize_Proc fn_GdipGetImageEncodersSize; | ||
| 95 | GdipGetImageEncoders_Proc fn_GdipGetImageEncoders; | ||
| 96 | GdipLoadImageFromFile_Proc fn_GdipLoadImageFromFile; | ||
| 97 | GdipGetImageThumbnail_Proc fn_GdipGetImageThumbnail; | ||
| 98 | GdipSaveImageToFile_Proc fn_GdipSaveImageToFile; | ||
| 84 | 99 | ||
| 85 | static bool | 100 | static bool |
| 86 | gdiplus_init (void) | 101 | gdiplus_init (void) |
| @@ -161,6 +176,26 @@ gdiplus_init (void) | |||
| 161 | if (!fn_SHCreateMemStream) | 176 | if (!fn_SHCreateMemStream) |
| 162 | return false; | 177 | return false; |
| 163 | } | 178 | } |
| 179 | fn_GdipGetImageEncodersSize = (GdipGetImageEncodersSize_Proc) | ||
| 180 | get_proc_addr (gdiplus_lib, "GdipGetImageEncodersSize"); | ||
| 181 | if (!fn_GdipGetImageEncodersSize) | ||
| 182 | return false; | ||
| 183 | fn_GdipGetImageEncoders = (GdipGetImageEncoders_Proc) | ||
| 184 | get_proc_addr (gdiplus_lib, "GdipGetImageEncoders"); | ||
| 185 | if (!fn_GdipGetImageEncoders) | ||
| 186 | return false; | ||
| 187 | fn_GdipLoadImageFromFile = (GdipLoadImageFromFile_Proc) | ||
| 188 | get_proc_addr (gdiplus_lib, "GdipLoadImageFromFile"); | ||
| 189 | if (!fn_GdipLoadImageFromFile) | ||
| 190 | return false; | ||
| 191 | fn_GdipGetImageThumbnail = (GdipGetImageThumbnail_Proc) | ||
| 192 | get_proc_addr (gdiplus_lib, "GdipGetImageThumbnail"); | ||
| 193 | if (!fn_GdipGetImageThumbnail) | ||
| 194 | return false; | ||
| 195 | fn_GdipSaveImageToFile = (GdipSaveImageToFile_Proc) | ||
| 196 | get_proc_addr (gdiplus_lib, "GdipSaveImageToFile"); | ||
| 197 | if (!fn_GdipSaveImageToFile) | ||
| 198 | return false; | ||
| 164 | 199 | ||
| 165 | return true; | 200 | return true; |
| 166 | } | 201 | } |
| @@ -180,6 +215,11 @@ gdiplus_init (void) | |||
| 180 | # undef GdipDisposeImage | 215 | # undef GdipDisposeImage |
| 181 | # undef GdipGetImageHeight | 216 | # undef GdipGetImageHeight |
| 182 | # undef GdipGetImageWidth | 217 | # undef GdipGetImageWidth |
| 218 | # undef GdipGetImageEncodersSize | ||
| 219 | # undef GdipGetImageEncoders | ||
| 220 | # undef GdipLoadImageFromFile | ||
| 221 | # undef GdipGetImageThumbnail | ||
| 222 | # undef GdipSaveImageToFile | ||
| 183 | 223 | ||
| 184 | # define GdiplusStartup fn_GdiplusStartup | 224 | # define GdiplusStartup fn_GdiplusStartup |
| 185 | # define GdiplusShutdown fn_GdiplusShutdown | 225 | # define GdiplusShutdown fn_GdiplusShutdown |
| @@ -196,6 +236,11 @@ gdiplus_init (void) | |||
| 196 | # define GdipDisposeImage fn_GdipDisposeImage | 236 | # define GdipDisposeImage fn_GdipDisposeImage |
| 197 | # define GdipGetImageHeight fn_GdipGetImageHeight | 237 | # define GdipGetImageHeight fn_GdipGetImageHeight |
| 198 | # define GdipGetImageWidth fn_GdipGetImageWidth | 238 | # define GdipGetImageWidth fn_GdipGetImageWidth |
| 239 | # define GdipGetImageEncodersSize fn_GdipGetImageEncodersSize | ||
| 240 | # define GdipGetImageEncoders fn_GdipGetImageEncoders | ||
| 241 | # define GdipLoadImageFromFile fn_GdipLoadImageFromFile | ||
| 242 | # define GdipGetImageThumbnail fn_GdipGetImageThumbnail | ||
| 243 | # define GdipSaveImageToFile fn_GdipSaveImageToFile | ||
| 199 | 244 | ||
| 200 | #endif /* WINDOWSNT */ | 245 | #endif /* WINDOWSNT */ |
| 201 | 246 | ||
| @@ -476,3 +521,159 @@ w32_load_image (struct frame *f, struct image *img, | |||
| 476 | } | 521 | } |
| 477 | return 1; | 522 | return 1; |
| 478 | } | 523 | } |
| 524 | |||
| 525 | struct cached_encoder { | ||
| 526 | int num; | ||
| 527 | char *type; | ||
| 528 | CLSID clsid; | ||
| 529 | }; | ||
| 530 | |||
| 531 | static struct cached_encoder last_encoder; | ||
| 532 | |||
| 533 | struct thumb_type_data { | ||
| 534 | const char *ext; | ||
| 535 | const wchar_t *mime; | ||
| 536 | }; | ||
| 537 | |||
| 538 | static struct thumb_type_data thumb_types [] = | ||
| 539 | { | ||
| 540 | /* jpg and png are at the front because 'image-dired-thumb-name' | ||
| 541 | uses them in most cases. */ | ||
| 542 | {"jpg", L"image/jpeg"}, | ||
| 543 | {"png", L"image/png"}, | ||
| 544 | {"bmp", L"image/bmp"}, | ||
| 545 | {"jpeg", L"image/jpeg"}, | ||
| 546 | {"gif", L"image/gif"}, | ||
| 547 | {"tiff", L"image/tiff"}, | ||
| 548 | {NULL, NULL} | ||
| 549 | }; | ||
| 550 | |||
| 551 | |||
| 552 | static int | ||
| 553 | get_encoder_clsid(const char *type, CLSID *clsid) | ||
| 554 | { | ||
| 555 | /* A simple cache based on the assumptions that many thumbnails will | ||
| 556 | be generated using the same TYPE. */ | ||
| 557 | if (last_encoder.type && stricmp (type, last_encoder.type) == 0) | ||
| 558 | { | ||
| 559 | *clsid = last_encoder.clsid; | ||
| 560 | return last_encoder.num; | ||
| 561 | } | ||
| 562 | |||
| 563 | const wchar_t *format = NULL; | ||
| 564 | struct thumb_type_data *tp = thumb_types; | ||
| 565 | for ( ; tp->ext; tp++) | ||
| 566 | { | ||
| 567 | if (stricmp (type, tp->ext) == 0) | ||
| 568 | { | ||
| 569 | format = tp->mime; | ||
| 570 | break; | ||
| 571 | } | ||
| 572 | } | ||
| 573 | if (!format) | ||
| 574 | return -1; | ||
| 575 | |||
| 576 | unsigned num = 0; | ||
| 577 | unsigned size = 0; | ||
| 578 | ImageCodecInfo *image_codec_info = NULL; | ||
| 579 | |||
| 580 | GdipGetImageEncodersSize (&num, &size); | ||
| 581 | if(size == 0) | ||
| 582 | return -1; | ||
| 583 | |||
| 584 | image_codec_info = xmalloc (size); | ||
| 585 | GdipGetImageEncoders (num, size, image_codec_info); | ||
| 586 | |||
| 587 | for (int j = 0; j < num; ++j) | ||
| 588 | { | ||
| 589 | if (wcscmp (image_codec_info[j].MimeType, format) == 0 ) | ||
| 590 | { | ||
| 591 | if (last_encoder.type) | ||
| 592 | xfree (last_encoder.type); | ||
| 593 | last_encoder.type = xstrdup (tp->ext); | ||
| 594 | last_encoder.clsid = image_codec_info[j].Clsid; | ||
| 595 | last_encoder.num = j; | ||
| 596 | *clsid = image_codec_info[j].Clsid; | ||
| 597 | xfree (image_codec_info); | ||
| 598 | return j; | ||
| 599 | } | ||
| 600 | } | ||
| 601 | |||
| 602 | xfree (image_codec_info); | ||
| 603 | return -1; | ||
| 604 | } | ||
| 605 | |||
| 606 | DEFUN ("w32image-create-thumbnail", Fw32image_create_thumbnail, | ||
| 607 | Sw32image_create_thumbnail, 5, 5, 0, | ||
| 608 | doc: /* Create a HEIGHT by WIDTH thumnail file THUMB-FILE for image INPUT-FILE. | ||
| 609 | TYPE is the image type to use for the thumbnail file, a string. It is | ||
| 610 | usually identical to the file-name extension of THUMB-FILE, but without | ||
| 611 | the leading period, and both "jpeg" and "jpg" can be used for JPEG. | ||
| 612 | TYPE is matched case-insensitively against supported types. Currently, | ||
| 613 | the supported TYPEs are BMP, JPEG, GIF, TIFF, and PNG; any other type | ||
| 614 | will cause the function to fail. | ||
| 615 | Return non-nil if thumbnail creation succeeds, nil otherwise. */) | ||
| 616 | (Lisp_Object input_file, Lisp_Object thumb_file, Lisp_Object type, | ||
| 617 | Lisp_Object height, Lisp_Object width) | ||
| 618 | { | ||
| 619 | /* Sanity checks. */ | ||
| 620 | CHECK_STRING (input_file); | ||
| 621 | CHECK_STRING (thumb_file); | ||
| 622 | CHECK_STRING (type); | ||
| 623 | CHECK_FIXNAT (height); | ||
| 624 | CHECK_FIXNAT (width); | ||
| 625 | |||
| 626 | if (!gdiplus_started) | ||
| 627 | { | ||
| 628 | if (!gdiplus_startup ()) | ||
| 629 | return Qnil; | ||
| 630 | } | ||
| 631 | |||
| 632 | /* Create an image by reading from INPUT_FILE. */ | ||
| 633 | wchar_t input_file_w[MAX_PATH]; | ||
| 634 | input_file = ENCODE_FILE (Fexpand_file_name (input_file, Qnil)); | ||
| 635 | unixtodos_filename (SSDATA (input_file)); | ||
| 636 | filename_to_utf16 (SSDATA (input_file), input_file_w); | ||
| 637 | GpImage *file_image; | ||
| 638 | GpStatus status = GdipLoadImageFromFile (input_file_w, &file_image); | ||
| 639 | |||
| 640 | if (status == Ok) | ||
| 641 | { | ||
| 642 | /* Create a thumbnail for the image. */ | ||
| 643 | GpImage *thumb_image; | ||
| 644 | status = GdipGetImageThumbnail (file_image, | ||
| 645 | XFIXNAT (width), XFIXNAT (height), | ||
| 646 | &thumb_image, NULL, NULL); | ||
| 647 | GdipDisposeImage (file_image); | ||
| 648 | CLSID thumb_clsid; | ||
| 649 | if (status == Ok | ||
| 650 | /* Get the GUID of the TYPE's encoder. */ | ||
| 651 | && get_encoder_clsid (SSDATA (type), &thumb_clsid) >= 0) | ||
| 652 | { | ||
| 653 | /* Save the thumbnail image to a file of specified TYPE. */ | ||
| 654 | wchar_t thumb_file_w[MAX_PATH]; | ||
| 655 | thumb_file = ENCODE_FILE (Fexpand_file_name (thumb_file, Qnil)); | ||
| 656 | unixtodos_filename (SSDATA (thumb_file)); | ||
| 657 | filename_to_utf16 (SSDATA (thumb_file), thumb_file_w); | ||
| 658 | status = GdipSaveImageToFile (thumb_image, thumb_file_w, | ||
| 659 | &thumb_clsid, NULL); | ||
| 660 | GdipDisposeImage (thumb_image); | ||
| 661 | } | ||
| 662 | else if (status == Ok) /* no valid encoder */ | ||
| 663 | status = InvalidParameter; | ||
| 664 | } | ||
| 665 | return (status == Ok) ? Qt : Qnil; | ||
| 666 | } | ||
| 667 | |||
| 668 | void | ||
| 669 | syms_of_w32image (void) | ||
| 670 | { | ||
| 671 | defsubr (&Sw32image_create_thumbnail); | ||
| 672 | } | ||
| 673 | |||
| 674 | void | ||
| 675 | globals_of_w32image (void) | ||
| 676 | { | ||
| 677 | /* This is only needed in an unexec build. */ | ||
| 678 | memset (&last_encoder, 0, sizeof last_encoder); | ||
| 679 | } | ||
diff --git a/src/w32term.h b/src/w32term.h index 3120c8bd71f..a19be1a9e6a 100644 --- a/src/w32term.h +++ b/src/w32term.h | |||
| @@ -909,6 +909,9 @@ extern void globals_of_w32menu (void); | |||
| 909 | extern void globals_of_w32fns (void); | 909 | extern void globals_of_w32fns (void); |
| 910 | extern void globals_of_w32notify (void); | 910 | extern void globals_of_w32notify (void); |
| 911 | 911 | ||
| 912 | extern void syms_of_w32image (void); | ||
| 913 | extern void globals_of_w32image (void); | ||
| 914 | |||
| 912 | extern void w32_init_main_thread (void); | 915 | extern void w32_init_main_thread (void); |
| 913 | 916 | ||
| 914 | #ifdef CYGWIN | 917 | #ifdef CYGWIN |