aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorEli Zaretskii2024-05-30 17:45:33 +0300
committerEli Zaretskii2024-05-30 17:45:33 +0300
commitae7d0e86b37eabc434c48f85f56df0a221e0e7c7 (patch)
tree6b4c65c594e47ee09f6a0b1fc6dc3856802bfe81 /src
parent1ebb9cb93b2fefa84f18a63fb24c1ed4fcf095a7 (diff)
downloademacs-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.c4
-rw-r--r--src/w32image.c201
-rw-r--r--src/w32term.h3
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)
65typedef GpStatus (WINGDIPAPI *GdipDisposeImage_Proc) (GpImage *); 65typedef GpStatus (WINGDIPAPI *GdipDisposeImage_Proc) (GpImage *);
66typedef GpStatus (WINGDIPAPI *GdipGetImageHeight_Proc) (GpImage *, UINT *); 66typedef GpStatus (WINGDIPAPI *GdipGetImageHeight_Proc) (GpImage *, UINT *);
67typedef GpStatus (WINGDIPAPI *GdipGetImageWidth_Proc) (GpImage *, UINT *); 67typedef GpStatus (WINGDIPAPI *GdipGetImageWidth_Proc) (GpImage *, UINT *);
68typedef GpStatus (WINGDIPAPI *GdipGetImageEncodersSize_Proc) (UINT *, UINT *);
69typedef GpStatus (WINGDIPAPI *GdipGetImageEncoders_Proc)
70 (UINT, UINT, ImageCodecInfo *);
71typedef GpStatus (WINGDIPAPI *GdipLoadImageFromFile_Proc)
72 (GDIPCONST WCHAR *,GpImage **);
73typedef GpStatus (WINGDIPAPI *GdipGetImageThumbnail_Proc)
74 (GpImage *, UINT, UINT, GpImage**, GetThumbnailImageAbort, VOID *);
75typedef GpStatus (WINGDIPAPI *GdipSaveImageToFile_Proc)
76 (GpImage *, GDIPCONST WCHAR *, GDIPCONST CLSID *,
77 GDIPCONST EncoderParameters *);
68 78
69GdiplusStartup_Proc fn_GdiplusStartup; 79GdiplusStartup_Proc fn_GdiplusStartup;
70GdiplusShutdown_Proc fn_GdiplusShutdown; 80GdiplusShutdown_Proc fn_GdiplusShutdown;
@@ -81,6 +91,11 @@ GdipCreateHBITMAPFromBitmap_Proc fn_GdipCreateHBITMAPFromBitmap;
81GdipDisposeImage_Proc fn_GdipDisposeImage; 91GdipDisposeImage_Proc fn_GdipDisposeImage;
82GdipGetImageHeight_Proc fn_GdipGetImageHeight; 92GdipGetImageHeight_Proc fn_GdipGetImageHeight;
83GdipGetImageWidth_Proc fn_GdipGetImageWidth; 93GdipGetImageWidth_Proc fn_GdipGetImageWidth;
94GdipGetImageEncodersSize_Proc fn_GdipGetImageEncodersSize;
95GdipGetImageEncoders_Proc fn_GdipGetImageEncoders;
96GdipLoadImageFromFile_Proc fn_GdipLoadImageFromFile;
97GdipGetImageThumbnail_Proc fn_GdipGetImageThumbnail;
98GdipSaveImageToFile_Proc fn_GdipSaveImageToFile;
84 99
85static bool 100static bool
86gdiplus_init (void) 101gdiplus_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
525struct cached_encoder {
526 int num;
527 char *type;
528 CLSID clsid;
529};
530
531static struct cached_encoder last_encoder;
532
533struct thumb_type_data {
534 const char *ext;
535 const wchar_t *mime;
536};
537
538static 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
552static int
553get_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
606DEFUN ("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.
609TYPE is the image type to use for the thumbnail file, a string. It is
610usually identical to the file-name extension of THUMB-FILE, but without
611the leading period, and both "jpeg" and "jpg" can be used for JPEG.
612TYPE is matched case-insensitively against supported types. Currently,
613the supported TYPEs are BMP, JPEG, GIF, TIFF, and PNG; any other type
614will cause the function to fail.
615Return 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
668void
669syms_of_w32image (void)
670{
671 defsubr (&Sw32image_create_thumbnail);
672}
673
674void
675globals_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);
909extern void globals_of_w32fns (void); 909extern void globals_of_w32fns (void);
910extern void globals_of_w32notify (void); 910extern void globals_of_w32notify (void);
911 911
912extern void syms_of_w32image (void);
913extern void globals_of_w32image (void);
914
912extern void w32_init_main_thread (void); 915extern void w32_init_main_thread (void);
913 916
914#ifdef CYGWIN 917#ifdef CYGWIN