surface.c 16.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
/*
 * 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;
64 65
    HRGN                    region;
    HRGN                    drawn;
66
    BOOL                    use_alpha;
67
    RGNDATA                *blit_data;
68 69 70 71 72
    BYTE                   *bits;
    pthread_mutex_t         mutex;
    BITMAPINFO              info;   /* variable size, must be last */
};

73
static struct macdrv_window_surface *get_mac_surface(struct window_surface *surface);
74

75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
/***********************************************************************
 *              update_blit_data
 */
static void update_blit_data(struct macdrv_window_surface *surface)
{
    HeapFree(GetProcessHeap(), 0, surface->blit_data);
    surface->blit_data = NULL;

    if (surface->drawn)
    {
        HRGN blit = CreateRectRgn(0, 0, 0, 0);

        if (CombineRgn(blit, surface->drawn, 0, RGN_COPY) > NULLREGION &&
            (!surface->region || CombineRgn(blit, blit, surface->region, RGN_AND) > NULLREGION) &&
            OffsetRgn(blit, surface->header.rect.left, surface->header.rect.top) > NULLREGION)
            surface->blit_data = get_region_data(blit, 0);

        DeleteObject(blit);
    }
}

96 97 98
/***********************************************************************
 *              macdrv_surface_lock
 */
99
static void CDECL macdrv_surface_lock(struct window_surface *window_surface)
100 101 102 103 104 105 106 107 108
{
    struct macdrv_window_surface *surface = get_mac_surface(window_surface);

    pthread_mutex_lock(&surface->mutex);
}

/***********************************************************************
 *              macdrv_surface_unlock
 */
109
static void CDECL macdrv_surface_unlock(struct window_surface *window_surface)
110 111 112 113 114 115 116 117 118
{
    struct macdrv_window_surface *surface = get_mac_surface(window_surface);

    pthread_mutex_unlock(&surface->mutex);
}

/***********************************************************************
 *              macdrv_surface_get_bitmap_info
 */
119 120
static void *CDECL macdrv_surface_get_bitmap_info(struct window_surface *window_surface,
                                                  BITMAPINFO *info)
121 122 123 124 125 126 127 128 129 130
{
    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
 */
131
static RECT *CDECL macdrv_surface_get_bounds(struct window_surface *window_surface)
132 133 134 135 136 137 138 139 140
{
    struct macdrv_window_surface *surface = get_mac_surface(window_surface);

    return &surface->bounds;
}

/***********************************************************************
 *              macdrv_surface_set_region
 */
141
static void CDECL macdrv_surface_set_region(struct window_surface *window_surface, HRGN region)
142 143 144 145 146
{
    struct macdrv_window_surface *surface = get_mac_surface(window_surface);

    TRACE("updating surface %p with %p\n", surface, region);

147 148
    window_surface->funcs->lock(window_surface);

149 150
    if (region)
    {
151 152
        if (!surface->region) surface->region = CreateRectRgn(0, 0, 0, 0);
        CombineRgn(surface->region, region, 0, RGN_COPY);
153
    }
154 155 156 157 158 159
    else
    {
        if (surface->region) DeleteObject(surface->region);
        surface->region = 0;
    }
    update_blit_data(surface);
160 161

    window_surface->funcs->unlock(window_surface);
162 163 164 165 166
}

/***********************************************************************
 *              macdrv_surface_flush
 */
167
static void CDECL macdrv_surface_flush(struct window_surface *window_surface)
168 169 170
{
    struct macdrv_window_surface *surface = get_mac_surface(window_surface);
    CGRect rect;
171
    HRGN region;
172 173 174 175 176 177 178 179

    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);
180 181 182 183 184 185 186 187 188 189 190 191

    if (!IsRectEmpty(&surface->bounds) && (region = CreateRectRgnIndirect(&surface->bounds)))
    {
        if (surface->drawn)
        {
            CombineRgn(surface->drawn, surface->drawn, region, RGN_OR);
            DeleteObject(region);
        }
        else
            surface->drawn = region;
    }
    update_blit_data(surface);
192 193 194 195 196 197 198 199 200 201 202
    reset_bounds(&surface->bounds);

    window_surface->funcs->unlock(window_surface);

    if (!CGRectIsEmpty(rect))
        macdrv_window_needs_display(surface->window, rect);
}

/***********************************************************************
 *              macdrv_surface_destroy
 */
203
static void CDECL macdrv_surface_destroy(struct window_surface *window_surface)
204 205 206 207
{
    struct macdrv_window_surface *surface = get_mac_surface(window_surface);

    TRACE("freeing %p bits %p\n", surface, surface->bits);
208 209 210
    if (surface->region) DeleteObject(surface->region);
    if (surface->drawn) DeleteObject(surface->drawn);
    HeapFree(GetProcessHeap(), 0, surface->blit_data);
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
    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,
};

227 228 229 230 231 232
static struct macdrv_window_surface *get_mac_surface(struct window_surface *surface)
{
    if (!surface || surface->funcs != &macdrv_surface_funcs) return NULL;
    return (struct macdrv_window_surface *)surface;
}

233 234 235
/***********************************************************************
 *              create_surface
 */
236 237
struct window_surface *create_surface(macdrv_window window, const RECT *rect,
                                      struct window_surface *old_surface, BOOL use_alpha)
