winemac: Add proper locking in macdrv_surface_set_region().
[wine/multimedia.git] / dlls / winemac.drv / surface.c
blob29fc7f885eb2c3263bca12085929277d632a9df2
1 /*
2 * Mac driver window surface implementation
4 * Copyright 1993, 1994, 2011 Alexandre Julliard
5 * Copyright 2006 Damjan Jovanovic
6 * Copyright 2012, 2013 Ken Thomases for CodeWeavers, Inc.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include "config.h"
25 #include "macdrv.h"
26 #include "winuser.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(macdrv);
31 /* only for use on sanitized BITMAPINFO structures */
32 static inline int get_dib_info_size(const BITMAPINFO *info, UINT coloruse)
34 if (info->bmiHeader.biCompression == BI_BITFIELDS)
35 return sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD);
36 if (coloruse == DIB_PAL_COLORS)
37 return sizeof(BITMAPINFOHEADER) + info->bmiHeader.biClrUsed * sizeof(WORD);
38 return FIELD_OFFSET(BITMAPINFO, bmiColors[info->bmiHeader.biClrUsed]);
41 static inline int get_dib_stride(int width, int bpp)
43 return ((width * bpp + 31) >> 3) & ~3;
46 static inline int get_dib_image_size(const BITMAPINFO *info)
48 return get_dib_stride(info->bmiHeader.biWidth, info->bmiHeader.biBitCount)
49 * abs(info->bmiHeader.biHeight);
52 static inline void reset_bounds(RECT *bounds)
54 bounds->left = bounds->top = INT_MAX;
55 bounds->right = bounds->bottom = INT_MIN;
59 struct macdrv_window_surface
61 struct window_surface header;
62 macdrv_window window;
63 RECT bounds;
64 BOOL use_alpha;
65 RGNDATA *region_data;
66 BYTE *bits;
67 pthread_mutex_t mutex;
68 BITMAPINFO info; /* variable size, must be last */
71 static struct macdrv_window_surface *get_mac_surface(struct window_surface *surface)
73 return (struct macdrv_window_surface *)surface;
76 /***********************************************************************
77 * macdrv_surface_lock
79 static void macdrv_surface_lock(struct window_surface *window_surface)
81 struct macdrv_window_surface *surface = get_mac_surface(window_surface);
83 pthread_mutex_lock(&surface->mutex);
86 /***********************************************************************
87 * macdrv_surface_unlock
89 static void macdrv_surface_unlock(struct window_surface *window_surface)
91 struct macdrv_window_surface *surface = get_mac_surface(window_surface);
93 pthread_mutex_unlock(&surface->mutex);
96 /***********************************************************************
97 * macdrv_surface_get_bitmap_info
99 static void *macdrv_surface_get_bitmap_info(struct window_surface *window_surface,
100 BITMAPINFO *info)
102 struct macdrv_window_surface *surface = get_mac_surface(window_surface);
104 memcpy(info, &surface->info, get_dib_info_size(&surface->info, DIB_RGB_COLORS));
105 return surface->bits;
108 /***********************************************************************
109 * macdrv_surface_get_bounds
111 static RECT *macdrv_surface_get_bounds(struct window_surface *window_surface)
113 struct macdrv_window_surface *surface = get_mac_surface(window_surface);
115 return &surface->bounds;
118 /***********************************************************************
119 * macdrv_surface_set_region
121 static void macdrv_surface_set_region(struct window_surface *window_surface, HRGN region)
123 struct macdrv_window_surface *surface = get_mac_surface(window_surface);
125 TRACE("updating surface %p with %p\n", surface, region);
127 window_surface->funcs->lock(window_surface);
129 HeapFree(GetProcessHeap(), 0, surface->region_data);
130 surface->region_data = NULL;
132 if (region)
134 int rc = OffsetRgn(region, surface->header.rect.left, surface->header.rect.top);
135 if (rc != ERROR)
136 surface->region_data = get_region_data(region, 0);
139 window_surface->funcs->unlock(window_surface);
142 /***********************************************************************
143 * macdrv_surface_flush
145 static void macdrv_surface_flush(struct window_surface *window_surface)
147 struct macdrv_window_surface *surface = get_mac_surface(window_surface);
148 CGRect rect;
150 window_surface->funcs->lock(window_surface);
152 TRACE("flushing %p %s bounds %s bits %p\n", surface, wine_dbgstr_rect(&surface->header.rect),
153 wine_dbgstr_rect(&surface->bounds), surface->bits);
155 rect = cgrect_from_rect(surface->bounds);
156 rect = CGRectOffset(rect, surface->header.rect.left, surface->header.rect.top);
157 reset_bounds(&surface->bounds);
159 window_surface->funcs->unlock(window_surface);
161 if (!CGRectIsEmpty(rect))
162 macdrv_window_needs_display(surface->window, rect);
165 /***********************************************************************
166 * macdrv_surface_destroy
168 static void macdrv_surface_destroy(struct window_surface *window_surface)
170 struct macdrv_window_surface *surface = get_mac_surface(window_surface);
172 TRACE("freeing %p bits %p\n", surface, surface->bits);
173 HeapFree(GetProcessHeap(), 0, surface->bits);
174 pthread_mutex_destroy(&surface->mutex);
175 HeapFree(GetProcessHeap(), 0, surface);
178 static const struct window_surface_funcs macdrv_surface_funcs =
180 macdrv_surface_lock,
181 macdrv_surface_unlock,
182 macdrv_surface_get_bitmap_info,
183 macdrv_surface_get_bounds,
184 macdrv_surface_set_region,
185 macdrv_surface_flush,
186 macdrv_surface_destroy,
189 /***********************************************************************
190 * create_surface
192 struct window_surface *create_surface(macdrv_window window, const RECT *rect, BOOL use_alpha)
194 struct macdrv_window_surface *surface;
195 int width = rect->right - rect->left, height = rect->bottom - rect->top;
196 DWORD *colors;
197 pthread_mutexattr_t attr;
198 int err;
200 surface = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
201 FIELD_OFFSET(struct macdrv_window_surface, info.bmiColors[3]));
202 if (!surface) return NULL;
204 err = pthread_mutexattr_init(&attr);
205 if (!err)
207 err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
208 if (!err)
209 err = pthread_mutex_init(&surface->mutex, &attr);
210 pthread_mutexattr_destroy(&attr);
212 if (err)
214 HeapFree(GetProcessHeap(), 0, surface);
215 return NULL;
218 surface->info.bmiHeader.biSize = sizeof(surface->info.bmiHeader);
219 surface->info.bmiHeader.biWidth = width;
220 surface->info.bmiHeader.biHeight = height; /* bottom-up */
221 surface->info.bmiHeader.biPlanes = 1;
222 surface->info.bmiHeader.biBitCount = 32;
223 surface->info.bmiHeader.biSizeImage = get_dib_image_size(&surface->info);
224 surface->info.bmiHeader.biCompression = BI_RGB;
225 surface->info.bmiHeader.biClrUsed = 0;
227 colors = (DWORD *)((char *)&surface->info + surface->info.bmiHeader.biSize);
228 colors[0] = 0x00ff0000;
229 colors[1] = 0x0000ff00;
230 colors[2] = 0x000000ff;
232 surface->header.funcs = &macdrv_surface_funcs;
233 surface->header.rect = *rect;
234 surface->header.ref = 1;
235 surface->window = window;
236 reset_bounds(&surface->bounds);
237 surface->use_alpha = use_alpha;
238 surface->bits = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, surface->info.bmiHeader.biSizeImage);
239 if (!surface->bits) goto failed;
241 TRACE("created %p for %p %s bits %p-%p\n", surface, window, wine_dbgstr_rect(rect),
242 surface->bits, surface->bits + surface->info.bmiHeader.biSizeImage);
244 return &surface->header;
246 failed:
247 macdrv_surface_destroy(&surface->header);
248 return NULL;
251 /***********************************************************************
252 * set_surface_use_alpha
254 void set_surface_use_alpha(struct window_surface *window_surface, BOOL use_alpha)
256 struct macdrv_window_surface *surface = get_mac_surface(window_surface);
257 surface->use_alpha = use_alpha;
260 /***********************************************************************
261 * set_window_surface
263 void set_window_surface(macdrv_window window, struct window_surface *window_surface)
265 struct macdrv_window_surface *surface = get_mac_surface(window_surface);
266 macdrv_set_window_surface(window, window_surface, surface ? &surface->mutex : NULL);
269 /***********************************************************************
270 * get_surface_region_rects
272 * Caller must hold the surface lock. Indirectly returns the surface
273 * region rects. Returns zero if the surface has no region set (it is
274 * unclipped); returns non-zero if the surface does have a region set.
276 * IMPORTANT: This function is called from non-Wine threads, so it
277 * must not use Win32 or Wine functions, including debug
278 * logging.
280 int get_surface_region_rects(void *window_surface, const CGRect **rects, int *count)
282 struct macdrv_window_surface *surface = get_mac_surface(window_surface);
284 if (surface->region_data)
286 *rects = (const CGRect*)surface->region_data->Buffer;
287 *count = surface->region_data->rdh.nCount;
289 else
291 *rects = NULL;
292 *count = 0;
295 return (surface->region_data != NULL);
298 /***********************************************************************
299 * create_surface_image
301 * Caller must hold the surface lock. On input, *rect is the requested
302 * image rect, relative to the window whole_rect, a.k.a. visible_rect.
303 * On output, it's been intersected with that part backed by the surface
304 * and is the actual size of the returned image. copy_data indicates if
305 * the caller will keep the returned image beyond the point where the
306 * surface bits can be guaranteed to remain valid and unchanged. If so,
307 * the bits are copied instead of merely referenced by the image.
309 * IMPORTANT: This function is called from non-Wine threads, so it
310 * must not use Win32 or Wine functions, including debug
311 * logging.
313 CGImageRef create_surface_image(void *window_surface, CGRect *rect, int copy_data)
315 CGImageRef cgimage = NULL;
316 struct macdrv_window_surface *surface = get_mac_surface(window_surface);
317 int width, height;
319 width = surface->header.rect.right - surface->header.rect.left;
320 height = surface->header.rect.bottom - surface->header.rect.top;
321 *rect = CGRectIntersection(cgrect_from_rect(surface->header.rect), *rect);
322 if (!CGRectIsEmpty(*rect))
324 CGRect visrect;
325 CGColorSpaceRef colorspace;
326 CGDataProviderRef provider;
327 int bytes_per_row, offset, size;
328 CGImageAlphaInfo alphaInfo;
330 visrect = CGRectOffset(*rect, -surface->header.rect.left, -surface->header.rect.top);
332 colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
333 bytes_per_row = get_dib_stride(width, 32);
334 offset = CGRectGetMinX(visrect) * 4 + (height - CGRectGetMaxY(visrect)) * bytes_per_row;
335 size = min(CGRectGetHeight(visrect) * bytes_per_row,
336 surface->info.bmiHeader.biSizeImage - offset);
338 if (copy_data)
340 CFDataRef data = CFDataCreate(NULL, (UInt8*)surface->bits + offset, size);
341 provider = CGDataProviderCreateWithCFData(data);
342 CFRelease(data);
344 else
345 provider = CGDataProviderCreateWithData(NULL, surface->bits + offset, size, NULL);
347 alphaInfo = surface->use_alpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
348 cgimage = CGImageCreate(CGRectGetWidth(visrect), CGRectGetHeight(visrect),
349 8, 32, bytes_per_row, colorspace,
350 alphaInfo | kCGBitmapByteOrder32Little,
351 provider, NULL, FALSE, kCGRenderingIntentDefault);
352 CGDataProviderRelease(provider);
353 CGColorSpaceRelease(colorspace);
356 return cgimage;