СПЕЦИАЛЬНЫЕ Windows 10 ВЫПУСК 2015

ТОМ 30 НОМЕР 11

Цифровые чернила - Перьевое взаимодействие в Windows 10

Коннор Уейнс | Windows 2015

Продукты и технологии:

DirectInk, приложения Universal Windows Platform

В статье рассматриваются:

  • получение рукописного ввода в приложениях;
  • редактирование, сохранение и загрузка рукописного текста;
  • дополнительная функциональность поддержки рукописного ввода.

В этой статье я рассмотрю, как вы сможете поспособствовать естественному для пользователя взаимодействию, используя рукописный ввод (inking). Так называемые цифровые чернила (digital ink) — во многом подобно перу на бумаге — «текут» с кончика вашего устройства перьевого ввода (digital pen device), стилуса, пальца или курсора мыши и визуализируются на экране. Чтобы приступить к работе с цифровыми чернилами и поддержкой рукописного ввода в Windows 10, я начну с обсуждения фундаментального вопроса: почему важно использовать рукописный ввод в приложении?

Люди столетиями выражали свои мысли и идеи через рукописный текст. Несмотря на изобретение мыши и клавиатуры, перо и бумага по-прежнему играют ключевую роль в нашей жизни — от стикеров и досок в наших офисах до тетрадей в школах и книжек-раскрасок для детей.

Письмо на бумаге отражает нашу индивидуальность и дает полную свободу, что делает его идеальным для выражения наших творчества и эмоций. Передача мыслей на бумаге и наброски схем в записках также удобнее для размышлений, запоминания и обучения, согласно исследованию за 2013 год, опубликованному в Psychological Science (bit.ly/1tKDrhv), в котором исследователи из Принстона и UCLA обнаружили, что рукописные заметки гораздо лучше для восприятия в долгосрочном плане, чем печатные.

Учитывая эти преимущества чернил над традиционным печатным вводом с клавиатуры, вообразите, что вы могли бы писать от руки на устройствах так же легко, как писать на бумаге, и задействовать вычислительную мощь компьютера для выполнения таких задач, которые нельзя выполнить в физическом мире. С помощью цифровых чернил вы можете легко варьировать цвет и внешний вид чернил, как и в реальном мире, но зайти несколько дальше, анализируя контент и форму рукописного ввода, чтобы предоставить метаданные, или преобразуя его в другой контент вроде текста, фигур или команд. Это открывает в рукописном вводе совершенно новое измерение, которое невозможно в ваших повседневных записях, что делает рукописный ввод мощным инструментом для рисования, создания заметок, аннотирования и взаимодействия в вашем приложении. По мере того, как рынок перьевых и сенсорных устройств продолжает расширяться, поддержка рукописного ввода становится важным методом взаимодействия для пользователей и разработчиков приложений.

В Windows 10 очень легко ввести поддержку цифровых чернил в приложение благодаря платформе DirectInk. DirectInk предоставляет набор богатых и расширяемых Windows Runtime (WinRT) API, которые позволяют вам принимать, визуализировать и управлять перьевым вводом в приложениях Universal Windows Platform (UWP). С помощью DirectInk вы получаете те же возможности и производительность в работе с перьевым вводом, что и браузер Microsoft Edge, Universal OneNote и Handwriting Panel. Вот краткий обзор средств, предлагаемых DirectInk вашему приложению.

  • Красивый рукописный текст DirectInk использует сглаживание рукописного ввода и алгоритмы рендеринга Безье, гарантируя, что ваш рукописный текст всегда будет выглядеть четко и красиво при использовании как сенсорного, так и перьевого ввода.
  • Низкая латентность, малый объем занимаемой памяти DirectInk использует высокоприоритетный фоновый поток и прогнозирование ввода, чтобы обеспечить немедленную визуализацию рукописного ввода, и эффективно управляет ресурсами для минимизации издержек в приложении.
  • Простая и расширяемая поверхность API DirectInk предоставляет такие API, как InkCanvas и InkPresenter, которые позволяют вам быстро приступить к приему рукописного ввода и управлению им и предлагают дополнительную функциональность, дающую возможность создавать богатую и сложную функциональность в вашем приложении.

