1+ #include " pch.h"
2+
3+ namespace com = ::Windows::Storage::Streams;
4+
5+ using namespace winrt ;
6+ using namespace winrt ::Windows::ApplicationModel::Activation;
7+ using namespace winrt ::Windows::Foundation;
8+ using namespace winrt ::Windows::Graphics::Imaging;
9+ using namespace winrt ::Windows::Storage;
10+ using namespace winrt ::Windows::Storage::Pickers;
11+ using namespace winrt ::Windows::Storage::Streams;
12+ using namespace winrt ::Windows::UI;
13+ using namespace winrt ::Windows::UI::Core;
14+ using namespace winrt ::Windows::UI::Xaml;
15+ using namespace winrt ::Windows::UI::Xaml::Controls;
16+ using namespace winrt ::Windows::UI::Xaml::Media;
17+ using namespace winrt ::Windows::UI::Xaml::Media::Imaging;
18+
19+ struct App : ApplicationT<App>
20+ {
21+ public:
22+
23+ // The Click and Rendering methods be default use IInspectable from winrt::ABI. They should use the version from winrt::Windows.
24+ void RenderingHandler (const IInspectable&, const IInspectable& args)
25+ {
26+ // Once the image is loaded successfully, then we can dynamically update the WritableBitmap.
27+ if (m_imageLoaded)
28+ {
29+ auto renderingArgs = args.as <IRenderingEventArgs>();
30+
31+ // Get elapsed time
32+ TimeSpan timeSpan = renderingArgs.RenderingTime ();
33+
34+ // Calculate twistAngle from -180 to 180 degrees
35+ double t = (timeSpan.count () % m_cycleDuration) / (double )m_cycleDuration;
36+ double tprime = 2 * (t < 0.5 ? t : 1 - t);
37+ double twistAngle = 2 * (tprime - 0.5 ) * 3.14159 ;
38+
39+ for (int yDst = 0 ; yDst < m_height; yDst++)
40+ {
41+ for (int xDst = 0 ; xDst < m_width; xDst++)
42+ {
43+ // Calculate length of point to center and angle
44+ int xDelta = xDst - m_xCenter;
45+ int yDelta = yDst - m_yCenter;
46+ double distanceToCenter = sqrt (xDelta * xDelta +
47+ yDelta * yDelta);
48+ double angleClockwise = atan2 (yDelta, xDelta);
49+
50+ // Calculation angle of rotation for twisting effect
51+ double xEllipse = m_xCenter * cos (angleClockwise);
52+ double yEllipse = m_yCenter * sin (angleClockwise);
53+ double radius = sqrt (xEllipse * xEllipse +
54+ yEllipse * yEllipse);
55+ double fraction = max (0.0 , 1 - distanceToCenter / radius);
56+ double twist = fraction * twistAngle;
57+
58+ // Calculate the source pixel for each destination pixel
59+ int xSrc = (int )(m_xCenter + (xDst - m_xCenter) * cos (twist)
60+ - (yDst - m_yCenter) * sin (twist));
61+ int ySrc = (int )(m_yCenter + (xDst - m_xCenter) * sin (twist)
62+ + (yDst - m_yCenter) * cos (twist));
63+ xSrc = max (0 , min (m_width - 1 , xSrc));
64+ ySrc = max (0 , min (m_height - 1 , ySrc));
65+
66+ // Calculate the indices
67+ int iDst = 4 * (yDst * m_width + xDst);
68+ int iSrc = 4 * (ySrc * m_width + xSrc);
69+
70+ // Transfer the pixel bytes
71+ m_destPixels[iDst++] = m_srcPixels[iSrc++];
72+ m_destPixels[iDst++] = m_srcPixels[iSrc++];
73+ m_destPixels[iDst++] = m_srcPixels[iSrc++];
74+ m_destPixels[iDst] = m_srcPixels[iSrc];
75+ }
76+ }
77+
78+ // Invalidate the bitmap
79+ m_writeableBitmap.Invalidate ();
80+ }
81+ }
82+
83+ std::future<void > ClickHandler (const IInspectable&, const IInspectable&)
84+ {
85+ FileOpenPicker picker;
86+ picker.ViewMode (PickerViewMode::Thumbnail);
87+ picker.SuggestedStartLocation (PickerLocationId::PicturesLibrary);
88+
89+ auto fileTypeFilters = picker.FileTypeFilter ();
90+ fileTypeFilters.Append (L" .jpg" );
91+ fileTypeFilters.Append (L" .jpeg" );
92+ fileTypeFilters.Append (L" .png" );
93+
94+ StorageFile selectedFile = await picker.PickSingleFileAsync ();
95+ if (!selectedFile)
96+ {
97+ return ;
98+ }
99+
100+ IRandomAccessStream imageStream = await selectedFile.OpenAsync (FileAccessMode::Read);
101+ BitmapDecoder decoder = await BitmapDecoder::CreateAsync (imageStream);
102+ BitmapFrame frame = await decoder.GetFrameAsync (0 );
103+
104+ m_width = frame.PixelWidth ();
105+ m_height = frame.PixelHeight ();
106+ m_xCenter = m_width / 2 ;
107+ m_yCenter = m_height / 2 ;
108+
109+ PixelDataProvider provider = await frame.GetPixelDataAsync ();
110+ m_srcPixels = provider.DetachPixelData ();
111+
112+ // Manipulation of the bitmap objects must be done on the UI thread
113+ m_uiDispatcher.RunAsync (CoreDispatcherPriority::Normal, [this ]() {
114+
115+ m_writeableBitmap = WriteableBitmap (m_width, m_height);
116+ m_image.Source (m_writeableBitmap);
117+
118+ IBuffer buffer = m_writeableBitmap.PixelBuffer ();
119+ com_ptr<com::IBufferByteAccess> byteAccess = buffer.as <com::IBufferByteAccess>();
120+ byteAccess->Buffer (&m_destPixels);
121+
122+ m_imageLoaded = true ;
123+ });
124+ }
125+
126+ void OnLaunched (LaunchActivatedEventArgs const &)
127+ {
128+ CompositionTarget::Rendering ({ this , &App::RenderingHandler });
129+
130+ m_uiDispatcher = CoreWindow::GetForCurrentThread ().Dispatcher ();
131+
132+ StackPanel panel;
133+ panel.Orientation (Orientation::Vertical);
134+
135+ Button pickerButton;
136+ TextBlock pickerButtonText;
137+ pickerButtonText.Text (L" Choose image..." );
138+ pickerButton.Content (pickerButtonText);
139+ pickerButton.Click ({ this , &App::ClickHandler });
140+
141+ m_image.Height (200.0 );
142+ m_image.Width (200.0 );
143+
144+ auto panelContent = panel.Children ();
145+ panelContent.Append (pickerButton);
146+ panelContent.Append (m_image);
147+
148+ Window window = Window::Current ();
149+ window.Content (panel);
150+ window.Activate ();
151+ }
152+
153+ private:
154+ CoreDispatcher m_uiDispatcher{ nullptr };
155+ Image m_image;
156+
157+ int m_width{ 0 };
158+ int m_height{ 0 };
159+ int m_xCenter{ 0 };
160+ int m_yCenter{ 0 };
161+
162+ byte* m_destPixels{ nullptr };
163+ com_array<uint8_t > m_srcPixels;
164+ WriteableBitmap m_writeableBitmap{ nullptr };
165+
166+ bool m_imageLoaded{ false };
167+
168+ static const int64_t m_cycleDuration{ 30000000 };
169+ };
170+
171+ int __stdcall wWinMain (HINSTANCE, HINSTANCE, PWSTR, int )
172+ {
173+ Application::Start ([](auto &&)
174+ {
175+ make<App>();
176+ });
177+ }
0 commit comments