Release 1.5.29.
[wine] / dlls / winemac.drv / surface.c
1 /*
2  * Mac driver window surface implementation
3  *
4  * Copyright 1993, 1994, 2011 Alexandre Julliard
5  * Copyright 2006 Damjan Jovanovic
6  * Copyright 2012, 2013 Ken Thomases for CodeWeavers, Inc.
7  *
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.
12  *
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.
17  *
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
21  */
22
23 #include "config.h"
24
25 #include "macdrv.h"
26 #include "winuser.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(macdrv);
29
30
31 /* only for use on sanitized BITMAPINFO structures */
32 static inline int get_dib_info_size(const BITMAPINFO *info, UINT coloruse)
33 {
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]);
39 }
40
41 static inline int get_dib_stride(int width, int bpp)
42 {
43     return ((width * bpp + 31) >> 3) & ~3;
44 }
45
46 static inline int get_dib_image_size(const BITMAPINFO *info)
47 {
48     return get_dib_stride(info->bmiHeader.biWidth, info->bmiHeader.biBitCount)
49         * abs(info->bmiHeader.biHeight);
50 }
51
52 static inline void reset_bounds(RECT *bounds)
53 {
54     bounds->left = bounds->top = INT_MAX;
55     bounds->right = bounds->bottom = INT_MIN;
56 }
57
58
59 struct macdrv_window_surface
60 {
61     struct window_surface   header;
62     macdrv_window           window;
63     RECT                    bounds;
64     HRGN                    region;
65     HRGN                    drawn;
66     BOOL                    use_alpha;
67     RGNDATA                *blit_data;
68     BYTE                   *bits;
69     pthread_mutex_t         mutex;
70     BITMAPINFO              info;   /* variable size, must be last */
71 };
72
73 static struct macdrv_window_surface *get_mac_surface(struct window_surface *surface)
74 {
75     return (struct macdrv_window_surface *)surface;
76 }
77
78 /***********************************************************************
79  *              update_blit_data
80  */
81 static void update_blit_data(struct macdrv_window_surface *surface)
82 {
83     HeapFree(GetProcessHeap(), 0, surface->blit_data);
84     surface->blit_data = NULL;
85
86     if (surface->drawn)
87     {
88         HRGN blit = CreateRectRgn(0, 0, 0, 0);
89
90         if (CombineRgn(blit, surface->drawn, 0, RGN_COPY) > NULLREGION &&
91             (!surface->region || CombineRgn(blit, blit, surface->region, RGN_AND) > NULLREGION) &&
92             OffsetRgn(blit, surface->header.rect.left, surface->header.rect.top) > NULLREGION)
93             surface->blit_data = get_region_data(blit, 0);
94
95         DeleteObject(blit);
96     }
97 }
98
99 /***********************************************************************
100  *              macdrv_surface_lock
101  */
102 static void macdrv_surface_lock(struct window_surface *window_surface)
103 {
104     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
105
106     pthread_mutex_lock(&surface->mutex);
107 }
108
109 /***********************************************************************
110  *              macdrv_surface_unlock
111  */
112 static void macdrv_surface_unlock(struct window_surface *window_surface)
113 {
114     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
115
116     pthread_mutex_unlock(&surface->mutex);
117 }
118
119 /***********************************************************************
120  *              macdrv_surface_get_bitmap_info
121  */
122 static void *macdrv_surface_get_bitmap_info(struct window_surface *window_surface,
123                                             BITMAPINFO *info)
124 {
125     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
126
127     memcpy(info, &surface->info, get_dib_info_size(&surface->info, DIB_RGB_COLORS));
128     return surface->bits;
129 }
130
131 /***********************************************************************
132  *              macdrv_surface_get_bounds
133  */
134 static RECT *macdrv_surface_get_bounds(struct window_surface *window_surface)
135 {
136     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
137
138     return &surface->bounds;
139 }
140
141 /***********************************************************************
142  *              macdrv_surface_set_region
143  */
144 static void macdrv_surface_set_region(struct window_surface *window_surface, HRGN region)
145 {
146     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
147
148     TRACE("updating surface %p with %p\n", surface, region);
149
150     window_surface->funcs->lock(window_surface);
151
152     if (region)
153     {
154         if (!surface->region) surface->region = CreateRectRgn(0, 0, 0, 0);
155         CombineRgn(surface->region, region, 0, RGN_COPY);
156     }
157     else
158     {
159         if (surface->region) DeleteObject(surface->region);
160         surface->region = 0;
161     }
162     update_blit_data(surface);
163
164     window_surface->funcs->unlock(window_surface);
165 }
166
167 /***********************************************************************
168  *              macdrv_surface_flush
169  */
170 static void macdrv_surface_flush(struct window_surface *window_surface)
171 {
172     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
173     CGRect rect;
174     HRGN region;
175
176     window_surface->funcs->lock(window_surface);
177
178     TRACE("flushing %p %s bounds %s bits %p\n", surface, wine_dbgstr_rect(&surface->header.rect),
179           wine_dbgstr_rect(&surface->bounds), surface->bits);
180
181     rect = cgrect_from_rect(surface->bounds);
182     rect = CGRectOffset(rect, surface->header.rect.left, surface->header.rect.top);
183
184     if (!IsRectEmpty(&surface->bounds) && (region = CreateRectRgnIndirect(&surface->bounds)))
185     {
186         if (surface->drawn)
187         {
188             CombineRgn(surface->drawn, surface->drawn, region, RGN_OR);
189             DeleteObject(region);
190         }
191         else
192             surface->drawn = region;
193     }
194     update_blit_data(surface);
195     reset_bounds(&surface->bounds);
196
197     window_surface->funcs->unlock(window_surface);
198
199     if (!CGRectIsEmpty(rect))
200         macdrv_window_needs_display(surface->window, rect);
201 }
202
203 /***********************************************************************
204  *              macdrv_surface_destroy
205  */
206 static void macdrv_surface_destroy(struct window_surface *window_surface)
207 {
208     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
209
210     TRACE("freeing %p bits %p\n", surface, surface->bits);
211     HeapFree(GetProcessHeap(), 0, surface->bits);
212     pthread_mutex_destroy(&surface->mutex);
213     HeapFree(GetProcessHeap(), 0, surface);
214 }
215
216 static const struct window_surface_funcs macdrv_surface_funcs =
217 {
218     macdrv_surface_lock,
219     macdrv_surface_unlock,
220     macdrv_surface_get_bitmap_info,
221     macdrv_surface_get_bounds,
222     macdrv_surface_set_region,
223     macdrv_surface_flush,
224     macdrv_surface_destroy,
225 };
226
227 /***********************************************************************
228  *              create_surface
229  */
230 struct window_surface *create_surface(macdrv_window window, const RECT *rect,
231                                       struct window_surface *old_surface, BOOL use_alpha)
232 {
233     struct macdrv_window_surface *surface;
234     struct macdrv_window_surface *old_mac_surface = get_mac_surface(old_surface);
235     int width = rect->right - rect->left, height = rect->bottom - rect->top;
236     DWORD *colors;
237     pthread_mutexattr_t attr;
238     int err;
239     DWORD window_background;
240
241     surface = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
242                         FIELD_OFFSET(struct macdrv_window_surface, info.bmiColors[3]));
243     if (!surface) return NULL;
244
245     err = pthread_mutexattr_init(&attr);
246     if (!err)
247     {
248         err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
249         if (!err)
250             err = pthread_mutex_init(&surface->mutex, &attr);
251         pthread_mutexattr_destroy(&attr);
252     }
253     if (err)
254     {
255         HeapFree(GetProcessHeap(), 0, surface);
256         return NULL;
257     }
258
259     surface->info.bmiHeader.biSize        = sizeof(surface->info.bmiHeader);
260     surface->info.bmiHeader.biWidth       = width;
261     surface->info.bmiHeader.biHeight      = height; /* bottom-up */
262     surface->info.bmiHeader.biPlanes      = 1;
263     surface->info.bmiHeader.biBitCount    = 32;
264     surface->info.bmiHeader.biSizeImage   = get_dib_image_size(&surface->info);
265     surface->info.bmiHeader.biCompression = BI_RGB;
266     surface->info.bmiHeader.biClrUsed     = 0;
267
268     colors = (DWORD *)((char *)&surface->info + surface->info.bmiHeader.biSize);
269     colors[0] = 0x00ff0000;
270     colors[1] = 0x0000ff00;
271     colors[2] = 0x000000ff;
272
273     surface->header.funcs = &macdrv_surface_funcs;
274     surface->header.rect  = *rect;
275     surface->header.ref   = 1;
276     surface->window = window;
277     reset_bounds(&surface->bounds);
278     if (old_mac_surface && old_mac_surface->drawn)
279     {
280         surface->drawn = CreateRectRgnIndirect(rect);
281         OffsetRgn(surface->drawn, -rect->left, -rect->top);
282         if (CombineRgn(surface->drawn, surface->drawn, old_mac_surface->drawn, RGN_AND) <= NULLREGION)
283         {
284             DeleteObject(surface->drawn);
285             surface->drawn = 0;
286         }
287     }
288     update_blit_data(surface);
289     surface->use_alpha = use_alpha;
290     surface->bits = HeapAlloc(GetProcessHeap(), 0, surface->info.bmiHeader.biSizeImage);
291     if (!surface->bits) goto failed;
292     window_background = macdrv_window_background_color();
293     memset_pattern4(surface->bits, &window_background, surface->info.bmiHeader.biSizeImage);
294
295     TRACE("created %p for %p %s bits %p-%p\n", surface, window, wine_dbgstr_rect(rect),
296           surface->bits, surface->bits + surface->info.bmiHeader.biSizeImage);
297
298     return &surface->header;
299
300 failed:
301     macdrv_surface_destroy(&surface->header);
302     return NULL;
303 }
304
305 /***********************************************************************
306  *              set_surface_use_alpha
307  */
308 void set_surface_use_alpha(struct window_surface *window_surface, BOOL use_alpha)
309 {
310     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
311     surface->use_alpha = use_alpha;
312 }
313
314 /***********************************************************************
315  *              set_window_surface
316  */
317 void set_window_surface(macdrv_window window, struct window_surface *window_surface)
318 {
319     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
320     macdrv_set_window_surface(window, window_surface, surface ? &surface->mutex : NULL);
321 }
322
323 /***********************************************************************
324  *              get_surface_blit_rects
325  *
326  * Caller must hold the surface lock.  Indirectly returns the surface
327  * blit region rects.  Returns zero if the surface has nothing to blit;
328  * returns non-zero if the surface does have rects to blit (drawn area
329  * which isn't clipped away by a surface region).
330  *
331  * IMPORTANT: This function is called from non-Wine threads, so it
332  *            must not use Win32 or Wine functions, including debug
333  *            logging.
334  */
335 int get_surface_blit_rects(void *window_surface, const CGRect **rects, int *count)
336 {
337     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
338
339     if (surface->blit_data)
340     {
341         *rects = (const CGRect*)surface->blit_data->Buffer;
342         *count = surface->blit_data->rdh.nCount;
343     }
344     else
345     {
346         *rects = NULL;
347         *count = 0;
348     }
349
350     return (surface->blit_data != NULL);
351 }
352
353 /***********************************************************************
354  *              create_surface_image
355  *
356  * Caller must hold the surface lock.  On input, *rect is the requested
357  * image rect, relative to the window whole_rect, a.k.a. visible_rect.
358  * On output, it's been intersected with that part backed by the surface
359  * and is the actual size of the returned image.  copy_data indicates if
360  * the caller will keep the returned image beyond the point where the
361  * surface bits can be guaranteed to remain valid and unchanged.  If so,
362  * the bits are copied instead of merely referenced by the image.
363  *
364  * IMPORTANT: This function is called from non-Wine threads, so it
365  *            must not use Win32 or Wine functions, including debug
366  *            logging.
367  */
368 CGImageRef create_surface_image(void *window_surface, CGRect *rect, int copy_data)
369 {
370     CGImageRef cgimage = NULL;
371     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
372     int width, height;
373
374     width  = surface->header.rect.right - surface->header.rect.left;
375     height = surface->header.rect.bottom - surface->header.rect.top;
376     *rect = CGRectIntersection(cgrect_from_rect(surface->header.rect), *rect);
377     if (!CGRectIsEmpty(*rect))
378     {
379         CGRect visrect;
380         CGColorSpaceRef colorspace;
381         CGDataProviderRef provider;
382         int bytes_per_row, offset, size;
383         CGImageAlphaInfo alphaInfo;
384
385         visrect = CGRectOffset(*rect, -surface->header.rect.left, -surface->header.rect.top);
386
387         colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
388         bytes_per_row = get_dib_stride(width, 32);
389         offset = CGRectGetMinX(visrect) * 4 + (height - CGRectGetMaxY(visrect)) * bytes_per_row;
390         size = min(CGRectGetHeight(visrect) * bytes_per_row,
391                    surface->info.bmiHeader.biSizeImage - offset);
392
393         if (copy_data)
394         {
395             CFDataRef data = CFDataCreate(NULL, (UInt8*)surface->bits + offset, size);
396             provider = CGDataProviderCreateWithCFData(data);
397             CFRelease(data);
398         }
399         else
400             provider = CGDataProviderCreateWithData(NULL, surface->bits + offset, size, NULL);
401
402         alphaInfo = surface->use_alpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
403         cgimage = CGImageCreate(CGRectGetWidth(visrect), CGRectGetHeight(visrect),
404                                 8, 32, bytes_per_row, colorspace,
405                                 alphaInfo | kCGBitmapByteOrder32Little,
406                                 provider, NULL, FALSE, kCGRenderingIntentDefault);
407         CGDataProviderRelease(provider);
408         CGColorSpaceRelease(colorspace);
409     }
410
411     return cgimage;
412 }
413
414 /***********************************************************************
415  *              surface_clip_to_visible_rect
416  *
417  * Intersect the accumulated drawn region with a new visible rect,
418  * effectively discarding stale drawing in the surface slack area.
419  */
420 void surface_clip_to_visible_rect(struct window_surface *window_surface, const RECT *visible_rect)
421 {
422     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
423
424     window_surface->funcs->lock(window_surface);
425
426     if (surface->drawn)
427     {
428         RECT rect;
429         HRGN region;
430
431         rect = *visible_rect;
432         OffsetRect(&rect, -rect.left, -rect.top);
433
434         if ((region = CreateRectRgnIndirect(&rect)))
435         {
436             CombineRgn(surface->drawn, surface->drawn, region, RGN_AND);
437             DeleteObject(region);
438
439             update_blit_data(surface);
440         }
441     }
442
443     window_surface->funcs->unlock(window_surface);
444 }