Надеюсь, что к этому моменту вы заинтересованы начать использовать рукописный ввод в своем приложении. Теперь я рассмотрю, как использовать платформу DirectInk в приложении и предоставлять пользователям удобную среду для рукописного ввода.

Прием рукописного ввода в приложении

Чтобы приступить к использованию цифровых чернил, первым делом нужно настроить поверхность, где будет приниматься ввод, а затем визуализироваться как чернила. В приложения Windows 8.1 Store введение поддержки рукописного ввода в приложение было весьма основательным процессом, который включал создание Canvas, прослушивание событий ввода, создание и рендеринг каждого движения пера (strokes), используя написанный вами код рендеринга. В UWP-приложении начало приема рукописного ввода требует лишь перетащить InkCanvas в ваше приложение:

<Grid>
  <InkCanvas x:Name="myInkCanvas"/>
</Grid>

Как видно на рис. 1, единственная строка кода дает вам прозрачный оверлей, который начинает принимать перьевой ввод и визуализировать его так, будто вы пишете черной шариковой ручкой. Кнопка стирания траектории пера также стирает любой принятый через это перо ввод. Это просто великолепно при первых шагах в работе с чернилами, но что делать, если нужно изменять то, как принимается или отображается рукописный ввод?

Применение InkCanvas для приема перьевого ввода с имитацией письма черной шариковой ручкой
Рис. 1. Применение InkCanvas для приема перьевого ввода с имитацией письма черной шариковой ручкой

Через InkCanvas вы можете обращаться к своему InkPresenter, который предоставляет функциональность для управления внешним видом и конфигурацией рукописного ввода. Хотя перьевой ввод обеспечивает самую удобную пользовательскую среду (UX) для работы с цифровыми чернилами, во многих системах нет пера. InkPresenter позволяет принимать «чернильные» данные при любой комбинации перьевого, сенсорного ввода и ввода от мыши, а типы ввода, не выбранные вами, будут просто доставляться как события указателя (pointer events) в XAML-элемент InkCanvas. С помощью InkPresenter вы также можете управлять атрибутами рисования «чернильных» данных, собираемых в InkCanvas, что позволяет изменять размер кисти, цвет и др. В качестве примера этих средств ваше приложение могло бы сконфигурировать InkCanvas на прием «чернильных» данных от пера, мыши и сенсорного ввода и эмуляцию каллиграфической кисти:

InkPresenter myPresenter = myInkCanvas.InkPresenter;
myPresenter.InputDeviceTypes = Windows.UI.Core.CoreInputDeviceTypes.Pen |
                               Windows.UI.Core.CoreInputDeviceTypes.Touch |
                               Windows.UI.Core.CoreInputDeviceTypes.Mouse;
InkDrawingAttributes myAttributes = myPresenter.CopyDefaultDrawingAttributes();
myAttributes.Color = Windows.UI.Colors.Crimson;
myAttributes.PenTip = PenTipShape.Rectangle;
myAttributes.PenTipTransform =
  System.Numerics.Matrix3x2.CreateRotation((float) Math.PI/4);
myAttributes.Size = new Size(2,6);
myPresenter.UpdateDefaultDrawingAttributes(myAttributes);

Это дало бы результат, приведенный на рис. 2.

Эмуляция каллиграфической кисти, используя атрибуты DrawingAttribute в InkPresenter
Рис. 2. Эмуляция каллиграфической кисти, используя атрибуты DrawingAttribute в InkPresenter

DirectInk поддерживает гораздо больше встроенных конфигураций для ввода и его визуализации, позволяя вам выполнять рендеринг «чернильных» данных, как при использовании маркера, получать событие при приеме очередного движения кисти, обращаться к более детализированным событиям ввода с применением нескольких указателей в дополнительных конфигурациях.

Редактирование, сохранение и загрузка рукописного текста

