wine/dlls/winemac.drv/surface.c

354 lines
12 KiB
C

/*
* Mac driver window surface implementation
*
* Copyright 1993, 1994, 2011 Alexandre Julliard
* Copyright 2006 Damjan Jovanovic
* Copyright 2012, 2013 Ken Thomases for CodeWeavers, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include "macdrv.h"
#include "winuser.h"
WINE_DEFAULT_DEBUG_CHANNEL(macdrv);
/* only for use on sanitized BITMAPINFO structures */
static inline int get_dib_info_size(const BITMAPINFO *info, UINT coloruse)
{
if (info->bmiHeader.biCompression == BI_BITFIELDS)
return sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD);
if (coloruse == DIB_PAL_COLORS)
return sizeof(BITMAPINFOHEADER) + info->bmiHeader.biClrUsed * sizeof(WORD);
return FIELD_OFFSET(BITMAPINFO, bmiColors[info->bmiHeader.biClrUsed]);
}
static inline int get_dib_stride(int width, int bpp)
{
return ((width * bpp + 31) >> 3) & ~3;
}
static inline int get_dib_image_size(const BITMAPINFO *info)
{
return get_dib_stride(info->bmiHeader.biWidth, info->bmiHeader.biBitCount)
* abs(info->bmiHeader.biHeight);
}
static inline void reset_bounds(RECT *bounds)
{
bounds->left = bounds->top = INT_MAX;
bounds->right = bounds->bottom = INT_MIN;
}
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;
BITMAPINFO info; /* variable size, must be last */
};
static struct macdrv_window_surface *get_mac_surface(struct window_surface *surface)
{
return (struct macdrv_window_surface *)surface;
}
/***********************************************************************
* macdrv_surface_lock
*/
static void macdrv_surface_lock(struct window_surface *window_surface)
{
struct macdrv_window_surface *surface = get_mac_surface(window_surface);
pthread_mutex_lock(&surface->mutex);
}
/***********************************************************************
* macdrv_surface_unlock
*/
static void macdrv_surface_unlock(struct window_surface *window_surface)
{
struct macdrv_window_surface *surface = get_mac_surface(window_surface);
pthread_mutex_unlock(&surface->mutex);
}
/***********************************************************************
* macdrv_surface_get_bitmap_info
*/
static void *macdrv_surface_get_bitmap_info(struct window_surface *window_surface,
BITMAPINFO *info)
{
struct macdrv_window_surface *surface = get_mac_surface(window_surface);
memcpy(info, &surface->info, get_dib_info_size(&surface->info, DIB_RGB_COLORS));
return surface->bits;
}
/***********************************************************************
* macdrv_surface_get_bounds
*/
static RECT *macdrv_surface_get_bounds(struct window_surface *window_surface)
{
struct macdrv_window_surface *surface = get_mac_surface(window_surface);
return &surface->bounds;
}
/***********************************************************************
* macdrv_surface_set_region
*/
static void macdrv_surface_set_region(struct window_surface *window_surface, HRGN region)
{
struct macdrv_window_surface *surface = get_mac_surface(window_surface);
TRACE("updating surface %p with %p\n", surface, region);
HeapFree(GetProcessHeap(), 0, surface->region_data);
surface->region_data = NULL;
if (region)
{
int rc = OffsetRgn(region, surface->header.rect.left, surface->header.rect.top);
if (rc != ERROR)
surface->region_data = get_region_data(region, 0);
}
}
/***********************************************************************
* macdrv_surface_flush
*/
static void macdrv_surface_flush(struct window_surface *window_surface)
{
struct macdrv_window_surface *surface = get_mac_surface(window_surface);
CGRect rect;
window_surface->funcs->lock(window_surface);
TRACE("flushing %p %s bounds %s bits %p\n", surface, wine_dbgstr_rect(&surface->header.rect),
wine_dbgstr_rect(&surface->bounds), surface->bits);
rect = cgrect_from_rect(surface->bounds);
rect = CGRectOffset(rect, surface->header.rect.left, surface->header.rect.top);
reset_bounds(&surface->bounds);
window_surface->funcs->unlock(window_surface);
if (!CGRectIsEmpty(rect))
macdrv_window_needs_display(surface->window, rect);
}
/***********************************************************************
* macdrv_surface_destroy
*/
static void macdrv_surface_destroy(struct window_surface *window_surface)
{
struct macdrv_window_surface *surface = get_mac_surface(window_surface);
TRACE("freeing %p bits %p\n", surface, surface->bits);
HeapFree(GetProcessHeap(), 0, surface->bits);
pthread_mutex_destroy(&surface->mutex);
HeapFree(GetProcessHeap(), 0, surface);
}
static const struct window_surface_funcs macdrv_surface_funcs =
{
macdrv_surface_lock,
macdrv_surface_unlock,
macdrv_surface_get_bitmap_info,
macdrv_surface_get_bounds,
macdrv_surface_set_region,
macdrv_surface_flush,
macdrv_surface_destroy,
};
/***********************************************************************
* create_surface
*/
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;
DWORD *colors;
pthread_mutexattr_t attr;
int err;
surface = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
FIELD_OFFSET(struct macdrv_window_surface, info.bmiColors[3]));
if (!surface) return NULL;
err = pthread_mutexattr_init(&attr);
if (!err)
{
err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
if (!err)
err = pthread_mutex_init(&surface->mutex, &attr);
pthread_mutexattr_destroy(&attr);
}
if (err)
{
HeapFree(GetProcessHeap(), 0, surface);
return NULL;
}
surface->info.bmiHeader.biSize = sizeof(surface->info.bmiHeader);
surface->info.bmiHeader.biWidth = width;
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);
surface->info.bmiHeader.biCompression = BI_RGB;
surface->info.bmiHeader.biClrUsed = 0;
colors = (DWORD *)((char *)&surface->info + surface->info.bmiHeader.biSize);
colors[0] = 0x00ff0000;
colors[1] = 0x0000ff00;
colors[2] = 0x000000ff;
surface->header.funcs = &macdrv_surface_funcs;
surface->header.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;
TRACE("created %p for %p %s bits %p-%p\n", surface, window, wine_dbgstr_rect(rect),
surface->bits, surface->bits + surface->info.bmiHeader.biSizeImage);
return &surface->header;
failed:
macdrv_surface_destroy(&surface->header);
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
*/
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_region_rects
*
* Caller must hold the surface lock. Indirectly returns the surface
* region rects. Returns zero if the surface has no region set (it is
* unclipped); returns non-zero if the surface does have a region set.
*
* IMPORTANT: This function is called from non-Wine threads, so it
* must not use Win32 or Wine functions, including debug
* logging.
*/
int get_surface_region_rects(void *window_surface, const CGRect **rects, int *count)
{
struct macdrv_window_surface *surface = get_mac_surface(window_surface);
if (surface->region_data)
{
*rects = (const CGRect*)surface->region_data->Buffer;
*count = surface->region_data->rdh.nCount;
}
else
{
*rects = NULL;
*count = 0;
}
return (surface->region_data != NULL);
}
/***********************************************************************
* create_surface_image
*
* Caller must hold the surface lock. On input, *rect is the requested
* image rect, relative to the window whole_rect, a.k.a. visible_rect.
* On output, it's been intersected with that part backed by the surface
* and is the actual size of the returned image. copy_data indicates if
* the caller will keep the returned image beyond the point where the
* surface bits can be guaranteed to remain valid and unchanged. If so,
* the bits are copied instead of merely referenced by the image.
*
* IMPORTANT: This function is called from non-Wine threads, so it
* must not use Win32 or Wine functions, including debug
* logging.
*/
CGImageRef create_surface_image(void *window_surface, CGRect *rect, int copy_data)
{
CGImageRef cgimage = NULL;
struct macdrv_window_surface *surface = get_mac_surface(window_surface);
int width, height;
width = surface->header.rect.right - surface->header.rect.left;
height = surface->header.rect.bottom - surface->header.rect.top;
*rect = CGRectIntersection(cgrect_from_rect(surface->header.rect), *rect);
if (!CGRectIsEmpty(*rect))
{
CGRect visrect;
CGColorSpaceRef colorspace;
CGDataProviderRef provider;
int bytes_per_row, offset, size;
CGImageAlphaInfo alphaInfo;
visrect = CGRectOffset(*rect, -surface->header.rect.left, -surface->header.rect.top);
colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
bytes_per_row = get_dib_stride(width, 32);
offset = CGRectGetMinX(visrect) * 4 + (height - CGRectGetMaxY(visrect)) * bytes_per_row;
size = min(CGRectGetHeight(visrect) * bytes_per_row,
surface->info.bmiHeader.biSizeImage - offset);
if (copy_data)
{
CFDataRef data = CFDataCreate(NULL, (UInt8*)surface->bits + offset, size);
provider = CGDataProviderCreateWithCFData(data);
CFRelease(data);
}
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,
alphaInfo | kCGBitmapByteOrder32Little,
provider, NULL, FALSE, kCGRenderingIntentDefault);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorspace);
}
return cgimage;
}