Silverlight2009.06.30 10:57
 이전 포스팅에서와 마찬가지로 Deepzoom Composer에서 추출 된 이미지 피라미드와 MultiScaleImage 엘리먼트의 화면 비율에 관한 이야기를 이어 보겠습니다.

2009/06/30 - [Image/Deepzoom] - Deepzoom Composer에서 Single Image와 MultiScaleImage 엘리먼트의 화면 비율

 이전 포스팅에서는 Single Image를 다뤘는데요, 이번에는 Multiple Images를 다뤄보겠습니다.

 Single Image에서는, Deepzoom Composer에서 추출 된 이미지 피라미드를 MultiScaleImage 엘리먼트의 소스에 할당 했을 경우, Single Image의 가로 : 세로 비율에 알맞는 MultiScaleImage 엘리먼트의 Size를 조정해야만 처음 로드시 MutliScaleImage에 꽉차게 로드 되게 됩니다.
 Multiple Images 일 경우는 어떨까요?
 Multiple Images 경우에는, 가로는 Compose에서 배치 된 Images 중, 최좌측에 배치 된 이미지 부터 최우측에 배치 된 이미지까지의 Size가 Multiple Image의 가로 Size가 됩니다. 마찬가지로, 세로는 Compose에서 배치 된 Images 중, 최상단에 배치 된 이미지부터 최하단에 배치 된 이미지까지의 Size가 Multiple Image의 세로 Size가 됩니다.
 이렇게 정해진, 가로 : 세로의 비율이 MutliScaleImage 엘리먼트 Size와 일치해야만 Deepzoom Composer에서 추출된 이미지를 MultiScaleImage 엘리먼트의 소스에 할당 했을 경우 첫 로드시 개발자가 원하는 화면이 로드 되게 되는 것이죠.

 그럼, 1024 : 768 사이즈의 이미지 3개로 실습 해 보겠습니다.

 1024 : 768 사이즈의 이미지를 다음과 같이 배치 한 뒤, Export 했습니다.

 화살표에서 보시는 바와 같이, 1024 : 768 이미지를 위와 같이 배치 했을 경우, Multiple image의 전체 비율이 2048 : 1536 가 되는 것을 확인 할 수 있습니다.

 그럼 2048 : 1536 비율의 Size를 갖는 MultiScaleImage 엘리먼트에 할당 해 보죠.

(간단한 수학 식이지만 2048 : 1536 = 400 : 300 입니다 ^^;)

 보시는 바와 같이, Deepzoom Composer 에서 추출 된 이미지가 MutliScaleImage에 꽉 맞게 로드 되는 것을 확인 할 수 있습니다. 이전 포스팅에서 보여드렸던 것과 마찬가지로 Mutiple Image의 가로 : 세로 비율과 MutliScaleImage 엘리먼트의 가로 : 세로 비율이 맞지 않게 된다면 이미지의 아랫 부분이 잘리거나, MultiScaleImage 엘리먼트의 하단에 공백이 생기게 되는 것입니다.

 이런 원리를 조금 더 응용해 본다면, 2개의 이미지로 화면 비율을 설정 해 두고, 나머지 이미지를 자유롭게 배치하여, 첫 로드시 정확히는 ViewportWidth가 1일 경우 MutiScaleImage 엘리먼트에 보여지는 모습을 지정하고, MultiScaleImage의 화면 비율까지도 미리 지정 해 둘 수 있겠죠 ^^

 빨간색 화살표로 표시 된, 두 이미지를 통해서, 전체 Multiple images 의 화면 비율을 맞춘 후 (여기서는 4:3 비율로 맞췄습니다.) 녹색 테두리의 두 이미지를 배치했습니다. 이렇게 해서 생성 된 이미지 피라미드를 MultiScaleImage의 Source에 할당 한 뒤, 로드시 화면 비율을 맞추기 위해서 사용 된 두 이미지를 감추게 되면, MultiScaleImage의 비율이 4:3이고, ViewportWidth가 1인 경우 어떤 해상도에서든 항상 다음과 같은 모양으로 보여지게 됩니다.
<이미지를 감추는 부분>

<MutliScaleImage의 Width="400", Height="300" 인 경우>

<MutliScaleImage의 Width="800", Height="600" 인 경우>

Posted by Min-gu, Kim
Silverlight2009.06.30 10:17
 Deepzoom Project를 위해서는 MultiScaleImage의 Source를 위해 이미지 피라미드와 XML 파일이 필요한데요, 바로 이 이미지 피라미드와 XML파일을 Seadragon의 Deepzoom Composer를 사용하여 추출해 낼 수 있습니다.

 이미 많은 분들이 Deepzoom Composer를 이용하여 이미지 피라미드와 XML파일을 추출해 내고 있으실 텐데요, Deepzoom Composer를 사용하여 Export 된 컬렉션 이미지들의 배치와 화면 비율에 관한 이야기를 해 보려고 합니다.


