diff --git a/Lab_1/Lab_1.sln b/Lab_1/Lab_1.sln new file mode 100644 index 0000000..c741e69 --- /dev/null +++ b/Lab_1/Lab_1.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30309.148 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Lab_1", "Lab_1\Lab_1.vcxproj", "{805D143F-4925-408E-8E98-71A5B106E6DF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {805D143F-4925-408E-8E98-71A5B106E6DF}.Debug|x64.ActiveCfg = Debug|x64 + {805D143F-4925-408E-8E98-71A5B106E6DF}.Debug|x64.Build.0 = Debug|x64 + {805D143F-4925-408E-8E98-71A5B106E6DF}.Debug|x86.ActiveCfg = Debug|Win32 + {805D143F-4925-408E-8E98-71A5B106E6DF}.Debug|x86.Build.0 = Debug|Win32 + {805D143F-4925-408E-8E98-71A5B106E6DF}.Release|x64.ActiveCfg = Release|x64 + {805D143F-4925-408E-8E98-71A5B106E6DF}.Release|x64.Build.0 = Release|x64 + {805D143F-4925-408E-8E98-71A5B106E6DF}.Release|x86.ActiveCfg = Release|Win32 + {805D143F-4925-408E-8E98-71A5B106E6DF}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {571BAF3D-8401-47F0-A299-8FE9B367693D} + EndGlobalSection +EndGlobal diff --git a/Lab_1/Lab_1/Lab_1.cpp b/Lab_1/Lab_1/Lab_1.cpp new file mode 100644 index 0000000..8144d60 --- /dev/null +++ b/Lab_1/Lab_1/Lab_1.cpp @@ -0,0 +1,448 @@ +#include +#include +#include "resource.h" + +#define _USE_MATH_DEFINES +#include + +#define WM_LOAD_SPRITE WM_USER +#define WM_UPDATE_SPRITE (WM_USER + 1) +#define WM_LOAD_DEFAULT_SPRITE (WM_USER + 2) + +constexpr auto WINDOW_NAME = "Lab_1"; +constexpr auto SPRITE_STEP = 10; +constexpr auto SPRITE_DEGREE_ROTATE_STEP = 15; + +constexpr auto VK_W = 0x57; +constexpr auto VK_A = 0x41; +constexpr auto VK_S = 0x53; +constexpr auto VK_D = 0x44; + +constexpr auto VK_Q = 0x51; +constexpr auto VK_E = 0x45; + +constexpr auto VK_L = 0x4c; + +ATOM RegisterWindowClass(HINSTANCE); +BOOL InitWindowInstance(HINSTANCE, int); +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); + +bool PostLoadDefaultSpriteMessage(HWND hWnd, HINSTANCE hInstance) +{ + return PostMessage(hWnd, WM_LOAD_DEFAULT_SPRITE, NULL, (LPARAM)hInstance); +} + +bool PostLoadSpriteMessage(HWND hWnd) +{ + return PostMessage(hWnd, WM_LOAD_SPRITE, NULL, NULL); +} + + +bool PostUpdateSpriteMessage(HWND hWnd) +{ + return PostMessage(hWnd, WM_UPDATE_SPRITE, NULL, NULL); +} + +SIZE GetClientWindowSize(HWND hWnd) +{ + RECT windowRectangle; + GetClientRect(hWnd, &windowRectangle); + + SIZE windowSize; + windowSize.cx = windowRectangle.right - windowRectangle.left; + windowSize.cy = windowRectangle.bottom - windowRectangle.top; + + return windowSize; +} + +SIZE GetSpriteSize(HBITMAP hBitmap) +{ + BITMAP sprite; + GetObject(hBitmap, sizeof(BITMAP), &sprite); + + SIZE spriteSize; + spriteSize.cx = sprite.bmWidth; + spriteSize.cy = sprite.bmHeight; + + return spriteSize; +} + +int FillWindowWithColor(HWND hWnd, COLORREF color) +{ + RECT windowRectangle; + GetClientRect(hWnd, &windowRectangle); + + HDC windowDC = GetDC(hWnd); + HBRUSH hBrush = CreateSolidBrush(color); + + int result = FillRect(windowDC, &windowRectangle, hBrush); + + DeleteObject(hBrush); + ReleaseDC(hWnd, windowDC); + + return result; +} + +COORD CreateNewSpritePosition(COORD spritePosition, COORD spriteSteps, HWND hWnd, HBITMAP sprite) +{ + SIZE windowSize = GetClientWindowSize(hWnd); + SIZE spriteSize = GetSpriteSize(sprite); + + COORD newSpritePosition; + + newSpritePosition.X = spritePosition.X + spriteSteps.X; + if (newSpritePosition.X < 0) + { + newSpritePosition.X = 0; + } + else if (newSpritePosition.X + spriteSize.cx > windowSize.cx) + { + newSpritePosition.X = (SHORT)(windowSize.cx - spriteSize.cx); + } + + newSpritePosition.Y = spritePosition.Y + spriteSteps.Y; + if (newSpritePosition.Y < 0) + { + newSpritePosition.Y = 0; + } + else if (newSpritePosition.Y + spriteSize.cy > windowSize.cy) + { + newSpritePosition.Y = (SHORT)(windowSize.cy - spriteSize.cy); + } + + return newSpritePosition; +} + +COORD CreateUpSteps() +{ + COORD steps; + steps.X = 0; + steps.Y = -SPRITE_STEP; + + return steps; +} + +COORD CreateRightSteps() +{ + COORD steps; + steps.X = SPRITE_STEP; + steps.Y = 0; + + return steps; +} + +COORD CreateDownSteps() +{ + COORD steps; + steps.X = 0; + steps.Y = SPRITE_STEP; + + return steps; +} + +COORD CreateLeftSteps() +{ + COORD steps; + steps.X = -SPRITE_STEP; + steps.Y = 0; + + return steps; +} + +XFORM GetMovementXform(COORD coordinates) +{ + XFORM xForm; + xForm.eM11 = 1; + xForm.eM12 = 0; + xForm.eM21 = 0; + xForm.eM22 = 1; + xForm.eDx = coordinates.X; + xForm.eDy = coordinates.Y; + + return xForm; +} + +XFORM GetRotationXform(short degreeAngle) +{ + XFORM xForm; + FLOAT radAngle = (FLOAT)(M_PI * degreeAngle / 180); + FLOAT angleSin = sin(radAngle); + FLOAT angleCos = cos(radAngle); + + xForm.eM11 = angleCos; + xForm.eM12 = angleSin; + xForm.eM21 = -angleSin; + xForm.eM22 = angleCos; + xForm.eDx = 0; + xForm.eDy = 0; + + return xForm; +} + +LRESULT LoadSprite(HWND hWnd, HBITMAP& sprite) +{ + char fileName[MAX_PATH] = { NULL }; + + OPENFILENAME openFileName; + openFileName.lStructSize = sizeof(OPENFILENAME); + openFileName.hwndOwner = hWnd; + openFileName.hInstance = NULL; + openFileName.lpstrFilter = "Images\0*.bmp;*.gif;*.jpeg;*.png;\0\0"; + openFileName.lpstrCustomFilter = NULL; + openFileName.nFilterIndex = 1; + openFileName.lpstrFile = fileName; + openFileName.nMaxFile = sizeof(fileName); + openFileName.lpstrFileTitle = NULL; + openFileName.lpstrInitialDir = NULL; + openFileName.lpstrTitle = "Select sprite"; + openFileName.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; + openFileName.lpstrDefExt = NULL; + + if (GetOpenFileName(&openFileName)) + { + int fileNameLength = MultiByteToWideChar(CP_ACP, 0, fileName, -1, NULL, 0); + WCHAR* wideCharFileName = new WCHAR[MultiByteToWideChar(CP_ACP, 0, fileName, -1, NULL, 0)]; + MultiByteToWideChar(CP_ACP, 0, fileName, -1, wideCharFileName, fileNameLength); + + Gdiplus::GdiplusStartupInput gdiplusStartupInput; + ULONG_PTR gdiplusToken; + GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); + + Gdiplus::Bitmap* sourceImage = Gdiplus::Bitmap::FromFile(wideCharFileName); + HBITMAP hBitmap; + Gdiplus::Color imageBackgroundColor; + imageBackgroundColor.SetFromCOLORREF(GetSysColor(COLOR_WINDOW)); + Gdiplus::Status bitmapStatus = sourceImage->GetHBITMAP(imageBackgroundColor, &hBitmap); + + Gdiplus::GdiplusShutdown(gdiplusToken); + + if (bitmapStatus != Gdiplus::Ok) + return -1; + + SIZE windowSize = GetClientWindowSize(hWnd), spriteSize = GetSpriteSize(hBitmap); + if ((windowSize.cx < spriteSize.cx) || (windowSize.cy < spriteSize.cy)) + return -2; + + sprite = hBitmap; + + return 0; + } + + return 1; +} + +bool PutSpriteOnWindow(HWND hWnd, HBITMAP sprite, COORD coordinates, short angle) +{ + HDC windowDC = GetDC(hWnd); + HDC spriteDC = CreateCompatibleDC(windowDC); + + HGDIOBJ oldObject = SelectObject(spriteDC, sprite); + SIZE bitmapSize = GetSpriteSize(sprite); + + XFORM xForm; + int graphicsMode = SetGraphicsMode(windowDC, GM_ADVANCED); + + xForm = GetMovementXform(coordinates); + SetWorldTransform(windowDC, &xForm); + + COORD transformationCenter; + transformationCenter.X = (SHORT)(-(coordinates.X + bitmapSize.cx / 2)); + transformationCenter.Y = (SHORT)(-(coordinates.Y + bitmapSize.cy / 2)); + xForm = GetMovementXform(transformationCenter); + ModifyWorldTransform(windowDC, &xForm, MWT_RIGHTMULTIPLY); + + xForm = GetRotationXform(angle); + ModifyWorldTransform(windowDC, &xForm, MWT_RIGHTMULTIPLY); + + transformationCenter.X = -transformationCenter.X; + transformationCenter.Y = -transformationCenter.Y; + xForm = GetMovementXform(transformationCenter); + ModifyWorldTransform(windowDC, &xForm, MWT_RIGHTMULTIPLY); + + bool result = BitBlt(windowDC, 0, 0, bitmapSize.cx, bitmapSize.cy, spriteDC, 0, 0, SRCCOPY); + ModifyWorldTransform(windowDC, NULL, MWT_IDENTITY); + SetGraphicsMode(windowDC, graphicsMode); + + SelectObject(spriteDC, oldObject); + DeleteDC(spriteDC); + ReleaseDC(hWnd, windowDC); + + return result; +} + +int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR cmdLine, int cmdShowMode) +{ + UNREFERENCED_PARAMETER(hPrevInstance); + UNREFERENCED_PARAMETER(cmdLine); + + MSG msg; + + RegisterWindowClass(hInstance); + + if (!InitWindowInstance(hInstance, cmdShowMode)) + return FALSE; + + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return (int)msg.wParam; +} + +ATOM RegisterWindowClass(HINSTANCE hInstance) +{ + WNDCLASSEX windowClassEx; + + windowClassEx.cbSize = sizeof(WNDCLASSEX); + windowClassEx.style = CS_HREDRAW | CS_VREDRAW; + windowClassEx.lpfnWndProc = WndProc; + windowClassEx.cbClsExtra = 0; + windowClassEx.cbWndExtra = 0; + windowClassEx.hInstance = hInstance; + windowClassEx.hIcon = LoadIcon(0, IDI_WINLOGO);; + windowClassEx.hCursor = LoadCursor(0, IDC_ARROW); + windowClassEx.hbrBackground = (HBRUSH)(COLOR_WINDOW); + windowClassEx.lpszMenuName = 0; + windowClassEx.lpszClassName = WINDOW_NAME; + windowClassEx.hIconSm = 0; + + return RegisterClassEx(&windowClassEx); +} + +BOOL InitWindowInstance(HINSTANCE hInstance, int cmdShowMode) +{ + HWND hWnd; + + hWnd = CreateWindow(WINDOW_NAME, WINDOW_NAME, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); + + if (!hWnd) + return FALSE; + + ShowWindow(hWnd, cmdShowMode); + UpdateWindow(hWnd); + + // Load default sprite. + PostLoadDefaultSpriteMessage(hWnd, hInstance); + + return TRUE; +} + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + static HBITMAP sprite = NULL; + static COORD spritePosition = { 0, 0 }; + static short angle = 0; + + switch (message) + { + case WM_LOAD_DEFAULT_SPRITE: + if ((sprite = LoadBitmap((HINSTANCE)lParam, MAKEINTRESOURCE(IDB_SPRITE))) != NULL) + PostUpdateSpriteMessage(hWnd); + break; + case WM_LOAD_SPRITE: + switch (LoadSprite(hWnd, sprite)) + { + case 0: + spritePosition = { 0, 0 }; + angle = 0; + PostUpdateSpriteMessage(hWnd); + break; + case -1: + MessageBox(hWnd, "An unknown error occurred while loading the sprite.", + "Unknown error", MB_OK | MB_ICONERROR); + break; + case -2: + MessageBox(hWnd, "The sprite does not fit into the window.", + "Large sprite size", MB_OK | MB_ICONERROR); + break; + default: + break; + } + break; + case WM_UPDATE_SPRITE: + FillWindowWithColor(hWnd, GetSysColor(COLOR_WINDOW)); + PutSpriteOnWindow(hWnd, sprite, spritePosition, angle); + break; + case WM_KEYDOWN: + switch (wParam) + { + case VK_UP: + case VK_W: + spritePosition = CreateNewSpritePosition(spritePosition, CreateUpSteps(), hWnd, sprite); + PostUpdateSpriteMessage(hWnd); + break; + case VK_RIGHT: + case VK_D: + spritePosition = CreateNewSpritePosition(spritePosition, CreateRightSteps(), hWnd, sprite); + PostUpdateSpriteMessage(hWnd); + break; + case VK_DOWN: + case VK_S: + spritePosition = CreateNewSpritePosition(spritePosition, CreateDownSteps(), hWnd, sprite); + PostUpdateSpriteMessage(hWnd); + break; + case VK_LEFT: + case VK_A: + spritePosition = CreateNewSpritePosition(spritePosition, CreateLeftSteps(), hWnd, sprite); + PostUpdateSpriteMessage(hWnd); + break; + case VK_Q: + angle -= SPRITE_DEGREE_ROTATE_STEP; + PostUpdateSpriteMessage(hWnd); + break; + case VK_E: + angle += SPRITE_DEGREE_ROTATE_STEP; + PostUpdateSpriteMessage(hWnd); + break; + case VK_L: + PostLoadSpriteMessage(hWnd); + break; + default: + break; + } + break; + case WM_MOUSEWHEEL: + if (GET_KEYSTATE_WPARAM(wParam) == MK_SHIFT) + { + if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) + { + spritePosition = CreateNewSpritePosition(spritePosition, CreateLeftSteps(), hWnd, sprite); + PostUpdateSpriteMessage(hWnd); + } + else + { + spritePosition = CreateNewSpritePosition(spritePosition, CreateRightSteps(), hWnd, sprite); + PostUpdateSpriteMessage(hWnd); + } + } + else + { + if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) + { + spritePosition = CreateNewSpritePosition(spritePosition, CreateUpSteps(), hWnd, sprite); + PostUpdateSpriteMessage(hWnd); + } + else + { + spritePosition = CreateNewSpritePosition(spritePosition, CreateDownSteps(), hWnd, sprite); + PostUpdateSpriteMessage(hWnd); + } + } + break; + case WM_ERASEBKGND: + return 1; + case WM_SIZE: + PostUpdateSpriteMessage(hWnd); + return DefWindowProc(hWnd, message, wParam, lParam); + case WM_DESTROY: + PostQuitMessage(0); + return 0; + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + + return 0; +} diff --git a/Lab_1/Lab_1/Lab_1.rc b/Lab_1/Lab_1/Lab_1.rc new file mode 100644 index 0000000..b0b820c --- /dev/null +++ b/Lab_1/Lab_1/Lab_1.rc @@ -0,0 +1,69 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_SPRITE BITMAP "sprite.bmp" + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Lab_1/Lab_1/Lab_1.vcxproj b/Lab_1/Lab_1/Lab_1.vcxproj new file mode 100644 index 0000000..646cc84 --- /dev/null +++ b/Lab_1/Lab_1/Lab_1.vcxproj @@ -0,0 +1,157 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {805d143f-4925-408e-8e98-71a5b106e6df} + Lab1 + 10.0 + + + + Application + true + v142 + MultiByte + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Windows + true + gdiplus.lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Lab_1/Lab_1/Lab_1.vcxproj.filters b/Lab_1/Lab_1/Lab_1.vcxproj.filters new file mode 100644 index 0000000..2cf59e0 --- /dev/null +++ b/Lab_1/Lab_1/Lab_1.vcxproj.filters @@ -0,0 +1,37 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + Header Files + + + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/Lab_1/Lab_1/resource.h b/Lab_1/Lab_1/resource.h new file mode 100644 index 0000000..47f81d8 --- /dev/null +++ b/Lab_1/Lab_1/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Lab_1.rc +// +#define IDB_SPRITE 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Lab_1/Lab_1/sprite.bmp b/Lab_1/Lab_1/sprite.bmp new file mode 100644 index 0000000..6e6fb38 Binary files /dev/null and b/Lab_1/Lab_1/sprite.bmp differ diff --git a/Lab_1/README.md b/Lab_1/README.md new file mode 100644 index 0000000..8fc1228 --- /dev/null +++ b/Lab_1/README.md @@ -0,0 +1,9 @@ +# Программа, позволяющая передвигать с помощью клавиатуры и мыши спрайт + +### Задание +Разработать программу, позволяющую передвигать с помощью клавиатуры и мыши спрайт (окрашенный прямоугольник или элип) внутри рабочей области окна. Обеспечить работу колесика мыши. Прокручивание двигает спрайт по вертикали. С удерживаемой клавишей *Shift* прокручивание колесика двигает спрайт по горизонтали. Реализовать возможность замены спрайта на картинку с непрямоугольным контуром (PNG картинка). Придать спрайту движение с откосом от границ окна. +### Важные моменты +При риализации использовались: + - Событийная архитектура Windows-приложений + - Механизм обработки сообщений + - Механизм перерисовки окна \ No newline at end of file