Теперь, когда вы собрали какое-то количество «чернильных» данных, возникает вопрос: что с ними можно сделать? Пользователям часто нужна возможность стирать или редактировать свой рукописный ввод либо сохранять его для доступа впоследствии. Чтобы предоставить такие возможности своим пользователям, вам понадобится обеспечить доступ и модификацию «чернильных» данных для всех движений кисти, которые DirectInk визуализировал на экране.

Когда эти данные принимаются в InkCanvas, DirectInk сохраняет их в InkStrokeContainer внутри InkPresenter. Этот InkStrokeContainer содержит WinRT-объекты, представляющие каждое движение (штрихи) кисти на вашем холсте (canvas); когда приложение вносит изменения в этот контейнер, они визуализируются на экране. Это позволяет программным способом добавлять, удалять или модифицировать штрихи кисти, а также дает возможность DirectInk держать вас в курсе любых изменений, вносимых в штрихи кисти на экране. Давайте рассмотрим некоторые из распространенных операций взаимодействия, которые вы можете реализовать, используя связь между InkPresenter и его InkStrokeContainer.

Стирание Хотя InkCanvas поддерживает стирание с помощью кнопки стирания перьевого ввода по умолчанию, для стирания ввода от мыши и сенсорного ввода требуется некоторая настройка в InkPresenter. DirectInk предлагает встроенную поддержку стирания «чернил» для любого поддерживаемого ввода через свойство Mode в InputProcessingConfiguration. Вот пример кнопки, которая задает режим Erasing:

private void Eraser_Click(object sender,
  RoutedEventArgs e)
{
  myInkCanvas.InkPresenter.
    InputProcessingConfiguration.Mode =
    InkInputProcessingMode.Erasing;
}

Когда эта кнопка нажата, весь DirectInk-ввод, принимаемый InkCanvas, интерпретируется как ластик. Если пользовательский ввод пересекается с каким-то штрихом после установки этого режима, этот штрих удаляется из InkStrokeContainer в InkPresenter и убирается с экрана. При использовании пера в режиме Inking нажатие кнопки стирания всегда обрабатывается как включение режима Erase.

Выделение К сожалению, на данный момент в DirectInk нет встроенной поддержки выделения, но предлагается способ самостоятельной разработки этой операции на основе событий Unprocessed Input (необрабатываемый ввод). Такие события генерируются всякий раз, когда DirectInk принимает ввод, который он должен прослушивать, но не визуализировать в «чернила». Это можно сделать для всего ввода, задав режим конфигурации обработки DirectInk как None, а также настроить это только для правой кнопки мыши и кнопки пера, используя свойство RightDragAction:

myInkCanvas.InkPresenter.InputProcessingConfiguration.RightDragAction =
  InkInputRightDragAction.LeaveUnprocessed;

Например, на рис. 3 показано, как можно использовать события Unprocessed Input для создания петли выделения (рис. 4), позволяющей выделять штрихи на экране.

Рис. 3. Применение событий Unprocessed Input для создания петли выделения

...
myInkCanvas.InkPresenter.UnprocessedInput.PointerPressed += StartLasso;
myInkCanvas.InkPresenter.UnprocessedInput.PointerMoved += ContinueLasso;
myInkCanvas.InkPresenter.UnprocessedInput.PointerReleased += CompleteLasso;
...
private void StartLasso(
  InkUnprocessedInput sender,Windows.UI.Core.PointerEventArgs args)
{
  selectionLasso = new Polyline()
  {
    Stroke = new SolidColorBrush(Windows.UI.Colors.Black),
    StrokeThickness = 2,
    StrokeDashArray = new DoubleCollection() { 7, 3},
  };
  selectionLasso.Points.Add(args.CurrentPoint.RawPosition);
  AddSelectionLassoToVisualTree();
}
private void ContinueLasso(
  InkUnprocessedInput sender, Windows.UI.Core.PointerEventArgs args)
{
  selectionLasso.Points.Add(args.CurrentPoint.RawPosition);
}
private void CompleteLasso(
  InkUnprocessedInput sender, Windows.UI.Core.PointerEventArgs args)
{
  selectionLasso.Points.Add(args.CurrentPoint.RawPosition);
  bounds =
    myInkCanvas.InkPresenter.StrokeContainer.SelectWithPolyLine(
    selectionLasso.Points);
  DrawBoundingRect(bounds);
}

