diff --git a/dlls/winemac.drv/cocoa_window.h b/dlls/winemac.drv/cocoa_window.h index c7617aa724d..d7ad62e2ea2 100644 --- a/dlls/winemac.drv/cocoa_window.h +++ b/dlls/winemac.drv/cocoa_window.h @@ -34,6 +34,11 @@ @interface WineWindow : NSPanel NSBezierPath* shape; BOOL shapeChangedSinceLastDraw; + + BOOL colorKeyed; + CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue; + + BOOL usePerPixelAlpha; } @end diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m index 7fd704089ef..7f073a82d1e 100644 --- a/dlls/winemac.drv/cocoa_window.m +++ b/dlls/winemac.drv/cocoa_window.m @@ -72,6 +72,10 @@ @interface WineWindow () @property (nonatomic) BOOL shapeChangedSinceLastDraw; @property (readonly, nonatomic) BOOL needsTransparency; +@property (nonatomic) BOOL colorKeyed; +@property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue; +@property (nonatomic) BOOL usePerPixelAlpha; + + (void) flipRect:(NSRect*)rect; @end @@ -117,13 +121,28 @@ - (void) drawRect:(NSRect)rect [window.shape addClip]; + if (window.colorKeyed) + { + CGImageRef maskedImage; + CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5, + window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5, + window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 }; + maskedImage = CGImageCreateWithMaskingColors(image, components); + if (maskedImage) + { + CGImageRelease(image); + image = maskedImage; + } + } + context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; CGContextSetBlendMode(context, kCGBlendModeCopy); CGContextDrawImage(context, imageRect, image); CGImageRelease(image); - if (window.shapeChangedSinceLastDraw) + if (window.shapeChangedSinceLastDraw || window.colorKeyed || + window.usePerPixelAlpha) { window.shapeChangedSinceLastDraw = FALSE; [window invalidateShadow]; @@ -143,6 +162,8 @@ @implementation WineWindow @synthesize disabled, noActivate, floating, latentParentWindow; @synthesize surface, surface_mutex; @synthesize shape, shapeChangedSinceLastDraw; + @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue; + @synthesize usePerPixelAlpha; + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf windowFrame:(NSRect)window_frame @@ -338,7 +359,7 @@ - (void) setDisabled:(BOOL)newValue - (BOOL) needsTransparency { - return (self.shape != nil); + return self.shape || self.colorKeyed || self.usePerPixelAlpha; } - (void) checkTransparency @@ -640,3 +661,68 @@ void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count) [pool release]; } + +/*********************************************************************** + * macdrv_set_window_alpha + */ +void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + WineWindow* window = (WineWindow*)w; + + [window setAlphaValue:alpha]; + + [pool release]; +} + +/*********************************************************************** + * macdrv_set_window_color_key + */ +void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen, + CGFloat keyBlue) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + WineWindow* window = (WineWindow*)w; + + OnMainThread(^{ + window.colorKeyed = TRUE; + window.colorKeyRed = keyRed; + window.colorKeyGreen = keyGreen; + window.colorKeyBlue = keyBlue; + [window checkTransparency]; + }); + + [pool release]; +} + +/*********************************************************************** + * macdrv_clear_window_color_key + */ +void macdrv_clear_window_color_key(macdrv_window w) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + WineWindow* window = (WineWindow*)w; + + OnMainThread(^{ + window.colorKeyed = FALSE; + [window checkTransparency]; + }); + + [pool release]; +} + +/*********************************************************************** + * macdrv_window_use_per_pixel_alpha + */ +void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + WineWindow* window = (WineWindow*)w; + + OnMainThread(^{ + window.usePerPixelAlpha = use_per_pixel_alpha; + [window checkTransparency]; + }); + + [pool release]; +} diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index 5f877a823db..15e4062b198 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -88,13 +88,17 @@ static inline RECT rect_from_cgrect(CGRect cgrect) RECT window_rect; /* USER window rectangle relative to parent */ RECT whole_rect; /* Mac window rectangle for the whole window relative to parent */ RECT client_rect; /* client area relative to parent */ + COLORREF color_key; /* color key for layered window; CLR_INVALID is not color keyed */ BOOL on_screen : 1; /* is window ordered in? */ BOOL shaped : 1; /* is window using a custom region shape? */ + BOOL layered : 1; /* is window layered and with valid attributes? */ + BOOL per_pixel_alpha : 1; /* is window using per-pixel alpha? */ struct window_surface *surface; }; extern RGNDATA *get_region_data(HRGN hrgn, HDC hdc_lptodp) DECLSPEC_HIDDEN; -extern struct window_surface *create_surface(macdrv_window window, const RECT *rect) DECLSPEC_HIDDEN; +extern struct window_surface *create_surface(macdrv_window window, const RECT *rect, 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; #endif /* __WINE_MACDRV_H */ diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h index 7edc4ef9068..b5f3a14dd86 100644 --- a/dlls/winemac.drv/macdrv_cocoa.h +++ b/dlls/winemac.drv/macdrv_cocoa.h @@ -154,5 +154,10 @@ extern int macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev, extern int get_surface_region_rects(void *window_surface, const CGRect **rects, int *count) 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; +extern void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen, + CGFloat keyBlue) DECLSPEC_HIDDEN; +extern void macdrv_clear_window_color_key(macdrv_window w) DECLSPEC_HIDDEN; +extern void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha) DECLSPEC_HIDDEN; #endif /* __WINE_MACDRV_COCOA_H */ diff --git a/dlls/winemac.drv/surface.c b/dlls/winemac.drv/surface.c index 391657d54bf..748562f5a56 100644 --- a/dlls/winemac.drv/surface.c +++ b/dlls/winemac.drv/surface.c @@ -61,6 +61,7 @@ struct macdrv_window_surface struct window_surface header; macdrv_window window; RECT bounds; + BOOL use_alpha; RGNDATA *region_data; BYTE *bits; pthread_mutex_t mutex; @@ -184,7 +185,7 @@ static const struct window_surface_funcs macdrv_surface_funcs = /*********************************************************************** * create_surface */ -struct window_surface *create_surface(macdrv_window window, const RECT *rect) +struct window_surface *create_surface(macdrv_window window, const RECT *rect, BOOL use_alpha) { struct macdrv_window_surface *surface; int width = rect->right - rect->left, height = rect->bottom - rect->top; @@ -229,6 +230,7 @@ struct window_surface *create_surface(macdrv_window window, const RECT *rect) surface->header.ref = 1; surface->window = window; reset_bounds(&surface->bounds); + surface->use_alpha = use_alpha; surface->bits = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, surface->info.bmiHeader.biSizeImage); if (!surface->bits) goto failed; @@ -242,6 +244,15 @@ failed: return NULL; } +/*********************************************************************** + * set_surface_use_alpha + */ +void set_surface_use_alpha(struct window_surface *window_surface, BOOL use_alpha) +{ + struct macdrv_window_surface *surface = get_mac_surface(window_surface); + surface->use_alpha = use_alpha; +} + /*********************************************************************** * set_window_surface */ @@ -310,6 +321,7 @@ CGImageRef create_surface_image(void *window_surface, CGRect *rect, int copy_dat CGColorSpaceRef colorspace; CGDataProviderRef provider; int bytes_per_row, offset, size; + CGImageAlphaInfo alphaInfo; visrect = CGRectOffset(*rect, -surface->header.rect.left, -surface->header.rect.top); @@ -328,9 +340,10 @@ CGImageRef create_surface_image(void *window_surface, CGRect *rect, int copy_dat else provider = CGDataProviderCreateWithData(NULL, surface->bits + offset, size, NULL); + alphaInfo = surface->use_alpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst; cgimage = CGImageCreate(CGRectGetWidth(visrect), CGRectGetHeight(visrect), 8, 32, bytes_per_row, colorspace, - kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, + alphaInfo | kCGBitmapByteOrder32Little, provider, NULL, FALSE, kCGRenderingIntentDefault); CGDataProviderRelease(provider); CGColorSpaceRelease(colorspace); diff --git a/dlls/winemac.drv/window.c b/dlls/winemac.drv/window.c index 0e4eba9f132..75f68d72d5e 100644 --- a/dlls/winemac.drv/window.c +++ b/dlls/winemac.drv/window.c @@ -249,6 +249,7 @@ static struct macdrv_win_data *alloc_win_data(HWND hwnd) if ((data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data)))) { data->hwnd = hwnd; + data->color_key = CLR_INVALID; EnterCriticalSection(&win_data_section); if (!win_datas) win_datas = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); @@ -394,6 +395,82 @@ static void sync_window_region(struct macdrv_win_data *data, HRGN win_region) } +/*********************************************************************** + * add_bounds_rect + */ +static inline void add_bounds_rect(RECT *bounds, const RECT *rect) +{ + if (rect->left >= rect->right || rect->top >= rect->bottom) return; + bounds->left = min(bounds->left, rect->left); + bounds->top = min(bounds->top, rect->top); + bounds->right = max(bounds->right, rect->right); + bounds->bottom = max(bounds->bottom, rect->bottom); +} + + +/*********************************************************************** + * sync_window_opacity + */ +static void sync_window_opacity(struct macdrv_win_data *data, COLORREF key, BYTE alpha, + BOOL per_pixel_alpha, DWORD flags) +{ + CGFloat opacity = 1.0; + BOOL needs_flush = FALSE; + + if (flags & LWA_ALPHA) opacity = alpha / 255.0; + + TRACE("setting window %p/%p alpha to %g\n", data->hwnd, data->cocoa_window, opacity); + macdrv_set_window_alpha(data->cocoa_window, opacity); + + if (flags & LWA_COLORKEY) + { + /* FIXME: treat PALETTEINDEX and DIBINDEX as black */ + if ((key & (1 << 24)) || key >> 16 == 0x10ff) + key = RGB(0, 0, 0); + } + else + key = CLR_INVALID; + + if (data->color_key != key) + { + if (key == CLR_INVALID) + { + TRACE("clearing color-key for window %p/%p\n", data->hwnd, data->cocoa_window); + macdrv_clear_window_color_key(data->cocoa_window); + } + else + { + TRACE("setting color-key for window %p/%p to RGB %d,%d,%d\n", data->hwnd, data->cocoa_window, + GetRValue(key), GetGValue(key), GetBValue(key)); + macdrv_set_window_color_key(data->cocoa_window, GetRValue(key), GetGValue(key), GetBValue(key)); + } + + data->color_key = key; + needs_flush = TRUE; + } + + if (!data->per_pixel_alpha != !per_pixel_alpha) + { + macdrv_window_use_per_pixel_alpha(data->cocoa_window, per_pixel_alpha); + data->per_pixel_alpha = per_pixel_alpha; + needs_flush = TRUE; + } + + if (needs_flush && data->surface) + { + RECT *bounds; + RECT rect; + + rect = data->whole_rect; + OffsetRect(&rect, -data->whole_rect.left, -data->whole_rect.top); + data->surface->funcs->lock(data->surface); + bounds = data->surface->funcs->get_bounds(data->surface); + add_bounds_rect(bounds, &rect); + data->surface->funcs->unlock(data->surface); + } +} + + /********************************************************************** * create_cocoa_window * @@ -406,6 +483,9 @@ static void create_cocoa_window(struct macdrv_win_data *data) CGRect frame; DWORD style, ex_style; HRGN win_rgn; + COLORREF key; + BYTE alpha; + DWORD layered_flags; if ((win_rgn = CreateRectRgn(0, 0, 0, 0)) && GetWindowRgn(data->hwnd, win_rgn) == ERROR) @@ -442,6 +522,10 @@ static void create_cocoa_window(struct macdrv_win_data *data) /* set the window region */ if (win_rgn) sync_window_region(data, win_rgn); + /* set the window opacity */ + if (!GetLayeredWindowAttributes(data->hwnd, &key, &alpha, &layered_flags)) layered_flags = 0; + sync_window_opacity(data, key, alpha, FALSE, layered_flags); + done: if (win_rgn) DeleteObject(win_rgn); } @@ -461,6 +545,7 @@ static void destroy_cocoa_window(struct macdrv_win_data *data) macdrv_destroy_cocoa_window(data->cocoa_window); data->cocoa_window = 0; data->on_screen = FALSE; + data->color_key = CLR_INVALID; if (data->surface) window_surface_release(data->surface); data->surface = NULL; } @@ -731,6 +816,34 @@ void CDECL macdrv_DestroyWindow(HWND hwnd) } +/*********************************************************************** + * SetLayeredWindowAttributes (MACDRV.@) + * + * Set transparency attributes for a layered window. + */ +void CDECL macdrv_SetLayeredWindowAttributes(HWND hwnd, COLORREF key, BYTE alpha, DWORD flags) +{ + struct macdrv_win_data *data = get_win_data(hwnd); + + TRACE("hwnd %p key %#08x alpha %#02x flags %x\n", hwnd, key, alpha, flags); + + if (data) + { + data->layered = TRUE; + if (data->cocoa_window) + { + sync_window_opacity(data, key, alpha, FALSE, flags); + /* since layered attributes are now set, can now show the window */ + if ((GetWindowLongW(hwnd, GWL_STYLE) & WS_VISIBLE) && !data->on_screen) + show_window(data); + } + release_win_data(data); + } + else + FIXME("setting layered attributes on window %p of other process not supported\n", hwnd); +} + + /***************************************************************** * SetParent (MACDRV.@) */ @@ -801,8 +914,19 @@ void CDECL macdrv_SetWindowStyle(HWND hwnd, INT offset, STYLESTRUCT *style) if (!(data = get_win_data(hwnd))) return; if (data->cocoa_window) + { + DWORD changed = style->styleNew ^ style->styleOld; + set_cocoa_window_properties(data); + if (offset == GWL_EXSTYLE && (changed & WS_EX_LAYERED)) /* changing WS_EX_LAYERED resets attributes */ + { + data->layered = FALSE; + sync_window_opacity(data, 0, 0, FALSE, 0); + if (data->surface) set_surface_use_alpha(data->surface, FALSE); + } + } + release_win_data(data); } @@ -821,6 +945,108 @@ void CDECL macdrv_SetWindowText(HWND hwnd, LPCWSTR text) } +/*********************************************************************** + * UpdateLayeredWindow (MACDRV.@) + */ +BOOL CDECL macdrv_UpdateLayeredWindow(HWND hwnd, const UPDATELAYEREDWINDOWINFO *info, + const RECT *window_rect) +{ + struct window_surface *surface; + struct macdrv_win_data *data; + BLENDFUNCTION blend = { AC_SRC_OVER, 0, 255, 0 }; + BYTE alpha; + char buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])]; + BITMAPINFO *bmi = (BITMAPINFO *)buffer; + void *src_bits, *dst_bits; + RECT rect; + HDC hdc = 0; + HBITMAP dib; + BOOL ret = FALSE; + + if (!(data = get_win_data(hwnd))) return FALSE; + + data->layered = TRUE; + + rect = *window_rect; + OffsetRect(&rect, -window_rect->left, -window_rect->top); + + surface = data->surface; + if (!surface || memcmp(&surface->rect, &rect, sizeof(RECT))) + { + data->surface = create_surface(data->cocoa_window, &rect, TRUE); + set_window_surface(data->cocoa_window, data->surface); + if (surface) window_surface_release(surface); + surface = data->surface; + } + else set_surface_use_alpha(surface, TRUE); + + if (surface) window_surface_add_ref(surface); + release_win_data(data); + + if (!surface) return FALSE; + if (!info->hdcSrc) + { + window_surface_release(surface); + return TRUE; + } + + if (info->dwFlags & ULW_ALPHA) + { + /* Apply SourceConstantAlpha via window alpha, not blend. */ + alpha = info->pblend->SourceConstantAlpha; + blend = *info->pblend; + blend.SourceConstantAlpha = 0xff; + } + else + alpha = 0xff; + + dst_bits = surface->funcs->get_info(surface, bmi); + + if (!(dib = CreateDIBSection(info->hdcDst, bmi, DIB_RGB_COLORS, &src_bits, NULL, 0))) goto done; + if (!(hdc = CreateCompatibleDC(0))) goto done; + + SelectObject(hdc, dib); + if (info->prcDirty) + { + IntersectRect(&rect, &rect, info->prcDirty); + surface->funcs->lock(surface); + memcpy(src_bits, dst_bits, bmi->bmiHeader.biSizeImage); + surface->funcs->unlock(surface); + PatBlt(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, BLACKNESS); + } + if (!(ret = GdiAlphaBlend(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, + info->hdcSrc, + rect.left + (info->pptSrc ? info->pptSrc->x : 0), + rect.top + (info->pptSrc ? info->pptSrc->y : 0), + rect.right - rect.left, rect.bottom - rect.top, + blend))) + goto done; + + if ((data = get_win_data(hwnd))) + { + if (surface == data->surface) + { + surface->funcs->lock(surface); + memcpy(dst_bits, src_bits, bmi->bmiHeader.biSizeImage); + add_bounds_rect(surface->funcs->get_bounds(surface), &rect); + surface->funcs->unlock(surface); + surface->funcs->flush(surface); + } + + /* The ULW flags are a superset of the LWA flags. */ + sync_window_opacity(data, info->crKey, alpha, TRUE, info->dwFlags); + + release_win_data(data); + } + +done: + window_surface_release(surface); + if (hdc) DeleteDC(hdc); + if (dib) DeleteObject(dib); + return ret; +} + + /********************************************************************** * WindowMessage (MACDRV.@) */ @@ -903,7 +1129,7 @@ void CDECL macdrv_WindowPosChanging(HWND hwnd, HWND insert_after, UINT swp_flags } else if (!(swp_flags & SWP_SHOWWINDOW) && !(style & WS_VISIBLE)) goto done; - *surface = create_surface(data->cocoa_window, &surface_rect); + *surface = create_surface(data->cocoa_window, &surface_rect, FALSE); done: release_win_data(data); @@ -988,7 +1214,9 @@ void CDECL macdrv_WindowPosChanged(HWND hwnd, HWND insert_after, UINT swp_flags, if (!data->on_screen || (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED))) set_cocoa_window_properties(data); - if (!data->on_screen) + /* layered windows are not shown until their attributes are set */ + if (!data->on_screen && + (data->layered || !(GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYERED))) show_window(data); } diff --git a/dlls/winemac.drv/winemac.drv.spec b/dlls/winemac.drv/winemac.drv.spec index a5209da83f5..53b978598ef 100644 --- a/dlls/winemac.drv/winemac.drv.spec +++ b/dlls/winemac.drv/winemac.drv.spec @@ -9,10 +9,12 @@ @ cdecl DestroyWindow(long) macdrv_DestroyWindow @ cdecl EnumDisplayMonitors(long ptr ptr long) macdrv_EnumDisplayMonitors @ cdecl GetMonitorInfo(long ptr) macdrv_GetMonitorInfo +@ cdecl SetLayeredWindowAttributes(long long long long) macdrv_SetLayeredWindowAttributes @ cdecl SetParent(long long long) macdrv_SetParent @ cdecl SetWindowRgn(long long long) macdrv_SetWindowRgn @ cdecl SetWindowStyle(ptr long ptr) macdrv_SetWindowStyle @ cdecl SetWindowText(long wstr) macdrv_SetWindowText +@ cdecl UpdateLayeredWindow(long ptr ptr) macdrv_UpdateLayeredWindow @ cdecl WindowMessage(long long long long) macdrv_WindowMessage @ cdecl WindowPosChanged(long long long ptr ptr ptr ptr ptr) macdrv_WindowPosChanged @ cdecl WindowPosChanging(long long long ptr ptr ptr ptr) macdrv_WindowPosChanging