238 239
{
    struct macdrv_window_surface *surface;
240
    struct macdrv_window_surface *old_mac_surface = get_mac_surface(old_surface);
241 242 243 244
    int width = rect->right - rect->left, height = rect->bottom - rect->top;
    DWORD *colors;
    pthread_mutexattr_t attr;
    int err;
245
    DWORD window_background;
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266

    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;
267
    surface->info.bmiHeader.biHeight      = -height; /* top-down */
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
    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);
284 285 286 287 288 289 290 291 292 293 294
    if (old_mac_surface && old_mac_surface->drawn)
    {
        surface->drawn = CreateRectRgnIndirect(rect);
        OffsetRgn(surface->drawn, -rect->left, -rect->top);
        if (CombineRgn(surface->drawn, surface->drawn, old_mac_surface->drawn, RGN_AND) <= NULLREGION)
        {
            DeleteObject(surface->drawn);
            surface->drawn = 0;
        }
    }
    update_blit_data(surface);
295
    surface->use_alpha = use_alpha;
296
    surface->bits = HeapAlloc(GetProcessHeap(), 0, surface->info.bmiHeader.biSizeImage);
297
    if (!surface->bits) goto failed;
298 299
    window_background = macdrv_window_background_color();
    memset_pattern4(surface->bits, &window_background, surface->info.bmiHeader.biSizeImage);
300 301 302 303 304 305 306 307 308 309 310

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

311 312 313 314 315 316
/***********************************************************************
 *              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);
317
    if (surface) surface->use_alpha = use_alpha;
318 319
}

320 321 322 323 324 325 326 327 328 329
/***********************************************************************
 *              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);
}

/***********************************************************************
330
 *              get_surface_blit_rects
331 332
 *
 * Caller must hold the surface lock.  Indirectly returns the surface
333 334 335
 * blit region rects.  Returns zero if the surface has nothing to blit;
 * returns non-zero if the surface does have rects to blit (drawn area
 * which isn't clipped away by a surface region).
336 337 338 339 340
 *
 * IMPORTANT: This function is called from non-Wine threads, so it
 *            must not use Win32 or Wine functions, including debug
 *            logging.
 */
341
int get_surface_blit_rects(void *window_surface, const CGRect **rects, int *count)
342 343 344
{
    struct macdrv_window_surface *surface = get_mac_surface(window_surface);

345
    if (rects && count)
346
    {
347 348 349 350 351 352 353 354 355 356
        if (surface->blit_data)
        {
            *rects = (const CGRect*)surface->blit_data->Buffer;
            *count = surface->blit_data->rdh.nCount;
        }
        else
        {
            *rects = NULL;
            *count = 0;
        }
357 358
    }

359
    return (surface->blit_data != NULL && surface->blit_data->rdh.nCount > 0);
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
}

/***********************************************************************
 *              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.
 */
377 378
CGImageRef create_surface_image(void *window_surface, CGRect *rect, int copy_data, int color_keyed,
        CGFloat key_red, CGFloat key_green, CGFloat key_blue)
379 380 381 382 383 384 385 386 387 388 389 390 391 392
{
    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;
393
        CGImageAlphaInfo alphaInfo;
394 395 396

        visrect = CGRectOffset(*rect, -surface->header.rect.left, -surface->header.rect.top);

397
        colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
398
        bytes_per_row = get_dib_stride(width, 32);
399
        offset = CGRectGetMinX(visrect) * 4 + CGRectGetMinY(visrect) * bytes_per_row;
400 401 402 403 404 405 406 407 408 409 410 411
        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);

412
        alphaInfo = surface->use_alpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
413 414
        cgimage = CGImageCreate(CGRectGetWidth(visrect), CGRectGetHeight(visrect),
                                8, 32, bytes_per_row, colorspace,
415
                                alphaInfo | kCGBitmapByteOrder32Little,
416
                                provider, NULL, retina_on, kCGRenderingIntentDefault);
417 418
        CGDataProviderRelease(provider);
        CGColorSpaceRelease(colorspace);
419 420 421 422 423 424 425 426 427 428 429 430 431 432

        if (color_keyed)
        {
            CGImageRef maskedImage;
            CGFloat components[] = { key_red   - 0.5, key_red   + 0.5,
                                     key_green - 0.5, key_green + 0.5,
                                     key_blue  - 0.5, key_blue  + 0.5 };
            maskedImage = CGImageCreateWithMaskingColors(cgimage, components);
            if (maskedImage)
            {
                CGImageRelease(cgimage);
                cgimage = maskedImage;
            }
        }
433 434 435 436
    }

    return cgimage;
}
437 438 439 440 441 442 443 444 445 446 447

/***********************************************************************
 *              surface_clip_to_visible_rect
 *
 * Intersect the accumulated drawn region with a new visible rect,
 * effectively discarding stale drawing in the surface slack area.
 */
void surface_clip_to_visible_rect(struct window_surface *window_surface, const RECT *visible_rect)
{
    struct macdrv_window_surface *surface = get_mac_surface(window_surface);

448
    if (!surface) return;
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
    window_surface->funcs->lock(window_surface);

    if (surface->drawn)
    {
        RECT rect;
        HRGN region;

        rect = *visible_rect;
        OffsetRect(&rect, -rect.left, -rect.top);

        if ((region = CreateRectRgnIndirect(&rect)))
        {
            CombineRgn(surface->drawn, surface->drawn, region, RGN_AND);
            DeleteObject(region);

            update_blit_data(surface);
        }
    }

    window_surface->funcs->unlock(window_surface);
}