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