unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Image resizing and rotation on NS port without imagemagick
@ 2017-10-08 20:38 Alan Third
  2017-10-08 20:53 ` Lars Ingebrigtsen
  2017-10-11 18:27 ` Charles A. Roelli
  0 siblings, 2 replies; 5+ messages in thread
From: Alan Third @ 2017-10-08 20:38 UTC (permalink / raw)
  To: Emacs-Devel devel

[-- Attachment #1: Type: text/plain, Size: 555 bytes --]

It’s always kind of bugged me that I need imagemagick to do image
resizing when Cocoa and GNUstep support it natively, so I’ve finally
got round to implementing it.

It seems to work fine. Any comments are welcome.

NOTE: if you have libjpg, libpng, or whatever installed, configure
includes them and emacs will use them preferentially over the built‐in
EmacsImage stuff, so you may need to use

    --without-png --without-gif --without-jpeg

I imagine there’s a way to switch the default to not use them when
compiling --with-ns?
-- 
Alan Third

[-- Attachment #2: 0001-Add-image-resizing-and-rotation-to-NS-port.patch --]
[-- Type: text/plain, Size: 6487 bytes --]

From 58351a5899ae2f6c040e48fb6852a3cd96a72401 Mon Sep 17 00:00:00 2001
From: Alan Third <alan@idiocy.org>
Date: Sun, 8 Oct 2017 11:08:16 +0100
Subject: [PATCH] Add image resizing and rotation to NS port

* lisp/image.el (image--get-imagemagick-and-warn): Bypass imagemagick
check when using NS.
* src/nsimage.m (ns_load_image): Add rotation and resizing
functionality. Move the getMetaData call to before the resize/rotation
so it returns correct metadata.
(EmacsImage::setSizeFromSpec, EmacsImage::rotate): New functions.
* src/nsterm.h (EmacsImage): Add new function prototypes.
(NSCompositingOperationCopy): Add define to older equivalent for
GNUstep and pre-10.12 macOS.
---
 lisp/image.el |   5 ++-
 src/nsimage.m | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/nsterm.h  |   3 ++
 3 files changed, 120 insertions(+), 4 deletions(-)

diff --git a/lisp/image.el b/lisp/image.el
index 1d0776180b..f20eeb6c16 100644
--- a/lisp/image.el
+++ b/lisp/image.el
@@ -976,11 +976,12 @@ image--get-image
     image))
 
 (defun image--get-imagemagick-and-warn ()
-  (unless (fboundp 'imagemagick-types)
+  (unless (or (fboundp 'imagemagick-types) (featurep 'ns))
     (error "Can't rescale images without ImageMagick support"))
   (let ((image (image--get-image)))
     (image-flush image)
-    (plist-put (cdr image) :type 'imagemagick)
+    (when (fboundp 'imagemagick-types)
+      (plist-put (cdr image) :type 'imagemagick))
     image))
 
 (defun image--change-size (factor)
diff --git a/src/nsimage.m b/src/nsimage.m
index 9d45b063af..52e3bae05f 100644
--- a/src/nsimage.m
+++ b/src/nsimage.m
@@ -76,8 +76,9 @@ Updated by Christian Limpach (chris@nice.ch)
 {
   EmacsImage *eImg = nil;
   NSSize size;
-  Lisp_Object lisp_index;
+  Lisp_Object lisp_index, lisp_rotation;
   unsigned int index;
+  double rotation;
 
   NSTRACE ("ns_load_image");
 
@@ -86,6 +87,9 @@ Updated by Christian Limpach (chris@nice.ch)
   lisp_index = Fplist_get (XCDR (img->spec), QCindex);
   index = INTEGERP (lisp_index) ? XFASTINT (lisp_index) : 0;
 
+  lisp_rotation = Fplist_get (XCDR (img->spec), QCrotation);
+  rotation = NUMBERP (lisp_rotation) ? XFLOATINT (lisp_rotation) : 0;
+
   if (STRINGP (spec_file))
     {
       eImg = [EmacsImage allocInitFromFile: spec_file];
@@ -113,6 +117,17 @@ Updated by Christian Limpach (chris@nice.ch)
       return 0;
     }
 
+  img->lisp_data = [eImg getMetadata];
+
+  if (rotation != 0)
+    {
+      EmacsImage *temp = [eImg rotate:rotation];
+      [eImg release];
+      eImg = temp;
+    }
+
+  [eImg setSizeFromSpec:XCDR (img->spec)];
+
   size = [eImg size];
   img->width = size.width;
   img->height = size.height;
@@ -120,7 +135,6 @@ Updated by Christian Limpach (chris@nice.ch)
   /* 4) set img->pixmap = emacsimage */
   img->pixmap = eImg;
 
-  img->lisp_data = [eImg getMetadata];
   return 1;
 }
 
@@ -510,4 +524,102 @@ - (BOOL)setFrame: (unsigned int) index
   return YES;
 }
 
+- (void)setSizeFromSpec: (Lisp_Object) spec
+{
+  NSSize size = [self size];
+  Lisp_Object value;
+  double scale = 1, aspect = size.width / size.height;
+  double width = -1, height = -1, max_width = -1, max_height = -1;
+
+  value = Fplist_get (spec, QCscale);
+  if (NUMBERP (value))
+    scale = XFLOATINT (value) ;
+
+  value = Fplist_get (spec, QCmax_width);
+  if (NUMBERP (value))
+    max_width = XFLOATINT (value);
+
+  value = Fplist_get (spec, QCmax_height);
+  if (NUMBERP (value))
+    max_height = XFLOATINT (value);
+
+  value = Fplist_get (spec, QCwidth);
+  if (NUMBERP (value))
+    {
+      width = XFLOATINT (value) * scale;
+      /* :width overrides :max-width. */
+      max_width = -1;
+    }
+
+  value = Fplist_get (spec, QCheight);
+  if (NUMBERP (value))
+    {
+      height = XFLOATINT (value) * scale;
+      /* :height overrides :max-height. */
+      max_height = -1;
+    }
+
+  if (width <= 0 && height <= 0)
+    {
+      width = size.width * scale;
+      height = size.height * scale;
+    }
+  else if (width > 0 && height <= 0)
+      height = width / aspect;
+  else if (height > 0 && width <= 0)
+      width = height * aspect;
+
+  if (max_width > 0 && width > max_width)
+    {
+      width = max_width;
+      height = max_width / aspect;
+    }
+
+  if (max_height > 0 && height > max_height)
+    {
+      height = max_height;
+      width = max_height * aspect;
+    }
+
+  [self setSize:NSMakeSize(width, height)];
+}
+
+- (instancetype)rotate: (double)rotation
+{
+  EmacsImage *new_image;
+  NSPoint new_origin;
+  NSSize new_size, size = [self size];
+  NSRect rect = { NSZeroPoint, [self size] };
+
+  /* Create a bezier path of the outline of the image and do the
+   * rotation on it.  */
+  NSBezierPath *bounds_path = [NSBezierPath bezierPathWithRect:rect];
+  NSAffineTransform *transform = [NSAffineTransform transform];
+  [transform rotateByDegrees: rotation * -1];
+  [bounds_path transformUsingAffineTransform:transform];
+
+  /* Now we can find out how large the rotated image needs to be.  */
+  new_size = [bounds_path bounds].size;
+  new_image = [[EmacsImage alloc] initWithSize:new_size];
+
+  new_origin = NSMakePoint((new_size.width - size.width)/2,
+                           (new_size.height - size.height)/2);
+
+  [new_image lockFocus];
+
+  /* Create the final transform.  */
+  transform = [NSAffineTransform transform];
+  [transform translateXBy:new_size.width/2 yBy:new_size.height/2];
+  [transform rotateByDegrees: rotation * -1];
+  [transform translateXBy:-new_size.width/2 yBy:-new_size.height/2];
+
+  [transform concat];
+  [self drawAtPoint:new_origin fromRect:NSZeroRect
+          operation:NSCompositingOperationCopy fraction:1];
+
+  [new_image unlockFocus];
+
+  return new_image;
+}
+
 @end
diff --git a/src/nsterm.h b/src/nsterm.h
index de96e0dbcb..c81bf5fb63 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -646,6 +646,8 @@ typedef id instancetype;
 - (NSColor *)stippleMask;
 - (Lisp_Object)getMetadata;
 - (BOOL)setFrame: (unsigned int) index;
+- (void)setSizeFromSpec: (Lisp_Object) spec;
+- (instancetype)rotate: (double)rotation;
 @end
 
 
@@ -1306,6 +1308,7 @@ extern char gnustep_base_version[];  /* version tracking */
 #define NSWindowStyleMaskUtilityWindow     NSUtilityWindowMask
 #define NSAlertStyleCritical               NSCriticalAlertStyle
 #define NSControlSizeRegular               NSRegularControlSize
+#define NSCompositingOperationCopy         NSCompositeCopy
 
 /* And adds NSWindowStyleMask. */
 #ifdef __OBJC__
-- 
2.14.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: Image resizing and rotation on NS port without imagemagick
  2017-10-08 20:38 Image resizing and rotation on NS port without imagemagick Alan Third
@ 2017-10-08 20:53 ` Lars Ingebrigtsen
  2017-10-08 22:01   ` Alan Third
  2017-10-11 18:27 ` Charles A. Roelli
  1 sibling, 1 reply; 5+ messages in thread
From: Lars Ingebrigtsen @ 2017-10-08 20:53 UTC (permalink / raw)
  To: Alan Third; +Cc: Emacs-Devel devel

Alan Third <alan@idiocy.org> writes:

> It’s always kind of bugged me that I need imagemagick to do image
> resizing when Cocoa and GNUstep support it natively, so I’ve finally
> got round to implementing it.

Allowing all the other image formats to support scaling and stuff would
be very nice, but this is just for the Apple port?  I think if Emacs has
(for instance) .png scaling, it should preferably be supported across
all architectures...

> +- (void)setSizeFromSpec: (Lisp_Object) spec
> +{
> +  NSSize size = [self size];
> +  Lisp_Object value;
> +  double scale = 1, aspect = size.width / size.height;
> +  double width = -1, height = -1, max_width = -1, max_height = -1;
> +
> +  value = Fplist_get (spec, QCscale);
> +  if (NUMBERP (value))
> +    scale = XFLOATINT (value) ;

[...]

> +  [self setSize:NSMakeSize(width, height)];

(Etc.)  This function seems to replicate the functionality of
compute_image_size in image.c?  Is there any reason why that couldn't
just be reused here, too?  (Perhaps the calling conventions would need
to be tweaked a bit...)

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Image resizing and rotation on NS port without imagemagick
  2017-10-08 20:53 ` Lars Ingebrigtsen
@ 2017-10-08 22:01   ` Alan Third
  0 siblings, 0 replies; 5+ messages in thread
From: Alan Third @ 2017-10-08 22:01 UTC (permalink / raw)
  To: Lars Ingebrigtsen; +Cc: Emacs-Devel devel

On Sun, Oct 08, 2017 at 10:53:28PM +0200, Lars Ingebrigtsen wrote:
> Alan Third <alan@idiocy.org> writes:
> 
> > It’s always kind of bugged me that I need imagemagick to do image
> > resizing when Cocoa and GNUstep support it natively, so I’ve finally
> > got round to implementing it.
> 
> Allowing all the other image formats to support scaling and stuff would
> be very nice, but this is just for the Apple port?  I think if Emacs has
> (for instance) .png scaling, it should preferably be supported across
> all architectures...

I was under the impression that imagemagick allows you to open and
scale almost any image format, so this wouldn’t be anything new.
Perhaps I misunderstood.

This should work on any platform that GNUstep runs on, though, not
just macOS. But the list of supported formats is probably very
different across platforms.

> > +- (void)setSizeFromSpec: (Lisp_Object) spec
> > +{
> > +  NSSize size = [self size];
> > +  Lisp_Object value;
> > +  double scale = 1, aspect = size.width / size.height;
> > +  double width = -1, height = -1, max_width = -1, max_height = -1;
> > +
> > +  value = Fplist_get (spec, QCscale);
> > +  if (NUMBERP (value))
> > +    scale = XFLOATINT (value) ;
> 
> [...]
> 
> > +  [self setSize:NSMakeSize(width, height)];
> 
> (Etc.)  This function seems to replicate the functionality of
> compute_image_size in image.c?  Is there any reason why that couldn't
> just be reused here, too?  (Perhaps the calling conventions would need
> to be tweaked a bit...)

I considered just copying and pasting it, but it would have needed a
reasonable amount of modification. The NS API uses doubles for most
sizes, while compute_image_size expects ints, so I’d have to convert
to int, do the maths, and convert back to double. It seemed as easy to
rewrite it.
-- 
Alan Third



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Image resizing and rotation on NS port without imagemagick
  2017-10-08 20:38 Image resizing and rotation on NS port without imagemagick Alan Third
  2017-10-08 20:53 ` Lars Ingebrigtsen
@ 2017-10-11 18:27 ` Charles A. Roelli
  2017-10-11 19:17   ` Alan Third
  1 sibling, 1 reply; 5+ messages in thread
From: Charles A. Roelli @ 2017-10-11 18:27 UTC (permalink / raw)
  To: Alan Third; +Cc: emacs-devel

> Date: Sun, 8 Oct 2017 21:38:22 +0100
> From: Alan Third <alan@idiocy.org>
> 
> It’s always kind of bugged me that I need imagemagick to do image
> resizing when Cocoa and GNUstep support it natively, so I’ve finally
> got round to implementing it.
> 
> It seems to work fine. Any comments are welcome.
> 
> NOTE: if you have libjpg, libpng, or whatever installed, configure
> includes them and emacs will use them preferentially over the built‐in
> EmacsImage stuff, so you may need to use
> 
>     --without-png --without-gif --without-jpeg
> 
> I imagine there’s a way to switch the default to not use them when
> compiling --with-ns?
> -- 
> Alan Third> 

Neat.  Thanks for working on this; it works well on macOS 10.6 too.
Do you know if we could get it to work with librsvg, so that
zooming/rotation could be done on SVGs as well?



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Image resizing and rotation on NS port without imagemagick
  2017-10-11 18:27 ` Charles A. Roelli
@ 2017-10-11 19:17   ` Alan Third
  0 siblings, 0 replies; 5+ messages in thread
From: Alan Third @ 2017-10-11 19:17 UTC (permalink / raw)
  To: Charles A. Roelli; +Cc: emacs-devel

On Wed, Oct 11, 2017 at 08:27:58PM +0200, Charles A. Roelli wrote:
> Neat.  Thanks for working on this; it works well on macOS 10.6 too.

Thanks for testing.

> Do you know if we could get it to work with librsvg, so that
> zooming/rotation could be done on SVGs as well?

This uses the built‐in image support in Cocoa/GNUstep, and they don’t
support SVG at all afaict. So I think librsvg is in the same boat as
libpng, libjpeg, etc. While it probably can be done it will have to be
specific to librsvg, or some generic system would have to do it and
we’re back to imagemagick.

-- 
Alan Third



^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2017-10-11 19:17 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-10-08 20:38 Image resizing and rotation on NS port without imagemagick Alan Third
2017-10-08 20:53 ` Lars Ingebrigtsen
2017-10-08 22:01   ` Alan Third
2017-10-11 18:27 ` Charles A. Roelli
2017-10-11 19:17   ` Alan Third

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).