[WIN32K]
[reactos.git] / reactos / win32ss / gdi / ntgdi / coord.c
1 /*
2 * COPYRIGHT: GNU GPL, See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Coordinate systems
5 * FILE: subsystems/win32/win32k/objects/coord.c
6 * PROGRAMER: Timo Kreuzer (timo.kreuzer@rectos.org)
7 */
8
9 /* Coordinate translation overview
10 * -------------------------------
11 *
12 * Windows uses 3 different coordinate systems, referred to as world space,
13 * page space and device space.
14 *
15 * Device space:
16 * This is the coordinate system of the physical device that displays the
17 * graphics. One unit matches one pixel of the surface. The coordinate system
18 * is always orthogonal.
19 *
20 * Page space:
21 * This is the coordinate system on the screen or on the paper layout for
22 * printer devices. The coordinate system is also orthogonal but one unit
23 * does not necessarily match one pixel. Instead there are different mapping
24 * modes that can be set using SetMapMode() that specify how page space units
25 * are transformed into device space units. These mapping modes are:
26 * - MM_TEXT: One unit matches one unit in device space (one pixel)
27 * - MM_TWIPS One unit matches 1/20 point (1/1440 inch)
28 * - MM_LOMETRIC: One unit matches 0.1 millimeter
29 * - MM_HIMETRIC: One unit matches 0.01 millimeter
30 * - MM_LOENGLISH: One unit matches 0.01 inch
31 * - MM_HIENGLISH: One unit matches 0.001 inch
32 * - MM_ISOTROPIC:
33 * - MM_ANISOTROPIC:
34 * If the mapping mode is either MM_ISOTROPIC or MM_ANISOTROPIC, the actual
35 * transformation is calculated from the window and viewport extension.
36 * The window extension can be set using SetWindowExtEx() and describes the
37 * extents of an arbitrary window (not to confuse with the gui element!) in
38 * page space coordinates.
39 * The viewport extension can be set using SetViewportExtEx() and describes
40 * the extent of the same window in device space coordinates. If the mapping
41 * mode is MM_ISOTROPIC one of the viewport extensions can be adjusted by GDI
42 * to make sure the mapping stays isotropic, i.e. that it has the same x/y
43 * ratio as the window extension.
44 *
45 * World space:
46 * World space is the coordinate system that is used for all GDI drawing
47 * operations. The metrics of this coordinate system depend on the DCs
48 * graphics mode, which can be set using SetGraphicsMode().
49 * If the graphics mode is GM_COMPATIBLE, world space is identical to page
50 * space and no additional transformation is applied.
51 * If the graphics mode is GM_ADVANCED, an arbitrary coordinate transformation
52 * can be set using SetWorldTransform(), which is applied to transform world
53 * space coordinates into page space coordinates.
54 *
55 * User mode data:
56 * All coordinate translation data is stored in the DC attribute, so the values
57 * might be invalid. This has to be taken into account. Values might also be
58 * zero, so when a division is made, the value has to be read first and then
59 * checked! This is true for both integer and floating point values, even if
60 * we cannot get floating point exceptions on x86, we can get them on all other
61 * architectures that use the FPU directly instead of emulation.
62 * The result of all operations might be completely random and invalid, if it was
63 * messed with in an illegal way in user mode. This is not a problem, since the
64 * result of coordinate transformations are never expected to be "valid" values.
65 * In the worst case, the drawing operation draws rubbish into the DC.
66 */
67
68 /* INCLUDES ******************************************************************/
69
70 #include <win32k.h>
71
72 #define NDEBUG
73 #include <debug.h>
74 C_ASSERT(sizeof(XFORML) == sizeof(XFORM));
75
76
77 /* GLOBALS *******************************************************************/
78
79 const MATRIX gmxIdentity =
80 {
81 FLOATOBJ_1, FLOATOBJ_0,
82 FLOATOBJ_0, FLOATOBJ_1,
83 FLOATOBJ_0, FLOATOBJ_0,
84 0, 0, XFORM_NO_TRANSLATION|XFORM_FORMAT_LTOL|XFORM_UNITY|XFORM_SCALE
85 };
86
87
88 /* FUNCTIONS *****************************************************************/
89
90 VOID
91 FASTCALL
92 DC_vFixIsotropicMapping(PDC pdc)
93 {
94 PDC_ATTR pdcattr;
95 LONG64 fx, fy;
96 LONG s;
97 SIZEL szlWindowExt, szlViewportExt;
98 ASSERT(pdc->pdcattr->iMapMode == MM_ISOTROPIC);
99
100 /* Get a pointer to the DC_ATTR */
101 pdcattr = pdc->pdcattr;
102
103 /* Read the extents, we rely on non-null values */
104 szlWindowExt = pdcattr->szlWindowExt;
105 szlViewportExt = pdcattr->szlViewportExt;
106
107 /* Check if all values are valid */
108 if ((szlWindowExt.cx == 0) || (szlWindowExt.cy == 0) ||
109 (szlViewportExt.cx == 0) || (szlViewportExt.cy == 0))
110 {
111 /* Someone put rubbish into the fields, just ignore it. */
112 return;
113 }
114
115 fx = abs((LONG64)szlWindowExt.cx * szlViewportExt.cy);
116 fy = abs((LONG64)szlWindowExt.cy * szlViewportExt.cx);
117
118 if (fx < fy)
119 {
120 s = (szlWindowExt.cy ^ szlViewportExt.cx) > 0 ? 1 : -1;
121 pdcattr->szlViewportExt.cx = (LONG)(fx * s / szlWindowExt.cy);
122 }
123 else if (fx > fy)
124 {
125 s = (szlWindowExt.cx ^ szlViewportExt.cy) > 0 ? 1 : -1;
126 pdcattr->szlViewportExt.cy = (LONG)(fy * s / szlWindowExt.cx);
127 }
128
129 /* Reset the flag */
130 pdc->pdcattr->flXform &= ~PAGE_EXTENTS_CHANGED;
131 }
132
133 VOID
134 FASTCALL
135 DC_vGetPageToDevice(PDC pdc, MATRIX *pmx)
136 {
137 PDC_ATTR pdcattr = pdc->pdcattr;
138 PSIZEL pszlViewPortExt;
139 SIZEL szlWindowExt;
140
141 /* Get the viewport extension */
142 pszlViewPortExt = DC_pszlViewportExt(pdc);
143
144 /* Copy the window extension, so no one can mess with it */
145 szlWindowExt = pdcattr->szlWindowExt;
146
147 /* No shearing / rotation */
148 FLOATOBJ_SetLong(&pmx->efM12, 0);
149 FLOATOBJ_SetLong(&pmx->efM21, 0);
150
151 /* Calculate scaling */
152 if (szlWindowExt.cx != 0)
153 {
154 FLOATOBJ_SetLong(&pmx->efM11, pszlViewPortExt->cx);
155 FLOATOBJ_DivLong(&pmx->efM11, szlWindowExt.cx);
156 }
157 else
158 FLOATOBJ_SetLong(&pmx->efM11, 1);
159
160 if (szlWindowExt.cy != 0)
161 {
162 FLOATOBJ_SetLong(&pmx->efM22, pszlViewPortExt->cy);
163 FLOATOBJ_DivLong(&pmx->efM22, szlWindowExt.cy);
164 }
165 else
166 FLOATOBJ_SetLong(&pmx->efM22, 1);
167
168 /* Calculate x offset */
169 FLOATOBJ_SetLong(&pmx->efDx, -pdcattr->ptlWindowOrg.x);
170 FLOATOBJ_Mul(&pmx->efDx, &pmx->efM11);
171 FLOATOBJ_AddLong(&pmx->efDx, pdcattr->ptlViewportOrg.x);
172
173 /* Calculate y offset */
174 FLOATOBJ_SetLong(&pmx->efDy, -pdcattr->ptlWindowOrg.y);
175 FLOATOBJ_Mul(&pmx->efDy, &pmx->efM22);
176 FLOATOBJ_AddLong(&pmx->efDy, pdcattr->ptlViewportOrg.y);
177 }
178
179 VOID
180 FASTCALL
181 DC_vUpdateWorldToDevice(PDC pdc)
182 {
183 XFORMOBJ xoPageToDevice, xoWorldToPage, xoWorldToDevice;
184 MATRIX mxPageToDevice;
185
186 // FIXME: make sure world-to-page is valid!
187
188 /* Construct a transformation to do the page-to-device conversion */
189 DC_vGetPageToDevice(pdc, &mxPageToDevice);
190 XFORMOBJ_vInit(&xoPageToDevice, &mxPageToDevice);
191
192 /* Recalculate the world-to-device xform */
193 XFORMOBJ_vInit(&xoWorldToPage, &pdc->pdcattr->mxWorldToPage);
194 XFORMOBJ_vInit(&xoWorldToDevice, &pdc->pdcattr->mxWorldToDevice);
195 XFORMOBJ_iCombine(&xoWorldToDevice, &xoWorldToPage, &xoPageToDevice);
196
197 /* Reset the flags */
198 pdc->pdcattr->flXform &= ~(PAGE_XLATE_CHANGED|PAGE_EXTENTS_CHANGED|WORLD_XFORM_CHANGED);
199 }
200
201 VOID
202 FASTCALL
203 DC_vUpdateDeviceToWorld(PDC pdc)
204 {
205 XFORMOBJ xoWorldToDevice, xoDeviceToWorld;
206 PMATRIX pmxWorldToDevice;
207
208 /* Get the world-to-device translation */
209 pmxWorldToDevice = DC_pmxWorldToDevice(pdc);
210 XFORMOBJ_vInit(&xoWorldToDevice, pmxWorldToDevice);
211
212 /* Create inverse of world-to-device transformation */
213 XFORMOBJ_vInit(&xoDeviceToWorld, &pdc->pdcattr->mxDeviceToWorld);
214 if (XFORMOBJ_iInverse(&xoDeviceToWorld, &xoWorldToDevice) == DDI_ERROR)
215 {
216 // FIXME: do we need to reset anything?
217 return;
218 }
219
220 /* Reset the flag */
221 pdc->pdcattr->flXform &= ~DEVICE_TO_WORLD_INVALID;
222 }
223
224 BOOL
225 NTAPI
226 GreCombineTransform(
227 XFORML *pxformDest,
228 XFORML *pxform1,
229 XFORML *pxform2)
230 {
231 MATRIX mxDest, mx1, mx2;
232 XFORMOBJ xoDest, xo1, xo2;
233
234 /* Check for illegal parameters */
235 if (!pxformDest || !pxform1 || !pxform2) return FALSE;
236
237 /* Initialize XFORMOBJs */
238 XFORMOBJ_vInit(&xoDest, &mxDest);
239 XFORMOBJ_vInit(&xo1, &mx1);
240 XFORMOBJ_vInit(&xo2, &mx2);
241
242 /* Convert the XFORMLs into XFORMOBJs */
243 XFORMOBJ_iSetXform(&xo1, pxform1);
244 XFORMOBJ_iSetXform(&xo2, pxform2);
245
246 /* Combine them */
247 XFORMOBJ_iCombine(&xoDest, &xo1, &xo2);
248
249 /* Translate back into XFORML */
250 XFORMOBJ_iGetXform(&xoDest, pxformDest);
251
252 return TRUE;
253 }
254
255 BOOL
256 APIENTRY
257 NtGdiCombineTransform(
258 LPXFORM UnsafeXFormResult,
259 LPXFORM Unsafexform1,
260 LPXFORM Unsafexform2)
261 {
262 BOOL Ret;
263
264 _SEH2_TRY
265 {
266 ProbeForWrite(UnsafeXFormResult, sizeof(XFORM), 1);
267 ProbeForRead(Unsafexform1, sizeof(XFORM), 1);
268 ProbeForRead(Unsafexform2, sizeof(XFORM), 1);
269 Ret = GreCombineTransform((XFORML*)UnsafeXFormResult,
270 (XFORML*)Unsafexform1,
271 (XFORML*)Unsafexform2);
272 }
273 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
274 {
275 Ret = FALSE;
276 }
277 _SEH2_END;
278
279 return Ret;
280 }
281
282 // FIXME: Should be XFORML and use XFORMOBJ functions directly
283 BOOL
284 APIENTRY
285 NtGdiGetTransform(
286 HDC hdc,
287 DWORD iXform,
288 LPXFORM pXForm)
289 {
290 PDC pdc;
291 BOOL ret = TRUE;
292 MATRIX mxPageToDevice;
293 XFORMOBJ xo;
294 PMATRIX pmx;
295
296 if (!pXForm)
297 {
298 EngSetLastError(ERROR_INVALID_PARAMETER);
299 return FALSE;
300 }
301
302 pdc = DC_LockDc(hdc);
303 if (!pdc)
304 {
305 EngSetLastError(ERROR_INVALID_HANDLE);
306 return FALSE;
307 }
308
309 switch (iXform)
310 {
311 case GdiWorldSpaceToPageSpace:
312 pmx = DC_pmxWorldToPage(pdc);
313 break;
314
315 case GdiWorldSpaceToDeviceSpace:
316 pmx = DC_pmxWorldToDevice(pdc);
317 break;
318
319 case GdiDeviceSpaceToWorldSpace:
320 pmx = DC_pmxDeviceToWorld(pdc);
321 break;
322
323 case GdiPageSpaceToDeviceSpace:
324 DC_vGetPageToDevice(pdc, &mxPageToDevice);
325 pmx = &mxPageToDevice;
326 break;
327
328 default:
329 DPRINT1("Unknown transform %lu\n", iXform);
330 ret = FALSE;
331 goto leave;
332 }
333
334 /* Initialize an XFORMOBJ */
335 XFORMOBJ_vInit(&xo, pmx);
336
337 _SEH2_TRY
338 {
339 ProbeForWrite(pXForm, sizeof(XFORML), 1);
340 XFORMOBJ_iGetXform(&xo, (XFORML*)pXForm);
341 }
342 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
343 {
344 ret = FALSE;
345 }
346 _SEH2_END;
347
348 leave:
349 DC_UnlockDc(pdc);
350 return ret;
351 }
352
353
354 /*!
355 * Converts points from logical coordinates into device coordinates.
356 * Conversion depends on the mapping mode,
357 * world transfrom, viewport origin settings for the given device context.
358 * \param hDC device context.
359 * \param Points an array of POINT structures (in/out).
360 * \param Count number of elements in the array of POINT structures.
361 * \return TRUE if success, FALSE otherwise.
362 */
363 BOOL
364 APIENTRY
365 NtGdiTransformPoints(
366 HDC hDC,
367 PPOINT UnsafePtsIn,
368 PPOINT UnsafePtOut,
369 INT Count,
370 INT iMode)
371 {
372 PDC pdc;
373 LPPOINT Points;
374 ULONG Size;
375 BOOL ret = TRUE;
376
377 if (Count <= 0)
378 return TRUE;
379
380 pdc = DC_LockDc(hDC);
381 if (!pdc)
382 {
383 EngSetLastError(ERROR_INVALID_PARAMETER);
384 return FALSE;
385 }
386
387 Size = Count * sizeof(POINT);
388
389 // FIXME: It would be wise to have a small stack buffer as optimization
390 Points = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEMP);
391 if (!Points)
392 {
393 DC_UnlockDc(pdc);
394 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
395 return FALSE;
396 }
397
398 _SEH2_TRY
399 {
400 ProbeForWrite(UnsafePtOut, Size, 1);
401 ProbeForRead(UnsafePtsIn, Size, 1);
402 RtlCopyMemory(Points, UnsafePtsIn, Size);
403 }
404 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
405 {
406 /* Do not set last error */
407 _SEH2_YIELD(goto leave;)
408 }
409 _SEH2_END;
410
411 switch (iMode)
412 {
413 case GdiDpToLp:
414 DC_vXformDeviceToWorld(pdc, Count, Points, Points);
415 break;
416
417 case GdiLpToDp:
418 DC_vXformWorldToDevice(pdc, Count, Points, Points);
419 break;
420
421 case 2: // Not supported yet. Need testing.
422 default:
423 {
424 EngSetLastError(ERROR_INVALID_PARAMETER);
425 ret = FALSE;
426 goto leave;
427 }
428 }
429
430 _SEH2_TRY
431 {
432 /* Pointer was already probed! */
433 RtlCopyMemory(UnsafePtOut, Points, Size);
434 }
435 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
436 {
437 /* Do not set last error */
438 ret = 0;
439 }
440 _SEH2_END;
441
442 //
443 // If we are getting called that means User XForms is a mess!
444 //
445 leave:
446 DC_UnlockDc(pdc);
447 ExFreePoolWithTag(Points, GDITAG_TEMP);
448 return ret;
449 }
450
451 BOOL
452 NTAPI
453 GreModifyWorldTransform(
454 PDC pdc,
455 const XFORML *pxform,
456 DWORD dwMode)
457 {
458 MATRIX mxSrc;
459 XFORMOBJ xoSrc, xoDC;
460
461 switch (dwMode)
462 {
463 case MWT_IDENTITY:
464 pdc->pdcattr->mxWorldToPage = gmxIdentity;
465 break;
466
467 case MWT_LEFTMULTIPLY:
468 XFORMOBJ_vInit(&xoDC, &pdc->pdcattr->mxWorldToPage);
469 XFORMOBJ_vInit(&xoSrc, &mxSrc);
470 if (XFORMOBJ_iSetXform(&xoSrc, pxform) == DDI_ERROR)
471 return FALSE;
472 XFORMOBJ_iCombine(&xoDC, &xoSrc, &xoDC);
473 break;
474
475 case MWT_RIGHTMULTIPLY:
476 XFORMOBJ_vInit(&xoDC, &pdc->pdcattr->mxWorldToPage);
477 XFORMOBJ_vInit(&xoSrc, &mxSrc);
478 if (XFORMOBJ_iSetXform(&xoSrc, pxform) == DDI_ERROR)
479 return FALSE;
480 XFORMOBJ_iCombine(&xoDC, &xoDC, &xoSrc);
481 break;
482
483 case MWT_MAX+1: // Must be MWT_SET????
484 XFORMOBJ_vInit(&xoDC, &pdc->pdcattr->mxWorldToPage);
485 if (XFORMOBJ_iSetXform(&xoDC, pxform) == DDI_ERROR)
486 return FALSE;
487 break;
488
489 default:
490 return FALSE;
491 }
492
493 /*Set invalidation flags */
494 pdc->pdcattr->flXform |= WORLD_XFORM_CHANGED|DEVICE_TO_WORLD_INVALID;
495
496 return TRUE;
497 }
498
499 BOOL
500 APIENTRY
501 NtGdiModifyWorldTransform(
502 HDC hdc,
503 LPXFORM pxformUnsafe,
504 DWORD dwMode)
505 {
506 PDC pdc;
507 XFORML xformSafe;
508 BOOL Ret = TRUE;
509
510 pdc = DC_LockDc(hdc);
511 if (!pdc)
512 {
513 EngSetLastError(ERROR_INVALID_HANDLE);
514 return FALSE;
515 }
516
517 /* The xform is permitted to be NULL for MWT_IDENTITY.
518 * However, if it is not NULL, then it must be valid even
519 * though it is not used. */
520 if ((pxformUnsafe != NULL) || (dwMode != MWT_IDENTITY))
521 {
522 _SEH2_TRY
523 {
524 ProbeForRead(pxformUnsafe, sizeof(XFORML), 1);
525 RtlCopyMemory(&xformSafe, pxformUnsafe, sizeof(XFORML));
526 }
527 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
528 {
529 Ret = FALSE;
530 }
531 _SEH2_END;
532 }
533
534 /* Safe to handle kernel mode data. */
535 if (Ret) Ret = GreModifyWorldTransform(pdc, &xformSafe, dwMode);
536 DC_UnlockDc(pdc);
537 return Ret;
538 }
539
540 BOOL
541 APIENTRY
542 NtGdiOffsetViewportOrgEx(
543 HDC hDC,
544 int XOffset,
545 int YOffset,
546 LPPOINT UnsafePoint)
547 {
548 PDC dc;
549 PDC_ATTR pdcattr;
550 NTSTATUS Status = STATUS_SUCCESS;
551
552 dc = DC_LockDc(hDC);
553 if (!dc)
554 {
555 EngSetLastError(ERROR_INVALID_HANDLE);
556 return FALSE;
557 }
558 pdcattr = dc->pdcattr;
559
560 if (UnsafePoint)
561 {
562 _SEH2_TRY
563 {
564 ProbeForWrite(UnsafePoint, sizeof(POINT), 1);
565 UnsafePoint->x = pdcattr->ptlViewportOrg.x;
566 UnsafePoint->y = pdcattr->ptlViewportOrg.y;
567 if (pdcattr->dwLayout & LAYOUT_RTL)
568 {
569 UnsafePoint->x = -UnsafePoint->x;
570 }
571 }
572 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
573 {
574 Status = _SEH2_GetExceptionCode();
575 }
576 _SEH2_END;
577
578 if (!NT_SUCCESS(Status))
579 {
580 SetLastNtError(Status);
581 DC_UnlockDc(dc);
582 return FALSE;
583 }
584 }
585
586 if (pdcattr->dwLayout & LAYOUT_RTL)
587 {
588 XOffset = -XOffset;
589 }
590 pdcattr->ptlViewportOrg.x += XOffset;
591 pdcattr->ptlViewportOrg.y += YOffset;
592 pdcattr->flXform |= PAGE_XLATE_CHANGED;
593
594 DC_UnlockDc(dc);
595
596 return TRUE;
597 }
598
599 BOOL
600 APIENTRY
601 NtGdiOffsetWindowOrgEx(
602 HDC hDC,
603 int XOffset,
604 int YOffset,
605 LPPOINT Point)
606 {
607 PDC dc;
608 PDC_ATTR pdcattr;
609
610 dc = DC_LockDc(hDC);
611 if (!dc)
612 {
613 EngSetLastError(ERROR_INVALID_HANDLE);
614 return FALSE;
615 }
616 pdcattr = dc->pdcattr;
617
618 if (Point)
619 {
620 NTSTATUS Status = STATUS_SUCCESS;
621
622 _SEH2_TRY
623 {
624 ProbeForWrite(Point, sizeof(POINT), 1);
625 Point->x = pdcattr->ptlWindowOrg.x;
626 Point->y = pdcattr->ptlWindowOrg.y;
627 }
628 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
629 {
630 Status = _SEH2_GetExceptionCode();
631 }
632 _SEH2_END;
633
634 if (!NT_SUCCESS(Status))
635 {
636 SetLastNtError(Status);
637 DC_UnlockDc(dc);
638 return FALSE;
639 }
640 }
641
642 pdcattr->ptlWindowOrg.x += XOffset;
643 pdcattr->ptlWindowOrg.y += YOffset;
644 pdcattr->flXform |= PAGE_XLATE_CHANGED|DEVICE_TO_WORLD_INVALID;
645
646 DC_UnlockDc(dc);
647
648 return TRUE;
649 }
650
651 BOOL
652 APIENTRY
653 NtGdiScaleViewportExtEx(
654 HDC hDC,
655 int Xnum,
656 int Xdenom,
657 int Ynum,
658 int Ydenom,
659 LPSIZE pSize)
660 {
661 PDC pDC;
662 PDC_ATTR pdcattr;
663 BOOL Ret = FALSE;
664 LONG X, Y;
665
666 pDC = DC_LockDc(hDC);
667 if (!pDC)
668 {
669 EngSetLastError(ERROR_INVALID_HANDLE);
670 return FALSE;
671 }
672 pdcattr = pDC->pdcattr;
673
674 if (pdcattr->iMapMode > MM_TWIPS)
675 {
676 if (Xdenom && Ydenom)
677 {
678 DC_pszlViewportExt(pDC);
679 X = Xnum * pdcattr->szlViewportExt.cx / Xdenom;
680 if (X)
681 {
682 Y = Ynum * pdcattr->szlViewportExt.cy / Ydenom;
683 if (Y)
684 {
685 pdcattr->szlViewportExt.cx = X;
686 pdcattr->szlViewportExt.cy = Y;
687 pdcattr->flXform |= PAGE_XLATE_CHANGED;
688
689 IntMirrorWindowOrg(pDC);
690
691 pdcattr->flXform |= (PAGE_EXTENTS_CHANGED |
692 INVALIDATE_ATTRIBUTES |
693 DEVICE_TO_WORLD_INVALID);
694
695 if (pdcattr->iMapMode == MM_ISOTROPIC)
696 {
697 DC_vFixIsotropicMapping(pDC);
698 }
699
700 Ret = TRUE;
701 }
702 }
703 }
704 }
705 else
706 Ret = TRUE;
707
708 if (pSize)
709 {
710 _SEH2_TRY
711 {
712 ProbeForWrite(pSize, sizeof(SIZE), 1);
713
714 pSize->cx = pdcattr->szlViewportExt.cx;
715 pSize->cy = pdcattr->szlViewportExt.cy;
716 }
717 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
718 {
719 SetLastNtError(_SEH2_GetExceptionCode());
720 Ret = FALSE;
721 }
722 _SEH2_END;
723 }
724
725 DC_UnlockDc(pDC);
726 return Ret;
727 }
728
729 BOOL
730 APIENTRY
731 NtGdiScaleWindowExtEx(
732 HDC hDC,
733 int Xnum,
734 int Xdenom,
735 int Ynum,
736 int Ydenom,
737 LPSIZE pSize)
738 {
739 PDC pDC;
740 PDC_ATTR pdcattr;
741 BOOL Ret = FALSE;
742 LONG X, Y;
743
744 pDC = DC_LockDc(hDC);
745 if (!pDC)
746 {
747 EngSetLastError(ERROR_INVALID_HANDLE);
748 return FALSE;
749 }
750 pdcattr = pDC->pdcattr;
751
752 if (pSize)
753 {
754 NTSTATUS Status = STATUS_SUCCESS;
755
756 _SEH2_TRY
757 {
758 ProbeForWrite(pSize, sizeof(SIZE), 1);
759
760 X = pdcattr->szlWindowExt.cx;
761 if (pdcattr->dwLayout & LAYOUT_RTL) X = -X;
762 pSize->cx = X;
763 pSize->cy = pdcattr->szlWindowExt.cy;
764 }
765 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
766 {
767 Status = _SEH2_GetExceptionCode();
768 }
769 _SEH2_END;
770
771 if (!NT_SUCCESS(Status))
772 {
773 SetLastNtError(Status);
774 DC_UnlockDc(pDC);
775 return FALSE;
776 }
777 }
778
779 if (pdcattr->iMapMode > MM_TWIPS)
780 {
781 if (Xdenom && Ydenom)
782 {
783 X = Xnum * pdcattr->szlWindowExt.cx / Xdenom;
784 if (X)
785 {
786 Y = Ynum * pdcattr->szlWindowExt.cy / Ydenom;
787 if (Y)
788 {
789 pdcattr->szlWindowExt.cx = X;
790 pdcattr->szlWindowExt.cy = Y;
791
792 IntMirrorWindowOrg(pDC);
793
794 pdcattr->flXform |= (PAGE_EXTENTS_CHANGED|INVALIDATE_ATTRIBUTES|DEVICE_TO_WORLD_INVALID);
795
796 Ret = TRUE;
797 }
798 }
799 }
800 }
801 else
802 Ret = TRUE;
803
804 DC_UnlockDc(pDC);
805 return Ret;
806 }
807
808 int
809 APIENTRY
810 IntGdiSetMapMode(
811 PDC dc,
812 int MapMode)
813 {
814 INT iPrevMapMode;
815 FLONG flXform;
816 PDC_ATTR pdcattr = dc->pdcattr;
817
818 flXform = pdcattr->flXform & ~(ISO_OR_ANISO_MAP_MODE|PTOD_EFM22_NEGATIVE|
819 PTOD_EFM11_NEGATIVE|POSITIVE_Y_IS_UP|PAGE_TO_DEVICE_SCALE_IDENTITY|
820 PAGE_TO_DEVICE_IDENTITY);
821
822 switch (MapMode)
823 {
824 case MM_TEXT:
825 pdcattr->szlWindowExt.cx = 1;
826 pdcattr->szlWindowExt.cy = 1;
827 pdcattr->szlViewportExt.cx = 1;
828 pdcattr->szlViewportExt.cy = 1;
829 flXform |= PAGE_TO_DEVICE_SCALE_IDENTITY;
830 break;
831
832 case MM_ISOTROPIC:
833 flXform |= ISO_OR_ANISO_MAP_MODE;
834 /* Fall through */
835
836 case MM_LOMETRIC:
837 pdcattr->szlWindowExt.cx = pdcattr->szlVirtualDeviceMm.cx * 10;
838 pdcattr->szlWindowExt.cy = pdcattr->szlVirtualDeviceMm.cy * 10;
839 pdcattr->szlViewportExt.cx = pdcattr->szlVirtualDevicePixel.cx;
840 pdcattr->szlViewportExt.cy = -pdcattr->szlVirtualDevicePixel.cy;
841 break;
842
843 case MM_HIMETRIC:
844 pdcattr->szlWindowExt.cx = pdcattr->szlVirtualDeviceMm.cx * 100;
845 pdcattr->szlWindowExt.cy = pdcattr->szlVirtualDeviceMm.cy * 100;
846 pdcattr->szlViewportExt.cx = pdcattr->szlVirtualDevicePixel.cx;
847 pdcattr->szlViewportExt.cy = -pdcattr->szlVirtualDevicePixel.cy;
848 break;
849
850 case MM_LOENGLISH:
851 pdcattr->szlWindowExt.cx = MulDiv(1000, pdcattr->szlVirtualDeviceMm.cx, 254);
852 pdcattr->szlWindowExt.cy = MulDiv(1000, pdcattr->szlVirtualDeviceMm.cy, 254);
853 pdcattr->szlViewportExt.cx = pdcattr->szlVirtualDevicePixel.cx;
854 pdcattr->szlViewportExt.cy = -pdcattr->szlVirtualDevicePixel.cy;
855 break;
856
857 case MM_HIENGLISH:
858 pdcattr->szlWindowExt.cx = MulDiv(10000, pdcattr->szlVirtualDeviceMm.cx, 254);
859 pdcattr->szlWindowExt.cy = MulDiv(10000, pdcattr->szlVirtualDeviceMm.cy, 254);
860 pdcattr->szlViewportExt.cx = pdcattr->szlVirtualDevicePixel.cx;
861 pdcattr->szlViewportExt.cy = -pdcattr->szlVirtualDevicePixel.cy;
862 break;
863
864 case MM_TWIPS:
865 pdcattr->szlWindowExt.cx = MulDiv(14400, pdcattr->szlVirtualDeviceMm.cx, 254);
866 pdcattr->szlWindowExt.cy = MulDiv(14400, pdcattr->szlVirtualDeviceMm.cy, 254);
867 pdcattr->szlViewportExt.cx = pdcattr->szlVirtualDevicePixel.cx;
868 pdcattr->szlViewportExt.cy = -pdcattr->szlVirtualDevicePixel.cy;
869 break;
870
871 case MM_ANISOTROPIC:
872 flXform &= ~(PAGE_TO_DEVICE_IDENTITY|POSITIVE_Y_IS_UP);
873 flXform |= ISO_OR_ANISO_MAP_MODE;
874 break;
875
876 default:
877 return 0;
878 }
879
880 /* Save the old map mode and set the new one */
881 iPrevMapMode = pdcattr->iMapMode;
882 pdcattr->iMapMode = MapMode;
883
884 /* Update xform flags */
885 pdcattr->flXform = flXform | (PAGE_XLATE_CHANGED|PAGE_EXTENTS_CHANGED|
886 INVALIDATE_ATTRIBUTES|DEVICE_TO_PAGE_INVALID|DEVICE_TO_WORLD_INVALID);
887
888 return iPrevMapMode;
889 }
890
891
892 BOOL
893 APIENTRY
894 NtGdiSetViewportOrgEx(
895 HDC hDC,
896 int X,
897 int Y,
898 LPPOINT Point)
899 {
900 PDC dc;
901 PDC_ATTR pdcattr;
902
903 dc = DC_LockDc(hDC);
904 if (!dc)
905 {
906 EngSetLastError(ERROR_INVALID_HANDLE);
907 return FALSE;
908 }
909 pdcattr = dc->pdcattr;
910
911 if (Point)
912 {
913 NTSTATUS Status = STATUS_SUCCESS;
914
915 _SEH2_TRY
916 {
917 ProbeForWrite(Point, sizeof(POINT), 1);
918 Point->x = pdcattr->ptlViewportOrg.x;
919 Point->y = pdcattr->ptlViewportOrg.y;
920 }
921 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
922 {
923 Status = _SEH2_GetExceptionCode();
924 }
925 _SEH2_END;
926
927 if (!NT_SUCCESS(Status))
928 {
929 SetLastNtError(Status);
930 DC_UnlockDc(dc);
931 return FALSE;
932 }
933 }
934
935 pdcattr->ptlViewportOrg.x = X;
936 pdcattr->ptlViewportOrg.y = Y;
937 pdcattr->flXform |= PAGE_XLATE_CHANGED;
938
939 DC_UnlockDc(dc);
940
941 return TRUE;
942 }
943
944 BOOL
945 APIENTRY
946 NtGdiSetWindowOrgEx(
947 HDC hDC,
948 int X,
949 int Y,
950 LPPOINT Point)
951 {
952 PDC dc;
953 PDC_ATTR pdcattr;
954
955 dc = DC_LockDc(hDC);
956 if (!dc)
957 {
958 EngSetLastError(ERROR_INVALID_HANDLE);
959 return FALSE;
960 }
961 pdcattr = dc->pdcattr;
962
963 if (Point)
964 {
965 NTSTATUS Status = STATUS_SUCCESS;
966
967 _SEH2_TRY
968 {
969 ProbeForWrite(Point, sizeof(POINT), 1);
970 Point->x = pdcattr->ptlWindowOrg.x;
971 Point->y = pdcattr->ptlWindowOrg.y;
972 }
973 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
974 {
975 Status = _SEH2_GetExceptionCode();
976 }
977 _SEH2_END;
978
979 if (!NT_SUCCESS(Status))
980 {
981 SetLastNtError(Status);
982 DC_UnlockDc(dc);
983 return FALSE;
984 }
985 }
986
987 pdcattr->ptlWindowOrg.x = X;
988 pdcattr->ptlWindowOrg.y = Y;
989 pdcattr->flXform |= PAGE_XLATE_CHANGED;
990
991 DC_UnlockDc(dc);
992
993 return TRUE;
994 }
995
996 //
997 // Mirror Window function.
998 //
999 VOID
1000 FASTCALL
1001 IntMirrorWindowOrg(PDC dc)
1002 {
1003 PDC_ATTR pdcattr;
1004 LONG X, cx;
1005
1006 pdcattr = dc->pdcattr;
1007
1008 if (!(pdcattr->dwLayout & LAYOUT_RTL))
1009 {
1010 pdcattr->ptlWindowOrg.x = pdcattr->lWindowOrgx; // Flip it back.
1011 return;
1012 }
1013
1014 /* Copy the window extension, so no one can mess with it */
1015 cx = pdcattr->szlViewportExt.cx;
1016 if (cx == 0) return;
1017 //
1018 // WOrgx = wox - (Width - 1) * WExtx / VExtx
1019 //
1020 X = (dc->erclWindow.right - dc->erclWindow.left) - 1; // Get device width - 1
1021
1022 X = (X * pdcattr->szlWindowExt.cx) / cx;
1023
1024 pdcattr->ptlWindowOrg.x = pdcattr->lWindowOrgx - X; // Now set the inverted win origion.
1025 pdcattr->flXform |= PAGE_XLATE_CHANGED;
1026
1027 return;
1028 }
1029
1030 VOID
1031 NTAPI
1032 DC_vSetLayout(
1033 IN PDC pdc,
1034 IN LONG wox,
1035 IN DWORD dwLayout)
1036 {
1037 PDC_ATTR pdcattr = pdc->pdcattr;
1038
1039 pdcattr->dwLayout = dwLayout;
1040
1041 if (!(dwLayout & LAYOUT_ORIENTATIONMASK)) return;
1042
1043 if (dwLayout & LAYOUT_RTL)
1044 {
1045 pdcattr->iMapMode = MM_ANISOTROPIC;
1046 }
1047
1048 //pdcattr->szlWindowExt.cy = -pdcattr->szlWindowExt.cy;
1049 //pdcattr->ptlWindowOrg.x = -pdcattr->ptlWindowOrg.x;
1050
1051 //if (wox == -1)
1052 // IntMirrorWindowOrg(pdc);
1053 //else
1054 // pdcattr->ptlWindowOrg.x = wox - pdcattr->ptlWindowOrg.x;
1055
1056 if (!(pdcattr->flTextAlign & TA_CENTER)) pdcattr->flTextAlign |= TA_RIGHT;
1057
1058 if (pdc->dclevel.flPath & DCPATH_CLOCKWISE)
1059 pdc->dclevel.flPath &= ~DCPATH_CLOCKWISE;
1060 else
1061 pdc->dclevel.flPath |= DCPATH_CLOCKWISE;
1062
1063 pdcattr->flXform |= (PAGE_EXTENTS_CHANGED |
1064 INVALIDATE_ATTRIBUTES |
1065 DEVICE_TO_WORLD_INVALID);
1066 }
1067
1068 // NtGdiSetLayout
1069 //
1070 // The default is left to right. This function changes it to right to left, which
1071 // is the standard in Arabic and Hebrew cultures.
1072 //
1073 /*
1074 * @implemented
1075 */
1076 DWORD
1077 APIENTRY
1078 NtGdiSetLayout(
1079 IN HDC hdc,
1080 IN LONG wox,
1081 IN DWORD dwLayout)
1082 {
1083 PDC pdc;
1084 DWORD dwOldLayout;
1085
1086 pdc = DC_LockDc(hdc);
1087 if (!pdc)
1088 {
1089 EngSetLastError(ERROR_INVALID_HANDLE);
1090 return GDI_ERROR;
1091 }
1092
1093 dwOldLayout = pdc->pdcattr->dwLayout;
1094 DC_vSetLayout(pdc, wox, dwLayout);
1095
1096 DC_UnlockDc(pdc);
1097 return dwOldLayout;
1098 }
1099
1100 /*
1101 * @implemented
1102 */
1103 LONG
1104 APIENTRY
1105 NtGdiGetDeviceWidth(
1106 IN HDC hdc)
1107 {
1108 PDC dc;
1109 LONG Ret;
1110 dc = DC_LockDc(hdc);
1111 if (!dc)
1112 {
1113 EngSetLastError(ERROR_INVALID_HANDLE);
1114 return 0;
1115 }
1116 Ret = dc->erclWindow.right - dc->erclWindow.left;
1117 DC_UnlockDc(dc);
1118 return Ret;
1119 }
1120
1121 /*
1122 * @implemented
1123 */
1124 BOOL
1125 APIENTRY
1126 NtGdiMirrorWindowOrg(
1127 IN HDC hdc)
1128 {
1129 PDC dc;
1130 dc = DC_LockDc(hdc);
1131 if (!dc)
1132 {
1133 EngSetLastError(ERROR_INVALID_HANDLE);
1134 return FALSE;
1135 }
1136 IntMirrorWindowOrg(dc);
1137 DC_UnlockDc(dc);
1138 return TRUE;
1139 }
1140
1141 /*
1142 * @implemented
1143 */
1144 BOOL
1145 APIENTRY
1146 NtGdiSetSizeDevice(
1147 IN HDC hdc,
1148 IN INT cxVirtualDevice,
1149 IN INT cyVirtualDevice)
1150 {
1151 PDC dc;
1152 PDC_ATTR pdcattr;
1153
1154 if (!cxVirtualDevice || !cyVirtualDevice)
1155 {
1156 return FALSE;
1157 }
1158
1159 dc = DC_LockDc(hdc);
1160 if (!dc) return FALSE;
1161
1162 pdcattr = dc->pdcattr;
1163
1164 pdcattr->szlVirtualDeviceSize.cx = cxVirtualDevice;
1165 pdcattr->szlVirtualDeviceSize.cy = cyVirtualDevice;
1166
1167 DC_UnlockDc(dc);
1168
1169 return TRUE;
1170 }
1171
1172 /*
1173 * @implemented
1174 */
1175 BOOL
1176 APIENTRY
1177 NtGdiSetVirtualResolution(
1178 IN HDC hdc,
1179 IN INT cxVirtualDevicePixel,
1180 IN INT cyVirtualDevicePixel,
1181 IN INT cxVirtualDeviceMm,
1182 IN INT cyVirtualDeviceMm)
1183 {
1184 PDC dc;
1185 PDC_ATTR pdcattr;
1186
1187 /* Check parameters (all zeroes resets to real resolution) */
1188 if (cxVirtualDevicePixel == 0 && cyVirtualDevicePixel == 0 &&
1189 cxVirtualDeviceMm == 0 && cyVirtualDeviceMm == 0)
1190 {
1191 cxVirtualDevicePixel = NtGdiGetDeviceCaps(hdc, HORZRES);
1192 cyVirtualDevicePixel = NtGdiGetDeviceCaps(hdc, VERTRES);
1193 cxVirtualDeviceMm = NtGdiGetDeviceCaps(hdc, HORZSIZE);
1194 cyVirtualDeviceMm = NtGdiGetDeviceCaps(hdc, VERTSIZE);
1195 }
1196 else if (cxVirtualDevicePixel == 0 || cyVirtualDevicePixel == 0 ||
1197 cxVirtualDeviceMm == 0 || cyVirtualDeviceMm == 0)
1198 {
1199 return FALSE;
1200 }
1201
1202 dc = DC_LockDc(hdc);
1203 if (!dc) return FALSE;
1204
1205 pdcattr = dc->pdcattr;
1206
1207 pdcattr->szlVirtualDevicePixel.cx = cxVirtualDevicePixel;
1208 pdcattr->szlVirtualDevicePixel.cy = cyVirtualDevicePixel;
1209 pdcattr->szlVirtualDeviceMm.cx = cxVirtualDeviceMm;
1210 pdcattr->szlVirtualDeviceMm.cy = cyVirtualDeviceMm;
1211
1212 // DC_vUpdateXforms(dc);
1213 DC_UnlockDc(dc);
1214 return TRUE;
1215 }
1216
1217 static
1218 VOID FASTCALL
1219 DC_vGetAspectRatioFilter(PDC pDC, LPSIZE AspectRatio)
1220 {
1221 if (pDC->pdcattr->flFontMapper & 1) // TRUE assume 1.
1222 {
1223 // "This specifies that Windows should only match fonts that have the
1224 // same aspect ratio as the display.", Programming Windows, Fifth Ed.
1225 AspectRatio->cx = pDC->ppdev->gdiinfo.ulLogPixelsX;
1226 AspectRatio->cy = pDC->ppdev->gdiinfo.ulLogPixelsY;
1227 }
1228 else
1229 {
1230 AspectRatio->cx = 0;
1231 AspectRatio->cy = 0;
1232 }
1233 }
1234
1235 BOOL APIENTRY
1236 NtGdiGetDCPoint(
1237 HDC hDC,
1238 UINT iPoint,
1239 PPOINTL Point)
1240 {
1241 BOOL Ret = TRUE;
1242 DC *pdc;
1243 POINTL SafePoint;
1244 SIZE Size;
1245 PSIZEL pszlViewportExt;
1246
1247 if (!Point)
1248 {
1249 EngSetLastError(ERROR_INVALID_PARAMETER);
1250 return FALSE;
1251 }
1252
1253 pdc = DC_LockDc(hDC);
1254 if (!pdc)
1255 {
1256 EngSetLastError(ERROR_INVALID_HANDLE);
1257 return FALSE;
1258 }
1259
1260 switch (iPoint)
1261 {
1262 case GdiGetViewPortExt:
1263 pszlViewportExt = DC_pszlViewportExt(pdc);
1264 SafePoint.x = pszlViewportExt->cx;
1265 SafePoint.y = pszlViewportExt->cy;
1266 break;
1267
1268 case GdiGetWindowExt:
1269 SafePoint.x = pdc->pdcattr->szlWindowExt.cx;
1270 SafePoint.y = pdc->pdcattr->szlWindowExt.cy;
1271 break;
1272
1273 case GdiGetViewPortOrg:
1274 SafePoint = pdc->pdcattr->ptlViewportOrg;
1275 break;
1276
1277 case GdiGetWindowOrg:
1278 SafePoint = pdc->pdcattr->ptlWindowOrg;
1279 break;
1280
1281 case GdiGetDCOrg:
1282 SafePoint = pdc->ptlDCOrig;
1283 break;
1284
1285 case GdiGetAspectRatioFilter:
1286 DC_vGetAspectRatioFilter(pdc, &Size);
1287 SafePoint.x = Size.cx;
1288 SafePoint.y = Size.cy;
1289 break;
1290
1291 default:
1292 EngSetLastError(ERROR_INVALID_PARAMETER);
1293 Ret = FALSE;
1294 break;
1295 }
1296
1297 if (Ret)
1298 {
1299 _SEH2_TRY
1300 {
1301 ProbeForWrite(Point, sizeof(POINT), 1);
1302 *Point = SafePoint;
1303 }
1304 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1305 {
1306 Ret = FALSE;
1307 }
1308 _SEH2_END;
1309 }
1310
1311 DC_UnlockDc(pdc);
1312 return Ret;
1313 }
1314
1315 /* EOF */