1024x768 사이즈의 이미지 한 장으로 이미지 피라미드를 구성하였습니다. 이 경우, 가로 : 세로 = 1024 : 768 의 비율로 Export 되기 때문에 위와 같이 Export 된 이미지 피라미드를 MultiScaleImage의 Soucre에 할당 할 경우, 처음 로드시 ViewportWidth 가 기본 1로 로드 되기 때문에 MultiScaleImage 엘리먼트의 사이즈가 1024: 768의 비율이라면 엘리먼트 내 꽉 차게 로드 되게 됩니다.

코드에서 처럼, MultiScaleImage 엘리먼트의 Width와 Height를 1024 : 768 의 비율로 해서 각각 400, 300 씩 지정하였습니다.


만일, 1024 : 768의 비율이 아니라면, 어떻게 될까요? 정답은, ViewportWidth 가 기본으로 1로 셋팅 된 상태로 로드된다는 점에 있습니다. 1024 : 768의 비율보다 세로의 비율이 더 적어진다면, 이미지의 아랫 부분이 짤리게 나오고, 세로의 비율이 더 커진다면 전체 이미지가 보이고, MultiScaleImage의 아랫 부분에 공백이 생기게 됩니다.
<MultiScaleImage Width = "400" Height="200" 인 경우>

<MultiScaleImage Width = "400" Height="400" 인 경우>


정리하자면, MultiScaleImage는 처음 로드시 ViewportWidth = 1, ViewportOrigin = (0, 0) 으로 할당 되 있기 때문에 Source 이미지는 가로(Width)로는 모두 표시 되지만, 세로는 해당 이미지의 가로:세로 비율과 해당 MultiScaleImage 엘리먼트의 Size에 따라서, 짤릴 수도 있고, 여백이 생길 수도 있게 되는 겁니다. (물론, 비율이 딱 맞게 조정 되 있다면, 화면에 꽉 차게 로드 되겠죠)
Posted by Min-gu, Kim
Silverlight2009.06.22 16:34
Deepzoom 에 관한 기초 지식을 기반으로 간략한 Test 솔루션을 만들어 보겠습니다.

Test 솔루션에서 구현 될 기능 리스트 입니다.
1. Mouse Left Button Click 시 Deepzoom Image 확대.
2. Shift + Mouse Left Button Click 시 Deepzoom Image 축소.
3. MouseWheelHelper를 사용한 Wheel 지원
4. Pan 기능(Mouse Drag를 통한 Deepzoom Image 이동).

Deepzoom Test Solution 을 하나 생성합니다.(Web Project 도 함께 추가합니다.)


Deepzoom Composer 를 이용한 Deepzoom Image 피라미드를 ClientBin 폴더에 복사합니다.

Deepzoom Composer를 통해 산출 된 Image 피라미드는 MultiScaleImage의 Source가 됩니다.

MainPage를 Design하고 MultiScaleImage 를 Layout Panel 안에 배치합니다. Layout Panel 에 배치 한 뒤, MultiScaleImage 의 Source를 지정합니다. MultiScaleImage의 Source는 Deepzoom Composer로 산출 된 dzc_output.xml 파일을 지정하시면 됩니다.
<MultiScaleImage x:Name="myMSI" Grid.Column="1" Grid.Row="1" Source="dzc_output.xml"/>

사실, 이 작업까지 한다면 "심플한" Deepzoom 솔루션을 위한 준비는 끝났습니다. 추가로 작업하실 부분은 이미지 축소, 확대, 이동 등에 대한 기능 구현이겠죠^^ 그럼, 기능 구현을 한 번 해보겠습니다.

1. Mouse Left Button Click 시 Deepzoom Image 확대.
유저 입장에서 Click이란 Mouse를 손가락으로 따닥 하는 것입니다. 하지만 "따닥" 이란 Mouse Button을 Down, Up 하는 과정이겠죠. Click시 Deepzoom Image 확대 라는 기능을 구현 시, Down에 확대 될 것인가 혹은, Up에 확대 될 것인가를 명확히 구분해야 한다는 뜻입니다. (이 예제에서는 Mouse Left Button을 Down 하여, Drag 할 경우 Deepzoom Image가 Mouse를 이동하는 방향과 같이 움직이게 구현 할 것이기 때문에, Down시 확대를 하지 않고, Up이 될 경우에 확대를 해야 합니다.)

