Create a jpeg Viewer in C++ with MFC Hardware: CPU: I7-12700KF Speicher: 192GB RAM GPU : Nvidia RTX 3060 TI 8 GB Modell: gpt-oss-20B Das war nix. Problem dieses Viewers. Er benutzt keine doppelte Pufferung, Double Buffer, Bildschirmspeicher, Aufbau eines zweiten Bildes, während Bild 1 sichtbar ist, für verschieben, vergrößern, verkleinern des Dialogfeldes bzw. des Elementes Static Label für die Ansicht eines JPG-Bildes. Die Funktion createcompabibleDC, der erste Versuch von Microsoft Win95 Game Engine, Windows Spiele zu produzieren, krachend gescheitert, dient dazu, einen zweiten Device Context (hier Bildschirm) als Zusatzpuffer zu Wechseln zwischen 2 Anzeigenzuständen,vgl. Animation, wird hier nicht gemacht. Ist aber Programmierstandard seit Einführung Windows95 / NT 4.0. Dadurch kann man das so nicht verwenden. Die Funktion OnSize wird nicht verwendet, also, Artefakte am Bildschirm. Vermutlich ein sehr, sehr schlecht gewähltes Github Beispiel, das es da reingeschafft hat. Ist Standard für die GDI+ Programmmierung. Der Müll im Netz wird der KI da noch zu schaffen machen. <|channel|>analysis<|message|>Need explain using MFC to load and display JPEG. Provide code: use GDI+ or WIC. Provide minimal example.<|end|> <|start|>assistant<|channel|>final<|message|>## Quick‑Start: An MFC “JPG Reader” (Single‑Dialog Application) Below is a **complete, copy‑paste‑ready** MFC program that 1. Lets the user pick any JPEG file with an `Open…` menu item. 2. Loads the image into memory using **GDI+** (the easiest way to read JPG in native C++). 3. Draws the picture on a custom control (or the whole dialog). > **Why GDI+?** > • It’s part of the Windows SDK, no third‑party libraries needed. > • Handles JPEG/PNG/BMP/etc. automatically. > • Works fine in 32‑bit and 64‑bit builds. The example is a *dialog‑based* MFC application (the simplest template). All you need to do after creating the project is paste the code snippets below into the appropriate files and build. --- ## 1. Project Setup | Step | What to do | Why | |------|------------|-----| | **Create a new MFC Application** | `File → New → Project → Visual C++ → MFC App` | Gives you the skeleton with proper include paths, libs, etc. | | **Choose “Dialog‑based”** | In the wizard pick *“Dialog based”* | One dialog = one window; perfect for an image viewer. | | **Keep the defaults** | Accept all default settings (Use MFC in a shared DLL). | Focus on code, not packaging. | After finishing the wizard you’ll have: ``` JpgReader.sln ├─ JpgReader/ (project files) │ ├─ CMainFrame.cpp/.h │ └─ ... etc. ``` --- ## 2. Add a Picture Control 1. Open **Resource View** → double‑click the dialog resource (`IDD_JPGREADER_DIALOG`). 2. Drag an *“Static”* control onto the dialog, set its properties: - `ID = IDC_STATIC_PIC` - `Style` → `WS_BORDER | WS_VISIBLE` (optional) 3. Resize it to fill most of the dialog – that’s where the image will appear. > **Tip:** The static control is just a window; we’ll subclass it in code so we can paint our bitmap into it. --- ## 3. GDI+ Helper Class Create two new files: `GdiplusUtil.h` and `GdiplusUtil.cpp`. ```cpp // GdiplusUtil.h #pragma once #include using namespace Gdiplus; class CGdiPlusHelper { public: static void Init(); // call once in WinMain or App::InitInstance static void Shutdown(); static Image* LoadImageFromFile(const CString& path); }; ``` ```cpp // GdiplusUtil.cpp #include "pch.h" #include "GdiplusUtil.h" static ULONG_PTR g_gdiplusToken = 0; void CGdiPlusHelper::Init() { if (g_gdiplusToken == 0) { GdiplusStartupInput gdiplusStartupInput; GdiplusStartup(&g_gdiplusToken, &gdiplusStartupInput, nullptr); } } void CGdiPlusHelper::Shutdown() { if (g_gdiplusToken != 0) { GdiplusShutdown(g_gdiplusToken); g_gdiplusToken = 0; } } Image* CGdiPlusHelper::LoadImageFromFile(const CString& path) { // GDI+ accepts wide strings, convert to std::wstring std::wstring ws(path.GetString()); return new Image(ws.c_str()); // will be nullptr if load fails } ``` > **Where to call Init/Shutdown?** > *In the application class (`CApp`), override `InitInstance()` and `ExitInstance()`.* --- ## 4. Application Class Changes Open `CApp.h` / `.cpp` (usually named after your project, e.g., `JpgReaderApp.h/cpp`). ```cpp // JpgReaderApp.cpp #include "pch.h" #include "framework.h" #include "JpgReader.h" #include "JpgReaderApp.h" #include "GdiplusUtil.h" #ifdef _DEBUG #define new DEBUG_NEW #endif BOOL CJpgReaderApp::InitInstance() { CWinApp::InitInstance(); // GDI+ init CGdiPlusHelper::Init(); // Standard initialization... ... } int CJpgReaderApp::ExitInstance() { CGdiPlusHelper::Shutdown(); // cleanup GDI+ return CWinApp::ExitInstance(); } ``` --- ## 5. Dialog Class – Store the Image Open `CMainFrame.h` (or whatever your dialog class is called). Add: ```cpp class CMainFrame : public CDialogEx { public: CMainFrame(CWnd* pParent = nullptr); #ifdef AFX_DESIGN_TIME enum { IDD = IDD_JPGREADER_DIALOG }; #endif protected: virtual BOOL OnInitDialog(); afx_msg void OnOpen(); // menu item “Open…” afx_msg void OnClose(); // close the image DECLARE_MESSAGE_MAP() private: CStatic m_picCtrl; // wrapper for IDC_STATIC_PIC Image* m_pImage = nullptr; // GDI+ image (owned by us) }; ``` --- ## 6. Dialog Implementation ```cpp // CMainFrame.cpp #include "pch.h" #include "framework.h" #include "JpgReader.h" #include "CMainFrame.h" #include "GdiplusUtil.h" BEGIN_MESSAGE_MAP(CMainFrame, CDialogEx) ON_WM_INITDIALOG() ON_COMMAND(ID_FILE_OPEN, &CMainFrame::OnOpen) // ID from menu ON_COMMAND(ID_APP_EXIT, &CMainFrame::OnClose) // Exit END_MESSAGE_MAP() BOOL CMainFrame::OnInitDialog() { CDialogEx::OnInitDialog(); m_picCtrl.Attach(GetDlgItem(IDC_STATIC_PIC)->GetSafeHwnd()); return TRUE; } void CMainFrame::OnOpen() { CFileDialog dlg(TRUE, _T("jpg"), nullptr, OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, _T("JPEG Files (*.jpg;*.jpeg)|*.jpg;*.jpeg|All Files (*.*)|*.*||")); if (dlg.DoModal() != IDOK) return; // free old image delete m_pImage; m_pImage = nullptr; CString path(dlg.GetPathName()); m_pImage = CGdiPlusHelper::LoadImageFromFile(path); if (!m_pImage || m_pImage->GetLastStatus() != Ok) { MessageBox(_T("Failed to load image."), _T("Error"), MB_ICONERROR); delete m_pImage; m_pImage = nullptr; return; } // force a repaint of the static control InvalidateRect(&m_picCtrl, FALSE); } void CMainFrame::OnClose() { delete m_pImage; // cleanup before exit EndDialog(IDOK); // or PostQuitMessage(0) } ``` ### Paint the Image We’ll subclass the static control to paint our bitmap. Add a small helper class inside `CMainFrame.cpp` (or a separate file). ```cpp class CPictureCtrl : public CStatic { public: CPictureCtrl() : m_pImage(nullptr) {} void SetImage(Image* pImg) { m_pImage = pImg; Invalidate(); } protected: afx_msg void OnPaint(); DECLARE_MESSAGE_MAP() private: Image* m_pImage; }; BEGIN_MESSAGE_MAP(CPictureCtrl, CStatic) ON_WM_PAINT() END_MESSAGE_MAP() void CPictureCtrl::OnPaint() { CPaintDC dc(this); if (!m_pImage) return; // get client rect CRect rcClient; GetClientRect(&rcClient); // calculate scaling (fit inside the control, preserve aspect) int imgW = m_pImage->GetWidth(); int imgH = m_pImage->GetHeight(); float scaleX = static_cast(rcClient.Width()) / imgW; float scaleY = static_cast(rcClient.Height()) / imgH; float scale = min(scaleX, scaleY); int dstW = static_cast(imgW * scale); int dstH = static_cast(imgH * scale); // center int dstX = (rcClient.Width() - dstW) / 2; int dstY = (rcClient.Height() - dstH) / 2; // draw using GDI+ Graphics g(dc.m_hDC); g.DrawImage(m_pImage, dstX, dstY, dstW, dstH); } ``` Now replace the default `CStatic` with this subclass: ```cpp // In CMainFrame::OnInitDialog (after Attach) m_picCtrl.SubclassDlgItem(IDC_STATIC_PIC, this); // attach to dialog's static ``` And change `m_picCtrl` declaration to `CPictureCtrl m_picCtrl;`. Finally, in `OnOpen`, after loading the image: ```cpp m_picCtrl.SetImage(m_pImage); ``` --- ## 7. Build & Run 1. **Build** (`Ctrl+Shift+B`). 2. **Run** (`F5` or `Ctrl+F5`). 3. Menu → *File → Open…* → pick a `.jpg`. 4. The image should appear, scaled to fit the static control. --- ## 8. Optional Enhancements | Feature | Quick Idea | |---------|------------| | **Zoom In/Out** | Add two menu items (`ID_ZOOM_IN`, `ID_ZOOM_OUT`). Store a float `m_zoom` and multiply `scale` in `OnPaint`. | | **Pan** | Capture mouse drag events on the static control; offset drawing accordingly. | | **Full‑screen view** | Hide dialog frame, maximize window, or create a new top‑level window that hosts the picture control. | | **Support PNG/JPEG/Bitmap** | GDI+ already supports them; just change filter in `CFileDialog`. | | **Error handling** | Show the image’s pixel format, width/height, file size, etc., in a status bar. | --- ## 9. Full Source Listing > The following is the minimal set of files you need to compile. ### GdiplusUtil.h / .cpp *(as shown earlier)* ### CApp (e.g., `JpgReaderApp.cpp`) *(only the InitInstance/ExitInstance snippets are changed)* ```cpp // JpgReaderApp.cpp #include "pch.h" #include "framework.h" #include "JpgReader.h" #include "JpgReaderApp.h" #include "GdiplusUtil.h" BOOL CJpgReaderApp::InitInstance() { CWinApp::InitInstance(); CGdiPlusHelper::Init(); // <-- GDI+ init ... } int CJpgReaderApp::ExitInstance() { CGdiPlusHelper::Shutdown(); // <-- cleanup return CWinApp::ExitInstance(); } ``` ### Dialog Class (CMainFrame) **Header (`CMainFrame.h`):** ```cpp #pragma once #include "GdiplusUtil.h" class CPictureCtrl; // forward class CMainFrame : public CDialogEx { public: CMainFrame(CWnd* pParent = nullptr); #ifdef AFX_DESIGN_TIME enum { IDD = IDD_JPGREADER_DIALOG }; #endif protected: virtual BOOL OnInitDialog(); afx_msg void OnOpen(); // ID_FILE_OPEN afx_msg void OnExit(); // ID_APP_EXIT DECLARE_MESSAGE_MAP() private: CPictureCtrl* m_pPic; // subclassed static Image* m_pImage = nullptr; }; ``` **Implementation (`CMainFrame.cpp`):** ```cpp #include "pch.h" #include "framework.h" #include "JpgReader.h" #include "CMainFrame.h" BEGIN_MESSAGE_MAP(CMainFrame, CDialogEx) ON_WM_INITDIALOG() ON_COMMAND(ID_FILE_OPEN, &CMainFrame::OnOpen) ON_COMMAND(ID_APP_EXIT, &CMainFrame::OnExit) END_MESSAGE_MAP() // --- CPictureCtrl definition (see above) --- BOOL CMainFrame::OnInitDialog() { CDialogEx::OnInitDialog(); // Subclass the static control m_pPic = new CPictureCtrl; m_pPic->SubclassDlgItem(IDC_STATIC_PIC, this); return TRUE; } void CMainFrame::OnOpen() { CFileDialog dlg(TRUE, _T("jpg"), nullptr, OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, _T("JPEG Files (*.jpg;*.jpeg)|*.jpg;*.jpeg|All Files (*.*)|*.*||")); if (dlg.DoModal() != IDOK) return; delete m_pImage; m_pImage = CGdiPlusHelper::LoadImageFromFile(dlg.GetPathName()); if (!m_pImage || m_pImage->GetLastStatus() != Ok) { MessageBox(_T("Could not load image."), _T("Error"), MB_ICONERROR); delete m_pImage; m_pImage = nullptr; return; } m_pPic->SetImage(m_pImage); // trigger repaint } void CMainFrame::OnExit() { delete m_pImage; EndDialog(IDOK); } ``` --- ## 10. Summary - **GDI+** handles JPEG decoding for you – no need to write a decoder. - A **subclassed static control** gives you full control over painting. - The code is **pure MFC + Win32**, so it will compile on any Windows machine that has the SDK. Happy coding, and enjoy your very own “JPG Reader” built with MFC!