Выделение (выбор) штрихов с помощью петли
Рис. 4. Выделение (выбор) штрихов с помощью петли

Выбрав штрихи, вы можете вызвать метод InkStrokeContainer.MoveSelected, чтобы транслировать штрихи, или использовать свойство InkStroke.PointTransform, чтобы применить к штрихам аффинное преобразование (affine transform). Когда штрих или набор штрихов, управляемых InkStrokeContainer, преобразуется таким образом, DirectInk будет подхватывать эти изменения и визуализировать их на экране.

Сохранение и загрузка DirectInk поддерживает сохранение и загрузку «чернильных» данных через Ink Serialized Format (ISF), который сохраняет эти данные в векторном формате, упрощающем их обмен и редактирование. Он доступен через InkStrokeContainer-функции SaveAsync и LoadAsync.

SaveAsync принимает данные штрихов, хранящиеся на этот момент в InkStrokeContainer, и сохраняет их как GIF-файл со встроенными ISF-данными. На рис. 5 показано, как сохранить штрихи из InkStrokeContainer.

Рис. 5. Сохранение «чернильных» данных из InkStrokeContainer

var savePicker = new FileSavePicker();
savePicker.SuggestedStartLocation =
  Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
savePicker.FileTypeChoices.Add("Gif with embedded ISF",
  new System.Collections.Generic.List<string> { ".gif" });
StorageFile file = await savePicker.PickSaveFileAsync();
if (null != file)
{
  try
  {
    using (IRandomAccessStream stream =
      await file.OpenAsync(FileAccessMode.ReadWrite))
    {
      await myInkCanvas.InkPresenter.StrokeContainer.SaveAsync(stream);
    } 
  }
  catch (Exception ex)
  {
    GenerateErrorMessage();
  }
}

LoadAsync будет выполнять противоположную операцию, очищая штрихи, уже находящиеся в InkStrokeContainer, и загружая новый набор штрихов из ISF- или GIF-файла со встроенными ISF-данными. DirectInk после загрузки штрихов в InkStrokeContainer автоматически визуализирует их на экране.

Дополнительная функциональность

Хотя возможность редактирования и других операций над рукописным вводом на экране крайне важна для поддержки взаимодействия пользователей с «чернилами», ее может оказаться недостаточно для удовлетворения всех ваших потребностей. Чем сложнее ваши замыслы, тем больше вам придется отходить в своем приложении от набора взаимодействий, предоставляемых DirectInk по умолчанию. Давайте рассмотрим несколько способов, которыми DirectInk позволяет создавать богатые и разнообразные функции для работы c рукописным вводом.

Распознавание рукописного ввода Результаты такого ввода — это не просто пиксели на экране. Рукописный ввод пользователя может интерпретироваться как картинка, схема, фигура или текст. Распознавание позволяет сопоставить рукописный ввод с его сутью или заменить на представляемый им контент. Например, если пользователь пишет текст в приложении для заметок, вы можете распознавать текст, представляемый рукописным вводом, и использовать эти текстовые данные для генерации результатов поиска, когда пользователь вводит запрос в строку поиска. Такое распознавание текста обеспечивается InkRecognizerContainer. На рис. 6 показано, как с помощью InkRecognizerContainer интерпретировать рукописный ввод в качестве символов на упрощенном китайском (Simplified Chinese).

Рис. 6. Интерпретация рукописного ввода как символов на упрощенном китайском с использованием InkRecognizerContainer

