winemac: Implement layered windows: SetLayeredWindowAttributes() and UpdateLayeredWindow().

This commit is contained in:
Ken Thomases 2013-01-14 20:24:02 -06:00 committed by Alexandre Julliard
parent 2d4bcc47be
commit c0d43fb51f
7 changed files with 350 additions and 7 deletions

View File

@ -34,6 +34,11 @@ @interface WineWindow : NSPanel <NSWindowDelegate>
NSBezierPath* shape;
BOOL shapeChangedSinceLastDraw;
BOOL colorKeyed;
CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
BOOL usePerPixelAlpha;
}
@end

View File

@ -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];
}

View File

@ -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 */

View File

@ -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 */

View File

@ -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);

View File

@ -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);
}

View File

@ -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