Subject: bug#73384: [PATCH] Draw coloured stipples on NS

From: Stefan Kangas <[email-masked]>
Date: Sun, 15 Dec 2024 14:21:31 +0000

Alan, do you have any comments on the below patch?  Thanks in advance.

Arash Esbati <[email-masked]> writes:

> Arash Esbati <[email-masked]> writes:
>
>> Stefan, did you have a chance to look at this again?  I'm attaching the
>> patch Ben sent me off-list.  Maybe you can advise how to proceed and get
>> this installed.  TIA.
>
> Ping!
>
> Do you see a chance to install this change?  Or should I ask Alan Third
> if he can kindly have a look?
>
> From 1c4b8efb82bd0e35c91d98f2759217702f3a7c65 Mon Sep 17 00:00:00 2001
> From: Ben Simms <[email-masked]>
> Date: Mon, 14 Oct 2024 19:32:53 +0100
> Subject: [PATCH] Use masked coregraphics images on cocoa NS
>
> ---
>  src/nsimage.m | 31 +++++++++++++++++++++++++++++++
>  src/nsterm.h  |  8 ++++++++
>  src/nsterm.m  | 47 +++++++++++++++++++++++++++++++++++++++++++++--
>  3 files changed, 84 insertions(+), 2 deletions(-)
>
> diff --git a/src/nsimage.m b/src/nsimage.m
> index ee72d6e0ea1..e36cbe5dc87 100644
> --- a/src/nsimage.m
> +++ b/src/nsimage.m
> @@ -35,6 +35,9 @@ Updated by Christian Limpach ([email-masked])
>  #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 @@ + (instancetype)allocInitFromFile: (Lisp_Object)file
>
>  - (void)dealloc
>  {
> +#ifdef NS_IMPL_COCOA
> +  CGImageRelease(stippleMask);
> +#else
>    [stippleMask release];
> +#endif
>    [bmRep release];
>    [transform release];
>    [super dealloc];
> @@ -300,7 +307,11 @@ - (id)copyWithZone:(NSZone *)zone
>  {
>    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 @@ - (void) setAlphaAtX: (int) x Y: (int) y to: (unsigned char) a
>      }
>  }
>
> +#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 @@ - (NSColor *)stippleMask
>        stippleMask = [[NSColor colorWithPatternImage: self] retain];
>    return stippleMask;
>  }
> +#endif /* NS_IMPL_COCOA */
>
>  /* Find the first NSBitmapImageRep which has multiple frames.  */
>  - (NSBitmapImageRep *)getAnimatedBitmapImageRep
> diff --git a/src/nsterm.h b/src/nsterm.h
> index 6c67653705e..2370f6ea3fb 100644
> --- a/src/nsterm.h
> +++ b/src/nsterm.h
> @@ -671,7 +671,11 @@ #define NSTRACE_UNSILENCE()
>  {
>    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 @@ #define NSTRACE_UNSILENCE()
>                 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;
> diff --git a/src/nsterm.m b/src/nsterm.m
> index f68a22d9fbc..a617669cb4d 100644
> --- a/src/nsterm.m
> +++ b/src/nsterm.m
> @@ -3823,8 +3823,31 @@ Function modeled after x_draw_glyph_string_box ().
>        if (s->stippled_p)
>  	{
>  	  struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
> +#ifdef NS_IMPL_COCOA
> +    [[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];
> +    CGRect bounds = CGRectMake (s->x, s->y + box_line_width,
> +        s->background_width,
> +        s->height - 2 * box_line_width);
> +    NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
> +    [ctx saveGraphicsState];
> +    CGContextRef context = [ctx CGContext];
> +    CGContextClipToRect (context, bounds);
> +    CGContextScaleCTM (context, 1, -1);
> +    [[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
> @@ -3847,7 +3870,9 @@ Function modeled after x_draw_glyph_string_box ().
>  	  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);
> @@ -4171,8 +4196,26 @@ Function modeled after x_draw_glyph_string_box ().
>  	  dpyinfo = FRAME_DISPLAY_INFO (s->f);
>  	  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];
> +	  else if (s->stippled_p) {
> +#ifdef NS_IMPL_COCOA
> +      [[NSColor colorWithUnsignedLong:s->face->background] set];
> +      NSRectFill (NSMakeRect (x, s->y, background_width, s->height));
> +      CGImageRef mask = [dpyinfo->bitmaps[s->face->stipple - 1].img stippleMask];
> +      CGRect bounds = CGRectMake (s->x, s->y, s->background_width, s->height);
> +      NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
> +      [ctx saveGraphicsState];
> +      CGContextRef context = [ctx CGContext];
> +      CGContextClipToRect(context, bounds);
> +      CGContextScaleCTM (context, 1, -1);
> +      [[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];
>
> --
> 2.46.0