{Для верстки: в следующем листинге будет много иероглифов, не потеряйте их}
async void OnRecognizeAsync(object sender, RoutedEventArgs e)
{
  InkRecognizerContainer recoContainer = new InkRecognizerContainer();
  IReadOnlyList<InkRecognizer> installedRecognizers =
    recoContainer.GetRecognizers();
  foreach (InkRecognizer recognizer in installedRecognizers)
  {
    if (recognizer.Name.Equals("Microsoft 中文(简体)手写识别器"))
    {
      recoContainer.SetDefaultRecognizer(recognizer);
      break;
    }
  }
  var results = await recoContainer.RecognizeAsync(
    myInkCanvas.InkPresenter.StrokeContainer,InkRecognitionTarget.All);
  if (results.Count > 0)
  {
    string str = "Result:";
    foreach (var r in results)
    {
      str += " " + r.GetTextCandidates()[0];
    }
  }
}

Хотя это позволяет распознавать рукописный ввод как текст, у InkRecognizerContainer есть ограничение в том плане, что в настоящее время он поддерживает распознавание текста только из 33 языковых пакетов. Если бы вам понадобилось распознавать текст на другом языке или распознавать символы, фигуры или другие, более абстрактные интерпретации, то пришлось бы создавать соответствующую логику с нуля. К счастью, объект InkStroke содержит функцию GetInkPoints, которая дает возможность получать x/y-координаты каждой точки ввода, используемой для конструирования штриха. А значит, вы можете создать алгоритм для анализа точек ввода штриха или набора штрихов и интерпретировать их как угодно — в качестве символов, фигур, команд или чего-то другого.

Независимый ввод DirectInk — мощный механизм для рендеринга рукописного ввода, который действует в соответствии с простым набором правил для ввода: чернила либо визуализируются для данного штриха, либо — нет. Чтобы принять это решение, он анализирует поддерживаемые типы ввода, а также конфигурацию обработки ввода, предоставляемую для выбранного режима, и конфигурацию для действий с перетаскиванием правой кнопкой мыши. Это приводит к основательной нехватке контекста, который мог бы понадобиться вашему приложению для рукописного ввода, скажем, в определенных областях холста запрещать рисование рукописного ввода или поддерживать какой-то жест, после распознавания которого прием рукописного ввода прекращается. Чтобы дать возможность для принятия таких решений, DirectInk обеспечивает доступ ко вводу до того, как он начнет его обработку, через события Independent Input (события независимого ввода). Эти события позволяют вам анализировать ввод до его визуализации DirectInk, так что, если вы принимаете событие нажатия в области, где рукописный ввод не разрешен, или событие перемещения, которое завершается отслеживаемым вами жестом, вы можете просто помечать событие как Handled (обработанное).

Когда событие помечено как Handled, DirectInk прекратит обработку штриха, и, если штрих уже находится в процессе обработки, он будет отменен и удален с экрана. Однако вы должны соблюдать осторожность, используя эти события. Поскольку они происходят в фоновом потоке DirectInk, а не в UI-потоке, любая интенсивная обработка, выполняемая вами в обработчике этого события, или ожидание операций, выполняемых в более медленном потоке, таком как UI-поток, может привести к задержке, которая заметно отразится на времени реакции на ваш рукописный ввод.

Режим Custom Drying Одно из самых сложных средств DirectInk — режим Custom Drying (настраиваемое высыхание), который позволяет вашему приложению визуализировать и управлять завершенными, или «сухими», чернильными штрихами на вашей поверхности DirectX, в то же время давая возможность DirectInk высокоэффективно обрабатывать визуализацию еще не завершенных, или «влажных», штрихов. Хотя режим высыхания по умолчанию в DirectInk позволяет обрабатывать большинство сценариев, которые могут вам понадобиться в приложении, несколько сценариев требуют независимого управления рукописным вводом:

  • чередование (interleaving) рукописного и нерукописного контента (текст, фигуры) с сохранением z-порядка;
  • высокопроизводительное панорамирование и масштабирование на большом холсте для рукописного ввода с большим количеством «чернильных» штрихов;
  • синхронно высыхающие чернила в DirectX-объекте вроде прямой линии или фигуры.

