From adcda111c7eb923838b61fd93d7c1d5f647decda Mon Sep 17 00:00:00 2001 From: Ben Simms Date: Sun, 5 Jan 2025 20:03:53 +0100 Subject: [PATCH] Support colored stipples on Cocoa NS (Bug#73384) 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 | 31 +++++++++++++++++++++++ src/nsterm.h | 8 ++++++ src/nsterm.m | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 106 insertions(+), 1 deletion(-) diff --git a/src/nsimage.m b/src/nsimage.m index e8c82269bce..8f1653d085a 100644 --- a/src/nsimage.m +++ b/src/nsimage.m @@ -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 +#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 diff --git a/src/nsterm.h b/src/nsterm.h index d03908eb521..0009f1ab2bf 100644 --- a/src/nsterm.h +++ b/src/nsterm.h @@ -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; diff --git a/src/nsterm.m b/src/nsterm.m index 12141dfac44..745addfc46c 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -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]; -- 2.39.5