확대 혹은 축소는, MultiScaleImage.ZoomAboutLogicalPoint 메소드를 통해서 가능합니다.
ZoomAboutLogicalPoint는 3개의 파라미터를 인자(double ZoomFactor, double LogicalXPoint, double LogicalYPoint) 로 요구합니다. 여기서 ZoomFactor 란, 이미지의 확대/축소를 결정하는 값입니다. 초기 값은 1이며, 1보다 큰 숫자이면 확대됩니다. 1보다 작으면 축소가 됩니다. 0 이하의 값을 사용하면 오류가 반환되고 확대/축소 변경이 적용되지 않습니다.

MultiScaleImage.MouseLeftButtonUp 이벤트의 Argument로 오는 MouseButtonEventArgs 에는 GetPosition 메소드가 있어 UIElement를 인자로 넘겨주면, UIElement 상에서 현재 Mouse 의 위치를 ElementPoint로 가져오게 됩니다. 이 ElementPoint를 LogicalPoint로 변경 한 뒤, ZoomAboutLogicalPoint 함수를 실행하면 됩니다.
<코드 1> 
this.myMSI.MouseLeftButtonUp += (sender, e) =>
{
    Point log_mousePosition = this.myMSI.ElementToLogicalPoint(e.GetPosition(this.myMSI)); 

    if (this.shiftKeyPressed)
    {
        this.myMSI.ZoomAboutLogicalPoint(0.7, log_mousePosition.X, log_mousePosition.Y);
    }
    else
    {
        this.myMSI.ZoomAboutLogicalPoint(1.3, log_mousePosition.X, log_mousePosition.Y);
    }
};


2. Shift + Mouse Left Button Click 시 Deepzoom Image 축소.
모든 UIElement 는 KeyDown, KeyUp 이벤트를 지원합니다. 하지만, 몇 개의 엘리먼트는 KeyDown, KeyUp 이벤트가 지원되지 않는데요, 그 이유는 KeyDown, KeyUp 이벤트는 엘리먼트가 포커싱 되 있는 경우에만 가능하기 때문에, 포커싱 될 수 없는 엘리먼트는 KeyDown, KeyUp 이벤트가 지원되지 않는 것입니다. MultiScaleImage도 그 중 하나입니다. 그래서, Shift+Click 으로 이미지의 축소를 구현하기 위해서는 MultiScaleImage가 배치 된 Layout Panel에 KeyDown, KeyUp 이벤트를 구현하여, Shift가 눌렸는지 확인해야 합니다.
Member변수로 Boolean 타입의 shiftKeyPressed 를 선언 한 뒤, MultiScaleImage가 배치 된 Layout Panel 에서 KeyDown 시 Shift 키가 눌렸는지 확인하여 Shift 가 눌리면 true로, 아니면 false를 할당하고, KeyUp 시에는 false를 할당합니다.
<코드 2> 
this.KeyDown += (sender, e) =>
{
    if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
        this.shiftKeyPressed = true;
    else
        this.shiftKeyPressed = false;
}; 

this.KeyUp += (sender, e) => this.shiftKeyPressed = false;


이렇게 맴버 변수 shiftKeyPressed를 관리하게 되면, MouseLeftButtonUp(이미지 확대, 축소가 구현 될 이벤트)에서 shiftKeyPressed 의 상태에 따라 true면 축소를, false이면 확대를 해 주면 됩니다. (<코드 1> 참조)

3. MouseWheelHelper를 사용한 Wheel 지원
Deepzoom Composer 최신 버전으로 Export 하시게 되면, MouseWheelHelper가 포함 되 있습니다. 혹시, 없으시면 => 다운
MouseWheelHelper를 Silverlight 솔루션에 추가 하신 뒤, Namespace명을 솔루션에 맞게 수정하신 뒤 사용하시면 됩니다.

그런 다음, MultiScaleImage 가 있는 MainPage 비하인드 코드에서, MouseWheelHelper 인스턴스를 생성 한 뒤, Moved 이벤트를 구현하시면 됩니다. Moved 이벤트는 MouseWheel 을 상하로 움직이는 경우에 발생하는 이벤트입니다.
MouseWheelEventArgs에 Deta Property가 있습니다. 이 Delta 값을 이용하여 MouseWheel이 위로 움직여졌는지, 아래로 움직여졌는지 확인 할 수 있습니다. Delta 값이 0보다 크면 위로, 0보다 작으면 아래 방향으로 MouseWheel이 동작 된 것입니다.
<코드 3> 
new MouseWheelHelper(this.myMSI).Moved += (sender, e) =>
{
    Double zoomFactor = 0;
    if (e.Delta > 0)
        zoomFactor = 1.3;
    else
        zoomFactor = 0.7; 

    Point log_mousePosition = this.myMSI.ElementToLogicalPoint(this.currentMousePosition);
    this.myMSI.ZoomAboutLogicalPoint(zoomFactor, log_mousePosition.X, log_mousePosition.Y);
};


