]> git.eshelyaron.com Git - emacs.git/commitdiff
Support colored stipples on Cocoa NS (Bug#73384)
authorBen Simms <ben@bensimms.moe>
Sun, 5 Jan 2025 19:03:53 +0000 (20:03 +0100)
committerEshel Yaron <me@eshelyaron.com>
Tue, 4 Mar 2025 21:05:53 +0000 (22:05 +0100)
On Cocoa builds of NS Emacs, stipples are now rendered
using masked CGImages instead of patterned NSColors so that
stipples now render with color.
* src/nsimage.m ([EmacsImage stippleMask:]): Use a CGImageMask to
store the stipple mask when building for Cocoa.
* src/nsterm.m (ns_maybe_dumpglyphs_background): Perform a masked
fill to draw stipples when building for Cocoa.
(ns_draw_stretch_glyph_string): Perform a masked fill to draw
stipples when building for Cocoa.

(cherry picked from commit 9cbbdcee132588a11dc03c3da371176aaa13927c)

src/nsimage.m
src/nsterm.h
src/nsterm.m

index e8c82269bced74b99458d4b8daf589d5ae8caa6a..8f1653d085a33dde8013f7c228b904aad8df9da8 100644 (file)
@@ -35,6 +35,9 @@ GNUstep port and post-20 update by Adrian Robert (arobert@cogsci.ucsd.edu)
 #include "frame.h"
 #include "coding.h"
 
+#ifdef NS_IMPL_COCOA
+#include <CoreGraphics/CoreGraphics.h>
+#endif
 
 #if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MAX_ALLOWED < 1070
 # define COLORSPACE_NAME NSCalibratedRGBColorSpace
@@ -289,7 +292,11 @@ ns_image_size_in_bytes (void *img)
 
 - (void)dealloc
 {
+#ifdef NS_IMPL_COCOA
+  CGImageRelease(stippleMask);
+#else
   [stippleMask release];
+#endif
   [bmRep release];
   [transform release];
   [super dealloc];
@@ -300,7 +307,11 @@ ns_image_size_in_bytes (void *img)
 {
   EmacsImage *copy = [super copyWithZone:zone];
 
+#ifdef NS_IMPL_COCOA
+  copy->stippleMask = CGImageCreateCopy(stippleMask);
+#else
   copy->stippleMask = [stippleMask copyWithZone:zone];
+#endif /* NS_IMPL_COCOA */
   copy->bmRep = [bmRep copyWithZone:zone];
   copy->transform = [transform copyWithZone:zone];
 
@@ -509,6 +520,25 @@ ns_image_size_in_bytes (void *img)
     }
 }
 
+#ifdef NS_IMPL_COCOA
+/* Returns a cached CGImageMask of the stipple pattern */
+- (CGImageRef)stippleMask
+{
+  if (stippleMask == nil)
+    {
+      CGDataProviderRef provider = CGDataProviderCreateWithData (NULL, [bmRep bitmapData],
+                                                               [self sizeInBytes], NULL);
+      CGImageRef mask = CGImageMaskCreate([self size].width,
+                                          [self size].height,
+                                          8, 8, [self size].width,
+                                          provider, NULL, 0);
+
+      CGDataProviderRelease(provider);
+      stippleMask = CGImageRetain(mask);
+    }
+  return stippleMask;
+}
+#else
 /* Returns a pattern color, which is cached here.  */
 - (NSColor *)stippleMask
 {
@@ -516,6 +546,7 @@ ns_image_size_in_bytes (void *img)
       stippleMask = [[NSColor colorWithPatternImage: self] retain];
   return stippleMask;
 }
+#endif /* NS_IMPL_COCOA */
 
 /* Find the first NSBitmapImageRep which has multiple frames.  */
 - (NSBitmapImageRep *)getAnimatedBitmapImageRep
index d03908eb52193f3258c58f3dcbd613804e6a2982..0009f1ab2bf27a6f7bd7a8c53f5314227c6074b6 100644 (file)
@@ -671,7 +671,11 @@ enum ns_return_frame_mode
 {
   NSBitmapImageRep *bmRep; /* used for accessing pixel data */
   unsigned char *pixmapData[5]; /* shortcut to access pixel data */
+#ifdef NS_IMPL_COCOA
+  CGImageRef stippleMask;
+#else
   NSColor *stippleMask;
+#endif /* NS_IMPL_COCOA */
 @public
   NSAffineTransform *transform;
   BOOL smoothing;
@@ -688,7 +692,11 @@ enum ns_return_frame_mode
                green: (unsigned char)g blue: (unsigned char)b
               alpha:(unsigned char)a;
 - (void)setAlphaAtX: (int)x Y: (int)y to: (unsigned char)a;
+#ifdef NS_IMPL_COCOA
+- (CGImageRef)stippleMask;
+#else
 - (NSColor *)stippleMask;
+#endif /* NS_IMPL_COCOA */
 - (Lisp_Object)getMetadata;
 - (BOOL)setFrame: (unsigned int) index;
 - (void)setTransform: (double[3][3]) m;
index 12141dfac444fbdc657eacf055b0f10036511641..745addfc46c5db27dfeb79a07459c517de97318d 100644 (file)
@@ -3817,8 +3817,41 @@ ns_maybe_dumpglyphs_background (struct glyph_string *s, char force_p)
       if (s->stippled_p)
        {
          struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
+#ifdef NS_IMPL_COCOA
+         /* On cocoa emacs the stipple is stored as a mask CGImage.
+            First we want to clear the background with the bg colour */
+         [[NSColor colorWithUnsignedLong:face->background] set];
+         r = NSMakeRect (s->x, s->y + box_line_width,
+                         s->background_width,
+                         s->height - 2 * box_line_width);
+         NSRectFill (r);
+         s->background_filled_p = 1;
+         CGImageRef mask =
+           [dpyinfo->bitmaps[face->stipple - 1].img stippleMask];
+
+         /* This part could possibly be improved, the author is
+            unfamiliar with NS/CoreGraphics and isn't sure if it's
+            possible to do this with NSImage */
+         NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
+         [ctx saveGraphicsState];
+         /* Checkpoint the graphics state and then focus in on the area
+            we're going to fill */
+         CGContextRef context = [ctx CGContext];
+         CGContextClipToRect (context, r);
+         CGContextScaleCTM (context, 1, -1);
+
+         /* Stamp the foreground colour using the stipple mask */
+         [[NSColor colorWithUnsignedLong:face->foreground] set];
+         CGRect imageSize = CGRectMake (0, 0, CGImageGetWidth (mask),
+                                        CGImageGetHeight (mask));
+         CGContextDrawTiledImage (context, imageSize, mask);
+
+         [[NSGraphicsContext currentContext] restoreGraphicsState];
+#else
          [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set];
          goto fill;
+#endif /* NS_IMPL_COCOA */
+
        }
       else if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width
               /* When xdisp.c ignores FONT_HEIGHT, we cannot trust font
@@ -3841,7 +3874,9 @@ ns_maybe_dumpglyphs_background (struct glyph_string *s, char force_p)
          else
            [FRAME_CURSOR_COLOR (s->f) set];
 
+#ifndef NS_IMPL_COCOA
        fill:
+#endif /* !NS_IMPL_COCOA */
          r = NSMakeRect (s->x, s->y + box_line_width,
                          s->background_width,
                          s->height - 2 * box_line_width);
@@ -4166,7 +4201,38 @@ ns_draw_stretch_glyph_string (struct glyph_string *s)
          if (s->hl == DRAW_CURSOR)
            [FRAME_CURSOR_COLOR (s->f) set];
          else if (s->stippled_p)
-           [[dpyinfo->bitmaps[s->face->stipple - 1].img stippleMask] set];
+           {
+#ifdef NS_IMPL_COCOA
+             /* On cocoa emacs the stipple is stored as a mask CGImage.
+                First we want to clear the background with the bg
+                colour */
+             [[NSColor colorWithUnsignedLong:s->face->background] set];
+             NSRectFill (NSMakeRect (x, s->y, background_width, s->height));
+
+             /* This part could possibly be improved, the author is
+                unfamiliar with NS/CoreGraphics and isn't sure if it's
+                possible to do this with NSImage */
+             CGImageRef mask = [dpyinfo->bitmaps[s->face->stipple - 1].img stippleMask];
+             CGRect bounds = CGRectMake (s->x, s->y, s->background_width, s->height);
+
+             /* Checkpoint the graphics state and then focus in on the
+                area we're going to fill */
+             NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
+             [ctx saveGraphicsState];
+             CGContextRef context = [ctx CGContext];
+             CGContextClipToRect(context, bounds);
+             CGContextScaleCTM (context, 1, -1);
+
+             /* Stamp the foreground colour using the stipple mask */
+             [[NSColor colorWithUnsignedLong:s->face->foreground] set];
+             CGRect imageSize = CGRectMake (0, 0, CGImageGetWidth (mask),
+                                            CGImageGetHeight (mask));
+             CGContextDrawTiledImage (context, imageSize, mask);
+             [[NSGraphicsContext currentContext] restoreGraphicsState];
+#else
+             [[dpyinfo->bitmaps[s->face->stipple - 1].img stippleMask] set];
+#endif /* NS_IMPL_COCOA */
+           }
          else
            [[NSColor colorWithUnsignedLong: s->face->background] set];