winemac: Add support for delay-rendered (a.k.a. promised) clipboard data.
[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     BOOL                    use_alpha;
65     RGNDATA                *region_data;
66     BYTE                   *bits;
67     pthread_mutex_t         mutex;
68     BITMAPINFO              info;   /* variable size, must be last */
69 };
70
71 static struct macdrv_window_surface *get_mac_surface(struct window_surface *surface)
72 {
73     return (struct macdrv_window_surface *)surface;
74 }
75
76 /***********************************************************************
77  *              macdrv_surface_lock
78  */
79 static void macdrv_surface_lock(struct window_surface *window_surface)
80 {
81     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
82
83     pthread_mutex_lock(&surface->mutex);
84 }
85
86 /***********************************************************************
87  *              macdrv_surface_unlock
88  */
89 static void macdrv_surface_unlock(struct window_surface *window_surface)
90 {
91     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
92
93     pthread_mutex_unlock(&surface->mutex);
94 }
95
96 /***********************************************************************
97  *              macdrv_surface_get_bitmap_info
98  */
99 static void *macdrv_surface_get_bitmap_info(struct window_surface *window_surface,
100                                             BITMAPINFO *info)
101 {
102     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
103
104     memcpy(info, &surface->info, get_dib_info_size(&surface->info, DIB_RGB_COLORS));
105     return surface->bits;
106 }
107
108 /***********************************************************************
109  *              macdrv_surface_get_bounds
110  */
111 static RECT *macdrv_surface_get_bounds(struct window_surface *window_surface)
112 {
113     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
114
115     return &surface->bounds;
116 }
117
118 /***********************************************************************
119  *              macdrv_surface_set_region
120  */
121 static void macdrv_surface_set_region(struct window_surface *window_surface, HRGN region)
122 {
123     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
124
125     TRACE("updating surface %p with %p\n", surface, region);
126
127     HeapFree(GetProcessHeap(), 0, surface->region_data);
128     surface->region_data = NULL;
129
130     if (region)
131     {
132         int rc = OffsetRgn(region, surface->header.rect.left, surface->header.rect.top);
133         if (rc != ERROR)
134             surface->region_data = get_region_data(region, 0);
135     }
136 }
137
138 /***********************************************************************
139  *              macdrv_surface_flush
140  */
141 static void macdrv_surface_flush(struct window_surface *window_surface)
142 {
143     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
144     CGRect rect;
145
146     window_surface->funcs->lock(window_surface);
147
148     TRACE("flushing %p %s bounds %s bits %p\n", surface, wine_dbgstr_rect(&surface->header.rect),
149           wine_dbgstr_rect(&surface->bounds), surface->bits);
150
151     rect = cgrect_from_rect(surface->bounds);
152     rect = CGRectOffset(rect, surface->header.rect.left, surface->header.rect.top);
153     reset_bounds(&surface->bounds);
154
155     window_surface->funcs->unlock(window_surface);
156
157     if (!CGRectIsEmpty(rect))
158         macdrv_window_needs_display(surface->window, rect);
159 }
160
161 /***********************************************************************
162  *              macdrv_surface_destroy
163  */
164 static void macdrv_surface_destroy(struct window_surface *window_surface)
165 {
166     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
167
168     TRACE("freeing %p bits %p\n", surface, surface->bits);
169     HeapFree(GetProcessHeap(), 0, surface->bits);
170     pthread_mutex_destroy(&surface->mutex);
171     HeapFree(GetProcessHeap(), 0, surface);
172 }
173
174 static const struct window_surface_funcs macdrv_surface_funcs =
175 {
176     macdrv_surface_lock,
177     macdrv_surface_unlock,
178     macdrv_surface_get_bitmap_info,
179     macdrv_surface_get_bounds,
180     macdrv_surface_set_region,
181     macdrv_surface_flush,
182     macdrv_surface_destroy,
183 };
184
185 /***********************************************************************
186  *              create_surface
187  */
188 struct window_surface *create_surface(macdrv_window window, const RECT *rect, BOOL use_alpha)
189 {
190     struct macdrv_window_surface *surface;
191     int width = rect->right - rect->left, height = rect->bottom - rect->top;
192     DWORD *colors;
193     pthread_mutexattr_t attr;
194     int err;
195
196     surface = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
197                         FIELD_OFFSET(struct macdrv_window_surface, info.bmiColors[3]));
198     if (!surface) return NULL;
199
200     err = pthread_mutexattr_init(&attr);
201     if (!err)
202     {
203         err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
204         if (!err)
205             err = pthread_mutex_init(&surface->mutex, &attr);
206         pthread_mutexattr_destroy(&attr);
207     }
208     if (err)
209     {
210         HeapFree(GetProcessHeap(), 0, surface);
211         return NULL;
212     }
213
214     surface->info.bmiHeader.biSize        = sizeof(surface->info.bmiHeader);
215     surface->info.bmiHeader.biWidth       = width;
216     surface->info.bmiHeader.biHeight      = height; /* bottom-up */
217     surface->info.bmiHeader.biPlanes      = 1;
218     surface->info.bmiHeader.biBitCount    = 32;
219     surface->info.bmiHeader.biSizeImage   = get_dib_image_size(&surface->info);
220     surface->info.bmiHeader.biCompression = BI_RGB;
221     surface->info.bmiHeader.biClrUsed     = 0;
222
223     colors = (DWORD *)((char *)&surface->info + surface->info.bmiHeader.biSize);
224     colors[0] = 0x00ff0000;
225     colors[1] = 0x0000ff00;
226     colors[2] = 0x000000ff;
227
228     surface->header.funcs = &macdrv_surface_funcs;
229     surface->header.rect  = *rect;
230     surface->header.ref   = 1;
231     surface->window = window;
232     reset_bounds(&surface->bounds);
233     surface->use_alpha = use_alpha;
234     surface->bits = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, surface->info.bmiHeader.biSizeImage);
235     if (!surface->bits) goto failed;
236
237     TRACE("created %p for %p %s bits %p-%p\n", surface, window, wine_dbgstr_rect(rect),
238           surface->bits, surface->bits + surface->info.bmiHeader.biSizeImage);
239
240     return &surface->header;
241
242 failed:
243     macdrv_surface_destroy(&surface->header);
244     return NULL;
245 }
246
247 /***********************************************************************
248  *              set_surface_use_alpha
249  */
250 void set_surface_use_alpha(struct window_surface *window_surface, BOOL use_alpha)
251 {
252     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
253     surface->use_alpha = use_alpha;
254 }
255
256 /***********************************************************************
257  *              set_window_surface
258  */
259 void set_window_surface(macdrv_window window, struct window_surface *window_surface)
260 {
261     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
262     macdrv_set_window_surface(window, window_surface, surface ? &surface->mutex : NULL);
263 }
264
265 /***********************************************************************
266  *              get_surface_region_rects
267  *
268  * Caller must hold the surface lock.  Indirectly returns the surface
269  * region rects.  Returns zero if the surface has no region set (it is
270  * unclipped); returns non-zero if the surface does have a region set.
271  *
272  * IMPORTANT: This function is called from non-Wine threads, so it
273  *            must not use Win32 or Wine functions, including debug
274  *            logging.
275  */
276 int get_surface_region_rects(void *window_surface, const CGRect **rects, int *count)
277 {
278     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
279
280     if (surface->region_data)
281     {
282         *rects = (const CGRect*)surface->region_data->Buffer;
283         *count = surface->region_data->rdh.nCount;
284     }
285     else
286     {
287         *rects = NULL;
288         *count = 0;
289     }
290
291     return (surface->region_data != NULL);
292 }
293
294 /***********************************************************************
295  *              create_surface_image
296  *
297  * Caller must hold the surface lock.  On input, *rect is the requested
298  * image rect, relative to the window whole_rect, a.k.a. visible_rect.
299  * On output, it's been intersected with that part backed by the surface
300  * and is the actual size of the returned image.  copy_data indicates if
301  * the caller will keep the returned image beyond the point where the
302  * surface bits can be guaranteed to remain valid and unchanged.  If so,
303  * the bits are copied instead of merely referenced by the image.
304  *
305  * IMPORTANT: This function is called from non-Wine threads, so it
306  *            must not use Win32 or Wine functions, including debug
307  *            logging.
308  */
309 CGImageRef create_surface_image(void *window_surface, CGRect *rect, int copy_data)
310 {
311     CGImageRef cgimage = NULL;
312     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
313     int width, height;
314
315     width  = surface->header.rect.right - surface->header.rect.left;
316     height = surface->header.rect.bottom - surface->header.rect.top;
317     *rect = CGRectIntersection(cgrect_from_rect(surface->header.rect), *rect);
318     if (!CGRectIsEmpty(*rect))
319     {
320         CGRect visrect;
321         CGColorSpaceRef colorspace;
322         CGDataProviderRef provider;
323         int bytes_per_row, offset, size;
324         CGImageAlphaInfo alphaInfo;
325
326         visrect = CGRectOffset(*rect, -surface->header.rect.left, -surface->header.rect.top);
327
328         colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
329         bytes_per_row = get_dib_stride(width, 32);
330         offset = CGRectGetMinX(visrect) * 4 + (height - CGRectGetMaxY(visrect)) * bytes_per_row;
331         size = min(CGRectGetHeight(visrect) * bytes_per_row,
332                    surface->info.bmiHeader.biSizeImage - offset);
333
334         if (copy_data)
335         {
336             CFDataRef data = CFDataCreate(NULL, (UInt8*)surface->bits + offset, size);
337             provider = CGDataProviderCreateWithCFData(data);
338             CFRelease(data);
339         }
340         else
341             provider = CGDataProviderCreateWithData(NULL, surface->bits + offset, size, NULL);
342
343         alphaInfo = surface->use_alpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
344         cgimage = CGImageCreate(CGRectGetWidth(visrect), CGRectGetHeight(visrect),
345                                 8, 32, bytes_per_row, colorspace,
346                                 alphaInfo | kCGBitmapByteOrder32Little,
347                                 provider, NULL, FALSE, kCGRenderingIntentDefault);
348         CGDataProviderRelease(provider);
349         CGColorSpaceRelease(colorspace);
350     }
351
352     return cgimage;
353 }