3 Copyright (c) 2003 Microsoft Corporation
7 Helper functions for HIDPI/Landscape support.
11 #include "ms_ui_helper.h"
15 BOOL
HIDPI_StretchBitmap(
26 HDC hdcSrc
, hdcDst
, hdcScreen
;
27 HBITMAP hbmOldSrc
, hbmOldDst
;
28 int cxSrcImg
, cySrcImg
;
29 int i
, j
, xDest
, yDest
, xBmp
, yBmp
;
31 if (!phbm
|| !*phbm
|| (cxDstImg
== 0 && cyDstImg
== 0) || (cImagesX
== 0 || cImagesY
== 0))
34 if ((sizeof(bm
) != GetObject(*phbm
, sizeof(bm
), &bm
)))
37 // If you hit this ASSERT, that mean your passed in image count in row and
38 // the column number of images is not correct.
39 // ASSERT(((bm.bmWidth % cImagesX) == 0) && ((bm.bmHeight % cImagesY) == 0));
41 cxSrcImg
= bm
.bmWidth
/ cImagesX
;
42 cySrcImg
= bm
.bmHeight
/ cImagesY
;
44 if (cxSrcImg
== cxDstImg
&& cySrcImg
== cyDstImg
)
51 cxDstImg
= HIDPIMulDiv(cyDstImg
, cxSrcImg
, cySrcImg
);
52 else if (cyDstImg
== 0)
53 cyDstImg
= HIDPIMulDiv(cxDstImg
, cySrcImg
, cxSrcImg
);
55 hdcSrc
= CreateCompatibleDC(NULL
);
56 hdcDst
= CreateCompatibleDC(NULL
);
57 hdcScreen
= GetDC(NULL
);
58 hbmOldSrc
= (HBITMAP
)SelectObject(hdcSrc
, *phbm
);
59 hbmNew
= CreateCompatibleBitmap(hdcScreen
, cxDstImg
* cImagesX
, cyDstImg
* cImagesY
);
60 hbmOldDst
= (HBITMAP
)SelectObject(hdcDst
, hbmNew
);
61 ReleaseDC(NULL
, hdcScreen
);
64 for (j
= 0, yDest
= 0, yBmp
= 0; j
< cImagesY
; j
++, yDest
+= cyDstImg
, yBmp
+= cySrcImg
)
66 for (i
= 0, xDest
= 0, xBmp
= 0; i
< cImagesX
; i
++, xDest
+= cxDstImg
, xBmp
+= cxSrcImg
)
68 StretchBlt(hdcDst
, xDest
, yDest
, cxDstImg
, cyDstImg
,
69 hdcSrc
, xBmp
, yBmp
, cxSrcImg
, cySrcImg
,
74 // Free allocated memory
75 SelectObject(hdcSrc
, hbmOldSrc
);
76 SelectObject(hdcDst
, hbmOldDst
);
80 // Delete the passed in bitmap
90 static BOOL
HIDPI_StretchIcon_Internal(
100 HBITMAP hbmImage
, hbmMask
;
106 hdc
= CreateCompatibleDC(NULL
);
108 hbmMask
= CreateCompatibleBitmap(hdc
, cxIcon
, cyIcon
);
109 hbmOld
= (HBITMAP
)SelectObject(hdc
, hbmMask
);
110 fDrawMaskOK
= DrawIconEx(hdc
, 0, 0, hiconIn
, cxIcon
, cyIcon
, 0, NULL
, DI_MASK
);
111 SelectObject(hdc
, hbmOld
);
113 hbmImage
= CreateBitmap(cxIcon
, cyIcon
, 1, GetDeviceCaps(hdc
, BITSPIXEL
), NULL
);
114 hbmOld
= (HBITMAP
)SelectObject(hdc
, hbmImage
);
115 fDrawImageOK
= DrawIconEx(hdc
, 0, 0, hiconIn
, cxIcon
, cyIcon
, 0, NULL
, DI_IMAGE
);
116 SelectObject(hdc
, hbmOld
);
118 if (fDrawImageOK
&& fDrawMaskOK
)
120 iconinfo
.fIcon
= TRUE
;
121 iconinfo
.hbmColor
= hbmImage
;
122 iconinfo
.hbmMask
= hbmMask
;
123 *phiconOut
= CreateIconIndirect(&iconinfo
);
126 DeleteObject(hbmImage
);
127 DeleteObject(hbmMask
);
131 return (fDrawImageOK
&& fDrawMaskOK
&& *phiconOut
!= NULL
) ? TRUE
: FALSE
;
134 BOOL
HIDPI_StretchIcon(
142 if (HIDPI_StretchIcon_Internal(*phic
, &hiconOut
, cxIcon
, cyIcon
))
153 BOOL
HIDPI_GetBitmapLogPixels(
162 HGLOBAL hResourceBitmap
= NULL
;
163 BITMAPINFO
* pBitmapInfo
;
164 int PelsPerMeterX
, PelsPerMeterY
;
169 hResource
= FindResource(hinst
, lpbmp
, RT_BITMAP
);
174 hResourceBitmap
= LoadResource(hinst
, hResource
);
175 if (!hResourceBitmap
)
179 pBitmapInfo
= (BITMAPINFO
*)LockResource(hResourceBitmap
);
185 // There are at least three kind value of PslsPerMeter used for 96 DPI bitmap:
186 // 0 - the bitmap just simply doesn't set this value
189 // So any value of PslsPerMeter under 3780 should be treated as 96 DPI bitmap.
190 PelsPerMeterX
= (pBitmapInfo
->bmiHeader
.biXPelsPerMeter
< 3780) ? 3780 : pBitmapInfo
->bmiHeader
.biXPelsPerMeter
;
191 PelsPerMeterY
= (pBitmapInfo
->bmiHeader
.biYPelsPerMeter
< 3780) ? 3780 : pBitmapInfo
->bmiHeader
.biYPelsPerMeter
;
193 // The formula for converting PelsPerMeter to LogPixels(DPI) is:
194 // LogPixels = PelsPerMeter / 39.37
195 // ( PelsPerMeter : Pixels per meter )
196 // ( LogPixels : Pixels per inch )
197 // Note: We need to round up.
198 *pnLogPixelsX
= (int)((PelsPerMeterX
* 100 + 1968) / 3937);
199 *pnLogPixelsY
= (int)((PelsPerMeterY
* 100 + 1968) / 3937);
208 HIMAGELIST
HIDPI_ImageList_LoadImage(
218 HBITMAP hbmImage
= NULL
;
219 HIMAGELIST piml
= NULL
;
221 int cImages
, cxImage
, cy
;
222 int BmpLogPixelsX
, BmpLogPixelsY
;
225 if ((uType
!= IMAGE_BITMAP
) || // Image type is not IMAGE_BITMAP
226 (cx
== 0)) // Caller doesn't care about the dimensions of the image - assumes the ones in the file
228 piml
= ImageList_LoadImage(hinst
, lpbmp
, cx
, cGrow
, crMask
, uType
, uFlags
);
232 if (!HIDPI_GetBitmapLogPixels(hinst
, lpbmp
, &BmpLogPixelsX
, &BmpLogPixelsY
))
237 hbmImage
= (HBITMAP
)LoadImage(hinst
, lpbmp
, uType
, 0, 0, uFlags
);
238 if (!hbmImage
|| (sizeof(bm
) != GetObject(hbmImage
, sizeof(bm
), &bm
)))
243 // do we need to scale this image?
244 if (BmpLogPixelsX
== g_HIDPI_LogPixelsX
)
246 piml
= ImageList_LoadImage(hinst
, lpbmp
, cx
, cGrow
, crMask
, uType
, uFlags
);
250 cxImage
= HIDPIMulDiv(cx
, BmpLogPixelsX
, g_HIDPI_LogPixelsX
);
252 // Bitmap width should be multiple integral of image width.
253 // If not, that means either your bitmap is wrong or passed in cx is wrong.
254 // ASSERT((bm.bmWidth % cxImage) == 0);
256 cImages
= bm
.bmWidth
/ cxImage
;
258 cy
= HIDPIMulDiv(bm
.bmHeight
, g_HIDPI_LogPixelsY
, BmpLogPixelsY
);
260 if ((g_HIDPI_LogPixelsX
% BmpLogPixelsX
) == 0)
262 HIDPI_StretchBitmap(&hbmImage
, cx
* cImages
, cy
, 1, 1);
266 // Here means the DPI is not integral multiple of standard DPI (96DPI).
267 // So if we stretch entire bitmap together, we are not sure each indivisual
268 // image will be stretch to right place. It is controled by StretchBlt().
269 // (for example, a 16 pixel icon, the first one might be stretch to 22 pixels
270 // and next one might be stretched to 20 pixels)
271 // What we have to do here is stretching indivisual image separately to make sure
272 // every one is stretched properly.
273 HIDPI_StretchBitmap(&hbmImage
, cx
, cy
, cImages
, 1);
277 // ILC_MASK is important for supporting CLR_DEFAULT
278 if (crMask
!= CLR_NONE
)
282 // ILC_COLORMASK bits are important if we ever want to Merge ImageLists
285 flags
|= (bm
.bmBitsPixel
& ILC_COLORMASK
);
288 // bitmap MUST be de-selected from the DC
289 // create the image list of the size asked for.
290 piml
= ImageList_Create(cx
, cy
, flags
, cImages
, cGrow
);
296 if (crMask
== CLR_NONE
)
298 added
= ImageList_Add(piml
, hbmImage
, NULL
);
302 added
= ImageList_AddMasked(piml
, hbmImage
, crMask
);
307 ImageList_Destroy(piml
);
313 DeleteObject(hbmImage
);
317 int HIDPI_ImageList_ReplaceIcon(HIMAGELIST himl
, int i
, HICON hicon
)
321 HICON hiconStretched
;
323 ImageList_GetIconSize(himl
, &cxIcon
, &cyIcon
);
324 HIDPI_StretchIcon_Internal(hicon
, &hiconStretched
, cxIcon
, cyIcon
);
325 if (hiconStretched
!= NULL
)
327 iRet
= ImageList_ReplaceIcon(himl
, i
, hiconStretched
);
328 DestroyIcon(hiconStretched
);
332 iRet
= ImageList_ReplaceIcon(himl
, i
, hicon
);
338 BOOL
HIDPI_RectangleInternal(HDC hdc
, int nLeft
, int nTop
, int nRight
, int nBottom
, int nThickness
)
340 int nOff
= nThickness
/2;
347 return Rectangle(hdc
, nLeft
, nTop
, nRight
, nBottom
);
350 #define BORDERX_PEN 32
352 BOOL
HIDPI_BorderRectangle(HDC hdc
, int nLeft
, int nTop
, int nRight
, int nBottom
)
357 hpenOld
= (HPEN
)SelectObject(hdc
, (HPEN
) GetStockObject(BORDERX_PEN
));
358 bRet
= HIDPI_RectangleInternal(hdc
, nLeft
, nTop
, nRight
, nBottom
, GetSystemMetrics(SM_CXBORDER
));
359 SelectObject(hdc
, hpenOld
);
364 BOOL
HIDPI_Rectangle(HDC hdc
, int nLeft
, int nTop
, int nRight
, int nBottom
)
369 // Obtain current pen thickness
370 hpenSel
= (HPEN
)GetCurrentObject(hdc
, OBJ_PEN
);
371 GetObject(hpenSel
, sizeof(lpenSel
), &lpenSel
);
373 return HIDPI_RectangleInternal(hdc
, nLeft
, nTop
, nRight
, nBottom
, lpenSel
.lopnWidth
.x
);
377 BOOL
HIDPI_PolylineInternal(HDC hdc
, const POINT
*lppt
, int cPoints
, int nStyle
, int nThickness
)
380 int nHOff
= 0, nVOff
= 0;
384 if (! (nStyle
& PS_BIAS_MASK
))
386 // No drawing bias. Draw normally
387 return Polyline(hdc
, lppt
, cPoints
);
390 // Make sure caller didn't try to get both a left and a right bias or both a down and an up bias
391 // ASSERT(!(nStyle & PS_LEFTBIAS) || !(nStyle & PS_RIGHTBIAS));
392 // ASSERT(!(nStyle & PS_UPBIAS) || !(nStyle & PS_DOWNBIAS));
394 if (nStyle
& PS_LEFTBIAS
)
396 nHOff
= -((nThickness
-1)/2);
399 if (nStyle
& PS_RIGHTBIAS
)
401 nHOff
= nThickness
/2;
404 if (nStyle
& PS_UPBIAS
)
406 nVOff
= -((nThickness
-1)/2);
409 if (nStyle
& PS_DOWNBIAS
)
411 nVOff
= nThickness
/2;
414 for (i
= 1; i
< cPoints
; i
++)
416 // Use the two points that specify current line segment
417 memcpy(pts
, &lppt
[i
-1], 2*sizeof(POINT
));
418 if (abs(lppt
[i
].x
- lppt
[i
-1].x
) <= abs(lppt
[i
].y
- lppt
[i
-1].y
))
420 // Shift current line segment horizontally if abs(slope) >= 1
426 // Shift current line segment vertically if abs(slope) < 1
430 bRet
= bRet
&& Polyline(hdc
, pts
, 2);
441 BOOL
HIDPI_BorderPolyline(HDC hdc
, const POINT
*lppt
, int cPoints
, int nStyle
)
446 hpenOld
= (HPEN
)SelectObject(hdc
, (HPEN
) GetStockObject(BORDERX_PEN
));
447 bRet
= HIDPI_PolylineInternal(hdc
, lppt
, cPoints
, nStyle
, GetSystemMetrics(SM_CXBORDER
));
448 SelectObject(hdc
, hpenOld
);
453 BOOL
HIDPI_Polyline(HDC hdc
, const POINT
*lppt
, int cPoints
, int nStyle
)
458 // Obtain current pen thickness
459 hpenSel
= (HPEN
)GetCurrentObject(hdc
, OBJ_PEN
);
460 GetObject(hpenSel
, sizeof(lpenSel
), &lpenSel
);
462 return HIDPI_PolylineInternal(hdc
, lppt
, cPoints
, nStyle
, lpenSel
.lopnWidth
.x
);
466 // Called by RelayoutDialog to advance to the next item in the dialog template.
468 static LPBYTE
WalkDialogData(LPBYTE lpData
)
470 LPWORD lpWord
= (LPWORD
)lpData
;
471 if (*lpWord
== 0xFFFF)
473 return (LPBYTE
)(lpWord
+ 2);
475 while (*lpWord
!= 0x0000)
479 return (LPBYTE
)(lpWord
+ 1);
483 // Post-processing step for each dialog item.
484 // Static controls and buttons: change text and bitmaps.
485 // Listboxes and combo boxes: ensures that the selected item is visible.
487 static void FixupDialogItem(
490 LPDLGITEMTEMPLATE lpDlgItem
,
494 if (lpClass
[0] == 0xFFFF)
498 case 0x0080: // button
499 case 0x0082: // static
501 if (lpData
[0] == 0xFFFF)
503 if (lpDlgItem
->style
& SS_ICON
)
505 HICON hOld
= (HICON
)SendDlgItemMessageW(hDlg
, lpDlgItem
->id
, STM_GETIMAGE
, IMAGE_ICON
, 0);
506 HICON hNew
= LoadIcon(hInst
, MAKEINTRESOURCE(lpData
[1]));
507 SendDlgItemMessageW(hDlg
, lpDlgItem
->id
, STM_SETIMAGE
, IMAGE_ICON
, (LPARAM
)hNew
);
510 else if (lpDlgItem
->style
& SS_BITMAP
)
512 HBITMAP hOld
= (HBITMAP
)SendDlgItemMessageW(hDlg
, lpDlgItem
->id
, STM_GETIMAGE
, IMAGE_BITMAP
, 0);
513 HBITMAP hNew
= LoadBitmap(hInst
, MAKEINTRESOURCE(lpData
[1]));
514 SendDlgItemMessageW(hDlg
, lpDlgItem
->id
, STM_SETIMAGE
, IMAGE_BITMAP
, (LPARAM
)hNew
);
518 else // lpData[0] is not 0xFFFF (it's text).
520 SetDlgItemTextW(hDlg
, lpDlgItem
->id
, (LPCTSTR
)lpData
);
525 case 0x0083: // list box
527 INT nSel
= SendDlgItemMessageW(hDlg
, lpDlgItem
->id
, LB_GETCURSEL
, 0, 0);
530 SendDlgItemMessageW(hDlg
, lpDlgItem
->id
, LB_SETCURSEL
, nSel
, 0);
535 case 0x0085: // combo box
537 INT nSel
= SendDlgItemMessageW(hDlg
, lpDlgItem
->id
, CB_GETCURSEL
, 0, 0);
540 SendDlgItemMessageW(hDlg
, lpDlgItem
->id
, CB_SETCURSEL
, nSel
, 0);
548 BOOL
RelayoutDialog(HINSTANCE hInst
, HWND hDlg
, LPCWSTR iddTemplate
)
550 HRSRC hRsrc
= FindResource((HMODULE
)hInst
, iddTemplate
, RT_DIALOG
);
554 LPDLGTEMPLATE lpTemplate
;
557 LPDLGITEMTEMPLATE lpDlgItem
;
567 hGlobal
= LoadResource((HMODULE
)hInst
, hRsrc
);
573 lpData
= (LPBYTE
)LockResource(hGlobal
);
574 lpTemplate
= (LPDLGTEMPLATE
)lpData
;
575 hDWP
= BeginDeferWindowPos(lpTemplate
->cdit
);
578 // For more information about the data structures that we are walking,
579 // consult the DLGTEMPLATE and DLGITEMTEMPLATE documentation on MSDN.
581 lpData
+= sizeof(DLGTEMPLATE
);
582 lpData
= WalkDialogData(lpData
); // menu
583 lpData
= WalkDialogData(lpData
); // class
584 lpData
= WalkDialogData(lpData
); // title
586 if (lpTemplate
->style
& DS_SETFONT
)
588 lpData
+= sizeof(WORD
); // font size.
589 lpData
= WalkDialogData(lpData
); // font face.
592 for (i
= 0; i
< lpTemplate
->cdit
; i
++)
594 lpData
= (LPBYTE
) (((INT
)lpData
+ 3) & ~3); // force to DWORD boundary.
595 lpDlgItem
= (LPDLGITEMTEMPLATE
)lpData
;
596 hwndCtl
= GetDlgItem(hDlg
, lpDlgItem
->id
);
598 if (lpDlgItem
->id
== 0xFFFF)
604 // Move the item around.
608 r
.left
= lpDlgItem
->x
;
609 r
.top
= lpDlgItem
->y
;
610 r
.right
= lpDlgItem
->x
+ lpDlgItem
->cx
;
611 r
.bottom
= lpDlgItem
->y
+ lpDlgItem
->cy
;
612 MapDialogRect(hDlg
, &r
);
613 DeferWindowPos(hDWP
, hwndCtl
, NULL
,
614 r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
, SWP_NOZORDER
);
617 lpData
+= sizeof(DLGITEMTEMPLATE
);
618 lpClass
= (LPWORD
)lpData
;
619 lpData
= WalkDialogData(lpData
); // class
622 // Do some special handling for each dialog item (changing text,
623 // bitmaps, ensuring visible, etc.
625 FixupDialogItem(hInst
, hDlg
, lpDlgItem
, lpClass
, (LPWORD
)lpData
);
627 lpData
= WalkDialogData(lpData
); // title
628 cbExtra
= *((LPWORD
)lpData
); // extra class data.
629 lpData
+= (cbExtra
? cbExtra
: sizeof(WORD
));
632 EndDeferWindowPos(hDWP
);
633 return nStatics
< 2 ? TRUE
: FALSE
;