<코드 3> 에서 ZoomAboutLogicalPoint 메소드를 실행하기 위해, 맴버 변수인 currentMousePosition을 LogicalPoint로 변환 하였습니다. (currentMousePosition의 관리는 MouseMove 이벤트에서 관리 하면 되겠죠^^)

4. Pan 기능(Mouse Drag를 통한 Deepzoom Image 이동).
MouseLeftButtonDown 후, Drag 를 통하여 Deepzoom Image를 이동하는 동작을 구현 해 보겠습니다.
우선, MouseMove 시 Image를 이동시켜야 하는데, Drag모드 인지 아닌지를 구별할 필요가 있기 때문에 맴버변수로 Boolean 타입의 dragging 을 선언합니다. Drag는 MouseLeftButton을 Down한 뒤 Move하는 동안 발생하는 것이기 때문에, dragging 변수의 관리는 MouseLeftButtonDown에서 true로 MouseLeftButtonUp에서 false로 해 주면 됩니다.
dragging 변수의 관리가 되 있다면, MouseMove시 dragging 인지를 확인한 뒤, Drag 모드 에서만 Image를 Move 해 주면 되는거죠.
<코드 4>
this.myMSI.MouseMove += (sender, e) =>
{
    this.currentMousePosition = e.GetPosition(this.myMSI);

    if (this.dragging)
    {
        Point newOrigin = new Point();

        // dragOffset = MouseLeftButtonDown 된 지점
        newOrigin.X = currentViewportOrigin.X - (((currentMousePosition.X - dragOffset.X) / this.myMSI.ActualWidth) * this.myMSI.ViewportWidth);

        newOrigin.Y = currentViewportOrigin.Y - (((currentMousePosition.Y - dragOffset.Y) / this.myMSI.ActualHeight) * this.myMSI.ViewportWidth);

        this.myMSI.ViewportOrigin = newOrigin;
    }
};



몇 가지 추가적으로 처리 해 줘야 하는 것이 있습니다. 사용자가 MultiScaleImage 상에서 MouseLeftButton을 Down한 뒤, Move하여 이미지를 이동하다가 Mouse가 MultiScaleImage 밖으로 나가게 되면, 이미지의 이동이 끝나야 하는데도 계속 마우스를 쫓아서 움직이게 됩니다. 더군다나, 그 상태에서 마우스가 MultiScaleImage 안으로 다시 들어오면 Drag 상태가 계속 지속됩니다. 이런 경우를 대비하기 위해서 MouseLeftButtonDown 시 MultiScaleImage.CaptureMouse 함수를 실행하고, MultiScaleImage에서 MouseLeave시 MultiScaleImage.ReleaseMouseCapture 함수를 실행합니다. 그리고 MouseLeave에서 추가적으로 dragging 맴버 변수를 false 로 할당해야 되겠죠.
<코드 5>
this.myMSI.MouseLeftButtonDown += (sender, e) =>
{
    this.myMSI.CaptureMouse();
    .
    .
    .
};

this.myMSI.MouseLeave += (sender, e) =>
{
    this.myMSI.ReleaseMouseCapture();
    if (this.dragging) this.dragging = false;
};


<코드 5> 처럼 처리하면, Drag 모드에서 마우스가 MultiScaleImage 밖으로 나가게 되면, Drag모드에서 벗어나게 되고, MouseCapture도 Release 됩니다. 혼란스러운 동작을 조금이나마 명확하게 없애주게 됩니다^^



이상 "간략한" Deepzoom Solution 예제의 전문입니다.
<Xaml - MainPage.xaml> 
<UserControl x:Class="DeepzoomSolution.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             Width="600" Height="500">
    <Grid x:Name="LayoutRoot" Background="#444444">
        <Grid.RowDefinitions>
            <RowDefinition Height="0.08*"/>
            <RowDefinition Height="0.768*"/>
            <RowDefinition Height="0.064*"/>
            <RowDefinition Height="0.088*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="0.08*"/>
            <ColumnDefinition Width="0.84*"/>
            <ColumnDefinition Width="0.08*"/>
        </Grid.ColumnDefinitions>
        <MultiScaleImage x:Name="myMSI" Grid.Column="1" Grid.Row="1" Source="dzc_output.xml"/>
        <Slider x:Name="mySlider" Margin="8,8,8,8" Grid.Column="1" Grid.Row="2"/>
        <StackPanel Grid.Column="1" Grid.Row="3" VerticalAlignment="Top" Height="47" >
            <TextBlock Text="{Binding Value, ElementName=mySlider}"/>
            <Button x:Name="testButton" Margin="8,0,0,0" Width="50" Content="Click"/>
        </StackPanel>
    </Grid>
</UserControl>

<C# Code - MainPage.xaml.cs>
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Diagnostics;

