■ ListView 클래스에서 ListViewItem 객체에 조명 효과를 사용하는 방법을 보여준다.
※ 비주얼 스튜디오에서 TestProject(Unpackaged) 모드로 빌드한다.
※ TestProject.csproj 프로젝트 파일에서 WindowsPackageType 태그를 None으로 추가했다.
[TestLibrary 프로젝트]
▶ BitmapDrawer.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
using System; using System.Threading.Tasks; using Windows.Foundation; using Windows.Graphics.Imaging; using Windows.Storage; using Windows.Storage.Streams; using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.UI.Composition; using Microsoft.UI.Composition; using Windows.UI; namespace TestLibrary; /// <summary> /// 비트맵 그리기 /// </summary> public class BitmapDrawer : IContentDrawer { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// URI /// </summary> private readonly Uri uri; /// <summary> /// 시간 효과 로드 핸들러 /// </summary> private LoadTimeEffectHandler loadTimeEffectHandler; /// <summary> /// 저장소 파일 /// </summary> private StorageFile storageFile; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region URI - URI /// <summary> /// URI /// </summary> public Uri URI { get { return this.uri; } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - BitmapDrawer(uri, loadTimeEffectHandler) /// <summary> /// 생성자 /// </summary> /// <param name="uri">URI</param> /// <param name="loadTimeEffectHandler">시간 효과 로드 핸들러</param> public BitmapDrawer(Uri uri, LoadTimeEffectHandler loadTimeEffectHandler) { this.uri = uri; this.loadTimeEffectHandler = loadTimeEffectHandler; } #endregion #region 생성자 - BitmapDrawer(storageFile, loadTimeEffectHandler) /// <summary> /// 생성자 /// </summary> /// <param name="storageFile">저장소 파일</param> /// <param name="loadTimeEffectHandler">시간 효과 로드 핸들러</param> public BitmapDrawer(StorageFile storageFile, LoadTimeEffectHandler loadTimeEffectHandler) { this.storageFile = storageFile; this.loadTimeEffectHandler = loadTimeEffectHandler; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 그리기 (비동기) - DrawAsync(device, drawingLock, surface, size) /// <summary> /// 그리기 (비동기) /// </summary> /// <param name="device">합성 그래픽스 디바이스</param> /// <param name="drawingLock">드로잉 잠금</param> /// <param name="surface">합성 드로잉 서피스</param> /// <param name="size">크기</param> /// <returns>태스크</returns> public async Task DrawAsync(CompositionGraphicsDevice device, object drawingLock, CompositionDrawingSurface surface, Size size) { CanvasDevice canvasDevice = CanvasComposition.GetCanvasDevice(device); CanvasBitmap canvasBitmap; if(this.storageFile != null) { SoftwareBitmap softwareBitmap = await LoadFromFileAsync(this.storageFile); canvasBitmap = CanvasBitmap.CreateFromSoftwareBitmap(canvasDevice, softwareBitmap); } else { canvasBitmap = await CanvasBitmap.LoadAsync(canvasDevice, this.uri); } Size canvasBitmapSize = canvasBitmap.Size; lock(drawingLock) { Size surfaceSize = size; if(surface.Size != size || surface.Size == new Size(0, 0)) { CanvasComposition.Resize(surface, canvasBitmapSize); surfaceSize = canvasBitmapSize; } if(this.loadTimeEffectHandler != null) { this.loadTimeEffectHandler(surface, canvasBitmap, device); } else { using CanvasDrawingSession canvasDrawingSession = CanvasComposition.CreateDrawingSession(surface); canvasDrawingSession.Clear(Color.FromArgb(0, 0, 0, 0)); canvasDrawingSession.DrawImage ( canvasBitmap, new Rect(0, 0, surfaceSize.Width , surfaceSize.Height ), new Rect(0, 0, canvasBitmapSize.Width, canvasBitmapSize.Height) ); } } } #endregion ////////////////////////////////////////////////////////////////////////////////////////// Private #region 파일에서 로드하기 (비동기) - LoadFromFileAsync(storageFile) /// <summary> /// 파일에서 로드하기 (비동기) /// </summary> /// <param name="storageFile">저장소 파일</param> /// <returns>소프트웨어 비트맵 태스크</returns> private async Task<SoftwareBitmap> LoadFromFileAsync(StorageFile storageFile) { SoftwareBitmap softwareBitmap; using(IRandomAccessStream randomAccessStream = await storageFile.OpenAsync(FileAccessMode.Read)) { BitmapDecoder bitmapDecoder = await BitmapDecoder.CreateAsync(randomAccessStream); softwareBitmap = await bitmapDecoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Premultiplied); } return softwareBitmap; } #endregion } |
▶ CircleDrawer.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
using System.Numerics; using System.Threading.Tasks; using Windows.Foundation; using Windows.UI; using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.UI.Composition; using Microsoft.UI; using Microsoft.UI.Composition; namespace TestLibrary; /// <summary> /// 원 그리기 /// </summary> public class CircleDrawer : IContentDrawer { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 반경 /// </summary> private float radius; /// <summary> /// 색상 /// </summary> private Color color; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 반경 - Radius /// <summary> /// 반경 /// </summary> public float Radius { get { return this.radius; } } #endregion #region 색상 - Color /// <summary> /// 색상 /// </summary> public Color Color { get { return this.color; } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - CircleDrawer(radius, color) /// <summary> /// 생성자 /// </summary> /// <param name="radius">반경</param> /// <param name="color">색상</param> public CircleDrawer(float radius, Color color) { this.radius = radius; this.color = color; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 그리기 (비동기) - DrawAsync(device, drawingLock, surface, size) /// <summary> /// 그리기 (비동기) /// </summary> /// <param name="device">합성 그래픽스 디바이스</param> /// <param name="drawingLock">드로잉 잠금</param> /// <param name="surface">합성 드로잉 서피스</param> /// <param name="size">크기</param> /// <returns>태스크</returns> public async Task DrawAsync(CompositionGraphicsDevice device, object drawingLock, CompositionDrawingSurface surface, Size size) { using CanvasDrawingSession session = CanvasComposition.CreateDrawingSession(surface); session.Clear(Colors.Transparent); session.FillCircle(new Vector2(this.radius, this.radius), this.radius, this.color); await Task.CompletedTask; } #endregion } |
▶ TextDrawer.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
using System.Threading.Tasks; using Windows.Foundation; using Windows.UI; using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.Text; using Microsoft.Graphics.Canvas.UI.Composition; using Microsoft.UI.Composition; namespace TestLibrary; /// <summary> /// 텍스트 그리기 /// </summary> public class TextDrawer : IContentDrawer { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 텍스트 /// </summary> private string text; /// <summary> /// 캔버스 텍스트 포맷 /// </summary> private CanvasTextFormat canvasTextFormat; /// <summary> /// 텍스트 색상 /// </summary> private Color textColor; /// <summary> /// 배경 색상 /// </summary> private Color backgroundColor; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - TextDrawer(text, canvasTextFormat, textColor, backgroundColor) /// <summary> /// 생성자 /// </summary> /// <param name="text">텍스트</param> /// <param name="canvasTextFormat">캔버스 텍스트 포맷</param> /// <param name="textColor">텍스트 색상</param> /// <param name="backgroundColor">배경 색상</param> public TextDrawer(string text, CanvasTextFormat canvasTextFormat, Color textColor, Color backgroundColor) { this.text = text; this.canvasTextFormat = canvasTextFormat; this.textColor = textColor; this.backgroundColor = backgroundColor; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 그리기 (비동기) - DrawAsync(device, drawingLock, surface, size) /// <summary> /// 그리기 (비동기) /// </summary> /// <param name="device">혼합 그래픽스 디바이스</param> /// <param name="drawingLock">드로잉 잠금</param> /// <param name="surface">혼합 드로잉 서피스</param> /// <param name="size">크기</param> /// <returns>태스크</returns> public async Task DrawAsync(CompositionGraphicsDevice device, object drawingLock, CompositionDrawingSurface surface, Size size) { using(CanvasDrawingSession session = CanvasComposition.CreateDrawingSession(surface)) { session.Clear(this.backgroundColor); session.DrawText ( this.text, new Rect(0, 0, surface.Size.Width, surface.Size.Height), this.textColor, this.canvasTextFormat ); } await Task.CompletedTask; } #endregion } |
▶ DependencyObjectExtension.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
using System; using System.Collections.Generic; using System.Linq; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Media; namespace TestLibrary; /// <summary> /// 비주얼 트리 확장자 /// </summary> public static class DependencyObjectExtension { //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Public #region 첫번째 자손 구하기 - GetFirstDescendantOfType<TDescendant>(startDependencyObject) /// <summary> /// 첫번째 자손 구하기 /// </summary> /// <typeparam name="TDescendant">자손 타입</typeparam> /// <param name="startDependencyObject">시작 의존 객체</param> /// <returns>자손</returns> public static TDescendant GetFirstDescendantOfType<TDescendant>(this DependencyObject startDependencyObject) where TDescendant : DependencyObject { return startDependencyObject.GetDescendantEnumerableOfType<TDescendant>().FirstOrDefault(); } #endregion #region 자손 열거 가능형 구하기 - GetDescendantEnumerableOfType<TDescendant>(startDependencyObject) /// <summary> /// 자손 열거 가능형 구하기 /// </summary> /// <typeparam name="TDescendant">자손 타입</typeparam> /// <param name="startDependencyObject">시작 의존 객체</param> /// <returns>자손 열거 가능형</returns> public static IEnumerable<TDescendant> GetDescendantEnumerableOfType<TDescendant>(this DependencyObject startDependencyObject) where TDescendant : DependencyObject { return startDependencyObject.GetDescendantEnumerable().OfType<TDescendant>(); } #endregion #region 자손 열거 가능형 구하기 - GetDescendantEnumerable(startDependencyObject) /// <summary> /// 자손 열거 가능형 구하기 /// </summary> /// <param name="startDependencyObject">시작 의존 객체</param> /// <returns>자손 열거 가능형</returns> public static IEnumerable<DependencyObject> GetDescendantEnumerable(this DependencyObject startDependencyObject) { Queue<DependencyObject> queue = new Queue<DependencyObject>(); int count1 = VisualTreeHelper.GetChildrenCount(startDependencyObject); for(int i = 0; i < count1; i++) { DependencyObject childDependencyObject = VisualTreeHelper.GetChild(startDependencyObject, i); yield return childDependencyObject; queue.Enqueue(childDependencyObject); } while(queue.Count > 0) { DependencyObject parentDependencyObject = queue.Dequeue(); int count2 = VisualTreeHelper.GetChildrenCount(parentDependencyObject); for(int i = 0; i < count2; i++) { DependencyObject childDependencyObject = VisualTreeHelper.GetChild(parentDependencyObject, i); yield return childDependencyObject; queue.Enqueue(childDependencyObject); } } } #endregion #region 첫번째 조상 구하기 - GetFirstAncestorOfType<TAncestor>(startDependencyObject) /// <summary> /// 첫번째 조상 구하기 /// </summary> /// <typeparam name="TAncestor">조상 타입</typeparam> /// <param name="startDependencyObject">시작 의존 객체</param> /// <returns>첫번째 조상</returns> public static TAncestor GetFirstAncestorOfType<TAncestor>(this DependencyObject startDependencyObject) where TAncestor : DependencyObject { return startDependencyObject.GetAncestorEnumerableOfType<TAncestor>().FirstOrDefault(); } #endregion #region 조상 열거 가능형 구하기 - GetAncestorEnumerableOfType<TAncestor>(startDependencyObject) /// <summary> /// 조상 열거 가능형 구하기 /// </summary> /// <typeparam name="TAncestor">조상 타입</typeparam> /// <param name="startDependencyObject">시작 의존 객체</param> /// <returns>조상 열거 가능형</returns> public static IEnumerable<TAncestor> GetAncestorEnumerableOfType<TAncestor>(this DependencyObject startDependencyObject) where TAncestor : DependencyObject { return startDependencyObject.GetAncestorEnumerable().OfType<TAncestor>(); } #endregion #region 조상 열거 가능형 구하기 - GetAncestorEnumerable(startDependencyObject) /// <summary> /// 조상 열거 가능형 구하기 /// </summary> /// <param name="startDependencyObject">시작 의존 객체</param> /// <returns>조상 열거 가능형</returns> public static IEnumerable<DependencyObject> GetAncestorEnumerable(this DependencyObject startDependencyObject) { DependencyObject parentDependencyObject = VisualTreeHelper.GetParent(startDependencyObject); while(parentDependencyObject != null) { yield return parentDependencyObject; parentDependencyObject = VisualTreeHelper.GetParent(parentDependencyObject); } } #endregion #region 비주얼 트리 존재 여부 구하기 - IsInVisualTree(dependencyObject) /// <summary> /// 비주얼 트리 존재 여부 구하기 /// </summary> /// <param name="dependencyObject">의존 객체</param> /// <returns>비주얼 트리 존재 여부</returns> public static bool IsInVisualTree(this DependencyObject dependencyObject) { return Window.Current.Content != null && dependencyObject.GetAncestorEnumerable().Contains(Window.Current.Content); } #endregion } |
▶ FrameworkElementExtension.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
using System; using System.Linq; using Microsoft.UI.Xaml; using Windows.Foundation; namespace TestLibrary; /// <summary> /// 프레임워크 엘리먼트 확장자 /// </summary> public static class FrameworkElementExtension { //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Public #region 바운딩 사각형 구하기 - GetBoundingRectangle(frameworkElement, relativeToFrameworkElement) /// <summary> /// 바운딩 사각형 구하기 /// </summary> /// <param name="frameworkElement">프레임워크 엘리먼트</param> /// <param name="relativeToFrameworkElement">관련 프레임워크 엘리먼트</param> /// <returns>바운딩 사각형</returns> public static Rect GetBoundingRectangle(this FrameworkElement frameworkElement, FrameworkElement relativeToFrameworkElement = null) { if(relativeToFrameworkElement == null) { relativeToFrameworkElement = Window.Current.Content as FrameworkElement; } if(relativeToFrameworkElement == null) { throw new InvalidOperationException("Element not in visual tree."); } if(frameworkElement == relativeToFrameworkElement) { return new Rect(0, 0, relativeToFrameworkElement.ActualWidth, relativeToFrameworkElement.ActualHeight); } DependencyObject[] ancestorDependencyObjectArray = frameworkElement.GetAncestorEnumerable().ToArray(); if(!ancestorDependencyObjectArray.Contains(relativeToFrameworkElement)) { throw new InvalidOperationException("Element not in visual tree."); } Point point1 = frameworkElement.TransformToVisual(relativeToFrameworkElement).TransformPoint(new Point()); Point point2 = frameworkElement.TransformToVisual(relativeToFrameworkElement).TransformPoint(new Point(frameworkElement.ActualWidth, frameworkElement.ActualHeight)); return new Rect(point1, point2); } #endregion } |
▶ IContentDrawer.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
using System.Threading.Tasks; using Windows.Foundation; using Microsoft.UI.Composition; namespace TestLibrary; /// <summary> /// 컨텐트 그리기 /// </summary> public interface IContentDrawer { //////////////////////////////////////////////////////////////////////////////////////////////////// Method #region 그리기 (비동기) - DrawAsync(device, drawingLock, surface, size) /// <summary> /// 그리기 (비동기) /// </summary> /// <param name="device">합성 그래픽스 디바이스</param> /// <param name="drawingLock">그리기 잠금</param> /// <param name="surface">합성 드로잉 서피스</param> /// <param name="size">크기</param> /// <returns>태스크</returns> Task DrawAsync(CompositionGraphicsDevice device, object drawingLock, CompositionDrawingSurface surface, Size size); #endregion } |
▶ CompositionImage.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 |
using System; using System.Diagnostics; using System.IO; using System.Linq; using System.Numerics; using Windows.Foundation; using Microsoft.UI; using Microsoft.UI.Composition; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Hosting; using Microsoft.UI.Xaml.Media; namespace TestLibrary; /// <summary> /// 합성 이미지 /// </summary> public sealed class CompositionImage : Control { //////////////////////////////////////////////////////////////////////////////////////////////////// Event ////////////////////////////////////////////////////////////////////////////////////////// Public #region 이미지 오픈시 이벤트 - ImageOpened /// <summary> /// 이미지 오픈시 이벤트 /// </summary> public event RoutedEventHandler ImageOpened; #endregion #region 이미지 실패시 이벤트 - ImageFailed /// <summary> /// 이미지 실패시 이벤트 /// </summary> public event RoutedEventHandler ImageFailed; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 초기화 여부 /// </summary> private static bool _initialized; /// <summary> /// 디폴트 플레이스홀더 합성 브러시 /// </summary> private static CompositionBrush _defaultPlaceholderCompositionBrush; /// <summary> /// FADE OUT 스칼라 키 프레임 애니메이션 /// </summary> private static ScalarKeyFrameAnimation _fadeOutScalarKeyFrameAnimation; /// <summary> /// 스케일 벡터2 키 프레임 애니메이션 /// </summary> private static Vector2KeyFrameAnimation _scaleVector2KeyFrameAnimation; #endregion ////////////////////////////////////////////////////////////////////////////////////////// Instance //////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 언로드 여부 /// </summary> private bool unloaded; /// <summary> /// 합성자 /// </summary> private Compositor compositor; /// <summary> /// 스프라이트 비주얼 /// </summary> private SpriteVisual spriteVisual; /// <summary> /// URI /// </summary> private Uri uri; /// <summary> /// 관리 서피스 /// </summary> private ManagedSurface managedSurface; /// <summary> /// 합성 서피스 브러시 /// </summary> private CompositionSurfaceBrush compositionSurfaceBrush; /// <summary> /// 합성 브러시 /// </summary> private CompositionBrush compositionBrush; /// <summary> /// 시간 효과 로드 핸들러 /// </summary> private LoadTimeEffectHandler loadTimeEffectHandler; /// <summary> /// 합성 스트레치 /// </summary> private CompositionStretch compositionStretch; /// <summary> /// 디스패처 타이머 /// </summary> private DispatcherTimer dispatcherTimer; /// <summary> /// 플레이스홀더 지연 시간 범위 /// </summary> private TimeSpan placeholderDelayTimeSpan; /// <summary> /// 플레이스홀더 합성 브러시 /// </summary> private CompositionBrush placeholderCompositionBrush; /// <summary> /// 서피스 공유 여부 /// </summary> private bool sharedSurface; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 스트레치 - Stretch /// <summary> /// 스트레치 /// </summary> public Stretch Stretch { get { Stretch stretch; switch(this.compositionStretch) { case CompositionStretch.Fill : stretch = Stretch.Fill; break; case CompositionStretch.Uniform : stretch = Stretch.Uniform; break; case CompositionStretch.UniformToFill : stretch = Stretch.UniformToFill; break; default : stretch = Stretch.None; break; } return stretch; } set { CompositionStretch compositionStretch; switch(value) { case Stretch.Fill : compositionStretch = CompositionStretch.Fill; break; case Stretch.Uniform : compositionStretch = CompositionStretch.Uniform; break; case Stretch.UniformToFill : compositionStretch = CompositionStretch.UniformToFill; break; default : compositionStretch = CompositionStretch.None; break; } if(compositionStretch != this.compositionStretch) { this.compositionStretch = compositionStretch; if(this.compositionSurfaceBrush != null) { this.compositionSurfaceBrush.Stretch = compositionStretch; } } } } #endregion #region 소스 - Source /// <summary> /// 소스 /// </summary> public Uri Source { get { return this.uri; } set { if(this.uri != value) { this.uri = value; LoadSurface(); } } } #endregion #region 컨텐트 로드 여부 - IsContentLoaded /// <summary> /// 컨텐트 로드 여부 /// </summary> public bool IsContentLoaded { get { return this.managedSurface != null; } } #endregion #region 서피스 공유 여부 - SharedSurface /// <summary> /// 서피스 공유 여부 /// </summary> public bool SharedSurface { get { return this.sharedSurface; } set { this.sharedSurface = value; } } #endregion #region 시간 효과 로드 핸들러 - LoadTimeEffectHandler /// <summary> /// 시간 효과 로드 핸들러 /// </summary> public LoadTimeEffectHandler LoadTimeEffectHandler { get { return this.loadTimeEffectHandler; } set { this.loadTimeEffectHandler = value; } } #endregion #region 브러시 - Brush /// <summary> /// 브러시 /// </summary> public CompositionBrush Brush { get { return this.compositionBrush; } set { this.compositionBrush = value; UpdateBrush(); } } #endregion #region 플레이스홀더 브러시 - PlaceholderBrush /// <summary> /// 플레이스홀더 브러시 /// </summary> public CompositionBrush PlaceholderBrush { get { return this.placeholderCompositionBrush; } set { this.placeholderCompositionBrush = value; if(this.spriteVisual != null) { SpriteVisual loadingSpriteVisual = (SpriteVisual)this.spriteVisual.Children.FirstOrDefault(); if(loadingSpriteVisual != null) { loadingSpriteVisual.Brush = this.placeholderCompositionBrush; } } } } #endregion #region 서피스 브러시 - SurfaceBrush /// <summary> /// 서피스 브러시 /// </summary> public CompositionSurfaceBrush SurfaceBrush { get { return this.compositionSurfaceBrush; } } #endregion #region 스프라이트 비주얼 - SpriteVisual /// <summary> /// 스프라이트 비주얼 /// </summary> public SpriteVisual SpriteVisual { get { return this.spriteVisual; } } #endregion #region 플레이스홀더 지연 시간 - PlaceholderDelay /// <summary> /// 플레이스홀더 지연 시간 /// </summary> public TimeSpan PlaceholderDelay { get { return this.placeholderDelayTimeSpan; } set { this.placeholderDelayTimeSpan = value; } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - CompositionImage /// <summary> /// 생성자 /// </summary> public CompositionImage() { DefaultStyleKey = typeof(CompositionImage); Background = new SolidColorBrush(Colors.Transparent); this.compositionStretch = CompositionStretch.Uniform; Loading += Control_Loading; Unloaded += Control_Unloaded; SizeChanged += Control_SizeChanged; this.compositor = ElementCompositionPreview.GetElementVisual(this).Compositor; #region 정적 필드를 초기화한다. if (!_initialized) { _defaultPlaceholderCompositionBrush = this.compositor.CreateColorBrush(Colors.DarkGray); TimeSpan duration = TimeSpan.FromMilliseconds(1000); _fadeOutScalarKeyFrameAnimation = this.compositor.CreateScalarKeyFrameAnimation(); _fadeOutScalarKeyFrameAnimation.InsertKeyFrame(0, 1); _fadeOutScalarKeyFrameAnimation.InsertKeyFrame(1, 0); _fadeOutScalarKeyFrameAnimation.Duration = duration; _scaleVector2KeyFrameAnimation = this.compositor.CreateVector2KeyFrameAnimation(); _scaleVector2KeyFrameAnimation.InsertKeyFrame(0, new Vector2(1.25f, 1.25f)); _scaleVector2KeyFrameAnimation.InsertKeyFrame(1, new Vector2(1, 1)); _scaleVector2KeyFrameAnimation.Duration = duration; _initialized = true; } #endregion this.placeholderDelayTimeSpan = TimeSpan.FromMilliseconds(50); this.compositionSurfaceBrush = this.compositor.CreateSurfaceBrush(null); } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Private //////////////////////////////////////////////////////////////////////////////// Private #region 측정하기 (오버라이드) - MeasureOverride(availableSize) /// <summary> /// 측정하기 (오버라이드) /// </summary> /// <param name="availableSize">이용 가능 크기</param> /// <returns>측정 크기</returns> protected override Size MeasureOverride(Size availableSize) { Size desiredSize = new Size(0, 0); if(this.managedSurface != null) { Size scalingSize = new Size(1, 1); Size imageSize = this.managedSurface.Size; if(!(double.IsInfinity(availableSize.Width) && double.IsInfinity(availableSize.Height)) && this.compositionStretch != CompositionStretch.None) { scalingSize = new Size ( availableSize.Width / imageSize.Width, availableSize.Height / imageSize.Height ); if(double.IsInfinity(availableSize.Width)) { scalingSize.Width = scalingSize.Height; } else if(double.IsInfinity(availableSize.Height)) { scalingSize.Height = scalingSize.Width; } else { switch(this.compositionStretch) { case CompositionStretch.Uniform : scalingSize.Width = scalingSize.Height = Math.Min(scalingSize.Width, scalingSize.Height); break; case CompositionStretch.UniformToFill : scalingSize.Width = scalingSize.Height = Math.Max(scalingSize.Width, scalingSize.Height); break; case CompositionStretch.Fill : default : break; } } } desiredSize.Width = imageSize.Width * scalingSize.Width; desiredSize.Height = imageSize.Height * scalingSize.Height; } else { if(!double.IsNaN(Width)) { desiredSize.Width = Width; } if(!double.IsNaN(Height)) { desiredSize.Height = Height; } } return new Size ( Math.Min(availableSize.Width , desiredSize.Width ), Math.Min(availableSize.Height, desiredSize.Height) ); } #endregion //////////////////////////////////////////////////////////////////////////////// Private ////////////////////////////////////////////////////////////////////// Event #region 컨트롤 로딩시 처리하기 - Control_Loading(sender, e) /// <summary> /// 컨트롤 로딩시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void Control_Loading(FrameworkElement sender, object e) { this.spriteVisual = this.compositor.CreateSpriteVisual(); this.spriteVisual.Size = new Vector2((float)ActualWidth, (float)ActualHeight); this.unloaded = false; if(!IsContentLoaded) { LoadSurface(); } else { UpdateBrush(); } ElementCompositionPreview.SetElementChildVisual(this, this.spriteVisual); } #endregion #region 컨트롤 언로드시 처리하기 - Control_Unloaded(sender, e) /// <summary> /// 컨트롤 언로드시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void Control_Unloaded(object sender, RoutedEventArgs e) { if(Parent != null) { return; } this.unloaded = true; ReleaseSurface(); if(this.spriteVisual != null) { ElementCompositionPreview.SetElementChildVisual(this, null); this.spriteVisual.Dispose(); this.spriteVisual = null; } } #endregion #region 컨트롤 크기 변경시 처리하기 - Control_SizeChanged(sender, e) /// <summary> /// 컨트롤 크기 변경시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void Control_SizeChanged(object sender, SizeChangedEventArgs e) { if(this.spriteVisual != null) { Vector2 actualSizeVector2 = new Vector2((float)ActualWidth, (float)ActualHeight); this.spriteVisual.Size = actualSizeVector2; Visual loadingSprite = this.spriteVisual.Children.FirstOrDefault(); if(loadingSprite != null) { loadingSprite.Size = actualSizeVector2; } } } #endregion #region 디스패처 타이머 틱 처리하기 - dispatcherTimer_Tick(sender, e) /// <summary> /// 디스패처 타이머 틱 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void dispatcherTimer_Tick(object sender, object e) { if(this.dispatcherTimer != null) { Debug.Assert(this.spriteVisual.Children.Count == 0, "Should not be any children"); SpriteVisual loadingSpriteVisual = this.compositor.CreateSpriteVisual(); loadingSpriteVisual = this.compositor.CreateSpriteVisual(); loadingSpriteVisual.Size = new Vector2((float)ActualWidth, (float)ActualHeight); loadingSpriteVisual.Brush = this.placeholderCompositionBrush != null ? this.placeholderCompositionBrush : _defaultPlaceholderCompositionBrush; this.spriteVisual.Children.InsertAtTop(loadingSpriteVisual); this.dispatcherTimer.Stop(); this.dispatcherTimer = null; } } #endregion #region 합성 범위 배치 완료시 처리하기 - compositionScopedBatch_Completed(sender, e) /// <summary> /// 합성 범위 배치 완료시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void compositionScopedBatch_Completed(object sender, CompositionBatchCompletedEventArgs e) { if(this.spriteVisual != null && this.spriteVisual.Children.Count > 0) { this.spriteVisual.Children.RemoveAll(); } } #endregion ////////////////////////////////////////////////////////////////////// Function #region 서피스 해제하기 - ReleaseSurface() /// <summary> /// 서피스 해제하기 /// </summary> private void ReleaseSurface() { if(this.managedSurface != null) { if(!this.sharedSurface) { this.managedSurface.Dispose(); this.compositionSurfaceBrush.Surface = null; } else { ImageLoader.Instance.UnregisterSurface(this.managedSurface); } this.managedSurface = null; } } #endregion #region 브러시 업데이트하기 - UpdateBrush() /// <summary> /// 브러시 업데이트하기 /// </summary> private void UpdateBrush() { this.compositionSurfaceBrush.Surface = this.managedSurface == null ? null : this.managedSurface.Surface; this.compositionSurfaceBrush.Stretch = this.compositionStretch; if(this.spriteVisual != null) { if(this.compositionBrush != null) { if(this.compositionBrush is CompositionEffectBrush) { ((CompositionEffectBrush)this.compositionBrush).SetSourceParameter("ImageSource", this.compositionSurfaceBrush); } this.spriteVisual.Brush = this.compositionBrush; } else { this.spriteVisual.Brush = this.compositionSurfaceBrush; } } } #endregion #region 서피스 로드하기 - LoadSurface() /// <summary> /// 서피스 로드하기 /// </summary> private async void LoadSurface() { if(this.uri == null) { ReleaseSurface(); return; } try { if(this.managedSurface == null && this.placeholderDelayTimeSpan >= TimeSpan.Zero) { this.dispatcherTimer = new DispatcherTimer(); this.dispatcherTimer.Interval = this.placeholderDelayTimeSpan; this.dispatcherTimer.Tick += dispatcherTimer_Tick; this.dispatcherTimer.Start(); } ManagedSurface managedSurface = await ImageLoader.Instance.LoadFromURIAsync(this.uri, Size.Empty, this.loadTimeEffectHandler); if(this.managedSurface != null) { ReleaseSurface(); } this.managedSurface = managedSurface; InvalidateMeasure(); if(this.unloaded) { ReleaseSurface(); return; } UpdateBrush(); ImageOpened?.Invoke(this, null); if(this.spriteVisual != null && this.spriteVisual.Children.Count > 0) { Debug.Assert(this.dispatcherTimer == null); StartAnimation(); } else if(this.dispatcherTimer != null) { this.dispatcherTimer.Stop(); this.dispatcherTimer = null; } } catch(FileNotFoundException) { ImageFailed?.Invoke(this, null); } } #endregion #region 애니메이션 시작하기 - StartAnimation() /// <summary> /// 애니메이션 시작하기 /// </summary> private void StartAnimation() { Debug.Assert(this.spriteVisual.Children.Count > 0, "Unexpected number of children"); CompositionScopedBatch compositionScopedBatch = this.compositor.CreateScopedBatch(CompositionBatchTypes.Animation); compositionScopedBatch.Completed += compositionScopedBatch_Completed; Visual loadingVisual = this.spriteVisual.Children.LastOrDefault(); loadingVisual.StartAnimation("Opacity", _fadeOutScalarKeyFrameAnimation); Vector2 spriteVisualSizeVector2 = this.spriteVisual.Size; this.compositionSurfaceBrush.CenterPoint = new Vector2(spriteVisualSizeVector2.X * 0.5f, spriteVisualSizeVector2.Y * 0.5f); this.compositionSurfaceBrush.StartAnimation("Scale", _scaleVector2KeyFrameAnimation); compositionScopedBatch.End(); } #endregion } |
▶ DeviceReplacedEventArgs.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
using System; using Microsoft.UI.Composition; namespace TestLibrary; /// <summary> /// 디바이스 대체시 이벤트 인자 /// </summary> public class DeviceReplacedEventArgs : EventArgs { //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 합성 그래픽스 디바이스 - GraphicsDevce /// <summary> /// 합성 그래픽스 디바이스 /// </summary> public CompositionGraphicsDevice GraphicsDevce { get; set; } #endregion #region 드로잉 잠금 - DrawingLock /// <summary> /// 드로잉 잠금 /// </summary> public object DrawingLock { get; set; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - DeviceReplacedEventArgs(device, drawingLock) /// <summary> /// 생성자 /// </summary> /// <param name="device">합성 그래픽스 디바이스</param> /// <param name="drawingLock">드로잉 잠금</param> public DeviceReplacedEventArgs(CompositionGraphicsDevice device, object drawingLock) { GraphicsDevce = device; DrawingLock = drawingLock; } #endregion } |
▶ ImageLoader.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 |
using System; using System.Diagnostics; using System.Threading.Tasks; using Windows.Foundation; using Windows.Storage; using Windows.UI; using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.Text; using Microsoft.Graphics.Canvas.UI.Composition; using Microsoft.Graphics.DirectX; using Microsoft.UI.Composition; namespace TestLibrary; /// <summary> /// 이미지 로더 /// </summary> public class ImageLoader { //////////////////////////////////////////////////////////////////////////////////////////////////// Event ////////////////////////////////////////////////////////////////////////////////////////// Private #region 디바이스 대체시 이벤트 - deviceReplacedEvent /// <summary> /// 디바이스 대체시 이벤트 /// </summary> private event EventHandler<object> deviceReplacedEvent; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 초기화 여부 /// </summary> private static bool _intialized; /// <summary> /// 이미지 로더 /// </summary> private static ImageLoader _imageLoader; #endregion ////////////////////////////////////////////////////////////////////////////////////////// Instance //////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 합성자 /// </summary> private Compositor compositor; /// <summary> /// 캔버스 디바이스 /// </summary> private CanvasDevice canvasDevice; /// <summary> /// 합성 그래픽스 디바이스 /// </summary> private CompositionGraphicsDevice compositionGraphicsDevice; /// <summary> /// 드로잉 잠금 /// </summary> private object drawingLock; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Public #region 인스턴스 - Instance /// <summary> /// 인스턴스 /// </summary> public static ImageLoader Instance { get { Debug.Assert(_intialized); return _imageLoader; } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - ImageLoader(compositor) /// <summary> /// 생성자 /// </summary> /// <param name="compositor">합성자</param> public ImageLoader(Compositor compositor) { Debug.Assert(compositor != null && this.compositor == null); this.compositor = compositor; this.drawingLock = new object(); this.canvasDevice = new CanvasDevice(); this.canvasDevice.DeviceLost += canvasDevice_DeviceLost; this.compositionGraphicsDevice = CanvasComposition.CreateCompositionGraphicsDevice(this.compositor, this.canvasDevice); this.compositionGraphicsDevice.RenderingDeviceReplaced += compositionGraphicsDevice_RenderingDeviceReplaced; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Public #region 초기화하기 - Initialize(compositor) /// <summary> /// 초기화하기 /// </summary> /// <param name="compositor">혼합자</param> public static void Initialize(Compositor compositor) { Debug.Assert(!_intialized); if(!_intialized) { _imageLoader = new ImageLoader(compositor); _intialized = true; } } #endregion ////////////////////////////////////////////////////////////////////////////////////////// Instance //////////////////////////////////////////////////////////////////////////////// Public #region 서피스 등록하기 - RegisterSurface(managedSurface) /// <summary> /// 서피스 등록하기 /// </summary> /// <param name="managedSurface"></param> public void RegisterSurface(ManagedSurface managedSurface) { this.deviceReplacedEvent += managedSurface.OnDeviceReplaced; } #endregion #region 서피스 등록 취소하기 - UnregisterSurface(ManagedSurface managedSurface) /// <summary> /// 서피스 등록 취소하기 /// </summary> /// <param name="managedSurface">관리 서피스</param> public void UnregisterSurface(ManagedSurface managedSurface) { this.deviceReplacedEvent -= managedSurface.OnDeviceReplaced; } #endregion #region URI에서 로드하기 - LoadFromURI(uri, size, loadTimeEffectHandler) /// <summary> /// URI에서 로드하기 /// </summary> /// <param name="uri">URI</param> /// <param name="size">크기</param> /// <param name="loadTimeEffectHandler">시간 효과 로드 핸들러</param> /// <returns></returns> public ManagedSurface LoadFromURI(Uri uri, Size size, LoadTimeEffectHandler loadTimeEffectHandler) { CompositionDrawingSurface compositionDrawingSurface = CreateCompositionDrawingSurface(size); ManagedSurface managedSurface = new ManagedSurface(compositionDrawingSurface); Task ignoredTask = managedSurface.DrawAsync ( this.compositionGraphicsDevice, this.drawingLock, new BitmapDrawer(uri, loadTimeEffectHandler) ); return managedSurface; } #endregion #region URI에서 로드하기 - LoadFromURI(uri, size) /// <summary> /// URI에서 로드하기 /// </summary> /// <param name="uri">URI</param> /// <param name="size">크기</param> /// <returns>관리 서피스</returns> public ManagedSurface LoadFromURI(Uri uri, Size size) { return LoadFromURI(uri, size, null); } #endregion #region URI에서 로드하기 - LoadFromURI(uri) /// <summary> /// URI에서 로드하기 /// </summary> /// <param name="uri">URI</param> /// <returns>관리 서피스</returns> public ManagedSurface LoadFromURI(Uri uri) { return LoadFromURI(uri, Size.Empty); } #endregion #region 파일에서 로드하기 - LoadFromFile(storageFile, size, loadTimeEffectHandler) /// <summary> /// 파일에서 로드하기 /// </summary> /// <param name="storageFile">저장소 파일</param> /// <param name="size">크기</param> /// <param name="loadTimeEffectHandler">시간 효과 로드 핸들러</param> /// <returns>관리 서피스</returns> public ManagedSurface LoadFromFile(StorageFile storageFile, Size size, LoadTimeEffectHandler loadTimeEffectHandler) { CompositionDrawingSurface compositionDrawingSurface = CreateCompositionDrawingSurface(size); ManagedSurface managedSurface = new ManagedSurface(compositionDrawingSurface); Task ignoredTask = managedSurface.DrawAsync ( this.compositionGraphicsDevice, this.drawingLock, new BitmapDrawer(storageFile, loadTimeEffectHandler) ); return managedSurface; } #endregion #region URL에서 로드하기 (비동기) - LoadFromURIAsync(uri) /// <summary> /// URL에서 로드하기 (비동기) /// </summary> /// <param name="uri">URI</param> /// <returns>관리 서피스 비동기 작업</returns> public IAsyncOperation<ManagedSurface> LoadFromURIAsync(Uri uri) { return LoadFromURIInternalAsync(uri, Size.Empty, null).AsAsyncOperation<ManagedSurface>(); } #endregion #region URI에서 로드하기 (비동기) - LoadFromURIAsync(uri, size) /// <summary> /// URI에서 로드하기 (비동기) /// </summary> /// <param name="uri">URI</param> /// <param name="size">크기</param> /// <returns>관리 서피스 비동기 작업</returns> public IAsyncOperation<ManagedSurface> LoadFromURIAsync(Uri uri, Size size) { return LoadFromURIInternalAsync(uri, size, null).AsAsyncOperation<ManagedSurface>(); } #endregion #region URI에서 로드하기 (비동기) - LoadFromURIAsync(uri, size, loadTimeEffectHandler) /// <summary> /// URI에서 로드하기 (비동기) /// </summary> /// <param name="uri">URI</param> /// <param name="size">크기</param> /// <param name="loadTimeEffectHandler">시간 효과 로드 핸들러</param> /// <returns>관리 서피스 비동기 작업</returns> public IAsyncOperation<ManagedSurface> LoadFromURIAsync(Uri uri, Size size, LoadTimeEffectHandler loadTimeEffectHandler) { return LoadFromURIInternalAsync(uri, size, loadTimeEffectHandler).AsAsyncOperation<ManagedSurface>(); } #endregion #region 원 로드하기 - LoadCircle(radius, color) /// <summary> /// 원 로드하기 /// </summary> /// <param name="radius">반경</param> /// <param name="color">색상</param> /// <returns>관리 서피스</returns> public ManagedSurface LoadCircle(float radius, Color color) { CompositionDrawingSurface compositionDrawingSurface = CreateCompositionDrawingSurface(new Size(radius * 2, radius * 2)); ManagedSurface managedSurface = new ManagedSurface(compositionDrawingSurface); Task ignoredTask = managedSurface.DrawAsync(this.compositionGraphicsDevice, this.drawingLock, new CircleDrawer(radius, color)); return managedSurface; } #endregion #region 텍스트 로드하기 - LoadText(text, size, textFormat, textColor, backgroundColor) /// <summary> /// 텍스트 로드하기 /// </summary> /// <param name="text">텍스트</param> /// <param name="size">크기</param> /// <param name="textFormat">텍스트 포맷</param> /// <param name="textColor">텍스트 색상</param> /// <param name="backgroundColor">배경 색상</param> /// <returns>관리 서피스</returns> public ManagedSurface LoadText(string text, Size size, CanvasTextFormat textFormat, Color textColor, Color backgroundColor) { CompositionDrawingSurface compositionDrawingSurface = CreateCompositionDrawingSurface(size); ManagedSurface managedSurface = new ManagedSurface(compositionDrawingSurface); Task ignoredTask = managedSurface.DrawAsync ( this.compositionGraphicsDevice, this.drawingLock, new TextDrawer(text, textFormat, textColor, backgroundColor) ); return managedSurface; } #endregion #region 리소스 해제하기 - Dispose() /// <summary> /// 리소스 해제하기 /// </summary> public void Dispose() { lock(this.drawingLock) { this.compositor = null; if(this.canvasDevice != null) { this.canvasDevice.DeviceLost -= canvasDevice_DeviceLost; this.canvasDevice.Dispose(); this.canvasDevice = null; } if(this.compositionGraphicsDevice != null) { this.compositionGraphicsDevice.RenderingDeviceReplaced -= compositionGraphicsDevice_RenderingDeviceReplaced; this.compositionGraphicsDevice.Dispose(); this.compositionGraphicsDevice = null; } } } #endregion //////////////////////////////////////////////////////////////////////////////// Private ////////////////////////////////////////////////////////////////////// Event #region 캔버스 디바이스 디바이스 상실시 처리하기 - canvasDevice_DeviceLost(sender, e) /// <summary> /// 캔버스 디바이스 디바이스 상실시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void canvasDevice_DeviceLost(CanvasDevice sender, object e) { sender.DeviceLost -= canvasDevice_DeviceLost; this.canvasDevice = new CanvasDevice(); this.canvasDevice.DeviceLost += canvasDevice_DeviceLost; CanvasComposition.SetCanvasDevice(this.compositionGraphicsDevice, this.canvasDevice); } #endregion #region 합성 그래픽스 디바이스 디바이스 대체시 렌더링 처리하기 - compositionGraphicsDevice_RenderingDeviceReplaced(sender, e) /// <summary> /// 합성 그래픽스 디바이스 디바이스 대체시 렌더링 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void compositionGraphicsDevice_RenderingDeviceReplaced(CompositionGraphicsDevice sender, RenderingDeviceReplacedEventArgs e) { Task.Run ( () => { if(this.deviceReplacedEvent != null) { FireDeviceReplacedEvent(); } } ); } #endregion ////////////////////////////////////////////////////////////////////// Function #region 디바이스 대체시 이벤트 발생시키기 - FireDeviceReplacedEvent() /// <summary> /// 디바이스 대체시 이벤트 발생시키기 /// </summary> private void FireDeviceReplacedEvent() { this.deviceReplacedEvent?.Invoke(this, new DeviceReplacedEventArgs(this.compositionGraphicsDevice, this.drawingLock)); } #endregion #region URI 로드하기 (내부용)(비동기) - LoadFromURIInternalAsync(uri, size, handler) /// <summary> /// URI 로드하기 (내부용)(비동기) /// </summary> /// <param name="uri">URI</param> /// <param name="size">크기</param> /// <param name="loadTimeEffectHandler">시간 효과 로드 핸들러</param> /// <returns>관리 서피스 태스크</returns> private async Task<ManagedSurface> LoadFromURIInternalAsync(Uri uri, Size size, LoadTimeEffectHandler loadTimeEffectHandler) { CompositionDrawingSurface compositionDrawingSurface = CreateCompositionDrawingSurface(size); ManagedSurface managedSurface = new ManagedSurface(compositionDrawingSurface); await managedSurface.DrawAsync(this.compositionGraphicsDevice, this.drawingLock, new BitmapDrawer(uri, loadTimeEffectHandler)); return managedSurface; } #endregion #region 합성 드로잉 서피스 생성하기 - CreateCompositionDrawingSurface(size) /// <summary> /// 합성 드로잉 서피스 생성하기 /// </summary> /// <param name="size">크기</param> /// <returns>합성 드로잉 서피스</returns> private CompositionDrawingSurface CreateCompositionDrawingSurface(Size size) { Size surfaceSize = size; if(surfaceSize.IsEmpty) { surfaceSize = default(Size); } CompositionDrawingSurface compositionDrawingSurface = this.compositionGraphicsDevice.CreateDrawingSurface(surfaceSize, DirectXPixelFormat.B8G8R8A8UIntNormalized, DirectXAlphaMode.Premultiplied); return compositionDrawingSurface; } #endregion } |
▶ LoadTimeEffectHandler.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
using Microsoft.Graphics.Canvas; using Microsoft.UI.Composition; namespace TestLibrary; #region 시간 효과 로드 핸들러 - LoadTimeEffectHandler(surface, bitmap, device) /// <summary> /// 시간 효과 로드 핸들러 /// </summary> /// <param name="surface">합성 드로잉 서피스</param> /// <param name="bitmap">캔버스 비트맵</param> /// <param name="device">합성 그래픽스 디바이스</param> public delegate void LoadTimeEffectHandler(CompositionDrawingSurface surface, CanvasBitmap bitmap, CompositionGraphicsDevice device); #endregion |
▶ ManagedSurface.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
using System.Diagnostics; using System.Threading.Tasks; using Windows.Foundation; using Microsoft.UI.Composition; namespace TestLibrary; /// <summary> /// 관리 서피스 /// </summary> public class ManagedSurface { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 합성 드로잉 서피스 /// </summary> private CompositionDrawingSurface compositionDrawingSurface; /// <summary> /// 컨텐트 그리기 /// </summary> private IContentDrawer contentDrawer; /// <summary> /// 합성 서피스 브러시 /// </summary> private CompositionSurfaceBrush compositionSurfaceBrush; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 합성 드로잉 서피스 - Surface /// <summary> /// 합성 드로잉 서피스 /// </summary> public CompositionDrawingSurface Surface { get { return this.compositionDrawingSurface; } } #endregion #region 합성 서피스 브러시 - Brush /// <summary> /// 합성 서피스 브러시 /// </summary> public CompositionSurfaceBrush Brush { get { if(this.compositionSurfaceBrush == null) { this.compositionSurfaceBrush = this.compositionDrawingSurface.Compositor.CreateSurfaceBrush(this.compositionDrawingSurface); } return this.compositionSurfaceBrush; } } #endregion #region 크기 - Size /// <summary> /// 크기 /// </summary> public Size Size { get { return (this.compositionDrawingSurface != null) ? this.compositionDrawingSurface.Size : Size.Empty; } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - ManagedSurface(compositionDrawingSurface) /// <summary> /// 생성자 /// </summary> /// <param name="compositionDrawingSurface">합성 드로잉 서피스</param> public ManagedSurface(CompositionDrawingSurface compositionDrawingSurface) { Debug.Assert(compositionDrawingSurface != null); this.compositionDrawingSurface = compositionDrawingSurface; ImageLoader.Instance.RegisterSurface(this); } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 그리기 (비동기) - DrawAsync(device, drawingLock, contentDrawer) /// <summary> /// 그리기 (비동기) /// </summary> /// <param name="device">합성 그래픽스 디바이스</param> /// <param name="drawingLock">드로잉 잠금</param> /// <param name="contentDrawer">컨텐트 그리기</param> /// <returns>태스크</returns> public async Task DrawAsync(CompositionGraphicsDevice device, object drawingLock, IContentDrawer contentDrawer) { Debug.Assert(this.compositionDrawingSurface != null); this.contentDrawer = contentDrawer; await this.contentDrawer.DrawAsync(device, drawingLock, this.compositionDrawingSurface, this.compositionDrawingSurface.Size); } #endregion #region 디바이스 변경시 처리하기 - OnDeviceReplaced(sender, e) /// <summary> /// 디바이스 변경시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> public async void OnDeviceReplaced(object sender, object e) { DeviceReplacedEventArgs deviceReplacedEventArgs = e as DeviceReplacedEventArgs; await ReloadContent(deviceReplacedEventArgs.GraphicsDevce, deviceReplacedEventArgs.DrawingLock); } #endregion #region 해제하기 - Dispose() /// <summary> /// 해제하기 /// </summary> public void Dispose() { if(this.compositionDrawingSurface != null) { this.compositionDrawingSurface.Dispose(); this.compositionDrawingSurface = null; } if(this.compositionSurfaceBrush != null) { this.compositionSurfaceBrush.Dispose(); this.compositionSurfaceBrush = null; } this.contentDrawer = null; ImageLoader.Instance.UnregisterSurface(this); } #endregion ////////////////////////////////////////////////////////////////////////////////////////// Private #region 컨텐트 다시 로드하기 - ReloadContent(device, drawingLock) /// <summary> /// 컨텐트 다시 로드하기 /// </summary> /// <param name="device">합성 그래픽스 디바이스</param> /// <param name="drawingLock">드로잉 잠금</param> /// <returns>태스크</returns> private async Task ReloadContent(CompositionGraphicsDevice device, object drawingLock) { await this.contentDrawer.DrawAsync(device, drawingLock, this.compositionDrawingSurface, this.compositionDrawingSurface.Size); } #endregion } |
[TestProject 프로젝트]
▶ Thumbnail.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
namespace TestProject; /// <summary> /// 썸네일 /// </summary> public class Thumbnail { //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 명칭 - Name /// <summary> /// 명칭 /// </summary> public string Name { get; set; } #endregion #region 이미지 URL - ImageURL /// <summary> /// 이미지 URL /// </summary> public string ImageURL { get; set; } #endregion #region 설명 - Description /// <summary> /// 설명 /// </summary> public string Description { get; set; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - Thumbnail() /// <summary> /// 생성자 /// </summary> public Thumbnail() { } #endregion #region 생성자 - Thumbnail(name, url, description) /// <summary> /// 생성자 /// </summary> /// <param name="name">명칭</param> /// <param name="url">URL</param> /// <param name="description">설명</param> public Thumbnail(string name, string url, string description) { Name = name; ImageURL = url; Description = description; } #endregion } |
▶ LocalDataSource.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
using System; using System.Collections.ObjectModel; namespace TestProject; /// <summary> /// 로컬 데이터 소스 /// </summary> public class LocalDataSource { //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 풍경 컬렉션 - LandscapeCollection /// <summary> /// 풍경 컬렉션 /// </summary> public ObservableCollection<Thumbnail> LandscapeCollection { get; set; } #endregion #region 추상 컬렉션 - AbstractCollection /// <summary> /// 추상 컬렉션 /// </summary> public ObservableCollection<Thumbnail> AbstractCollection { get; set; } #endregion #region 자연 컬렉션 - NatureCollection /// <summary> /// 자연 컬렉션 /// </summary> public ObservableCollection<Thumbnail> NatureCollection { get; set; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - LocalDataSource() /// <summary> /// 생성자 /// </summary> public LocalDataSource() { LandscapeCollection = new ObservableCollection<Thumbnail>(); LandscapeCollection.Add(new Thumbnail("Rocky Mountains" , "ms-appx:///Assets/Landscapes/Landscape-01.jpg", "Landscape shot of mountains in rocky terrain" )); LandscapeCollection.Add(new Thumbnail("Sunny Landscape" , "ms-appx:///Assets/Landscapes/Landscape-02.jpg", "Picturesque scene with the sun high in the sky")); LandscapeCollection.Add(new Thumbnail("Mountain Road" , "ms-appx:///Assets/Landscapes/Landscape-03.jpg", "Winding road through a mountain pass" )); LandscapeCollection.Add(new Thumbnail("Harvest" , "ms-appx:///Assets/Landscapes/Landscape-04.jpg", "Corn stalks on a clear day" )); LandscapeCollection.Add(new Thumbnail("Rock Formation" , "ms-appx:///Assets/Landscapes/Landscape-05.jpg", "Unique rock formation off of a mountain" )); LandscapeCollection.Add(new Thumbnail("At Sea" , "ms-appx:///Assets/Landscapes/Landscape-06.jpg", "Sunset over the water" )); LandscapeCollection.Add(new Thumbnail("Snowy Mountain" , "ms-appx:///Assets/Landscapes/Landscape-07.jpg", "A snowy mountain framed by pine trees" )); LandscapeCollection.Add(new Thumbnail("Sea to Sky" , "ms-appx:///Assets/Landscapes/Landscape-08.jpg", "A lake framed by mountains and pine trees" )); LandscapeCollection.Add(new Thumbnail("On the Beach" , "ms-appx:///Assets/Landscapes/Landscape-09.jpg", "Shot of the beach with greenery" )); LandscapeCollection.Add(new Thumbnail("Lush Mountains" , "ms-appx:///Assets/Landscapes/Landscape-10.jpg", "Landscape shot of mountains in the forrest" )); LandscapeCollection.Add(new Thumbnail("White Dunes" , "ms-appx:///Assets/Landscapes/Landscape-11.jpg", "White sand dunes and a clear sky" )); LandscapeCollection.Add(new Thumbnail("Dunes with Tracks", "ms-appx:///Assets/Landscapes/Landscape-12.jpg", "Sand dunes after driving on an ATV" )); LandscapeCollection.Add(new Thumbnail("Shadowed Dunes" , "ms-appx:///Assets/Landscapes/Landscape-13.jpg", "Sand dunes casting a shadow" )); AbstractCollection = new ObservableCollection<Thumbnail>(); AbstractCollection.Add(new Thumbnail("Pink Bubbles" , "ms-appx:///Assets/Abstract/Abstract-1.jpg", "A macro shot of bubbles with a pink background" )); AbstractCollection.Add(new Thumbnail("Blue Bubbles" , "ms-appx:///Assets/Abstract/Abstract-2.jpg", "A macro shot of bubbles with a blue background" )); AbstractCollection.Add(new Thumbnail("Orange Bubbles", "ms-appx:///Assets/Abstract/Abstract-3.jpg", "A portrait macro shot orange bubbles" )); AbstractCollection.Add(new Thumbnail("Green Bubbles" , "ms-appx:///Assets/Abstract/Abstract-4.jpg", "A macro shot of green oil bubbles" )); AbstractCollection.Add(new Thumbnail("Drop" , "ms-appx:///Assets/Abstract/Abstract-5.jpg", "A macro shot of a droplet of water against nature")); AbstractCollection.Add(new Thumbnail("Petals" , "ms-appx:///Assets/Abstract/Abstract-6.jpg", "A close up shot of flower petals" )); AbstractCollection.Add(new Thumbnail("Up Close" , "ms-appx:///Assets/Abstract/Abstract-7.jpg", "A zoomed in shot of the center of a flower" )); NatureCollection = new ObservableCollection<Thumbnail>(); NatureCollection.Add(new Thumbnail("Cardoon" , "ms-appx:///Assets/Nature/Nature-01.jpg", "Close up shot of a purple cardoon" )); NatureCollection.Add(new Thumbnail("Meadow" , "ms-appx:///Assets/Nature/Nature-02.jpg", "Purple flowers in a meadow" )); NatureCollection.Add(new Thumbnail("Pink Flower" , "ms-appx:///Assets/Nature/Nature-03.jpg", "A close up shot of a unique pink and yellow flower" )); NatureCollection.Add(new Thumbnail("Red Flowers" , "ms-appx:///Assets/Nature/Nature-04.jpg", "A close up shot of a red flower amid a flower patch" )); NatureCollection.Add(new Thumbnail("Dahlia" , "ms-appx:///Assets/Nature/Nature-05.jpg", "A pink dahlia on a window sill" )); NatureCollection.Add(new Thumbnail("Petals" , "ms-appx:///Assets/Nature/Nature-06.jpg", "A shot focused on the petals of a pink flower" )); NatureCollection.Add(new Thumbnail("Cynthia" , "ms-appx:///Assets/Nature/Nature-07.jpg", "Cynthia butterfly landing on a flower" )); NatureCollection.Add(new Thumbnail("Painted Lady" , "ms-appx:///Assets/Nature/Nature-08.jpg", "Cynthia butterfly showing its painted lady wings" )); NatureCollection.Add(new Thumbnail("Macro Snail" , "ms-appx:///Assets/Nature/Nature-09.jpg", "A macro shot of a snail in the grass" )); NatureCollection.Add(new Thumbnail("Snail" , "ms-appx:///Assets/Nature/Nature-10.jpg", "A curious snail raising his head to take a look around")); NatureCollection.Add(new Thumbnail("Mushroom" , "ms-appx:///Assets/Nature/Nature-11.jpg", "A small mushroom coming out for spring" )); NatureCollection.Add(new Thumbnail("Japanese Macaques", "ms-appx:///Assets/Nature/Nature-12.jpg", "Two japanese macaque monkeys take care of each other" )); NatureCollection.Add(new Thumbnail("Bird Calls" , "ms-appx:///Assets/Nature/Nature-13.jpg", "A bird calls out looking for its family" )); } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 임의 썸네일 컬렉션 구하기 - GetRandomThumbnailCollection(sourceCollection) /// <summary> /// 임의 썸네일 컬렉션 구하기 /// </summary> /// <param name="sourceCollection">소스 컬렉션</param> /// <returns>임의 썸네일 컬렉션</returns> public static ObservableCollection<Thumbnail> GetRandomThumbnailCollection(ObservableCollection<Thumbnail> sourceCollection) { Random random = new Random(); for(int i = sourceCollection.Count-1; i > 0; i--) { int swapIndex = random.Next(i + 1); Thumbnail thumbnail = sourceCollection[i]; sourceCollection[i] = sourceCollection[swapIndex]; sourceCollection[swapIndex] = thumbnail; } return sourceCollection; } #endregion #region 썸네일 컬렉션 구하기 - GetThumbnailCollection(sourceCollectionArray) /// <summary> /// 썸네일 컬렉션 구하기 /// </summary> /// <param name="sourceCollectionArray">소스 컬렉션 배열</param> /// <returns>썸네일 컬렉션</returns> public ObservableCollection<Thumbnail> GetThumbnailCollection(ObservableCollection<Thumbnail>[] sourceCollectionArray) { ObservableCollection<Thumbnail> targetCollection = new ObservableCollection<Thumbnail>(); foreach(ObservableCollection<Thumbnail> sourceCollection in sourceCollectionArray) { foreach(Thumbnail thumbnail in sourceCollection) { targetCollection.Add(thumbnail); } } return GetRandomThumbnailCollection(targetCollection); } #endregion } |
▶ MainPage.xaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
<?xml version="1.0" encoding="utf-8"?> <Page x:Class="TestProject.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" xmlns:library="using:TestLibrary" xmlns:local="using:TestProject" FontFamily="나눔고딕코딩" FontSize="16"> <Grid Margin="10"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <StackPanel Grid.Row="0" HorizontalAlignment="Left" Orientation="Horizontal"> <ComboBox Name="lightingSelectionComboBox" VerticalAlignment="Center" /> <CheckBox Name="mouseHoverCheckBox" VerticalAlignment="Center" Margin="10 0 0 0" Content="Enable Mouse hover" /> </StackPanel> <RelativePanel Name="rootRelativePanel" Grid.Row="1" Margin="0 10 0 0" BorderThickness="1" BorderBrush="DarkGray"> <ListView Name="thumbnailListView" IsItemClickEnabled="True"> <ListView.ItemsPanel> <ItemsPanelTemplate> <ItemsWrapGrid Orientation="Horizontal" /> </ItemsPanelTemplate> </ListView.ItemsPanel> <ListView.ItemTemplate> <DataTemplate x:DataType="local:Thumbnail"> <Grid Width="160" Height="160" Margin="0 10 0 10"> <library:CompositionImage Width="160" Height="160" Stretch="UniformToFill" /> <Image HorizontalAlignment="Center" VerticalAlignment="Center" Width="100" Height="100" Source="{x:Bind ImageURL}" /> <TextBlock HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0 0 0 10" Foreground="White" FontWeight="Bold" FontSize="16" Text="{x:Bind Name}" /> </Grid> </DataTemplate> </ListView.ItemTemplate> </ListView> </RelativePanel> </Grid> </Page> |
▶ MainPage.xaml.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 |
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Numerics; using Windows.Graphics.Effects; using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.Effects; using Microsoft.UI; using Microsoft.UI.Composition; using Microsoft.UI.Composition.Effects; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Hosting; using Microsoft.UI.Xaml.Input; using TestLibrary; namespace TestProject; /// <summary> /// 메인 페이지 /// </summary> public sealed partial class MainPage : Page { //////////////////////////////////////////////////////////////////////////////////////////////////// Enumeration ////////////////////////////////////////////////////////////////////////////////////////// Public #region 조명 타입 - LightingType /// <summary> /// 조명 타입 /// </summary> public enum LightingType { PointDiffuse, PointSpecular, SpotLightDiffuse, SpotLightSpecular, DistantDiffuse, DistantSpecular } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 합성자 /// </summary> private Compositor compositor; /// <summary> /// 합성 효과 팩토리 /// </summary> private CompositionEffectFactory compositionEffectFactory; /// <summary> /// 주변 라이트 /// </summary> private AmbientLight ambientLight; /// <summary> /// 포인트 라이트 /// </summary> private PointLight pointLight; /// <summary> /// 원거리 라이트 /// </summary> private DistantLight distantLight; /// <summary> /// 스포트 라이트 /// </summary> private SpotLight spotLight; /// <summary> /// 구체 법선 맵 관리 서피스 /// </summary> private ManagedSurface sphereNormalMapManagedSurface; /// <summary> /// 엣지 법선 맵 관리 서피스 /// </summary> private ManagedSurface edgeNormalMapManagedSurface; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 데이터 소스 - DataSource /// <summary> /// 데이터 소스 /// </summary> public LocalDataSource DataSource { get; set; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - MainPage() /// <summary> /// 생성자 /// </summary> public MainPage() { Compositor compositor = ElementCompositionPreview.GetElementVisual(this).Compositor; ImageLoader.Initialize(compositor); DataSource = new LocalDataSource(); InitializeComponent(); this.compositor = ElementCompositionPreview.GetElementVisual(this).Compositor; this.ambientLight = this.compositor.CreateAmbientLight(); this.pointLight = this.compositor.CreatePointLight(); this.distantLight = this.compositor.CreateDistantLight(); this.spotLight = this.compositor.CreateSpotLight(); Loaded += Page_Loaded; Unloaded += Page_Unloaded; this.lightingSelectionComboBox.SelectionChanged += lightingSelectionComboBox_SelectionChanged; this.mouseHoverCheckBox.Click += mouseHoverCheckBox_Checked; this.thumbnailListView.SizeChanged += thumbnailListView_SizeChanged; this.thumbnailListView.ContainerContentChanging += thumbnailListView_ContainerContentChanging; this.thumbnailListView.ItemClick += thumbnailListView_ItemClick; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Private //////////////////////////////////////////////////////////////////////////////// Event #region 페이지 로드시 처리하기 - Page_Loaded(sender, e) /// <summary> /// 페이지 로드시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private async void Page_Loaded(object sender, RoutedEventArgs e) { IList<ComboBoxItem> comboBoxItemList = new List<ComboBoxItem>(); foreach(LightingType lightingType in Enum.GetValues(typeof(LightingType))) { ComboBoxItem comboBoxItem = new ComboBoxItem(); comboBoxItem.Content = lightingType.ToString(); comboBoxItem.Tag = lightingType; comboBoxItemList.Add(comboBoxItem); } this.lightingSelectionComboBox.ItemsSource = comboBoxItemList; this.lightingSelectionComboBox.SelectedIndex = 2; this.thumbnailListView.ItemsSource = DataSource.GetThumbnailCollection ( new ObservableCollection<Thumbnail>[] { DataSource.LandscapeCollection, DataSource.NatureCollection } ); this.sphereNormalMapManagedSurface = await ImageLoader.Instance.LoadFromURIAsync(new Uri("ms-appx:///Assets/NormalMapsAndMasks/SphericalWithMask.png")); this.sphereNormalMapManagedSurface.Brush.Stretch = CompositionStretch.Fill; this.edgeNormalMapManagedSurface = await ImageLoader.Instance.LoadFromURIAsync(new Uri("ms-appx:///Assets/NormalMapsAndMasks/BeveledEdges.jpg")); this.edgeNormalMapManagedSurface.Brush.Stretch = CompositionStretch.Fill; UpdateEffectBrush(); } #endregion #region 페이지 언로드시 처리하기 - Page_Unloaded(sender, e) /// <summary> /// 페이지 언로드시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void Page_Unloaded(object sender, RoutedEventArgs e) { if(this.sphereNormalMapManagedSurface != null) { this.sphereNormalMapManagedSurface.Dispose(); this.sphereNormalMapManagedSurface = null; } if(this.edgeNormalMapManagedSurface != null) { this.edgeNormalMapManagedSurface.Dispose(); this.edgeNormalMapManagedSurface = null; } } #endregion #region 조명 선택 콤보 박스 선택 변경시 처리하기 - lightingSelectionComboBox_SelectionChanged(sender, e) /// <summary> /// 조명 선택 콤보 박스 선택 변경시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void lightingSelectionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { UpdateLightingEffect(); } #endregion #region 마우스 호버 체크 박스 체크시 처리하기 - mouseHoverCheckBox_Checked(sender, e) /// <summary> /// 마우스 호버 체크 박스 체크시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void mouseHoverCheckBox_Checked(object sender, RoutedEventArgs e) { if(this.mouseHoverCheckBox.IsChecked == true) { this.thumbnailListView.PointerMoved += thumbnailListView_PointerMoved; StopAnimations(); } else { this.thumbnailListView.PointerMoved -= thumbnailListView_PointerMoved; UpdateAnimations(); } } #endregion #region 썸네일 리스트 뷰 크기 변경시 처리하기- thumbnailListView_SizeChanged(sender, e) /// <summary> /// 썸네일 리스트 뷰 크기 변경시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void thumbnailListView_SizeChanged(object sender, SizeChangedEventArgs e) { UpdateAnimations(); } #endregion #region 썸네일 리스트 뷰 컨테이너 컨텐트 변경시 처리하기 - thumbnailListView_ContainerContentChanging(sender, e) /// <summary> /// 썸네일 리스트 뷰 컨테이너 컨텐트 변경시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void thumbnailListView_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs e) { CompositionImage compositionImage = e.ItemContainer.ContentTemplateRoot.GetFirstDescendantOfType<CompositionImage>(); Thumbnail thumbnail = e.Item as Thumbnail; Uri uri = new Uri(thumbnail.ImageURL); SetImageEffect(compositionImage); compositionImage.Source = uri; } #endregion #region 썸네일 리스트 뷰 포인터 이동시 처리하기 - thumbnailListView_PointerMoved(sender, e) /// <summary> /// 썸네일 리스트 뷰 포인터 이동시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void thumbnailListView_PointerMoved(object sender, PointerRoutedEventArgs e) { Vector2 offsetVector2 = e.GetCurrentPoint(thumbnailListView).Position.ToVector2(); ComboBoxItem comboBoxItem = this.lightingSelectionComboBox.SelectedValue as ComboBoxItem; switch((LightingType)comboBoxItem.Tag) { case LightingType.PointDiffuse : case LightingType.PointSpecular : this.pointLight.Offset = new Vector3(offsetVector2.X, offsetVector2.Y, 75); break; case LightingType.SpotLightDiffuse : case LightingType.SpotLightSpecular : this.spotLight.Offset = new Vector3(offsetVector2.X, offsetVector2.Y, 150); break; case LightingType.DistantDiffuse : case LightingType.DistantSpecular : Vector3 positionVector3 = new Vector3((float)thumbnailListView.ActualWidth / 2 , (float)thumbnailListView.ActualHeight / 2 , 200); Vector3 lookAtVector3 = new Vector3((float)thumbnailListView.ActualWidth - offsetVector2.X, (float)thumbnailListView.ActualHeight - offsetVector2.Y, 0 ); this.distantLight.Direction = Vector3.Normalize(lookAtVector3 - positionVector3); break; default: break; } } #endregion #region 썸네일 리스트 뷰 항목 클릭시 처리하기- thumbnailListView_ItemClick(sender, e) /// <summary> /// 썸네일 리스트 뷰 항목 클릭시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void thumbnailListView_ItemClick(object sender, ItemClickEventArgs e) { ListViewItem listViewItem = this.thumbnailListView.ContainerFromItem(e.ClickedItem) as ListViewItem; CompositionImage compositionImage = listViewItem.ContentTemplateRoot.GetFirstDescendantOfType<CompositionImage>(); SpriteVisual spriteVisual = compositionImage.SpriteVisual; spriteVisual.RotationAxis = new Vector3(1, 0, 0); spriteVisual.CenterPoint = new Vector3(spriteVisual.Size.X / 2, spriteVisual.Size.Y / 2, 0); ScalarKeyFrameAnimation scalarKeyFrameAnimation = this.compositor.CreateScalarKeyFrameAnimation(); scalarKeyFrameAnimation.InsertKeyFrame(0, 0 ); scalarKeyFrameAnimation.InsertKeyFrame(1, 360); scalarKeyFrameAnimation.Duration = TimeSpan.FromSeconds(2); spriteVisual.StartAnimation("RotationAngleInDegrees", scalarKeyFrameAnimation); } #endregion //////////////////////////////////////////////////////////////////////////////// Function #region 이미지 효과 설정하기 - SetImageEffect(compositionImage) /// <summary> /// 이미지 효과 설정하기 /// </summary> /// <param name="compositionImage">합성 이미지</param> private void SetImageEffect(CompositionImage compositionImage) { CompositionEffectBrush compositionEffectBrush = this.compositionEffectFactory.CreateBrush(); ComboBoxItem comboBoxItem = this.lightingSelectionComboBox.SelectedValue as ComboBoxItem; switch((LightingType)comboBoxItem.Tag) { case LightingType.SpotLightSpecular : case LightingType.PointSpecular : case LightingType.DistantDiffuse : case LightingType.DistantSpecular : compositionEffectBrush.SetSourceParameter("NormalMap", this.sphereNormalMapManagedSurface == null ? null : this.sphereNormalMapManagedSurface.Brush); break; default : compositionEffectBrush.SetSourceParameter("NormalMap", this.edgeNormalMapManagedSurface == null ? null : this.edgeNormalMapManagedSurface.Brush); break; } compositionImage.Brush = compositionEffectBrush; } #endregion #region 효과 브러시 업데이트하기 - UpdateEffectBrush() /// <summary> /// 효과 브러시 업데이트하기 /// </summary> private void UpdateEffectBrush() { if(this.thumbnailListView.ItemsPanelRoot != null) { foreach(ListViewItem listViewItem in this.thumbnailListView.ItemsPanelRoot.Children) { CompositionImage compositionImage = listViewItem.ContentTemplateRoot.GetFirstDescendantOfType<CompositionImage>(); SetImageEffect(compositionImage); } } } #endregion #region 애니메이션 업데이트하기 - UpdateAnimations() /// <summary> /// 애니메이션 업데이트하기 /// </summary> private void UpdateAnimations() { Vector2 sizeLightBoundVector2 = new Vector2 ( (float)this.rootRelativePanel.ActualWidth, (float)this.rootRelativePanel.ActualHeight ); Vector3KeyFrameAnimation lightPositionVector3KeyFrameAnimation; ColorKeyFrameAnimation lightColorColorKeyFrameAnimation; ComboBoxItem comboBoxItem = this.lightingSelectionComboBox.SelectedValue as ComboBoxItem; switch((LightingType)comboBoxItem.Tag) { case LightingType.PointDiffuse : case LightingType.PointSpecular : { float zDistance = 50f; lightPositionVector3KeyFrameAnimation = this.compositor.CreateVector3KeyFrameAnimation(); lightPositionVector3KeyFrameAnimation.InsertKeyFrame(0f , new Vector3(0f , 0f , zDistance)); lightPositionVector3KeyFrameAnimation.InsertKeyFrame(0.25f, new Vector3(sizeLightBoundVector2.X * .2f , sizeLightBoundVector2.Y * .5f, zDistance)); lightPositionVector3KeyFrameAnimation.InsertKeyFrame(0.50f, new Vector3(sizeLightBoundVector2.X * .75f, sizeLightBoundVector2.Y * .5f, zDistance)); lightPositionVector3KeyFrameAnimation.InsertKeyFrame(0.75f, new Vector3(sizeLightBoundVector2.X * .2f , sizeLightBoundVector2.Y * .2f, zDistance)); lightPositionVector3KeyFrameAnimation.InsertKeyFrame(1f , new Vector3(0f , 0f , zDistance)); lightPositionVector3KeyFrameAnimation.IterationBehavior = AnimationIterationBehavior.Forever; lightPositionVector3KeyFrameAnimation.Duration = TimeSpan.FromMilliseconds(7500); lightColorColorKeyFrameAnimation = this.compositor.CreateColorKeyFrameAnimation(); lightColorColorKeyFrameAnimation.InsertKeyFrame(0f , Colors.White ); lightColorColorKeyFrameAnimation.InsertKeyFrame(0.33f, Colors.White ); lightColorColorKeyFrameAnimation.InsertKeyFrame(0.66f, Colors.Yellow); lightColorColorKeyFrameAnimation.InsertKeyFrame(1f , Colors.White ); lightColorColorKeyFrameAnimation.IterationBehavior = AnimationIterationBehavior.Forever; lightColorColorKeyFrameAnimation.Duration = TimeSpan.FromMilliseconds(20000); this.pointLight.StartAnimation("Offset", lightPositionVector3KeyFrameAnimation); this.pointLight.StartAnimation("Color" , lightColorColorKeyFrameAnimation ); break; } case LightingType.SpotLightDiffuse : case LightingType.SpotLightSpecular : { lightPositionVector3KeyFrameAnimation = this.compositor.CreateVector3KeyFrameAnimation(); lightPositionVector3KeyFrameAnimation.InsertKeyFrame(0f , new Vector3(0f , 0f , 100f)); lightPositionVector3KeyFrameAnimation.InsertKeyFrame(0.33f, new Vector3(sizeLightBoundVector2.X * .5f, sizeLightBoundVector2.Y * .5f, 200f)); lightPositionVector3KeyFrameAnimation.InsertKeyFrame(0.66f, new Vector3(sizeLightBoundVector2.X , sizeLightBoundVector2.Y * .5f, 400f)); lightPositionVector3KeyFrameAnimation.InsertKeyFrame(1f , new Vector3(0f , 0f , 100f)); lightPositionVector3KeyFrameAnimation.IterationBehavior = AnimationIterationBehavior.Forever; lightPositionVector3KeyFrameAnimation.Duration = TimeSpan.FromMilliseconds(7500); lightColorColorKeyFrameAnimation = this.compositor.CreateColorKeyFrameAnimation(); lightColorColorKeyFrameAnimation.InsertKeyFrame(0f , Colors.White ); lightColorColorKeyFrameAnimation.InsertKeyFrame(0.33f, Colors.White ); lightColorColorKeyFrameAnimation.InsertKeyFrame(0.66f, Colors.Yellow); lightColorColorKeyFrameAnimation.InsertKeyFrame(1f , Colors.White ); lightColorColorKeyFrameAnimation.IterationBehavior = AnimationIterationBehavior.Forever; lightColorColorKeyFrameAnimation.Duration = TimeSpan.FromMilliseconds(20000); this.spotLight.StartAnimation("Offset" , lightPositionVector3KeyFrameAnimation); this.spotLight.StartAnimation("InnerConeColor", lightColorColorKeyFrameAnimation ); break; } case LightingType.DistantDiffuse : case LightingType.DistantSpecular : { Vector3 positionVector3 = new Vector3(0, 0, 100); float offsetCenter = 700f; Vector3KeyFrameAnimation lightDirectionAnimation = this.compositor.CreateVector3KeyFrameAnimation(); lightDirectionAnimation.InsertKeyFrame(0f , Vector3.Normalize(new Vector3( 0, 0, 0) - positionVector3)); lightDirectionAnimation.InsertKeyFrame(0.25f, Vector3.Normalize(new Vector3( offsetCenter, 0, 0) - positionVector3)); lightDirectionAnimation.InsertKeyFrame(0.5f, Vector3.Normalize(new Vector3(-offsetCenter, offsetCenter, 0) - positionVector3)); lightDirectionAnimation.InsertKeyFrame(0.75f, Vector3.Normalize(new Vector3( 0, -offsetCenter, 0) - positionVector3)); lightDirectionAnimation.InsertKeyFrame(1f , Vector3.Normalize(new Vector3( 0, 0, 0) - positionVector3)); lightDirectionAnimation.Duration = TimeSpan.FromMilliseconds(7500); lightDirectionAnimation.IterationBehavior = AnimationIterationBehavior.Forever; this.distantLight.StartAnimation("Direction", lightDirectionAnimation); break; } default : break; } } #endregion #region 애니메이션 중단하기 - StopAnimations() /// <summary> /// 애니메이션 중단하기 /// </summary> private void StopAnimations() { ComboBoxItem comboBoxItem = this.lightingSelectionComboBox.SelectedValue as ComboBoxItem; switch((LightingType)comboBoxItem.Tag) { case LightingType.PointDiffuse : case LightingType.PointSpecular : this.pointLight.StopAnimation("Offset"); this.pointLight.StopAnimation("Color"); break; case LightingType.SpotLightDiffuse : case LightingType.SpotLightSpecular : this.spotLight.StopAnimation("Offset"); this.spotLight.StopAnimation("InnerConeColor"); break; case LightingType.DistantDiffuse : case LightingType.DistantSpecular : this.distantLight.StopAnimation("Direction"); break; default : break; } } #endregion #region 조명 효과 업데이트하기 - UpdateLightingEffect() /// <summary> /// 조명 효과 업데이트하기 /// </summary> private void UpdateLightingEffect() { this.ambientLight.Targets.RemoveAll(); this.pointLight.Targets.RemoveAll(); this.distantLight.Targets.RemoveAll(); this.spotLight.Targets.RemoveAll(); ComboBoxItem comboBoxItem = this.lightingSelectionComboBox.SelectedValue as ComboBoxItem; switch((LightingType)comboBoxItem.Tag) { case LightingType.PointDiffuse : { IGraphicsEffect graphicsEffect = new CompositeEffect() { Mode = CanvasComposite.Add, Sources = { new CompositionEffectSourceParameter("ImageSource"), new SceneLightingEffect() { AmbientAmount = 0, DiffuseAmount = 0.75f, SpecularAmount = 0, NormalMapSource = new CompositionEffectSourceParameter("NormalMap"), } } }; this.compositionEffectFactory = this.compositor.CreateEffectFactory(graphicsEffect); Visual lightRootVisual = ElementCompositionPreview.GetElementVisual(this.thumbnailListView); this.pointLight.CoordinateSpace = lightRootVisual; this.pointLight.Targets.Add(lightRootVisual); break; } case LightingType.PointSpecular : { IGraphicsEffect graphicsEffect = new CompositeEffect() { Mode = CanvasComposite.DestinationIn, Sources = { new ArithmeticCompositeEffect() { Source1Amount = 1, Source2Amount = 1, MultiplyAmount = 0, Source1 = new ArithmeticCompositeEffect() { MultiplyAmount = 1, Source1Amount = 0, Source2Amount = 0, Source1 = new CompositionEffectSourceParameter("ImageSource"), Source2 = new SceneLightingEffect() { AmbientAmount = 0.6f, DiffuseAmount = 1f, SpecularAmount = 0f, NormalMapSource = new CompositionEffectSourceParameter("NormalMap"), } }, Source2 = new SceneLightingEffect() { AmbientAmount = 0, DiffuseAmount = 0f, SpecularAmount = 1f, SpecularShine = 100, NormalMapSource = new CompositionEffectSourceParameter("NormalMap"), } }, new CompositionEffectSourceParameter("NormalMap") } }; this.compositionEffectFactory = this.compositor.CreateEffectFactory(graphicsEffect); Visual lightRootVisual = ElementCompositionPreview.GetElementVisual(this.thumbnailListView); this.ambientLight.Targets.Add(lightRootVisual); this.pointLight.CoordinateSpace = lightRootVisual; this.pointLight.Targets.Add(lightRootVisual); break; } case LightingType.SpotLightDiffuse : { IGraphicsEffect graphicsEffect = new CompositeEffect() { Mode = CanvasComposite.Add, Sources = { new CompositionEffectSourceParameter("ImageSource"), new SceneLightingEffect() { AmbientAmount = 0, DiffuseAmount = 0.75f, SpecularAmount = 0, NormalMapSource = new CompositionEffectSourceParameter("NormalMap"), } } }; this.compositionEffectFactory = this.compositor.CreateEffectFactory(graphicsEffect); Visual lightRootVisual = ElementCompositionPreview.GetElementVisual(this.thumbnailListView); this.spotLight.CoordinateSpace = lightRootVisual; this.spotLight.Targets.Add(lightRootVisual); this.spotLight.InnerConeAngle = (float)(Math.PI / 4 ); this.spotLight.OuterConeAngle = (float)(Math.PI / 3.5); this.spotLight.Direction = new Vector3(0, 0, -1); break; }; case LightingType.SpotLightSpecular : { IGraphicsEffect graphicsEffect = new CompositeEffect() { Mode = CanvasComposite.DestinationIn, Sources = { new ArithmeticCompositeEffect() { Source1Amount = 1, Source2Amount = 1, MultiplyAmount = 0, Source1 = new ArithmeticCompositeEffect() { MultiplyAmount = 1, Source1Amount = 0, Source2Amount = 0, Source1 = new CompositionEffectSourceParameter("ImageSource"), Source2 = new SceneLightingEffect() { AmbientAmount = 0.6f, DiffuseAmount = 1f, SpecularAmount = 0f, NormalMapSource = new CompositionEffectSourceParameter("NormalMap"), } }, Source2 = new SceneLightingEffect() { AmbientAmount = 0, DiffuseAmount = 0f, SpecularAmount = 1f, SpecularShine = 100, NormalMapSource = new CompositionEffectSourceParameter("NormalMap"), } }, new CompositionEffectSourceParameter("NormalMap") } }; this.compositionEffectFactory = this.compositor.CreateEffectFactory(graphicsEffect); Visual lightRootVisual = ElementCompositionPreview.GetElementVisual(this.thumbnailListView); this.ambientLight.Targets.Add(lightRootVisual); this.spotLight.CoordinateSpace = lightRootVisual; this.spotLight.Targets.Add(lightRootVisual); this.spotLight.InnerConeAngle = (float)(Math.PI / 4 ); this.spotLight.OuterConeAngle = (float)(Math.PI / 3.5); this.spotLight.Direction = new Vector3(0, 0, -1); break; }; case LightingType.DistantDiffuse : { IGraphicsEffect graphicsEffect = new CompositeEffect() { Mode = CanvasComposite.DestinationIn, Sources = { new CompositeEffect() { Mode = CanvasComposite.Add, Sources = { new CompositionEffectSourceParameter("ImageSource"), new SceneLightingEffect() { AmbientAmount = 0, DiffuseAmount = 0.5f, SpecularAmount = 0, NormalMapSource = new CompositionEffectSourceParameter("NormalMap"), } } }, new CompositionEffectSourceParameter("NormalMap") } }; this.compositionEffectFactory = this.compositor.CreateEffectFactory(graphicsEffect); Visual lightRootVisual = ElementCompositionPreview.GetElementVisual(this.thumbnailListView); this.distantLight.CoordinateSpace = lightRootVisual; this.distantLight.Targets.Add(lightRootVisual); break; }; case LightingType.DistantSpecular : { IGraphicsEffect graphicsEffect = new CompositeEffect() { Mode = CanvasComposite.DestinationIn, Sources = { new ArithmeticCompositeEffect() { Source1Amount = 1, Source2Amount = 1, MultiplyAmount = 0, Source1 = new ArithmeticCompositeEffect() { MultiplyAmount = 1, Source1Amount = 0, Source2Amount = 0, Source1 = new CompositionEffectSourceParameter("ImageSource"), Source2 = new SceneLightingEffect() { AmbientAmount = 0.6f, DiffuseAmount = 1f, SpecularAmount = 0f, NormalMapSource = new CompositionEffectSourceParameter("NormalMap"), } }, Source2 = new SceneLightingEffect() { AmbientAmount = 0, DiffuseAmount = 0f, SpecularAmount = 1f, SpecularShine = 100, NormalMapSource = new CompositionEffectSourceParameter("NormalMap"), } }, new CompositionEffectSourceParameter("NormalMap") } }; this.compositionEffectFactory = this.compositor.CreateEffectFactory(graphicsEffect); Visual lightRootVisual = ElementCompositionPreview.GetElementVisual(this.thumbnailListView); this.distantLight.CoordinateSpace = lightRootVisual; this.distantLight.Targets.Add(lightRootVisual); break; }; default : break; } UpdateAnimations(); UpdateEffectBrush(); } #endregion } |