1 // TortoiseIDiff - an image diff viewer in TortoiseSVN
3 // Copyright (C) 2006-2009 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "PicWindow.h"
25 #pragma comment(lib, "Msimg32.lib")
26 #pragma comment(lib, "shell32.lib")
28 bool CPicWindow::RegisterAndCreateWindow(HWND hParent
)
32 // Fill in the window class structure with default parameters
33 wcx
.cbSize
= sizeof(WNDCLASSEX
);
34 wcx
.style
= CS_HREDRAW
| CS_VREDRAW
;
35 wcx
.lpfnWndProc
= CWindow::stWinMsgHandler
;
38 wcx
.hInstance
= hResource
;
39 wcx
.hCursor
= LoadCursor(NULL
, IDC_ARROW
);
40 wcx
.lpszClassName
= _T("TortoiseIDiffPicWindow");
41 wcx
.hIcon
= LoadIcon(hResource
, MAKEINTRESOURCE(IDI_TORTOISEIDIFF
));
42 wcx
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+1);
43 wcx
.lpszMenuName
= MAKEINTRESOURCE(IDC_TORTOISEIDIFF
);
44 wcx
.hIconSm
= LoadIcon(wcx
.hInstance
, MAKEINTRESOURCE(IDI_TORTOISEIDIFF
));
46 if (CreateEx(WS_EX_ACCEPTFILES
| WS_EX_CLIENTEDGE
, WS_CHILD
| WS_HSCROLL
| WS_VSCROLL
| WS_VISIBLE
, hParent
))
48 ShowWindow(m_hwnd
, SW_SHOW
);
56 void CPicWindow::PositionTrackBar()
60 HWND slider
= m_AlphaSlider
.GetWindow();
61 if ((pSecondPic
)&&(m_blend
== BLEND_ALPHA
))
63 MoveWindow(slider
, 0, rc
.top
-4+SLIDER_WIDTH
, SLIDER_WIDTH
, rc
.bottom
-rc
.top
-SLIDER_WIDTH
+8, true);
64 ShowWindow(slider
, SW_SHOW
);
65 MoveWindow(hwndAlphaToggleBtn
, 0, rc
.top
-4, SLIDER_WIDTH
, SLIDER_WIDTH
, true);
66 ShowWindow(hwndAlphaToggleBtn
, SW_SHOW
);
70 ShowWindow(slider
, SW_HIDE
);
71 ShowWindow(hwndAlphaToggleBtn
, SW_HIDE
);
75 LRESULT CALLBACK
CPicWindow::WinMsgHandler(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
82 // create a slider control
84 ShowWindow(m_AlphaSlider
.GetWindow(), SW_HIDE
);
87 RECT rect
; // for client area coordinates
89 hwndTT
= CreateWindowEx(WS_EX_TOPMOST
,
92 WS_POPUP
| TTS_NOPREFIX
| TTS_ALWAYSTIP
,
109 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOACTIVATE
);
111 ::GetClientRect(hwnd
, &rect
);
113 ti
.cbSize
= sizeof(TOOLINFO
);
114 ti
.uFlags
= TTF_TRACK
| TTF_ABSOLUTE
;
116 ti
.hinst
= hResource
;
118 ti
.lpszText
= LPSTR_TEXTCALLBACK
;
119 // ToolTip control will cover the whole window
120 ti
.rect
.left
= rect
.left
;
121 ti
.rect
.top
= rect
.top
;
122 ti
.rect
.right
= rect
.right
;
123 ti
.rect
.bottom
= rect
.bottom
;
125 SendMessage(hwndTT
, TTM_ADDTOOL
, 0, (LPARAM
) (LPTOOLINFO
) &ti
);
126 SendMessage(hwndTT
, TTM_SETMAXTIPWIDTH
, 0, 600);
127 nHSecondScrollPos
= 0;
128 nVSecondScrollPos
= 0;
133 InvalidateRect(*this, NULL
, FALSE
);
146 if ((pSecondPic
)&&((HWND
)lParam
== m_AlphaSlider
.GetWindow()))
148 if (LOWORD(wParam
) == TB_THUMBTRACK
)
150 // while tracking, only redraw after 50 milliseconds
151 ::SetTimer(*this, TIMER_ALPHASLIDER
, 50, NULL
);
154 SetBlendAlpha(m_blend
, SendMessage(m_AlphaSlider
.GetWindow(), TBM_GETPOS
, 0, 0) / 16.0f
);
158 UINT nPos
= HIWORD(wParam
);
159 bool bForceUpdate
= false;
160 if (LOWORD(wParam
) == SB_THUMBTRACK
|| LOWORD(wParam
) == SB_THUMBPOSITION
)
162 // Get true 32-bit scroll position
164 si
.cbSize
= sizeof(SCROLLINFO
);
165 si
.fMask
= SIF_TRACKPOS
;
166 GetScrollInfo(*this, SB_VERT
, &si
);
171 OnVScroll(LOWORD(wParam
), nPos
);
172 if (bLinkedPositions
)
174 pTheOtherPic
->OnVScroll(LOWORD(wParam
), nPos
);
176 ::UpdateWindow(*pTheOtherPic
);
182 UINT nPos
= HIWORD(wParam
);
183 bool bForceUpdate
= false;
184 if (LOWORD(wParam
) == SB_THUMBTRACK
|| LOWORD(wParam
) == SB_THUMBPOSITION
)
186 // Get true 32-bit scroll position
188 si
.cbSize
= sizeof(SCROLLINFO
);
189 si
.fMask
= SIF_TRACKPOS
;
190 GetScrollInfo(*this, SB_VERT
, &si
);
195 OnHScroll(LOWORD(wParam
), nPos
);
196 if (bLinkedPositions
)
198 pTheOtherPic
->OnHScroll(LOWORD(wParam
), nPos
);
200 ::UpdateWindow(*pTheOtherPic
);
206 OnMouseWheel(GET_KEYSTATE_WPARAM(wParam
), GET_WHEEL_DELTA_WPARAM(wParam
));
208 pTheOtherPic
->OnMouseWheel(GET_KEYSTATE_WPARAM(wParam
), GET_WHEEL_DELTA_WPARAM(wParam
));
213 OnMouseWheel(GET_KEYSTATE_WPARAM(wParam
)|MK_SHIFT
, GET_WHEEL_DELTA_WPARAM(wParam
));
215 pTheOtherPic
->OnMouseWheel(GET_KEYSTATE_WPARAM(wParam
)|MK_SHIFT
, GET_WHEEL_DELTA_WPARAM(wParam
));
220 ptPanStart
.x
= GET_X_LPARAM(lParam
);
221 ptPanStart
.y
= GET_Y_LPARAM(lParam
);
222 startVScrollPos
= nVScrollPos
;
223 startHScrollPos
= nHScrollPos
;
224 startVSecondScrollPos
= nVSecondScrollPos
;
225 startHSecondScrollPos
= nHSecondScrollPos
;
234 SendMessage(hwndTT
, TTM_TRACKACTIVATE
, FALSE
, 0);
238 mevt
.cbSize
= sizeof(TRACKMOUSEEVENT
);
239 mevt
.dwFlags
= TME_LEAVE
;
240 mevt
.dwHoverTime
= HOVER_DEFAULT
;
241 mevt
.hwndTrack
= *this;
242 ::TrackMouseEvent(&mevt
);
243 POINT pt
= {((int)(short)LOWORD(lParam
)), ((int)(short)HIWORD(lParam
))};
244 if (pt
.y
< HEADER_HEIGHT
)
246 ClientToScreen(*this, &pt
);
247 if ((abs(m_lastTTPos
.x
- pt
.x
) > 20)||(abs(m_lastTTPos
.y
- pt
.y
) > 10))
252 SendMessage(hwndTT
, TTM_TRACKPOSITION
, 0, MAKELONG(pt
.x
, pt
.y
));
254 ti
.cbSize
= sizeof(TOOLINFO
);
257 SendMessage(hwndTT
, TTM_TRACKACTIVATE
, TRUE
, (LPARAM
)&ti
);
262 SendMessage(hwndTT
, TTM_TRACKACTIVATE
, FALSE
, 0);
266 if (wParam
& MK_LBUTTON
)
269 int xPos
= GET_X_LPARAM(lParam
);
270 int yPos
= GET_Y_LPARAM(lParam
);
271 if (wParam
& MK_CONTROL
)
273 nHSecondScrollPos
= startHSecondScrollPos
+ (ptPanStart
.x
- xPos
);
274 nVSecondScrollPos
= startVSecondScrollPos
+ (ptPanStart
.y
- yPos
);
278 nHScrollPos
= startHScrollPos
+ (ptPanStart
.x
- xPos
);
279 nVScrollPos
= startVScrollPos
+ (ptPanStart
.y
- yPos
);
282 InvalidateRect(*this, NULL
, TRUE
);
284 if (bLinkedPositions
)
286 pTheOtherPic
->nHScrollPos
= nHScrollPos
;
287 pTheOtherPic
->nVScrollPos
= nVScrollPos
;
288 pTheOtherPic
->SetupScrollBars();
289 InvalidateRect(*pTheOtherPic
, NULL
, TRUE
);
290 UpdateWindow(*pTheOtherPic
);
297 // we show a hand cursor if the image can be dragged,
298 // and a hand-down cursor if the image is currently dragged
299 if ((*this == (HWND
)wParam
)&&(LOWORD(lParam
)==HTCLIENT
))
302 GetClientRect(&rect
);
303 LONG width
= picture
.m_Width
;
304 LONG height
= picture
.m_Height
;
307 width
= max(width
, pSecondPic
->m_Width
);
308 height
= max(height
, pSecondPic
->m_Height
);
311 if ((GetKeyState(VK_LBUTTON
)&0x8000)||(HIWORD(lParam
) == WM_LBUTTONDOWN
))
313 SetCursor(curHandDown
);
321 return DefWindowProc(hwnd
, uMsg
, wParam
, lParam
);
326 HDROP hDrop
= (HDROP
)wParam
;
327 TCHAR szFileName
[MAX_PATH
];
328 // we only use the first file dropped (if multiple files are dropped)
329 DragQueryFile(hDrop
, 0, szFileName
, sizeof(szFileName
)/sizeof(TCHAR
));
330 SetPic(szFileName
, _T(""), bMainPic
);
332 InvalidateRect(*this, NULL
, TRUE
);
337 switch (LOWORD(wParam
))
342 if (bLinkedPositions
)
343 pTheOtherPic
->PrevImage();
350 if (bLinkedPositions
)
351 pTheOtherPic
->NextImage();
357 bPlaying
= !bPlaying
;
359 if (bLinkedPositions
)
360 pTheOtherPic
->Animate(bPlaying
);
364 case ALPHATOGGLEBUTTON_ID
:
366 WORD msg
= HIWORD(wParam
);
369 case BN_DOUBLECLICKED
:
371 SendMessage(hwndAlphaToggleBtn
, BM_SETSTATE
, 1, 0);
372 SetTimer(*this, ID_ALPHATOGGLETIMER
, 1000, NULL
);
376 KillTimer(*this, ID_ALPHATOGGLETIMER
);
385 m_blend
= BLEND_ALPHA
;
387 InvalidateRect(*this, NULL
, TRUE
);
394 InvalidateRect(*this, NULL
, TRUE
);
404 case ID_ANIMATIONTIMER
:
407 if (nCurrentFrame
> picture
.GetNumberOfFrames(0))
409 long delay
= picture
.SetActiveFrame(nCurrentFrame
);
410 delay
= max(100, delay
);
411 SetTimer(*this, ID_ANIMATIONTIMER
, delay
, NULL
);
412 InvalidateRect(*this, NULL
, FALSE
);
415 case TIMER_ALPHASLIDER
:
417 SetBlendAlpha(m_blend
, SendMessage(m_AlphaSlider
.GetWindow(), TBM_GETPOS
, 0, 0)/16.0f
);
418 KillTimer(*this, TIMER_ALPHASLIDER
);
421 case ID_ALPHATOGGLETIMER
:
431 LPNMHDR pNMHDR
= (LPNMHDR
)lParam
;
432 if (pNMHDR
->code
== TTN_GETDISPINFO
)
434 if ((HWND
)wParam
== m_AlphaSlider
.GetWindow())
438 lpttt
= (LPTOOLTIPTEXT
) lParam
;
439 lpttt
->hinst
= hResource
;
440 TCHAR stringbuf
[MAX_PATH
] = {0};
441 _stprintf_s(stringbuf
, MAX_PATH
, _T("%i%% alpha"), (int)(SendMessage(m_AlphaSlider
.GetWindow(),TBM_GETPOS
,0,0)/16.0f
*100.0f
));
442 lpttt
->lpszText
= stringbuf
;
446 NMTTDISPINFOA
* pTTTA
= (NMTTDISPINFOA
*)pNMHDR
;
447 NMTTDISPINFOW
* pTTTW
= (NMTTDISPINFOW
*)pNMHDR
;
448 BuildInfoString(m_wszTip
, sizeof(m_wszTip
)/sizeof(TCHAR
), true);
449 if (pNMHDR
->code
== TTN_NEEDTEXTW
)
451 pTTTW
->lpszText
= m_wszTip
;
455 pTTTA
->lpszText
= m_szTip
;
456 ::WideCharToMultiByte(CP_ACP
, 0, m_wszTip
, -1, m_szTip
, 8192, NULL
, NULL
);
467 bWindowClosed
= TRUE
;
470 return DefWindowProc(hwnd
, uMsg
, wParam
, lParam
);
476 void CPicWindow::NextImage()
479 if (nCurrentDimension
> picture
.GetNumberOfDimensions())
480 nCurrentDimension
= picture
.GetNumberOfDimensions();
482 if (nCurrentFrame
> picture
.GetNumberOfFrames(0))
483 nCurrentFrame
= picture
.GetNumberOfFrames(0);
484 picture
.SetActiveFrame(nCurrentFrame
>= nCurrentDimension
? nCurrentFrame
: nCurrentDimension
);
485 InvalidateRect(*this, NULL
, FALSE
);
489 void CPicWindow::PrevImage()
492 if (nCurrentDimension
< 1)
493 nCurrentDimension
= 1;
495 if (nCurrentFrame
< 1)
497 picture
.SetActiveFrame(nCurrentFrame
>= nCurrentDimension
? nCurrentFrame
: nCurrentDimension
);
498 InvalidateRect(*this, NULL
, FALSE
);
502 void CPicWindow::Animate(bool bStart
)
506 SendMessage(hwndPlayBtn
, BM_SETIMAGE
, (WPARAM
)IMAGE_ICON
, (LPARAM
)hStop
);
507 SetTimer(*this, ID_ANIMATIONTIMER
, 0, NULL
);
511 SendMessage(hwndPlayBtn
, BM_SETIMAGE
, (WPARAM
)IMAGE_ICON
, (LPARAM
)hPlay
);
512 KillTimer(*this, ID_ANIMATIONTIMER
);
516 void CPicWindow::SetPic(tstring path
, tstring title
, bool bFirst
)
519 picpath
=path
;pictitle
=title
;
520 picture
.SetInterpolationMode(InterpolationModeHighQualityBicubic
);
521 bValid
= picture
.Load(picpath
);
522 nDimensions
= picture
.GetNumberOfDimensions();
524 nFrames
= picture
.GetNumberOfFrames(0);
529 InvalidateRect(*this, NULL
, FALSE
);
533 void CPicWindow::DrawViewTitle(HDC hDC
, RECT
* rect
)
536 hFont
= CreateFont(-MulDiv(pSecondPic
? 8 : 10, GetDeviceCaps(hDC
, LOGPIXELSY
), 72), 0, 0, 0, FW_DONTCARE
, false, false, false, ANSI_CHARSET
, OUT_DEFAULT_PRECIS
, CLIP_DEFAULT_PRECIS
, CLEARTYPE_QUALITY
, DEFAULT_PITCH
, _T("MS Shell Dlg"));
537 HFONT hFontOld
= (HFONT
)SelectObject(hDC
, (HGDIOBJ
)hFont
);
540 textrect
.left
= rect
->left
;
541 textrect
.top
= rect
->top
;
542 textrect
.right
= rect
->right
;
543 textrect
.bottom
= rect
->top
+ HEADER_HEIGHT
;
544 if (HasMultipleImages())
545 textrect
.bottom
+= HEADER_HEIGHT
;
548 crBk
= ::GetSysColor(COLOR_SCROLLBAR
);
549 crFg
= ::GetSysColor(COLOR_WINDOWTEXT
);
550 SetBkColor(hDC
, crBk
);
551 ::ExtTextOut(hDC
, 0, 0, ETO_OPAQUE
, &textrect
, NULL
, 0, NULL
);
553 if (GetFocus() == *this)
554 DrawEdge(hDC
, &textrect
, EDGE_BUMP
, BF_RECT
);
556 DrawEdge(hDC
, &textrect
, EDGE_ETCHED
, BF_RECT
);
558 SetTextColor(hDC
, crFg
);
560 // use the path if no title is set.
561 tstring
* title
= pictitle
.empty() ? &picpath
: &pictitle
;
563 tstring realtitle
= *title
;
564 tstring imgnumstring
;
566 if (HasMultipleImages())
570 _stprintf_s(buf
, sizeof(buf
)/sizeof(TCHAR
), (const TCHAR
*)ResString(hResource
, IDS_DIMENSIONSANDFRAMES
), nCurrentFrame
, nFrames
);
572 _stprintf_s(buf
, sizeof(buf
)/sizeof(TCHAR
), (const TCHAR
*)ResString(hResource
, IDS_DIMENSIONSANDFRAMES
), nCurrentDimension
, nDimensions
);
577 if (GetTextExtentPoint32(hDC
, realtitle
.c_str(), (int)realtitle
.size(), &stringsize
))
579 int nStringLength
= stringsize
.cx
;
580 int texttop
= pSecondPic
? textrect
.top
+ (HEADER_HEIGHT
/2) - stringsize
.cy
: textrect
.top
+ (HEADER_HEIGHT
/2) - stringsize
.cy
/2;
582 max(textrect
.left
+ ((textrect
.right
-textrect
.left
)-nStringLength
)/2, 1),
587 (UINT
)realtitle
.size(),
591 realtitle
= (pictitle2
.empty() ? picpath2
: pictitle2
);
593 max(textrect
.left
+ ((textrect
.right
-textrect
.left
)-nStringLength
)/2, 1),
594 texttop
+ stringsize
.cy
,
598 (UINT
)realtitle
.size(),
602 if (HasMultipleImages())
604 if (GetTextExtentPoint32(hDC
, imgnumstring
.c_str(), (int)imgnumstring
.size(), &stringsize
))
606 int nStringLength
= stringsize
.cx
;
609 max(textrect
.left
+ ((textrect
.right
-textrect
.left
)-nStringLength
)/2, 1),
610 textrect
.top
+ HEADER_HEIGHT
+ (HEADER_HEIGHT
/2) - stringsize
.cy
/2,
613 imgnumstring
.c_str(),
614 (UINT
)imgnumstring
.size(),
618 SelectObject(hDC
, (HGDIOBJ
)hFontOld
);
622 void CPicWindow::SetupScrollBars()
625 GetClientRect(&rect
);
627 SCROLLINFO si
= {sizeof(si
)};
629 si
.fMask
= SIF_POS
| SIF_PAGE
| SIF_RANGE
| SIF_DISABLENOSCROLL
;
631 double width
= double(picture
.m_Width
)*picscale
;
632 double height
= double(picture
.m_Height
)*picscale
;
635 width
= max(width
, double(pSecondPic
->m_Width
)*pTheOtherPic
->GetZoom());
636 height
= max(height
, double(pSecondPic
->m_Height
)*pTheOtherPic
->GetZoom());
639 bool bShowHScrollBar
= (nHScrollPos
> 0); // left of pic is left of window
640 bShowHScrollBar
= bShowHScrollBar
|| (width
-nHScrollPos
> rect
.right
); // right of pic is outside right of window
641 bShowHScrollBar
= bShowHScrollBar
|| (width
+nHScrollPos
> rect
.right
); // right of pic is outside right of window
642 bool bShowVScrollBar
= (nVScrollPos
> 0); // top of pic is above window
643 bShowVScrollBar
= bShowVScrollBar
|| (height
-nVScrollPos
+rect
.top
> rect
.bottom
); // bottom of pic is below window
644 bShowVScrollBar
= bShowVScrollBar
|| (height
+nVScrollPos
+rect
.top
> rect
.bottom
); // bottom of pic is below window
646 // if the image is smaller than the window, we don't need the scrollbars
647 ShowScrollBar(*this, SB_HORZ
, bShowHScrollBar
);
648 ShowScrollBar(*this, SB_VERT
, bShowVScrollBar
);
652 si
.nPos
= nVScrollPos
;
653 si
.nPage
= rect
.bottom
-rect
.top
;
654 if (height
< rect
.bottom
-rect
.top
)
659 si
.nMax
= rect
.bottom
+nVScrollPos
-rect
.top
;
663 si
.nMin
= nVScrollPos
;
664 si
.nMax
= int(height
);
672 si
.nMax
= int(max(height
, rect
.bottom
+nVScrollPos
-rect
.top
));
677 si
.nMax
= int(height
-nVScrollPos
);
680 SetScrollInfo(*this, SB_VERT
, &si
, TRUE
);
685 si
.nPos
= nHScrollPos
;
686 si
.nPage
= rect
.right
-rect
.left
;
687 if (width
< rect
.right
-rect
.left
)
692 si
.nMax
= rect
.right
+nHScrollPos
-rect
.left
;
696 si
.nMin
= nHScrollPos
;
697 si
.nMax
= int(width
);
705 si
.nMax
= int(max(width
, rect
.right
+nHScrollPos
-rect
.left
));
710 si
.nMax
= int(width
-nHScrollPos
);
713 SetScrollInfo(*this, SB_HORZ
, &si
, TRUE
);
719 void CPicWindow::OnVScroll(UINT nSBCode
, UINT nPos
)
722 GetClientRect(&rect
);
727 nVScrollPos
= LONG(double(picture
.GetHeight())*picscale
);;
739 nVScrollPos
+= (rect
.bottom
-rect
.top
);
742 nVScrollPos
-= (rect
.bottom
-rect
.top
);
744 case SB_THUMBPOSITION
:
753 LONG height
= LONG(double(picture
.GetHeight())*picscale
);
756 height
= max(height
, LONG(double(pSecondPic
->GetHeight())*picscale
));
760 InvalidateRect(*this, NULL
, TRUE
);
763 void CPicWindow::OnHScroll(UINT nSBCode
, UINT nPos
)
766 GetClientRect(&rect
);
771 nHScrollPos
= LONG(double(picture
.GetWidth())*picscale
);
783 nHScrollPos
+= (rect
.right
-rect
.left
);
786 nHScrollPos
-= (rect
.right
-rect
.left
);
788 case SB_THUMBPOSITION
:
797 LONG width
= LONG(double(picture
.GetWidth())*picscale
);
800 width
= max(width
, LONG(double(pSecondPic
->GetWidth())*picscale
));
804 InvalidateRect(*this, NULL
, TRUE
);
807 void CPicWindow::OnMouseWheel(short fwKeys
, short zDelta
)
810 GetClientRect(&rect
);
811 LONG width
= long(double(picture
.m_Width
)*picscale
);
812 LONG height
= long(double(picture
.m_Height
)*picscale
);
815 width
= max(width
, long(double(pSecondPic
->m_Width
)*picscale
));
816 height
= max(height
, long(double(pSecondPic
->m_Height
)*picscale
));
818 if ((fwKeys
& MK_SHIFT
)&&(fwKeys
& MK_CONTROL
)&&(pSecondPic
))
820 // ctrl+shift+wheel: change the alpha channel
821 float a
= blendAlpha
;
822 a
-= float(zDelta
)/120.0f
/4.0f
;
827 SetBlendAlpha(m_blend
, a
);
829 else if (fwKeys
& MK_SHIFT
)
831 // shift means scrolling sideways
832 OnHScroll(SB_THUMBPOSITION
, GetHPos()-zDelta
);
833 if ((bLinkedPositions
)&&(pTheOtherPic
))
835 pTheOtherPic
->OnHScroll(SB_THUMBPOSITION
, pTheOtherPic
->GetHPos()-zDelta
);
838 else if (fwKeys
& MK_CONTROL
)
840 // control means adjusting the scale factor
841 Zoom(zDelta
>0, true);
842 if ((!bFitSizes
)&&(pTheOtherPic
))
843 pTheOtherPic
->Zoom(zDelta
>0, true);
845 InvalidateRect(*this, NULL
, FALSE
);
846 SetWindowPos(*this, NULL
, 0, 0, 0, 0, SWP_FRAMECHANGED
|SWP_NOSIZE
|SWP_NOREPOSITION
|SWP_NOMOVE
);
850 OnVScroll(SB_THUMBPOSITION
, GetVPos()-zDelta
);
851 if ((bLinkedPositions
)&&(pTheOtherPic
))
853 pTheOtherPic
->OnVScroll(SB_THUMBPOSITION
, pTheOtherPic
->GetVPos()-zDelta
);
858 void CPicWindow::GetClientRect(RECT
* pRect
)
860 ::GetClientRect(*this, pRect
);
861 pRect
->top
+= HEADER_HEIGHT
;
862 if (HasMultipleImages())
864 pRect
->top
+= HEADER_HEIGHT
;
867 pRect
->left
+= SLIDER_WIDTH
;
870 void CPicWindow::GetClientRectWithScrollbars(RECT
* pRect
)
872 GetClientRect(pRect
);
873 ::GetWindowRect(*this, pRect
);
874 pRect
->right
= pRect
->right
-pRect
->left
;
875 pRect
->bottom
= pRect
->bottom
-pRect
->top
;
878 pRect
->top
+= HEADER_HEIGHT
;
879 if (HasMultipleImages())
881 pRect
->top
+= HEADER_HEIGHT
;
884 pRect
->left
+= SLIDER_WIDTH
;
888 void CPicWindow::SetZoom(double dZoom
, bool centermouse
)
890 // Set the interpolation mode depending on zoom
891 double oldPicscale
= picscale
;
893 { // Zoomed out, use high quality bicubic
894 picture
.SetInterpolationMode(InterpolationModeHighQualityBicubic
);
896 pSecondPic
->SetInterpolationMode(InterpolationModeHighQualityBicubic
);
898 else if (!((int)(dZoom
*100.0)%100))
899 { // "Even" zoom sizes should be shown w-o any interpolation
900 picture
.SetInterpolationMode(InterpolationModeNearestNeighbor
);
902 pSecondPic
->SetInterpolationMode(InterpolationModeNearestNeighbor
);
905 { // Arbitrary zoomed in, use bilinear that is semi-smoothed
906 picture
.SetInterpolationMode(InterpolationModeBilinear
);
908 pSecondPic
->SetInterpolationMode(InterpolationModeBilinear
);
912 if ((bFitSizes
)&&(bMainPic
))
914 double width
, height
;
915 double zoomWidth
, zoomHeight
;
916 width
= double(picture
.m_Width
)*dZoom
;
917 height
= double(picture
.m_Height
)*dZoom
;
918 zoomWidth
= width
/double(pTheOtherPic
->GetPic()->m_Width
);
919 zoomHeight
= height
/double(pTheOtherPic
->GetPic()->m_Height
);
920 pTheOtherPic
->SetZoom(min(zoomWidth
, zoomHeight
), centermouse
);
923 // adjust the scrollbar positions according to the new zoom and the
924 // mouse position: if possible, keep the pixel where the mouse pointer
925 // is at the same position after the zoom
927 DWORD ptW
= GetMessagePos();
928 cpos
.x
= GET_X_LPARAM(ptW
);
929 cpos
.y
= GET_Y_LPARAM(ptW
);
930 ScreenToClient(*this, &cpos
);
932 GetClientRect(&clientrect
);
933 if ((PtInRect(&clientrect
, cpos
))&&(centermouse
))
935 // the mouse pointer is over our window
936 nHScrollPos
= int(double(nHScrollPos
+ cpos
.x
)*(dZoom
/oldPicscale
))-cpos
.x
;
937 nVScrollPos
= int(double(nVScrollPos
+ cpos
.y
)*(dZoom
/oldPicscale
))-cpos
.y
;
941 nHScrollPos
= int(double(nHScrollPos
+ ((clientrect
.right
-clientrect
.left
)/2))*(dZoom
/oldPicscale
))-((clientrect
.right
-clientrect
.left
)/2);
942 nVScrollPos
= int(double(nVScrollPos
+ ((clientrect
.bottom
-clientrect
.top
)/2))*(dZoom
/oldPicscale
))-((clientrect
.bottom
-clientrect
.top
)/2);
947 InvalidateRect(*this, NULL
, TRUE
);
950 void CPicWindow::Zoom(bool in
, bool centermouse
)
954 // Find correct zoom factor and quantize picscale
955 if (!in
&& picscale
<= 0.2)
960 else if ((in
&& picscale
< 1.0) || (!in
&& picscale
<= 1.0))
962 picscale
= 0.1 * RoundDouble(picscale
/0.1, 0); // Quantize to 0.1
965 else if ((in
&& picscale
< 2.0) || (!in
&& picscale
<= 2.0))
967 picscale
= 0.25 * RoundDouble(picscale
/0.25, 0); // Quantize to 0.25
972 picscale
= RoundDouble(picscale
,0);
979 if ((pSecondPic
)&&(!bFitSizes
))
980 pTheOtherPic
->SetZoom(pTheOtherPic
->GetZoom()+zoomFactor
, false);
981 SetZoom(picscale
+zoomFactor
, centermouse
);
985 if ((pSecondPic
)&&(!bFitSizes
))
986 pTheOtherPic
->SetZoom(pTheOtherPic
->GetZoom()-zoomFactor
, false);
987 SetZoom(picscale
-zoomFactor
, centermouse
);
991 double CPicWindow::RoundDouble(double doValue
, int nPrecision
)
993 static const double doBase
= 10.0;
994 double doComplete5
, doComplete5i
;
996 doComplete5
= doValue
* pow(doBase
, (double) (nPrecision
+ 1));
1007 doComplete5
/= doBase
;
1008 modf(doComplete5
, &doComplete5i
);
1010 return doComplete5i
/ pow(doBase
, (double) nPrecision
);
1012 void CPicWindow::FitImageInWindow()
1017 GetClientRectWithScrollbars(&rect
);
1019 if (rect
.right
-rect
.left
)
1021 if (((rect
.right
- rect
.left
) > picture
.m_Width
+2)&&((rect
.bottom
- rect
.top
)> picture
.m_Height
+2))
1023 // image is smaller than the window
1028 // image is bigger than the window
1029 double xscale
= double(rect
.right
-rect
.left
-2)/double(picture
.m_Width
);
1030 double yscale
= double(rect
.bottom
-rect
.top
-2)/double(picture
.m_Height
);
1031 dZoom
= min(yscale
, xscale
);
1035 if (((rect
.right
- rect
.left
) > pSecondPic
->m_Width
+2)&&((rect
.bottom
- rect
.top
)> pSecondPic
->m_Height
+2))
1037 // image is smaller than the window
1038 pTheOtherPic
->SetZoom(min(1.0, dZoom
), false);
1042 // image is bigger than the window
1043 double xscale
= double(rect
.right
-rect
.left
-2)/double(pSecondPic
->m_Width
);
1044 double yscale
= double(rect
.bottom
-rect
.top
-2)/double(pSecondPic
->m_Height
);
1045 pTheOtherPic
->SetZoom(min(yscale
, xscale
), false);
1047 nHSecondScrollPos
= 0;
1048 nVSecondScrollPos
= 0;
1050 SetZoom(dZoom
, false);
1054 InvalidateRect(*this, NULL
, TRUE
);
1057 void CPicWindow::CenterImage()
1060 GetClientRectWithScrollbars(&rect
);
1061 double width
= (double(picture
.m_Width
)*picscale
) + 2.0;
1062 double height
= (double(picture
.m_Height
)*picscale
) + 2.0;
1065 width
= max(width
, (double(pSecondPic
->m_Width
)*pTheOtherPic
->GetZoom()) + 2.0);
1066 height
= max(height
, (double(pSecondPic
->m_Height
)*pTheOtherPic
->GetZoom()) + 2.0);
1069 bool bPicWidthBigger
= (int(width
) > (rect
.right
-rect
.left
));
1070 bool bPicHeightBigger
= (int(height
) > (rect
.bottom
-rect
.top
));
1071 // set the scroll position so that the image is drawn centered in the window
1072 // if the window is bigger than the image
1073 if (!bPicWidthBigger
)
1075 nHScrollPos
= -((rect
.right
-rect
.left
+4)-int(width
))/2;
1077 if (!bPicHeightBigger
)
1079 nVScrollPos
= -((rect
.bottom
-rect
.top
+4)-int(height
))/2;
1084 void CPicWindow::FitSizes(bool bFit
)
1090 nHSecondScrollPos
= 0;
1091 nVSecondScrollPos
= 0;
1093 SetZoom(GetZoom(), false);
1096 void CPicWindow::ShowPicWithBorder(HDC hdc
, const RECT
&bounds
, CPicture
&pic
, double scale
)
1098 ::SetBkColor(hdc
, transparentColor
);
1099 ::ExtTextOut(hdc
, 0, 0, ETO_OPAQUE
, &bounds
, NULL
, 0, NULL
);
1102 picrect
.left
= bounds
.left
- nHScrollPos
;
1103 picrect
.top
= bounds
.top
- nVScrollPos
;
1104 if (!bLinkedPositions
&& (pTheOtherPic
) && (&pic
!= &picture
))
1106 picrect
.left
= bounds
.left
- nHSecondScrollPos
;
1107 picrect
.top
= bounds
.top
- nVSecondScrollPos
;
1109 picrect
.right
= (picrect
.left
+ LONG(double(pic
.m_Width
) * scale
));
1110 picrect
.bottom
= (picrect
.top
+ LONG(double(pic
.m_Height
) * scale
));
1112 pic
.Show(hdc
, picrect
);
1115 border
.left
= picrect
.left
-1;
1116 border
.top
= picrect
.top
-1;
1117 border
.right
= picrect
.right
+1;
1118 border
.bottom
= picrect
.bottom
+1;
1120 HPEN hPen
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DDKSHADOW
));
1121 HPEN hOldPen
= (HPEN
)SelectObject(hdc
, hPen
);
1122 MoveToEx(hdc
, border
.left
, border
.top
, NULL
);
1123 LineTo(hdc
, border
.left
, border
.bottom
);
1124 LineTo(hdc
, border
.right
, border
.bottom
);
1125 LineTo(hdc
, border
.right
, border
.top
);
1126 LineTo(hdc
, border
.left
, border
.top
);
1127 SelectObject(hdc
, hOldPen
);
1131 void CPicWindow::Paint(HWND hwnd
)
1135 RECT rect
, fullrect
;
1137 GetUpdateRect(hwnd
, &rect
, FALSE
);
1138 if (IsRectEmpty(&rect
))
1141 ::GetClientRect(*this, &fullrect
);
1142 hdc
= BeginPaint(hwnd
, &ps
);
1144 // Exclude the alpha control and button
1145 if ((pSecondPic
)&&(m_blend
== BLEND_ALPHA
))
1146 ExcludeClipRect(hdc
, 0, m_inforect
.top
-4, SLIDER_WIDTH
, m_inforect
.bottom
+4);
1148 CMyMemDC
memDC(hdc
);
1149 if ((pSecondPic
)&&(m_blend
!= BLEND_ALPHA
))
1151 // erase the place where the alpha slider would be
1152 ::SetBkColor(memDC
, transparentColor
);
1153 RECT bounds
= {0, m_inforect
.top
-4, SLIDER_WIDTH
, m_inforect
.bottom
+4};
1154 ::ExtTextOut(memDC
, 0, 0, ETO_OPAQUE
, &bounds
, NULL
, 0, NULL
);
1157 GetClientRect(&rect
);
1160 ShowPicWithBorder(memDC
, rect
, picture
, picscale
);
1163 HDC secondhdc
= CreateCompatibleDC(hdc
);
1164 HBITMAP hBitmap
= CreateCompatibleBitmap(hdc
, rect
.right
- rect
.left
, rect
.bottom
- rect
.top
);
1165 HBITMAP hOldBitmap
= (HBITMAP
)SelectObject(secondhdc
, hBitmap
);
1166 SetWindowOrgEx(secondhdc
, rect
.left
, rect
.top
, NULL
);
1168 if ((pSecondPic
)&&(m_blend
!= BLEND_ALPHA
))
1170 // erase the place where the alpha slider would be
1171 ::SetBkColor(secondhdc
, transparentColor
);
1172 RECT bounds
= {0, m_inforect
.top
-4, SLIDER_WIDTH
, m_inforect
.bottom
+4};
1173 ::ExtTextOut(secondhdc
, 0, 0, ETO_OPAQUE
, &bounds
, NULL
, 0, NULL
);
1175 ShowPicWithBorder(secondhdc
, rect
, *pSecondPic
, pTheOtherPic
->GetZoom());
1177 if (m_blend
== BLEND_ALPHA
)
1179 BLENDFUNCTION blender
;
1180 blender
.AlphaFormat
= 0;
1181 blender
.BlendFlags
= 0;
1182 blender
.BlendOp
= AC_SRC_OVER
;
1183 blender
.SourceConstantAlpha
= (BYTE
)(blendAlpha
*255);
1187 rect
.right
-rect
.left
,
1188 rect
.bottom
-rect
.top
,
1192 rect
.right
-rect
.left
,
1193 rect
.bottom
-rect
.top
,
1196 else if (m_blend
== BLEND_XOR
)
1201 rect
.right
-rect
.left
,
1202 rect
.bottom
-rect
.top
,
1206 //rect.right-rect.left,
1207 //rect.bottom-rect.top,
1209 InvertRect(memDC
, &rect
);
1211 SelectObject(secondhdc
, hOldBitmap
);
1212 DeleteObject(hBitmap
);
1213 DeleteDC(secondhdc
);
1215 int sliderwidth
= 0;
1216 if ((pSecondPic
)&&(m_blend
== BLEND_ALPHA
))
1217 sliderwidth
= SLIDER_WIDTH
;
1218 m_inforect
.left
= rect
.left
+4+sliderwidth
;
1219 m_inforect
.top
= rect
.top
;
1220 m_inforect
.right
= rect
.right
+sliderwidth
;
1221 m_inforect
.bottom
= rect
.bottom
;
1223 SetBkColor(memDC
, transparentColor
);
1226 TCHAR infostring
[8192];
1227 BuildInfoString(infostring
, sizeof(infostring
)/sizeof(TCHAR
), false);
1229 NONCLIENTMETRICS metrics
= {0};
1230 metrics
.cbSize
= sizeof(NONCLIENTMETRICS
);
1231 SystemParametersInfo(SPI_GETNONCLIENTMETRICS
, 0, &metrics
, FALSE
);
1232 HFONT hFont
= CreateFontIndirect(&metrics
.lfStatusFont
);
1233 HFONT hFontOld
= (HFONT
)SelectObject(memDC
, (HGDIOBJ
)hFont
);
1234 // find out how big the rectangle for the text has to be
1235 DrawText(memDC
, infostring
, -1, &m_inforect
, DT_EDITCONTROL
| DT_EXPANDTABS
| DT_LEFT
| DT_VCENTER
| DT_CALCRECT
);
1237 // the text should be drawn with a four pixel offset to the window borders
1238 m_inforect
.top
= rect
.bottom
- (m_inforect
.bottom
-m_inforect
.top
) - 4;
1239 m_inforect
.bottom
= rect
.bottom
-4;
1241 // first draw an edge rectangle
1243 edgerect
.left
= m_inforect
.left
-4;
1244 edgerect
.top
= m_inforect
.top
-4;
1245 edgerect
.right
= m_inforect
.right
+4;
1246 edgerect
.bottom
= m_inforect
.bottom
+4;
1247 ::ExtTextOut(memDC
, 0, 0, ETO_OPAQUE
, &edgerect
, NULL
, 0, NULL
);
1248 DrawEdge(memDC
, &edgerect
, EDGE_BUMP
, BF_RECT
| BF_SOFT
);
1250 SetTextColor(memDC
, GetSysColor(COLOR_WINDOWTEXT
));
1251 DrawText(memDC
, infostring
, -1, &m_inforect
, DT_EDITCONTROL
| DT_EXPANDTABS
| DT_LEFT
| DT_VCENTER
);
1252 SelectObject(memDC
, (HGDIOBJ
)hFontOld
);
1253 DeleteObject(hFont
);
1258 SetBkColor(memDC
, ::GetSysColor(COLOR_WINDOW
));
1259 ::ExtTextOut(memDC
, 0, 0, ETO_OPAQUE
, &rect
, NULL
, 0, NULL
);
1261 ResString str
= ResString(hResource
, IDS_INVALIDIMAGEINFO
);
1262 if (GetTextExtentPoint32(memDC
, str
, (int)_tcslen(str
), &stringsize
))
1264 int nStringLength
= stringsize
.cx
;
1267 max(rect
.left
+ ((rect
.right
-rect
.left
)-nStringLength
)/2, 1),
1268 rect
.top
+ ((rect
.bottom
-rect
.top
) - stringsize
.cy
)/2,
1276 DrawViewTitle(memDC
, &fullrect
);
1278 EndPaint(hwnd
, &ps
);
1281 bool CPicWindow::CreateButtons()
1283 // Ensure that the common control DLL is loaded.
1284 INITCOMMONCONTROLSEX icex
;
1285 icex
.dwSize
= sizeof(INITCOMMONCONTROLSEX
);
1286 icex
.dwICC
= ICC_BAR_CLASSES
| ICC_WIN95_CLASSES
;
1287 InitCommonControlsEx(&icex
);
1289 hwndLeftBtn
= CreateWindowEx(0,
1292 WS_CHILD
| WS_VISIBLE
| BS_PUSHBUTTON
| BS_ICON
| BS_FLAT
,
1295 (HMENU
)LEFTBUTTON_ID
,
1298 if (hwndLeftBtn
== INVALID_HANDLE_VALUE
)
1300 hLeft
= (HICON
)LoadImage(hResource
, MAKEINTRESOURCE(IDI_BACKWARD
), IMAGE_ICON
, 16, 16, LR_LOADTRANSPARENT
);
1301 SendMessage(hwndLeftBtn
, BM_SETIMAGE
, (WPARAM
)IMAGE_ICON
, (LPARAM
)hLeft
);
1302 hwndRightBtn
= CreateWindowEx(0,
1305 WS_CHILD
| WS_VISIBLE
| BS_PUSHBUTTON
| BS_ICON
| BS_FLAT
,
1308 (HMENU
)RIGHTBUTTON_ID
,
1311 if (hwndRightBtn
== INVALID_HANDLE_VALUE
)
1313 hRight
= (HICON
)LoadImage(hResource
, MAKEINTRESOURCE(IDI_FORWARD
), IMAGE_ICON
, 16, 16, LR_LOADTRANSPARENT
);
1314 SendMessage(hwndRightBtn
, BM_SETIMAGE
, (WPARAM
)IMAGE_ICON
, (LPARAM
)hRight
);
1315 hwndPlayBtn
= CreateWindowEx(0,
1318 WS_CHILD
| WS_VISIBLE
| BS_PUSHBUTTON
| BS_ICON
| BS_FLAT
,
1321 (HMENU
)PLAYBUTTON_ID
,
1324 if (hwndPlayBtn
== INVALID_HANDLE_VALUE
)
1326 hPlay
= (HICON
)LoadImage(hResource
, MAKEINTRESOURCE(IDI_START
), IMAGE_ICON
, 16, 16, LR_LOADTRANSPARENT
);
1327 hStop
= (HICON
)LoadImage(hResource
, MAKEINTRESOURCE(IDI_STOP
), IMAGE_ICON
, 16, 16, LR_LOADTRANSPARENT
);
1328 SendMessage(hwndPlayBtn
, BM_SETIMAGE
, (WPARAM
)IMAGE_ICON
, (LPARAM
)hPlay
);
1329 hwndAlphaToggleBtn
= CreateWindowEx(0,
1332 WS_CHILD
| WS_VISIBLE
| BS_PUSHBUTTON
| BS_ICON
| BS_FLAT
| BS_NOTIFY
| BS_PUSHLIKE
,
1335 (HMENU
)ALPHATOGGLEBUTTON_ID
,
1338 if (hwndAlphaToggleBtn
== INVALID_HANDLE_VALUE
)
1340 hAlphaToggle
= (HICON
)LoadImage(hResource
, MAKEINTRESOURCE(IDI_ALPHATOGGLE
), IMAGE_ICON
, 16, 16, LR_LOADTRANSPARENT
);
1341 SendMessage(hwndAlphaToggleBtn
, BM_SETIMAGE
, (WPARAM
)IMAGE_ICON
, (LPARAM
)hAlphaToggle
);
1346 void CPicWindow::PositionChildren()
1349 ::GetClientRect(*this, &rect
);
1350 if (HasMultipleImages())
1352 SetWindowPos(hwndLeftBtn
, HWND_TOP
, rect
.left
+3, rect
.top
+ HEADER_HEIGHT
+ (HEADER_HEIGHT
-16)/2, 16, 16, SWP_FRAMECHANGED
|SWP_SHOWWINDOW
);
1353 SetWindowPos(hwndRightBtn
, HWND_TOP
, rect
.left
+23, rect
.top
+ HEADER_HEIGHT
+ (HEADER_HEIGHT
-16)/2, 16, 16, SWP_FRAMECHANGED
|SWP_SHOWWINDOW
);
1355 SetWindowPos(hwndPlayBtn
, HWND_TOP
, rect
.left
+43, rect
.top
+ HEADER_HEIGHT
+ (HEADER_HEIGHT
-16)/2, 16, 16, SWP_FRAMECHANGED
|SWP_SHOWWINDOW
);
1357 ShowWindow(hwndPlayBtn
, SW_HIDE
);
1361 ShowWindow(hwndLeftBtn
, SW_HIDE
);
1362 ShowWindow(hwndRightBtn
, SW_HIDE
);
1363 ShowWindow(hwndPlayBtn
, SW_HIDE
);
1368 bool CPicWindow::HasMultipleImages()
1370 return (((nDimensions
> 1)||(nFrames
> 1))&&(pSecondPic
== NULL
));
1373 void CPicWindow::CreateTrackbar(HWND hwndParent
)
1375 HWND hwndTrack
= CreateWindowEx(
1376 0, // no extended styles
1377 TRACKBAR_CLASS
, // class name
1378 _T("Trackbar Control"), // title (caption)
1379 WS_CHILD
| WS_VISIBLE
| TBS_VERT
| TBS_TOOLTIPS
| TBS_AUTOTICKS
, // style
1382 hwndParent
, // parent window
1383 (HMENU
)TRACKBAR_ID
, // control identifier
1385 NULL
// no WM_CREATE parameter
1388 SendMessage(hwndTrack
, TBM_SETRANGE
,
1389 (WPARAM
) TRUE
, // redraw flag
1390 (LPARAM
) MAKELONG(0, 16)); // min. & max. positions
1391 SendMessage(hwndTrack
, TBM_SETTIPSIDE
,
1395 m_AlphaSlider
.ConvertTrackbarToNice(hwndTrack
);
1398 void CPicWindow::BuildInfoString(TCHAR
* buf
, int size
, bool bTooltip
)
1400 // Unfortunately, we need two different strings for the tooltip
1401 // and the info box. Because the tooltips use a different tab size
1402 // than ExtTextOut(), and to keep the output aligned we therefore
1403 // need two different strings.
1404 // Note: some translations could end up with two identical strings, but
1405 // in English we need two - even if we wouldn't need two in English, some
1406 // translation might then need two again.
1409 _stprintf_s(buf
, size
,
1410 (TCHAR
const *)ResString(hResource
, bTooltip
? IDS_DUALIMAGEINFOTT
: IDS_DUALIMAGEINFO
),
1411 picture
.GetFileSizeAsText().c_str(), picture
.GetFileSizeAsText(false).c_str(),
1412 picture
.m_Width
, picture
.m_Height
,
1413 picture
.GetHorizontalResolution(), picture
.GetVerticalResolution(),
1414 picture
.m_ColorDepth
,
1415 (UINT
)(GetZoom()*100.0),
1416 pSecondPic
->GetFileSizeAsText().c_str(), pSecondPic
->GetFileSizeAsText(false).c_str(),
1417 pSecondPic
->m_Width
, pSecondPic
->m_Height
,
1418 pSecondPic
->GetHorizontalResolution(), pSecondPic
->GetVerticalResolution(),
1419 pSecondPic
->m_ColorDepth
,
1420 (UINT
)(pTheOtherPic
->GetZoom()*100.0));
1424 _stprintf_s(buf
, size
,
1425 (TCHAR
const *)ResString(hResource
, bTooltip
? IDS_IMAGEINFOTT
: IDS_IMAGEINFO
),
1426 picture
.GetFileSizeAsText().c_str(), picture
.GetFileSizeAsText(false).c_str(),
1427 picture
.m_Width
, picture
.m_Height
,
1428 picture
.GetHorizontalResolution(), picture
.GetVerticalResolution(),
1429 picture
.m_ColorDepth
,
1430 (UINT
)(GetZoom()*100.0));