namespace DeepzoomSolution
{
    public partial class MainPage : UserControl
    {
        private Boolean shiftKeyPressed = false;
        private Point currentMousePosition = new Point();
        private Point currentViewportOrigin = new Point();
        private Point dragOffset = new Point();
        private Boolean dragging = false;
        private Boolean moved = false

        public MainPage()
        {
            InitializeComponent(); 

            this.KeyDown += (sender, e) =>
            {
                if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
                    this.shiftKeyPressed = true;
                else
                    this.shiftKeyPressed = false;
            }; 

            this.KeyUp += (sender, e) => this.shiftKeyPressed = false

            new MouseWheelHelper(this.myMSI).Moved += (sender, e) =>
            {
                Double zoomFactor = 0;
                if (e.Delta > 0)
                    zoomFactor = 1.3;
                else
                    zoomFactor = 0.7; 

                Point log_mousePosition = this.myMSI.ElementToLogicalPoint(this.currentMousePosition);

                this.myMSI.ZoomAboutLogicalPoint(zoomFactor, log_mousePosition.X, log_mousePosition.Y);
            }; 

            this.myMSI.MouseMove += (sender, e) =>
            {
                this.currentMousePosition = e.GetPosition(this.myMSI);
                this.moved = true

                if (this.dragging)
                {
                    Point newOrigin = new Point();

                    // dragOffset = MouseLeftButtonDown 된 지점
                    newOrigin.X = currentViewportOrigin.X - (((currentMousePosition.X - dragOffset.X) / this.myMSI.ActualWidth) * this.myMSI.ViewportWidth);

                    newOrigin.Y = currentViewportOrigin.Y - (((currentMousePosition.Y - dragOffset.Y) / this.myMSI.ActualHeight) * this.myMSI.ViewportWidth);

                    this.myMSI.ViewportOrigin = newOrigin;
                }
            }; 

            this.myMSI.MouseLeftButtonDown += (sender, e) =>
            {
                this.myMSI.CaptureMouse();
                this.dragOffset = e.GetPosition(this.myMSI);
                this.currentViewportOrigin = this.myMSI.ViewportOrigin;
                this.dragging = true;
            }; 

            this.myMSI.MouseLeftButtonUp += (sender, e) =>
            {
                this.dragging = false
                Point log_mousePosition = this.myMSI.ElementToLogicalPoint(e.GetPosition(this.myMSI)); 

                if (!moved)
                {
                    if (this.shiftKeyPressed)
                    {
                        this.myMSI.ZoomAboutLogicalPoint(0.7, log_mousePosition.X, log_mousePosition.Y);
                    }
                    else
                    {
                        this.myMSI.ZoomAboutLogicalPoint(1.3, log_mousePosition.X, log_mousePosition.Y);
                    }
                }

                this.moved = false;
            }; 

            this.myMSI.MouseLeave += (sender, e) =>
            {
                this.myMSI.ReleaseMouseCapture();

                if (this.dragging) this.dragging = false;
            };

            this.testButton.Click += (sender, e) =>
            {
                this.myMSI.ViewportOrigin = new Point(-0.3, -0.3);
            };

            Boolean viewPortChanged = false;
            Boolean sliderValueChanged = false

            this.myMSI.ViewportChanged += (sender, e) =>
            {
                if (sliderValueChanged) return;
                viewPortChanged = true;
                this.mySlider.Value = this.myMSI.ViewportWidth;
            };

             this.myMSI.MotionFinished += (s, e) =>
            {
                viewPortChanged = false;
                sliderValueChanged = false;
            }; 

            this.mySlider.ValueChanged += (sender, e) =>
            {
                if (viewPortChanged) return;
                sliderValueChanged = true;
                this.myMSI.ViewportWidth = this.mySlider.Value;
            };

            this.testButton.Click += (sender, e) =>
            {
                this.myMSI.ViewportOrigin = new Point(0, 0);
                this.myMSI.ViewportWidth = 1;
            };
        }
    }
}


(사실, 전문에는 Slider로 ViewportWidth 를 Binding 해 보는 코드가 포함 되 있습니다. 첨엔 Element To Element Binding으로 구현 해 봤는데, Slider의 Value에 Minium 값을 지정 해 줘야만 하는데다 -ViewportWidth가 0이 되면 다시는 Image를 볼 수 없더라고요-, TwoWay Mode로 해도 Zoom In/Out 시 ViewportWidth가 Slider에 제대로 Binding 되지 않아서 억지로 구현 해 봤는데요.. 코드도 명확하지 못하고, 구현도 정확히 작동하지 않아서 본문에는 포함시키지 않았습니다 ㅠ.ㅠ)
Posted by Min-gu, Kim
Silverlight2009.06.10 17:18

AspectRatio
MultiScaleImage 의 Source Image의 가로 세로 비율.
the width of the image divided by its height. ( 이미지의 가로를 세로로 나눈 )
Readonly Property.    

