Windows Ink를 지원하는 Windows 앱은 잉크 스트로크를 직렬화하고 ISF(Ink Serialized Format) 파일로 역직렬화할 수 있습니다. ISF 파일은 모든 잉크 스트로크 속성 및 동작에 대한 추가 메타데이터가 있는 GIF 이미지입니다. 잉크를 사용하지 않는 앱은 알파 채널 배경 투명도를 포함하여 정적 GIF 이미지를 볼 수 있습니다.
비고
ISF는 잉크의 가장 압축된 영구 표현입니다. GIF 파일과 같은 이진 문서 형식 내에 포함되거나 클립보드에 직접 배치할 수 있습니다.
ISF(잉크 직렬화된 형식) 사양은 Microsoft 다운로드 센터에서 다운로드할 수 있습니다.
중요 API: InkCanvas, Windows.UI.Input.Inking
파일에 잉크 스트로크 저장
여기서는 InkCanvas 컨트롤에 그려진 잉크 스트로크를 저장하는 방법을 보여 줍니다.
이 샘플을 다운로드합니다 ISF(Ink Serialized Format) 파일에서 잉크 스트로크를 저장하고 불러옵니다
먼저 UI를 설정합니다.
UI에는 "저장", "로드" 및 "지우기" 단추 및 InkCanvas가 포함됩니다.
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="HeaderPanel" Orientation="Horizontal" Grid.Row="0">
<TextBlock x:Name="Header"
Text="Basic ink store sample"
Style="{ThemeResource HeaderTextBlockStyle}"
Margin="10,0,0,0" />
<Button x:Name="btnSave"
Content="Save"
Margin="50,0,10,0"/>
<Button x:Name="btnLoad"
Content="Load"
Margin="50,0,10,0"/>
<Button x:Name="btnClear"
Content="Clear"
Margin="50,0,10,0"/>
</StackPanel>
<Grid Grid.Row="1">
<InkCanvas x:Name="inkCanvas" />
</Grid>
</Grid>
그런 다음 몇 가지 기본 잉크 입력 동작을 설정합니다.
InkPresenter 펜과 마우스의 입력 데이터를 잉크 스트로크(InputDeviceTypes)로 해석하도록 구성되며 단추의 클릭 이벤트에 대한 수신기가 선언됩니다.
public MainPage()
{
this.InitializeComponent();
// Set supported inking device types.
inkCanvas.InkPresenter.InputDeviceTypes =
Windows.UI.Core.CoreInputDeviceTypes.Mouse |
Windows.UI.Core.CoreInputDeviceTypes.Pen;
// Listen for button click to initiate save.
btnSave.Click += btnSave_Click;
// Listen for button click to initiate load.
btnLoad.Click += btnLoad_Click;
// Listen for button click to clear ink canvas.
btnClear.Click += btnClear_Click;
}
마지막으로 저장 단추의 클릭 이벤트 처리기에 잉크를 저장합니다.
FileSavePicker를 통해 사용자는 잉크 데이터를 저장할 파일과 위치를 선택할 수 있습니다.
파일이 선택되면 설정이 ReadWrite인 IRandomAccessStream 스트림을 엽니다.
그런 다음 SaveAsync를 호출하여 InkStrokeContainer에 의해 관리되는 잉크 스트로크를 스트림으로 직렬화합니다.
// Save ink data to a file.
private async void btnSave_Click(object sender, RoutedEventArgs e)
{
// Get all strokes on the InkCanvas.
IReadOnlyList<InkStroke> currentStrokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
// Strokes present on ink canvas.
if (currentStrokes.Count > 0)
{
// Let users choose their ink file using a file picker.
// Initialize the picker.
Windows.Storage.Pickers.FileSavePicker savePicker =
new Windows.Storage.Pickers.FileSavePicker();
savePicker.SuggestedStartLocation =
Windows.Storage.Pickers.PickerLocationId.DocumentsLibrary;
savePicker.FileTypeChoices.Add(
"GIF with embedded ISF",
new List<string>() { ".gif" });
savePicker.DefaultFileExtension = ".gif";
savePicker.SuggestedFileName = "InkSample";
// Show the file picker.
Windows.Storage.StorageFile file =
await savePicker.PickSaveFileAsync();
// When chosen, picker returns a reference to the selected file.
if (file != null)
{
// Prevent updates to the file until updates are
// finalized with call to CompleteUpdatesAsync.
Windows.Storage.CachedFileManager.DeferUpdates(file);
// Open a file stream for writing.
IRandomAccessStream stream = await file.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite);
// Write the ink strokes to the output stream.
using (IOutputStream outputStream = stream.GetOutputStreamAt(0))
{
await inkCanvas.InkPresenter.StrokeContainer.SaveAsync(outputStream);
await outputStream.FlushAsync();
}
stream.Dispose();
// Finalize write so other apps can update file.
Windows.Storage.Provider.FileUpdateStatus status =
await Windows.Storage.CachedFileManager.CompleteUpdatesAsync(file);
if (status == Windows.Storage.Provider.FileUpdateStatus.Complete)
{
// File saved.
}
else
{
// File couldn't be saved.
}
}
// User selects Cancel and picker returns null.
else
{
// Operation cancelled.
}
}
}
비고
GIF는 잉크 데이터를 저장하는 데 지원되는 유일한 파일 형식입니다. 그러나 다음 섹션에서 설명한 LoadAsync 메서드는 이전 버전과의 호환성을 위해 추가 형식을 지원합니다.
파일에서 잉크 획 불러오기
여기서는 파일에서 잉크 스트로크를 로드하고 InkCanvas 컨트롤에 렌더링하는 방법을 보여 줍니다.
이 샘플을 다운로드합니다 ISF(Ink Serialized Format) 파일에서 잉크 스트로크를 저장하고 불러옵니다
먼저 UI를 설정합니다.
UI에는 "저장", "로드" 및 "지우기" 단추 및 InkCanvas가 포함됩니다.
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="HeaderPanel" Orientation="Horizontal" Grid.Row="0">
<TextBlock x:Name="Header"
Text="Basic ink store sample"
Style="{ThemeResource HeaderTextBlockStyle}"
Margin="10,0,0,0" />
<Button x:Name="btnSave"
Content="Save"
Margin="50,0,10,0"/>
<Button x:Name="btnLoad"
Content="Load"
Margin="50,0,10,0"/>
<Button x:Name="btnClear"
Content="Clear"
Margin="50,0,10,0"/>
</StackPanel>
<Grid Grid.Row="1">
<InkCanvas x:Name="inkCanvas" />
</Grid>
</Grid>
그런 다음 몇 가지 기본 잉크 입력 동작을 설정합니다.
InkPresenter 펜과 마우스의 입력 데이터를 잉크 스트로크(InputDeviceTypes)로 해석하도록 구성되며 단추의 클릭 이벤트에 대한 수신기가 선언됩니다.
public MainPage()
{
this.InitializeComponent();
// Set supported inking device types.
inkCanvas.InkPresenter.InputDeviceTypes =
Windows.UI.Core.CoreInputDeviceTypes.Mouse |
Windows.UI.Core.CoreInputDeviceTypes.Pen;
// Listen for button click to initiate save.
btnSave.Click += btnSave_Click;
// Listen for button click to initiate load.
btnLoad.Click += btnLoad_Click;
// Listen for button click to clear ink canvas.
btnClear.Click += btnClear_Click;
}
마지막으로 로드 단추의 클릭 이벤트 처리기에서 잉크를 로드 합니다.
FileOpenPicker를 통해 사용자는 파일과 저장된 잉크 데이터를 불러올 위치를 모두 선택할 수 있습니다.
파일이 선택되면 가읽기로 설정된 IRandomAccessStream 스트림을 엽니다.
그런 다음 LoadAsync 호출하여 저장된 잉크 스트로크를 읽고, 역직렬화하고, InkStrokeContainer에 로드합니다. InkStrokeContainer에 스트로크를 로드하면 InkPresenter에서 즉시 InkCanvas로 렌더링됩니다.
비고
새 스트로크가 로드되기 전에 InkStrokeContainer의 모든 기존 스트로크가 지워집니다.
// Load ink data from a file.
private async void btnLoad_Click(object sender, RoutedEventArgs e)
{
// Let users choose their ink file using a file picker.
// Initialize the picker.
Windows.Storage.Pickers.FileOpenPicker openPicker =
new Windows.Storage.Pickers.FileOpenPicker();
openPicker.SuggestedStartLocation =
Windows.Storage.Pickers.PickerLocationId.DocumentsLibrary;
openPicker.FileTypeFilter.Add(".gif");
// Show the file picker.
Windows.Storage.StorageFile file = await openPicker.PickSingleFileAsync();
// User selects a file and picker returns a reference to the selected file.
if (file != null)
{
// Open a file stream for reading.
IRandomAccessStream stream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
// Read from file.
using (var inputStream = stream.GetInputStreamAt(0))
{
await inkCanvas.InkPresenter.StrokeContainer.LoadAsync(inputStream);
}
stream.Dispose();
}
// User selects Cancel and picker returns null.
else
{
// Operation cancelled.
}
}
비고
GIF는 잉크 데이터를 저장하는 데 지원되는 유일한 파일 형식입니다. 그러나 LoadAsync 메서드는 이전 버전과의 호환성을 위해 다음 형식을 지원합니다.
포맷 | 설명 |
---|---|
잉크 직렬화 형식 (InkSerializedFormat) | ISF를 사용하여 유지되는 잉크를 지정합니다. 잉크의 가장 압축된 영구 표현입니다. 이진 문서 형식 내에 포함되거나 클립보드에 직접 배치할 수 있습니다. |
Base64InkSerializedFormat | ISF를 base64 스트림으로 인코딩하여 유지되는 잉크를 지정합니다. 이 형식은 XML 또는 HTML 파일에서 직접 잉크를 인코딩할 수 있도록 제공됩니다. |
GIF | ISF가 포함된 GIF 파일을 파일 내에 포함된 메타데이터로 사용하여 유지되는 잉크를 지정합니다. 이렇게 하면 잉크를 사용할 수 없는 애플리케이션에서 잉크를 볼 수 있으며 잉크 사용 애플리케이션으로 반환될 때 전체 잉크 충실도를 유지할 수 있습니다. 이 형식은 HTML 파일 내에서 잉크 콘텐츠를 전송하고 잉크 및 잉크가 아닌 애플리케이션에서 사용할 수 있도록 하는 데 적합합니다. |
Base64Gif | base64로 인코딩된 강화된 GIF를 사용하여 유지되는 잉크를 지정합니다. 이 형식은 잉크를 XML 또는 HTML 파일에서 직접 인코딩하여 나중에 이미지로 변환할 때 제공됩니다. 가능한 사용은 모든 잉크 정보를 포함하도록 생성된 XML 형식이며 XSLT(Extensible Stylesheet Language Transformations)를 통해 HTML을 생성하는 데 사용됩니다. |
클립보드를 사용하여 잉크 스트로크 복사 및 붙여넣기
여기서는 클립보드를 사용하여 앱 간에 잉크 스트로크를 전송하는 방법을 보여 줍니다.
클립보드 기능을 지원하기 위해 기본 제공 InkStrokeContainer 잘라내기 및 복사 명령을 사용하려면 하나 이상의 잉크 스트로크를 선택해야 합니다.
이 예제에서는 펜 배럴 단추(또는 마우스 오른쪽 단추)를 사용하여 입력을 수정할 때 스트로크 선택을 사용하도록 설정합니다. 스트로크 선택 구현에 대한 전체 예제를 보려면 고급 처리를 위한 통과 입력의 펜 및 스타일러스 상호 작용을 참조하세요.
이 샘플을 다운로드하십시오 클립보드에서 잉크 스트로크를 저장하고 로드하십시오
먼저 UI를 설정합니다.
UI에는 InkCanvas 및 선택 캔버스와 함께 "잘라내기", "복사", "붙여넣기" 및 "지우기" 단추가 포함됩니다.
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="HeaderPanel" Orientation="Horizontal" Grid.Row="0">
<TextBlock x:Name="tbHeader"
Text="Basic ink store sample"
Style="{ThemeResource HeaderTextBlockStyle}"
Margin="10,0,0,0" />
<Button x:Name="btnCut"
Content="Cut"
Margin="20,0,10,0"/>
<Button x:Name="btnCopy"
Content="Copy"
Margin="20,0,10,0"/>
<Button x:Name="btnPaste"
Content="Paste"
Margin="20,0,10,0"/>
<Button x:Name="btnClear"
Content="Clear"
Margin="20,0,10,0"/>
</StackPanel>
<Grid x:Name="gridCanvas" Grid.Row="1">
<!-- Canvas for displaying selection UI. -->
<Canvas x:Name="selectionCanvas"/>
<!-- Inking area -->
<InkCanvas x:Name="inkCanvas"/>
</Grid>
</Grid>
그런 다음 몇 가지 기본 잉크 입력 동작을 설정합니다.
InkPresenter는 펜과 마우스의 입력 데이터를 잉크 스트로크(InputDeviceTypes)로 해석하도록 구성되어 있습니다. 단추의 클릭 이벤트 수신기뿐만 아니라 선택 기능을 위한 포인터 및 스트로크 이벤트 수신기도 여기에 선언되어 있습니다.
스트로크 선택 구현에 대한 전체 예제를 보려면 고급 처리를 위한 통과 입력의 펜 및 스타일러스 상호 작용을 참조하세요.
public MainPage()
{
this.InitializeComponent();
// Set supported inking device types.
inkCanvas.InkPresenter.InputDeviceTypes =
Windows.UI.Core.CoreInputDeviceTypes.Mouse |
Windows.UI.Core.CoreInputDeviceTypes.Pen;
// Listen for button click to cut ink strokes.
btnCut.Click += btnCut_Click;
// Listen for button click to copy ink strokes.
btnCopy.Click += btnCopy_Click;
// Listen for button click to paste ink strokes.
btnPaste.Click += btnPaste_Click;
// Listen for button click to clear ink canvas.
btnClear.Click += btnClear_Click;
// By default, the InkPresenter processes input modified by
// a secondary affordance (pen barrel button, right mouse
// button, or similar) as ink.
// To pass through modified input to the app for custom processing
// on the app UI thread instead of the background ink thread, set
// InputProcessingConfiguration.RightDragAction to LeaveUnprocessed.
inkCanvas.InkPresenter.InputProcessingConfiguration.RightDragAction =
InkInputRightDragAction.LeaveUnprocessed;
// Listen for unprocessed pointer events from modified input.
// The input is used to provide selection functionality.
inkCanvas.InkPresenter.UnprocessedInput.PointerPressed +=
UnprocessedInput_PointerPressed;
inkCanvas.InkPresenter.UnprocessedInput.PointerMoved +=
UnprocessedInput_PointerMoved;
inkCanvas.InkPresenter.UnprocessedInput.PointerReleased +=
UnprocessedInput_PointerReleased;
// Listen for new ink or erase strokes to clean up selection UI.
inkCanvas.InkPresenter.StrokeInput.StrokeStarted +=
StrokeInput_StrokeStarted;
inkCanvas.InkPresenter.StrokesErased +=
InkPresenter_StrokesErased;
}
마지막으로 스트로크 선택 지원을 추가한 후 잘라내기, 복사및 붙여넣기 버튼의 클릭 이벤트 처리기에서 클립보드 기능을 구현합니다.
잘라내기를 위해 먼저 CopySelectedToClipboard을(를) InkPresenter의 InkStrokeContainer에서 호출합니다.
다음으로, 잉크 캔버스에서 스트로크를 제거하기 위해 DeleteSelected을 호출합니다.
마지막으로 선택 캔버스에서 모든 선택 스트로크를 삭제합니다.
private void btnCut_Click(object sender, RoutedEventArgs e)
{
inkCanvas.InkPresenter.StrokeContainer.CopySelectedToClipboard();
inkCanvas.InkPresenter.StrokeContainer.DeleteSelected();
ClearSelection();
}
// Clean up selection UI.
private void ClearSelection()
{
var strokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
foreach (var stroke in strokes)
{
stroke.Selected = false;
}
ClearDrawnBoundingRect();
}
private void ClearDrawnBoundingRect()
{
if (selectionCanvas.Children.Any())
{
selectionCanvas.Children.Clear();
boundingRect = Rect.Empty;
}
}
복사를 위해서는 InkPresenter의 InkStrokeContainer 에서 CopySelectedToClipboard 를 호출하기만 하면 됩니다.
private void btnCopy_Click(object sender, RoutedEventArgs e)
{
inkCanvas.InkPresenter.StrokeContainer.CopySelectedToClipboard();
}
붙여넣기에서는 CanPasteFromClipboard 를 호출하여 클립보드의 콘텐츠를 잉크 캔버스에 붙여넣을 수 있도록 합니다.
이 경우, 클립보드에 있는 잉크 스트로크를 InkStrokeContainer 에 삽입하기 위해 PasteFromClipboard 을 호출하고, 그런 다음 InkPresenter에서 스트로크를 잉크 캔버스에 렌더링합니다.
private void btnPaste_Click(object sender, RoutedEventArgs e)
{
if (inkCanvas.InkPresenter.StrokeContainer.CanPasteFromClipboard())
{
inkCanvas.InkPresenter.StrokeContainer.PasteFromClipboard(
new Point(0, 0));
}
else
{
// Cannot paste from clipboard.
}
}
관련 문서
주제 샘플
기타 샘플
Windows developer