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.
This commit is contained in:
Ben Simms 2025-01-05 20:03:53 +01:00 committed by Eli Zaretskii
parent fe7a8c92be
commit 9cbbdcee13
3 changed files with 106 additions and 1 deletions

View file

@ -35,6 +35,9 @@ Updated by Christian Limpach (chris@nice.ch)
#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

View 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;

View file

@ -3817,8 +3817,41 @@ 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
/* 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 @@ 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);
@ -4166,7 +4201,38 @@ Function modeled after x_draw_glyph_string_box ().
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];