AspectRatio Property의 활용 : ?  

ViewPort
ViewPort 란 MultiScaleImage Control의 Source Image를 보는 창이다.
(Source Image의 특정 영역을 보는 데 사용하는 사각형 영역) Source Image를 삼차원으로 다루는 하나의 방식이라고 생각할 수 있다.

ViewPort 를 제어 할 수 있는 Property로 ViewportWidth와 ViewportOrigin 가 있다.
ViewportWidth property는 크기를, ViewportOrigin property는 위치를 제어한다.

ViewportWidth
Default로 ViewPort는 MultiScaleImage컨트롤의 크기와 같지만, ViewportWidth property를 통해 변경 할 수도 있다.
ViewportWidth property(double value)를 1.0로 설정하면, ViewPort는 MultiScaleImage control과 동일한 크기가 된다.
ViewportWidth property를 1.0보다 크게 변경하면, MultiScaleImage control 보다 사이즈가 더 커지고, 이것은 사용자가 볼 때는 Source Image를 축소한 것과 같은 효과를 보인다.
반대로 ViewportWidth property를 1.0보다 작게 변경하면, MultiScaleImage control 보다 사이즈가 더 작아지고, 사용자가 볼 때는 Source Image를 확대한 것과 같은 효과를 보이게 된다.

ViewportOrigin
ViewportOrigin property는 ViewPort의 최상단 좌측의 좌표이다.   

좌표계
좌표계란 어떤 공간 내의 한 점의 위치를 기준점(원점)과 비교하여 거리를 설명하는 방법을 말한다.
MultiScaleImage Control에서 좌표를 표현하는 방법은 ElementPoint와 LogicalPoint 두 가지가 있다.


ElementPoint
ElementPoint는 컨트롤의 좌측 최상단을 원점으로 하여,
임의의 지점을 X(수평), Y(수직) Point로 하여 X,Y를 픽셀로 표시하는 것을 말한다.
예를 들어, Width, Height 가 각각 500, 300 인 MultiScaleImage Control이 있다면,
MultiScaleImage Control 내 임의의 지점의 ElementPoint는 위 그림과 같이 (300, 150) 이다.

LogicalPoint
LogicalPoint는 컨트롤의 좌측 최상단(0,0)을 원점으로 하고,
컨트롤의 Width, Height 를 각각 1이라고 정의 한 뒤 임의의 지점을
0과 1사이의 논리적인 좌표로 표시하는 것을 말한다. (LogicalPoint를 논리 좌표계라고 한다.)
 
ElementPoint 의 예와 같은 MultiScaleImage Control의 임의의 지점을 LogicalPoint로 표시한다면,
그림과 같이 (0.6, 0.5)가 된다. 
   
MultiScaleImage Control은 ElementPoint와 LogicalPoint의 좌표 시스템을 상호 변환하는 함수를 제공한다.
ElementToLogicalPoint : ElementPoint를 LogicalPoint로 변환
LogicalToElementPoint : LogicalPoint를 ElementPoint로 변환 

두 함수를 반드시 숙지해야 하는 이유는, MultiScaleImage Control 의 Source Image를 축소, 확대 할 경우와,
축소, 확대 시 어떤 부분의 Source Image를 축소, 확대할 것인지를 선택하는데 중요하기 때문이다.

MultiScaleImage Control 을 제외한 대부분의 Silverlight Control은 ElementLogical을 사용한다.
예를 들어, Mouse Point 지점을 축소, 확대 하고자 한다면,
MultiScaleImage Control 상의 Mouse Point (ElementPoint)를 Logical Point로 변환 한 뒤,
ZoomAboutLogicalPoint 메서드의 파라미터 인자로 넘겨야 한다.
(ZoomAboutLogicalPoint는 MutiScaleImage Control 의 함수이며, 축소, 확대할 것인지와, 축소 또는 확대할 지점을 LogicalPoint로 받아서 처리한다.)

'Silverlight' 카테고리의 다른 글

Style 정의하기  (0) 2009.06.16
VisualStateManager  (0) 2009.06.15
Storyboard (Animation in Silverlight)  (0) 2009.06.15
Layout Panel #5 (WrapPanel)  (0) 2009.06.12
Layout Panel #4 (DockPanel)  (0) 2009.06.12
Layout Panel #3 (StackPanel)  (0) 2009.06.12
Layout Panel #2 (Grid)  (0) 2009.06.11
Layout Panel #1 (Canvas)  (0) 2009.06.11
Coposition에서의 ViewPort, AspectRatio, 좌표계, ElementalPoint, LogicalPoint  (0) 2009.06.10
Deepzoom Composer로 Export 된 결과물  (0) 2009.06.09
Deepzoom  (0) 2009.06.09
Posted by Min-gu, Kim
Silverlight2009.06.09 17:07

