[PATCH v2 12/12] macdrv: Improve GDI drawing performance.
Elaine Lefler
elaineclefler at gmail.com
Thu Mar 24 23:46:26 CDT 2022
Signed-off-by: Elaine Lefler <elaineclefler at gmail.com>
---
v2: Was PATCH 3/3. Splitting into smaller patches as per feedback.
---
dlls/winemac.drv/cocoa_app.h | 2 +
dlls/winemac.drv/cocoa_app.m | 59 +++++++---
dlls/winemac.drv/cocoa_window.h | 3 +-
dlls/winemac.drv/cocoa_window.m | 201 +++++++++++++++-----------------
dlls/winemac.drv/macdrv.h | 1 -
dlls/winemac.drv/macdrv_cocoa.h | 5 +-
dlls/winemac.drv/surface.c | 124 +++++++++-----------
dlls/winemac.drv/window.c | 4 +-
8 files changed, 204 insertions(+), 195 deletions(-)
diff --git a/dlls/winemac.drv/cocoa_app.h b/dlls/winemac.drv/cocoa_app.h
index 52c91c0621f..b55c55f2995 100644
--- a/dlls/winemac.drv/cocoa_app.h
+++ b/dlls/winemac.drv/cocoa_app.h
@@ -124,6 +124,8 @@ @interface WineApplicationController : NSObject <NSApplicationDelegate>
id<WineClipCursorHandler> clipCursorHandler;
+ NSMutableArray* wineWindows;
+
NSImage* applicationIcon;
BOOL beenActive;
diff --git a/dlls/winemac.drv/cocoa_app.m b/dlls/winemac.drv/cocoa_app.m
index 8c525333e8d..e8b88286d24 100644
--- a/dlls/winemac.drv/cocoa_app.m
+++ b/dlls/winemac.drv/cocoa_app.m
@@ -47,6 +47,25 @@ + (void) setAllowsAutomaticWindowTabbing:(BOOL)allows;
#endif
+ at interface NSWindow (WineNSPointExtensions)
+
+/* Reimplementation of -convertPointFromScreen: which isn't available on all
+ * supported macOS versions */
+- (NSPoint)wineConvertPointFromScreen:(NSPoint)point;
+
+ at end
+
+ at implementation NSWindow (WineNSPointExtensions)
+
+- (NSPoint)wineConvertPointFromScreen:(NSPoint)point
+{
+ NSPoint origin = self.frame.origin;
+ return NSMakePoint(point.x - origin.x, point.y - origin.y);
+}
+
+ at end
+
+
/***********************************************************************
* WineLocalizedString
*
@@ -204,6 +223,7 @@ - (void) dealloc
[keyWindows release];
[eventQueues release];
[eventQueuesLock release];
+ [wineWindows release];
if (requestsManipQueue) dispatch_release(requestsManipQueue);
[requests release];
if (requestSource)
@@ -571,7 +591,6 @@ - (WineWindow*) frontWineWindow
- (void) adjustWindowLevels:(BOOL)active
{
NSArray* windowNumbers;
- NSMutableArray* wineWindows;
NSNumber* windowNumber;
NSUInteger nextFloatingIndex = 0;
__block NSInteger maxLevel = NSIntegerMin;
@@ -582,6 +601,8 @@ - (void) adjustWindowLevels:(BOOL)active
if ([NSApp isHidden]) return;
+ [wineWindows release];
+
windowNumbers = [NSWindow windowNumbersWithOptions:0];
wineWindows = [[NSMutableArray alloc] initWithCapacity:[windowNumbers count]];
@@ -652,8 +673,6 @@ - (void) adjustWindowLevels:(BOOL)active
NSEnableScreenUpdates();
- [wineWindows release];
-
// The above took care of the visible windows on the current space. That
// leaves windows on other spaces, minimized windows, and windows which
// are not ordered in. We want to leave windows on other spaces alone
@@ -1337,19 +1356,33 @@ - (void) handleMouseMove:(NSEvent*)anEvent
targetWindow = (WineWindow*)[anEvent window];
else
{
- /* Because of the way -[NSWindow setAcceptsMouseMovedEvents:] works, the
- event indicates its window is the main window, even if the cursor is
- over a different window. Find the actual WineWindow that is under the
- cursor and post the event as being for that window. */
+ /* Due to our use of NSTrackingArea and the way Cocoa directs mouse
+ * moves, the window receiving the event is probably not the one
+ * with the cursor. Find the window that actually has the cursor by
+ * hit-testing front to back. */
CGPoint cgpoint = CGEventGetLocation([anEvent CGEvent]);
NSPoint point = [self flippedMouseLocation:NSPointFromCGPoint(cgpoint)];
- NSInteger windowUnderNumber;
+ WineWindow* window;
- windowUnderNumber = [NSWindow windowNumberAtPoint:point
- belowWindowWithWindowNumber:0];
- targetWindow = (WineWindow*)[NSApp windowWithWindowNumber:windowUnderNumber];
- if (!NSMouseInRect(point, [targetWindow contentRectForFrameRect:[targetWindow frame]], NO))
- targetWindow = nil;
+ targetWindow = nil;
+
+ for (window in wineWindows)
+ {
+ NSPoint windowPoint = [window wineConvertPointFromScreen:point];
+ BOOL isHit = ([window.contentView hitTest:windowPoint] != nil);
+
+ /* Windows with transparency must be instructed to ignore
+ * mouse-downs when the hovered pixel is not visible. The
+ * window's tracking area still reports events. */
+ if (window.needsTransparency)
+ [window setIgnoresMouseEvents:!isHit && NSMouseInRect(windowPoint, window.contentView.frame, NO)];
+
+ if (isHit)
+ {
+ targetWindow = (WineWindow*)window;
+ break;
+ }
+ }
}
if ([targetWindow isKindOfClass:[WineWindow class]])
diff --git a/dlls/winemac.drv/cocoa_window.h b/dlls/winemac.drv/cocoa_window.h
index 596e3c52b3e..675abcc659b 100644
--- a/dlls/winemac.drv/cocoa_window.h
+++ b/dlls/winemac.drv/cocoa_window.h
@@ -45,7 +45,6 @@ @interface WineWindow : NSPanel <NSWindowDelegate>
WineEventQueue* queue;
void* surface;
- pthread_mutex_t* surface_mutex;
CGDirectDisplayID _lastDisplayID;
NSTimeInterval _lastDisplayTime;
@@ -95,6 +94,8 @@ @interface WineWindow : NSPanel <NSWindowDelegate>
@property (readonly, nonatomic) BOOL noForeground;
@property (readonly, nonatomic) BOOL preventsAppActivation;
@property (readonly, nonatomic) BOOL floating;
+ at property (readonly, nonatomic) BOOL needsTransparency;
+ at property (readonly, nonatomic) BOOL needsLayerTransparency;
@property (readonly, getter=isFullscreen, nonatomic) BOOL fullscreen;
@property (readonly, getter=isFakingClose, nonatomic) BOOL fakingClose;
@property (readonly, nonatomic) NSRect wine_fractionalFrame;
diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m
index 656a5ba2283..74a84aab630 100644
--- a/dlls/winemac.drv/cocoa_window.m
+++ b/dlls/winemac.drv/cocoa_window.m
@@ -309,32 +309,6 @@ static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTi
@end
-#ifndef MAC_OS_X_VERSION_10_14
- at protocol NSViewLayerContentScaleDelegate <NSObject>
- at optional
-
- - (BOOL) layer:(CALayer*)layer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow*)window;
-
- at end
-#endif
-
-
- at interface CAShapeLayer (WineShapeMaskExtensions)
-
- at property(readonly, nonatomic, getter=isEmptyShaped) BOOL emptyShaped;
-
- at end
-
- at implementation CAShapeLayer (WineShapeMaskExtensions)
-
- - (BOOL) isEmptyShaped
- {
- return CGRectEqualToRect(CGPathGetBoundingBox(self.path), CGRectZero);
- }
-
- at end
-
-
@interface WineBaseView : NSView
@end
@@ -351,10 +325,11 @@ - (id) initWithFrame:(NSRect)frame device:(id<MTLDevice>)device;
#endif
- at interface WineContentView : WineBaseView <NSTextInputClient, NSViewLayerContentScaleDelegate>
+ at interface WineContentView : WineBaseView <NSTextInputClient>
{
NSMutableArray* glContexts;
NSMutableArray* pendingGlContexts;
+ BOOL _shouldBeHidden;
BOOL _everHadGLContext;
BOOL _cachedHasGLDescendant;
BOOL _cachedHasGLDescendantValid;
@@ -363,7 +338,6 @@ @interface WineContentView : WineBaseView <NSTextInputClient, NSViewLayerContent
NSMutableAttributedString* markedText;
NSRange markedTextSelection;
- BOOL _retinaMode;
int backingSize[2];
#ifdef HAVE_METAL_METAL_H
@@ -402,11 +376,9 @@ @interface WineWindow ()
@property (retain, readwrite, nonatomic) WineEventQueue* queue;
@property (nonatomic) void* surface;
- at property (nonatomic) pthread_mutex_t* surface_mutex;
@property (retain, nonatomic) NSData* shapeData;
@property (nonatomic) BOOL shapeChangedSinceLastDraw;
- at property (readonly, nonatomic) BOOL needsTransparency;
@property (nonatomic) BOOL colorKeyed;
@property (nonatomic) uint8_t colorKeyRed, colorKeyGreen, colorKeyBlue;
@@ -490,13 +462,32 @@ - (BOOL) isFlipped
return YES;
}
+ - (BOOL) wantsDefaultClipping
+ {
+ /* Don't need this, we already limit our drawing to the dirty region */
+ return NO;
+ }
+
+ - (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event
+ {
+ /* Returning NSNull ensures the layer will never animate itself */
+ return [NSNull null];
+ }
+
+ - (BOOL) isOpaque
+ {
+ WineWindow* window = (WineWindow*)[self window];
+ return window.contentView == self && !window.needsLayerTransparency;
+ }
+
- (NSView*) hitTest:(NSPoint)point
{
WineWindow* window = (WineWindow*)[self window];
NSPoint localPoint;
CGPoint cgPoint;
- if (window.contentView != self || !window.shapeData)
+ if (window.contentView != self
+ || (!window.shapeData && !window.needsLayerTransparency))
return [super hitTest:point];
localPoint = [self convertPoint:point fromView:self.superview];
@@ -523,72 +514,76 @@ - (NSView*) hitTest:(NSPoint)point
return nil;
}
- return [super hitTest:point];
- }
+ if (window.needsLayerTransparency)
+ {
+ /* Transparent pixels are not supposed to be clickable, but due to
+ * contentView.layer.drawsAsynchronously, Cocoa does not enforce it.
+ * Therefore, we must perform our own per-pixel hit test. */
+ if (!surface_hit_test(window.surface, cgPoint, window.colorKeyed,
+ window.colorKeyRed, window.colorKeyGreen, window.colorKeyBlue))
+ return nil;
+ }
- - (BOOL) wantsUpdateLayer
- {
- return YES /*!_everHadGLContext*/;
+ return [super hitTest:point];
}
- - (void) updateLayer
+ - (void) drawRect:(NSRect)rect
{
WineWindow* window = (WineWindow*)[self window];
- CGImageRef image = NULL;
- CGRect imageRect;
- CALayer* layer = [self layer];
+ CGRect imageRect = cgrect_win_from_mac(NSRectToCGRect(rect));
+ CGImageRef image;
- if ([window contentView] != self)
- return;
+ for (WineOpenGLContext* context in pendingGlContexts)
+ {
+ if (!clearedGlSurface)
+ {
+ context.shouldClearToBlack = TRUE;
+ clearedGlSurface = TRUE;
+ }
+ context.needsUpdate = TRUE;
+ }
+ [glContexts addObjectsFromArray:pendingGlContexts];
+ [pendingGlContexts removeAllObjects];
- if (window.closing || !window.surface || !window.surface_mutex)
+ if ([window contentView] != self)
return;
- pthread_mutex_lock(window.surface_mutex);
- if (get_surface_blit_rects(window.surface, NULL, NULL))
+ if ((image = create_surface_image(window.surface, &imageRect, window.colorKeyed,
+ window.colorKeyRed, window.colorKeyGreen, window.colorKeyBlue)) != NULL)
{
- imageRect = layer.bounds;
- imageRect.origin.x *= layer.contentsScale;
- imageRect.origin.y *= layer.contentsScale;
- imageRect.size.width *= layer.contentsScale;
- imageRect.size.height *= layer.contentsScale;
- image = create_surface_image(window.surface, &imageRect, window.colorKeyed,
- window.colorKeyRed, window.colorKeyGreen, window.colorKeyBlue);
- }
- pthread_mutex_unlock(window.surface_mutex);
+ CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+ CGContextSetBlendMode(context, kCGBlendModeCopy);
+ /* HQ interpolation should be used with retina to prevent artifacts
+ * on mixed DPI. Not needed for standard DPI. */
+ CGContextSetInterpolationQuality(context, retina_on ? kCGInterpolationHigh : kCGInterpolationNone);
+
+ CGContextDrawImage(context, cgrect_mac_from_win(imageRect), image);
+ CGImageRelease(image);
- if (image)
- {
- layer.contents = (id)image;
- CFRelease(image);
[window windowDidDrawContent];
+ }
- // If the window may be transparent, then we have to invalidate the
- // shadow every time we draw. Also, if this is the first time we've
- // drawn since changing from transparent to opaque.
- if (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw)
- {
- window.shapeChangedSinceLastDraw = FALSE;
- [window invalidateShadow];
- }
+ /* If the window may be transparent, then we have to invalidate the
+ * shadow every time we draw. Also, if this is the first time we've
+ * drawn since changing from transparent to opaque. */
+ if (window.drawnSinceShown && (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw))
+ {
+ window.shapeChangedSinceLastDraw = FALSE;
+ [window invalidateShadow];
}
}
- - (void) viewWillDraw
+ - (void) setHidden:(BOOL)hidden
{
- [super viewWillDraw];
-
- for (WineOpenGLContext* context in pendingGlContexts)
+ if (self.window.contentView == self)
{
- if (!clearedGlSurface)
- {
- context.shouldClearToBlack = TRUE;
- clearedGlSurface = TRUE;
- }
- context.needsUpdate = TRUE;
+ [super setHidden:hidden];
+ return;
}
- [glContexts addObjectsFromArray:pendingGlContexts];
- [pendingGlContexts removeAllObjects];
+
+ /* Client views should always remain hidden, unless we have OpenGL */
+ [super setHidden:hidden || !_everHadGLContext];
+ _shouldBeHidden = hidden;
}
- (void) addGLContext:(WineOpenGLContext*)context
@@ -617,7 +612,10 @@ - (void) addGLContext:(WineOpenGLContext*)context
_everHadGLContext = YES;
if (!hadContext)
+ {
+ [super setHidden:_shouldBeHidden];
[self invalidateHasGLDescendant];
+ }
[(WineWindow*)[self window] updateForGLSubviews];
}
@@ -721,18 +719,9 @@ - (void) setRetinaMode:(int)mode
[self setWantsBestResolutionOpenGLSurface:mode];
[self updateGLContexts];
- _retinaMode = !!mode;
- [self layer].contentsScale = mode ? 2.0 : 1.0;
- [self layer].minificationFilter = mode ? kCAFilterLinear : kCAFilterNearest;
- [self layer].magnificationFilter = mode ? kCAFilterLinear : kCAFilterNearest;
[super setRetinaMode:mode];
}
- - (BOOL) layer:(CALayer*)layer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow*)window
- {
- return (_retinaMode || newScale == 1.0);
- }
-
- (void) viewDidHide
{
[super viewDidHide];
@@ -992,7 +981,7 @@ @implementation WineWindow
@synthesize disabled, noForeground, preventsAppActivation, floating, fullscreen, fakingClose, closing, latentParentWindow, hwnd, queue;
@synthesize drawnSinceShown;
- @synthesize surface, surface_mutex;
+ @synthesize surface;
@synthesize shapeData, shapeChangedSinceLastDraw;
@synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
@synthesize usePerPixelAlpha;
@@ -1026,7 +1015,6 @@ + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)w
[window disableCursorRects];
[window setShowsResizeIndicator:NO];
[window setHasShadow:wf->shadow];
- [window setAcceptsMouseMovedEvents:YES];
[window setDelegate:window];
[window setBackgroundColor:[NSColor clearColor]];
[window setOpaque:NO];
@@ -1045,12 +1033,10 @@ + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)w
if (!contentView)
return nil;
[contentView setWantsLayer:YES];
- [contentView layer].minificationFilter = retina_on ? kCAFilterLinear : kCAFilterNearest;
- [contentView layer].magnificationFilter = retina_on ? kCAFilterLinear : kCAFilterNearest;
- [contentView layer].contentsScale = retina_on ? 2.0 : 1.0;
+ [contentView.layer setDrawsAsynchronously:YES];
[contentView setAutoresizesSubviews:NO];
- /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
+ /* We use tracking areas instead of setAcceptsMouseMovedEvents:YES
because they give us mouse moves in the background. */
trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
options:(NSTrackingMouseMoved |
@@ -2044,28 +2030,37 @@ - (void) setDisabled:(BOOL)newValue
}
}
+ - (BOOL) needsLayerTransparency
+ {
+ return self.colorKeyed || self.usePerPixelAlpha;
+ }
+
- (BOOL) needsTransparency
{
- return self.contentView.layer.mask || self.colorKeyed || self.usePerPixelAlpha ||
+ return self.shapeData || self.needsLayerTransparency ||
(gl_surface_mode == GL_SURFACE_BEHIND && [(WineContentView*)self.contentView hasGLDescendant]);
}
- (void) checkTransparency
{
- if (![self isOpaque] && !self.needsTransparency)
+ if (!self.opaque && !self.needsTransparency)
{
self.shapeChangedSinceLastDraw = TRUE;
- [[self contentView] setNeedsDisplay:YES];
+ [self.contentView setNeedsDisplay:YES];
[self setBackgroundColor:[NSColor windowBackgroundColor]];
[self setOpaque:YES];
+ /* Ensure WineApplicationController hasn't cut off mouse events */
+ [self setIgnoresMouseEvents:NO];
}
- else if ([self isOpaque] && self.needsTransparency)
+ else if (self.opaque && self.needsTransparency)
{
self.shapeChangedSinceLastDraw = TRUE;
- [[self contentView] setNeedsDisplay:YES];
+ [self.contentView setNeedsDisplay:YES];
[self setBackgroundColor:[NSColor clearColor]];
[self setOpaque:NO];
}
+
+ [self.contentView.layer setOpaque:!self.needsLayerTransparency];
}
- (void) setShapeData:(NSData*)newShapeData
@@ -2712,8 +2707,6 @@ - (void) setRetinaMode:(int)mode
[transform scaleBy:scale];
- [[self contentView] layer].mask.contentsScale = mode ? 2.0 : 1.0;
-
for (WineBaseView* subview in [self.contentView subviews])
{
if ([subview isKindOfClass:[WineBaseView class]])
@@ -3319,6 +3312,7 @@ void macdrv_destroy_cocoa_window(macdrv_window w)
WineWindow* window = (WineWindow*)w;
OnMainThread(^{
+ window.surface = nil;
window.closing = TRUE;
[window doOrderOut];
[window close];
@@ -3485,14 +3479,13 @@ void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
/***********************************************************************
* macdrv_set_window_surface
*/
-void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
+void macdrv_set_window_surface(macdrv_window w, void *surface)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
WineWindow* window = (WineWindow*)w;
OnMainThread(^{
window.surface = surface;
- window.surface_mutex = mutex;
});
[pool release];
@@ -3659,9 +3652,7 @@ macdrv_view macdrv_create_view(CGRect rect)
view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
[view setWantsLayer:YES];
- [view layer].minificationFilter = retina_on ? kCAFilterLinear : kCAFilterNearest;
- [view layer].magnificationFilter = retina_on ? kCAFilterLinear : kCAFilterNearest;
- [view layer].contentsScale = retina_on ? 2.0 : 1.0;
+ [view.layer setDrawsAsynchronously:YES];
[view setAutoresizesSubviews:NO];
[view setAutoresizingMask:NSViewNotSizable];
[view setHidden:YES];
diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h
index 3eecb26a01e..7b410fbdef0 100644
--- a/dlls/winemac.drv/macdrv.h
+++ b/dlls/winemac.drv/macdrv.h
@@ -209,7 +209,6 @@ extern DWORD CDECL macdrv_MsgWaitForMultipleObjectsEx(DWORD count, const HANDLE
extern void activate_on_following_focus(void) DECLSPEC_HIDDEN;
extern struct window_surface *create_surface(macdrv_window window, const RECT *rect,
struct window_surface *old_surface, BOOL use_alpha) DECLSPEC_HIDDEN;
-extern void set_window_surface(macdrv_window window, struct window_surface *window_surface) DECLSPEC_HIDDEN;
extern void set_surface_use_alpha(struct window_surface *window_surface, BOOL use_alpha) DECLSPEC_HIDDEN;
extern void surface_clip_to_visible_rect(struct window_surface *window_surface, const RECT *visible_rect) DECLSPEC_HIDDEN;
diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h
index b0eb86133c4..34769771fa7 100644
--- a/dlls/winemac.drv/macdrv_cocoa.h
+++ b/dlls/winemac.drv/macdrv_cocoa.h
@@ -580,10 +580,11 @@ extern void macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev,
extern void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame) DECLSPEC_HIDDEN;
extern void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame) DECLSPEC_HIDDEN;
extern void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent) DECLSPEC_HIDDEN;
-extern void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex) DECLSPEC_HIDDEN;
+extern void macdrv_set_window_surface(macdrv_window w, void *surface) DECLSPEC_HIDDEN;
extern CGImageRef create_surface_image(void *window_surface, CGRect *dirty_area, int color_keyed,
uint8_t key_red, uint8_t key_green, uint8_t key_blue) DECLSPEC_HIDDEN;
-extern int get_surface_blit_rects(void *window_surface, const CGRect **rects, int *count) DECLSPEC_HIDDEN;
+extern int surface_hit_test(void *window_surface, CGPoint point, int color_keyed,
+ uint8_t key_red, uint8_t key_green, uint8_t key_blue) DECLSPEC_HIDDEN;
extern void macdrv_window_needs_display(macdrv_window w, CGRect rect) DECLSPEC_HIDDEN;
extern void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count) DECLSPEC_HIDDEN;
extern void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha) DECLSPEC_HIDDEN;
diff --git a/dlls/winemac.drv/surface.c b/dlls/winemac.drv/surface.c
index eed31229473..0eb1f09d417 100644
--- a/dlls/winemac.drv/surface.c
+++ b/dlls/winemac.drv/surface.c
@@ -64,7 +64,6 @@ struct macdrv_window_surface
HRGN region;
HRGN drawn;
BOOL use_alpha;
- RGNDATA *blit_data;
struct shadow_surface *shadow;
BYTE *bits;
pthread_mutex_t mutex;
@@ -317,27 +316,6 @@ static void shadow_cfdata_dealloc(void *ptr, void *info)
shadow_bitmap_return(info);
}
-/***********************************************************************
- * update_blit_data
- */
-static void update_blit_data(struct macdrv_window_surface *surface)
-{
- HeapFree(GetProcessHeap(), 0, surface->blit_data);
- surface->blit_data = NULL;
-
- if (surface->drawn)
- {
- HRGN blit = CreateRectRgn(0, 0, 0, 0);
-
- if (CombineRgn(blit, surface->drawn, 0, RGN_COPY) > NULLREGION &&
- (!surface->region || CombineRgn(blit, blit, surface->region, RGN_AND) > NULLREGION) &&
- OffsetRgn(blit, surface->header.rect.left, surface->header.rect.top) > NULLREGION)
- surface->blit_data = get_region_data(blit, 0);
-
- DeleteObject(blit);
- }
-}
-
/***********************************************************************
* macdrv_surface_lock
*/
@@ -401,7 +379,6 @@ static void macdrv_surface_set_region(struct window_surface *window_surface, HRG
if (surface->region) DeleteObject(surface->region);
surface->region = 0;
}
- update_blit_data(surface);
window_surface->funcs->unlock(window_surface);
}
@@ -433,7 +410,6 @@ static void macdrv_surface_flush(struct window_surface *window_surface)
else
surface->drawn = region;
}
- update_blit_data(surface);
reset_bounds(&surface->bounds);
window_surface->funcs->unlock(window_surface);
@@ -459,7 +435,7 @@ static void macdrv_surface_destroy(struct window_surface *window_surface)
}
if (surface->bits && surface->bits != MAP_FAILED)
munmap(surface->bits, surface->info.bmiHeader.biSizeImage);
- HeapFree(GetProcessHeap(), 0, surface->blit_data);
+
pthread_mutex_destroy(&surface->mutex);
HeapFree(GetProcessHeap(), 0, surface);
}
@@ -515,7 +491,7 @@ struct window_surface *create_surface(macdrv_window window, const RECT *rect,
surface->info.bmiHeader.biSize = sizeof(surface->info.bmiHeader);
surface->info.bmiHeader.biWidth = width;
- surface->info.bmiHeader.biHeight = -height; /* top-down */
+ surface->info.bmiHeader.biHeight = height; /* bottom-up */
surface->info.bmiHeader.biPlanes = 1;
surface->info.bmiHeader.biBitCount = 32;
surface->info.bmiHeader.biSizeImage = get_dib_image_size(&surface->info);
@@ -542,7 +518,6 @@ struct window_surface *create_surface(macdrv_window window, const RECT *rect,
surface->drawn = 0;
}
}
- update_blit_data(surface);
surface->use_alpha = use_alpha;
surface->shadow = shadow_surface_create(surface);
if (!surface->shadow) goto failed;
@@ -574,48 +549,6 @@ void set_surface_use_alpha(struct window_surface *window_surface, BOOL use_alpha
if (surface) surface->use_alpha = use_alpha;
}
-/***********************************************************************
- * set_window_surface
- */
-void set_window_surface(macdrv_window window, struct window_surface *window_surface)
-{
- struct macdrv_window_surface *surface = get_mac_surface(window_surface);
- macdrv_set_window_surface(window, window_surface, surface ? &surface->mutex : NULL);
-}
-
-/***********************************************************************
- * get_surface_blit_rects
- *
- * Caller must hold the surface lock. Indirectly returns the surface
- * blit region rects. Returns zero if the surface has nothing to blit;
- * returns non-zero if the surface does have rects to blit (drawn area
- * which isn't clipped away by a surface region).
- *
- * IMPORTANT: This function is called from non-Wine threads, so it
- * must not use Win32 or Wine functions, including debug
- * logging.
- */
-int get_surface_blit_rects(void *window_surface, const CGRect **rects, int *count)
-{
- struct macdrv_window_surface *surface = get_mac_surface(window_surface);
-
- if (rects && count)
- {
- if (surface->blit_data)
- {
- *rects = (const CGRect*)surface->blit_data->Buffer;
- *count = surface->blit_data->rdh.nCount;
- }
- else
- {
- *rects = NULL;
- *count = 0;
- }
- }
-
- return (surface->blit_data != NULL && surface->blit_data->rdh.nCount > 0);
-}
-
/***********************************************************************
* create_surface_image
*
@@ -779,10 +712,59 @@ void surface_clip_to_visible_rect(struct window_surface *window_surface, const R
{
CombineRgn(surface->drawn, surface->drawn, region, RGN_AND);
DeleteObject(region);
-
- update_blit_data(surface);
}
}
window_surface->funcs->unlock(window_surface);
}
+
+/***********************************************************************
+ * surface_hit_test
+ *
+ * Performs a per-pixel hit test on the given surface. Returns FALSE if
+ * the chosen pixel is transparent or keyed out, TRUE if the pixel is
+ * clickable.
+ *
+ * IMPORTANT: This function is called from non-Wine threads, so it
+ * must not use Win32 or Wine functions, including debug
+ * logging.
+ */
+int surface_hit_test(void *window_surface, CGPoint point, int color_keyed,
+ uint8_t key_red, uint8_t key_green, uint8_t key_blue)
+{
+ struct macdrv_window_surface *surface = get_mac_surface(window_surface);
+ DWORD key = (key_red << 16) | (key_green << 8) | (key_blue);
+ DWORD pixel;
+ int surface_width, bytes_per_row;
+ /* Note: coordinates can be non-integers. Truncate. */
+ int point_x = point.x, point_y = point.y;
+ int retval = TRUE;
+
+ if (!surface)
+ return TRUE;
+
+ pthread_mutex_lock(&surface->mutex);
+ surface_width = surface->header.rect.right - surface->header.rect.left;
+ bytes_per_row = get_dib_stride(surface_width, 32);
+
+ if (!surface->use_alpha && !color_keyed)
+ /* Opaque surface always succeeds */
+ goto done;
+
+ if (point_x < surface->header.rect.left || point_x >= surface->header.rect.right
+ || point_y < surface->header.rect.top || point_y >= surface->header.rect.bottom)
+ {
+ retval = FALSE;
+ goto done;
+ }
+
+ pixel = *(DWORD*)(surface->bits + (point_x - surface->header.rect.left) * 4
+ + (surface->header.rect.bottom - point_y) * bytes_per_row);
+
+ retval = !((color_keyed && (pixel & 0x00FFFFFF) == key)
+ || (surface->use_alpha && (pixel & 0xFF000000) == 0));
+
+done:
+ pthread_mutex_unlock(&surface->mutex);
+ return retval;
+}
diff --git a/dlls/winemac.drv/window.c b/dlls/winemac.drv/window.c
index 9177f493a5f..dba87df37de 100644
--- a/dlls/winemac.drv/window.c
+++ b/dlls/winemac.drv/window.c
@@ -1899,7 +1899,7 @@ BOOL CDECL macdrv_UpdateLayeredWindow(HWND hwnd, const UPDATELAYEREDWINDOWINFO *
if (!surface || !EqualRect(&surface->rect, &rect))
{
data->surface = create_surface(data->cocoa_window, &rect, NULL, TRUE);
- set_window_surface(data->cocoa_window, data->surface);
+ macdrv_set_window_surface(data->cocoa_window, data->surface);
if (surface) window_surface_release(surface);
surface = data->surface;
if (data->unminimized_surface)
@@ -2139,7 +2139,7 @@ void CDECL macdrv_WindowPosChanged(HWND hwnd, HWND insert_after, UINT swp_flags,
}
else
{
- set_window_surface(data->cocoa_window, surface);
+ macdrv_set_window_surface(data->cocoa_window, surface);
if (data->unminimized_surface)
{
window_surface_release(data->unminimized_surface);
--
2.32.0 (Apple Git-132)
More information about the wine-devel
mailing list