kernelbase: Ignore URL_PARTFLAG_KEEPSCHEME when used with URL_PART_SCHEME or URL_PART...
[wine.git] / dlls / win32u / dc.c
blobaed2d3684a701d7cc413406d63ef48e89a3a20aa
1 /*
2 * GDI Device Context functions
4 * Copyright 1993 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #if 0
22 #pragma makedep unix
23 #endif
25 #include <assert.h>
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <pthread.h>
31 #include "windef.h"
32 #include "winbase.h"
33 #include "wingdi.h"
34 #include "winreg.h"
35 #include "winnls.h"
36 #include "winternl.h"
37 #include "winerror.h"
38 #include "ntgdi_private.h"
40 #include "wine/debug.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(dc);
44 static pthread_mutex_t dc_attr_lock = PTHREAD_MUTEX_INITIALIZER;
46 struct dc_attr_bucket
48 struct list entry;
49 DC_ATTR *entries;
50 DC_ATTR *next_free;
51 DC_ATTR *next_unused;
54 static struct list dc_attr_buckets = LIST_INIT( dc_attr_buckets );
56 static BOOL DC_DeleteObject( HGDIOBJ handle );
58 static const struct gdi_obj_funcs dc_funcs =
60 NULL, /* pGetObjectW */
61 NULL, /* pUnrealizeObject */
62 DC_DeleteObject /* pDeleteObject */
66 static inline DC *get_dc_obj( HDC hdc )
68 DWORD type;
69 DC *dc = get_any_obj_ptr( hdc, &type );
70 if (!dc) return NULL;
72 switch (type)
74 case NTGDI_OBJ_DC:
75 case NTGDI_OBJ_MEMDC:
76 case NTGDI_OBJ_ENHMETADC:
77 return dc;
78 default:
79 GDI_ReleaseObj( hdc );
80 SetLastError( ERROR_INVALID_HANDLE );
81 return NULL;
85 /* alloc DC_ATTR from a pool of memory accessible from client */
86 static DC_ATTR *alloc_dc_attr(void)
88 struct dc_attr_bucket *bucket;
89 DC_ATTR *dc_attr = NULL;
91 pthread_mutex_lock( &dc_attr_lock );
93 LIST_FOR_EACH_ENTRY( bucket, &dc_attr_buckets, struct dc_attr_bucket, entry )
95 if (bucket->next_free)
97 dc_attr = bucket->next_free;
98 bucket->next_free = *(void **)bucket->next_free;
99 break;
101 if ((char *)bucket->next_unused - (char *)bucket->entries + sizeof(*dc_attr) <=
102 system_info.AllocationGranularity)
104 dc_attr = bucket->next_unused++;
105 break;
109 if (!dc_attr && (bucket = malloc( sizeof(*bucket) )))
111 SIZE_T size = system_info.AllocationGranularity;
112 bucket->entries = NULL;
113 if (!NtAllocateVirtualMemory( GetCurrentProcess(), (void **)&bucket->entries, 0, &size,
114 MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE ))
116 bucket->next_free = NULL;
117 bucket->next_unused = bucket->entries + 1;
118 dc_attr = bucket->entries;
119 list_add_head( &dc_attr_buckets, &bucket->entry );
121 else free( bucket );
124 if (dc_attr) memset( dc_attr, 0, sizeof( *dc_attr ));
126 pthread_mutex_unlock( &dc_attr_lock );
128 return dc_attr;
132 static void free_dc_attr( DC_ATTR *dc_attr )
134 struct dc_attr_bucket *bucket;
136 pthread_mutex_lock( &dc_attr_lock );
138 LIST_FOR_EACH_ENTRY( bucket, &dc_attr_buckets, struct dc_attr_bucket, entry )
140 if (bucket->entries > dc_attr || dc_attr >= bucket->next_unused) continue;
141 *(void **)dc_attr = bucket->next_free;
142 bucket->next_free = dc_attr;
143 break;
146 pthread_mutex_unlock( &dc_attr_lock );
150 /***********************************************************************
151 * set_initial_dc_state
153 static void set_initial_dc_state( DC *dc )
155 dc->attr->wnd_org.x = 0;
156 dc->attr->wnd_org.y = 0;
157 dc->attr->wnd_ext.cx = 1;
158 dc->attr->wnd_ext.cy = 1;
159 dc->attr->vport_org.x = 0;
160 dc->attr->vport_org.y = 0;
161 dc->attr->vport_ext.cx = 1;
162 dc->attr->vport_ext.cy = 1;
163 dc->attr->miter_limit = 10.0f; /* 10.0 is the default, from MSDN */
164 dc->attr->layout = 0;
165 dc->attr->rop_mode = R2_COPYPEN;
166 dc->attr->font_code_page = CP_ACP;
167 dc->attr->poly_fill_mode = ALTERNATE;
168 dc->attr->stretch_blt_mode = BLACKONWHITE;
169 dc->attr->rel_abs_mode = ABSOLUTE;
170 dc->attr->background_mode = OPAQUE;
171 dc->attr->background_color = RGB( 255, 255, 255 );
172 dc->attr->brush_color = RGB( 255, 255, 255 );
173 dc->attr->pen_color = RGB( 0, 0, 0 );
174 dc->attr->text_color = RGB( 0, 0, 0 );
175 dc->attr->brush_org.x = 0;
176 dc->attr->brush_org.y = 0;
177 dc->attr->mapper_flags = 0;
178 dc->attr->text_align = TA_LEFT | TA_TOP | TA_NOUPDATECP;
179 dc->attr->char_extra = 0;
180 dc->breakExtra = 0;
181 dc->breakRem = 0;
182 dc->attr->map_mode = MM_TEXT;
183 dc->attr->graphics_mode = GM_COMPATIBLE;
184 dc->attr->cur_pos.x = 0;
185 dc->attr->cur_pos.y = 0;
186 dc->attr->arc_direction = AD_COUNTERCLOCKWISE;
187 dc->xformWorld2Wnd.eM11 = 1.0f;
188 dc->xformWorld2Wnd.eM12 = 0.0f;
189 dc->xformWorld2Wnd.eM21 = 0.0f;
190 dc->xformWorld2Wnd.eM22 = 1.0f;
191 dc->xformWorld2Wnd.eDx = 0.0f;
192 dc->xformWorld2Wnd.eDy = 0.0f;
193 dc->xformWorld2Vport = dc->xformWorld2Wnd;
194 dc->xformVport2World = dc->xformWorld2Wnd;
195 dc->vport2WorldValid = TRUE;
197 reset_bounds( &dc->bounds );
200 /***********************************************************************
201 * alloc_dc_ptr
203 DC *alloc_dc_ptr( DWORD magic )
205 DC *dc;
207 if (!(dc = calloc( 1, sizeof(*dc) ))) return NULL;
208 if (!(dc->attr = alloc_dc_attr()))
210 free( dc );
211 return NULL;
214 dc->nulldrv.funcs = &null_driver;
215 dc->physDev = &dc->nulldrv;
216 dc->thread = GetCurrentThreadId();
217 dc->refcount = 1;
218 dc->hPen = GDI_inc_ref_count( get_stock_object( BLACK_PEN ));
219 dc->hBrush = GDI_inc_ref_count( get_stock_object( WHITE_BRUSH ));
220 dc->hFont = GDI_inc_ref_count( get_stock_object( SYSTEM_FONT ));
221 dc->hPalette = get_stock_object( DEFAULT_PALETTE );
223 set_initial_dc_state( dc );
225 if (!(dc->hSelf = alloc_gdi_handle( &dc->obj, magic, &dc_funcs )))
227 free_dc_attr( dc->attr );
228 free( dc );
229 return NULL;
231 dc->attr->hdc = dc->nulldrv.hdc = dc->hSelf;
232 set_gdi_client_ptr( dc->hSelf, dc->attr );
234 if (!font_driver.pCreateDC( &dc->physDev, NULL, NULL, NULL ))
236 free_dc_ptr( dc );
237 return NULL;
239 return dc;
244 /***********************************************************************
245 * free_dc_state
247 static void free_dc_state( DC *dc )
249 if (dc->hClipRgn) NtGdiDeleteObjectApp( dc->hClipRgn );
250 if (dc->hMetaRgn) NtGdiDeleteObjectApp( dc->hMetaRgn );
251 if (dc->hVisRgn) NtGdiDeleteObjectApp( dc->hVisRgn );
252 if (dc->region) NtGdiDeleteObjectApp( dc->region );
253 if (dc->path) free_gdi_path( dc->path );
254 free_dc_attr( dc->attr );
255 free( dc );
259 /***********************************************************************
260 * free_dc_ptr
262 void free_dc_ptr( DC *dc )
264 assert( dc->refcount == 1 );
266 while (dc->physDev != &dc->nulldrv)
268 PHYSDEV physdev = dc->physDev;
269 dc->physDev = physdev->next;
270 physdev->funcs->pDeleteDC( physdev );
272 GDI_dec_ref_count( dc->hPen );
273 GDI_dec_ref_count( dc->hBrush );
274 GDI_dec_ref_count( dc->hFont );
275 if (dc->hBitmap) GDI_dec_ref_count( dc->hBitmap );
276 free_gdi_handle( dc->hSelf );
277 free_dc_state( dc );
281 /***********************************************************************
282 * get_dc_ptr
284 * Retrieve a DC pointer but release the GDI lock.
286 DC *get_dc_ptr( HDC hdc )
288 DC *dc = get_dc_obj( hdc );
289 if (!dc) return NULL;
290 if (dc->attr->disabled)
292 GDI_ReleaseObj( hdc );
293 return NULL;
296 if (!InterlockedCompareExchange( &dc->refcount, 1, 0 ))
298 dc->thread = GetCurrentThreadId();
300 else if (dc->thread != GetCurrentThreadId())
302 WARN( "dc %p belongs to thread %04x\n", hdc, dc->thread );
303 GDI_ReleaseObj( hdc );
304 return NULL;
306 else InterlockedIncrement( &dc->refcount );
308 GDI_ReleaseObj( hdc );
309 return dc;
313 /***********************************************************************
314 * release_dc_ptr
316 void release_dc_ptr( DC *dc )
318 LONG ref;
320 dc->thread = 0;
321 ref = InterlockedDecrement( &dc->refcount );
322 assert( ref >= 0 );
323 if (ref) dc->thread = GetCurrentThreadId(); /* we still own it */
327 /***********************************************************************
328 * update_dc
330 * Make sure the DC vis region is up to date.
331 * This function may call up to USER so the GDI lock should _not_
332 * be held when calling it.
334 void update_dc( DC *dc )
336 if (InterlockedExchange( &dc->dirty, 0 ) && dc->hookProc)
337 dc->hookProc( dc->hSelf, DCHC_INVALIDVISRGN, dc->dwHookData, 0 );
341 static void set_bk_color( DC *dc, COLORREF color )
343 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pSetBkColor );
344 dc->attr->background_color = physdev->funcs->pSetBkColor( physdev, color );
348 static void set_text_color( DC *dc, COLORREF color )
350 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pSetTextColor );
351 dc->attr->text_color = physdev->funcs->pSetTextColor( physdev, color );
355 /***********************************************************************
356 * DC_InitDC
358 * Setup device-specific DC values for a newly created DC.
360 void DC_InitDC( DC* dc )
362 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pRealizeDefaultPalette );
363 physdev->funcs->pRealizeDefaultPalette( physdev );
364 set_text_color( dc, dc->attr->text_color );
365 set_bk_color( dc, dc->attr->background_color );
366 NtGdiSelectPen( dc->hSelf, dc->hPen );
367 NtGdiSelectBrush( dc->hSelf, dc->hBrush );
368 NtGdiSelectFont( dc->hSelf, dc->hFont );
369 update_dc_clipping( dc );
370 NtGdiSetVirtualResolution( dc->hSelf, 0, 0, 0, 0 );
371 physdev = GET_DC_PHYSDEV( dc, pSetBoundsRect );
372 physdev->funcs->pSetBoundsRect( physdev, &dc->bounds, dc->bounds_enabled ? DCB_ENABLE : DCB_DISABLE );
376 /***********************************************************************
377 * DC_InvertXform
379 * Computes the inverse of the transformation xformSrc and stores it to
380 * xformDest. Returns TRUE if successful or FALSE if the xformSrc matrix
381 * is singular.
383 static BOOL DC_InvertXform( const XFORM *xformSrc, XFORM *xformDest )
385 double determinant;
387 determinant = xformSrc->eM11*xformSrc->eM22 -
388 xformSrc->eM12*xformSrc->eM21;
389 if (determinant > -1e-12 && determinant < 1e-12)
390 return FALSE;
392 xformDest->eM11 = xformSrc->eM22 / determinant;
393 xformDest->eM12 = -xformSrc->eM12 / determinant;
394 xformDest->eM21 = -xformSrc->eM21 / determinant;
395 xformDest->eM22 = xformSrc->eM11 / determinant;
396 xformDest->eDx = -xformSrc->eDx * xformDest->eM11 -
397 xformSrc->eDy * xformDest->eM21;
398 xformDest->eDy = -xformSrc->eDx * xformDest->eM12 -
399 xformSrc->eDy * xformDest->eM22;
401 return TRUE;
404 /* Construct a transformation to do the window-to-viewport conversion */
405 static void construct_window_to_viewport(DC *dc, XFORM *xform)
407 double scaleX, scaleY;
408 scaleX = (double)dc->attr->vport_ext.cx / (double)dc->attr->wnd_ext.cx;
409 scaleY = (double)dc->attr->vport_ext.cy / (double)dc->attr->wnd_ext.cy;
411 if (dc->attr->layout & LAYOUT_RTL) scaleX = -scaleX;
412 xform->eM11 = scaleX;
413 xform->eM12 = 0.0;
414 xform->eM21 = 0.0;
415 xform->eM22 = scaleY;
416 xform->eDx = (double)dc->attr->vport_org.x - scaleX * (double)dc->attr->wnd_org.x;
417 xform->eDy = (double)dc->attr->vport_org.y - scaleY * (double)dc->attr->wnd_org.y;
418 if (dc->attr->layout & LAYOUT_RTL)
419 xform->eDx = dc->attr->vis_rect.right - dc->attr->vis_rect.left - 1 - xform->eDx;
422 /***********************************************************************
423 * linear_xform_cmp
425 * Compares the linear transform portion of two XFORMs (i.e. the 2x2 submatrix).
426 * Returns 0 if they match.
428 static inline int linear_xform_cmp( const XFORM *a, const XFORM *b )
430 return memcmp( a, b, FIELD_OFFSET( XFORM, eDx ) );
433 /***********************************************************************
434 * DC_UpdateXforms
436 * Updates the xformWorld2Vport, xformVport2World and vport2WorldValid
437 * fields of the specified DC by creating a transformation that
438 * represents the current mapping mode and combining it with the DC's
439 * world transform. This function should be called whenever the
440 * parameters associated with the mapping mode (window and viewport
441 * extents and origins) or the world transform change.
443 void DC_UpdateXforms( DC *dc )
445 XFORM xformWnd2Vport, oldworld2vport;
447 construct_window_to_viewport(dc, &xformWnd2Vport);
449 oldworld2vport = dc->xformWorld2Vport;
450 /* Combine with the world transformation */
451 combine_transform( &dc->xformWorld2Vport, &dc->xformWorld2Wnd, &xformWnd2Vport );
453 /* Create inverse of world-to-viewport transformation */
454 dc->vport2WorldValid = DC_InvertXform( &dc->xformWorld2Vport,
455 &dc->xformVport2World );
457 /* Reselect the font and pen back into the dc so that the size
458 gets updated. */
459 if (linear_xform_cmp( &oldworld2vport, &dc->xformWorld2Vport ) &&
460 get_gdi_object_type( dc->hSelf ) != NTGDI_OBJ_METADC)
462 NtGdiSelectFont(dc->hSelf, dc->hFont);
463 NtGdiSelectPen(dc->hSelf, dc->hPen);
468 /***********************************************************************
469 * reset_dc_state
471 static BOOL reset_dc_state( HDC hdc )
473 DC *dc, *dcs, *next;
475 if (!(dc = get_dc_ptr( hdc ))) return FALSE;
477 set_initial_dc_state( dc );
478 set_bk_color( dc, RGB( 255, 255, 255 ));
479 set_text_color( dc, RGB( 0, 0, 0 ));
480 NtGdiSelectBrush( hdc, get_stock_object( WHITE_BRUSH ));
481 NtGdiSelectFont( hdc, get_stock_object( SYSTEM_FONT ));
482 NtGdiSelectPen( hdc, get_stock_object( BLACK_PEN ));
483 NtGdiSetVirtualResolution( hdc, 0, 0, 0, 0 );
484 NtUserSelectPalette( hdc, get_stock_object( DEFAULT_PALETTE ), FALSE );
485 NtGdiSetBoundsRect( hdc, NULL, DCB_DISABLE );
486 NtGdiAbortPath( hdc );
488 if (dc->hClipRgn) NtGdiDeleteObjectApp( dc->hClipRgn );
489 if (dc->hMetaRgn) NtGdiDeleteObjectApp( dc->hMetaRgn );
490 dc->hClipRgn = 0;
491 dc->hMetaRgn = 0;
492 update_dc_clipping( dc );
494 for (dcs = dc->saved_dc; dcs; dcs = next)
496 next = dcs->saved_dc;
497 free_dc_state( dcs );
499 dc->saved_dc = NULL;
500 dc->attr->save_level = 0;
501 release_dc_ptr( dc );
502 return TRUE;
506 /***********************************************************************
507 * DC_DeleteObject
509 static BOOL DC_DeleteObject( HGDIOBJ handle )
511 DC *dc;
513 TRACE( "%p\n", handle );
515 if (!(dc = get_dc_ptr( handle ))) return FALSE;
516 if (dc->refcount != 1)
518 FIXME( "not deleting busy DC %p refcount %u\n", dc->hSelf, dc->refcount );
519 release_dc_ptr( dc );
520 return FALSE;
523 /* Call hook procedure to check whether is it OK to delete this DC,
524 * gdi_lock should not be locked */
525 if (dc->hookProc && !dc->hookProc( dc->hSelf, DCHC_DELETEDC, dc->dwHookData, 0 ))
527 release_dc_ptr( dc );
528 return TRUE;
530 reset_dc_state( handle );
531 free_dc_ptr( dc );
532 return TRUE;
536 /***********************************************************************
537 * NtGdiSaveDC (win32u.@)
539 INT WINAPI NtGdiSaveDC( HDC hdc )
541 DC *dc, *newdc;
542 INT ret;
544 if (!(dc = get_dc_ptr( hdc ))) return 0;
546 if (!(newdc = calloc( 1, sizeof(*newdc ))))
548 release_dc_ptr( dc );
549 return 0;
551 if (!(newdc->attr = calloc( 1, sizeof(*newdc->attr) )))
553 free( newdc );
554 release_dc_ptr( dc );
555 return 0;
558 *newdc->attr = *dc->attr;
559 newdc->hPen = dc->hPen;
560 newdc->hBrush = dc->hBrush;
561 newdc->hFont = dc->hFont;
562 newdc->hBitmap = dc->hBitmap;
563 newdc->hPalette = dc->hPalette;
564 newdc->breakExtra = dc->breakExtra;
565 newdc->breakRem = dc->breakRem;
566 newdc->xformWorld2Wnd = dc->xformWorld2Wnd;
567 newdc->xformWorld2Vport = dc->xformWorld2Vport;
568 newdc->xformVport2World = dc->xformVport2World;
569 newdc->vport2WorldValid = dc->vport2WorldValid;
571 /* Get/SetDCState() don't change hVisRgn field ("Undoc. Windows" p.559). */
573 if (dc->hClipRgn)
575 newdc->hClipRgn = NtGdiCreateRectRgn( 0, 0, 0, 0 );
576 NtGdiCombineRgn( newdc->hClipRgn, dc->hClipRgn, 0, RGN_COPY );
578 if (dc->hMetaRgn)
580 newdc->hMetaRgn = NtGdiCreateRectRgn( 0, 0, 0, 0 );
581 NtGdiCombineRgn( newdc->hMetaRgn, dc->hMetaRgn, 0, RGN_COPY );
584 if (!PATH_SavePath( newdc, dc ))
586 release_dc_ptr( dc );
587 free_dc_state( newdc );
588 return 0;
591 newdc->saved_dc = dc->saved_dc;
592 dc->saved_dc = newdc;
593 ret = ++dc->attr->save_level;
594 release_dc_ptr( dc );
595 return ret;
599 /***********************************************************************
600 * NtGdiRestoreDC (win32u.@)
602 BOOL WINAPI NtGdiRestoreDC( HDC hdc, INT level )
604 DC *dc, *dcs, *first_dcs;
605 INT save_level;
607 TRACE("%p %d\n", hdc, level );
608 if (!(dc = get_dc_ptr( hdc ))) return FALSE;
609 update_dc( dc );
611 /* find the state level to restore */
612 if (abs(level) > dc->attr->save_level || level == 0)
614 release_dc_ptr( dc );
615 return FALSE;
618 if (level < 0) level = dc->attr->save_level + level + 1;
619 first_dcs = dc->saved_dc;
620 for (dcs = first_dcs, save_level = dc->attr->save_level; save_level > level; save_level--)
621 dcs = dcs->saved_dc;
623 /* restore the state */
625 if (!PATH_RestorePath( dc, dcs ))
627 release_dc_ptr( dc );
628 return FALSE;
631 dc->attr->layout = dcs->attr->layout;
632 dc->attr->rop_mode = dcs->attr->rop_mode;
633 dc->attr->poly_fill_mode = dcs->attr->poly_fill_mode;
634 dc->attr->stretch_blt_mode = dcs->attr->stretch_blt_mode;
635 dc->attr->rel_abs_mode = dcs->attr->rel_abs_mode;
636 dc->attr->background_mode = dcs->attr->background_mode;
637 dc->attr->background_color = dcs->attr->background_color;
638 dc->attr->text_color = dcs->attr->text_color;
639 dc->attr->brush_color = dcs->attr->brush_color;
640 dc->attr->pen_color = dcs->attr->pen_color;
641 dc->attr->brush_org = dcs->attr->brush_org;
642 dc->attr->mapper_flags = dcs->attr->mapper_flags;
643 dc->attr->text_align = dcs->attr->text_align;
644 dc->attr->char_extra = dcs->attr->char_extra;
645 dc->attr->map_mode = dcs->attr->map_mode;
646 dc->attr->graphics_mode = dcs->attr->graphics_mode;
647 dc->attr->cur_pos = dcs->attr->cur_pos;
648 dc->attr->arc_direction = dcs->attr->arc_direction;
649 dc->attr->wnd_org = dcs->attr->wnd_org;
650 dc->attr->wnd_ext = dcs->attr->wnd_ext;
651 dc->attr->vport_org = dcs->attr->vport_org;
652 dc->attr->vport_ext = dcs->attr->vport_ext;
653 dc->attr->virtual_res = dcs->attr->virtual_res;
654 dc->attr->virtual_size = dcs->attr->virtual_size;
656 dc->breakExtra = dcs->breakExtra;
657 dc->breakRem = dcs->breakRem;
658 dc->xformWorld2Wnd = dcs->xformWorld2Wnd;
659 dc->xformWorld2Vport = dcs->xformWorld2Vport;
660 dc->xformVport2World = dcs->xformVport2World;
661 dc->vport2WorldValid = dcs->vport2WorldValid;
663 if (dcs->hClipRgn)
665 if (!dc->hClipRgn) dc->hClipRgn = NtGdiCreateRectRgn( 0, 0, 0, 0 );
666 NtGdiCombineRgn( dc->hClipRgn, dcs->hClipRgn, 0, RGN_COPY );
668 else
670 if (dc->hClipRgn) NtGdiDeleteObjectApp( dc->hClipRgn );
671 dc->hClipRgn = 0;
673 if (dcs->hMetaRgn)
675 if (!dc->hMetaRgn) dc->hMetaRgn = NtGdiCreateRectRgn( 0, 0, 0, 0 );
676 NtGdiCombineRgn( dc->hMetaRgn, dcs->hMetaRgn, 0, RGN_COPY );
678 else
680 if (dc->hMetaRgn) NtGdiDeleteObjectApp( dc->hMetaRgn );
681 dc->hMetaRgn = 0;
683 DC_UpdateXforms( dc );
684 update_dc_clipping( dc );
686 NtGdiSelectBitmap( hdc, dcs->hBitmap );
687 NtGdiSelectBrush( hdc, dcs->hBrush );
688 NtGdiSelectFont( hdc, dcs->hFont );
689 NtGdiSelectPen( hdc, dcs->hPen );
690 set_bk_color( dc, dcs->attr->background_color);
691 set_text_color( dc, dcs->attr->text_color);
692 NtUserSelectPalette( hdc, dcs->hPalette, FALSE );
694 dc->saved_dc = dcs->saved_dc;
695 dcs->saved_dc = 0;
696 dc->attr->save_level = save_level - 1;
698 /* now destroy all the saved DCs */
700 while (first_dcs)
702 DC *next = first_dcs->saved_dc;
703 free_dc_state( first_dcs );
704 first_dcs = next;
706 release_dc_ptr( dc );
707 return TRUE;
711 /***********************************************************************
712 * NtGdiOpenDCW (win32u.@)
714 HDC WINAPI NtGdiOpenDCW( UNICODE_STRING *device, const DEVMODEW *devmode, UNICODE_STRING *output,
715 ULONG type, BOOL is_display, HANDLE hspool, DRIVER_INFO_2W *driver_info,
716 void *pdev )
718 const struct gdi_dc_funcs *funcs = NULL;
719 HDC hdc;
720 DC * dc;
722 /* gdi_lock should not be locked */
723 if (is_display)
724 funcs = get_display_driver();
725 else if (hspool)
727 const struct gdi_dc_funcs * (CDECL *wine_get_gdi_driver)( unsigned int ) = hspool;
728 funcs = wine_get_gdi_driver( WINE_GDI_DRIVER_VERSION );
730 if (!funcs)
732 ERR( "no driver found\n" );
733 return 0;
736 if (!(dc = alloc_dc_ptr( NTGDI_OBJ_DC ))) return 0;
737 hdc = dc->hSelf;
739 dc->hBitmap = GDI_inc_ref_count( get_stock_object( DEFAULT_BITMAP ));
741 TRACE("(device=%s, output=%s): returning %p\n",
742 debugstr_us(device), debugstr_us(output), dc->hSelf );
744 if (funcs->pCreateDC)
746 if (!funcs->pCreateDC( &dc->physDev, device ? device->Buffer : NULL,
747 output ? output->Buffer : NULL, devmode ))
749 WARN("creation aborted by device\n" );
750 free_dc_ptr( dc );
751 return 0;
755 if (is_display && device)
757 memcpy( dc->display, device->Buffer, device->Length );
758 dc->display[device->Length / sizeof(WCHAR)] = 0;
762 dc->attr->vis_rect.left = 0;
763 dc->attr->vis_rect.top = 0;
764 dc->attr->vis_rect.right = NtGdiGetDeviceCaps( hdc, DESKTOPHORZRES );
765 dc->attr->vis_rect.bottom = NtGdiGetDeviceCaps( hdc, DESKTOPVERTRES );
767 DC_InitDC( dc );
768 release_dc_ptr( dc );
769 return hdc;
773 /***********************************************************************
774 * NtGdiCreateCompatibleDC (win32u.@)
776 HDC WINAPI NtGdiCreateCompatibleDC( HDC hdc )
778 DC *dc, *origDC;
779 HDC ret;
780 const struct gdi_dc_funcs *funcs;
781 PHYSDEV physDev = NULL;
783 /* gdi_lock should not be locked */
785 if (hdc)
787 if (!(origDC = get_dc_ptr( hdc ))) return 0;
788 physDev = GET_DC_PHYSDEV( origDC, pCreateCompatibleDC );
789 funcs = physDev->funcs;
790 release_dc_ptr( origDC );
792 else funcs = get_display_driver();
794 if (!(dc = alloc_dc_ptr( NTGDI_OBJ_MEMDC ))) return 0;
796 TRACE("(%p): returning %p\n", hdc, dc->hSelf );
798 dc->hBitmap = GDI_inc_ref_count( get_stock_object( DEFAULT_BITMAP ));
799 dc->attr->vis_rect.left = 0;
800 dc->attr->vis_rect.top = 0;
801 dc->attr->vis_rect.right = 1;
802 dc->attr->vis_rect.bottom = 1;
803 dc->device_rect = dc->attr->vis_rect;
805 ret = dc->hSelf;
807 if (funcs->pCreateCompatibleDC && !funcs->pCreateCompatibleDC( physDev, &dc->physDev ))
809 WARN("creation aborted by device\n");
810 free_dc_ptr( dc );
811 return 0;
814 if (!dib_driver.pCreateDC( &dc->physDev, NULL, NULL, NULL ))
816 free_dc_ptr( dc );
817 return 0;
819 physDev = GET_DC_PHYSDEV( dc, pSelectBitmap );
820 physDev->funcs->pSelectBitmap( physDev, dc->hBitmap );
822 DC_InitDC( dc );
823 release_dc_ptr( dc );
824 return ret;
828 /***********************************************************************
829 * NtGdiResetDC (win32u.@)
831 BOOL WINAPI NtGdiResetDC( HDC hdc, const DEVMODEW *devmode, BOOL *banding,
832 DRIVER_INFO_2W *driver_info, void *dev )
834 DC *dc;
835 BOOL ret = FALSE;
837 if ((dc = get_dc_ptr( hdc )))
839 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pResetDC );
840 ret = physdev->funcs->pResetDC( physdev, devmode ) != 0;
841 if (ret) /* reset the visible region */
843 dc->dirty = 0;
844 dc->attr->vis_rect.left = 0;
845 dc->attr->vis_rect.top = 0;
846 dc->attr->vis_rect.right = NtGdiGetDeviceCaps( hdc, DESKTOPHORZRES );
847 dc->attr->vis_rect.bottom = NtGdiGetDeviceCaps( hdc, DESKTOPVERTRES );
848 if (dc->hVisRgn) NtGdiDeleteObjectApp( dc->hVisRgn );
849 dc->hVisRgn = 0;
850 update_dc_clipping( dc );
852 release_dc_ptr( dc );
854 return ret;
858 /***********************************************************************
859 * NtGdiGetDeviceCaps (win32u.@)
861 INT WINAPI NtGdiGetDeviceCaps( HDC hdc, INT cap )
863 DC *dc;
864 INT ret = 0;
866 if ((dc = get_dc_ptr( hdc )))
868 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pGetDeviceCaps );
869 ret = physdev->funcs->pGetDeviceCaps( physdev, cap );
870 release_dc_ptr( dc );
872 return ret;
876 static BOOL set_graphics_mode( DC *dc, int mode )
878 if (mode == dc->attr->graphics_mode) return TRUE;
879 if (mode <= 0 || mode > GM_LAST) return FALSE;
881 /* One would think that setting the graphics mode to GM_COMPATIBLE
882 * would also reset the world transformation matrix to the unity
883 * matrix. However, in Windows, this is not the case. This doesn't
884 * make a lot of sense to me, but that's the way it is.
886 dc->attr->graphics_mode = mode;
888 /* font metrics depend on the graphics mode */
889 NtGdiSelectFont(dc->hSelf, dc->hFont);
890 return TRUE;
894 /***********************************************************************
895 * NtGdiGetAndSetDCDword (win32u.@)
897 BOOL WINAPI NtGdiGetAndSetDCDword( HDC hdc, UINT method, DWORD value, DWORD *prev_value )
899 PHYSDEV physdev;
900 BOOL ret = TRUE;
901 DWORD prev;
902 DC *dc;
904 if (!(dc = get_dc_ptr( hdc ))) return 0;
906 switch (method)
908 case NtGdiSetMapMode:
909 prev = dc->attr->map_mode;
910 ret = set_map_mode( dc, value );
911 break;
913 case NtGdiSetBkColor:
914 prev = dc->attr->background_color;
915 set_bk_color( dc, value );
916 break;
918 case NtGdiSetTextColor:
919 prev = dc->attr->text_color;
920 set_text_color( dc, value );
921 break;
923 case NtGdiSetDCBrushColor:
924 physdev = GET_DC_PHYSDEV( dc, pSetDCBrushColor );
925 prev = dc->attr->brush_color;
926 value = physdev->funcs->pSetDCBrushColor( physdev, value );
927 if (value != CLR_INVALID) dc->attr->brush_color = value;
928 break;
930 case NtGdiSetDCPenColor:
931 physdev = GET_DC_PHYSDEV( dc, pSetDCPenColor );
932 prev = dc->attr->pen_color;
933 value = physdev->funcs->pSetDCPenColor( physdev, value );
934 if (value != CLR_INVALID) dc->attr->pen_color = value;
935 break;
937 case NtGdiSetGraphicsMode:
938 prev = dc->attr->graphics_mode;
939 ret = set_graphics_mode( dc, value );
940 break;
942 default:
943 WARN( "unknown method %u\n", method );
944 ret = FALSE;
945 break;
948 release_dc_ptr( dc );
949 if (!ret || !prev_value) return FALSE;
950 *prev_value = prev;
951 return TRUE;
955 /***********************************************************************
956 * NtGdiSetBrushOrg (win32u.@)
958 BOOL WINAPI NtGdiSetBrushOrg( HDC hdc, INT x, INT y, POINT *oldorg )
960 DC *dc;
962 if (!(dc = get_dc_ptr( hdc ))) return FALSE;
963 if (oldorg) *oldorg = dc->attr->brush_org;
964 dc->attr->brush_org.x = x;
965 dc->attr->brush_org.y = y;
966 release_dc_ptr( dc );
967 return TRUE;
971 /***********************************************************************
972 * NtGdiGetTransform (win32u.@)
974 * Undocumented
976 * Returns one of the co-ordinate space transforms
978 * PARAMS
979 * hdc [I] Device context.
980 * which [I] Which xform to return:
981 * 0x203 World -> Page transform (that set by SetWorldTransform).
982 * 0x304 Page -> Device transform (the mapping mode transform).
983 * 0x204 World -> Device transform (the combination of the above two).
984 * 0x402 Device -> World transform (the inversion of the above).
985 * xform [O] The xform.
988 BOOL WINAPI NtGdiGetTransform( HDC hdc, DWORD which, XFORM *xform )
990 BOOL ret = TRUE;
991 DC *dc = get_dc_ptr( hdc );
992 if (!dc) return FALSE;
994 switch(which)
996 case 0x203:
997 *xform = dc->xformWorld2Wnd;
998 break;
1000 case 0x304:
1001 construct_window_to_viewport(dc, xform);
1002 break;
1004 case 0x204:
1005 *xform = dc->xformWorld2Vport;
1006 break;
1008 case 0x402:
1009 *xform = dc->xformVport2World;
1010 break;
1012 default:
1013 FIXME("Unknown code %x\n", which);
1014 ret = FALSE;
1017 release_dc_ptr( dc );
1018 return ret;
1022 /***********************************************************************
1023 * SetDCHook (win32u.@)
1025 * Note: this doesn't exist in Win32, we add it here because user32 needs it.
1027 BOOL WINAPI SetDCHook( HDC hdc, DCHOOKPROC hookProc, DWORD_PTR dwHookData )
1029 DC *dc = get_dc_obj( hdc );
1031 if (!dc) return FALSE;
1032 if (dc->attr->disabled)
1034 GDI_ReleaseObj( hdc );
1035 return 0;
1038 dc->dwHookData = dwHookData;
1039 dc->hookProc = hookProc;
1040 GDI_ReleaseObj( hdc );
1041 return TRUE;
1045 /***********************************************************************
1046 * GetDCHook (win32u.@)
1048 * Note: this doesn't exist in Win32, we add it here because user32 needs it.
1050 DWORD_PTR WINAPI GetDCHook( HDC hdc, DCHOOKPROC *proc )
1052 DC *dc = get_dc_obj( hdc );
1053 DWORD_PTR ret;
1055 if (!dc) return 0;
1056 if (dc->attr->disabled)
1058 GDI_ReleaseObj( hdc );
1059 return 0;
1061 if (proc) *proc = dc->hookProc;
1062 ret = dc->dwHookData;
1063 GDI_ReleaseObj( hdc );
1064 return ret;
1068 /***********************************************************************
1069 * SetHookFlags (win32u.@)
1071 * Note: this doesn't exist in Win32, we add it here because user32 needs it.
1073 WORD WINAPI SetHookFlags( HDC hdc, WORD flags )
1075 DC *dc = get_dc_obj( hdc ); /* not get_dc_ptr, this needs to work from any thread */
1076 LONG ret = 0;
1078 if (!dc) return 0;
1080 TRACE("hDC %p, flags %04x\n",hdc,flags);
1082 if (flags & DCHF_INVALIDATEVISRGN)
1083 ret = InterlockedExchange( &dc->dirty, 1 );
1084 else if (flags & DCHF_VALIDATEVISRGN || !flags)
1085 ret = InterlockedExchange( &dc->dirty, 0 );
1087 if (flags & DCHF_DISABLEDC)
1088 ret = InterlockedExchange( &dc->attr->disabled, 1 );
1089 else if (flags & DCHF_ENABLEDC)
1090 ret = InterlockedExchange( &dc->attr->disabled, 0 );
1092 GDI_ReleaseObj( hdc );
1094 if (flags & DCHF_RESETDC) ret = reset_dc_state( hdc );
1095 return ret;
1098 /***********************************************************************
1099 * NtGdiGetDeviceGammaRamp (win32u.@)
1101 BOOL WINAPI NtGdiGetDeviceGammaRamp( HDC hdc, void *ptr )
1103 BOOL ret = FALSE;
1104 DC *dc = get_dc_ptr( hdc );
1106 TRACE("%p, %p\n", hdc, ptr);
1107 if( dc )
1109 if (get_gdi_object_type( hdc ) != NTGDI_OBJ_MEMDC)
1111 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pGetDeviceGammaRamp );
1112 ret = physdev->funcs->pGetDeviceGammaRamp( physdev, ptr );
1114 else SetLastError( ERROR_INVALID_PARAMETER );
1115 release_dc_ptr( dc );
1117 return ret;
1120 static BOOL check_gamma_ramps(void *ptr)
1122 WORD *ramp = ptr;
1124 while (ramp < (WORD*)ptr + 3 * 256)
1126 float r_x, r_y, r_lx, r_ly, r_d, r_v, r_e, g_avg, g_min, g_max;
1127 unsigned i, f, l, g_n, c;
1129 f = ramp[0];
1130 l = ramp[255];
1131 if (f >= l)
1133 TRACE("inverted or flat gamma ramp (%d->%d), rejected\n", f, l);
1134 return FALSE;
1136 r_d = l - f;
1137 g_min = g_max = g_avg = 0.0;
1139 /* check gamma ramp entries to estimate the gamma */
1140 TRACE("analyzing gamma ramp (%d->%d)\n", f, l);
1141 for (i=1, g_n=0; i<255; i++)
1143 if (ramp[i] < f || ramp[i] > l)
1145 TRACE("strange gamma ramp ([%d]=%d for %d->%d), rejected\n", i, ramp[i], f, l);
1146 return FALSE;
1148 c = ramp[i] - f;
1149 if (!c) continue; /* avoid log(0) */
1151 /* normalize entry values into 0..1 range */
1152 r_x = i/255.0; r_y = c / r_d;
1153 /* compute logarithms of values */
1154 r_lx = log(r_x); r_ly = log(r_y);
1155 /* compute gamma for this entry */
1156 r_v = r_ly / r_lx;
1157 /* compute differential (error estimate) for this entry */
1158 /* some games use table-based logarithms that magnifies the error by 128 */
1159 r_e = -r_lx * 128 / (c * r_lx * r_lx);
1161 /* compute min & max while compensating for estimated error */
1162 if (!g_n || g_min > (r_v + r_e)) g_min = r_v + r_e;
1163 if (!g_n || g_max < (r_v - r_e)) g_max = r_v - r_e;
1165 /* add to average */
1166 g_avg += r_v;
1167 g_n++;
1170 if (!g_n)
1172 TRACE("no gamma data, shouldn't happen\n");
1173 return FALSE;
1175 g_avg /= g_n;
1176 TRACE("low bias is %d, high is %d, gamma is %5.3f\n", f, 65535-l, g_avg);
1178 /* check that the gamma is reasonably uniform across the ramp */
1179 if (g_max - g_min > 12.8)
1181 TRACE("ramp not uniform (max=%f, min=%f, avg=%f), rejected\n", g_max, g_min, g_avg);
1182 return FALSE;
1185 /* check that the gamma is not too bright */
1186 if (g_avg < 0.2)
1188 TRACE("too bright gamma ( %5.3f), rejected\n", g_avg);
1189 return FALSE;
1192 ramp += 256;
1195 return TRUE;
1198 /***********************************************************************
1199 * NtGdiSetDeviceGammaRamp (win32u.@)
1201 BOOL WINAPI NtGdiSetDeviceGammaRamp( HDC hdc, void *ptr )
1203 BOOL ret = FALSE;
1204 DC *dc = get_dc_ptr( hdc );
1206 TRACE( "%p, %p\n", hdc, ptr );
1207 if( dc )
1209 if (get_gdi_object_type( hdc ) != NTGDI_OBJ_MEMDC)
1211 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pSetDeviceGammaRamp );
1213 if (check_gamma_ramps(ptr))
1214 ret = physdev->funcs->pSetDeviceGammaRamp( physdev, ptr );
1216 else SetLastError( ERROR_INVALID_PARAMETER );
1217 release_dc_ptr( dc );
1219 return ret;
1223 /***********************************************************************
1224 * NtGdiGetBoundsRect (win32u.@)
1226 UINT WINAPI NtGdiGetBoundsRect( HDC hdc, RECT *rect, UINT flags )
1228 PHYSDEV physdev;
1229 RECT device_rect;
1230 UINT ret;
1231 DC *dc = get_dc_ptr( hdc );
1233 if ( !dc ) return 0;
1235 physdev = GET_DC_PHYSDEV( dc, pGetBoundsRect );
1236 ret = physdev->funcs->pGetBoundsRect( physdev, &device_rect, DCB_RESET );
1237 if (!ret)
1239 release_dc_ptr( dc );
1240 return 0;
1242 if (dc->bounds_enabled && ret == DCB_SET) add_bounds_rect( &dc->bounds, &device_rect );
1244 if (rect)
1246 if (is_rect_empty( &dc->bounds ))
1248 rect->left = rect->top = rect->right = rect->bottom = 0;
1249 ret = DCB_RESET;
1251 else
1253 *rect = dc->bounds;
1254 rect->left = max( rect->left, 0 );
1255 rect->top = max( rect->top, 0 );
1256 rect->right = min( rect->right, dc->attr->vis_rect.right - dc->attr->vis_rect.left );
1257 rect->bottom = min( rect->bottom, dc->attr->vis_rect.bottom - dc->attr->vis_rect.top );
1258 ret = DCB_SET;
1260 dp_to_lp( dc, (POINT *)rect, 2 );
1262 else ret = 0;
1264 if (flags & DCB_RESET) reset_bounds( &dc->bounds );
1265 release_dc_ptr( dc );
1266 return ret;
1270 /***********************************************************************
1271 * NtGdiSetBoundsRect (win32u.@)
1273 UINT WINAPI NtGdiSetBoundsRect( HDC hdc, const RECT *rect, UINT flags )
1275 PHYSDEV physdev;
1276 UINT ret;
1277 DC *dc;
1279 if ((flags & DCB_ENABLE) && (flags & DCB_DISABLE)) return 0;
1280 if (!(dc = get_dc_ptr( hdc ))) return 0;
1282 physdev = GET_DC_PHYSDEV( dc, pSetBoundsRect );
1283 ret = physdev->funcs->pSetBoundsRect( physdev, &dc->bounds, flags );
1284 if (!ret)
1286 release_dc_ptr( dc );
1287 return 0;
1290 ret = (dc->bounds_enabled ? DCB_ENABLE : DCB_DISABLE) |
1291 (is_rect_empty( &dc->bounds ) ? ret & DCB_SET : DCB_SET);
1293 if (flags & DCB_RESET) reset_bounds( &dc->bounds );
1295 if ((flags & DCB_ACCUMULATE) && rect)
1297 RECT rc = *rect;
1299 lp_to_dp( dc, (POINT *)&rc, 2 );
1300 add_bounds_rect( &dc->bounds, &rc );
1303 if (flags & DCB_ENABLE) dc->bounds_enabled = TRUE;
1304 if (flags & DCB_DISABLE) dc->bounds_enabled = FALSE;
1306 release_dc_ptr( dc );
1307 return ret;
1311 /***********************************************************************
1312 * NtGdiSetLayout (win32u.@)
1314 * Sets left->right or right->left text layout flags of a dc.
1317 DWORD WINAPI NtGdiSetLayout( HDC hdc, LONG wox, DWORD layout )
1319 DWORD old_layout = GDI_ERROR;
1320 DC *dc;
1322 if ((dc = get_dc_ptr( hdc )))
1324 old_layout = dc->attr->layout;
1325 dc->attr->layout = layout;
1326 if (layout != old_layout)
1328 if (layout & LAYOUT_RTL) dc->attr->map_mode = MM_ANISOTROPIC;
1329 DC_UpdateXforms( dc );
1331 release_dc_ptr( dc );
1334 TRACE("hdc : %p, old layout : %08x, new layout : %08x\n", hdc, old_layout, layout);
1336 return old_layout;
1339 /**********************************************************************
1340 * __wine_get_icm_profile (win32u.@)
1342 BOOL CDECL __wine_get_icm_profile( HDC hdc, BOOL allow_default, DWORD *size, WCHAR *filename )
1344 PHYSDEV physdev;
1345 DC *dc;
1346 BOOL ret;
1348 if (!(dc = get_dc_ptr(hdc))) return FALSE;
1350 physdev = GET_DC_PHYSDEV( dc, pGetICMProfile );
1351 ret = physdev->funcs->pGetICMProfile( physdev, allow_default, size, filename );
1352 release_dc_ptr(dc);
1353 return ret;