Deepzoom Composer 를 사용하여 Export 된 폴더 

- Composition으로 Export 했을 경우

폴더 안에는 dzc_output_files 폴더와
dzc_output.xml, SparseImageSceneGraph.xml 두 개의 파일이 생성 된다. 
dzc_output.xml은 Silverlight Control에서 MultiScaleImage 의 Source 가 되며,

  <?xml version="1.0" encoding="utf-8" ?>
- <Image TileSize="256" Overlap="1" Format="jpg" xmlns="http://schemas.microsoft.com/deepzoom/2008"> 
     <Size Width="3872" Height="2592" /> 
  </Image>
이미지의 사이즈 정보를 가지고 있다.

   

dzc_output_files 폴더 안에는
Deep Zoom Composer 로 Export 된 이미지 피라미드가 생성 되 있으며,
MultiScaleImage 컨트롤에서 로드 시 이 폴더의 이미지 피라미드를 로드하게 된다.
 
12부터 원본이미지의 타일이 각 수준별로 저장 되 있으며,
0 폴더안에는 1픽셀까지 축소 된 이미지가 저장 되 있다.


- Collection으로 Export 했을 경우

폴더 안에는 dzc_output_files, dzc_output_images 두 개의 폴더와
dzc_output.xml, Metadata.xml, SparseImageSceneGraph.xml 세 개의 파일이 생성 된다. 

또한, dzc_output_images 폴더 안에는
 
컬렉션 내 개별 이미지의 이미지 피라미드와 이미지 피라미드와 매치되는 xml파일이 생성 된다. 

마찬가지로 dzc_output.xml은 Silverlight Control에서 MultiScaleImage 의 Source 가 되며,

컬렉션 내 개별 이미지의 이미지 피라미드를 매칭 할 수 있는 xml 파일이 Source 속성에 지정 되 있다.

  <?xml version="1.0" encoding="utf-8" ?>
- <Collection MaxLevel="8" TileSize="256" Format="jpg" NextItemId="15" xmlns="http://schemas.microsoft.com/deepzoom/2008">
- <Items>
- <I Id="0" N="0" Source="dzc_output_images/forest.xml"> 
  <Size Width="1024" Height="768" /> 
  <Viewport Width="3" X="-0" Y="-0" /> 
  </I>
- <I Id="1" N="1" Source="dzc_output_images/oryx%20antelope.xml"> 
  <Size Width="1024" Height="768" /> 
  <Viewport Width="3" X="-1" Y="-0" /> 
  </I>
- <I Id="2" N="2" Source="dzc_output_images/green%20sea%20turtle.xml"> 
  <Size Width="1024" Height="768" /> 
  <Viewport Width="3" X="-0" Y="-0.75" /> 
  </I>
- <I Id="3" N="3" Source="dzc_output_images/toco%20toucan.xml"> 
  <Size Width="1024" Height="768" /> 
  <Viewport Width="3" X="-1" Y="-0.75" /> 
  </I>
- <I Id="4" N="4" Source="dzc_output_images/dock.xml"> 
  <Size Width="1024" Height="768" /> 
  <Viewport Width="3" X="-0" Y="-1.5" /> 
  </I>
- <I Id="5" N="5" Source="dzc_output_images/garden.xml"> 
  <Size Width="1024" Height="768" /> 
  <Viewport Width="3" X="-1" Y="-1.5" /> 
  </I>
- <I Id="6" N="6" Source="dzc_output_images/frangipani%20flowers.xml"> 
  <Size Width="1024" Height="768" /> 
  <Viewport Width="3" X="-2" Y="-0" /> 
  </I>
- <I Id="7" N="7" Source="dzc_output_images/humpback%20whale.xml"> 
  <Size Width="1024" Height="768" /> 
  <Viewport Width="3" X="-2" Y="-0.75" /> 
  </I>
- <I Id="8" N="8" Source="dzc_output_images/tree.xml"> 
  <Size Width="1024" Height="768" /> 
  <Viewport Width="3" X="-2" Y="-1.5" /> 
  </I> 
  </Items> 
  </Collection> 
   
단일 이미지에서는 볼 수 없었던, Metadata.xml 이 생성 되는데,
Metadata.xml은 원본 이미지의 Path와 이미지의 위치(뷰포인트)를 가리키는 X, Y, 이미지의 크기인 Width, Height, 그리고 ZOrder와 Tage 속성이 있다. 

 
<?xml version="1.0" ?>
- <Metadata version="1"> 
  <AspectRatio>1.33333333333333</AspectRatio>
- <Image> 
  <FileName>C:\Users\qordir\Desktop\UntitledProject1\Source Images\forest.jpg</FileName> 
  <x>0</x> 
  <y>0</y> 
  <Width>0.333333333333333</Width> 
  <Height>0.333333333333333</Height> 
  <ZOrder>1</ZOrder> 
  <Tag /> 
  </Image>