В Windows 10 режим Custom Drying поддерживает синхронизацию с SurfaceImageSource (SIS) или VirtualSurfaceImageSource (VSIS). Как SIS, так и VSIS предоставляют общую (shared) поверхность DirectX для вашего приложения, на которой оно должно рисовать и выполнять композицию, но VSIS создает виртуальную поверхность, большую размера экрана, для высокопроизводительного панорамирования и масштабирования. Поскольку визуальное обновление этих поверхностей синхронизируется с XAML UI-потоком, при рендеринге рукописного ввода в SIS или VSIS он может одновременно удаляться из «влажного» слоя DirectInk. Custom Drying также поддерживает высыхание чернил в SwapChainPanel, но не гарантирует синхронизацию. Так как SwapChainPanel не синхронизируется с UI-потоком, будет небольшое перекрытие между моментом рендеринга рукописного ввода в SwapChainPanel и моментом его удаления из влажного слоя DirectInk.

Активировав Custom Drying, вы получаете тонкий контроль над большей частью функциональности DirectInk, которую она предоставляет по умолчанию, что позволяет вам писать логику, управляющую тем, как происходит визуализация «чернильных» данных и их стирание с сухой поверхности, а также определяющую, как ваше приложение управляет данными «чернильных» штрихов. Чтобы помочь вам в создании этой функциональности, многие компоненты DirectInk доступны в виде автономных объектов. Когда активируется Custom Drying, DirectInk предоставляет объект InkSynchronizer, который позволяет начинать и заканчивать процесс высыхания, чтобы чернила удалялись из влажного слоя DirectInk синхронно с тем, как вы добавляете их в собственный сухой слой. Логика рендеринга сухих чернил DirectInk по умолчанию также доступна через InkD2DRender, который обеспечивает согласованное появление чернил между влажным и сухим слоями. В случае стирания можно использовать события Unprocessed Input для создания логики стирания по аналогии с более ранним примером.

Более подробные сведения и примеры использования Custom Drying см. в примере ComplexInk на GitHub по ссылке bit.ly/1NkRjt7.

Заключение

Используя все то, что вы узнали на данный момент об InkCanvas, InkPresenter и InkStrokeContainer, вы можете теперь принимать «чернильные» данные для разных типов ввода, настраивать внешний вид этих данных на экране, обращаться к данным штрихов и вносить в них изменения, которые отражаются в том, что визуализирует DirectInk. С помощью этого простого уровня функциональности можно создавать широкий спектр операций взаимодействия с пользовательским вводом — от простого рисования каракулей до реализации более ориентированных на специфические сценарии средств вроде записи заметок и приема подписи пользователя. Кроме того, у вас есть средства для создания более сложных операций взаимодействия через InkRecognizerContainer, события Independent Input и режим Custom Drying.

Благодаря этим средствам ваше приложение сможет задействовать все преимущества цифровых чернил. Поскольку количество устройств с поддержкой перьевого и сенсорного ввода постоянно увеличивается, обеспечение удобной UX для рукописного ввода становится все важнее для завоевания благосклонности пользовательской аудитории. Надеюсь, что вы не пожалеете некоторого времени на то, чтобы обдумать, как поддержка цифровых чернил могла бы работать в вашем приложении, и приступите к экспериментам с DirectInk.

И в заключение замечу, что поддержка рукописного ввода по-прежнему остается важной областью инвестиций для Microsoft, и одним из главных ключей к совершенствованию и расширению платформы DirectInk в будущих выпусках является обратная связь с нашим сообществом разработчиков. Если у вас возникнут какие-либо вопросы, комментарии или идеи в процессе разработки с применением DirectInk, пожалуйста, направляйте их по адресу DirectInk@microsoft.com.


Коннор Уейнс (Connor Weins) — менеджер программ, работает в подгруппе Pen, Stylus and Inking группы Windows Developer Ecosystem Platform. С ним можно связаться по адресу conwei@microsoft.com.

Выражаю благодарность за рецензирование статьи экспертам Microsoft Кришнану Менону (Krishnan Menon) и Шиоу Ту (Xiao Tu).