- <Image> 
  <FileName>C:\Users\qordir\Desktop\UntitledProject1\Source Images\oryx antelope.jpg</FileName> 
  <x>0.333333333333333</x> 
  <y>0</y> 
  <Width>0.333333333333333</Width> 
  <Height>0.333333333333333</Height> 
  <ZOrder>2</ZOrder> 
  <Tag /> 
  </Image>
.
.
.

Tag 는 비워져 있는데, 이 Tag에 값을 할당하여, 활용할 수 있다. (카테고리 등으로 활용).

'Silverlight' 카테고리의 다른 글

Style 정의하기  (0) 2009.06.16
VisualStateManager  (0) 2009.06.15
Storyboard (Animation in Silverlight)  (0) 2009.06.15
Layout Panel #5 (WrapPanel)  (0) 2009.06.12
Layout Panel #4 (DockPanel)  (0) 2009.06.12
Layout Panel #3 (StackPanel)  (0) 2009.06.12
Layout Panel #2 (Grid)  (0) 2009.06.11
Layout Panel #1 (Canvas)  (0) 2009.06.11
Coposition에서의 ViewPort, AspectRatio, 좌표계, ElementalPoint, LogicalPoint  (0) 2009.06.10
Deepzoom Composer로 Export 된 결과물  (0) 2009.06.09
Deepzoom  (0) 2009.06.09
Posted by Min-gu, Kim
Silverlight2009.06.09 17:07

Silverlight에서 고해상도의 이미지를 자유롭게 확대/축소할 수 있는 기능.


어떤 방법으로 가능한가?

Deep zoom을 이용하면, 고해상도의 이미지를 확대/축소 하면서 탐색할 수 있다.
그것이 가능하게 하기 위한 핵심은 바로 '이미지 피라미드'.    

이미지 피라미드
원본 이미지를 256x256 크기의 이미지 타일로 만들고,
원본 이미지를 1/4로 축소한 이미지를 256x256 크기의 이미지 타일로 만들고,
축소된 이미지를 다시 1/4로 축소한 이미지를 256x256 크기의 이미지 타일로 만들고를 반복하여,
최종 축소된 이미지가 1픽셀이 될 때까지 수행하여,
각 타일을 별도의 파일로 저장한 뒤, 단계(수준)별로 별도의 폴도에 저장한다.
(256x256 사이즈는 고정 된 것이 아니라, 임의의 크기이며 수정 가능하다.)
(이미지 피라미드는 BitmapImage 클래스에서 지원하는 이미지 파일만 지원.)

위와 같은 방식으로 이미지 피라미드를 만든 후,
Deep zoom에서 전체 이미지를 다운로드 하는 대신, 화면 이미지의 현재 크기에 필요한 타일만 다운로드하여 표시. 로드시간을 단축시킨다.

또한, 이미지를 로드시 고해상도인 경우 파일 사이즈가 적지 않기 때문에, 저해상도의 이미지를 미리 다운로드 한 뒤 표시하고, 고해상도의 이미지가 다운로드 될 때마다 이미지를 변경하여 준다.
유저에게는 뿌옇게 표시 된 뒤(저해상도의 이미지가 확대 되어), 시간이 지날 수록(고해상도의 이미지를 다운 받을 수록) 선명해지는 것처럼 느낄 수 있도록 표현된다.

이미지 피라미드를 사용자가 직접 만드는 것은 매우 힘든 일이며, 이를 위한 Deep zoom Composer 같은 도구를 사용하여 이미지 피라미드를 생성.

   

스프링 에니메이션
이미지 피라미드 간 축소,확대 또는 이미지 이동 의 움직임에는 스프링 에니메이션이 사용된다.
사용자에게 이미지의 이동, 확대, 축소가 자연스럽게 표시된다.

'Silverlight' 카테고리의 다른 글

Style 정의하기  (0) 2009.06.16
VisualStateManager  (0) 2009.06.15
Storyboard (Animation in Silverlight)  (0) 2009.06.15
Layout Panel #5 (WrapPanel)  (0) 2009.06.12
Layout Panel #4 (DockPanel)  (0) 2009.06.12
Layout Panel #3 (StackPanel)  (0) 2009.06.12
Layout Panel #2 (Grid)  (0) 2009.06.11
Layout Panel #1 (Canvas)  (0) 2009.06.11
Coposition에서의 ViewPort, AspectRatio, 좌표계, ElementalPoint, LogicalPoint  (0) 2009.06.10
Deepzoom Composer로 Export 된 결과물  (0) 2009.06.09
Deepzoom  (0) 2009.06.09
Posted by Min-gu, Kim

티스토리 툴바