Уровень интеграции сред

Изобретение относится к способам обработки графической и иной видеоинформации для отображения в компьютерных системах. Техническим результатом изобретения является усовершенствование графической модели, которая позволяет пользоваться многочисленными особенностями и возможностями обработки графики и эффективно выводить сложные графические и аудиовизуальные данные. Технический результат достигается благодаря тому, что уровень интеграции сред, включающий в себя интерфейс прикладного программирования (API) и объектную модель, позволяет разработчикам программного кода согласованно взаимодействовать со структурой данных графа сцены для вывода графики. Посредством интерфейсов программный код добавляет дочерние визуальные объекты к другим визуальным объектам для построения иерархического графа сцены, записывает списки команд, например данные геометрии, данные изображения, данные анимации и другие данные для вывода, и может задавать на визуальных объектах свойства преобразования, усечения и непрозрачности. Уровень интеграции сред и API позволяют программистам выполнять композиционные эффекты в своих приложениях напрямую, в то же время усиливая блок обработки графики таким образом, чтобы не оказывать неблагоприятное воздействие на нормальную производительность приложений. Многоуровневая система включает в себя возможность объединения различных типов сред (например, двумерной графики, трехмерной графики, видео, аудио, текста и построения изображения) и их гладкой и бесстыковой анимации. 3 н. и 63 з.п. ф-лы, 42 ил.

 

ОБЛАСТЬ ТЕХНИКИ, К КОТОРОЙ ОТНОСИТСЯ ИЗОБРЕТЕНИЕ

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

ПРЕДШЕСТВУЮЩИЙ УРОВЕНЬ ТЕХНИКИ

Традиционная модель непосредственного режима для доступа к графике в компьютерных системах достигла своего предела, в частности, потому, что быстродействие памяти и шины не соответствует развитию главных процессоров и/или графических процессоров. В общем случае, современная модель (например, WMJPAINT) для подготовки файла требует слишком большого объема обработки данных, чтобы соответствовать частоте обновления, при необходимости сложных графических эффектов. В результате, при попытках осуществления сложных графических эффектов с помощью традиционных графических моделей, вместо своевременного совершения изменений, приводящих к воспринимаемым зрительным эффектам, для следующего кадра, изменения могут добавляться по разным кадрам, что приводит к результатам, которые зрительно и заметно нежелательны.

Новая модель для управления выводом графики описана в патентных заявках США №№ 10/184,795, 10/184,796, 10/185,775, 10/401,717, 10/402,322 и 10/402,268, права на которые принадлежат правообладателю настоящего изобретения и включенных в данное описание посредством ссылки. Эта новая модель обеспечивает ряд существенных усовершенствований технологии обработки графики. Например, патентная заявка США № 10/184,795, в целом, посвящена многоуровневой системе обработки графики и соответствующему способу, где компонент высокого уровня (например, операционная система) осуществляет аспекты, требующие большого объема вычислений, построения графа сцены, обновления параметров анимации и обхода структур данных графа сцены со сравнительно низкой рабочей скоростью, чтобы передавать упрощенные структуры данных и/или команды графики компоненту низкого уровня. Поскольку обработка высокого уровня значительно упрощает данные, компонент низкого уровня может работать на более высокой скорости (относительно компонента высокого уровня), например на скорости, которая соответствует частоте обновления кадра графической подсистемы, чтобы обрабатывать данные в постоянные выходные данные для графической подсистемы. При использовании анимации, вместо того, чтобы перерисовывать всю сцену с изменениями, обработка низкого уровня может интерполировать интервалы параметров по мере необходимости для получения мгновенных значений, которые при визуализации обеспечивают слегка измененную сцену для каждого кадра, обеспечивая гладкую анимацию.

В патентной заявке США № 10/184,796 описан параметризованный граф сцены, который обеспечивает изменяющиеся (анимированные) значения, и контейнеры параметризованного графа, так что программный код, который хочет рисовать графику (например, прикладная программа или компонент операционной системы), может избирательно изменять определенные аспекты описания графа сцены, оставляя другие аспекты без изменений. Программный код может также повторно использовать ранее построенные фрагменты графа сцены, возможно, с другими параметрами. Можно предполагать, что возможность легко изменять внешний вид отображаемых предметов посредством параметризации и/или повторного использования существующих частей графа сцены обеспечивает существенное повышение общей эффективности обработки графики.

В патентной заявке США № 10/185,775, в целом, описана структура данных кэширования и соответствующие механизмы хранения визуальной информации посредством объектов и данных в графе сцены. Структура данных, в общем случае, связана с механизмами, которые интеллектуально управляют размещением и использованием визуальной информации в ней. Например, в отсутствие особого запроса со стороны прикладной программы наибольшая часть информации, хранящейся в структуре данных, не имеет внешней ссылки на себя, что позволяет оптимизировать или иначе обрабатывать эту информацию. Следует ожидать, что это обеспечивает эффективное использование и экономию ресурсов например, данные в структуре данных кэширования можно преобразовывать в другой формат, который является более компактным и/или уменьшает необходимость в последующей, повторной обработке, например битовый образ или другой результат последующей обработки.

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

СУЩНОСТЬ ИЗОБРЕТЕНИЯ

В целом, настоящее изобретение предусматривает уровень интеграции сред, который обеспечивает интерфейс прикладного программирования (API) для программистов для осуществления, возможно, сложных композиционных эффектов в их приложениях непосредственным образом, в то же время усиливая блок обработки графики таким образом, чтобы не оказывать неблагоприятное воздействие на нормальную работу приложения. Один аспект предусматривает возможность комбинирования разных типов сред (например, двумерную (2D) графику, трехмерную (3D) графику, видео, аудио, текст, изображения и т.д.) и их совместного анимирования гладким и бесстыковым образом.

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

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

Объектная модель элементов векторной графики, в общем случае, соответствует элементам формы и другим элементам, включая элементы изображения и видео, которые коррелируют с объектной моделью графа сцены для графа сцены. Посредством синтаксического разбора разметку можно преобразовать в данные, включающие в себя элементы в дереве элементов, которое транслируется в объекты структуры данных графа сцены. Другую разметку можно транслировать непосредственно в данные и вызовы, которые создают объекты графа сцены. Язык разметки обеспечивает различные пути описания элемента, в том числе простой строковый формат или сложный синтаксис свойств, которым можно присваивать имена, что позволяет повторно использовать их в других местах разметки. Один из аспектов MIL предусматривает интеграцию анимации и хронирования по набору API, обеспечивая анимацию как внутреннюю концепцию базового уровня. Для облегчения гладкой анимации MIL обеспечивает многоуровневую систему обработки графики и соответствующий способ (например, операционной системы). Одна такая многоуровневая система обработки графики содержит два компонента, а именно компонент тактирования по требованию, иначе, высокоуровневый компонент медленного тактирования и низкоуровневый компонент быстрого тактирования (например, на частоте обновления кадра графического оборудования). В общем случае, высокоуровневый, низкочастотный компонент осуществляет аспекты, требующие большого объема вычислений, обновления параметров анимации и обхода структур данных сцены, чтобы передавать упрощенные структуры данных компоненту низкого уровня. Компонент низкого уровня работает на более высокой частоте, например частоте обновления кадра графической подсистемы, для преобразования структур данных в постоянные выходные данные для графической подсистемы. Обработка низкого уровня включает в себя интерполяцию любых необходимых интервалов параметров для получения мгновенных значений для визуализации сцены для каждого кадра анимации.

Объекты MIL верхнего уровня включают в себя дерево визуальных объектов (визуалов), являющееся объектом, который содержит главное содержимое, подлежащее рисованию. Средства управления извлекают визуалы из дерева напрямую. Визуалы не зависят от устройства и родительского контекста. Пункт назначения визуализации - это устройство, в которое рисуется визуал. Этот объект (например, экран) может иметь свой собственный механизм отбраковки или аннулирования. Различные пункты назначения визуализации включают в себя экран в окне, "принтер" (Printer), "метафайл" (Metafile), "поверхность" (Surface), файл потоковых сред (например, DVD) и "подокно", которое является частью сцены, которая рисуется отдельно от остальной сцены. Другие объекты, относящиеся к рисованию, включают в себя "визуализатор визуала" (Visual Renderer), содержащий объект, сконфигурированный на рисование дерева визуалов в пункт назначения визуализации, и объект "планировщик отображения" (Display Scheduler), который знает, когда рисовать дерево визуалов в пункт назначения визуализации. Менеджер (средство управления) времени (Time Manager) - это контекстный объект для набора узлов хронирования, и это объект, на котором тактируются вызовы планировщика.

Предусмотрен API визуала, который, по существу, является начальной точкой для рисования через уровень интеграции сред и содержит множественные типы объектов, в том числе объект "менеджер (средство управления) визуалов" (VisualManager), который связывает "дерево визуалов" (VisualTree) со средой. Различные типы "менеджеров визуалов" (VisualManager) (например, "экран" (Screen), "принтер" (Printer) и "поверхность" (Surface)) отвечают за визуализацию дерева визуалов в свою конкретную среду. Визуал - это то, где программист рисует; это вершина в "дереве визуалов", которая обеспечивает программе место для рисования.

API контекста рисования (DrawingContext) представляет модель программирования на основе контекста относительно того, как строить содержимое визуала, которое заполняет визуал (Visual), или осуществляется визуализация в данные изображения (ImageData). Предусмотрены классы "контекст рисования" (DrawingContext), а также классы и точки ввода, необходимые для получения "контекста рисования" и перечисления содержимого визуала в удержанном визуале/визуале рисования (RetainedVisual/DrawingVisual).

Для обеспечения изменчивости предусмотрен единый набор типов, которые являются производными от общего базового класса "изменяемый объект" (Changeable). Любой тип, для которого изменчивость является желаемым изменением, может быть производным от класса "Changeable". Например, в программировании графики объектная модель включает в себя "кисти" (Brush), "перья" (Pen), "геометрии" (Geometry), "анимации на основе чисел с плавающей точкой" (FloatAnimation), "ограничители градиента" (GradientStop), "отрезки" (Segment) и т.д. Свойство "изменяемо" (IsChangeable) указывает, можно ли изменить изменяемый объект в зависимости от его текущего значения, которое задает состояние.

Кисть - это объект, который представляет метод для заливки плоскости. Помимо возможности заливать плоскость абсолютным способом, кисти уровня интеграции сред также способны приспосабливаться к заливке плоскости относительно размера объекта, который они заливают. Примеры типов кисти включают в себя "кисть чистого цвета" (SolidColorBrush), "кисть визуала" (VisualBrush) (которая может ссылаться на ресурс векторной графики/визуал), "кисть рисования" (DrawingBrush), "линейный градиент" (LinearGradient), "радиальный градиент" (RadialGradient), "кисть изображения" (ImageBrush) и "кисть девятиячеечной сетки" (NineGridBrush). Некоторые объекты кисти располагают информацией о том, как они связаны с системой координат, когда они используются, и информацией о том, как они связаны с ограничивающим прямоугольником геометрии, с которой они используются. Этот размер зависит от объекта, который заливает кисть. Некоторые типы кисти (например, "кисть визуала") также могут быть мозаичными для создания шаблонов, заданных программистом. Базовый класс "Brush" имеет "преобразование" (Transform), общую непрозрачность и режим смешивания. Объекты "Brush" (и другие объектные ресурсы в векторной графике и API MIL) являются Changeable и записываемыми после их создания и отвечают общей схеме Changeable в отношении их поведения после их использования в квалифицированном использовании.

Класс "геометрия" (Geometry) объектов можно использовать для усечения, тестирования на попадание и визуализации данных на основе 2-мерных векторов с помощью "пера" (Pen) и "кисти" (Brush). Производные от Geometry классы обеспечивают более конкретные построение и семантику перечисления. Предусмотрены несколько типов "геометрии", зависящих от формы, а также обобщенная "геометрия пути" (PathGeometry), которая допускает явное определение "геометрии" более сложной формы. Geometry - это абстрактный базовый класс. "Коллекция геометрий" (GeometryCollection) - это коллекция множества объектов Geometry, скомбинированных с использованием конкретных операций "режима комбинирования" (CombineMode) в заданной для них области. Этот объект обеспечивает построение визуальных комбинаций объектов "геометрия" по сравнению с использованием исключительно объектов "фигура пути" (PathFigure) в "геометрии пути".

"Источник изображения" (ImageSource) - это абстрактный класс, содержащий основной компоновочный блок для построения изображения. "Источник изображения", в принципе, представляет единый, постоянный набор пикселей при определенных размере и разрешении. Например, "источник изображения" может представлять собой единичный кадр в файле изображения, который может обеспечивать "декодер" (Decoder) или может являться результатом преобразования, действующего на его собственном "источнике изображения". "Источник изображения" является изменяемым не потому, что его собственные свойства могут изменяться, но потому, что свойства его подклассов потенциально могут изменяться.

Предусмотрен класс "преобразование" (Transform) объектов для масштабирования, вращения, параллельного переноса и перекоса векторной и растровой графики. Производные от Transform классы обеспечивают дружественное использование и семантику перечисления.

Эффекты (Effects) обеспечивают средства изменения визуального содержимого сцены способом, ориентированном на визуализацию. Например, "эффекты изображения" (ImageEffects) (эффекты битового образа на растровой основе) действуют на основанном на изображении, полностью составленном представлении участка сцены. Эффекты подразделяются на различные типы, в том числе "эффекты изображения" (ImageEffects), "режимы смешивания" (BlendModes) и "векторные эффекты" (VectorEffects). "Эффекты изображения" можно использовать в сцене удержанного режима, применяя их к подграфу или "элементу" (Element), или можно использовать в автономном конвейере изображений. "Режимы смешивания" являются особым случаем эффектов на основе изображения, которые можно применять к сцене удержанного режима, в общем случае, таким же образом, как "эффекты изображения". Режимы смешивания осуществляют объединение начального и конечного цветов при композиции источника, например умножение или сложение.

Тестирование на попадание используется для выборки визуалов в сцене и действует, начиная с вершины дерева средств управления и возвращая средство управления или набор средств управления посредством точки или геометрии. Средство управления может указывать наличие или отсутствие попадания на него с помощью услуг поддержки, включающих в себя визуализированную геометрию, ограничивающий прямоугольник, внеполосную геометрию (область попадания), непрозрачность или маску изображения и свою собственную логику. Средство управления может возвращать в случае попадания конкретные данные, относящиеся к попаданию. Механизм тестирования на попадание может эффективно фильтровать результаты тестирования на попадание. Обход тестирования на попадание является глубоким обходом дерева визуалов справа налево, при котором о попаданиях сообщается посредством обратного вызова в z-порядке, сверху вниз. Снижаясь, средство тестирования на попадание просматривает фильтрацию применительно к соотношениям уровня элементов, например, холста с формами или фиксируемой панели с внутренним холстом. При обнаружении попадания средство тестирования на попадание может либо продолжить обработку последующих попаданий (если таковые имеются), либо остановиться.

Предусмотрена система анимации, содержащая средство управления хронированием и набор объектов анимации. Средство хронирования - это услуга, которую могут использовать любые объекты, проявляющие поведение, изменяющееся со временем, например объект анимации и аудио- или видеообъекты. Объекты анимации реализуют набор функций, которые отображают временные диапазоны в другие типы данных, которые затем используются в качестве входных данных в другие объекты высокого уровня. Графическая анимация достигается связыванием коллекции анимаций с операцией визуализации. Каждая анимация, используемая в операции визуализации, может выполняться на отдельном тактовом сигнале, который называется "временной шкалой". Множественные временные шкалы могут быть организованы в дерево хронирования для поддержки иерархического хронирования. После того как нарисован анимированный примитив и заданы параметры анимации, система визуализации низкого уровня заботится о перерисовке сцены с регулярными интервалами. Каждый раз при визуализации кадра текущее значение анимаций, участвующих в сцене, вычисляется на основании истекшего времени (в большинстве случаев измеряемого посредством системного тактового сигнала), после чего анимированные примитивы перерисовываются.

Различные типы примитивов, цветовые особенности и поддержка сред также обеспечиваются посредством MIL. Для воспроизведения любого аудио/видеосодержимого можно использовать "данные сред" (MediaData).

Другие преимущества и достоинства явствуют из нижеследующего подробного описания, приведенного в сочетании с чертежами, в которых:

ПЕРЕЧЕНЬ ФИГУР ЧЕРТЕЖЕЙ

Фиг.1 - блок-схема, представляющая иллюстративную компьютерную систему, в которую может быть внедрено настоящее изобретение.

Фиг.2 - блок-схема, обобщенно представляющая многоуровневую архитектуру графики, в которую может быть внедрено настоящее изобретение.

Фиг.3 - представление графа сцены, состоящего из визуалов и соответствующих компонентов для обработки графа сцены, например, путем обхода графа сцены для обеспечения команд графики и других данных в соответствии с аспектом настоящего изобретения.

Фиг.4 - представление графа сцены из визуалов подтверждения, визуалов рисования и соответствующих "списков команд", построенных в соответствии с аспектом настоящего изобретения.

Фиг.5 - представление класса визуалов объектной модели в соответствии с аспектом настоящего изобретения.

Фиг.6 - представление различных других объектов объектной модели в соответствии с аспектом настоящего изобретения.

Фиг.7 - представление иерархии классов преобразования в соответствии с аспектом настоящего изобретения.

Фиг.8 и 9 - представления преобразований данных визуала в геометрическом масштабе и неоднородном масштабе соответственно в соответствии с аспектом настоящего изобретения.

Фиг.10 - представление классов геометрии объектной модели в соответствии с аспектом настоящего изобретения.

Фиг.11 - представление структуры "геометрии пути" в соответствии с аспектом настоящего изобретения.

Фиг.12 - представление графа сцены, состоящего из визуалов и "списков команд", показывающее иллюстративную графику, создаваемую примитивами, в соответствии с аспектом настоящего изобретения.

Фиг.13 - представление классов кисти объектной модели в соответствии с аспектом настоящего изобретения.

Фиг.14 - блок-схема, обобщенно представляющая изменяемую архитектуру, в которой запросы обрабатываются конечным автоматом для управления изменчивостью типа, в соответствии с аспектом настоящего изобретения.

Фиг.15-17 - диаграммы состояний, представляющие, как состояния свойств управляют поведением изменяемых типов, в соответствии с аспектом настоящего изобретения.

Фиг.18-23 - иерархические представления объектов в иллюстративном графе сцены, показывающие, как свойства управляют переходами между состояниями и поведениями клонирования для иллюстративного кода, в соответствии с аспектом настоящего изобретения.

Фиг.24 - представление визуализированной графики, полученной на основании данных в объекте кисти линейного градиента, в соответствии с аспектом настоящего изобретения.

Фиг.25 - представление визуализированной графики, полученной на основании данных в объекте кисти радиального градиента, в соответствии с аспектом настоящего изобретения.

Фиг.26 - представление визуализированной графики, обусловленной наличием различных значений растяжения, в соответствии с аспектом настоящего изобретения.

Фиг.27 - представление визуализированной графики, обусловленной наличием различных значений элементов мозаичного изображения, в соответствии с аспектом настоящего изобретения.

Фиг.28 - представление визуализированного объекта кисти девятиячеечной сетки в соответствии с аспектом настоящего изобретения.

Фиг.29-40 - графические представления иллюстративных временных шкал, используемых в анимации, в соответствии с аспектом настоящего изобретения.

Фиг.41 - представление иллюстративного трехмерного изображения, построенного посредством трехмерного визуала, в соответствии с аспектом настоящего изобретения.

Фиг.42 - представление трехмерных концепций для обеспечения трехмерной поддержки в соответствии с аспектом настоящего изобретения.

ПОДРОБНОЕ ОПИСАНИЕ ИЗОБРЕТЕНИЯ

ИЛЛЮСТРАТИВНАЯ ОПЕРАЦИОННАЯ СРЕДА

На фиг.1 показан пример подходящей среды 100 вычислительной системы, в которой можно реализовать изобретение. Среда 100 вычислительной системы является только одним примером подходящей вычислительной среды и не призвана накладывать какое-либо ограничение на объем использования или функциональные возможности изобретения. Кроме того, вычислительную среду 100 не следует рассматривать как имеющую какую-либо зависимость или требование в отношении любого из компонентов или их комбинации, проиллюстрированных в иллюстративной операционной среде 100.

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

Изобретение можно описать в общем контексте машиноисполняемых команд, например программных модулей, выполняемых на компьютере.

В общем случае, программные модули включают в себя процедуры, программы, объекты, компоненты, структуры данных и т.д., которые выполняют конкретные задачи или реализуют определенные абстрактные типы данных. Изобретение также можно осуществлять на практике в распределенных вычислительных средах, где задачи выполняются удаленными устройствами обработки, которые связаны между собой посредством сети связи. В распределенной вычислительной среде программные модули могут размещаться как в локальных, так и в удаленных компьютерных носителях информации, включающих в себя запоминающие устройства.

Согласно фиг.1 иллюстративная система для реализации изобретения включает в себя вычислительное устройство общего назначения в виде компьютера 110. Компоненты компьютера 110 могут включать в себя, но без ограничения, процессор 120, системную память 130 и системную шину 121, которая подключает различные компоненты системы к процессору 120. Системная шина 121 может относиться к любому из нескольких типов шинных структур, включая шину памяти или контроллер памяти, периферийную шину и локальную шину, с использованием любой из различных шинных архитектур. В порядке примера, но не ограничения, такие архитектуры включают в себя шину промышленного стандарта (ISA), шину микроканальной архитектуры, шину расширенного ISA (EISA), локальную шину по стандарту Ассоциации по стандартам в области видеоэлектроники (VESA), шину ускоренного графического порта (AGP) и шину взаимосоединения периферийных компонентов (PCI), именуемую также шиной расширения.

Компьютер 110 обычно включает в себя разнообразные машиночитаемые носители. Машиночитаемый носитель может представлять собой любой имеющийся носитель, доступ к которому может осуществлять компьютер 110, и включает в себя энергозависимый и энергонезависимый носитель, сменный и стационарный носитель. В порядке примера, но не ограничения, машиночитаемый носитель может содержать компьютерный носитель информации и среду передачи информации. Компьютерный носитель информации включает в себя энергозависимый и энергонезависимый, сменный и стационарный носитель, реализованный посредством любого метода или технологии хранения информации, например машиночитаемых команд, структур данных, программных модулей или других данных. Компьютерный носитель информации включает в себя, но без ограничения, ОЗУ, ПЗУ, электрически стираемое программируемое ПЗУ (ЭСППЗУ), флэш-память или память иной технологии, CD-ROM, цифровые универсальные диски (DVD) или другие носители информации на основе оптического диска, магнитные кассеты, магнитную ленту, носитель информации на основе магнитного диска или другие магнитные запоминающие устройства или любой другой носитель, который можно использовать для хранения полезной информации и доступ к которому может осуществлять компьютер 110. Среда передачи информации обычно воплощает машиночитаемые команды, структуры данных, программные модули или другие данные в виде модулированного информационного сигнала, например несущей волны или другого транспортного механизма, и включает в себя любую среду доставки информации. Термин "модулированный информационный сигнал" означает сигнал, одна или несколько характеристик которого заданы или изменены таким образом, чтобы кодировать информацию в этом сигнале. В порядке примера, но не ограничения среда передачи информации включает в себя проводную среду, например, проводную сеть или прямое проводное соединение, и беспроводную среду, например акустическую, ВЧ, инфракрасную или иную беспроводную среду. Понятие машиночитаемого носителя также может охватывать комбинации любых из вышеописанных сред и носителей.

Системная память 130 включает в себя компьютерный носитель информации в виде энергозависимой и/или энергонезависимой памяти, например постоянного запоминающего устройства (ПЗУ) 131 и оперативного запоминающего устройства (ОЗУ) 132. Базовая система ввода/вывода 133 (BIOS), содержащая базовые процедуры, помогающие переносить информацию между элементами компьютера 110, например, при запуске, обычно хранится в ПЗУ 131. ОЗУ 132 обычно содержит данные и/или программные модули, к которым процессор 120 имеет непосредственный доступ или которыми он в данный момент оперирует. В порядке примера, но не ограничения на фиг.1 показаны операционная система 134, прикладные программы 135, другие программные модули 136 и программные данные 137.

Компьютер 110 может также включать в себя другой сменный/стационарный, энергозависимый/энергонезависимый компьютерный носитель информации. Исключительно в порядке примера на фиг.1 показаны накопитель 141 на жестких дисках, который считывает со стационарного энергонезависимого магнитного носителя или записывает на него, магнитный дисковод 151, который считывает со сменного энергонезависимого магнитного диска 152 или записывает на него, и оптический дисковод 155, который считывает со сменного энергонезависимого оптического диска 156, например CD-ROM или иного оптического носителя, или записывает на него. Другие сменные/стационарные, энергозависимые/энергонезависимые компьютерные носители информации, которые можно использовать в иллюстративной операционной среде, включают в себя, но без ограничения, кассеты с магнитной лентой, карты флэш-памяти, цифровые универсальные диски, ленту для цифровой видеозаписи, полупроводниковое ОЗУ, полупроводниковое ПЗУ и т.п. Накопитель 141 на жестких дисках обычно подключен к системной шине 121 через интерфейс стационарной памяти, например интерфейс 140, а магнитный дисковод 151 и оптический дисковод 155 обычно подключены к системной шине 121 через интерфейс сменной памяти, например интерфейс 150.

Накопители и дисководы и соответствующие им компьютерные носители информации, рассмотренные выше и показанные на фиг.1, обеспечивают хранение машиночитаемых команд, структур данных, программных модулей и других данных для компьютера 110. Например, на фиг.1 показано, что на жестком диске 141 хранятся операционная система 144, прикладные программы 145, другие программные модули 146 и данные 147 программ. Заметим, что эти компоненты могут быть идентичны или отличаться от операционной системы 134, прикладных программ 135, других программных модулей 136 и программных данных 137. Операционная система 144, прикладные программы 145, другие программные модули 146 и данные 147 программ обозначены здесь другими позициями, чтобы указать, что они, как минимум, являются другими копиями. Пользователь может вводить команды и информацию в компьютер 110 через устройства ввода, например планшет (электронный цифровой преобразователь) 164, микрофон 163, клавиатуру 162 и указательное устройство 161, под которым обычно подразумевают мышь, шаровой манипулятор или сенсорную панель. Другие устройства ввода (не показаны) могут включать в себя джойстик, игровой пульт, спутниковую антенну, сканер и т.п. Эти и другие устройства ввода часто бывают подключены к процессору 120 через интерфейс 160 пользовательского ввода, который подключен к системной шине, но могут быть подключены посредством других интерфейсов и шинных структур, например параллельного порта, игрового порта или универсальной последовательной шины (USB). Монитор 191 или устройство отображения другого типа также подключено к системной шине 121 через интерфейс, например, видеоинтерфейс 190. Монитор 191 может также быть объединен с панелью 193 сенсорного экрана и т.п., которая может вводить оцифрованный ввод, например рукописный ввод в компьютерную систему 110 через интерфейс 192 сенсорного экрана. Заметим, что монитор и/или панель сенсорного экрана может быть физически подключен к корпусу, в который заключено вычислительное устройство 110, например, как в персональном компьютере планшетного типа, в котором экранная панель 193, по существу, служит планшетом 164. Кроме того, компьютеры, например вычислительное устройство 110, также может включать в себя другие периферийные устройства вывода, например громкоговорители 195 и принтер 196, которые могут быть подключены через выходной периферийный интерфейс 194 и т.п.

Компьютер 110 может работать в сетевой среде с использованием логических соединений с одним или несколькими удаленными компьютерами, например удаленным компьютером 180. Удаленный компьютер 180 может представлять собой персональный компьютер, сервер, маршрутизатор, сетевой ПК, одноранговое устройство или другой общий сетевой узел и обычно включает в себя многие или все элементы, описанные выше применительно к компьютеру 110, хотя на фиг.1 показано только запоминающее устройство 181. Логические соединения, показанные на фиг.1, включают в себя локальную сеть (ЛС, LAN) 171 и глобальную сеть (ГС, WAN) 173, но могут включать в себя и другие сети. Такие сетевые среды широко распространены в учрежденческих сетях, сетях в масштабе предприятия, интрасетях и Интернете.

При использовании в сетевой среде ЛС компьютер 110 подключен к ЛС 171 через сетевой интерфейс или адаптер 170. При использовании в сетевой среде ГС компьютер 110 обычно включает в себя модем 172 или другое средство установления соединений по ГС 173, например Интернету. Модем 172, который является внутренним или внешним, может быть подключен к системной шине 121 через интерфейс 160 пользовательского ввода или иной подходящий механизм. В сетевой среде программные модули, указанные применительно к компьютеру 110 или его частям, могут хранится в удаленном запоминающем устройстве. В порядке примера, но не ограничения, на фиг.1 показаны удаленные прикладные программы 185, размещенные в запоминающем устройстве 181. Очевидно, что показанные сетевые соединения являются иллюстративными и что можно использовать другие средства установления линии связи между компьютерами.

УРОВЕНЬ ИНТЕГРАЦИИ СРЕД (MIL)

Один аспект настоящего изобретения, в общем случае, предусматривает архитектуру, которая называется уровнем интеграции сред (MIL), который включает в себя интерфейс прикладного программирования (API) графики непосредственного режима, структуру данных разбиения экрана и API, который также играет роль в кэшировании команд рисования, набор объектов уровня управления и язык разметки. В целом, архитектура позволяет программному коду, например приложению или компоненту операционной системы, передавать команды рисования и другую информацию (например, битовые образы изображения) графическим компонентам для визуализации графических выходных данных на дисплее системы. Аспект настоящего изобретения предусматривает несколько заданных функций и методов, например, в виде интерфейсов API к объектной модели, которые позволяют программам заполнять граф сцены структурами данных, списками команд (примитивами/командами рисования) и другими данными, относящимися к графике. При обработке граф сцены обеспечивает графику, отображаемую на экране.

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

На фиг.2 представлена в общем виде многоуровневая архитектура (MIL) 200, в которой может быть реализовано настоящее изобретение. Согласно фиг.2 может быть разработан программный код 202 (например, прикладная программа или компонент операционной системы и т.п.) для вывода графических данных одним или несколькими различными способами, в том числе посредством построения 204 изображения, посредством элементов 206 векторной графики и/или посредством вызовов функции/метода, обращенных непосредственно к уровню 212 программного интерфейса приложения (API) визуала (Visual). В общем случае, построение 204 изображения обеспечивает программный код 202 механизмом загрузки, редактирования и сохранения изображений, например битовых образов. Согласно описанному ниже эти изображения могут использоваться другими частями системы, и также существует способ использования кода рисования примитива для непосредственного рисования изображения. Элементы 206 векторной графики обеспечивают другой способ рисования графики, согласующийся с остальной объектной моделью (описано ниже). Элементы 206 векторной графики можно создавать посредством языка разметки, который система 208 элементов/свойств и система 210 схем интерпретируют, чтобы делать соответствующие вызовы на уровень 212 API визуала. Элементы 206 векторной графики совместно с системой 208 элементов/свойств и системой 210 схем описаны в вышеупомянутой совместно рассматриваемой патентной заявке № 10/401,717.

Таким образом, MIL 200 включает в себя различные уровни, на которых программист может программировать, включая построение 204 изображения, которое является конвейером загрузки, редактирования и сохранения изображений. Эти изображения при желании могут использоваться в остальной системе. Кроме того, имеется способ - код рисования примитива для непосредственного рисования изображения.

Другой уровень содержит API 212 визуала, который является API, который, в основном, обеспечивает доступ к структуре данных 216 для организации предметов, подлежащих рисованию. Каждый из этих предметов можно загружать с помощью команд рисования, которые могут кэшироваться системой. Имеются различные способы задания этой структуры данных и того, что рисуется; в типичном приложении, знающем MIL, этот API может использоваться из системы 210 схем.

Третий уровень программирования содержит уровень 206 элементов векторной графики, который является языком разметки для описания и рисования графики способом, согласующимся с остальной объектной моделью управления/элемента (Element). Элементы векторной графики открывают систему графики через систему элементов. Это включает в себя множество элементов для визуализации и множество свойств, которые работают на любом произвольном элементе. В одной реализации имеется два подмножества, включая векторную графику уровня элементов, которая разбирается посредством синтаксического анализа на элементы и создает элементы, и векторную графику уровня ресурсов, которая разбирается посредством синтаксического анализа и сохраняется эффективным способом. Объектная модель уровня элементов относится к миру управления более высокого уровня, который импортируется в дерево элементов, систему свойств и систему 210 схем. Что касается разбора посредством синтаксического анализа, многие динамические свойства на уровне элементов являются типами MIL. В общем случае, разметка разрешается до объектов, в которых схема расширяемого языка разметки (XML) для разметки расширяемого языка разметки приложений (XAML) обычно объявляется вверху файла разметки следующим образом:

<Canvas xmlns="http:// schemas.microsoft.com/2003/xaml"

xmlns:def="Defmition"

def:Class="Surfin.ChannelBrowser"

def:CodeBehind="ChannelBrowser.xaml.cs"

ID="mainCanvas" Background="Black" Width="100%" Height="100%"

Loaded="PageLoaded">

Например, при использовании тега <Path> синтаксический анализатор использует схему поиска соответствующего пространства имен (например, System.Windows.Shapes) для разрешения и построения объекта. Согласно тому, что в общем виде описано в вышеупомянутой совместно рассматриваемой патентной заявке № 10/401,717, синтаксический анализатор опирается на преобразователи типов для преобразования из строки в экземпляр объекта MIL. Каждый из этих типов, требующих сложного синтаксиса, имеет записываемое свойство CLR (общеязыковой среды исполнения), объявленное как необязательный атрибут XML, которое разбирается посредством синтаксического анализа таким же образом, как динамическое свойство. Некоторые типы (в особенности, кисти) можно разлагать в простой или сложной форме.

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

Как будет понятно, а также представлено на фиг.2, система 220 анимации распространяется на весь API. Согласно описанному здесь анимационные значения можно передавать практически повсюду, в том числе на уровне 208 элементов/свойств, внутри API 212 визуала и в любых других ресурсах. Система хронирования также открыта на уровне элементов и на уровне визуалов.

В одной реализации многоуровневая архитектура 200 графики включает в себя средство 214 композиции и анимации высокого уровня, которое включает в себя или иначе связана со структурой 216 данных кэширования. Структура 216 данных кэширования содержит граф сцены, содержащий иерархически организованные объекты, управляемые согласно заданной объектной модели, описанной ниже. В общем случае уровень 212 API визуала снабжает программный код 202 (и систему 210 схем) интерфейсом к структуре 216 данных кэширования, включающим в себя возможность создания объектов, открытия и закрытия объектов для предоставления им данных и т.д. Иными словами, средство 214 композиции и анимации высокого уровня предоставляет уровень 212 API унифицированных сред, посредством которого разработчики могут выражать намерения относительно графики и сред для отображения графической информации и снабжать нижележащую платформу достаточной информацией, что позволяет платформе оптимизировать использование оборудования для программного кода. Например, нижележащая платформа может отвечать за кэширование, согласование ресурсов и интеграцию сред.

В одной реализации средство 214 композиции и анимации высокого уровня передает поток команд и, возможно, другие данные (например, указатели на битовые образы) быстрому средству 218 композиции и анимации низкого уровня. В общем случае, средство композиции и анимации низкого уровня/визуализатор 218 обеспечивает набор систем, который управляет фактическим рисованием и композицией на экране. Заметим, что используемые здесь термины "высокий уровень" и "низкий уровень" подобны тем, что используются в других сценариях вычислений, где, в общем случае, чем ниже компонент программного обеспечения относительно более высоких компонентов, тем ближе этот компонент к оборудованию. Таким образом, например, графическая информация, передаваемая от средства 214 композиции и анимации высокого уровня, может быть принята на средстве 218 композиции и анимации низкого уровня, где информация используется для отправки графических данных на графическую подсистему, содержащую оборудование 222. Заметим, что настоящее изобретение можно распространить на многоуровневую композицию за пределами двух слоев.

Кроме того, заметим, что для упрощения пользовательских интерфейсов, независимых от разрешения и устройства, концепция пикселя не объявляется основной единицей в главных API. Вместо этого, исходная система координат задается так, что одна единица в этой система координат равна 1/96 дюйма. Это может относиться к dip или, альтернативно, к px, даже если в некоторых системах (например, мониторе или принтере с высоким разрешением) он не отображается в пиксель. В "длине" (Length) единица dip переводится непосредственно в одну пользовательскую единицу. Коэффициент между другими физическими единицами (дюймами, сантиметрами, пунктами и т.д.) фиксируется на 1/96 дюйма. Это значит, что если используется преобразование масштаба, оно будет влиять на все, что рисуется, даже, если это задано в физических терминах. Значение 1/96 дюйма было выбрано так, чтобы принятая по умолчанию пользовательская единица была эквивалентна пикселю на современных дисплеях с настройками, заданными по умолчанию. Заметим, что может быть предусмотрен механизм для предоставления подсказок системе компоновки и другому пользовательскому коду, чтобы их можно было оптимизировать на предмет выходного разрешения устройства, на котором они визуализируются.

Средство 214 композиции и анимации высокого уровня совместно с программным кодом 202 строит граф сцены для представления графической сцены, обеспечиваемой программным кодом 202. Например, каждый предмет, подлежащий рисованию, можно загружать с помощью команд рисования, которые система может кэшировать в структуре 216 данных графа сцены. Согласно описанному ниже имеется несколько различных способов задания этой структуры 216 данных и того, что рисуется. Кроме того, средство 214 композиции и анимации высокого уровня интегрируется с системами 220 хронирования и анимации для обеспечения декларативного (или иного) управления анимацией (например, интервалов анимации) и управления хронированием. Заметим, что система анимации позволяет передавать анимационные значения, по существу, повсюду в системе, в том числе, например, на уровне 208 элементов/свойств, внутри уровня 212 API визуала или в любых других ресурсах. Система хронирования открыта на уровнях элементов и визуалов.

Средство 218 композиции и анимации низкого уровня управляет композицией, анимированием и визуализацией сцены, которая затем предоставляется графической подсистеме 222. Средство 218 низкого уровня составляет визуализации для сцен из множества нитей (например, из одного или нескольких приложений) и с помощью компонентов визуализации реализует фактическую визуализацию графики на экране. Заметим, однако, что иногда может быть необходимо и/или выгодно, чтобы некоторая визуализация осуществлялась на более высоких уровнях. Например, хотя услуга более низких уровней запрашивает из множества нитей, более высокие уровни обрабатываются для каждой нити в отдельности, что позволяет посредством механизмов 204 построения изображения осуществлять требующую много времени или зависящую от нити визуализацию на более высоких уровнях и передавать ссылки на битовый образ на более низкие уровни.

MIL 200 предусматривает несколько концепций, которые объединяются для обеспечения усовершенствований в графическом и аудиовизуальном программировании, например, набор ресурсов и классов, которые совместно используются посредством всего этого многоуровневого стека, включая "перья", "кисти", "геометрию", "преобразования" и "эффекты". Также предусмотрены простые типы примитивов, включая "точки" (Point), "прямоугольники" (Rectangle) и т.д. "Перо" и "кисть" являются сложными типами, которые описывают, как влиять на визуализацию на этих различных уровнях. Предусмотрен также особый тип "кисти", именуемый "кисть визуала" (VisualBrush), который позволяет программистам использовать произвольный графический "метафайл" для заливки области (в явном виде через "кисть визуала" или "перо", которое ссылается на "кисть визуала"). Поскольку это сжатая форма для сохранения и использования произвольной графики, она играет роль графического ресурса. Имеется конкретный профиль синтаксиса разметки векторной графики, используемый для непосредственного создания этих объектов. Хотя, в общем случае, "кисть рисования" (DrawingBrush) аналогична "кисти визуала", она более сжата и систематизирована и, по существу, является кистью метафайла, тогда как "кисть визуала" является кистью графа сцены.

Другие классы включают в себя "геометрию" (Geometry), которая представляет собой сложный тип, который используется для задания области для заливки, штриховки или усечения. "Преобразование" (Transform) - это другая иерархия сложных типов, для задания того, как преобразовывать координатные пространства. "Эффекты" (Effects) описывают систему для помещения произвольных эффектов фильтра на участке содержимого, например размывание. Заметим, что это также включает в себя добавленную модель расширяемости.

Обеспечен API визуала, который использует вышеописанные типы для вывода битов на экран и другие цели. Это включает в себя подключение базового уровня к остальной системе (через hWnd или другой механизм) совместно с введенной выше структурой данных разбиения экрана. Построение изображения позволяет программисту направлять изображения в основывающуюся на MIL систему и из нее. Среда обеспечивает возможность использовать другие формы среды, включая аудио и видео. В общем случае, API визуала относится к набору API, который действует ниже системы элементов и системы схем, и относится к программированию с помощью визуалов и непосредственно к визуалам, а не на более высоком уровне. Заметим, что визуал - это базовый объект в структуре 216 данных кэширования, которая содержит удержанную структуру данных для визуальных сущностей на экране, а также кэширует списки команд и ресурсы, зависящие от устройства в целях производительности.

На фиг.3 и 4 показаны иллюстративные графы сцены 300 и 400 соответственно, включающие в себя базовый объект, именуемый "визуалом" (Visual). В общем случае, "визуал" содержит объект, который представляет пользователю виртуальную поверхность и имеет представление "визуала" на дисплее. Согласно фиг.5 базовый класс Visual обеспечивает базовые функциональные возможности для других типов визуала, т.е. класс Visual - это абстрактный базовый класс, производными от которого являются типы визуала.

Согласно фиг.3 "визуал" 302 верхнего уровня (или корневой) соединен с объектом 304 "менеджер визуалов" (VisualManager), который также имеет отношение (например, посредством описателя) с окном (HWnd) 306 или аналогичным блоком, в котором графические данные выводятся для программного кода. "Менеджер визуалов" 304 управляет рисованием визуала верхнего уровня (и любых потомков этого визуала) в окне 306. На фиг.6 "менеджер визуалов" показан как один из множества других объектов 620 в объектной модели описанной здесь графической системы.

Для рисования "менеджер визуалов" 304 обрабатывает (например, обходит или пропускает) граф сцены, как запланировано диспетчером 308, и передает команды графики и другие данные компоненту 218 нижнего уровня (фиг.2) для его соответствующего окна 306, что в общем виде описано в вышеупомянутых патентных заявках США. Обработка графа сцены обычно планируется диспетчером 308 с частотой, более низкой по отношению к частоте обновления компонента 218 более низкого уровня или графической подсистемы 222. На фиг.3 показан ряд дочерних визуалов 310-314, размещенных в иерархическом порядке ниже визуала 302 верхнего уровня (корня), некоторые из которых представлены как заполненные контекстами рисования 316, 317 (изображенными в виде пунктирных прямоугольников для представления их временной природы) с соответствующими списками команд 318 и 319 соответственно, например, содержащими списки команд и другие визуалы. Визуалы также могут содержать другую информацию свойств. В общем случае, доступ к базовому классу визуалов осуществляется через интерфейс IVisual, и визуал выводится из "объекта зависимости" (DependencyObject), как показано на фиг.5. Визуалы (дополнительно описанные в Приложении) могут также содержать другую информацию свойств, что показано в следующем иллюстративном классе Visual:

public class Visual: IVisual, DependencyObject
{
protected Visual();
protected Visual(UIContext context);
bool IVisual.HasChildren { get; }
VisualCollection IVisual.Children { get; }
Visual IVisual.Parent { get; }
Geometry IVisual.Clip {get; set;}
Bool IVisual.Show {get; set;}
double IVisual.Opacity {get; set;}
BlendMode IVisual.BlendMode {get; set;}
Matrix IVisual.TransformToAncestor(Visual ancestor);
Matrix IVisual.TransformToDescendant(Visual descendant);
Matrix IVisual.TransformFromAncestor(Visual ancestor);
Matrix IVisual.TransformFromDescendant(Visual descendant);
Matrix IVisual.TransformToVisual(Visual visual);
Matrix IVisual.TransformFromVisual(Visual visual);
bool IVisual.IsAncestorOf(Visual descendant);
bool IVisual.IsDescendantOf(Visual ancestor);
Visual IVisual.FindCommonVisualAncestor(Visual otherVisual);
PointHitTestResult IVisual.HitTest(Point point);
void IVisual.HitTest(
HitTestFilterDelegate filterHitDelegate,
HitTestResultDelegate resultHitDelegate,
HitTestParameters hitTestParams);
Rect IVisual.VisualContentBounds {get;}
Rect IVisual.VisualDescendantBounds {get;}
protected virtual PointHitTestResult HitTestCore(
PointHitTestParameters point);
protected virtual GeometryHitTestResult HitTestCore(
GeometryHitTestParameters geometry);
protected virtual Rect HitTestBounds {get;}
}

Визуал - это контейнер для графического содержимого и множества потомков. Различные свойства визуала можно использовать для управления поведением визуализации визуала. Например, благодаря заданию отсечения на визуале содержимое визуала усекается до заданной формы. Другие свойства представляют собой "преобразование", "режим смешивания", "непрозрачность", "показ" и т.д. Всеми этими свойствами можно управлять через свойства get и set.

Свойство "показ" (Show) используется для показа/сокрытия визуала, например, когда "ложь", визуал невидим, иначе, визуал видим. Кроме того, объекты MIL (будь то визуалы на уровне API визуала или элементы на уровне элементов) существуют в иерархии. Система координат наследуется вниз по этой иерархии. Таким образом, родитель может "проталкивать" преобразование координат, которое изменяет проход визуализации и применяется к потомкам этого родителя.

Преобразование для визуала находится на связи с этим визуалом. Иными словами, оно задается через [Get|Set]ChildTransform на свойстве VisualCollection Children родителя. См. также VisualCollection, описанную ниже.

Заметим, что преобразования координат можно применять единообразно ко всему, как если бы это было в битовом образе. Заметим, что это не означает, что преобразования всегда применяются к битовым образам, но то, что визуализируется, одинаково подвергается преобразованиям. Например, если пользователь рисует круг с помощью круглого пера шириной один дюйм, а затем применяет масштабное преобразование в направлении Х с коэффициентом два к этому кругу, то перо будет иметь ширину два дюйма влево и вправо и ширину только один дюйм вверх и вниз. Это иногда называют преобразованием композиции или битового образа (в отличие от скелетного или геометрического масштабного преобразования, которое влияет только на геометрию). На фиг.8 представлено масштабное преобразование, где слева показано непреобразованное изображение 800, а справа показано преобразованное изображение 802 с неоднородным масштабированием. На фиг.9 представлено масштабное преобразование, где слева показано непреобразованное изображение 800, а справа показано преобразованное изображение 904 с геометрическим масштабированием.

Что касается преобразования координат визуала, TransformToDescendant ("преобразование к потомку") возвращает преобразование, которое отражает изменение координатного пространства, идущее от опорного визуала к визуалу-потомку. Затем преобразование можно использовать, например, для преобразования точки из координатного пространства опорного визуала в координатное пространство визуала-потомка. TransformFromDescendant ("преобразование от потомка") возвращает аналогичное преобразование, которое описывает изменение координатного пространства, идущее от визуала-потомка к опорному визуалу. Затем преобразование можно использовать для преобразования точки из координатного пространства визуала-потомка в координатное пространство опорного визуала. Для удобства, API визуала также обеспечивает TransformToAncestor ("преобразование к предку"), TransformFromAncestor ("преобразование от предка"), TransformFromVisual ("преобразование от визуала) и TransformToVisual ("преобразование к визуалу"), которые также возвращают преобразования для соответствующих изменений координатного пространства. Заметим, что в двух последних API соотношение между визуалами не задано. Они могут быть даже равноправными узлами в дереве визуалов, пока они совместно используют общих предков. Реализация предусматривает отыскание общего предка, а затем вычисление преобразования координат от опорного визуала к общему предку, а затем от общего предка к конечному визуалу. Результирующее преобразование можно использовать, например, для преобразования точки между заданными визуалами.

Имеются два свойства get, которые можно использовать для определения ограничивающего прямоугольника содержимого визуала, а именно VisualDescendantBounds ("границы потомка визуала"), который является ограничивающим прямоугольником всего графического содержимого потомков, и VisualContentBounds ("границы содержимого визуала"), который является границами содержимого. Применение к ним "объединения" (Union) обеспечивает полные границы визуала.

Свойство "усечение" (clip) задает (и получает) область усечения визуала. Любую "геометрию" (класс "геометрия" показан на фиг.10 и описан ниже в разделе Геометрия) можно использовать как область усечения. В одной реализации область усечения задается по умолчанию как пустое множество (null), т.е. отсутствие усечения, что можно рассматривать как бесконечно большой прямоугольник усечения от (-∞, -∞) до (+∞, +∞).

Свойство "непрозрачность" (Opacity) получает/задает значение непрозрачности визуала, так что содержимое визуала смешивается на поверхности рисования в зависимости от значения непрозрачности и выбранного режима смешивания. Свойство "режим смешивания" (BlendMode) можно использовать для задания (или получения) используемого режима смешивания. Например, значение непрозрачности (альфа) можно задавать между 0,0 и 1,0, причем линейное смешивание с коэффициентом альфа задается в качестве режима, например, Цвет = альфа * цвет фона + (1,0 - альфа) * цвет фона). В визуал могут быть включены и другие услуги, например свойства спецэффектов, в том числе размытие, монохромное изображение и т.д.

Визуал также имеет свойство "дети" (Children) для управления множеством потомков. Он также обеспечивает свойство "имеет детей" (HasChildren) для проверки того, имеются ли вообще потомки у визуала. Свойство "дети" возвращает "коллекцию визуалов" (VisualCollection), которая позволяет пользователю осуществлять такие операции, как добавление, удаление, вставка и т.д. на множестве потомков. Ниже показан пример "коллекции визуалов":

public class Visual : ICollection, IEnumerable
{
public Int32 Add(Visual visual)
public Void Clear()
public Boolean Contains(Visual visual)
public virtual sealed Void CopyTo(Array array, Int32 index)
public Int32 get_Capacity()
public virtual sealed Int32 get_Count()
public virtual sealed Boolean get_IsSynchronized()
public Visual get_Item(Int32 index)
public virtual sealed Object get_SyncRoot()
public virtual sealed IEnumerator GetEnumerator()
public Transform GetTransform(Int32 index)
public Transform GetTransform(Visual child)
public Int32 IndexOf(Visual visual)
public Void Insert(Int32 index, Visual visual)
public Void Remove(Visual visual)
public Void RemoveAt(Int32 index)
public Void RemoveRange(Int32 index, Int32 count)
public Void set_Capacity(Int32 value)
public Void set_Item(Int32 index, Visual value)
public Void SetTransform(Int32 index, Transform transform)
public Void SetTransform(Visual child, Transform transform)
public Void TrimToSize()
}

Порядок визуалов в "коллекции визуалов" определяет порядок визуализации визуалов, т.е. визуалы визуализируются от самого низкого индекса к самому высокому индексу от заднего плана к переднему плану (порядок раскраски).

"Посреднический визуал" (ProxyVisual) - это визуал, который можно добавлять более одного раза в граф сцены, например, ниже визуала контейнера. Поскольку любого визуала, на который ссылается посреднический визуал, можно достичь множественными путями от корня, услуги чтения (TransformToDescendent, TransformFromDescendent и HitTest) не действуют через посреднический визуал. По существу, имеется один канонический путь от любого визуала к корню дерева визуалов, и этот путь не содержит ни одного посреднического визуала.

На фиг.4 показан иллюстративный граф 400 сцены, в котором "визуалы-контейнеры" (ContainerVisual) и "визуалы рисования" (DrawingVisual) связаны в графе сцены и имеют соответствующие данные в виде "списков команд" (например, в соответствующих контекстах рисования). "Визуал-контейнер" - это визуал, который имеет только структурное содержимое и который является производным от базового класса Visual. Визуалы могут быть произвольно вложены один в другой. В частности, это справедливо для вложения "визуалов-контейнеров". Главной целью "визуала-контейнера" является обеспечение контейнера для визуалов, к которым можно удобно осуществлять доступ, не проходя через интерфейс IVisual. Поэтому "визуал-контейнер" повторно реализует все методы IVisual как открытые методы. Потомками "визуала-контейнера" можно манипулировать с помощью методов на свойстве VisualCollection Children "визуала-контейнера".

Согласно фиг.5 еще один визуал - это HwndVisual 505, который размещается в дочернем HWnd Win32 в графе сцены. В частности, традиционные программы по-прежнему будут работать посредством метода WM_PAINT (или подобного), который рисует на дочернее HWnd (или подобное) на основании более ранней технологии графики. Для поддержки таких программ в новой модели обработки графики HwndVisual позволяет, чтобы Hwnd содержалось в графе сцены и перемещалось при повторном размещении родительского визуала. Возможны и другие типы визуала 506, например трехмерные (3D) визуалы, которые обеспечивают связь между двухмерными и трехмерными мирами, например, камероподобный обзор возможен посредством двухмерного визуала, имеющего обзор трехмерного мира. Такой 3D визуал описан ниже.

Согласно описанному выше визуалы можно рисовать, заполняя их контекст рисования различными примитивами рисования, включая "геометрию" (Geometry), "источник изображения" (ImageSource) и "данные среды" (MediaData). Кроме того, имеется множество ресурсов и классов, совместно используемых во всем этом стеке. Это включает в себя "перья" (Pen), "кисти" (Brush), "геометрию" (Geometry), "преобразования" (Transform) и "эффекты" (Effect). Абстрактный класс DrawingContext ("контекст рисования") предоставляет набор операций рисования и состояния контекста, которые можно использовать для заполнения "визуала рисования" (DrawingVisual), "удержанного визуала" (RetainedVisual), "данных изображения" (ImageData) и т.д. Иными словами, абстрактный класс "контекст рисования" предоставляет набор операций рисования и операций проталкивания/выталкивания; для каждой операции рисования и проталкивания имеются два метода, один из которых берет в качестве аргументов константы, а другой берет в качестве аргументов аниматоры. Примерами операций проталкивания/выталкивания являются PushTransform, PopTarnsform, PushClip, PopClip, PushOpacity, PopOpacity и т.д.

В отношении контекста рисования можно проталкивать и выталкивать различные услуги (включая преобразование, непрозрачность и усечение), и операции проталкивания/выталкивания могут быть вложенными, пока для каждого вызова проталкивания имеется соответствующий вызов выталкивания.

Метод PushTransform проталкивает преобразование. Последующие операции рисования выполняются в отношении протолкнутого преобразования. Вызов выталкивания выталкивает преобразование, протолкнутое соответствующим вызовом PushTransform:

void PushTransform(Transform transform);

void PushTransform(Matrix matrix);

void Pop().

Аналогично, метод PushOpacity проталкивает значение непрозрачности. Последующие операции рисования визуализируются на временной поверхности с заданным значением непрозрачности, а затем составляются в сцену. Pop() выталкивает непрозрачность, протолкнутую соответствующим вызовом PushOpacity:

void PushOpacity(float opacity);

void PushOpacity(FloatAnimation opacity);

void Pop().

Метод PushClip проталкивает геометрию усечения. Последующие операции рисования усекаются до геометрии. Усечение применяется в пространстве после преобразования. Pop() выталкивает область усечения, протолкнутую соответствующим вызовом PushClip:

void PushClip(Geometry clip);

void Pop().

Заметим, что операции проталкивания могут быть произвольным образом выложены, пока операции выталкивания соответствуют проталкиванию. Например, справедливо следующее:

PushTransform(...);
DrawLine(...);
PushClip(...);
DrawLine(...);
Pop();
PushTransform(...);
DrawRect(...);
Pop();
Pop();

"Геометрия" (Geometry) - это тип класса (фиг.10), который задает скелет векторной графики, без контура или заливки. Каждый геометрический объект является простой формой (LineGeometry, EllipseGeometry, RectangleGeometry), сложной единичной формой (PathGeometry) или списком таких форм GeometryCollection с заданной операцией комбинирования (например, объединение, пересечение и т.п.). Эти объекты образуют иерархию классов, показанную на фиг.10.

Согласно фиг.11 PathGeometry - это коллекция объектов "фигура" (Figure). В свою очередь, каждый из объектов "фигура" составлен из одного или нескольких объектов "отрезок" (Segment), которые фактически задают форму фигуры. "Фигура" является подразделением "геометрии", которое определяет коллекцию отрезков. Эта коллекция отрезков является единой связной последовательностью двухмерных объектов "отрезок". "Фигура" может быть либо замкнутой формой с заданной областью, либо просто связной последовательностью "отрезков", которые задают кривую без какой-либо замкнутой области.

Согласно фиг.12 при отрисовке геометрии (например, прямоугольника) кисть или перо можно задавать, как описано ниже. Кроме того, объект "перо" также имеет объект "кисть". Объект "кисть" задает, как графически заливать плоскость, и имеется иерархия классов объектов "кисть". Это представлено на фиг.12 залитым прямоугольником 1202, который имеет место при обработке визуала, включающего в себя команды, и параметров прямоугольника и кисти. Объект "перо" удерживается на "кисти" совместно со свойствами Thickness, LineJoin, LineCap, EndCap, MiterLimit, DashArray и DashOffset, описанными ниже. Ниже также описано, что некоторые типы кистей (например, градиенты и девятиячеечные сетки) сами устанавливают свои размеры. При использовании размер для этих кистей получают из ограничивающего прямоугольника, например, когда единицы градиента/единицы пункта назначения (GradientUnits/DestinationUnits) для кисти заданы как RelativeToBoundingBox, то используется ограничивающий прямоугольник рисуемого примитива. Если эти свойства заданы как "абсолютные" (Absolute), то используется координатное пространство.

Как отмечено выше и дополнительно описано ниже, объектная модель графики настоящего изобретения включает в себя объектную модель "кисти", которая, в общем случае, направлена на концепцию покрытия плоскости пикселями. Примеры типов кистей представлены в иерархии, показанной на фиг.13, и, под базовым классом "кисть", включают в себя Gradient Brush ("градиентную кисть"), NineGridBrush ("кисть девятиячеечной сетки"), SolidColorBrush ("кисть чистого цвета") и TileBrush ("мозаичная кисть"). "Градиентная кисть" включает в себя объекты "линейный градиент" (LinearGradient) и "радиальный градиент" (RadialGradient). "Кисть рисования" (DrawingBrush) и "кисть изображения" (ImageBrush) являются производными от "мозаичной кисти". Возможны альтернативные организации классов, например, производными от "мозаичной кисти" могут быть "кисть изображения", "кисть визуала" (VisualBrush), "кисть видео" (VideoBrush), "кисть девятиячеечной сетки" и "кисть рисования". Заметим, что объекты "кисть" могут распознавать, как они соотносятся с системой координат при использовании и/или как они соотносятся с ограничивающим прямоугольником формы, на которой они используются. В общем случае, такую информацию, как размер, можно выводить из объекта, на котором рисуется кисть. В частности, многие типы кисти используют систему координат для задания некоторых их параметров. Эту систему координат можно задавать либо по отношению к простому ограничивающему прямоугольнику формы, к которой применяется кисть, либо по отношению к координатному пространству, которое активно в момент использования кисти. Они известны, соответственно, как режим RelativeToBoundingBox и режим Absolute.

API ВИЗУАЛА

API визуала является отправной точкой для рисования посредством уровня интеграции сред и содержит множественные типы объектов, в том числе объект "менеджер визуалов" (VisualManager), который соединяет Дерево визуалов (VisualTree) со средой. Различные типы "менеджеров визуалов" (например, "экран", "принтер", "поверхность") управляют процессом визуализации дерева визуалов в их конкретной среде. "Менеджер визуалов" дополнительно описан здесь в разделе, озаглавленном "Объекты MIL верхнего уровня".

Визуал - это то, где рисует пользователь. Это узел в дереве визуалов (объект-контейнер, который является структурой для сцены, как описано ниже) и обеспечивает место для программы, чтобы рисовать. Имеются различные типы визуалов, каждый из который настроен для различных использований. Визуал аналогичен визуалу/выходной стороне hWnd Win32.

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

Для визуализации визуальной сцены, обходят дерево визуалов, например, сверху вниз, слева направо, сначала визуализируя содержимое, потом обходя потомков визуала слева направо. Любой из потомков визуала рисуют до содержимого самого этого визуала. Если содержимое требует обратного вызова пользователя, это происходит синхронно во время визуализации. Ниже приведен псевдокод, который система исполняет (через "визуализатор визуала" (VisualRenderer)) для визуализации дерева визуалов на устройстве:

Visual.RenderToDevice(device)
{
this.RenderContents(device);
foreach (Visual child in this.Children)
{
Child.Walk(ctx);
}
}

Производный визуал может делать обратный вызов пользователю в ходе вызова RenderContents.

На фиг.13 представлена иерархия классов "визуал" в одной реализации. В ходе обратных вызовов, которые имеют место как часть прохода визуализации (содержащих обратный вызов на IRetainedVisual.Render или обратный вызов на PaintingVisual.RenderCore), дерево визуалов "фиксируется" из соображений производительности. Эта фиксация осуществляется для любого контекста, т.е. никакой контекст не может изменить дерево, когда оно зафиксировано, вне зависимости от того, какому дереву визуалов принадлежит визуал. Когда дерево зафиксировано, потомки визуала не могут изменяться, и содержимое других визуалов нельзя изменить никаким способом (например, Open, 3D модель задания корня и т.д.), "преобразование", "непрозрачность", "усечение", "режим смешивания" или "эффект" на визуале нельзя устанавливать, тестирование на попадание не работает, а также не работает операция получения информации границ.

Функциональные возможности на визуале предоставляются через интерфейс IVisual, что позволяет делать функциональные возможности открытыми, в то же время защищая объектную модель. Ниже показан интерфейс Ivisual в одной иллюстративной реализации:

public interface IVisual
{
bool HasChildren {get;}
VisualCollection Children {get;}
Visual Parent {get;}
Geometry Clip {get; set;}
bool Show {get; set;}
double Opacity {get; set;}
Matrix TransformToAncestor(Visual ancestor);
Matrix TransformToDescendant(Visual descendant);
Matrix TransformFromAncestor(Visual ancestor);
Matrix TransformFromDescendant(Visual descendant);
Matrix TransformToVisual(Visual visual);
Matrix TransformFromVisual(Visual visual);
bool IsAncestorOf(Visual descendant);
bool IsDescendantOf(Visual ancestor);
Visual FindCommonVisualAncestor(Visual otherVisual);
PointHitTestResult HitTest(Point point);
void HitTest(
HitTestFilterDelegate filterHitDelegate,
HitTestResultDelegate resultHitDelegate,
HitTestParameters hitTestParams);
Rect VisualContentBounds {get;}
Rect VisualDescendantBounds {get;}
}

Визуал содержит содержимое визуализации и коллекцию потомков. Ряд свойств, в том числе преобразование, усечение, непрозрачность, режим смешивания и т.д., можно использовать для управления фактической визуализацией дерева визуалов. Заметим, что визуалу не требуется одновременно иметь содержимое и потомков. В одной реализации содержимое визуализации и коллекцию потомков можно создавать по требованию для оптимизации использования памяти. API визуала (Visual) позволяет пользователю делать его производным от Visual и конкретизировать его.

Преобразования потомков осуществляются посредством преобразования на свойстве "дети" (Children) типа "коллекция визуалов" (VisualCollection).

public void SetChildTransform(Visual visual, Transform transform);
public Transform GetChildTransform(Visual visual);

Услуги чтения преобразования обеспечивают методы, которые позволяют пользователям получать матрицу (Matrix), представляющую составное преобразование из одной системы координат в другую:

Matrix TransformToDescendant(Visual descendant);
Matrix TransformFromDescendant(Visual descendant);
Matrix TransformToAncestor(Visual ancestor);
Matrix TransformFromAncestor(Visual ancestor);
Matrix TransformToVisual(Visual visual);
Matrix TransformFromVisual(Visual visual);

Методы TransformToAncestor и TransformToDescendant более эффективны, но требуют, чтобы вызывающая сторона знала соотношение между двумя визуалами. Более общие методы TransformTo/FromVisual находят общего предка и вычисляют преобразование к этому визуалу. Заметим, что они могут обуславливать обновление блоков кэш-памяти и вызовы OnRender на произвольных визуалах. Если визуалы не связаны, или встречается вырожденное преобразование, вырывается исключение.

Предусмотрено также вычисление границ:

protected Rect VisualDescendantBounds {get;}
protected Rect VisualContentBounds {get;}

VisualDescendantBounds возвращает объединение ограничивающих прямоугольников содержимого для потомков текущего визуала, но не включающих в себя содержимое текущего визуала. VisualContentBounds возвращает ограничивающий прямоугольник для содержимого текущего визуала.

Свойство Opacity ("непрозрачность") (например, число с плавающей точкой с двойной точностью (double)) задает необязательное "значение непрозрачности" для применения к визуалу, при композиции в его родителя. По умолчанию, это значение равно 1,0, при котором содержимое появляется с полной "непрозрачностью". Заметим, что поскольку это значение перемножается любыми другими данными "непрозрачности" в подграфе, "непрозрачность" 1,0 ничего не меняет. "Непрозрачность" 0,0 обуславливает прозрачность всего содержимого, значение 0,25 обуславливает непрозрачность, составляющую двадцать пять процентов от его номинального значения и т.д. Непрозрачность применяется до "режима смешивания" (BlendMode).

"Режим смешивания" - это свойство, которое задает необязательный "режим смешивания" для применения к содержимому подграфа и пункту назначения при композиции этого визуала. По умолчанию, это значение равно BlendMode.Normal, что осуществляет композицию со знанием альфа-канала в пункт назначения. В случае задания этого свойства равным некоторому другому значению, будет осуществляться композиция с содержимым визуала в качестве источника и содержимым конечного пункта в качестве пункта назначения. Это применяется после применения свойства "непрозрачность".

Базовый класс Visual представляет интерфейсы для особенностей, которые являются общими для визуалов:

public class System.Windows.Media.Visuals.Visual :
DependencyObject, IVisual
{
// Конструкторы
protected Visual();
protected Visual(UiContext Context);
// Деструктор
~Visual();
protected virtual PointHitTestResult HitTestCore(
PointHitTestParameters point);
protected virtual GeometryffitTestResult HitTestCore(
GeometryHitTestParameters geometry);
protected virtual Rect HitTestBounds {get;}
// Реализованы все методы для IVisual. Они реализованы
// только в явном виде для интерфейса. Вот пример:
bool IVisual.HasChildren {get;}
}

ContainerVisual является непосредственно производным от класса Visual и продвигает защищенные свойства так, чтобы они были открытыми. Это позволяет пользователю создавать контейнерность визуала без необходимости создавать новый производный класс.

public class System. Windows.Media.Visuals.ContainerVisual : Visual
{
// Конструкторы
public Visual();
public Visual(UiContext Context);
// Все методы IVisual "повторно открыты", поэтому
// они public. Этот класс всего лишь для удобства
// над и за пределами визуала
}

RetainedVisual - это визуал, который вводит "поток удержанных команд", который можно использовать для рисования:

public class System.Windows.Media.Visuals.RetainedVisual: Visual
{
protected RetainedVisual();
protected RetainedVisual(UiContext Context);
// Семантика открытия/закрытия
protected DrawingContext RenderOpen();
protected DrawingContext RenderAppend();
// Аннулирование
protected bool IsVisualInvalid {get;}
protected bool InvalidateVisual();
Rect RenderBounds { set; get;}
}
public class System.Windows.Media.Visuals.IRetainedRender
{
void Render(DrawingContext drawingContext);
}

Поток команд можно использовать в режиме "по требованию" (OnDemand), в котором пользователь, при необходимости, получает обратный вызов для визуализации. Пользователю требуется реализовать IRetainedRender. Поток команд можно использовать в императивном (Imperative) режиме, в котором пользователь может непосредственно вызывать RenderOpen и получать контекст рисования. В общем случае пользователь будет каждый раз использовать один из этих методов, но их можно использовать в сочетании.

RenderOpen и RenderAppend будут влиять на текущий поток и доступны в разных сценариях. Они будут вызывать исключение, если этот визуал в данный момент находится в обратном вызове "визуализации" (Render). RenderOpen очищает все предыдущее содержимое, которое было в "удержанном визуале", а RenderAppend присоединяет новое содержимое к концу потока. Если пользователь реализовал IRetainedRender на визуале, то пользователь сигнализирует системе, что следует также использовать режим "по требованию". Система будет использовать значение, заданное в свойстве RenderBounds ("границы визуализации") в качестве границ для содержимого, обеспечиваемого вызовом Render. Система может принять решение на оптимизацию сцены и отбрасывание содержимого в любой момент, когда реализован IRetainedRender. RenderBounds по умолчанию задано как пустой прямоугольник, хотя возможными альтернативами являются Rect.Infinite или неустановленное значение. Чтобы получить повышение производительности виртуализации, обеспечиваемые обратным вызовом, пользователь должен установить разумное значение.

При визуализации сцены система, по идее, проверяет каждый визуал (заметим, что в действительности система способна игнорировать большинство визуалов большую часть времени). Если этот визуал имеет IsVisualInvalid, заданное равным "истина", и на основании RenderBounds содержимое визуала будет нужно, то система вызовет IRetainedVisual.Render, чтобы заполнить содержимое этого визуала. Это заменит любое содержимое, которое уже имеется. Пользователь может вручную указать системе отбросить поток содержимого, вызвав Invalidate ("аннулировать").

Если IRetainedRender не реализован, то IsVisualInvalid всегда будет возвращать "ложь". Invalidate не будет ничего делать. Заметим, что IRetainedRender так называется (например, вместо IRender), потому что он недостаточно общий, чтобы использоваться во всех случаях обратного вызова визуализации. Например, "визуал раскраски" (PaintingVisual) делает обратный вызов с неправильным прямоугольником.

Визуал рисования (DrawingVisual) очень похож на удержанный визуал (RetainedVisual), но предназначен для использования без производных классов. Защищенные методы продвигаются в открытые. Кроме того, отсутствует обратный вызов Render или нужно реализовать интерфейс IRetainedRender. Вследствие этого, содержимое всегда удерживается, наподобие того, когда IRetainedRender не реализован на RetainedVisual.

public class System.Windows.Media.Visuals.DrawingVisual : ContainerVisual
{
// Конструкторы
public DrawingVisual();
public DrawingVisual(UiContext Context);
// Интерфейсы дерева
public bool HasVisualChildren {get;}
public Visual VisualParent {get;}
public bool IsVisualAncestorOf(Visual descendant);
public bool IsVisualDescendantOf(Visual ancestor);
public Visual FindCommonVisualAncestor(Visual otherVisual);
// Свойства стандартной композиции
public new Geometry VisualClip {get; set;}
// Значение по умолчанию равно 1.0
public new double VisualOpacity {get; set;}
public new DoubleAnimationCollection VisualOpacityAnimations {get; set;}
// Значение по умолчанию равно BlendModes.Normal
public new BlendMode VisualBlendMode {get; set;}
// Преобразования
public Transform TransformToDescendant(Visual descendant);
public Transform TransformFromDescendant(Visual descendant);
public Transform TransformToAncestor(Visual ancestor);
public Transform TransformFromAncestor(Visual ancestor);
public Transform TransformToVisual(Visual visual);
public Transform TransformFromVisual(Visual visual);
// Вычисление границ
public new Rect VisualDescendantBounds {get;}
public new Rect VisualContentBounds {get;}
// Операции открытия/закрытия
public new DrawingContext RenderOpen();
public new DrawingContext RenderAppend();
}

Предусмотрен также "визуал раскраски" (PaintingVisual):

public class System. Windows.Media.Visuals.PaintingVisual : Visual
{
protected SurfaceVisual();
protected SurfaceVisual(UiContext Context);
// Свойства поверхности
protected Rect PaintingBounds {get; set;}
protected Color FillColor {get; set;} // по умолчанию прозрачный - эффективнее, если непрозрачный
// Семантика открытия/закрытия
protected StaticDrawingContext PaintingOpen();
protected StaticDrawingContext PaintingAppend();
// Семантика RenderCore
protected abstract void RenderCore(Rect invalidRect);
protected void InvalidateRender();
protected void InvalidateRender(Rect invalidationRect);
}

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

Одно отличие "визуала раскраски" от "визуала рисования" состоит в том, что методы обеспечивают "статический контекст рисования" (StaticDrawingContext), который не допускает никакой анимации. Если где-нибудь используется анимированный аргумент, то возникает исключение. Другое отличие состоит в том, что "присоединение" наращивает поток команд гораздо дешевле в смысле затрат памяти. Кроме того, свойство "границы раскраски" (PaintingBounds), которое, по существу, задает жесткое усечение для пользователя, требуется и необходимо. Заметим, что оно отличается от свойства усечения, поскольку оно ограничивает содержимое этого визуала, тогда как "усечение" (Clip) усекает содержимое визуала и всех его потомков. Также реализовано RenderCore ("ядро визуализации") (аналогичное IRetainedVisual.Render), в котором, если меняется разрешение или системе нужно по какой-то причине повторно визуализировать содержимое, пользователь обеспечивает этот механизм.

"Визуал раскраски" потенциально значительно легковеснее "визуала поверхности" (SurfaceVisual), по причине отсутствия явной поддержки этого визуала поверхностью. Напротив, это узел в дереве, т.е. в некоторой более низкой точке поддерживается поверхностью.

Для достижения "визуала поверхности" пользователь должен создать "удержанный визуал", вызвать DrawImage, а затем изменить изображение позади сцен. В этом случае пользователь явно управляет растеризацией вместо того, чтобы позволить системе осуществлять обратный вызов. Это будет API непосредственного режима для работы непосредственно на изображении. Этот API позволяет пользователю получить "статический контекст рисования" (StaticDrawingContext), который работает на этом изображении. Заметим, что API для "визуала поверхности" подобен hWnd, DUser Gadget или узлу дерева отображения Trident. "Присоединение" содержимого (фактически, создание маленькой дельты, которая составляется поверх того, что уже существует) - это дешевая операция. Т.е., в общем и целом, не происходит никакого отъема памяти для присоединения содержимого, в отличие от "удержанного визуала", в результате которого RenderAppend приводит ко все большему удлинению потока команд, потенциально вызывая экспоненциальное нарастание. Поскольку поверхность, которая может поддерживать этот визуал, может появляться и исчезать, визуал должен реализовать виртуальный RenderCore "по требованию".

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

ОБЪЕКТЫ MIL ВЕРХНЕГО УРОВНЯ

Можно легко понять, что различные объекты обеспечиваются для работы в типичном оконном сценарии. Заметим, что это не обязательно должны быть абстрактные классы (например, это не явный интерфейс планировщика или объект).

Один такой объект содержит дерево визуалов, который является объектом, который содержит главное содержимое, подлежащее отрисовке. Средства управления непосредственно выводятся из визуалов дерева. Визуалы независимы от устройства и контекста.

Конечным пунктом визуализации является устройство, на котором рисуется визуал. Этот объект (например, экран) может иметь свой собственный механизм отбраковки или аннулирования, который необходим для поддержки визуальной системы посредством традиционного hWnd. Различные конечные пункты визуализации включают в себя экран в окне, "принтер", "метафайл", "поверхность" и "подокно", которое является частью сцены, которая рисуется отдельно от остальной сцены. Это механизм, позволяющий рисовать через нити, эквивалентный составляемым объектам машины нижнего уровня.

Другие объекты, относящиеся к рисованию, включают в себя "визуализатор визуала", содержащий объект, сконфигурированный рисовать дерево визуалов на конечный пункт визуализации, и объект "планировщик отображения", который знает, когда рисовать дерево визуалов на конечный пункт визуализации. "Менеджер времени" - это объект контекста для множества узлов хронирования и объект, который активизируют вызовы планировщика.

Ниже приведен пример последовательности операций по рисованию на экран:

1. Пользователь каким-то образом получает "контекст пользовательского интерфейса (ПИ)" (UiContext) и начинает изменять дерево визуалов. Это может происходить при запуске приложения или, возможно, в ответ на входное событие ПИ.

2. Извещение об отбраковке распространяется вверх по дереву визуалов. Корневой визуал знает, с каким визуализатором визуала он связан и пересылает извещение об отбраковке. Это извещение является приватным.

3. Визуализатор визуала через открытое событие сообщает, что он изменился и вышел из синхронизма со своим конечным пунктом визуализации.

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

5. Пользователь выдает "контекст ПИ" (UiContext) и позволяет диспетчеру действовать.

6. Диспетчер действует и вызывает отложенный планировщиком рабочий элемент. (Наиболее вероятно, любые отложенные рабочие элементы в одном контексте будут объединены.)

7. Планировщик выполняет свой главный цикл обновления:

а. Хронирует "менеджер времени" (TimeManager).

b. Запускает схему, помимо прочего.

с. Предписывает визуализатору визуала визуализировать новые изменения на конечный пункт визуализации. Затем визуализатор:

i. Обходит отбракованные части дерева визуалов и обновляет внутренне кэшированные ограничивающие прямоугольники.

ii. Вызывает все необходимые визуалы "по требованию" для визуализации. (По умолчанию, визуалы "по требованию" имеют пустой прямоугольник в качестве своих границ, и, следовательно, они не будут вызываться, пока компоновка не запустится и не задаст их.)

iii. Вновь обходит отбракованные части и отправляет необходимые обновления визуализации в графические системы нижнего уровня.

Заметим, что визуальная система ничего не знает о диспетчере. Это дело объекта-планировщика заботиться об этих деталях.

Кроме того, есть идея инкрементального визуализатора визуала и визуализатора визуала моментального снимка. Может быть желательно, чтобы визуал принадлежал одновременно одному и только одному инкрементальному визуализатору визуала. Это ограничение необходимо для эффективного кэширования данных на самом визуале. Однако разумно иметь также способ делать "мгновенный снимок" всего дерева визуалов в конечный пункт визуализации. В этом случае нет постоянных связей между деревом визуалов и визуализатором. Это можно использовать для получения снимка экрана высокого разрешения или для отправки дерева визуалов (когда оно на экране) непосредственно на принтер.

"Окно" играет роль вышеописанного конечного пункта визуализации. Оно также является управляемой заменой hWnd.

class System.Windows.Media.WindowContext : IDisposable
{
public WindowContext();
// IDispose
public void Dispose();
public bool IsDisposed {get; set;}
// Размер окна
public Size Size {get;}
public event EventHandler SizeChanged {add; remove;}
// Проверка окна
public bool IsInvalid {get;}
public event EventHandler IsInvalidChanged {add; remove;}
// Информация разрешения
public ResolutionInformation Resolutionlnformation {get;}
public event EventHandler ResolutionInformationChanged {add; remove;}
}

Управление менеджера окон находится вне этого объекта, однако его можно объединить с "контекстом окна" WindowContext, придав свойствам (например, размеру) значения чтения/записи и обеспечив дополнительные свойства, например положение, заголовок окна и т.д. Заметим, что "размер" представляет размер окна в физических единицах (1/96 дюйма). Это не размер пикселя. Заметим, что могут быть ситуации, когда данные, визуализированные в окне, теряются по какой-то причине, например переключение видеорежима или переключение от локальной консоли на сеанс сервера удаленного терминала.

"Визуализаторы визуала" и "менеджеры визуалов" являются другими объектами, отвечающими за визуализацию дерева визуалов в конечный пункт визуализации. Визуализатор визуала обеспечивает простую модель "одной вспышки", которая визуализирует в среду, тогда как менеджеры визуалов устанавливают удержанную связь между деревом визуалов и пунктом назначения, на который они визуализируются. Так поддерживается "инкрементальная" визуализация в среду.

Ниже приведен пример того, как выглядит "визуализатор визуала" в одной реализации:

public class System. Windows.Media.Visuals.VisualRenderer : System.Threading.ContextAffinityObject, IDisposable
{
// Конструкторы
internal VisualRenderer();
internal VisualRenderer(System.Threading.UiContext context);
// ContextAffinityObject
public UiContext Context {get;}
public bool CheckContext();
public void VerifyContext();
public void SetContext(System.Threading.UiContext context);
// IDisposable+
public void Dispose()
public bool IsDisposed {get;}
// Свойства
public Color BackgroundColor {get; set;}
public Visual RootVisual {get; set;}
// Информация устройства
public Size RootSize {get;}
public ResolutionInformation Resolutionlnformation {get; set;}
// Управление визуализацией
public void RenderNow();
}

Экземпляр этого класса нельзя создавать открытым образом, ввиду отсутствия среды "по умолчанию". Визуализатор визуала также является объектом ContextAffinity.

Предусмотрено свойство "цвет фона" (BackgroundColor):

public Color BackgroundColor {get; set;}

Это принятый по умолчанию цвет фона менеджера визуалов, который может быть по умолчанию прозрачным для менеджеров визуалов. Однако некоторые среды (например, визуализация в традиционные HWnds) не могут поддерживать попиксельную прозрачность, и, таким образом, каждый менеджер визуалов может задавать свое собственное значение по умолчанию для этого свойства. Большинство приложений будут игнорировать это свойство и, например, могут быть установлены на цвет фона системного окна или прозрачными.

Свойство "корневой визуал" (RootVisual) идентифицирует корневой визуал для визуализации:

public Visual RootVisual {get; set;}

Он задан по умолчанию равным null. Когда свойство "корневой визуал" равен null, менеджер визуалов рисует цвет фона в среду.

Свойство "размер корня" RootSize возвращает, в виртуальных единицах, размер конечного пункта визуализации. Например, для менеджера визуалов, поддерживаемого окном, это будет клиентским размером окна:

public abstract Size RootSize {get;}

Предусмотрена также информация разрешения:

public ResolutionInformation ResolutionInformation {get; set;}

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

Исходное преобразование "мира в устройство", заданное для "корневого визуала" делает так, что одна единица в этом визуале равна 1/96 дюйма на устройстве. Например, при наличии "менеджера визуала экрана" (ScreenVisualManager), который поддерживается устройством, имеющим 192 точек на дюйм, то исходное преобразование должно быть задано так, что одна единица в системе координат для "корневого визуала" должна быть равна двум единицам на устройстве. В этом случае, ResoultionInformation.PixelSize будет возвращать (0,5, 0,5), чтобы указывать, что каждый пиксель равен 1/48 дюйма на стороне.

Менеджер визуалов устанавливает долгосрочную связь с корневым визуалом и конечный пункт визуализации и разности дорожек:public System.Windows.Media.Visuals.VisualManager : System.Windows.Media.Visuals.VisualRenderer
{
// Конструкторы
internal VisualManager();
internal VisualManager(System.Threading.UiContext context);
// Отбраковка дерева визуалов
public bool IsVisualTreeDirty {get;}
public event EventHandler VisualTreeDirtyChanged {add; remove;}
}

WindowVisualManager - это основной способ рисования на экране. Он управляет визуализацией дерева визуалов в "контекст окна" (WindowContext):

public sealed class System.Windows.Media.Visuals.ScreenVisualManager : VisualManager
{
// Конструкторы
public ScreenVisualManagerWindowContext windowContext);
public ScreenVisualManager(WindowContext windowContext, System.Threading.UiContext context);
// Контекст окна
public WindowContext WindowContext {get; set;}
}

КОНТЕКСТ РИСОВАНИЯ

API DrawingContext ("контекст рисования") представляет привычную для специалистов в данной области модель программирования "на основе контекста", позволяющую конструировать содержимое визуала, которое заполняет визуал или визуализируется в "данные изображения" (ImageData). В этом разделе описаны классы "контекст рисования" (DrawingContext), а также классы и точки входа, необходимые для получения "контекста рисования" и перечисления содержимого визуала в "удержанном визуале"/"визуале рисования".

Приложения не строят непосредственно "контекст рисования", и представленные версии "контекста рисования" являются абстрактными классами. Существует несколько способов получения "контекста рисования" для помещения туда содержимого визуала. Они включают в себя RetainedVisual.RenderOpen() или RetainedVisual.RenderAppend(), каждая из которых возвращает "контекст рисования" для выдачи в него команд. Другие способы включают в себя IRetainedRender.Render(), DrawingVisual.RenderOpen(), DrawingVisual.RenderAppend() и PaintingVisual.PaintingOpen() или PaintingVisual.PaintingAppend() (хотя "визуалы раскраски" не обрабатывают анимации). "Данные изображения" (ImageData) (или подкласс "данных изображения") имеет механизм возврата "контекста рисования" для визуализации на поверхность битового образа фиксированного разрешения. "Данные изображения" также не обрабатывают анимации.

Ниже приведен API "контекста рисования"

public abstract class System. Windows.Media.StaticDrawingContext : ContextAffinityObject, IDisposable
{
// Нет открытых конструкторов
// Методы рисования векторной графики
public abstract void DrawLine(Pen pen, Point point0, Point point1);
public abstract void DrawLine(Pen pen,
Point point0, PointAnimationCollection point0Animations
Point point1, PointAnimationCollection point1Animations);
public abstract void DrawRectangle(Brush brush, Pen pen, Rect rectangle);
public abstract void DrawRectangle(Brush brush, Pen pen, Rect rectangle, RectAnimationCollection rectangleAnimations);
public abstract void DrawRoundedRectangle(Brush brush, Pen pen, Rect rectangle, double radiusX, double radiusY);
public abstract void DrawRoundedRectangle(Brush brush, Pen pen, Rect rectangle, RectAnimationCollection rectangleAnimations, double radiusX, double radiusY);
public abstract void DrawEllipse(Brush brush, Pen pen, Point center, double radiusX, double radiusY);
public abstract void DrawEllipse(Brush brush, Pen pen, Point center, PointAnimationCollection centerAnimations,
double radiusX, DoubleAnimationCollection radiusXAnimations,
double radiusY, DoubleAnimationCollection radiusYAnimations);
public abstract void DrawGeometry(Brush brush, Pen pen, Geometry geometry);
public abstract void DrawDrawing(Drawing drawing, Point location);
public abstract void DrawDrawing(Drawing drawing,
Point location,
PointAnimationCollectionlocationAnimation);
// Рисование изображения и видео
public abstract void DrawImage(ImageData image, Rect rectangle);
public abstract void DrawImage(ImageData image, Rect rectangle, RectAnimationCollection rectangleAnimations);
public abstract void DrawVideo(MediaData video, Rect rectangle);
public abstract void DrawVideo(MediaData video,
Rect rectangle, RectAnimationCollection rectangleAnimations);
// Рисование текста
public abstract void DrawText(FormattedText text, Point origin);
public abstract void DrawText(FormattedText text,
Point origin, PointAnimationCollection originAnimations);
public abstract void DrawGlyphs(GlyphRun glyphRun);
// Методы состояния контекста
public abstract void PushClip(Geometry clipGeometry);
public abstract void PushOpacity(double opacity);
public abstract void PushOpacity(double opacity,
DoubleAnimationCollectiondoubleAnimations);
public abstract void PushTransform(Transform transform);
public abstract void PushBlendMode(BlendMode blendMode);
public abstract void Pop(); // применяется к последнему протолкнутому состоянию.
// Метрики качества
// Подсказки перечисления
public abstract bool PreserveReadbackOrder {get; set;} // default = false
// Close() можно вызывать без вызова Pop на все соответствующие Push.
public abstract void Close(); // то же самое, что IDisposable.Dispose();
}

Большинство методов объекта "контекст рисования" самоочевидны для специалистов в данной области, однако следует заметить, что "контекст рисования" является ContextAffinityObject и должен использоваться только из единичного "контекста ПИ" (UIContext). Объекты "контекст рисования" также являются IDisposable, и рекомендованной схемой, в C#, состоит в том, чтобы использовать их в операторе "использования", например, при получении от RenderOpen/Append. Кроме того, заметим, что здесь нет таких методов, как DrawArc, DrawPie, DrawBezier и DrawPolyline. Они требуют построения соответствующей "геометрии" и использования DrawGeometry (описанной ниже).

Кроме того, хотя имеется множество методов Push*, имеется только один метод Pop. Это подразумевает, что не может быть перекрывающихся атрибутов. Атрибуты, установленные Push*(), составляются надлежащим образом. Например, Clip составляется через оператор Intersection ("пересечение"), Opacity - через операцию Multiplication ("умножение"), и Transform - через операцию ComposeTransform.

Поскольку пользователь может вызывать, например, DrawGeometry с "кистью", имеющей значение null, или "пером", имеющим значение null, ее можно вызывать со значением null и для "кисти", и для "пера", однако, не все будет визуализироваться или проходить тестирование на попадание.

Любые анимационные свойства, обеспеченные контексту рисования при использовании в неанимированном пункте назначения (например, при визуализации непосредственно в растр) будут привязаны к началу отсчета времени (хотя неанимированные пункты назначения могут быть альтернативно привязаны к моменту "сейчас"). Это позволяет более легко переводить код, написанный для динамического "контекста рисования", для использования "контекста рисования", который не обрабатывает анимацию.

Перечисление содержимого внутри "удержанного" визуала тесно связано с "контекстом рисования", посредством которого вставлено это содержимое. Перечисление содержимого визуала и/или изменение потока команд можно осуществлять, при желании, через механизм Changeable ("изменчивый"), что описано ниже. Общая идея состоит в обеспечении механизма перечисления "модели проталкивания".

Класс "контекст рисования" сам по себе обеспечивает интерфейс для регистрации обхода содержимого в режиме проталкивания, подкласс "контекста рисования", именуемый DrawingContextWalker (с большинством методов, остающимися абстрактными). Пользователи создают подклассы DrawingContextWalker, после чего передают экземпляр в визуал, чтобы начать перечисление. Визуал осуществляет обратный вызов надлежащего метода DrawingContext для передачи содержимого, который он имеет. DrawingContextWalker также обеспечивает порцию состояния, которым можно манипулировать на нем, чтобы контролировать ход перечисления (например, нужно ли его немедленно остановить).

Ниже приведен иллюстративный код объекта DrawingContextWalker:

public class MyContextWalker : DrawingContextWalker
{
public void DrawLine(Pen pen, Point point0, Point point1)
{
Console.WriteLine("Line from {0} to {1} with Pen {2}", point0, point1, pen);
}
public void DrawRectangle(Brush brush, Pen pen, Rect rectangle, RectAnimationCollection rectangleAnimations)
{
Console.WriteLine("AnimateRectangle with...",...);
}
// и т.д., и т.п....
}
// Получает содержимое любого "удержанного визуала"
// (включая "средства управления", "визуалы рисования", и т.д.)
MyContext Walker ctx Walker = new MyContextWalker();
ctx Walker. WalkContent(myFancyControl, 0); // приводит к вызовам на ctx Walker

В базовом классе DrawingContextWalker предусмотрен метод WalkContent:

public abstract class DrawingContextWalker : DrawingContext
{
protected DrawingContextWalker(); // позволяет открытым подклассам // начать появляться
public void WalkContent (RetainedVisual visual, DrawingContext WalkOptions options);
protected void StopWalking();
// реализация абстрактных методов из DrawingContext
// Это не имеет смысла на обходчике контекста и будет вызывать
//исключение при обращении
public bool PreserveReadbackOrder {get; set;}
// DrawText() распространяется в GlyphRun'ы, поэтому он никогда не будет,
// вызван обратно, поэтому мы не хотим, чтобы пользователю пришлось
//реализовать его.
public void DrawText(... regular ...); // следует контролировать, как только вызван
public void DrawText(... animate ...); // следует контролировать, как только вызван
// другие методы остаются абстрактными, и подлежат реализации создателем подкласса.
}

Пользователи создают его подклассы и, по желанию, реализуют все абстрактные методы. Пользователи могут создавать экземпляр объекта и вызывать на нем WalkContent. Затем WalkContent осуществляет обратный вызов соответствующих методов по мере обхода визуала. Реализация любого из этих методов может остановить обход, при желании, путем вызова защищенного метода StopWalking(). Заметим, что незаконно начинать обход, когда "контекст рисования" на визуале открыт для визуализации в него.

Опции определяют, как совершается обход:

[Flags]
public enum DrawingContextWalkOptions
{
IncludeAnimations = 0x1
}

Если установлено IncludeAnimations, обходчик вызывает соответствующие методы с анимированным содержимым. В противном случае, мгновенное значение содержимого передается методам DrawingContextWalker.

"Контекст рисования" имеет булево значение PreserveReadbackOrder, который может предоставить визуалу порядок или структуру содержимого в порядке и структуре, возвращенной через класс DrawingContextWalker. Это значение по умолчанию равно "ложь", но может быть задано равным "истина" перед вставкой содержимого, когда важно сохранить порядок.

Например, как описано выше, DrawGeometry может быть обеспечена с "кистью", имеющей значение null, или "пером", имеющим значение null. Если PreserveReadbackOrder равно "истина", эту команду нужно поддерживать в состоянии визуала. Если PreserveReadbackOrder равно "ложь", то реализация может отбросить эту команду.

Заметим, что этот подход проталкивания имеет многочисленные преимущества над другими подходами, сохраняющими тип, в том числе в том, что не требуется параллельное задание типов для отражения выхода перечисления, и в том, что не обязательно требовать резервирования памяти для выполнения в интерфейсе обратного вызова. Кроме того, методы можно вызывать непосредственно без создания объектов для обратного прохода, интерфейс "контекста рисования" уже присутствует, и не нужен никакой дополнительный API на самом визуале, чтобы позволять обход.

Теперь рассмотрим изменение содержимого визуала. Один способ выражения изменений в "содержимом визуала" состоит в использовании ресурсов ("перьев", "кистей" и т.д.), которые, как описано здесь, являются подклассами Changeable, со StatusOfNextUse = UseStatus.ChangeableReference. Это позволит приложению поддерживать ссылки на данные, которые находятся в управляемых структурах, отправляемых в "контекст рисования". Это представляет единообразный способ, позволяющий производить изменения, и, поскольку эти объекты находятся в известном состоянии, которое в явном виде задано в них, реализация знает, какие объекты, скорее всего, подвергнутся изменению. Заметим, что это не допускает, например, изменений упорядочения команд или добавления или удаления команд (хотя имеется RenderAppend() для добавлений).

РИСОВАНИЕ

Класс "рисование" (Drawing) содержит коллекцию команд рисования. Он в точности эквивалентен содержимому, хранящемуся на "визуале рисования", и построен посредством "контекста рисования". "Рисование" не имеет сродства к контексту, когда неизменяем, и, таким образом, его и родственный класс "кисть рисования" (DrawingBrush) можно использовать по контекстам и в листах со свойствами, принятыми по умолчанию, когда он сам неизменяем.

Он непосредственно не поддерживает иерархию, поскольку он не обеспечивает никаких средств итерации потомков или нахождения родителя, но через "контекст рисования" "рисование" можно рисовать в другой "рисование". "Рисование" можно рисовать в "контекст рисования" через DrawDrawing(Drawing), и его можно использовать как описание содержимого для "кисти рисования".

"Рисование" является полностью анимируемым и поддерживает обратное чтение/итерацию таким же образом, как "удержанный визуал".

public class System.Windows.Media.DrawingCollection: Changeable

{

// Конструкторы

public Drawing Collection();

public new DrawingCollection Copy();

// Свойства

public Rect Bounds {get;}

// Операции открытия/закрытия

public DrawingContext Open();

public DrawingContext Append();

}

СХЕМА CHANGEABLE

В целях объяснения настоящее изобретение будет описано, главным образом, применительно к среде программирования, в которой строятся, используются и изменяются иллюстративные объекты в графической сцене. Однако, как будет понятно, хотя настоящее изобретение обеспечивает значительные преимущества в средах программирования, связанных с графикой, настоящее изобретение не ограничивается средами программирования, связанными с графикой, но, в общем случае, применимо ко многим другим типам сред программирования.

В одной реализации настоящее изобретение обеспечивает единый набор типов, которые являются производными от общего базового класса, например, System.Windows.Changeable. Любой класс может быть изменяемым, будучи производным от класса Changeable и, таким образом, получения семантики значение-тип, которые предлагает Changeable. Например, в программировании графики объектная модель включает в себя, "кисти", "перья", "геометрии", "анимации с плавающей точкой", "ограничители градиента", "отрезки" и т.д., что в общем виде описано в вышеупомянутой патентной заявке США № 10/402,268. Например, иерархия для кисти рисования может выглядеть как: Object:Changeable:Animatable:Brush:TileBrush:DrawingBrush.

В целях основного использования изменяемый объект включает в себя следующие свойство и методы:

Public class System.Windows.Changeable
{
public bool IsChangeable {get;} // по умолчанию истина
public Changeable Copy();
public void MakeUnchangeable();
}

Свойство IsChangeable указывает, можно ли изменить изменяемый объект в зависимости от его текущего значения. Например, попытка задать свойство непрозрачности кисти будет успешна только, если этот объект кисти имеет свойство IsChangeable, равное "истина". В противном случае возникнет исключение. При конструировании изменяемые объекты имеют свойство IsChangeable, равное "истина" по умолчанию, и потому являются непосредственно изменяемыми.

Согласно фиг.14 запросы 1402 принимаются, например, посредством функциональных вызовов, исходящих от прикладной программы, которые адресованы изменяемым классам 1404. В общем случае, обработчик 1406 запросов, включающий в себя конечный автомат 1408, обрабатывает запросы и поддерживает данные состояния и объекта через поддерживающую структуру данных 1410, клонируя структуру данных в клонированную копию 1412 с соответствующим состоянием свойств, в зависимости от текущего состояния свойств, как описано ниже. Исключения 1414 могут возникать, например, в случае запроса на неразрешенный переход из текущего состояния свойств. Состояния свойств описаны ниже со ссылкой на фиг.15-17.

Следует заметить, что функциональные вызовы, адресованные изменяемым классам, можно обрабатывать прямо или косвенно. Например, обработчик 1406 запросов на фиг.14 может включать в себя набор API, который обеспечивает интерфейсы к конечному автомату. Альтернативно, обработчик 1406 запросов может содержать код посреднического программного обеспечения, который преобразует запросы, принятые в одной операционной системе, в вызовы API, обрабатываемые другой операционной системой. Таким образом, запросы в данном использовании через обработчик запросов "обуславливают" запрашиваемое поведение, независимо от того, где происходит фактическая обработка посредством конечного автомата или где обеспечены структуры данных и классы.

Таким образом, (помимо других механизмов) приложения могут конструировать изменяемые объекты посредством "нового" запроса, задавать значения в них, использовать их, продолжать задавать значения в них и продолжать использовать их.

Ниже приведен пример того, как приложение создает кисть чистого цвета (scb), изменяет кисть, чтобы она имела чистый цвет (красный), и использует кисть для окрашивания фона кнопки в красный цвет:

SolidColorBrush scb = new SolidColorBrush();
scb.Color = Colors.Red;
Button1.Background = scb; // это квалифицируется как "использование" (что описано ниже)

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

Когда значение используется в одном из этих квалифицированных видов использования, с точки зрения пользовательской модели, делается его клон, и этот клон имеет свое свойство IsChangeable, заданное равным "ложь". Заметим, что в действительности клон не обязательно создается, а когда используется, не обязательно глубок (в иерархии объектов, как объясняется ниже). Тем не менее, с точки зрения модели следует считать, что клон делают, и, таким образом, используемое здесь понятие "клон" охватывает клон, который фактически создан, клон, созданный частично, и/или клон, логически созданный с точки зрения модели, даже если его не обязательно создавать. Клон - это то, что фактически используется, и, по умолчанию, клон нельзя изменять.

Как показано выше, изменяемый объект также содержит методы, включающие в себя Copy() и MakeUnchangeable(). Явный вызов метода Copy() создает копию изменяемого объекта, причем свойство IsChangeable копии задано равным "истина". Этот вызов не изменяет объект, на котором вызывается этот метод. Метод MakeUnchangeable() можно вызывать на любом изменяемом объекте и изменяет свойство IsChangeable, чтобы сделать его ложным (если уже не ложное, в каковом случае вызов не имеет эффекта).

Вышеозначенные механизмы обеспечивают схему для замены свойства. Для изменения изменяемого объекта значение свойства IsChangeable должно быть "истина". Поскольку квалифицированное использование объекта создает клон, который не является изменяемым, этот объект нужно копировать посредством метода Copy(), изменять и снова использовать. Это эффективно заменяет исходный объект, который присутствовал, новым объектом, который является измененной копией оригинала. Примеры этого изложены ниже. Заметим, что используемый изменяемый объект - это то, что было использовано, и, таким образом, по вышеприведенному определению не является изменяемым, поскольку свойство IsChangeable устанавливается равным "ложь" после использования. Таким образом, изменяемый объект не изменяется, но вместо этого изменяемый объект копируется и заменяется. Заметим, что в случае изменяемых объектов имеется только один набор типов, что, в общем случае, гораздо желательнее с точки зрения программирования. Кроме того, истинная изменчивость обеспечивается дополнительными свойствами, как описано ниже.

Согласно описанному выше создание кисти, ее изменение и ее использование является непосредственным. Пример простого использования в операции рисования приведен ниже:

SolidColorBrush scb = new SolidColorBrush();
scb.Color = Colors.Red;
ctx.DrawRectangle(scb,...); // это "использование"
scb.Color = Colors.Green;
ctx.DrawRectangle(scb,...); // это "использование"

Выполнение вышеуказанных команд рисует один красный прямоугольник и один зеленый прямоугольник. Заметим, что 'scb', в сущности, клонируется после каждого его использования.

Более сложная конструкция, использующая кисть линейного градиента (lgb), в которой цвет изменяется (например, линейно) от одного ограничителя к другому, приведена ниже:

LinearGradientBrush lgb = new LinearGradientBrush();
GradientStop gs1 = new GradientStop(Colors.Red, 0.3);
GradientStop gs2 = new GradientStop(Colors.Blue, 0.2);
lgb.Stops.Add(gs1); // это "использование" gs1
lgb.Stops.Add(gs2); // это "использование" gs2
Button2.Background = lgb; // это "использование" lgb

Здесь процесс состоит в построении значений (GradientStops) и использовании их в определениях более сложных значений.

Рассмотрим другой пример, направленный на изменение непрозрачности (которая может изменяться от нуля до единицы) фона кнопки (Btn) до 0,4. В этом конкретном использовании фон (Background) копируется в объект со свойством IsChangeable, установленным на значение "истина", фон изменяется и устанавливается обратно.

Brush b = Btn.Background.Copy(); // получить копию IsChangeable
b.Opacity = 0.4;
Btn.Background = b; // "использовать" измененное значение

Заметим, что присвоение "Bm.Background" в последней строке отделяет любое унаследованное значение или значение ведомости свойств, которое могло поступить.

Изменения, которые глубже в иерархии объектов, не выглядят для пользователя отличными от изменений, которые расположены менее глубоко:

Brush b = Btn2.Background.Copy();
LinearGradientBrush lgb = (LinearGradientBrush)b;
lgb.Stops[1].Color = Colors.Green;
lgb.Stops[0].Location = 0.2;
Btn2.Background = b;

Заметим, что Copy() нужно вызывать только на объекте верхнего уровня, а не на отдельных "ограничителях градиента" (GradientStops). Это потому, что система должна гарантировать, что подобъекты объекта со свойством IsChangeable, равным "истина", сами по себе устанавливаются с IsChangeable, равным "истина", когда к ним осуществляют доступ.

На фиг.15 показана диаграмма состояний, демонстрирующая состояния изменяемого объекта в основном использовании, начинающаяся со свойства IsChangeable, равного "истина", при новом создании. В общем случае, сплошные стрелки показывают состояния объекта, переходящего из текущего состояния в конечное состояние, в то время как любая пунктирная стрелка представляет операцию, которая оставляет объект в неизменном виде, но создает новый объект в конечном состоянии. В этой диаграмме состояний имеются два состояния, и переходы происходят при вызове либо Copy(), либо MakeUnchangeable(), и когда объект используется способом, квалифицируемым как использование, что описано выше. Заметим, что вызов метода Copy() из любого состояния приводит к новому значению с его свойством IsChangeable, заданным равным"истина", тогда как вызов MakeUnchangeable() приводит к конечному значению, причем IsChangeable задано равным "ложь".

Вышеприведенное описание представляет прямую, самосогласованную модель, которая описывает основное использование с только двумя состояниями, методы Copy() и MakeUnchangeable() и понятие "использования" значения Changeable. Однако в отношении изменений вышеприведенные примеры изменения основаны на концепции замены, т.е. копирования существующего элемента, изменения его на месте и копирования его обратно. Это предполагает резервирование памяти для выполнения (которое может быть потенциально значимым, в зависимости от того, насколько глубоко должно быть произведено изменение и насколько широк объект сам по себе для мелких клонов), а также дополнительную нагрузку на программиста для поддержания некоторого механизма отыскания пути для изменения атрибута.

Согласно аспекту настоящего изобретения для добавления поддержки концепции истинной изменчивости значений к модели добавляется еще одно свойство, именуемое StatusOfNextUse типа UseStatus. Заметим, что основная проблема, препятствующая изменчивости в модели единичного свойства, состоит в том, что квалифицированное использование значения безусловно приводит к результирующему значению со свойством IsChangeable, равным "ложь". Свойство StatusOfNextUse разрешает эту проблему.

public enum System.Windows.UseStatus
{
Unchangeable,
ChangeableCopy
}

По умолчанию, свойство StatusOfNextUse равно UseStatus.Unchangeable, но может быть задано равным UseStatus.ChangeableCopy, за счет чего использование значения, на которое оно установлено, приведет к созданию объекта клона, который имеет свойство IsChangeable, заданное равным "истина". В результате значение объекта можно изменять на месте без какого-либо дополнительного резервирования памяти для выполнения.

Кроме того, поскольку значения при использовании могут изменяться в этой модели, предусмотрено извещение, когда происходят такие изменения, через простое событие изменения. Кроме того, поскольку объект уже не является неизменным, сродство к контексту обеспечивается посредством члена UIContext. Заметим, что, когда объект является изменяемым, он имеет значение null. В противном случае он принадлежит UIContext, в котором он был создан. Результирующее определение класса Changeable будет:

public class System.Windows.Changeable
{
// Перенос из вышеописанного "основного использования"
public bool IsChangeable {get;} // по умолчанию истина
public Changeable Copy();
public void MakeUnchangeable();
// Новые члены
public bool CanMakeUnchangeable {get;} // MakeUnchangeable
// будет успешным?
public UseStatus StatusOfNextUse {get; set;} // по умолчанию
// Unchangeable
public event EventHandler Changed {add; remove;}
public UIContext UIContext {get;} // null когда неизменяемый
}

Вышеприведенный пример простой, неглубокой изменчивости описывал требования к изменению непрозрачности на "кисти" (Brush), с кодом, который должен действовать всякий раз, когда нужно изменять непрозрачность. Напротив, с механизмом изменчивости на основе свойства StatusOfNextUse, сначала сам Btn.Background имеет значение свойства IsChangeable, равное "истина":

Brush b = Btn.Background.Copy(); // получить копию IsChangeable
b.StatusOfNextUse = UseStatus.ChangeableCopy;
Btn.Background = b;

Выше использовалось (в квалифицированном использовании) значение с StatusOfNextUse равным UseStatus.ChangeableCopy, так что результат сам является изменяемым. После установки программист может производить нужные изменения, как в нижеследующем примере:

Btn.Background.Opacity = 0.3;

Программист может производить такие изменения так часто, как он хочет, и изменение будет происходить напрямую, без какого-либо выделения объекта при последующих настройках.

Заметим, что вышеприведенный пример не описывает, как Btn.Background появляется в первом месте, и, таким образом, его копию нужно делать посредством метода Copy(). В ситуации преднамеренной изменчивости, когда программист хочет создать фон, подлежащий изменению, лучше всего сделать это непосредственно, как в следующем примере:

SolidColorBrush b = new SolidColorBrush();
b.Color = Colors.Red;
b.StatusOfNextUse = UseStatus.ChangeableCopy; // задает это напрямую
Btn.Background = b;

При этом программист может задавать непрозрачность (Btn.Background.Opacity = ...) всякий раз, когда это необходимо, поскольку кисть первоначально была создана как StatusOfNextUse, равный UseStatus.ChangeableCopy.

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

В качестве примера такой более глубокой изменчивости рассмотрим LinearGradientBrush (lgb), где программист повторно хочет изменить свет седьмого ограничителя (lgb.Stops[6]). Программист может использовать те же команды, которые описаны выше, чтобы установить изменяемую версию в Btn.Background:

Brush b = Btn.Background.Copy(); // получить копию IsChangeable
b.StatusOfNextUse = UseStatus.ChangeableCopy;
Btn.Background = b;

Затем программист может повторно производить нужные изменения:

LinearGradientBrush lgb = ((LinearGradientBrush)Bm.Background);
lgb.Stops[6].Color=...some new color...;

Программист может также однократно обратиться к переменной "lgb", сохранить ее, а затем повторно задать ее, что очень эффективно.

На фиг.16 показано расширение диаграммы состояний, показанной на фиг.15, с дополнительным состоянием, представленным добавленным свойством StatusOfNextUse. Заметим, что модель только немного усложняется, поскольку основная диаграмма состояний, показанная на фиг.16, имеет два состояния и семь переходов, тогда как диаграмма состояний изменчивости имеет три состояния и одиннадцать переходов. Как можно видеть из фиг.15, значительным добавлением является состояние (StatusOfNextUse == ChangeableCopy) и переход "использование" из этого состояния, который приводит к новой копии со значением свойства IsChangeable, заданным равным "истина".

Согласно фиг.15 вызов метода Copy() приводит к новому значению со свойством IsChangeable, равным "истина", со свойством StatusOfNextUse, заданным равным Unchangeable. Аналогично, вызов метода MakeUnchangeable() приводит к нулевому значению для свойства IsChangeable, заданной равным "ложь". Заметим, что хотя гибкость была добавлена посредством изменчивости, эти константы не изменились.

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

Кроме того, бывают ситуации, когда изменяемый объект нельзя сделать неизменяемым. Примеры включают в себя "кисть визуала" (VisualBrush) (как описано в вышеупомянутой патентной заявке США № 10/402,268), в которой нельзя ограничить изменение нижележащего визуала, и поэтому он будет нечувствителен к состоянию, когда "кисть визуала" является "неизменяемой". Анимации и "видеоданные" (VideoData) (поскольку они изменяются со временем) также являются примерами. Попытки вызвать MakeUnchangeable() на таких объектах создают исключения или, что хуже, могут оставить объект в плохом состоянии, поскольку его части можно сделать неизменяемыми, а другие нельзя. Этих проблем можно избежать посредством другого свойства, CanMakeUnchangeable. Если это свойство возвращает значение "истина", то гарантируется, что MakeUnchangeable() увенчивается успехом с учетом того, что между этими вызовами не происходит никаких изменений объекта.

Имеется конфликт в семантике, который иногда возникает между StatusOfNextUse и CanMakeUnchangeable. Если CanMakeUnchangeable равно "ложь", то значение UseStatus.Unchangeable для StatusOfNextUse действительно не имеет смысла, поскольку следующее использование не может быть неизменяемым. Поэтому при запросе StatusOfNextUse, когда CanMakeUnchangeable равно "ложь", оно никогда не возвращает UseStatus.Unchangeable. Вместо того чтобы возвращать UseStatus.Unchangeable, оно возвращает UseStatus.ChangeableCopy.

Выше представлена модель, где каждое (квалифицированное) использование изменяемого объекта (IsChangeable равно "истина") приводит к копированию этого объекта и в зависимости от значения StatusOfNextUse это "использование" может быть или не быть само по себе изменяемым объектом (Changeable). То, что вышеописанная модель не обеспечивает, это использование значения в множестве мест и поддержание совместно используемой ссылки на это значение. Например, в вышеописанной модели программист не может создать LinearGradientBrush (кисть линейного градиента), использовать ее на двух средствах управления Button (кнопка) и затем изменить один раз LinearGradientBrush, чтобы повлиять на оба средства управления. Вместо этого программисту нужно использовать ее дважды, получить кисть обратно из средств управления, а затем задать каждую из них независимо. В общем случае, эта модель оказывается наиболее ожидаемой и/или наименее неожиданной для программистов, но существуют сценарии, где желательны дополнительные функциональные возможности.

Один такой сценарий состоит в "анимации" (Animation), где, если программист хочет создать сцену с n элементами, каждый из которых отвечает одной и той же временной шкале, эту временную шкалу нужно клонировать n раз и запрашивать BeginIn() n раз. Гораздо лучший подход с точки зрения как производительности, так и эффективности, а также для удобства модели программирования состоит в совместном использовании ссылки на единую временную шкалу, вызове BeginIn() на ней и ее распространении по мере надобности.

Для реализации этого сценария третье значение, ChangeableReference, предусмотрено с перечислением UseStatus. UseStatus теперь выглядит как:

public enum System.Windows. UseStatus
{
Unchangeable,
ChangeableCopy,
ChangeableReference
}

Когда изменяемый объект (Changeable), который имеет StatusOfNextUse, заданное равным UseStatus.ChangeableReference, используется (квалифицированным образом), это значение уже не копируется. Вместо этого, раздается ссылка на существующее значение, и последующие изменения этого значения (или любых ранее или затем розданных ссылок) будут влиять на результат использования. Иными словами, изменяемое значение теперь совместно используется с потенциально любым количеством использований.

Ниже приведен пример использования уровня элементов:

Brush b = new SolidCoiorBrush(...);
b.Color = Colors. Yellow;
b.StatusOfNextUse = UseStatus.ChangeableReference;
Btn1.Background = b;
Btn2.Background = Btn1.Background;
Btn3 .Background = Btn2.Background;
Btn4.Background = b;
// в этот момент, все четыре кнопки желтые
((SolidColorBrush)Btn3.Background).Color = Colors.Purple;
// Они меняются на пурпурные, и о них оповещают.

В вышеприведенном примере простая операция рисования была описана с двумя генерированными прямоугольниками, одним красным и одним голубым:

SolidColorBrush scb = new SolidColorBrush();
scb.Color = Colors.Red;
ctx.DrawRectangle(scb,...); // это "использование"
scb.Color = Colors.Green;
ctx.DrawRectangle(scb,...); // это "использование"

Это желаемое поведение. Однако, если программист вместо этого пожелает, чтобы кисть совместно использовалась, можно использовать следующие команды:

SolidColorBrush scb = new SolidColorBrush();
scb.UseResult = UseResult.ChangeableReference;
scb.Color = Colors.Red;
ctx.DrawRectangle(scb,...); // это "использование"
scb.Color = Colors.Green; // исходный прямоугольник становится зеленым
ctx.DrawRectangle(scb,...); // это "использование"

Здесь оба прямоугольника зеленые. Если позднее цвет изменяется, например scb.Color = Colors.Yellow, оба прямоугольника станут зелеными. Заметим, что ctx.DrawRectangle(...) оказывается командой рисования непосредственного режима, однако она фактически строит список отображения/метафайл, подлежащий удержанию и последующему отображению.

С точки зрения пользовательской модели режим ChangeableReference гарантирует, что части, использующие изменяемый объект, будут извещаться о любых изменениях этого значения. Это делается посредством события "Changed" ("изменился"), которое, как и другие события, являются делегатом группового вещания. Для реализации система должна быть уверена, что множественные использования с единым приемником извещений не извещают этот приемник для каждого использования. Кроме того, механизмы очистки имеют требования при удалении элементов, так чтобы только удалить приемник, когда уходят использования, подключенные к этому приемнику. Один подход к этому состоит в том, чтобы ссылаться на делегаты счета. Данная реализация может достигать этих требований через закрытую ("приватную") структуру данных, например RefCountedMulticastEventHandler.

На фиг.17 показана диаграмма состояний, основанная на фиг.15 и 16, но в которую добавлено состояние ChangeableReference (через другую настройку в свойстве StatusOfNextUse). Заметим, что согласно аспекту настоящего изобретения состояние ChangeableReference и переход "использование" (Use) этого узла не делает копию. Вместо этого квалифицированное использование приводит к тому, что свойство состояния следующего использования остается в состоянии изменяемой ссылки, тем самым обеспечивая истинную изменчивость. Кроме того, заметим что, хотя фиг.17 сложнее, чем фиг.15 и 16, поведение методов Copy() и MakeUnchangeable() остается постоянным; метод Copy() по-прежнему приводит к новому объекту значения, имеющему свойство IsChangeable, равное "истина", и свойство StatusOfNextUse, равное Unchangeable, и метод MakeUnchangeable() по-прежнему приводит к тому, что конечный объект значения имеет свойство IsChangeable, равное "ложь".

Следует заметить, что помимо преимуществ единого набора типов настоящее изобретение обеспечивает значительную гибкость для программистов. Например, большинство значений, построенных приложением обычным образом, не будет изменяемым, неизменяемые значения потребляют меньше ресурсов. Однако, как описано выше, имеется изменчивость, дающая программистам мощный и интуитивно ясный способ изменения значений, в особенности глубоких значений, с высокой производительностью. Заметим, что, хотя это и не представлено на фиг.17, состояние, в котором создается новый тип (свойство IsChangeable равно "истина", свойство StatusOfNextUse равно Unchangeable), является единственно возможным состоянием по умолчанию. Таким образом, в альтернативных реализациях тип может быть в другом состоянии после создания, например, свойство IsChangeable равно "истина", свойство StatusOfNextUse равно ChangeableReference), например, чтобы задать по умолчанию изменяемые значения.

Обращаясь к объяснению работы настоящего изобретения, настоящее изобретение обеспечивает значительные преимущества, когда имеет дело с глубокими свойствами объекта, что называют "пунктир вниз". Например, рассмотрим следующее:

GeometryCollection g;
g.Geometries[12].Figures[2].Segments[0].Points[17] =
new Point(0.2, 0.3);

Глубокий доступ в объект геометрии 'g' - это пример того, что называют "пунктир вниз". Заметим, что доступ к свойствам (геометриям, [12], фигурам, [2], отрезкам, [0] и точкам) вызывают получатели, а не установщики свойств; [17] это единственный доступ к свойству, который приводит к вызову установщика. Языки программирования, в общем случае, не могут различать между доступом к свойству с целью задания значения свойства глубже вниз и доступом для считывания значения глубже вниз.

Когда "пунктир вниз" начинается с неизменяемого объекта, осуществляется доступ к переменной локального члена. Пример включает в себя доступ к элементу, который не был сделан изменяемым путем использования свойства "ChangeableValue" ("изменяемое значение").

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

Аспект настоящего изобретения состоит в том, что неглубокое клонирование по требованию осуществляется только тогда, когда необходимо. Это максимизирует совместное использование, минимизирует резервирование памяти для выполнения, допускает изменения без резервирования памяти для выполнения и не продвигает концепцию клонирования в пользовательскую модель. Это приобретает особую важность в более глубоких деревьях и при работе в трех измерениях. Для этого метод Copy() обеспечивает иллюзию глубокой копии, но в действительности сначала делает только неглубокую копию, а затем медленно делает более глубокие копии по мере необходимости. Такой "пунктир вниз" обеспечивает значительное повышение производительности.

Согласно аспекту настоящего изобретения другое свойство на изменяемом объекте (в общем случае, невидимом для приложений) состоит в том, что изменяемый объект имеет событие "изменился" (Changed) (типа EventHandler). Когда свойство "изменяемого объекта" (Changeable) изменяется, вызывается "изменяемый делегат" на этом изменяемом объекте, с изменяющимся изменяемым объектом в качестве отправителя. Действие делания неглубокого клона посредством "пунктира вниз" проталкивает обработчики события Changed вниз в мелкий клон. Это позволяет последующим изменениям появляться на более глубоких элементах и запускать надлежащие обработчики событий. Заметим, что также существует событие Changed, так что клиенты, отличные от системы свойств, могут использовать эту систему и регистрироваться на извещения.

Изменения обработчика события Changed распространяются вниз на подобъекты. Кроме того, изменения изменяемого объекта, которое само по себе задействует другие изменяемые объекты (например, добавляет изменяемый подобъект в изменяемый объект, удаляет его и т.д.), приводят к тому, что обработчики событий, содержащих изменяемые объекты, удаляются из старых и проталкиваются в новые рекурсивно.

На фиг.18-23 показано, как действует неглубокое клонирование и "пунктир вниз", на основании следующего кода в качестве примера:

LinearGradientBrush lgb = new LinearGradientBrush();
lgb.Stops.Add(newGradientStop(Colors.Red,0.3));
lgb. Stops. Add (new GradientStop(Colors.Blue,0.2));
lgb.Stops.Add (new GradientStop(Colors.Green,0.1));
Btn1.Background = lgb;
Btn2.Background = lgb;

Как показано на фиг.18, кнопка 1 и кнопка 2 указывают на одну и ту же кисть 1802 линейного градиента, которая имеет узел 1804 ограничителей и свойства цвета и позиции для заданных ограничителей, размещенные иерархически ниже. Рассмотрим код:

Btn1.Background = Btn1.Background.Copy();

Выполнение этого кода приводит к тому, что копия 1902 кисти 1802 линейного градиента выполнена и указана кнопкой 1, как показано на фиг.19.

Выполнение кода:

LinearGradientBrush lgb = ((LinearGradientBrash)Btn1.Background);

lgb.Stops[1].Color = Colors.Orange;

обеспечивает доступ к свойству, имеющему значение Changeable, изменяемого объекта, который имеет IsChangeable == "истина", что позволяет гарантировать, что то, что извлекается, является записываемым. Как представлено в общем виде на фиг.20-22, выполнение этого кода приводит к тому, что (1) в иерархию вставлен другой узел 2004 ограничителей, который указывает на каждый из отдельных ограничителей (фиг.20); (2) делается копия 2110 (фиг.21) второго узла ограничителя (узел остановки [1], обозначенный 2010 на фиг.20 и 21, который имеет свойство "синий" ниже), так что родитель этой копии, ранее скопированный узел 2004 ограничителей, имеет в качестве своего потомка эту копию 2110 (вместо исходного узла 2010 ограничителя для свойства "синий"); и (3) свойство "синий" этого узла 2010 меняется на "оранжевый", как представлено на фиг.20. Заметим, что "оранжевый" - это тип значения, обозначенный ромбами на фигурах, и последующие изменения не приводят ни к какому выделению, например смене на цвет "красный" на фиг.23.

В неизменяемом состоянии изменяемый объект может считываться из и записываться в любой контекст. Если в измененном состоянии, то UiContext, определенный во время построения, можно использовать для связывания с изменяемым объектом, чтобы только позволить доступ из этого контекста. Если MakeUnchangeable позднее используется, контекст переходит в null. Кроме того, всякий раз, когда делается Copy() изменяемого объекта, новая копия получает UiContext от вызывающей стороны, не из контекста изменяемого источника. API обеспечивает свойство "только получения" "контекста ПИ" на изменяемом объекте, которое равно null, в случае неизменяемости. Это свойство является открытым, поэтому приложения могут указывать, возможен ли доступ к данному объекту.

Изменяемые объекты, построенные с null, переданным конструктору, будут определены с пустым "контекстом ПИ". Если изменяемый объект имеет пустой UiContext и свойство IsChangeable, заданное равным "истина", то приложению нужно решать любые вопросы конкуренции нитей, которые могут возникать. В этом случае система не препятствует одновременному доступу из множества контекстов.

Может возникнуть ситуация, когда изменяемый объект пытается встроить в себя другой изменяемый объект (например, задавая ограничитель градиента в кисти линейного градиента), и "контексты ПИ" не совпадают. Например, рассмотрим LinearGradientBrush lgb, имеющую UIContext, равный А, тогда как GradientStop gs имеет UIContext, равный B, и StatusOfNextUse, равный ChangeableReference. Попытка установить gs в lgb приведет к возникновению исключения, поскольку это попытка смешать "контексты ПИ", что запрещено.

При выполнении изменения в изменяемом объекте наступает событие Changed ("изменился"), и этот изменяемый объект обеспечивается как объект отправителя для обработчика событий. Однако бывают ситуации, когда отправка объекта, который действительно изменился, нежелательна, и когда было бы полезнее иметь другой объект в качестве отправителя. Примером этого являются анимированные объекты, где анимация (сама по себе изменяемая) удерживается на временной шкале (иногда именуемой тактовым сигналом), которая описывает ее анимационное поведение. Такие события, как Pause(), происходят на временной шкале, и не анимация, а, в общем случае, приложения хотят знать, что анимация приостановилась.

Возможны различные решения, например запуск событий Changed вверх по цепи изменяемых объектов. Это создает ряд проблем, включая принятие решения, где остановиться, вызывающих большое количество извещений, когда возникает гораздо больше событий, даже когда ничто не принимает и/или не использует события, и эти изменяемые объекты, умышленно, не знают своих родителей, но, напротив, в общем случае, знают, о чем извещать при событии изменения. Наличие схемы, в которой изменяемые объекты отслеживают своих родителей, потребовало бы дополнительного хранилища и учета использования системных ресурсов. Тем не менее, такое решение можно реализовать.

Другое решение состоит в реализации изменяемого объекта, при которой отправитель является изменяемым объектом, а не внутренним изменяемым объектом, который фактически изменился. PropagateEventHandler реализуется не для того, чтобы проталкивать вниз обработчика, который он принимает, но, напротив, чтобы сохранять этот обработчик в другом месте, создавать новый, локальный обработчик, который, будучи вызван, вызывает сохраненный обработчик, но с изменяемым объектом в качестве аргумента отправителя. Новый локальный обработчик проталкивается вниз на PropagateEventHandler на потомках изменяемого объекта. Заметим, что этот метод прерывает каждый обработчик событий, требуя, чтобы этот PropagateEventHandler правильно обрабатывался, будучи вызван со значением "ложь" (когда обработчики должны быть удалены), и, таким образом, требуя осуществления учета использования системных ресурсов.

Заметим, что это решение не имеет явного механизма обзора BeginChange/EndChange, делая его более прямым и устойчивым к исключениям (поскольку не предусмотрено никакой модальности и нет такой EndChange(), которая была бы пропущена при возникновении неожиданного исключения). Однако Begin/EndChange существовали, чтобы "получателям" объектов не приходилось делать неглубокие клоны значений, которые они получают, когда эти клоны были заморожены, и система не была в режиме записи. В противном случае эти замороженные значения в режиме записи получают мелкий клон, сделанный из них. В результате дерево разрастается более часто, чем с Begin/EndChange, и может делать это, когда не используется никакой настройки, а только получение. Тем не менее, если получателю предписано начать с неизменяемого значения, он не будет делать неглубокий клон (заметим, что это отличается от случая, когда получатель вызывается на изменяемом значении, и значение, получаемое через "get", является неизменяемым, что имеет место при операции клонирования).

Например, при обращении к Btn.Background.Opacity, когда Btn.Background не является изменяемым (например, по умолчанию), копирование не производится. Вместо этого копирование происходит, когда имеет место "Btn.Background = Btn.Background.ChangeableValue" или подобное, т.е. затраты на копирование происходят только при использовании. Иными словами, если намерение изменить значение не выражается, то произвольное "получение" не приводит к затратам на копирование. Заметим, что если значения поддерживают извещение об их "последнем созданном клоне", то этот клон можно раздавать по использованию объекта, пока объект не изменился вследствие создания клона (такие изменения просто приводят к освобождению этого кэшированного клона). Это допускает большее совместное использование. Кроме того, заметим, что реализаторы управления не настолько чрезмерно загружены участием в этом шаблоне, как это делается для шаблона самого по себе, чтобы шаблон был полезен для пользователей. Аналогично, если предусмотрена расширяемость типов, запись "типов сред" не должна быть слишком сложной.

Реализатор управления представлен с помощью одной и той же модели для работы с "изменяемым объектом" (Changeable), как с любым другим значением. Например, следующий код обеспечивает средство управления "сетка" (Grid) со свойством AlternateBrush типа Brush:

public static readonly DynamicProperty AlternateBrushID = ...;
private Brush_alternateBrush;
public Brush AlternateBrush
{
get
{
return ComputeValue(AlternateBrushID, ref_alternateBrush);
}
set
{
WriteLocal(AlternateBrushID, value);
}
}

Заметим, что это идентично общему свойству, участвующему в системе свойств. Это потому, что WriteLocal будет производить особую обработку для глубоких свойств, которые являются производными от класса Changeable.

Реализатор изменяемого типа нуждается в однострочной преамбуле и однострочном постскриптуме на всем, что изменяет изменяемый объект (например, свойства). Также простая, однострочная преамбула нужна на объектах, которые обращаются к состоянию изменяемого объекта (например, получателях свойств). Нужны реализации CloneCore(), MakeUnchangeableCore(), PropagateEventHandlerCore(), PropagateEventHandlers() (заметим, что только последние три нужны для типов, которые имеют другие "изменяемые объекты" в качестве свойств), и также необходим упаковщик с сохранением типов для Copy().

Нижеприведенные примеры происходят из справочного прототипа, включающего в себя (искусственный) пример "ограничителя градиента" (GradientStop), который является простым изменяемым типом (который упрощен в том, что ни один из его подтипов не является изменяемым). Заметим, что на практике очень немногие изменяемые типы будут таким образом упрощены, поскольку все, что содержит коллекцию анимаций (саму по себе изменяемую), будет более сложным:

public class GradientStop : Changeable
{
public GradientStop()
{
}
public GradientStop(Color color, float location)
{
_color = color;
_location = location;
}
public Color Color
{
get
{
ReadPreamble();
return _color;
}
set
{
// Это схема для задания простых типов значений.
WritePreamble();
if(_color != value)
{
_color = value;
WritePostscript();
}
}
}
public float Location
{
get
{
ReadPreamble();
return _location;
}
set
{
// Это схема для задания простых типов значений.
WritePreamble();
if (_location != value)
{
_location = value;
WritePostscript();
}
}
}
// Создает новую, сохраняющую типы, версию Copy().
public new GradientStop Copy()
{
return (GradientStop)base.Copy();
}
protected override Changeable CloneCore(bool shallowClone)
{
// Неглубокие и глубокие обрабатываются здесь одинаково, когда
// все члены являются типами значения
return new GradientStop(_color, _location);
}
private Color _color;
private float _location;
}

Ниже приведен пример "кисти линейного градиента" (LinearGradientBrush), которая является более сложным изменяемым типом (поскольку некоторые из его подтипов, а именно "ограничители градиента" (GradientStop), сами по себе являются изменяемыми):

public class LinearGradientBrush : Brush
{
public LinearGradientBrush()
{
}
public LinearGradientBrush(GradientStop stop1, GradientStop stop2,
double opacity)
: base(opacity)
{
// Присвоение через свойства приводит к "использованию"
// параметров.
Stop1 = stop1;
Stop2 = stop2;
}
public GradientStop Stop1
{
get
{
_stop1 = (GradientStop)EmbeddedChangeableGetter(_stop1);
return _stop1;
}
set
{
if(_stop1 != value)
{
_stop1 = (GradientStop)EmbeddedChangeableSetter(_stop1, value);
WritePostscript();
}
}
}
public GradientStop Stop2
{
get
{
_stop2 = (GradientStop)EmbeddedChangeableReader(_stop2);
return _stop2;
}
set
{
if(_stop2 != value)
{
_stop2 = (GradientStop)EmbeddedChangeableWriter(_stop2, value);
WritePostscript();
}
}
}
// Вместо изменения к r/o (или проверки того, можно ли это сделать).
// Помощники проверяют, равны ли аргументы null.
protected override bool MakeUnchangeableCore(bool checking)
{
return Changeable.MakeUnchangeable(_stop1, checking);
&& Changeable.MakeUnchangeable(_stop2, checking);
}
// Распространение изменений события
protected override void PropagateEventHandlerCore(EventHandler handler, bool adding)
{
Changeable.ModifyHandlerIfChangeable(stop1, handler, adding);
Changeable.ModifyHandlerIfChangeable(_stop2, handler, adding);
}
protected override Changeable CloneCore(bool shallowClone)
{
// Всегда используйте поля членов, а не свойств, для клонирования.
// Иначе вы непреднамеренно вызовете дополнительное
// неглубокое клонирование.
if (shallowClone)
{
return new LinearGradientBrush(_stop1, _stop2, _opacity);
}
else
{
return new LinearGradientBrush(
(GradientStop)Changeable.CloneDownToUnchangeable(_stop 1),
(GradientStop)Changeable.CloneDownToUnchangeable(_stop2),
_opacity);
}
}
private GradientStop _stop_;
private GradientStop _stop2;
}

Изменяемая объектная модель делится на открытую часть и часть, которую видят расширители и хостеры. Однако, заметим вновь, что эти вещи являются прямыми для написателя компонентов, который использует эти типы.

Заметим, что действие квалифицированного использования изменяемого объекта, опирающееся только на его StatusOfNextUse, не работает точно в каждой ситуации. В общем случае, проблема состоит в том, что, когда изменяемый объект (например, "кисть", "видеоданные" (VideoData) и т.д.) присваивается свойству "элемент" (Element) (например, VideoSource), этот изменяемый объект "используется" в квалифицированном использовании. В случае анимационных изменяемых объектов (например, VideoData, но также любой анимации) действие "использования" создает клон, что является правильным и ожидаемым поведением. Затем, при вызове метода OnRender() элементов, реализация OnRender() обычно проталкивает значение в "контекст рисования" (DrawingContext), например через DrawingContext.DrawVideo(videoData,...). Этот вызов в "контекст рисования" также использует изменяемый объект (в данном случае, VideoData), что приводит к созданию другого клона.

Оба поведения, когда изменяемые объекты "используются" таким образом, правильны и имеют смысл при рассмотрении по отдельности. Однако при их объединении возникает проблема, состоящая в том, что реализатор управления не ожидает квалифицированного использования всякий раз, когда вызывается OnRender(), и в действительности нет никакой выгоды делать это, поскольку использование не предоставлено приложению и на самом деле является чистой служебной нагрузкой, от которой желательно избавиться. Кроме того, при объединении зависимых анимаций и независимых анимаций OnRender() будет часто вызываться и анимации будут повторно копироваться, что не является правильным поведением. Механизм, именуемый "ссылкой на изменяемый объект" (ChangeableReference), позволяет реально не копировать "использование", но вместо этого только получать ссылку на используемое значение.

Решение состоит в организации взаимодействия между такой сущностью, как "контекст рисования" (DrawingContext) и "свойствами зависимости" (DependencyProperty) на "объекте зависимости" (DependencyObject). В частности, "свойство зависимости" средства управления, когда в него установлено значение, должно указывать, что оно "позволяет" обрабатывать "изменяемый объект" как "ссылку на изменяемый объект", если для конкретного контекста, в котором оно впоследствии используется, это желательно. Затем операции "контекста рисования", скажем, указывают, что они "предпочитают", чтобы "изменяемый объект" обрабатывался как "ссылка на изменяемый объект", с условием, что сам "изменяемый объект" позволит это.

Для этого предусмотрено "булево" (Boolean) свойство, именуемое Changeable.AllowChangeableReferenceOverride, используемое в ChangeableHelper.UseChangeable. В этой реализации UseChangeable действует, как раньше с отображением истины/лжи в ForceUnchangeable/NoOverride. Если UseChangeable вызывается с PreferChangeableReference и изменяемый объект имеет IsChangeable==true, и изменяемый объект имеет AllowChangeableReferenceOverride==true, то "изменяемый объект" будет использоваться как "ссылка на изменяемый объект".

Для этого DependencyObjectSetValue() задает "изменяемый объект", который она удержала (когда его можно модифицировать), равным AllowChangeableReferenceOverride, и методы DrawingContext вызывают UseChangeable с UsageOverridePreferChangeableReference.

Заметим, что когда оба условия не соответствуют истине, также имеет место правильное поведение, заключающееся в том, что Elt2.Prop = Elt1.Prop будет использовать свойство, как ожидается в квалифицированном использовании, копируя его, если оно является модифицируемым, если оно не было явно задано равным ChangeableReference, поскольку UseChangeable не будет вызываться с PreferChangeableReference. Прямое использование "контекста рисования" также будет функционировать надлежащим образом, поскольку "изменяемые объекты", отправляемые вниз на него, не будут иметь AllowChangeableReferenceOverride.

Заметим, что при наличии изменяемого объекта, подобъектом которого является "ссылка на изменяемый объект", можно сделать неглубокий клон и глубокий клон. "Неглубокий клон" должен работать, поскольку метод CloneCore создает новую неглубокую "оболочку" и назначает по потомкам, не проходя глубоко в них. С глубокими клонами процесс является прямым в случае дерева "копий изменяемых объектов" (ChangeableCopy) и "неизменяемых объектов" (Unchangeable), клонируя вниз на "неизменяемые объекты", делая каждый клон вдоль пути сам по себе "неизменяемым объектом" (предполагая, что CanMakeUnchangeable равно "истина"). Это обеспечивает глубокий клон, когда верхний уровень является Changeable, а все, что ниже его, является Unchangeable. Заметим, что "пунктир вниз" будет снова делать подэлементы изменяемыми.

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

Рассмотрим следующий пример:

Brush b = new LinearGradientBrush();
b.Stops = new GradientStopCollection();
GradientStop gs = new GradientStop();
gs.StatusOfNextUse = UseStatus.ChangeableReference;
b.Stops.Add(gs);
Button button1, button2;
Button1.Background = b;
button2.Background = b; (or button2.Background = button1 .Background)
gs.Color = Colors.Purple;

Здесь создается "кисть линейного градиента" в виде коллекции его "ограничителей" (Stop) и единичного "ограничителя" (Stop) и делается "ссылкой на изменяемый объект" (ChangeableReference). Кисть можно использовать во многих местах, и изменение в ChangeableReference GradientStop должно влиять на обе кисти.

ПРИМЕРЫ (ДЕЙСТВИТЕЛЬНЫЕ И НЕДЕЙСТВИТЕЛЬНЫЕ) ИСПОЛЬЗОВАНИЯ ИЗМЕНЯЕМЫХ ОБЪЕКТОВ

В нижеследующем разделе изложена сущность использования объектов и манипулирования ими, каковые являются производными от класса "изменяемый объект" (Changeable), благодаря чему такие объекты, как кисти, перья и анимации, изменяются под управлением программиста. Классы, являющиеся производными от Changeable, моделируют изменчивость путем автоматического построения своей неизменяемой версии при использовании в квалифицированном использовании. Согласно описанному выше Changeable рассматривается как используемый в квалифицированном использовании, когда объект установлен в свойство "системы свойств", используемое как подобъект в сложном объекте класса Changeable или используемое в команде "контекст рисования" (DrawingContext).

При разработке приложений с такими объектами объекты системы графики и сред, в общем случае, создаются, задаются, используются, после чего никогда не изменяются. Например, чтобы задать фон "кнопки", программист может использовать "кисть чистого цвета" (SolidColorBrush), который является производным от Changeable; но программист никогда не может вновь изменить фон кнопки в ходе выполнения приложения. Рассмотрим один пример:

// C#
SolidColorBrush myBrush = new SolidColorBrush(Colors. Yellow);
myBrush.Opacity = 0.5;
// "использование" myBrush.
myButton.Background = myBrush;
' VB .NET
Затемнить myBrush как new _
System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors. Yellow)
myBrush.Opacity = 0.5
' "использование" myBrush.
myButton.Background = myBrush

При таком использовании Changeable ведет себя как тип значения, например, Rect ("прямоугольник") или Color ("цвет"). Changeable копируется в свой пункт назначения, и изменения к оригиналу не влияют на изменения используемого значения. Однако бывают ситуации, когда программисту может понадобиться изменить объект после того, как он был использован. Например, предположим, что программист хочет изменить цвет кнопки в предыдущем коде после того, как пользователь щелкает по ней.

Структура Changeable существует, чтобы удовлетворять потребность в различных ситуациях, например вышеописанной. В общем случае, Changeable - это значение, которое может или не может быть изменяемым, что определяется значением свойства IsChangeable. Попытка изменить значение, когда IsChangeable равно "ложь", приводит к исключению. Кроме того, объекты Changeable, которые можно изменять, порождают события "изменился" (Changed), когда они изменяются или когда изменяется любой из их членов. Таким образом, при работе с изменяемыми объектами (Changeable) важно понимать, когда Changeable "используется" в квалифицированном использовании.

По умолчанию, когда изменяемый объект используется в квалифицированном использовании, создается неизменяемая копия, и эта копия фактически используется. Копия имеет значение IsChangeable, равное "ложь". Следующий код обуславливает возникновение исключения, поскольку код пытается изменить неизменяемую копию myBrush, которая использовалась для задания фона кнопки:

//C#
SolidColorBrush myBrush = new SolidColorBrush(Colors. Yellow);
myBrush.Opacity = 0.5;
myButton.Background = myBrush;
// Вызывает возникновение исключения.
((SolidColorBrush)myButton.Background).Color = Colors.Blue;
'VB .NET
Затемнить myBrush как new _
System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Yellow)
myBrush.Opacity = 0.5
myButton.Background = myBrush
' Вызывает возникновение исключения.
CType(myButton.Background, System.Windows.Media.SolidColorBrush).Color=
_System.Windows.Media.Colors.Blue

Изменение исходного изменяемого объекта не обновляет копии:

//C#
SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);
myBrush.Opacity = 0.5;
myButton.Background = myBrush;
// Не изменяет фон кнопки.
myBrush.Color = Colors.Blue;
' VB .NET
Затемнить myBrash как new _
System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors. Yellow)
myBrush.Opacity = 0.5
myButton.Background = myBrush
' Не изменяет фон кнопки.
myBrush.Color = System.Windows.Media.Colors.Blue

Чтобы изменить фон кнопки в этом примере, программист повторно присваивает измененную кисть свойству фона кнопки:

//C#
SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);
myBrush.Opacity = 0.5;
myButton.Background = myBrush;
// Не изменяет фон кнопки.
myBrush.Color = Colors.Blue;
// Обновляет фон кнопки.
myButton.Background = myBrush;
'VB.NET
Затемнить myBrush как new _
System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Yellow)
myBrush.Opacity = 0.5
myButton.Background = myBrush
' Не изменяет фон кнопки.
myBrush.Color = System.Windows.Media.Colors.Blue
' Обновляет фон кнопки.
myButton.Background = myBrush

Программист также может использовать метод "копирование" (Copy) для извлечения изменяемой копии используемого изменяемого объекта. Извлеченная копия по-прежнему повторно присваивается обратно свойству, чтобы иметь действие:

//C#
SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);
myBrush.Opacity = 0.5;
myButton.Background = myBrush;
SolidColorBrush anotherBrush = (SolidColorBrush)myButton.Background.Copy();
anotherBrush. Color = Colors.Purple;
// Обновляет фон кнопки.
myButton.Background = anotherBrush;
' VB .NET
Затемнить myBrush как new _
System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Yellow)
myBrush. Opacity = 0.5
myButton.Background = myBrush
Dim anotherBrush As new System.Windows.Media.SolidColorBrush
anotherBrush = _
CType(myButton.Background.Copy, System.Windows.Media.SolidColorBrush)
anotherBrush. Color = System.Windows.Media.Colors.Purple
' Обновляет фон кнопки.
myButton.Background = anotherBrush

Поскольку это не идеальное поведение Changeable во всех ситуациях, например, если программист хочет изменить используемую версию (рабочую копию) Changeable, класс Changeable позволяет программисту задавать, как он ведет себя при использовании, обеспечивая свойства StatusOfNextUse.

StatusOfNextUse предусматривает три варианта поведения Changeable при использовании:

Unchangeable ("неизменяемый"): поведение по умолчанию, показанное в примерах в предыдущем разделе. Когда используется объект Changeable, он создает неизменяемую копию самого себя, которая используется вместо исходного объекта. Программист может продолжать изменять исходный объект; исходная версия (сделанная копия) не подвержена изменениям, вносимым в исходный объект, и не может быть изменена. Для изменения используемой версии используется метод "копирование" для получения изменяемой версии, эта версия обновляется, и новая версия заменяет используемую версию.

ChangeableCopy ("изменяемая копия"): когда используется объект Changeable, он создает изменяемую копию самого себя, которая используется вместо исходного объекта. Программист может продолжать изменять исходный объект; исходная версия (сделанная копия) не подвержена изменениям, вносимым в исходный объект, но также является изменяемой. Используемая версия имеет StatusOfNextUse, равный Unchangeable.

ChangeableReference ("ссылка на изменяемый объект"): когда используется объект Changeable, он обеспечивает ссылку на самого себя. Программист может продолжать изменять исходный объект; изменения исходного объекта влияют на используемую версию - они являются одним и тем же объектом.

ChangeableCopy изменяет поведение Changeable так, что при использовании он создает изменяемую копию самого себя, а не неизменяемую копию (как в случае, когда по умолчанию задано Unchangeable. Нижеследующие код (показанный ранее) вызывает исключение, поскольку свойство StatusOfNextUse для myBrush по умолчанию задано равным Unchangeable:

//C#
SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);
myBrush.Opacity = 0.5;
myButton.Background = myBrush;
// Вызывает возникновение исключения.
((SolidColorBrash)myButton.Background).Color = Colors.Blue;
' VB .NET
Затемнить myBrush как new _
System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Yellow)
myBrush. Opacity = 0.5
myButton.Background = myBrush
' Вызывает возникновение исключения.
CType(myButton.Background, System.Windows.Media.SolidColorBrush).Color =
_System.Windows.Media.Colors.Blue

Если же свойство StatusOfNextUse кисти задано равным ChangeableCopy, код работает, как предусмотрено:

//C#
SolidColorBrush myBrush = new SolidColorBrush(Colors. Yellow);
myBrush. StatusOfNextUse = UseStatus.ChangeableCopy;
myBrush.Opacity = 0.5;
myButton.Background = myBrush;
// Работает, поскольку кисть имеет UseStatus, равный ChangeableCopy.
((SolidColorBrush)myButton.Background).Color = Colors.Blue;
' VB .NET
Затемнить myBrush как new
_System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Yellow)
myBrush.StatusOfNextUse = MSAvalon.Windows.UseStatus.ChangeableCopy
myBrush.Opacity = 0.5
myButton.Background = myBrush
' Работает, поскольку кисть имеет UseStatus, равный ChangeableCopy.
CType(myButton.Background, System.Windows.Media.SolidColorBrush).Color =
_System.Windows.Media.Colors.Blue

Задание ChangeableCopy также сохраняет любые подобъекты главного объекта изменяемыми. В следующем примере "кисти линейного градиента" (LinearGradientBrush) придается StatusOfNextUse, равный ChangeableCopy. В результате "кисть линейного градиента" и ее подобъекты остаются изменяемыми после того, как их использовали; программисту не нужно задавать свойство StatusOfNextUse любых объектов Changeable, содержащихся в объекте, в этом примере, GradientStop:

//C#
LinearGradientBrush myBrush = new LinearGradientBrush();
myBrush.StatusOfNextUse = UseStatus.ChangeableCopy;
myBrush.GradientStops.Add(new GradientStop(Colors.Blue, 0));
myBrush.GradientStops.Add(new GradientStop(Colors.Green, 1));
myButton.Background = myBrush;
// Работает, поскольку кисть имеет UseStatus, равный ChangeableCopy.
((LinearGradientBrush)myButton.Background).GradientStops[0].Color = Colors.LightBlue;
'VB .NET
Затемнить myBrush как new System.Windows.Media.LinearGradientBrush
myBrush.StatusOfNextUse = MSAvalon.Windows.UseStatus.ChangeableCopy
myBrush.GradientStops.Add(_
new System.Windows.Media.GradientStop(System.Windows.Media.Colors.Blue,0))
myBrush.GradientStops.Add(new _
System.Windows.Media.GradientStop(System.Windows.Media.Colors.Green, 1))
myButton.Background = myBrush
' Работает, поскольку кисть имеет UseStatus, равный ChangeableCopy.
CType(myButton.Background, _
System.Windows.Media.LinearGradientBrush).GradientStops(0).Color = _
System.Windows.Media.Colors.LightBlue

При использовании объектов Changeable с StatusOfNextUse равным ChangeableCopy, программист также может удержать описатель используемой версии Changeable и использовать эту ссылку для изменения объекта. В следующем примере ссылка на используемую LinearGradientBrush извлекается и используется для изменения фона кнопки:

//C#
LinearGradientBrush myBrush = new LinearGradientBrush();
myBrush. StatusOfNextUse = UseStatus.ChangeableCopy;
myBrush.GradientStops.Add(new GradientStop(Colors.Blue, 0));
myBrush.GradientStops.Add(new GradientStop(Colors.Green, 1));
myButton.Background = myBrush;
LinearGradientBrush usedBrush = (LinearGradientBrush)myButton.Background;
// Работает, поскольку кисть имеет UseStatus, равный ChangeableCopy.
usedBrush.GradientStops[0].Color = Colors.LightBlue;
' VB .NET
Затемнить myBrush как new System. Windows.Media.LinearGradientBrush
myBrush.StatusOfNextUse = MSAvalon.Windows.UseStatus.ChangeableCopy
myBrush.GradientStops.Add( _
new
System.Windows.Media.GradientStop(System.Windows.Media.Colors.Blue,0))
myBrush.GradientStops.Add( _
new
System.Windows.Media.GradientStop(System.Windows.Media.Colors.Green,l))
myButton.Background = myBrush
Затемнить usedBrush As new System. Windows.Media.LinearGradientBrush
usedBrush = Ctype(myButton.Background, LinearGradientBrush)
' Работает, поскольку кисть имеет UseStatus, равный ChangeableCopy.
usedBrush. GradientStops(0).Color = System. Windows.Media.Colors.LightBlue

Задание ChangeableReference изменяет поведение Changeable, так что он обеспечивает ссылку на самого себя при использовании. Программист может продолжать изменять исходный объект; изменения исходного объекта влияют на используемую версию, поскольку они являются одним и тем же объектом. Ниже приведен пример:

//C#
SolidColorBrush changeableReferenceBrush = new SolidColorBrush();
changeableReferenceBrush. Color = Colors.LimeGreen;
button1.Background = changeableReferenceBrush;
button2.Background = changeableReferenceBrush;
button3.Background = changeableReferenceBrush;
//Изменяет цвет трех кнопок.
changeableReferenceBrush.Color = Colors.Purple;
// Также изменяет цвет всех трех кнопок.
((SolidColorBrush)button1.Background).Color = Colors.Blue;
' VB .NET
Затемнить changeableReferenceBrush как new System.Windows.Media.SolidColorBrush
changeableReferenceBrush. Color = System.Windows.Media.Colors.LimeGreen
button1.Background = changeableReferenceBrush
button2.Background = changeableReferenceBrush
button3.Background = changeableReferenceBrush
' Изменяет цвет всех трех кнопок.
changeableReferenceBrush.Color = System. Windows.Media.Colors.Purple
' Также изменяет цвет всех трех кнопок.
CType(buttonl.Background, System.Windows.Media.SolidColorBrush).Color = _
System.Windows.Media.Colors.Blue

КИСТЬ И ПЕРО

Кисть - это объект, который представляет метод заливки плоскости. Помимо способности заливать плоскость абсолютным способом, кисти уровня интеграции сред также способны приспосабливаться к заливке плоскости в зависимости от размера объекта, который они заливают. Примеры типов кисти включают в себя "кисть чистого цвета" (SolidColorBrush), "кисть визуала" (VisualBrush) (которая может ссылаться на визуал), "кисть рисования" (DrawingBrush) (которая может ссылаться на ресурс векторной графики), "линейного градиента" (LinearGradient), "радиального градиента" (RadialGradient), "кисть изображения" (ImageBrush) и "кисть девятиячеечной сетки" (NineGridBrush). Принятые по умолчанию значения свойств кисти указаны ниже и, в общем случае, являются значениями, которые не приводят ни к какому действию. Таким образом, цвета, по умолчанию, заданы прозрачными и т.д. Также коллекция анимаций, по умолчанию, задана равной null.

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

Базовый класс "кисть" (Brush) имеет "преобразование" (Transform), общую непрозрачность и режим смешивания:

public abstract class System.Windows.Media.Brush : Changeable
{
internal Brush();
public new Brush Copy(); // скрывает Changeable.Copy()
// По умолчанию Transform.Identity
public Transform Transform {get; set;}
[Animation("OpacityAnimations")]
public double Opacity {get; set;} // По умолчанию 1.0
public DoubleAnimationCollection OpacityAnimations {get;set;}
/// BlendMode для применения к этой Brush и ее пункту назначения при рисовании.
/// По умолчанию BlendModes.Normal
public BlendMode BlendMode {get; set;}
}

Объекты Brush (и другие объектные ресурсы в векторной графике и API MIL) являются "изменяемыми" (Changeable) и записываемыми после того, как созданы, и подчиняются общей схеме Changeable в отношении того, как они ведут себя после использования в квалифицированном использовании.

Кисти (за исключением VisualBrush и DrawingBrush) имеют простой синтаксис для использования в разметке, однако этот простой синтаксис не позволяет осуществлять доступ ко всем свойствам и поведению. Если программисту будет нужен более обширный доступ, чем обеспечивает простой синтаксис, программисту потребуется использовать сложный синтаксис. Заметим, что во избежание избыточности здесь задокументирован только простой синтаксис, поскольку сложный синтаксис подчиняется тому же шаблону, что и другие классы CLR.

Ниже приведены типы кисти с простым синтаксисом для разметки в данной реализации:

кисть:
кисть чистого цвета |
кисть линейного градиента |
кисть радиального градиента |
кисть изображения |
кисть видео |
кисть девятиячеечной сетки

Многие из типов кисти используют систему координат для задания некоторых из их параметров. Эту систему координат можно задать по отношению к простому ограничивающему прямоугольнику геометрии, с которым используется кисть, или она может быть абсолютной и интерпретироваться в координатном пространстве, которое активно во время использования кисти. Это называется соответственно режимом RelativeToBoundingBox ("относительно ограничивающего прямоугольника" и режимом Absolute ("абсолютный").

public enum System.Windows.Media.BrushMappingMode
{
Absolute,
RelativeToBoundingBox,
}

SolidColorBrush заливает плоскость чистым цветом. При наличии альфа-компонента цвета, он объединяется путем умножения с соответствующим атрибутом непрозрачности в Brush.

public sealed class System. Windows.Media. SolidColorBrush : Brush
{
// Конструкторы
public SolidColorBrush(); // инициализировать в прозрачный
public SolidColorBrash(Color color);
public new SolidColorBrush Copy(); // скрывает Changeable.Copy()
// По умолчанию прозрачный
[Animation("ColorAnimations")]
public Color Color {get; set;}
public ColorAnimationCollection ColorAnimations {get; set;}
}

Поскольку это простой тип (т.е. ни одно из его свойств не является Changeable), единственным защищенным методом, который нужно реализовать, является CloneCore(). Кроме того, поскольку нет комбинации значений, которые делают этот объект недействительным, нет необходимости обеспечивать метод ValidateObjectState(). Эти методы и другие родственные методы описаны в прилагаемом Приложении.

Простой синтаксис для разметки для SolidColorBrush:

краска чистого цвета:
цвет

Класс "кисти" (Brushes) содержит статические свойства для экземпляров SolidColorBrush, которые открыты. Каждый задается равным значению цвета одного и того же имени. Заметим, что, поскольку это стандартизованные кисти, их значения IsChangeable заданы равными "ложь" (например, реализационные вызовы MakeUnchangeable() после построения).

Ниже приведены некоторые стандартные цвета:

Рисование градиентов осуществляется путем задания ограничителей градиента. Эти ограничители градиента указывают цвета вдоль некоторого рода прогрессии. В настоящее время поддерживаются два типа градиентов, а именно линейный и радиальный градиенты. Рисование градиента осуществляется посредством интерполяций между ограничителями градиента в заданном цветовом пространстве.

Градиенты образованы списком ограничителей градиента. Каждый из этих ограничителей градиента содержит цвет (в который включено значение альфа) и смещение. Если не задано никаких ограничителей градиента, кисть рисуется как прозрачная (как если не задано никакой кисти). Если задан только один ограничитель градиента, кисть рисуется как чистый цвет с одним заданным цветом. Рассматриваются любые ограничители градиента со смещениями в диапазоне от нуля до единицы (0,0... 1,0) совместно с наибольшим ограничителем в диапазоне (-∞...0,0] и наименьшим ограничителем в диапазоне [1,0...+∞). Если набор рассматриваемых ограничителей включает в себя ограничитель, находящийся вне диапазона от нуля до единицы, выводится неявный ограничитель в нуле (и/или единице), который представляет интерполированный цвет, который имел бы место в этом ограничителе. Кроме того, если два или более ограничителя заданы с одним и тем же смещением, на этом смещении происходит жесткий (а не интерполированный) переход. Порядок добавления ограничителей определяет поведение при этом смещении; первый добавляемый ограничитель - это эффективный цвет до этого смещения, последний добавляемый ограничитель - это эффективный цвет после этого ограничителя, и любые дополнительные ограничители при этом смещении игнорируются.

Этот класс является Changeable, как и другие классы ресурсов:

public sealed class System.Windows.MediaGradientStop : Changeable
{
public GradientStop();
public GradientStop(Color color, double offset);
public GradientStop(Color color, ColorAnimationCoIlection colorAnimations,
double offset, DoubleAnimationCollection offsetAnimations);
public new GradientStop Copy(); // скрывает Changeable.Copy()
// По умолчанию прозрачный
[Animation("ColorAnimations")]
public Color Color {get; set;}
public ColorAnimationCoIlection ColorAnimations {get; set;}
//По умолчанию 0
[Animation("OffsetAnimations")]
public double Offset {get; set;}
public DoubleAnimationCollection OffsetAnimations {get; set;}
}

Аналогично SolidColorBrush, это имеет вложенные Changeable в коллекциях анимаций.

Перечисление GradientSpreadMethod задает, как нужно рисовать градиент вне заданного вектора или пространства. Имеются три возможных значения, включая Pad ("набивка"), в которой концевые цвета (первый и последний) используются для заливки оставшегося пространства, Reflect ("отражение"), в которой ограничители повторяются по порядку, пока пространство не будет залито. Значение по умолчанию для свойств этого типа равно Pad:

public enum System.Windows.Media.GradientSpreadMethod
{
Pad,
Reflect,
Repeat
}

На фиг.24 показаны некоторые примеры GradientSpreadMethod (хотя в градации серого, а не в цвете). Каждая форма имеет линейный градиент, идущий от белого к серому. Сплошная линия представляет вектор градиента.

Перечисление ColorInterpolationMode задает режим интерполяции для цветов в градиенте. Имеются две опции: PhysicallyLinearGamma ("физически линейная гамма") и PerceptuallyLinearGamma (перцептивно линейная гамма").

public enum ColorInterpolationMode
{
// Цвета интерполируются в пространство Гамма 1.0
PhysicallyLinearGamma,
// Цвета интерполируются в пространство Гамма 2.2
PerceptuallyLinearGamma
}

Это абстрактный базовый класс.

public abstract class System.Windows.Media.GradientBrush : Brush
{
internal GradientBrush();
public new GradientBrush Copy(); // скрывает Changeable.Copy()
// По умолчанию "PerceptuallyLinearGamma"
public CoIorInterpolationMode ColorInterpolationMode {get; set;}
// По умолчанию RelativeToBoundingBox
public BrushMappingMode MappingMode {get; set;}
// По умолчанию Pad
public GradientSpreadMethod SpreadMethod {get; set;}
// Ограничители градиента
public void AddStop(Color color, double offset);
public GradientStopCollection GradientStops {get; set;}
// Режим интерполяции цветов
public ColorInterpolationMode ColorInterpolationMode {get; set;}
}

"Линейный градиент" (LinearGradient) задает кисть линейного градиента вдоль вектора. Отдельные ограничители задают ограничители цветов вдоль этого вектора.

public sealed class System.Windows.MediaXinearGradient: GradientBrush
{
public LinearGradient(); // инициализирует в прозрачный
// Устанавливает градиент с двумя цветами и вектором градиента
// указывает заливать объект, к которому применяется градиент.
// Это предусматривает RelativeToBoundingBox для свойства GradientUnits
public LinearGradient(Color color1, Color color2, double angle);
public LinearGradient(Color color1, Color color2,
Point vectorStart, Point vectorEnd);
public new LinearGradient Copy(); // hides Changeable.Copy()
// Начальная точка вектора градиента
// По умолчанию 0,0
[Animation("StartPointAnimations")]
public Point StartPoint {get; set;}
public PointAnimationCollection StartPointAnimations {get; set;}
//По умолчанию 1,1
[Animation("EndPointAnimations")]
public Point EndPoint {get; set;}
public PointAnimationCollection EndPointAnimations {get; set;}
}

Простой синтаксис разметки для "кисти линейного градиента" (LinearGradientBrush):

Кисть линейного градиента:

"HorizontalGradient" запятая-пп (пробел) цвет запятая-пп цвет |

"VerticalGradient" запятая-пп цвет запятая-пп цвет |

"LinearGradient" запятая-пп пара координат запятая-пп цвет запятая-пп цвет

Разметка для "линейного градиента" (LinearGradient) позволяет задавать "линейный градиент" с двумя ограничителями цветов, при смещениях нуль и единица. Если используется версия "LinearGradient", указываются начальная точка и конечная точка соответственно. Если используется "HorizontalGradient", начальная точка задается равной 0,0, и конечная точка задается равной 1,0. Если используется "VerticalGradient", начальная точка задается равной 0,0, и конечная точка задается равной 0,1. В этих случаях, по умолчанию используется "режим отображения" (MappingMode), который является RelativeToBoundingBox.

Радиальный градиент подобен по модели программирования линейному градиенту. Однако, тогда как линейный градиент имеет начальную и конечную точки для задания вектора градиента, радиальный градиент имеет окружность совместно с фокальной точкой для задания поведения градиента. Окружность задает конечную точку градиента, иными словами, ограничитель градиента на 1,0 задает цвет на окружности. Фокальная точка задает центр градиента. Ограничитель градиента на 0,0 задает цвет в фокальной точке. На фиг.25 представлен "радиальный градиент" (RadialGradient), который (в градации серого) переходит от белого к серому. Внешняя окружность представляет окружность градиента, а жирная точка обозначает фокальную точку. Этот градиент имеет SpreadMethod, заданный равным Pad.

public sealed class System. Windows.Media.RadialGradient: GradientBrush
{
public RadialGradient(); // инициализировать в прозрачный
// Задает градиент с двумя цветами.
// Это предполагает RelativeToBoundingBox для свойства GradientUnits
// с центром в (0.5,0.5)
// радиусом 0.5 и фокальной точкой в (0.5,0.5)
public RadialGradient(Color colorl, Color color2);
public new RadialGradient Copy(); // hides Changeable.Copy()
// По умолчанию 0.5,0.5
[Animation("CenterAnimations")]
public Point Center {get; set;}
public PointAnimationCollection CenterAnimations {get; set;}
// По умолчанию 0.5
[Animation("RadiusXAnimations")]
public double RadiusX {get; set;}
public DoubleAnimationCollection RadiusXAnimations {get; set;}
// По умолчанию 0.5
[Animation("RadiusYAnimations")]
public double RadiusY {get; set;}
public DoubleAnimationCollection RadiusYAnimations {get; set;}
// По умолчанию 0.5,0.5
[Animation("FocusAnimations")]
public Point Focus {get; set;}
public PointAnimationCollection FocusAnimations {get; set;}
}

Разметка для "радиального градиента" (RadialGradient) позволяет задавать "радиальный градиент" с двумя ограничителями цвета при смещениях 0 и 1 соответственно. По умолчанию, используется MappingMode, который является RelativeToBoundingBox, поскольку являются радиусами, заданными по умолчанию, 0,5:

Кисть-радиального-градиента:
"RadialGradient" запятая-пп цвет запятая-пп цвет

TileBrush "мозаичная кисть" - это абстрактный базовый класс, который содержит логические средства для описания мозаики и средство, с помощью которого эта мозаика должна заливать область. Подклассы "TileBrush" содержат содержимое и логически задают способ заливки бесконечной плоскости.

Перечисление Stretch ("растяжение") используется для описания того, как ViewBox ("прямоугольник просмотра") (начальное координатное пространство) отображается в ViewPort ("порт просмотра") (конечное координатное пространство). Это используется в TileBrush:

public enum System.Windows.Stretch
{
// Сохраняет исходный размер
None,
// Форматное соотношение не сохраняется, ViewBox заливает ViewPort
Fill,
// Форматное соотношение сохраняется, VewBox однородно масштабируется
// как можно больше, чтобы ширина и высота укладывалась в ViewPort
Uniform,
// Форматное соотношение сохраняется, VewBox однородно масштабируется
// как можно меньше, чтобы весь ViewPort заливался ViewBox
UniformToFill
}

На фиг.26 представлены примеры растяжения. В этих примерах содержимое выравнивается по верхнему/левому краю.

Перечисление TileMode ("режим мозаики") используется для описания того, заливается ли и как заливается пространство "элементами мозаичного изображения" (Tile). TileBrush задает, где находится базовый Tile (определяется посредством ViewPort). Остальное пространство заливается на основании значения TileMode.

public enum System.Windows.Media.TileMode
{
// Не заливает мозаичной плиткой - рисуется только базовый элемент мозаичного изображения,
// остальная область остается прозрачной
None,
// Режим базовой мозаики - рисуется базовый элемент мозаичного изображения, и остальная область
// заливается повторением базового элемента мозаичного изображения, чтобы правый край одного элемента
// мозаичного изображения примыкал к левому краю следующего элемента,
// и аналогично для нижнего и верхнего
Tile,
// То же, что элемент мозаичного изображения, но столбцы элементов мозаичного изображения через один отражаются
// горизонтально. Базовый элемент мозаичного изображения рисуется без преобразования.
FlipX,
// То же, что элемент мозаичного изображения, но строки элементов мозаичного изображения через один отражаются вертикально
// Базовый элемент мозаичного изображения рисуется без преобразования.
FlipY,
// Комбинация FlipX и FlipY. Базовый элемент мозаичного изображения рисуется без преобразования
FlipXY
}

На фиг.27 представлены примеры TileMode. В каждом примере элемент мозаичного изображения, расположенный в верхнем левом углу, является базовым элементом мозаичного изображения. Эти примеры представляют None, Tile, FlipX, FlipY и FlipXY.

Перечисление VerticalAlignment используется для описания того, как размещается содержимое в контейнере по вертикали:

public enum System. Windows.VerticalAlignment
{
// Выравнивает содержимое по отношению к верхнему краю пространства
Top,
// Центрирует содержимое по вертикали
Center,
// Выравнивает содержимое по отношению к нижнему краю пространства
Bottom,
}

Перечисление HorizontalAlignment используется для описания того, как размещается содержимое в контейнере по горизонтали.

public enum System.Windows.HorizontalAlignment
{
// Выравнивает содержимое по отношению к левому краю пространства
Left,
// Центрирует содержимое по горизонтали
Center,
// Выравнивает содержимое по отношению к правому краю пространства
Right,
}

Свойства TileBrush выделяют прямоугольный участок бесконечной плоскости, подлежащий мозаичной заливке (ViewBox), и описывают конечный прямоугольник (ViewPort), который будет базовым Tile в заливаемой области. Оставшаяся конечная область будет залита на основании свойства TileMode, которое управляет тем, дублируется ли исходный элемент мозаичного изображения и как он дублируется для заливки остального пространства:

public abstract class System. Windows.Media.TileBrush : Brush
{
public new TileBrush Copy(); // hides Brush.Copy()
// По умолчанию RelativeToBoundingBox
public BrushMappingMode ViewPortUnits {get; set;}
// По умолчанию RelativeToBoundingBox
public BrushMappingMode ContentUnits {get; set;}
// По умолчанию RectEmpty
[Ammation("ViewBoxAnimations")]
public Rect ViewBox {get; set;}
public RectAnimationCollection ViewBoxAnimations {get; set;}
// По умолчанию Fill
public Stretch Stretch {get; set;}
// По умолчанию None
public TileMode TileMode {get; set;}
// По умолчанию Center
public HorizontalAlignment HorizontalAlignment {get; set;}
// По умолчанию Center
public VerticalAlignment VerticalAlignment {get; set;}
// По умолчанию 0,0,1,1
[Animation("ViewPortAnimations")]
public Rect ViewPort {get; set;}
public RectAnimationCollection ViewPortAnimations {get; set;}
}

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

ViewPort задает положение, где содержимое, в конце концов, будет нарисовано, создавая базовый элемент мозаичного изображения для этой кисти. Если значение ViewPortUnits равно Absolute, значение ViewPort рассматривается в локальном пространстве во время применения. Если же значение ViewPortUnits равно RelativeToBoundingBox, то значение ViewPort рассматривается в координатном пространстве, где 0,0 это левый верхний угол ограничивающего прямоугольника раскрашиваемого объекта, а 1,1 это правый нижний угол того же прямоугольника. Например, рассмотрим заливаемую "геометрию прямоугольника" (RectangleGeometry), которая рисуется от 100,100 до 200,200. Тогда, если ViewPortUnits равно Absolute, ViewPort для (100,100,100,100) будет описывать всю область контекста. Если размер (Size) ViewPort пуст и Stretch не равно None, эта Brush ничего не визуализирует.

ViewBox задан в пространстве содержимого. Этот прямоугольник преобразуется, чтобы поместиться внутри ViewPort, как определено свойствами "выравнивание" (Alignment) и свойством "растяжение" (Stretch). Если Stretch равно None, то к содержимому не применяется никакого масштабирования. Если Stretch равно Fill, то ViewBox масштабируется независимо по X и Y, чтобы иметь такой же размер, как ViewPort. Если Stretch равно Uniform или UniformToFill, логика аналогична, но размеры по X и Y масштабируются однородно, сохраняя форматное соотношение содержимого. Если Stretch равно Uniform, ViewBox масштабируется, чтобы иметь более ограниченный размер, равный размеру ViewPort. Если Stretch равно UniformToFill, то ViewBox масштабируется, чтобы иметь менее ограниченный размер, равный размеру ViewPort. Другой способ рассмотрения этого состоит в том, что Uniform и UniformToFill сохраняют форматное соотношение, но Uniform гарантирует, что весь ViewBox находится внутри ViewPort (потенциально оставляя участки ViewPort непокрытыми ViewBox), а UniformToFill гарантирует, что весь ViewPort залит ViewBox (потенциально заставляя участки ViewBox находиться вне ViewPort). Если область ViewBox пуста, то никакое растяжение не применяется. Выравнивание все же имеет место и размещает "точечный" ViewBox.

Когда ViewPort ("порт просмотра") определен (на основании ViewPortUnits) и размер конечного ViewBox ("прямоугольника просмотра") определен (на основании Stretch), ViewBox нужно разместить в ViewPort. Если ViewBox имеет такой же размер, как ViewPort (если Stretch равно Fill, или если это справедливо только для одного из трех значений Stretch), то ViewBox размещается в "начале координат" (Origin) так, чтобы быть идентичным ViewPort. В противном случае рассматриваются "горизонтальное выравнивание" (HorizontalAlignment) и "вертикальное выравнивание" (VerticalAlignment). Если HorizontalAlignment равно Left, то левый край ViewBox будет размещен на левом краю ViewPort. Если это Center, то центр ViewBox будет размещен в центре ViewPort, а если Right, то будет совмещение с правым краем. Процесс повторяется для направления Y.

Если ViewBox равен Empty, он считается незаданным. Если он не задан, то рассматриваются ContentUnits. Если ContentUnits равны Absolute, не происходит никакого масштабирования или смещения, и содержимое рисуется в ViewPort без всякого преобразования. Если ContentUnits равны RelativeToBoundingBox, то начало отсчета содержимого выравнивается с началом отсчета ViewPort и содержимое масштабируется по ширине и высоте ограничивающего прямоугольника объекта.

При заливке пространства TileBrush содержимое отображается в ViewPort, как указано выше, и усекается к ViewPort. Это образует базовый элемент мозаичного изображения для заливки, и остальное пространство заливается на основании TileMode, соответствующего Brush. Если он задан, применяется преобразование Brush, что происходит после другого отображения, масштабирования, смещения и т.д.

"Кисть визуала" (VisualBrush) - это "мозаичная кисть" (TileBrush), содержимое которой задано визуалом. Эту кисть можно использовать для создания сложных схем или ее можно использовать для рисования дополнительных копий содержимого других частей сцены.

public sealed class System.Windows.Media.VisualBrush : TileBrush
{
public VisualBrush(); // инициализирует в прозрачное
public VisualBrush(Visual v);
public new VisualBrush Copy(); // скрывает TileBrush.Copy()
// Визуал - по умолчанию null (прозрачная кисть)
public Visual Visual {get; set;}
}

Как было отмечено, VisualBrush не имеет простого синтаксиса разметки, хотя ее можно описывать посредством сложного синтаксиса.

"Кисть рисования" (DrawingBrush) является "мозаичной кистью" (TileBrush), содержимое которой задано "рисованием" (Drawing). Эту кисть можно использовать для создания сложных схем, которые были созданы посредством "контекста рисования" (DrawingContext).

public sealed class System.Windows.MediaDrawingBrush : TileBrush
{
public DrawingBrush(); // инициализирует в прозрачный
public DrawingBrush(Drawing drawing);
public new DrawingBrush Copy(); // скрывает TileBrush.Copy()
// Визуал - по умолчанию null (прозрачная кисть)
public Drawing Drawing {get; set;}
}

Как было отмечено, DrawingBrush не имеет простого синтаксиса разметки, хотя ее можно описать посредством сложного синтаксиса.

"Кисть изображения" (ImageBrush) является "мозаичной кистью" (TileBrush), содержимое которой задается посредством "источника изображения" (ImageSource). Эту кисть можно использовать для заливки пространства изображением.

public sealed class System. Windows.MediaJmageBrash: TileBrush
{
public ImageBrush(); // Инициализирует в прозрачное содержимое
// Задает изображение, задает ViewBox равным (0,0,Width,Height)
// и Stretch равным Fill
public ImageBrush(ImageData image);
public new ImageBrush Copy(); // скрывает TileBrush.Copy()
// По умолчанию null
public ImageSource ImageSource {get; set;}
// По умолчанию "истина"
// Если "истина", то свойство ViewBox будет подменено
// и эффективно установлено на собственный размер изображения
public bool SizeViewBoxToContent {get; set;}
}

Простой синтаксис разметки для ImageBrush:

кисть изображения:

"изображение" uri-изображения

"Кисть видео" (VideoBrush) является TileBrush, содержимое которой задано "видеоданными" (VideoData). Эту кисть можно использовать для заливки пространства посредством "видео" (Video).

public sealed class System. Windows.Media.VideoBrush : TileBrush
{
public VideoBrush(); // Инициализирует в прозрачное содержимое
// Задает изображение, задает ViewBox равным (0,0, Width,Height) и
// Stretch равным Fill
public VideoBrush(VideoData video);
public new VideoBrush Copy(); // скрывает TileBrash.Copy()
// По умолчанию null
public VideoData VideoData {get; set;}
// По умолчанию "истина"
// Если "истина", то свойство ViewBox будет подменено
// и эффективно установлено на собственный размер видео
public bool SizeViewBoxToContent {get; set;}
}

Простой синтаксис разметки для VideoBrush:

Кисть видео:

"видео" uri-видео

"Кисть девятиячеечной сетки" (NineGridBrush) - это кисть, которая всегда заливает ограничивающий прямоугольник объекта изображением своего содержимого, и растяжение изображения не выполняется посредством исключительно визуального масштабирования. Источник изображения делится на девять прямоугольников четырьмя границами (отсюда название "девятиячеечная сетка"). Содержимое изображения в каждой из этих девяти областей масштабируется в 0, 1 или 2 измерениях, пока они не заполнят ограничивающий прямоугольник объекта. Измерения, в которых масштабируется каждая секция, можно видеть на этой схеме: фиг.28 представляет концепцию "девятиячеечной сетки", показывая девять ячеек, заданных верхней, левой, нижней и правой границами. Стрелки в каждом квадрате сетки показывают измерение(я), в которых их содержимое будет растянуто, чтобы соответствовать размеру ViewPort.

Помимо областей девятиячеечной сетки, изображенных выше, имеется необязательная "десятая" ячейка. Она принимает форму дополнительного изображения, которое центрировано в ViewPort и не масштабировано. Ее можно использовать вместо формы в центре кнопки и т.д. Эта "десятая ячейка" называется глифом и предоставляется свойством GlyphImageSource:

public sealed class System. Windows.Media.NineGridBrush : Brush
{
public NineGridBrush(ImageSource ImageSource,
int LeftBorder,
int RightBorder,
int TopBorder,
int BottomBorder);
public NineGridBrush(ImageSource ImageSource,
int LeftBorder,
int RightBorder,
int TopBorder,
int BottomBorder,
ImageSource glyphImage);
public new NineGridBrush Copy(); // скрывает Brush.Copy()
// По умолчанию null
public ImageSource ImageSource {get; set;}
// По умолчанию 0
public int LeftBorder {get; set;}
// По умолчанию 0
public int RightBorder {get; set;}
// По умолчанию 0
public int TopBorder {get; set;}
// По умолчанию 0
public int BottomBorder {get; set;}
// По умолчанию null
public ImageSource GlyphImageSource{get; set;}
}

Заметим, что члены границы отсчитываются от края изображения в пикселях изображения.

Простой синтаксис разметки для NineGridBrush:

Кисть-девятиячеечной-сетки:

"девятиячеечная сетка" uri-изображения целое целое целое целое [uri-глифового-изображения]

Четыре целых числа - это значения LeftBorder (левой границы), RightBorder (правой границы), TopBorder (верхней границы) и BottomBorder (нижней границы) соответственно. Конечный URI для десятой ячейки или глифа является необязательным.

"Перо" (Pen) - это объект, который берет "кисть" (Brush) и другие параметры, которые описывают, как оконтуривать пространство/геометрию. В принципе, "перо" описывает, как создавать область контура из "геометрии" (Geometry). Создается новая область, которая базируется на краях Geometry, толщины (Thickness) пера, PenLineJoin, PenLineCap и т.д. После создания области она заливается Brush.

public sealed class System.Windows.Media.Pen : Changeable
{
// Конструкторы
Public Pen();
public Pen(Brush brush, double thickness);
public new Pen Copy(); //скрывает Changeable.Copy()
// Свойства
// По умолчанию DashArrays.Solid (нет штрихов)
public DoubleCollection DashArray {get; set;}
// По умолчанию 0
[Animations(DashOffsetAnimations)]
public double DashOffset {get; set;}
public DoubleAnimationCollection DashOffsetAnimations {get; set;}
// По умолчанию Flat
public PenLineCap StartLineCap {get; set;}
// По умолчанию Flat
public PenLineCap EndLineCap {get; set;}
// По умолчанию Flat
public PenDashCap DashCap {get; set;}
// По умолчанию Miter
public PenLineJoin LineJoin {get; set;}
// По умолчанию 10
public double MiterLimit {get; set;}
// По умолчанию null
public Brush Brush {get; set;}
// По умолчанию 1.0
[Animations(ThicknessAnimations)]
public double Thickness {get; set;}
public DoubleAnimationCollection Thickness Animations {get; set;}
}

PenLineCap определяет, как рисовать концы штриховой линии:

public enum System. Windows.Media.PenLineCap
{
// Эффективно, никакого конца линии - линия застыла
// в последней точке линии
Flat,
// Линия оканчивается полукругом с диаметром,
// равным ширине линии
Round,
// Штрих оканчивается треугольником
Triangle,
// Линия оканчивается квадратом со стороной
// равной ширине линии и с центром в конечной точке
Square
}

PenDashCap определяет, как рисовать концы каждого штриха в пунктирной штриховой линии:

public enum System.Windows.Media.PenDashCap
{
// Эффективно, никакого конца штриха - линия застыла
// в последней точке линии
Flat,
// Штрих оканчивается полукругом с диаметром,
// равным ширине линии
Round,
// Штрих оканчивается треугольником
Triangle
}

PenLineJoin определяет, как рисовать соединения при вычерчивании линии:

public enum System.Windows.Media.PenLineJoin
{
// На пересечении внешних краев пересекающихся отрезков линий создается
// острый угол
Miter,
// Аналогично Miter, но угол закруглен
Round,
// Скошенное соединение, которое создает диагональный угол
Bevel
}

Класс DashArrays содержит статические свойства, которые обеспечивают доступ к общим, широко известным стилям штриха:

public sealed System.Windows.MediaDashArrays
{
// Сплошной массив штрихов (нет штрихов)
public static DoubleCollection Solid {get;}
//Штрих - 3 вкл., 1 выкл.
public static DoubleCollection Dash {get;}
//Штрих - 1 вкл., 1 выкл.
public static DoubleCollection Dot {get;}
// Штрих-пунктир - 3 вкл., 1 выкл., 1 вкл., 1 выкл.
public static DoubleCollection DashDot {get;}
// Штрих-пунктир-пунктир - 3 вкл., 1 выкл, 1 вкл., 1 выкл., 1 вкл., 1 выкл.
public static DoubleCollection DashDotDot {get;}
}

ЦВЕТ

Архитектура цвета построена на некоторых общих принципах, включая то, что "цвет" (Color) требует контекст; поэтому значения цвета имеют явно присвоенный контекст или неявно предполагаемый по умолчанию для минимизации несовпадений цветов в последовательностях операций. Кроме того, конструкция основной платформы требует минимальных кодовых путей и API с продолжительными сроками службы для безопасности, надежности, удобства сопровождения, дальнейшего расширения и производительности; поэтому ядро визуализации может ограничиваться, в основном, кодовым путем scRGB, в который будут преобразовываться входящие потоки содержимого и из которого будут преобразовываться исходящие потоки (также допустим дополнительный традиционный путь sRGB, который будет иметь более низкое качество). Заметим, что "scRGB" относится к внутреннему представлению по умолчанию векторной графики, основанному на международном стандарте IEC 61966-2-2 (хотя не было предоставлено никакого официального определения, что означает "sc", здесь будем использовать "стандартную композицию", чтобы помочь прояснить, что это оптимальное пространство для обработки композиции).

Производительность требует, чтобы сложная обработка осуществлялась как можно ближе к стадии определения/задания цветового объекта, а не на стадии визуализации в режиме реального времени; это требует, чтобы цветовые параметры для API преобразовывались в scRGB (практически сразу) после задания и чтобы цветовые значения scRGB поддерживались и синхронизировались для объектов, заданных не в scRGB. Простота использования требует многоуровневого API, в котором наиболее обычные случаи разработчика предоставляются в первую очередь, а наиболее сложный случай имеет ясные, но минимальные API; поэтому обеспечиваются API sRGB (но немедленно внутренне преобразуются в scRGB), обеспечиваются API scRGB и обеспечивается минимальный API, связанный с контекстом для поддержки усовершенствованной CMYK (голубой-сиреневый-желтый-черный), монохромной, человеческой визуальной системы и многоканальных решений. Поскольку scRGB является, по существу, "бесконечной" цветовой гаммой, необходимы дополнительное описание устройства и решения отображения гаммы для "сцепления" последовательностей операций scRGB с устройствами реального мира.

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

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

В общем случае, архитектура пытается минимизировать внутренние кодовые пути, что в некоторой степени осуществляется путем внутреннего обеспечения двух основных кодовых путей, одного - для качества и будущего оборудования и другого - для ограничений памяти/производительности и существующих решений. Машина внутренней визуализации и композиции ядра MIL поддерживает sRGB с разрешением 32 бит/пиксель и scRGB с плавающей точкой с разрешением 128 бит/пиксель (хотя рассматривается также scRGB 64 бит/пиксель, и некоторые варианты поддержки 64 бит/пиксель наилучшим образом реализуются посредством неподвижной точки, некоторые - посредством плавающей точки и некоторые - посредством целого).

Архитектура обеспечивает путь scRGB 128 бит/пиксель от захвата к отображению, к редактированию, к сохранению, к печати (отображение будет задним буфером 128 бит/пиксель и передним буфером 10 бит/канал или более) и допускает традиционный путь sRGB 32 бит/пиксель, который жертвует качеством ради производительности, памяти и/или пропускной способности.

Управление цветами, отвечающее настоящему изобретению, устраняет предыдущие недостатки за счет предоставления устройствам и приложениям дополнительной гибкости и обеспечивает решение управления цветами на основе профиля. Наиболее общие сценарии базируются на цветовых объектах scRGB и sRGB для поддержки получения и задания цветовых значений для общих элементов ПИ и по максимуму поддерживают создание содержимого для Web-, мультимедийной и компьютерной графики. Менее общие сценарии включают в себя использование цветовых контекстов RGB с конкретными профилями рабочего пространства для последовательностей операций профессиональной фотографии, использование цветовых значений CMYK для редактирования цветового объекта подготовки к печати и конструирования графики и последовательности операций для одного цвета и многоканального цвета, которые поддерживают узкоспециализированную печать и сценарии печати, а также обеспечивают гибкость для поддержки дальнейших последовательностей операций, которые не были заданы. Последовательности операций HVSV (пространства на основе человеческой зрительной системы) поддерживают некоторые узкопрофессиональные сценарии редактирования фотографии.

Чтобы соответствовать технологии захвата датчика, которая продолжает развиваться в направлении повышения качества и битовой глубины, построение изображения будет поддерживать, по меньшей мере, форматы 64 бит/пиксель для каждой особенности/API, чтобы поддерживать современные спецификации DigitalNegativeInitiative. Поскольку настоящее изобретение реализует новую архитектуру для векторной графики, векторная графика будет реализована с битовой точностью, соответствующей 32-битовому числу с плавающей точкой (float) на канал. Эта реализация в действительности "скрывается" с целью обеспечения традиционного цветового доступа 8 бит/канал, а также градации серого и интерфейсов HSV.

Другой тип цветовых данных - это именованные цветовые данные, как то цвета "CornflowerBlue" (васильковый) или "Pantone". Благодаря обеспечению цветового контекста, который базируется на расширении традиционных профилей управления цветами, обеспечивается очень общий и мощный интерфейс именования цветов. Для поддержки некоторой традиционной совместимости с предыдущими API и обычной практикой конструкторы по умолчанию будут смещаться ко входу sRGB.

Поддержка цветового пространства для векторной графики делится на собственную поддержку scRGB, поддержку свойств для scRGB и аналогичные пространства, которые не требуют никакого явного цветового контекста, поддержку метода для цветовых пространств, тесно связанных с sRGB или scRGB, например HSV (оттенок, насыщенность, значение), которые также не требуют никакого явно связанного цветового контекста, именованные цвета и родственные цветовые пространства, например палитру и индексированные цветовые пространства, которые базируются на неявно либо на явно связанном цветовом контексте, и цветовые пространства, которые требуют дополнительных цветовых каналов, а также явно связанных цветовых контекстов, например CMYK, цвета hi-fi (CMYK плюс оранжевый и зеленый), цветовых пространств CcMmYK для струйной печати и потенциально поддержки спектральных цветов в будущем.

Хотя эти цветовые пространства преобразуются в scRGB или sRGB для визуализации в ядре MIL или средстве композиции, они могут сохраняться или отправляться на принтеры (например, CMYK) с использованием языка разметки векторной графики в качестве языка конструирования программ. Синтаксис разметки цветов включает в себя четыре основных механизма задания: шестнадцатеричный, известные цвета, свойства sRGB и расширенный цветовой контекст. Первые три предусматривают цветовой контекст цветового пространства sRGB.

Нижеприведенный пример создает градиент с использованием этих четырех механизмов:

Пример:
<Canvas Width="100px" Height="100px"
Background="VerticalGradient #ee7711 CornFlowerBlue"
xmlns="using:System.Windows;System.Windows.Controls;System.Windows.Documents;
System.Windows.Shapes;System.Windows.Media;System.Windows.Presenters"
>
<Rectangle Top="12.5%" Left="0" Width="100%" Height="12.5%">
<Rectangle.Fill>
<RadialGradient>
<GradientStop Offset="0" Color="sGray.icc 0.5"/>
<GradientStop Offset="0.5">
<GradientStop.Color>
<Color A="0.8" R="0.2" G="1" B="0.2"/>
</GradientStop.Color>
</GradientStop>
<GradientStop Offset="l"Color="mswopintent8.icm 0.9 0.2 0.1 0.3"/>
</RadialGradient>
</Rectangle.Fill>
</Rectangle>
</Canvas>

Первый цвет фона задается как шестнадцатеричное число (#ee7711). Это шестнадцатеричное представление идентично заданию цвета в инфраструктуре .NET V1 и WinForms. Оно гибко, чтобы допускать четыре разных вариации; #RGB, #ARGB, #RRGBBB или #AARRGGBB.

Второй цвет фона задан как известный цвет (CornFlowerBlue). Это представление идентично заданию цвета в инфрастуктуре .NET V1 и WinForms. Оно базируется на именованных цветовых значениях. Решения именованных цветов позволяет поддерживать Pantone, Trumatch и другие именованные цвета с цветовыми контекстами. Это также будет поддерживать настройки альфа-канала.

Первый ограничитель градиента задают с использованием цветового контекста ("sGray.icc 0.5"). Текстовая строка задает имя файла цветового контекста, который может быть URL или URI. Этот образец призван иллюстрировать чистую поддержку монохромной печати без требования значений RGB, которые сначала нужно перевести в монохромные значения во время визуализации печати.

Второй ограничитель градиента задают с использованием свойств sRGB (A="0.8" R="0.2" G="1" B="0.2"). Это представление идентично заданию цвета в инфраструктуре .NET V1 и WinForms.

Третий ограничитель градиента задают с использованием цветового контекста(="mswopintent8.icm 0.9 0.2 0.1 0.3"). Текстовая строка задает имя файла цветового контекста, который может быть URL или URI, и может поддерживать настройки альфа-канала. Этот образец демонстрирует поддержку CMYK, например, необходимую для Publisher или других подобных приложений.

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

Согласно вышеописанному цветовой контекст требуется для цветовых объектов как на векторной, так и на растровой основе. На грубом уровне цветовой контекст можно рассматривать как профиль, обеспечивающий соотношение между цветовым пространством цветового объекта и человеческой зрительной системой. Цветовой контекст обеспечивает информацию по соотношению между цветовым пространством пользователя и цветовым пространством scRGB (или цветом человеческой зрительной системы). Это позволяет осуществлять "полный обход" CMYK и другой цветовой информации, что раньше было невозможно делать эффективно.

В современной практике существуют буквально сотни различных цветовых пространств, как то sRGB, scRGB, AdobeRGB, BruceRGB, AppleRGB, TomRGB, CorbisRGB, JoeRGB, HSV, HSB, XYZ, LAB, LUV, YUV, YCC, CMYK, CcMmYK, CMYKOG, серая шкала светлоты, серая шкала яркости и многие, многие другие. Многие из этих отдельных цветовых пространств можно разбить по классам цветовых пространств, например пространств RGB, которые, в основном, заданы тремя каналами, содержащими приближения к зрительному восприятию красного, зеленого и синего с задающими семантиками, включая гамму, основные цвета, белую точку и битовую точность. Битовая точность необходима, поскольку более низкие битовые точности (например, 8 бит на канал) обычно требуют обширных, нелинейных компенсаций для достижения приемлемой эффективности кодирования.

Обеспечение класса цветового контекста уменьшает набор возможных классов цветового пространства до гораздо меньшего набора, например градация серого, RGB, HSV, CMYK, LAB, LCH, CcMmYK, CMYKOG, спектральные цвета и пространства спецэффектов, как то двухтоновые, трехтоновые и четырехтоновые пространства, используемые для художественных эффектов. Дальнейшее сокращение возможно путем объединения пространств, которые совместно используют один и тот же лежащий в основе смысл, но обеспечивают разные системы координат (аналогично прямоугольной и полярной геометриям). Это делает HSV методом поверх класса цветового пространства RGB и LCH методом поверх класса цветового пространства LAB.

Возможно также объединить цветовые пространства спецэффектов со спектральным цветом и включать поддержку CcMmYK и CMYKOG и иметь цветовое значение только в этом контексте, являющееся динамическим массивом, поскольку только опытные пользователи будут использовать эту особенность. Дальнейшее сокращение возможно для сужения цветовых пространств до scRGB, которое будет поддерживать sRGB и другие пространства RGB, и многоканального цветового пространства с цветовым контекстом. Это оставляет разумное количество базовых классов цветового пространства для поддержки, включая только scRGB и многоканальные пространства.

ColorContext ("цветовой контекст") связан либо с классом "цвет" (Color) векторной графики, либо с объектом "данные изображения" (ImageData). Другая альтернатива состоит в ограничении визуалов единственным ColorContext. Это поможет оптимизировать объем цветовых преобразований и подтверждений во многих обстоятельствах и может быть более естественным для разработчиков приложений, например, индивидуальное управление, скорее всего, не использует цвета из множественных цветовых систем. Заметим, что цветовому профилю все же позволено изменяться для усовершенствованных приложений, которые напрямую используют множественные типы цветов, через механизм, который допускает динамические изменения. ColorContext также позволяет отображать намерение визуализации или цветовую гамму между двумя гаммами устройства, подлежащими заданию и, таким образом, связывать его(ее) с цветовым объектом. Поскольку ColorContext имеет дело с единственным цветовым объектом, конечная гамма может быть виртуальным устройством. Это позволяет ColorContext содержать как объективное описание цветового пространства, так и субъективное намерение визуализации для цвета.

Имена цветов являются простыми поисковыми таблицами, внедренными в профиль, который связан с цветовым контекстом, который обеспечивает связь между цветовым значением, основанным на типе ColorContext, и фактическим именем цвета. Это также допускает различные словари именования цветов для каждого цветового объекта. Например, можно связать один тип системы именования, например Trumatch, с объектами основных цветов и другой тип системы именования, например Pantone, с объектами заказных цветов.

Открытый тип Color векторной графики соответствует системе более низкого уровня для оптимизации рабочих характеристик путем минимизации преобразований при передаче данных этой системе более низкого уровня. Отдельный "собственный" (т.е. CMYK или подобный) набор значений с плавающей точкой сохраняется в цветовом контексте и синхронизируется, когда происходят какие-либо изменения. Собственное "цветовое значение" (ColorValue) "цветового контекста" (ColorContext) является типом значения (структурой), основанной на массиве чисел с плавающей точкой для прозрачной поддержки цветовых пространств градации серого, RGB и даже CMYK. Массив ColorValue "цветового контекста" должен быть динамическим и не ограничиваться 1, 3, 4 или даже 8 цветовыми каналами. Это допускает решения обработки спектральных или сокращенных спектральных цветов именно с этой архитектурой. Заметим, что хотя затраты на выделение довольно значительны по сравнению с затратами на пятиэлементный массив, который иногда используется только частично, это гарантирует согласованное, понятное и гибкое решение для будущего, и нет затрат, когда используются последовательности операций scRGB. Значение "альфа" (Alpha) отдельно от ColorValue "цветового контекста", поскольку оно является другой концепцией и в большинстве случаев обрабатывается иначе.

Имя Тип Описание Другая информация
InternalColor Float Red, Float Green, Float Blue, Float Alpha Структура, идентичная внутренней цветовой структуре визуализации для оптимизации рабочих характеристик Внутренняя структура, эта вторая внутренняя структура используется для поддержки эффективной маршрутизации данных
context ColorContext Цветовой контекст, обеспечивающий методы, относящиеся к информации цветового контекста Несет собственные цветовые значения, которые синхронизированы со значениями InternalColor

Имя Аргументы Описание Другая информация
FromProfile(...) String (строка) ICC или другой конструктор на основе имени файла профиля Public static (открытый статический
FromProfileAndRenderingIntent(...) String, String ICC или другой конструктор на основе имени файла профиля Public static
FromAValues(...) Float, float[], filename Общий конструктор на основе значения альфа-канала, массива значений с плавающей точкой и ICC или другого имени файла профиля Public static
FromValues(...) Float[], filename То же, что FromAValues(...), но альфа предполагается равной 1.0f Public static
FromARGB(...) byte, byte, byte, byte Традиционный конструктор sRGB на основе значений sRGB alpha, red, green и blue Public static, значения sRGB внутренне преобразуются в scRGB для обработки
FromRGB(...) byte, byte, byte Традиционный конструктор sRGB на основе значений sRGB red, green и blue (alpha предполагается равной 1.0f) Public static, значения sRGB внутренне преобразуются в scRGB для обработки
FromScRGB(...) Float, float, float, float Конструктор scRGB на основе значений scRGB alpha, red, green и blue Public static

Заметим, что получение цветового значения системы цветовых объектов ПИ может зависеть от контекста, как и другие, более развитые, типы тем, и должны быть собраны с другими метриками системы с помощью модели приложения/API тем.

Имя Возвращаемый тип Описание Другая информация
R byte Значение red sRGB красного компонента scRGB для текущего цвета Ч/З (чтение/запись)
G byte Значение green sRGB красного компонента scRGB для текущего цвета Ч/З
В byte Значение blue sRGB красного компонента scRGB для текущего цвета Ч/З
A byte Значение alpha sRGB красного компонента scRGB для текущего цвета Ч/З
ScR float Значение red scRGB красного компонента scRGB для текущего цвета Ч/З
ScG float Значение green scRGB красного компонента scRGB для текущего цвета Ч/З
ScB float Значение blue scRGB красного компонента scRGB для текущего цвета Ч/З
ScA float Значение alpha scRGB красного компонента scRGB для текущего цвета Ч/З

Заметим, что значения scRGB могут варьироваться ниже 0,0 и выше 1,0 для поддержки расширенных динамических диапазонов и гамм.

Переопределения операторов для объектов Color зависят от контекста, поскольку смешивание и добавление цветов зависит от цветового пространства. Например, пространства яркости RGB являются аддитивными и линейными, так что обычные математические операции являются довольно интуитивными, но пространства светлоты RGB, так же как пространства CMYK, не являются одновременно линейными и аддитивными, вследствие чего эти операции приводят к разным визуальным эффектам. Кроме того, большинство операций Color могут приводить к значениям за пределами нужной гаммы и, таким образом, требуют компенсации отображения гаммы. Это может быть так же просто, как фиксация низкого качества, или может быть значительно более изощренным.

Можно предусмотреть те или иные перегрузки операторов анимации, если они ограничены конкретно "цветовым контекстом" scRGB, поскольку scRGB основано на физическом свете и смешивает линейно и аддитивно. CMYK, sRGB и другие цветовые пространства имеют очень разные модели смешивания.

Имя Возвращаемый тип Аргументы Описание Другая информация
+ Color Color, Color Сложение цветов, зависящее от контекста Контекст RGB линеен относительно яркости (как смешиваются фотоны), что справедливо для scRGB и компьютерной графики
Add Color Color, Color Сложение цветов, зависящее от контекста Public
- Color Color, Color Вычитание цветов, зависящее от контекста Public
Subtract Color Color, Color Вычитание цветов, зависящее от контекста Public
* Color Color, float Зависящее от контекста цветовое умножение цвета на значение с плавающей точкой Public
Multiply Color Color, float Зависящее от контекста цветовое умножение цвета на значение с плавающей точкой Public
Equals Bool Color, color Возвращает "истину", если два цветовых значения равны Public
Equals Bool Object Возвращает "истину", если цветовой объект равен текущему цвету Public
== Bool Color, color Возвращает "истину", если два цветовых значения равны Public
IsEqual Bool Color, color Возвращает "истину", если два цветовых значения равны Public
!= Bool Color, color Возвращает "истину", если два цветовых значения не равны Public
IsNotEqual Bool Color, color Возвращает "истину", если два цветовых значения не равны Public

Аналогичные методы при использовании в "многоканальных" цветах можно также использовать для поддержки HSB, YCC и YUV и аналогичных цветовых пространств, которые тесно связаны с sRGB или scRGB.

Имя Возвращаемый тип Аргументы Описание Другая информация
Clamp Color Void Фиксирует цветовые значения в диапазоне [0,0...1,0] Public
GetHashCode int Void Возвращает хэш-код цвета Public
Name String Возвращает имя цвета Избыточное с вызовом цветового контекста
ToRgbColor нет Color Возвращает scRGB-эквивалентный цвет текущего цвета Public
ToString String Void Возвращает форматированное строковое значение цвета Public
AreClose Bool Color, Color Возвращает "истину", если цветовые значения близки с использованием функции FloatUtil Static
IsClose Bool Color Возвращает "истину", если цвет близок к текущему цвету с использованием функции FloatUtil Public
SetRenderingIntent Bool String Возвращает "истину", если намерение визуализации для ColorContext успешно Public

Значения с плавающей точкой в формах rgb и argb на основе sRGB заданы в масштабе от 0,0 до 1,0. По определению эти значения никогда не будут вне этого диапазона и потому должны быть фиксированы. Напротив, значения на основе scRGB действительны ниже 0,0 и выше 1,0. Эти значения следует фиксировать только, если конечное устройство не может поддерживать расширенную цветовую гамму. Это можно определить, запросив профиль, связанный с конечной гаммой. В идеале для дисплеев графическое оборудование может заботиться об этом вопросе с использованием функций управления гаммой DX.

Если анализируемая строка непригодна, цвет будет инициализироваться на ARGB=(0.0,0.0, 0.0, 0.0)

При сохранении значение будет записано как известное имя цвета, если цвет создан как известный цвет. Если нет, то будет использоваться форма rgb(float, float, float), если альфа равна 1,0. Если альфа не равна 1,0, то будет использоваться форма argb(float, float, float, float).

Растровая графика или построение изображения или форматы пикселя отличаются от вышеописанных решений векторной графики. Короче говоря, вход построения изображения может приблизительно составлять от 1 бит/пиксель до 128 бит/пиксель с различными поддерживаемыми цветовыми пространствами от черно-белого к sRGB, к scRGB, к CMYK. Поэтому цветовое решение для "данных изображения" (ImageData) требует "цветового контекста" (ColorContext) для ImageData или форматов пикселя. Это можно генерировать из внедренных профилей или внедренной гаммы и информации цветности в стандартных форматах файла. Это устраняет необходимость обеспечения гаммы или других избыточных свойств или полей в классе ImageData.

Код MILRender понимает цветовые спецификации 32 бит/канал (scRGB). Входное преобразование цвета должно происходить над кодом визуализации (но не обязательно вне неуправляемого кода).

Анимация цветовых значений должна в основном происходить в линейном пространстве. Это может быть линеаризованное пространство HSV или scRGB, но оно должно быть линейным для того, чтобы имели смысл "рикошет" и другие поканальные анимации.

Описаны три режима воспроизведения цвета, а именно:

Полный - 32 бит/канала через систему; задний буфер 128 бит/пиксель/10 бит/канал + передний буфер; полная композиция 32 бит/канал.

Смешанный - цветовые спецификации/интерполяция 32 бит/канал; сглаживание или фиксация цветов к предварительному составу 32 бит/пиксель.

Традиционный - цветовая спецификация 32 бит/канал - преобразованная непосредственно к 32 бит/пиксель; композиция/вывод 32 бит/пиксель.

Эти режимы будут поддерживаться с двумя форматами заднего буфера, а именно гаммой 1,0 128 бит/пиксель (scRGB) и гаммой 2,2 32 бит/пиксель (sRGB). Поддержка также обеспечивается для манипуляции сценариями переднего буфера более низкой битовой глубины (отображения 16 и 8 бит/пиксель). Задний буфер сглаживается на Present для предотвращения потери в композиции.

Класс "геометрия" (Geometry) объектов можно использовать для усечения, тестирования на попадание и визуализации данных на основе двухмерных векторов с "пером" (Pen) и "кистью" (Brush). Производные классы Geometry обеспечивают более конкретную семантику построения и перечисления. Предусмотрены ряд типов Geometry, зависящих от формы, а также обобщенная "геометрия пути" (PathGeometry), которая позволяет в явном виде задавать Geometry более сложной формы. Для тех, кто хорошо знаком с GDI+, это больше всего походит на GraphicsPath.

Учебник GDI+ Настоящее изобретение
Path GraphicsPath PathGeometry
SubPath GraphicsPath PathFigure

Geometry - это абстрактный базовый класс.

[TypeConverter(typeof(PathGeometryConverter))]
public abstract class Geometry : Animatable, IDisposable
{
internal Geometry();
public new Geometry Copy();
// Если анимировано, Bounds возвращает "текущие" границы
// Это не учитывает никакое перо
public virtual Rect Bounds {get;}
// Это учитывает перо. При наличии анимаций
// это берет "текущие" значения геометрии и пера.
public virtual Rect GetBounds(Pen pen);
// Возвращает тождество в отсутствие преобразования.
public Transform Transform {get; set;}
// Освобождает ресурсы, кэшированные Geometry (мозаики, данные пути и пр.)
public virtual void Dispose();
}

Чтобы задать преобразование, применяемое к геометрии, задают свойство "преобразование" (Transform). "Коллекция геометрий" (GeometryCollection) - это коллекция множества объектов Geometry, которые были скомбинированы с использованием конкретных булевых операций в их заданной области. Этот объект позволяет легче строить визуальные комбинации объектов Geometry, чем возможно с использованием исключительно объектов PathFigure в "геометрии пути" (PathGeometry).

Перечисление комбинированного режима направляет комбинацию геометрической области в коллекции. Булевы операции объединения (Union), исключающего ИЛИ (XOR), пересечения (Intersect) являются коммутативными и поэтому применяются к геометриям независимо от порядка. Операции дополнения (Complement) и исключения (Exclude) не являются коммутативными и потому заданы между первой геометрией и отдельной оставшейся геометрией. Иными словами, комбинация исключений для {g1, g2, g3} будет применяться как ((g1 исключить g2) и (g1 исключить g3)). Дополнение указывает, что существующая область заменяется результатом удаления существующей области из новой области. Другими словами, существующая область исключается из новой области. Исключение указывает, что существующая область заменяется результатом удаления новой области из существующей области. Другими словами, новая область исключается из существующей области. Пересечение относится к комбинированию областей путем взятия их пересечения, объединение относится к комбинированию областей путем взятия объединения их обеих, и исключающее ИЛИ относится к комбинированию областей путем взятия только площадей, охватываемых одной или другой областью, но не обеими:

public enum System.Windows.CombineMode
{
Complement,
Exclude,
Intersect,
Union,
Xor
}
public sealed class GeometryCollection: Geometry, IAddChild, IList
{
public GeometryCollection();
public GeometryCollection(int capacity);
public GeometryCollection(
CombineMode combineMode,
params Geometry[] collection);
public GeometryCollection(
CombineMode combineMode,
ICollection collection);
public GeometryCollection(
CombineMode combineMode,
ICollection collection,
Transform transform);
public new GeometryCollection Copy();
// Указывает, как комбинировать геометрию
[DefaultValue(CombineMode.Union)]
public CombineMode CombineMode {get; set;}
// IEnumerable
public IEnumerator GetEnumerator();
// ICollection
public int Count {get;}
public bool IsSynchronized {get;}
public object SyncRoot();
public void CopyTo(Array array, int index);
//IList
public bool IsFixedSize {get;}
bool IList.IsReadOnly {get;}
object IList.this[int index] {get; set;}
int IList.Add(object value);
public void Clear();
bool IList.Contains(object value);
int IList.IndexOf(object item);
void IList.Insert(int index, object value);
void IList.Remove(object value);
public void RemoveAt(int index);
// Дополнительные функции
public int Capacity {get; set;}
public Geometry this[int index] {get; set;}
public int Add(Geometry geometry);
public bool Contains(Geometry geometry);
public int IndexOf(Geometry geometry);
public int IndexOf(Geometry geometry, int startIndex);
public int IndexOf(Geometry geometry, int startIndex, int count);
public int LastIndexOf(Geometry geometry);
public int LastIndexOf(Geometry geometry, int startIndex);
public int LastIndexOf(Geometry geometry, int startIndex, int count);
public void Insert(int index, Geometry geometry);
public void Remove(Geometry geometry);
public void AddRange(GeometryCollection geometryCollection);
public void SetRange(int index, GeometryCollection geometryCollection);
public void InsertRange(int index, GeometryCollection geometryCollection);
public void RemoveRange(int index, int count);
public GeometryCollection GetRange(int index, int count);
public Geometry GetOptimizedGeometry();
}

GetOptimizedGeometry() свертывает коллекцию геометрии, где возможно, результатом не обязательно является GeometryCollection. Это может включать в себя такие оптимизации, как комбинирование геометрии примыкающих прямоугольников в геометрию одного прямоугольника, осуществление булевой операции между геометрией примыкающих путей для создания геометрии нового пути или выравнивание GeometryCollection с одним и тем же комбинированным режимом. В случаях, когда геометрия используется во многих разных контекстах, это обеспечивает повышение производительности при обработке и хранении.

Ниже приведен пример разметки, в которой используется GeometryCollection:

<Path Fill="Red">
<Path.Data>
<GeometryCollection Transform="translate(425 25)" CombineMode="Xor">
<RectangleGeometry Rect="0 0 100 100" />
<RectangleGeometry Rect="50 50 100 100" />
</GeometryCollection>
</Path.Data>
</Path>

"Геометрия пути" (PathGeometry) - это коллекция объектов "фигура пути" (PathFigure). Каждый из объектов PathFigure состоит из одного или нескольких объектов "отрезок пути" (PathSegment), которые фактически задают их форму. Залитая область PathGeometry задается взятием содержащихся PathFigure, имеющих свойство "залито" (Filled) равное "истине", и применения "правила заливки" (FillRule) для определения охватываемой области. На фиг.13 представлены соотношения объектов PathGeometry.

Перечисление FillRule задает, как комбинируются пересекающиеся области объектов "фигура" (Figure), содержащихся в Geometry для формирования результирующей области Geometry:

public enum System.Windows.Media.FillRule
{
EvenOdd,
NonZero
}
{
public PathGeometry();
public PathGeometry(params PathFigure[] figures);
public PathGeometry(ICollection figureCollection);
public PathGeometry(
ICollection figureCollection,
FillRule FillRule,
Transform transform);
public new PathGeometry Copy();
[DefaultValue(FillRule.EvenOdd)]
public FillRule FillRule {get; set;}
// Этот метод будет добавлять "фигуры" (Figures) из конкретной
// Geometry в эту PathGeometry, но будет использовать
// текущее FillRule вместо FillRule геометрии.
public void AddGeometry(Geometry geometry);
public void AddFigure(PathFigure figure);
public void AddPointAndTypes(
Point[] points,
byte[] types);
public PathFigureCollection Figures {get; set;}
public PathGeometry GetWidenedPathGeometry(Pen pen);
public PathGeometry GetOutlinedPathGeometry();
}

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

Правило EvenOdd определяет "нахождение внутри" для точки на холсте, проводя луч из этой точки в бесконечность в любом направлении и подсчитывая количество "отрезков" пути из данной формы, которые пересекают луч. Если число нечетное, значит точка находится внутри; если четное, значит точка находится снаружи.

Для преобразования других типов геометрии в геометрию пути для включения с другими фигурами используется метод "добавление геометрии" (AddGeometry). Он добавляет фигуру, которая геометрически совпадает со входной геометрией. Для неанимированной геометрии совпадение является точным, тогда как анимированная геометрия может быть с потерями при преобразовании. Причина для преобразования с потерями состоит в том, что анимированные параметры входной геометрии не соответствуют форме, которая подходит к отрезку.

Геометрия Без потерь/с потерями Фигуры
Геометрия линии (LineGeometry) С потерями при анимации Начальный отрезок (StartSegment) и отрезок линии (LineSegment)
Геометрия прямоугольника (RectangleGeometry) С потерями при анимации Отрезок полилинии (PolyLineSegment)
Геометрия эллипса (EllipseGeometry) С потерями при анимации Отрезок дуги (ArcSegment) и отрезок дуги (ArcSegment)
Коллекция геометрий (GeometryCollection) С потерями при анимации Много видов отрезков
Геометрия пути (PathGeometry) С потерями при анимации Все за исключением отрезков дуги (Arc) и кривых второго порядка (Quadratic)

Не гарантируется, что перечисление и структура результирующей PathGeometry точно соответствуют входной геометрии.

Коллекция фигур является коллекцией объектов PathFigure и основного содержимого, задающего PathGeometry:

public sealed class PathFigureCollection : Animatable, IAddChild, IList
{
public PathFigureCollection();
public PathFigureCollection(int capacity);
public PathFigureCollection(params PathFigure[] figures);
public PathFigureCollection(ICollection figureCollection);
public new PathFigureCollection Copy();
// IEnumerable
public IEnumerator GetEnumerator();
// ICollection
public int Count {get;}
public bool IsSynchronized {get;}
public object SyncRoot();
public void CopyTo(Array array, int index);
//IList
public bool IsFixedSize {get;}
bool IList.IsReadOnly {get;}
object IList.this[int index] {get; set;}
int IList.Add(object value);
public void Clear();
bool IList.Contains(object value);
int IList.IndexOf(object item);
void IList.Insert(int index, object value);
void IList.Remove(object value);
public void RemoveAt(int index);
// Дополнительные функции
public int Capacity {get; set;}
public PathFigure this[int index] {get; set;}
public int Add(PathFigure figure);
public bool Contains(PathFigure figure);
public int IndexOf(PathFigure figure);
public int IndexOf(PathFigure figure, int startIndex);
public int IndexOf(PathFigure figure, int startIndex, int count);
public int LastIndexOf(PathFigure figure);
public int LastIndexOf(PathFigure figure, int startIndex);
public int LastIndexOf(PathFigure figure, int startIndex, int count);
public void Insert(int index, PathFigure figure);
public void Remove(PathFigure figure);
public void AddRange(PathFigureCollection figureCollection);
public void SetRange(int index, PathFigureCollection figureCollection);
public void InsertRange(int index, PathFigureCollection figureCollection);
public void RemoveRange(int index, int count);
public PathFigureCollection GetRange(int index, int count);
}

PathFigure - это подразделение Geometry, которое задает коллекцию отрезков. Эта коллекция отрезков является единой связной последовательностью двухмерных объектов "отрезок пути" (PathSegment). PathFigure может быть замкнутой формой определенной площади или связной последовательностью "отрезков" (Segment), которые задают кривую, но не замкнутую область. Класс PathFigure включает в себя ряд удобных функций, упрощающих построение PathFigure из явных вызовов метода ArcTo/LineTo/(и других), не требующих объекта PathSegment. Явный вызов AddSegment ("добавление отрезка") можно использовать для добавления составного "отрезка" (Segment).

public sealed class PathFigure : Animatable
{
public PathFigure();
public PathFigure(params PathSegment[]segments);
public PathFigure(ICollection segmentCollection);
public PathFigure(ICollection segmentCollection, bool isFilled);
public new PathFigure Copy();
[DefaultValue(true)]
public bool IsFilled {get; set;}
// Начать фигуру в начальной точке.
public void StartAt(Point pt);
public void StartAt(Point pt, PointAnimationCollection ptAnimations);
// Добавляет отрезок линии из текущей точки в указанную
// новую точку. Текущая точка обновляется новым местоположением.
public void LineTo(Point pt);
public void LineTo(Point pt, PointAnimationCollection ptAnimations);
// Добавляет полилинию, начиная с текущей точки и затем
// через каждую точку в массиве точек. Текущая точка
// обновляется последней точкой массива.
public void PolyLineTo(Point[] points);
public void ArcTo(
Point pt,
Size sz,
double xRotation,
bool largeArcFlag;
bool sweepFlag);
public void ArcTo(
Point pt,
PointAnimationCollection ptAnimations,
Size sz,
SizeAnimationCollection szAnimations,
double xRotation,
DoubleAnimationCollectionxRotationAnimations,
bool largeArcFlag, bool sweepFlag);
// Добавляет отрезок кубического сплайна Безье, используя текущую точку
// как первую контрольную точку. Текущая точка обновляется
// конечной точкой.
public void BezierTo(
Point pt1,
Point pt2,
Point ptDest);
public void BezierTo(
Point pt1,
PointAnimationCollection pt1Animations,
Point pt2,
PointAnimationCollection pt2Animations,
Point ptDest,
PointAnimationCollection ptDestAnimations);
// Добавляет отрезки кубического сплайна Безье, используя текущую точку
// как первую контрольную точку, затем повторяя через каждые 3 точки.
// Текущая точка обновляется последней точкой массива.
// Когда количество пройденных точек не кратно 3,
// вызывается исключение, соответствующее непригодному аргументу.
public void PolyBezierTo(Point[] points);
// Добавляет отрезок квадратного сплайна Безье, используя текущую точку
// как первую контрольную точку. Текущая точка обновляется
// конечной точкой.
public void QuadraticBezierTo(Point pt1, Point ptDest);
public void QuadraticBezierTo(
Point pt1,
PointAnimationCollection pt1 Animations,
Point ptDest,
PointAnimationCollection ptDestAnimations);
// Добавляет отрезки квадратного сплайна Безье, используя текущую точку
// как первую контрольную точку, затем повторяя через каждые 2 точки.
// Текущая точка обновляется последней точкой массива.
// Когда количество пройденных точек не кратно 2,
// вызывается исключение, соответствующее непригодному аргументу.
public void PolyQuadraticBezierTo(Point[] points);
// Замыкает фигуру. Новые отрезки нельзя добавлять.
public void CloseFigure();
// Оконтуривает отрезки новой фигуры. По умолчанию "истина".
public void StrokeNewSegments(bool strokeNewSegments);
// Добавляет новый Segment в PathFigure
public void AddSegment(PathSegment segment);
public PathSegmentCollection Segments {get; set;}
public PathFigure GetFlattenedPathFigure(float flatness);
}

Фигуре требуется начальная точка, поскольку каждый отрезок поддерживает непрерывность по отношению к последней добавленной точке. Чтобы задать начальную точку, вызывают StartAt(pt) или Add(new StartSegment(pt)). После добавления отрезков для добавления надлежащим образом замыкающего отрезка, который соединяет последнюю точку обратно с начальной точкой, используют CloseFigure() или Add(new CloseSegment()). Начальный и замыкающий отрезки оказываются в коллекция отрезков.

Исключение возникает, если PathFigure построена и "начальный отрезок" (StartSegment) не является первым отрезком в коллекции, или "замыкающий отрезок" (CloseSegment), если существует, не является последним отрезком в коллекции. StartSegment и CloseSegment не пригодны в любых других позициях в фигуре, исключение соответствует полностью пустой коллекции отрезков.

Свойство PathFigure.IsFilled явно управляет тем, подлежит ли содержащаяся область замкнутой фигуры использованию для тестирования на попадание, визуализации и усечения. Если это свойство задано равным "ложь", то будет использоваться только очертание PathFigure и его содержащаяся область не будет вносить вклад в общую область PathGeometry. По умолчанию, значение этого свойства равно "истина".

Для перечисления содержимого PathFigure как точек один прямой путь заключается в выравнивании фигуры и проверки полученной "коллекции отрезков пути" (PathSegmentCollection). Процесс выравнивания является процессом с потерями в отношении параметров анимации и отрезков кривых, однако необработанные данные точек предоставляются через "отрезок полилинии" (PolyLineSegment) для дальнейшей обработки точек.

PathSegmentCollection - это коллекция объектов PathSegment и основного содержимого, задающего PathFigure:

public sealed class PathSegmentCollection : Animatable, LAddChild, IList
{
public PathSegmentCollection();
public PathSegmentCollection(int capacity);
public PathSegmentCollection(params PathSegment[] segments);
public PathSegmentCollection(ICollection segments);
public new PathSegmentCollection Copy();
// IEnumerable
public IEnumerator GetEnumerator();
// ICollection
public int Count {get;}
public bool IsSynchronized {get;}
public object SyncRoot();
public void CopyTo(Array array, int index);
// IList
public bool IsFixedSize {get;}
bool IList.IsReadOnly {get;}
object IList.this[int index] {get; set;}
int IList.Add(object value);
public void Clear();
bool IList.Contains(object value);
int IList.IndexOf(object item);
void IList.Insert(int index, object value);
void IList.Remove(object value);
public void RemoveAt(int index);
// Дополнительные функции
public int Capacity {get; set;}
public PathSegment this[int index] {get; set;}
public int Add(PathSegment segment);
public bool Contains(PathSegment segment);
public int IndexOf(PathSegment segment);
public int IndexOf(PathSegment segment, int startIndex);
public int IndexOf(PathSegment segment, int startIndex, int count);
public int LastIndexOf(PathSegment segment);
public int LastIndexOf(PathSegment segment, int startIndex);
public int LastIndexOf(PathSegment segment, int startIndex, int count);
public void Insert(int index, PathSegment segment);
public void Remove(PathSegment segment);
public void AddRange(PathSegmentCollection segmentCollection);
public void SetRange(int index, PathSegmentCollection segmentCollection);
public void InsertRange(int index, PathSegmentCollection segmentCollection);
public void RemoveRange(int index, int count);
public PathSegmentCollection GetRange(int index, int count);
}

PathSegment представляет фрагмент очертания PathFigure. Простые прямолинейные отрезки, отрезки эллиптической дуги, отрезки кубического сплайна Безье и отрезки квадратного сплайна Безье могут объединяться друг с другом для формирования PathFigure.

Каждый из объектов PathSegment, которые добавлены в PathFigure, также имеют свойство PathSegmentIsStroked. Если это свойство PathSegment задано равным "истина", то конкретный PathSegment будет вносить вклад в оконтуренную область PathGeometry при визуализации с помощью Pen. Это также применяется к тестированию на попадание и к явному "расширена" (Widen) "геометрии пути" (PathGeometry). Конкретное поведение при переключении между оконтуренными и неоконтуренными отрезками PathSegment в PathFigure такое же, как если бы Pen было задано штриховым, в том, что надлежащие концы штрихов будут применяться к концам PathSegment.

Нижеследующий пример демонстрирует разметку, которая использует PathGeometry, PathFigure и PathSegment:

<PathFill="#4000FF00" Stroke="Yellow" >
<Path.Data>
<PathGeometry Transform="translate(225 25)" FillRule="EvenOdd">
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure IsFilled="True">
<PathFigure.Segments>
<PathSegmentCollection>
<StartSegment Point="0 0" IsStroked="False" />
<LineSegment Point="100 0" />
<BezierSegment Point1="125 25" Point2="125 75" Point3="100 100"/>
<QuadraticBezierSegment Point1="50 50" Point2="0 100" />
<ArcSegmentPoint="100 150" Size="100 100" XRotation="45" LargeArc="False" SweepFlag="True" />
<PolyLineSegment Points="100 175 0 175" />
<PolyBezierSegment Points="50 225 50 275 0 300 50 325 50 375 0 400" />
<PolyQuadraticBezierSegment Points="50 450 0 500 50 550 0 600"/>
<CloseSegment IsStroked="True" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
<Path.Data>
</Path>

"Геометрия прямоугольника" (RectangleGeometry) задает прямоугольник или прямоугольный геометрический объект с закругленными углами. Радиус Х и радиус Y относятся к радиальной длине, выровненной относительно оси, закругленных углов:

public sealed class RectangleGeometry : Geometry
{
public RectangleGeometry();
public RectangleGeometry(Rect rect);
public RectangleGeometry(
Rect rect,
double radiusX,
double radiusY);
public RectangleGeometry(
Rect rect, RectAnimationCollection rectAnimations,
double radiusX, DoubleAnimationCollection radiusXAnimations,
double radiusY, DoubleAnimationCollection radiusYAnimations);
public RectangleGeometry(
Rect rect, RectAnimationCollection rectAnimations,
double radiusX, DoubleAnimationCollection radiusXAnimations,
double radius Y, DoubleAnimationCollection radiusYAnimations,
Transform);
public new RectangleGeometry Copy();
[Animation("RectAnimations")]
public Rect Rect {get; set;}
public RectAnimationCollection RectAnimations {get; set;}
[Animation("RadiusXAnimations")]
[DefaultValue(0.0)]
public double RadiusX {get; set;}
public DoubleAnimationCollection RadiusXAnimations {get; set;}
[Animation("RadiusYAnimations")]
[DefaultValue(0.0)]
public double RadiusY {get; set;}
public DoubleAnimationCollection RadiusYAnimations {get; set;}
}

Следующий пример демонстрирует разметку, которая использует RectangleGeometry:

<Path Fill="#4000FF00">
<Path.Data>
<RectangleGeometry Rect="125 125 75 75" RadiusX="10" Radius Y="5">
</RectangleGeometry>
</Path.Data>
</Path>

"Геометрия эллипса" (EllipseGeometry) определяет эллиптическую область, заданную радиальными длинами Х и Y, выровненными относительно оси:

public sealed class EllipseGeometry : Geometry
{
public EllipseGeometry();
public EllipseGeometry(Rectangle rect);
public EllipseGeometry(
Point center,
double radiusX,
double radiusY);
public EllipseGeometry(
Point center, PointAnimationCollection centerAnimations,
double radiusX, DoubleAnimationCollection radiusXAnimations,
double radiusY, DoubleAnimationCollection radiusYAnimations);
public EllipseGeometry(
Point center, PointAnimationCollection centerAnimations,
double radiusX, DoubleAnimationCollection radiusXAnimations,
double radiusY, DoubleAnimationCollection radiusYAnimations,
Transform transform);
public new EllipseGeometry Copy();
[Animation("CenterAnimations")]
public Point Center {get; set;}
public PointAnimationCollection CenterAnimations {get; set;}
[Anhnation("RadiusXAnimations")]
public double RadiusX {get; set;}
public DoubleAnimationCollection RadiusXAnimations {get; set;}
[Animation("RadiusYAnimations")]
public double RadiusY {get; set;}
public DoubleAnimationCollection RadiusYAnimations {get; set;}
}

Следующий пример демонстрирует разметку, которая использует EllipseGeometry:

<Path Fill="#4000FF00">
<Path.Data>
<EllipseGeometry Center="50 300" RadiusX="50" Radius Y="75">
</EllipseGeometry>
<Path.Data>
</Path>

"Геометрия линии" (LineGeometry) задает отрезок линии между двумя точками и потому не содержит никакой залитой области:

public sealed class LineGeometry: Geometry
{
public LineGeometry();
public LineGeometry(Point pt1, Point pt2);
public LineGeometry(
Point startPoint,
PointAnimationCollection startPointAnimations,
Point endPoint,
PointAnimationCollection endPointAnimations);
public LineGeometry(
Point startPoint,
PointAnimationCollection startPointAnimations,
Point endPoint,
PointAnimationCollection endPointAnimations,
Transform transform);
public new LineGeometry Copy();
[Animation("Point1Animations")]
public Point StartPoint {get; set;}
public PointAnimationCollection StartPointAnimations {get; set;}
[Animation("Point2Animations")]
public Point Point2 {get; set;}
public PointAnimationCollection Point2Animations {get; set;}
}

Следующий пример демонстрирует разметку, которая использует LineGeometry:

<Path Stroke="#4000FF00"
StrokeThickness="20"
StrokeStartLineCap="Flat"
StrokeEndLineCap="Triangle"
StrokeDashCap="Round"
StrokeLineJoin="Bevel"
StrokeMiterLimit=" 100"
StrokeDashOffset="0"
StrokeDashArray="1.0 2.0">
<Path.Data>
<LineGeometry StartPoint="125 25" EndPoint="175 75">
</LineGeometry>
</Path.Data>
</Path>

ПОСТРОЕНИЕ ИЗОБРАЖЕНИЯ

"Источник изображения" (ImageSource) - это абстрактный класс, содержащий основной компоновочный блок для конвейера построения изображения. ImageSource, в принципе, представляет единичный, постоянный набор пикселей с определенным размером и разрешением. Например, ImageSource может быть единичным кадром в файле изображения, который мог бы обеспечить декодер (Decoder), или может быть результатом преобразования, которое действует в отношении определенного собственного ImageSource. ImageSource не является группой кадров или анимацией. ImageSource является изменяемым не потому, что его собственные свойства могут изменяться, но потому, что свойства его подклассов потенциально могут изменяться.

По соображениям производительности "источники изображения" поддерживают неуправляемый доступ к изображению с использованием интерфейса источника битового образа IMIL (IMILBitmapSource). Если подкласс ImageSource не обеспечивает его, то это делает базовый класс ImageSource (используя класс-упаковщик).

namespace System.Windows.Media
{
public abstract class ImageSource : Changeable
{
/// Внутренний формат данных изображения.
/// Если ImageSource считывается напрямую, то это формат,
/// в котором должны быть пиксели, когда они считываются.
public abstract PixelFormat Format {get;}
/// Может ли ImageSource преобразовать свои данные в заданный формат.
/// Если нет, для этого преобразования нужно использовать
/// преобразователь форматов.
/// Заметим: для лучшей производительности, ImageSource должен
/// обеспечивать поддержку PixelFormat32bppPARGB.
public virtual bool CanConvertTo(PixelFormat targetPixelFormat)
/// Ширина изображения, в пикселях.
public abstract int PixelWidth {get;}
/// Высота изображения, в пикселях.
public abstract int PixelHeight {get;}
/// Горизонтальное DPI (количество пикелов на дюйм) изображения.
public abstract double DpiX {get;}
///Вертикальное DPI изображения.
public abstract double DpiY {get;}
/// Получить ширину изображения в единицах измерения (1/96 дюйма).
public double Width {get;}
/// Получить высоту изображения в единицах измерения (1/96 дюйма).
public double Height {get;}
/// Получить палитру изображения, если она одна.
ImagePalette Palette {get;}
/// Копировать пиксельные данные из изображения в массив пикселей, имеющий
/// заданный шаг, начиная с pixelOffset (указывающий количество
/// пикселей от начала). Пиксели следует копировать в
/// заданный pixelFormat. Чтобы выяснить, поддерживается ли pixelFormat,
/// сначала вызывается CanConvertTo.
public void Copy(PixelFormat pixelFormat, byte[] pixels, int stride,
int pixelOffset)
/// Копировать прямоугольник пиксельных данных из изображения в массив
/// пикселей, имеющий заданный шаг, начиная с pixelOffset (указывающий
/// количество пикселей от начала). Пиксели нужно копировать в
/// заданный pixelFormat. Чтобы выяснить, поддерживается ли pixelFormat,
/// сначала вызывается CanConvertTo. В случае пустого прямоугольника
/// (ширина или высота которого равна 0) нужно игнорировать прямоугольник
/// и копировать все изображение.
public abstract void Copy(IntegerRect sourceRect, PixelFormat pixelFormat,
byte[] pixels, int stride, int pixelOffset);
/// При наличии внедренного уменьшенного изображения, возвратить его.
/// Иначе возвратить null. Этот метод НЕ создает уменьшенное изображение
/// для изображений, которые еще не имеют его.
public virtual ImageSource EmbeddedThumbnail {get; set;}
/// Получить уменьшенное изображение для изображения.
/// Этот метод всегда возвращает уменьшенное изображение, которое имеет
/// примерно такое же форматное соотношение, что и исходное изображение.
///
/// При наличии внедренного уменьшенного изображения, этот метод возвращает
/// это уменьшенное изображение, масштабированное до указанного размера. В отсутствие внедренного
/// уменьшенного изображения, само изображение масштабируется до указанного размера.
///
/// Заданные ширина и высота уменьшенного изображения должны быть >0.
/// Выбираем, использовать ли заданное значение ширины или заданное
/// значение высоты, определяя, что даст наибольшее возможное уменьшенное изображение,
/// имеющее то же форматное соотношение и остающееся меньшим
/// или равным обоим значениям ширины и высоты.
///
/// Поэтому, чтобы указать, что вы хотите использовать только значение
/// ширины, вам нужно использовать Int32.MaxValue для значения высоты.
public virtual ImageSource GetThumbnail(ImageSizeOptions sizeOptions);
/// Обеспечивает доступ к метаданным изображения.
public virtual ImageMetaData MetaData {get; set;}
/// Возвращает IMILBitmapSource для этого ImageSource.
unsafe internal IntPtr BitmapSource {get;}
}
}

"Данные изображения" (ImageData) - это подкласс "источника изображения" (ImageSource). ImageData реализует ImageSource для нескольких разных источников изображений:

HICON
HBITMAP
Пиксельные данные в управляемой памяти.
Пиксельные данные в неуправляемой памяти
Изображения в System.IO.Stream (требуется декодер)
Другой ImageSource

ImageData обеспечивает услуги, включающие в себя кэширование декодированного изображения в системной памяти, обрезку изображения до заданного начального прямоугольника (в кэш) и задание размеров изображения в соответствии с указанными шириной и высотой декодирования (в кэш). Для сценариев декодирования изображения ImageData позволяет задавать, какой декодер использовать, или автоматически обнаруживать кодек, на основании входного потока и типа MIME. ImageData не поддерживает загрузку изображения непосредственно из URI. Загрузчик следует использовать для отображения URI в поток, который можно использовать для построения ImageData. После построения ImageData его единственными изменяемыми свойствами являются его внедренное уменьшенное изображение, его метаданные и его пиксельные данные. Другие свойства считаются неизменяемыми.

Пиксельные данные ImageData можно изменять, в том числе одним из двух путей: (1) получая "контекст рисования" (DrawingContext) для ImageData и используя команды через "контекст рисования" для рисования на изображении или (2) используя ImageData как пункт назначения (RenderTarget) для "менеджера визуалов" (VisualManager) и выдавая команду визуализировать дерево визуалов (сцену) в ImageData. В любом случае рисование осуществляется в изображение в памяти. Изменяется только кэшированное изображение памяти - файл изображения сам по себе не затрагивается (пока ImageData не будет позднее закодирован в файл изображения с использованием "кодера изображений" (ImageEncoder).

namespace System.Windows.Media
{
/// ImageData можно построить с помощью и без помощи кэша.
/// При построении без помощи кэша, изображение будет декодироваться
/// при каждом использовании. Преимущество этого подхода в том, что кэш не
/// хранится в памяти и что декодирование и фильтрация могут быть
/// оптимальными для размера, который рисуется. Недостаток состоит в том, что
/// декодирование нужно выполнять при каждой перерисовке. Другой
/// подход состоит в кэшировании декодированного изображения.
/// Есть несколько вариантов создания декодированного, кэшированного
/// изображения. Его можно создавать с конкретным размером и в конкретном
/// начальном прямоугольнике (нужной области из начального изображения)
/// Если изображение не кэшируется, вариантов нет - оно
/// декодируется без обрезки источника. Если изображение кэшируется,
/// кэшированная версия изображения становится начальным изображением, и
/// источник исходного изображения отбрасывается
public class ImageData : ImageSource
{
/// Построить ImageData из Stream.
/// Вызывающая сторона владеет потоком и отвечает за его закрытие.
public ImageData(System.IO.Stream imageStream);
/// Построить ImageData из Stream.
/// Вызывающая сторона владеет потоком и отвечает за его закрытие.
public ImageData(System.IO.Stream imageStream,
CodecInfo decoderInfo, // идентифицирует кодек, подлежащий использованию (или null)
bool createCache, // если "ложь", sourceRect и sizeOptions игнорируются
IntegerRect sourceRect,
ImageSizeOptions sizeOptions
);
/// Построить ImageData из массива пикселей.
unsafe public ImageData(
int pixelWidth,
int pixelHeight,
double dpiX,
double dpiY,
PixelFormat pixelFormat,
ImagePalette imagePalette,
byte[] pixels,
int stride,
IntegerRect sourceRect,
ImageSizeOptions sizeOptions
);
/// Построить ImageData из массива пикселей в неуправляемой памяти
/// (например, DibSection).
public ImageData(
int pixelWidth,
int pixelHeight,
double dpiX,
double dpiY,
PixelFormat pixelFormat,
ImagePalette imagePalette,
IntPtr pixels, // неуправляемый массив пикселей, например, DibSection
int stride,
bool createCache, // если "ложь", sourceRect и sizeOptions игнорируются
IntegerRect sourceRect,
ImageSizeOptions sizeOptions
);
/// Построить ImageData из HBITMAP.
public ImageData(
HBITMAP hbitmap,
HPALETTE hpalette,
IntegerRect sourceRect,
ImageSizeOptions sizeOptions
);
/// Построить ImageData из HICON.
public ImageData(
HICON hicon,
IntegerRect sourceRect,
ImageSizeOptions sizeOptions
);
/// Построить ImageData из ImageSource.
public ImageData(
ImageSource ImageSource,
bool createCache, // если "ложь", sourceRect и sizeOptions игнорируются
IntegerRect sourceRect,
ImageSizeOptions sizeOptions
);
/// Получить информацию о конкретном кодеке, который использовался
/// для декодирования изображения (если кодек был нужен, и мы
/// имеем эту информацию).
public CodecInfo DecoderInfo {get;}
/// Получить DrawingContext для рисования на ImageData.
public DrawingContext Open();
}
}

ImageDecoder ("декодер изображений") - это абстрактный класс, который обеспечивает базовый класс для декодеров. Он обеспечивает способ определения количества кадров в изображении и перечисления (или индексирования) кадров. Как отмечено выше, каждый кадр изображения является "источником изображения" (ImageSource). Добавленные кодеки создают объект "данные изображения" (ImageData) для каждого запрашиваемого кадра. Встроенные кодеки могут использовать разные подклассы для возвращения ImageSource для каждого кадра. ImageDecoder является не самим ImageSource, но контейнером для одного или нескольких "источников изображения". Заметим, что каждый кадр изображения потенциально может иметь разные атрибуты (разный размер, разное разрешение и т.д.).

namespace System. Windows.Media
{
/// ImageDecoder по существу является контейнером для кадров изображения.
/// Каждый кадр изображения является ImageSource.
public abstract class ImageDecoder: ICollection, IEnumerable
{
/// Количество кадров изображения в этом изображении.
public int Count {get;}
/// Синхронизирован ли доступ к ICollection (сохранение потоков).
public bool IsSynchronized {get;}
/// Получает объект, который можно использовать для синхронизации
/// доступа к ICollection.
public object SyncRoot {get;}
/// Копирует кадры в "массив", начиная с конкретного индекса в "массиве".
public void CopyTo(Array array, int index);
/// Возвращает перечислитель для итерации по кадрам изображения.
public IEnumerator GetEnumerator();
/// Возвращает объект, зависящий от кодека, который идентифицирует
/// свойства, которые использовались для кодирования этого изображения
/// (если поддерживается кодеком).
/// Эти свойства, зависящие от кодека, можно передавать обратно
/// соответствующему кодеру (если есть) для повторного получения
/// того же типа кодирования.
public virtual object CustomEncoderProperties {get;}
/// Информация, идентифицирующая этот кодек (включая любой
/// соответствующий кодер).
public abstract CodecInfo Info {get;}
/// Получить формат пикселя для первого кадра.
public virtual System.Windows.Media.PixelFormat Format {get;}
/// Получить ширину пикселя для первого кадра.
public virtual int PixelWidth {get;}
/// Получить высоту пикселя для первого кадра.
public virtual int PixelHeight {get;}
/// Получить горизонтальное dpi для первого кадра.
public virtual double DpiX {get;}
/// Получить вертикальное dpi для первого кадра.
public virtual double DpiY {get;}
/// Получить ширину изображения в единицах измерения (1/96).
public double Width {get;}
/// Получить высоту изображения в единицах измерения (1/96 дюйма).
public double Height {get;}
/// При наличии внедренного уменьшенного изображения для первого кадра, возвратить его.
/// Иначе возвратить null. Этот метод НЕ создает уменьшенное изображение
/// для изображений, которые еще не имеют его.
public virtual ImageSource EmbeddedThumbnail {get;}
/// Возвратить кадр изображения, обрезанный до заданного
/// sourceRect.
/// В случае пустого прямоугольника (ширина и/или высота которого 0),
/// нужно игнорировать прямоугольник и получить весь кадр.
public virtual ImageSource GetFrame(
int frameIndex,
bool createCache,
IntegerRect sourceRect);
/// Возвратить кадр изображения, с применением всех указанных
/// обрезки и задания размеров.
/// При наличии пустого начального прямоугольника
///( ширина и/или высота которого 0),
/// нужно игнорировать прямоугольник и получить весь кадр.
public virtual ImageSource GetFrame(
int frameIndex,
bool createCache,
IntegerRect sourceRect,
ImageSizeOptions sizeOptions);
///Индексатор для возвращения заданного кадра изображения
/// (полноразмерного).
/// Индекс должен быть в диапазоне: (NumFrames > index >= 0)
public abstract ImageSource this[int frameIndex] {get;}
/// Преобразование из ImageDecoder в ImageSource с возвращением первого
///кадра.
public static explicit operator ImageSource(ImageDecoder decoder);
/// Обеспечивает доступ только для чтения к метаданным этого изображения.
public abstract ImageMetaData MetaData {get;}
}
}

MIL предусматривает ряд встроенных декодеров, в том числе ImageDecoderBmp.cs, ImageDecoderGif.cs, ImageDecoderIcon.cs, ImageDecoderJpeg.cs, ImageDecoderPng.cs, ImageDecoderTiff.cs и ImageDecoderWmp.cs. Каждый из них реализует ImageDecoder и единый конструктор, который использует System.IO.Stream для инициализации декодера, как в следующем примере:

/// Если этот декодер не может обрабатывать поток
/// изображения, он вызывает исключение.
public ImageDecoderJpeg(System.IO.Stream imageStream);

"Кодер изображения" (ImageEncoder) - эта коллекция "источников изображения" (кадров изображения), каждый из которых имеет собственные метаданные и уменьшенное изображение. Также могут иметь место глобальные уменьшенные изображения и метаданные, связанные со всем набором кадров. Также можно выбирать кодек для обеспечения поддержки свойств кодирования, используемых для определения того, как кодировать изображение. Коллекцию кадров можно сохранять (кодировать) в любое количество заданных потоков (по одному). Коллекцию можно очищать и затем наполнять другой коллекцией и снова сохранять.

namespace System.Windows.Media
{
/// ImageEncoder собирает набор кадров (источников изображения) с
/// соответствующими уменьшенными изображениями и метаданными и сохраняет их в заданном потоке.
/// Помимо эскизов и метаданных для заданного кадра, могут быть уменьшенное изображение
/// и метаданные для всего изображения (глобальные), если кодек поддерживает их.
public abstract class ImageEncoder : IList, IDisposable
{
public void Dispose();
/// Получает значение, указывающее, имеет ли IList фиксированный размер.
public bool IsFixedSize {get;}
/// Получает значение, указывающее предназначен ли IList только для чтения.
public bool IsReadOnly {get {return false;}}
/// Индексатор для заданного кадра изображения. Метод set - это
/// операция Replace для этого кадра. Объект должен быть ImageSource.
public object IList.this[int frameIndex] {get; set;}
public ImageSource this[int frameIndex] {get; set;}
/// Добавляет кадр к изображению.
public int IList.Add(object value);
public int Add(ImageSource imageFrame);
/// Переустанавливает кодер в пустое состояние, без всяких кадров,
/// уменьшенных изображений, метаданных или свойств кодирования.
public void Clear();
/// Определяет, содержит ли кодер (Encoder) заданный кадр.
public bool IList.Contains(object value);
public bool Contains(ImageSource imageFrame);
/// Определяет индекс заданного кадра.
public int IList.IndexOf(object value);
public int IndexOf(ImageSource imageFrame);
/// Этот метод вставляет кадр в заданное местоположение для кадра, с
/// с соответствующими уменьшенным изображением и метаданными (если имеются).
public void IList.Insert(int frameIndex, object value);
public void Insert(int frameIndex, ImageSource imageFrame);
/// Удаляет первый встреченный заданный кадр,
public void IList.Remove(object value);
public void Remove(ImageSource imageFrame);
/// Удаляет заданный кадр из изображения. Последующие кадры
/// перемещаются вверх по списку.
public void RemoveAt(int frameIndex);
/// Количество кадров изображения в этом изображении.
public int Count {get;}
/// Синхронизирован ли доступ к ICollection (сохранение потока).
public bool IsSynchronized {get;}
/// Получает объект, который можно использовать для синхронизации
/// доступа к ICollection.
public object SyncRoot {get;}
/// Копирует кадры в "массив" (Array), начиная с конкретного индекса Array.
public void CopyTo(Array array, int index);
/// Возвращает перечислитель для итерации по кадрам изображения.
public IEnumerator GetEnumerator();
/// Информация, которая идентифицирует этот кодек (включая любой
/// соответствующий декодер).
public CodecInfo Info {get;}
/// Объект, зависящий от кодека, который идентифицирует свойства,
/// используемые для кодирования этого изображения.
public object CustomEncoderProperties {get; set;}
/// Задать или получить глобальное внедренное уменьшенное изображение для изображения,
/// если таковое существует.
public ImageSource EmbeddedThumbnail {get; set;}
/// Обеспечивает доступ к глобальным метаданным этого изображения.
public ImageMetaData MetaData {get; set;}
/// Поддерживает ли этот кодек изображения с более чем одним кадром.
public bool SupportsMultiFrame {get;}
/// Копировать изображение из заданного декодера, включая все кадры,
/// все эскизы и все метаданные, включая покадровые
/// и глобальные данные. Это в явном виде очищает любые данные, которые
/// уже заданы в кодере.
public void CopyImage(ImageDecoder decoder);
/// Сохраняет (кодирует) изображение в указанный поток.
public void Save(System.IO.Stream destStream);
}
}

MIL предусматривает ряд встроенных кодеров, в том числе ImageEncoderBmp.cs, ImageEncoderGif.cs, ImageEncoderJpeg.cs, ImageEncoderPng.cs, ImageEncoderTiff.cs и ImageEncoderWmp.cs.

ImageSizeOptions ("опции размеров изображения") используются для задания размеров уменьшенных изображений и размеров кэшированных изображений. Опции включают в себя ширину, высоту, сохраняется ли форматное соотношение исходного изображения, и угол поворота (кратный 90 градусам).

namespace System.Windows.Media
{
/// Опции задания размеров для изображения. Результирующее изображение
/// будет масштабировано на основании этих опций.
public class ImageSizeOptions
{
/// Применяемый поворот; поддерживаются только кратные 90 градусов.
public enum Rotation
{
/// Без поворота
Rotate0 = 0,
/// Поворот на 90 градусов
Rotate90 = 90,
/// Поворот на 180 градусов
Rotate180 = 180,
/// Поворот на 270 градусов
Rotate270 = 270,
}
/// Построить объект ImageSizeOptions.
/// Еще нужно задать свойства "ширина" (Width) и "высота" (Height).
public ImageSizeOptions();
/// Построить объект ImageSizeOptions.
public ImageSizeOptions(
bool preserveAspectRatio,
int pixelWidth,
int pixelHeight
);
/// Сохранять ли форматное соотношение исходного
/// изображения. Если да, то PixelWidth and PixelHeight являются
/// максимальными значениями для размера изображения. В отношении
/// результирующего изображения гарантируется только, что оно будет
///иметь либо ширину, либо высоту, совпадающую с заданными
/// значениями. Например, если вы хотите задать высоту, в то же время
/// сохранив форматное соотношение для ширины, задайте высоту равной
/// нужному значению и задайте ширину равной Int32.MaxValue.
///
/// Если вы не хотите сохранить форматное соотношение, то используйте
/// заданную ширину и заданную высоту, и
/// изображение будет растянуто в соответствии с обоими этими значениями.
public bool PreserveAspectRatio {get; set;}
/// PixelWidth для результирующего изображения. См. описание
/// PreserveAspectRatio на предмет того, как использовать это значение.
///
/// PixelWidth, чтобы быть пригодным, должно быть задано большим нуля.
public int PixelWidth {get; set;}
/// PixelHeight для результирующего изображения. См. описание
/// PreserveAspectRatio на предмет того, как использовать это значение.
///
/// PixelHeight, чтобы быть пригодным, должно быть задано большим нуля.
public int PixelHeight {get; set;}
/// RotationAngle для поворота изображения. Поддерживаются только кратные 90.
public Rotation RotationAngle {get; set;}
/// Пригодны ли опции размеров. Чтобы быть пригодными,
/// оба должны быть больше нуля, и, самое большее, один
/// должен быть задан равным Int32.MaxValue.
public bool IsValid {get;}
}
}

Ниже приведено определение формата пикселя для изображений и пиксельных поверхностей:

namespace System.Windows.Media
{
/// Определение формата пикселя для изображений и пиксельных поверхностей
public struct PixelFormat
{
/// Описывает порядок каждого канала пиксельных данных
public enum ChannelDescription
{
/// Описание неопределенного канала
Undefined = 0,
/// индексированные (пакетизированные) форматы одного канала
Index = 1,
/// серые форматы одного канала
Gray = 2,
///красный-зеленый-синий
RGB = 3,
/// синий-зеленый-красный
BGR = 4,
/// альфа-красный-зеленый-синий
ARGB =5,
/// голубой-сиреневый-желтый-черный
CMYK =6
}
/// op_equality - возвращает, равны ли два формата пикселя
public static bool operator == (PixelFormat left, PixelFormat right);
/// Equals - возвращает, равны ли два формата пикселя
public static bool Equals(PixelFormat left, PixelFormat right);
/// op_inequality - возвращает, не равны ли два формата пикселя
public static bool operator != (PixelFormat left, PixelFormat right);
/// Equals - возвращает, равно ли это объекту
public override bool Equals(Object obj);
/// GetHashCode - возвращает хэш-код
public override int GetHashCode();
/// Число битов на пиксель в этом формате.
public int BitsPerPixel {get;}
/// Является ли этот формат пакетизированным (индексированным) форматом.
public bool IsPalletized {get;}
/// Является ли этот формат набором значений серого.
public bool IsGray {get;}
/// Является ли этот формат значениями CMYK
/// (голубой-сиреневый-желтый-черный)
public bool IsCMYK {get;}
/// Является ли этот формат SRGB (Гамма приблизительно 2.2)
public bool IsSRGB {get;}
/// Является ли формат линейным (Гамма 1.0)
public bool IsScRGB {get;}
/// Имеет ли этот формат предварительно умноженное альфа.
public bool IsPremultiplied {get;}
/// Описывает данные этого формата и их порядок.
public ChannelDescription Channel {get;}
/// Получить маску для использования для получения красного канала для
///этого формата.
/// Сдвинуть маску на величину leftShift, чтобы получить маску
/// для использования.
public void GetRedMask(out uint mask, out int leftShift);
/// Получить маску для использования для получения зеленого канала для
/// этого формата.
/// Сдвинуть маску на величину leftShift, чтобы получить маску
/// для использования.
public void GetGreenMask(out uint mask, out int leftShift);
/// Получить маску для использования для получения синего канала для
/// этого формата.
/// Сдвинуть маску на величину leftShift, чтобы получить маску
/// для использования.
public void GetBlueMask(out uint mask, out int leftShift);
/// Получить маску для использования для получения альфа-канала для
/// этого формата.
/// Сдвинуть маску на величину leftShift, чтобы получить маску
/// для использования.
public void GetAlphaMask(out uint mask, out int leftShift);
/// Получить маски для использования для получения каждого канала для
/// этого формата.
/// Сдвинуть маски на величину leftShift, чтобы получить маски
/// для использования.
public void GetRGBMasks(out uint redMask, out int redLeftShift,
out uint greenMask, out int greenLeftShift,
out uint blueMask, out int blueLeftShift);
}
}

Каждый кодек (ImageEncoder и ImageDecoder) требуется для обеспечения CodecInfo, которая дает информацию о кодеке, обеспечивает методы создания экземпляров для соответствующего кодера/декодера и обеспечивает метод, который определяет, согласуется ли кодек с обеспечиваемым "фильтром кодеков" (CodecFilter).

namespace System.Windows.Media
{
/// Информация о конкретном кодеке и фабрике для создания кодека.
/// Это возвращается из перечислителя кодека.
public abstract class CodecInfo
{
/// Число байтов, необходимых из заголовка изображения для
/// определения того, поддерживается ли изображение этим кодеком.
public abstract int RequiredHeaderSize {get;}
/// Поддерживает ли кодек это изображение, на основании первых байтов
/// RequiredHeaderSize из изображения. Заголовок должен содержать
/// по меньшей мере, первые байты RequiredHeaderSize из изображения.
public abstract bool IsImageSupported(byte[] header);
/// Дружественное имя кодека
public abstract string FriendlyName {get;}
/// Какие типы MIME поддерживает кодек.
public abstract string[] MimeTypes {get;}
/// Кто создал кодек.
public abstract string CodecAuthor {get;}
/// Номер версии кодека.
public abstract System.Version CodecVersion {get;}
/// Имеется ли декодер, связанный с этим кодеком.
public abstract bool HasDecoder {get;}
/// Имеется ли кодер, связанный с этим кодеком.
public abstract bool HasEncoder {get;}
/// Является ли этот кодек встроенным.
public bool IsBuiltIn {get;}
///Определяет, согласуется ли кодек с заданным фильтром.
/// Заметим: это НЕ проверяет/анализирует байты в потоке.
public virtual bool MatchesFilter(CodecFilter filter);
/// Получить экземпляр декодера, связанного с этим кодеком (если есть).
public abstract ImageDecoder CreateDecoderInstance(System.IO.Stream
imageStream);
// Получить экземпляр кодера, связанного с этим кодеком (если есть).
public abstract ImageEncoder CreateEncoderInstance();
}
}

MIL предусматривает встроенные объекты CodecInfo, включая CodecInfoBmp.cs, CodechInfoGif.cs, CodecmInfoIcon.cs, CodecInfoJpeg.cs, CodecInfoPng.cs, CodechInfoTiff.cs и CodecInfoWmp.cs.

"Фильтр кодеков" (CodecFilter) используется перечислителем кодеков для перечисления кодеков на основании заданных критериев. Критерии, которые не заданы, игнорируются при поиске подходящего кодека. Например, если MimeType ("тип MIME") не задан, рассматриваются кодеки с любым типом MIME.

namespace System.Windows.Media
{
/// Фильтр для перечисления кодеков. Перечисляться будут только те кодеки,
/// которые согласуются со свойствами.
public class CodecFilter
{
/// Найти кодек, который может обрабатывать этот поток изображения
/// (анализируя поток).
public System.IO.Stream ImageStream {get; set;}
/// Дружественное имя кодека
public string FriendlyName {get; set;}
/// Поддерживает ли кодек эти типы MIME.
public string MimeType {get; set;}
/// Соответствует ли кодек этому автору.
public string CodecAuthor {get; set;}
/// Версия кодека >= этой версии?
public System.Version MinVersion {get; set;}
/// Версия кодека <= этой версии?
public System.Version MaxVersion {get; set;}
/// Найти кодеки, имеющие согласующийся декодер. Задание равным
/// "ложь" означает, что мы не фильтруем наличие
/// декодера для этого кодека.
public bool HasDecoder {get; set;}
/// Найти кодеки, имеющие согласующийся кодер. Задание равным
/// "ложь" означает, что мы не фильтруем наличие
/// кодера для этого кодека.
public bool HasEncoder {get; set;}
/// Найти кодеки, которые являются встроенными (не добавленными).
/// Задание равным "ложь" означает, что мы не фильтруем,
/// является ли кодек одним из встроенных.
public bool IsBuiltIn {get; set;}
}
}

Когда перечислитель построен (его ctor являются внутренними), задается "фильтр кодеков" (CodecFilter). Этот фильтр используется для определения того, какие кодеки перечислять. Перечисляются только те, которые согласуются с фильтром (если существуют).

namespace System.Windows.Media
{
/// Перечислитель для файлов изображения.
public class ImageCodecEnumerator: IEnumerator
{
/// Reset - повторно задает позицию перед первым объектом в коллекции.
/// Вызов MoveNext должен предшествовать любому вызову Current
/// после Reset.
public void Reset();
/// MoveNext - Перейти к следующему объекту в коллекции. Возвращает
///"ложь", если перечислитель дошел до конца коллекции.
public bool MoveNext();
/// Current - возвращает текущий объект в коллекции
public object Current {get;}
}
}

ImageEffect ("эффект изображения") - это базовый класс для эффектов построения изображения на растровой основе. ImageEffect можно рассматривать как коллекцию из 0 или более входов и 0 или более выходов. Все входы и выходы "эффекта изображения" относятся к типу ImageSource. ImageEffect обычно инициализируется со своими входами и своими свойствами, а затем его выходы используются либо для рисования части сцены, либо как кадры для ImageEncoder. Встроенные эффекты включают в себя (но не в ограничительном смысле) следующие: ImageEffectBlur, ImageEffectFlipRotate, ImageEffectGammaCorrect, ImageEffectGlow, ImageEffectGrayscale, ImageEffectNegate, ImageEffectSharpen, ImageEffectTint.

ПРЕОБРАЗОВАНИЕ

Класс "преобразование" (Transform) объектов, представленных на фиг.7, можно использовать для масштабирования, вращения, параллельного переноса и перекоса векторной и растровой графики. Производные классы Transform обеспечивают дружественное использование и семантику перечисления. Иерархия классов преобразования отличается от структуры "матрица" (Matrix) тем, что является классом и поддерживает анимацию и семантику перечисления:

TransformCollection (семантика перечисления и т.д.)

TransformCollection.AddScale(...)

Animate MatrixTransform

Transform является абстрактным базовым классом:

[TypeConverter(typeof(TransformConverter))]
public abstract class Transform : Animatable, IDisposeable
{
internal Transform();
public new Transform Copy();
public static MatrixTransform CreateMatrix(Matrix matrix);
public static TranslateTransform CreateTranslation(double x, double y);
public static RotateTransform CreateRotation(double angle);
public static RotateTransform CreateRotation(double angle, Point center);
public static ScaleTransform CreateScale(double scaleX, double scaleY);
public static ScaleTransform CreateScale(
double scaleX,
double scale Y,
Point center);
public static SkewTransform CreateSkew(double angleX, double angleY);
public static SkewTransform CreateSkew(
double angleX,
double angle Y,
Point center);
// Тождественное преобразование
public static Transform Identity {get;}
public abstract Matrix Value {get;}
}

"Коллекция преобразований" (TransformCollection) - это коллекция объектов Transform, значение которой соответствует матричному умножению отдельных значений Transform. Значение составлено слева направо, согласуясь с первым и последним элементами коллекции:

public sealed class TransformCollection : Transform, IAddChild, IList
{
public TransformCollection();
public TransformCollection(int capacity);
public TransformCollection(params Transform[] transforms);
public TransformCollection(ICollection transformCollection);
public new TransformCollection Copy();
// IEnumerable
public IEnumerator GetEnumerator();
// ICollection
public int Count {get;}
public bool IsSynchronized {get;}
public object SyncRoot();
public void CopyTo(Array array, int index);
// IList
public bool IsFixedSize {get;}
bool IList.IsReadOnly {get;}
object IList.this[int index] {get; set;}
int IList.Add(object value);
public void Clear();
bool IList.Contains(object value);
int IList.IndexOf(object item);
void IList.Insert(int index, object value);
void IList.Remove(object value);
public void RemoveAt(int index);
// Дополнительные функции
public int Capacity {get; set;}
public Transform this[int index] {get; set;}
public int Add(Transform transform);
public bool Contains(Transform transform);
public int IndexOf(Transform transform);
public int IndexOf(Transform transform, int startIndex);
public int IndexOf(Transform transform, int startIndex, int count);
public int LastIndexOf(Transform transform);
public int LastIndexOf(Transform transform, int startIndex);
public int LastIndexOf(Transform transform, int startIndex, int count);
public void Insert(int index, Transform transform);
public void Remove(Transform transform);
public void AddRange(TransformCollection transformCollection);
public void SetRange(int index, TransformCollection transformCollection);
public void InsertRange(int index, TransformCollection transformCollection);
public void RemoveRange(int index, int count);
public TransformCollection GetRange(int index, int count);
// IAddChild
void IAddChild.AddChild(Object o);
void IAddChild.AddText(string s);
public override Matrix Value {get;}
public void AddMatrix(
double m11,
double m12,
double m21,
double m22,
double offsetX,
double offsetY);
public void AddMatrix(Matrix matrix);
public void AddTranslate(double translateX, double translateY);
public void AddRotate(double angle);
public void AddRotate(double angle, Point center);
public void AddScale(double scaleX, double scaleY);
public void AddScale(double scaleX, double scaleY, Point center);
public void AddSkew(double angleX, double angleY);
public void AddSkew(double angleX, double angleY, Point center);
public Transform GetOptimizedTransform();
}

"Преобразование поворота" (RotateTransform) задает поворот на угол относительно конкретной центральной точки (по умолчанию, 0,0). Угол задан в градусах. Статическое матричное представление для угла поворота вокруг точки x,y имеет следующий вид.

public sealed class RotateTransform : Transform
{
public RotateTransform();
public RotateTransform(double angle);
public RotateTransform(
double angle,
Point center);
public RotateTransform(
double angle,
DoubleAnimationCollection angle,
Point center,
PointAnimationCollection center);
public new RotateTransform Copy();
[Animations("AngleAnimations")]
public double Angle {get; set;}
public DoubleAnimationCollection AngleAnimations {get; set;}
[Animations("CenterAnimations")]
public Point Center {get; set;}
public PointAnimationCollection CenterAnimations {get; set;}
public override Matrix Value {get;}
}

"Преобразование параллельного переноса" (TranslateTransform) задает перенос вдоль оси в направлении x и y. Статическое матричное представление параллельного переноса со сдвигом dx,dy выглядит следующим образом.

public sealed class System.Windows.Media.TranslateTransform : Transform
{
public TranslateTransform();
public TranslateTransform(
double offsetx,
double offsety);
public TranslateTransform(
double offsetx, DoubleAnimationCollection offsetx,
double offsety, DoubleAnimationCollection offsety);
public new TranslateTransform Copy();
[Animations("XAnimations")]
public double X {get; set;}
public DoubleAnimationCollection XAnimations {get; set;}
[Animations("YAnimations")]
public double Y {get; set;}
public DoubleAnimationCollection YAnimations {get; set;}
public override Matrix Value {get;}
}

"Преобразование масштабирования" (ScaleTransform) задает масштабирование в направлении x и y относительно центральной точки (по умолчанию 0,0). Статическое матричное представление масштабирования sx, sy относительно точки x,y выглядит следующим образом.

public sealed class ScaleTransform : Transform
{
public ScaleTransform();
public ScaleTransform(
double scaleX,
double scaleY);
public ScaleTransform(
double scaleX,
double scaleY,
Point center);
public ScaleTransform(
double scaleX,
DoubleAnimationCollection scaleXAnimations,
double scale Y,
DoubleAnimationCollection scaleYAnimations,
Point center,
PointAnimationCollection center);
public new ScaleTransform Copy();
[Animations("XAnimations")]
public double ScaleX {get; set;}
public DoubleAnimationCollection ScaleXAnimations {get; set;}
[Animations("YAnimations")]
public double ScaleY {get; set;}
public DoubleAnimationCollection Scale YAnimations {get; set;}
[Animations("CenterAnimations")]
public Point Center {get; set;}
public PointAnimationCollection CenterAnimations {get; set;}
public override Matrix Value {get;}
}

"Преобразование перекоса" (SkewTransform) задает перекос на угол в градусах в направлении x и y. Угол перекоса измеряется в градусах. Статическое матричное представление перекоса под углом выглядит следующим образом.

public sealed class SkewTransform : Transform
{
public SkewTransform();
public SkewTransform(
double angleX,
double angleY);
public SkewTransform(
double angleX,
double angleY,
Point center);
public SkewTransform(
double angleX,
DoubleAnimationCollection angleXAnimations,
double angle Y,
DoubleAnimationCollection angleYAnimations,
Point center,
PointAnimationCollection pointAnimations);
public new SkewTransform Copy();
[Animations("AngleXAnimations")]
public double AngleX {get; set;}
public DoubleAnimationCollection AngleXAnimations {get; set;}
[Animations("AngleYAnimations")]
public double AngleY {get; set;}
public DoubleAnimationCollection AngleYAnimations {get; set;}
[Animations("CenterAnimations")]
public Point Center {get; set;}
public PointAnimationCollection CenterAnimations {get; set;}
public override Matrix Value {get;}
}

"Матричное преобразование" (MatrixTransform) задает преобразование через его математическое представление:

public sealed class MatrixTransform : Transform
{
public MatrixTransform();
public MatrixTransform(
double m11,
double m12,
double m21,
double m22,
double offsetX,
double offsetY);
public MatrixTransform(Matrix matrix);
public new MatrixTransform Copy();
public Matrix Matrix {get; set;}
public override Matrix Value {get;}
}

Когда свойство типа Transform задано в разметке, система свойств использует преобразователь типа Transform для преобразования представления строки в надлежащий производный объект Transform. В настоящее время не существует способа описания анимированных свойств с использованием этого синтаксиса.

Синтаксис находится в векторной графике, и соответствующая конструкция Transform кратко изложена ниже, причем параметры, обозначенные "<>" представляют необязательные параметры:

- matrix(m11 m12 m21 m22 offsetX offset Y)

- AddMatrix(m11, m12, m21, m22, offsetX, offsetY)

- translate(tx <ty>)

- AddTranslate(tx, ty).

- Если не задано, предполагается равным 0.

- scale(sx <sy>)

- AddScale(sx, sy).

- Если не задано, предполагается таким же, как sx.

- rotate(angle <cx> <cy>)

- AddRotate(angle, Point(cx,cy)).

- Если cx, cy не заданы, предполагаются равными 0,0.

- skewX(angle)

- AddSkew(angle, 0)

- skewY(angle)

- AddSkew(0, angle)

список преобразований:
пп* преобразования? пп*
преобразования:
преобразование
| преобразование запятая-пп + преобразования
преобразование:
матрица
| параллельный перенос
| масштабирование
| поворот
| skewX (перекос по X)
| skewY (перекос по Y)
матрица:
"матрица" пп* "(" пп*
число запятая-пп
число запятая-пп
число запятая-пп
число запятая-пп
число запятая-пп
число пп* ")"
параллельный перенос:
"параллельный перенос" пп* "(" пп* число (запятая-пп число)? пп* ")"
масштабирование:
"масштабирование" пп* "(" пп* число (запятая-пп число)? пп* ")"
поворот:
"поворот" пп* "(" пп* число (запятая-пп число запятая-пп число)? пп* ")"
скашивание X:
"skewX" пп* "(" пп* число пп* ")"
скашивание Y:
"skewY" пп* "(" пп* число пп* ")"

ЭФФЕКТЫ

Эффекты обеспечивают средство изменения содержимого визуала способом, ориентированным на визуализацию. Например, "эффекты изображения" (ImageEffects) (эффекты битового образа на растровой основе) действуют в отношении основанного на изображении, полностью скомпонованного представления части сцены. Эффекты разбиваются на различные типы, включающие в себя ImageEffects ("эффекты изображения"), BlendModes ("режимы смешивания") и VectorEffects ("векторные эффекты").

ImageEffects можно использовать в сцене удержанного режима, применяя их к подрафу или "элементу" (Element), или их можно использовать в автономном конвейере изображений. В общем случае, "эффект изображения" (ImageEffect) имеет нуль или более входов и нуль или более выходов, которые относятся к типу ImageSource. В конвейере изображений непосредственного режима не нужен выход, поскольку ImageEffect может выявлять другие свойства, которые описывают атрибуты входа. Например, ImageEffect может выдавать информацию гистограммы цветов или информацию обнаружения грани. В сцене удержанного режима есть дополнительный мета-вход, который обеспечивает доступ к визуализируемому содержимому подграфа, к которому применяется эффект.

/// Класс ImageEffect является базовым классом для всех эффектов построения
/// изображения (размытие, градация серого и т.д.)
///
/// Возможно, что эффект не имеет входов, но эффект должен всегда иметь,
/// по меньшей мере, один выход. Это предполагают реализации по умолчанию. Если
/// производный эффект собирается играть с выходом/выходами, удостоверьтесь,
/// что, по меньшей мере, один присутствует.
public abstract class System.Windows.Media.ImageEffect: Changeable
{
/// Этот конструктор позволяет производному классу задавать количество входов и
/// выходов для этого эффекта, и этот класс затем будет обрабатывать входные и
/// выходные массивы, включая проверку индексов.
/// Это задано по умолчанию 1 и 1. Если для эффекта желательно иметь переменное
/// количество входов и выходов, он может передавать -1 для какого-либо
/// (или обоих) счетчиков, и входные и выходные коллекции будут позволять это.
/// Наконец, все эти методы являются виртуальными, поэтому производные классы
/// могут, по выбору, не делегировать обратно в базовый класс, в каковом случае
/// не привлекается никаких дополнительных затрат.
protected ImageEffect(int inputCount, int outputCount);
/// Этот конструктор позволяет производному классу задавать количество входов и
/// выходов для этого эффекта, и этот класс затем будет обрабатывать входные и
/// выходные массивы, включая проверку индексов.
/// Это задано по умолчанию 1 и 1. Если эффект желает иметь переменное
/// количество входов и выходов, он может передавать -1 для какого-либо
/// (или обоих) счетчиков, и входные и выходные коллекции будут позволять это.
/// Наконец, все эти методы являются виртуальными, поэтому производные классы
/// могут, по выбору, не делегировать обратно в базовый класс, в каковом случае
/// не привлекается никаких дополнительных затрат.
protected ImageEffect(int inputCount, int outputCount, double scaleX, double scaleY);
/// Это первый вход, и является псевдонимом для Inputs[0]
///
/// Предупреждение о производительности:
/// Если вход эффекта НЕ в формате, который
/// поддерживает эффект, эффект будет преобразовывать
/// вход в работоспособный формат для вас.
public virtual ImageSource Input {get; set;}
/// Это коллекция входов
public virtual ImageSourceCollection Inputs {get;}
/// Это первый выход и является псевдонимом для Outputs[0]
public virtual ImageSource Output {get;}
/// Это коллекция выходов
public virtual ImageEffectSourceCollection Outputs {get;}
protected internal virtual ImageSource GetOutput(int outputIndex);
/// Эти значения содержат горизонтальный и вертикальный масштаб,
/// применяемый к этому источнику.
/// Бывают случаи, когда эффекту нужно работать с другим разрешением
/// или в другом координатном пространстве, чем текущее, логическое
/// координатное пространство.
/// Таким образом, эти свойства позволяют потребителю отображать между
/// локальным пространством и пространством ImageEffectSource.
protected internal virtual double GetScaleX(int outputIndex);
protected internal virtual double GetScaleY(int outputIndex);
protected internal virtual PixelFormat GetFormat(int outputIndex);
/// Ширина изображения в пикселях.
protected internal virtual int GetPixelWidth(int outputIndex);
/// Высота изображения в пикселях.
protected internal virtual int GetPixelHeight(int outputIndex);
/// Горизонтальное DPI изображения.
protected internal virtual double GetDpiX(int outputIndex);
/// Вертикальное DPI изображения.
protected internal virtual double GetDpiY(int outputIndex);
/// Получить палитру для конкретного выхода
protected internal virtual ImagePalette GetPalette(int outputIndex);
/// Реализация абстрактного метода ImageSource
///
/// PixelOffset, в действительности, ничего не делает. Если вы не хотите начать с (0,0)
/// во входе, то ваш sourceRect начинается в той точке, где вы хотите.
protected internal abstract void Copy(int outputIndex, IntegerRect sourceRect,
PixelFormat pixelFormat, byte[] pixels, int stride, int pixelOffset);
}
}

"Режимы смешивания" (BlendModes) являются особой формой эффектов на основе изображения. Их можно применить к сцене удержанного режима, в общем случае, таким же образом, как "эффекты изображения" (ImageEffects). Таким образом, имеется свойство "элемента" (Element) ("BlendMode"), а также свойство BlendMode на визуале, метод PushBlendMode на IDrawingContext и свойство BlendMode на Brush. Режимы смешивания осуществляют объединение начального и конечного цветов при композиции источника. Примером "режимов смешивания" являются "умножение" (Multiply), "сложение" (Add) и т.д. "Векторные эффекты" (VectorEffects) являются другим типом эффектов.

Как описано в обзоре, "режимы смешивания" описывают операцию, которая управляет тем, как изображение составляется в другое изображение или в сцену. "Режимы смешивания" можно применять к сцене и к кистям. Каждый "режим смешивания" (BlendMode) описывает, как комбинировать начальный пиксель и конечный пиксель, и применяется к каждому составляемому пикселю. "Режимы смешивания" применяются после масштабирования или иного преобразования источника или после применения любых эффектов (включая "непрозрачность" (Opacity)). Заметим, что при применении операций BlendMode источник и пункт назначения находятся в формате с предварительным умножением на альфа. Чтобы задать "режим смешивания", программист может использовать один из "режимов смешивания", заданных в статическом классе "режимы смешивания", или может в явном виде задавать начальный и конечный коэффициенты. Поскольку в одной реализации коэффициенты не являются расширяемыми и они не имеют параметров, они представляются перечислением:

/// Четыре значения, связанные с каждым BlendModeMultiplier
/// умножаются на соответствующий канал в цвете, к которому
/// применяется BlendModeMultiplier.
/// Коэффициенты умножаются на каналы Alpha, Red, Green и Blue,
/// соответственно,
/// где значения в гамме находятся в пределах [0..1].
public enum BlendModeMultipliers
{
///Нуль - (0,0,0,0)
Zero,
///Единица - (1,1,1,1)
One,
/// SourceColor - (Alphaист, Redист, Greenист, Blueист)
SourceColor,
///InverseSourceColor-(1-Alphaист, 1-Redист, 1-Greenист,
/// 1-Blueист)
InverseSourceColor,
/// SourceAlpha - (Alphaист, Alphaист, Alphaист, Alphaист)
SourceAlpha,
/// InverseSourceAlpha - (1-Alphaист, 1-Alphaист, 1-Alphaист,
/// 1-Alphaист)
InverseSource Alpha,
/// DestinationColor - (Alphaназн, Redназн, Greenназн,
/// Blueназн)
DestinationColor,
/// InverseDestinationColor -
/// (1-Alphaназн, 1-Redназн, 1-Greenназн,
/// 1-Blueназн)
InverseDestinationColor,
/// DestinationAlpha - (Alphaназн, Alphaназн,
/// Alphaназн, Alphaназн)
DestinationAlpha,
/// InverseDestinationAlpha -
/// (1-Alphaназн, 1-Alphaназн, 1-Alphaназн,
/// 1-Alphaназн)
InverseDestionaAlpha,
/// SourceAlphaSaturate - f = min(Alphaист, 1 - Alphaназн),
///(1,f,f,f)
SourceAlphaSaturate
}
/// Этот класс представляет операцию смешивания между начальной и
/// конечной поверхностью
public sealed class BlendMode: Changeable
{
// Строит заказной BlendMode из обеспеченных
// BlendModeMultiplier
public BlendMode(BlendModeMultiplier sourceMultiplier,
BlendModeMultiplier destinationMultiplier);
public new BlendMode Copy();
public BlendModeMultiplier SourceMultiplier {get;}
public BlendModeMultiplier DestinationMultiplier {get;}
}
/// Это коллекция общеизвестных вариантов BlendMode
public sealed class BlendModes
{
/// Normal - начальный - единица, конечный - InverseSourceAlpha
public static Normal {get;}
/// SourceCopy - начальный - единица, конечный - нуль
public static SourceCopy {get;}
}

ТЕСТИРОВАНИЕ НА ПОПАДАНИЕ

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

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

Механизм тестирования на попадание может эффективно фильтровать результаты тестирования на попадание. Кроме того, механизм тестирования на попадание обеспечивает гибкость для расширения на другие типы визуалов и разрешения вниз до подпримитивов в визуале, например, одним примером этого является "удержанный трехмерный визуал" (Retained3DVisual).

Обход тестирования на попадание является глубоким обходом дерева визуалов справа налево. Существуют три участника, тестер попаданий, обходчик и средство управления/визуал. Тестер попаданий реализует два обратных вызова, один - для управления обходчиком, и другой - для раннего окончания обхода на определенных визуалах, на которые имеется попадание. Средство управления реализует виртуальный метод для определения того, что является попаданием. Обходчик является фиксированной частью системы и обходит дерево визуалов на основании поведения обратного вызова, по существу, опрашивая каждое средство управления, было ли попадание на средство управления. О попаданиях сообщается посредством обратного вызова в порядке по z-координате, сверху вниз.

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

Логика последовательности операций с точки зрения обходчика попаданий проиллюстрирована в следующем псевдокоде:

- Для каждого визуала, начиная с корня:
- Если точка попадания внутри объединенных границ попадания потомков (например, HitTestBounds.Contains(HitPoint))
- Вызвать делегат тестера попаданий(например, HitTestFilterDelegate)
- Если null или возвращает "продолжить" (Continue), то (например, HitTestFilterBehavior.Continue)
- Для каждого дочернего визуала
- Преобразовать точку попадания в его локальное пространство
- Тестировать дочерний визуал на попадание (например,Visual.HitTest(...))
- Если потомок возвращает "остановить" (Stop), то возвратить (например, HitTestResultBehavior.Stop)
- Если точка попадания внутри визуала (например, Visual.HitTestCore(...))
- Вызвать делегат результата тестера попаданий (HitTestResultDelegate)
- Продолжить либо остановить (например, HitTestResultBehavior.Continue)

Тестер попаданий использует открытые методы для инициирования теста попаданий и обеспечивает делегатов для управления поведением. Поведение по умолчанию состоит в тестировании всех визуалов и возвращении первого попадания. Если не выдается никакого результирующего делегата, возникает исключение.

Средство управления принимает решение по своей логике тестирования на попадание, подменяя HitTestCore ("ядро теста на попадания") для точки и геометрии. При инициировании теста попаданий внутренний обходчик дерева визуалов вызывает HitTestCore, в сущности, спрашивая средство управления, попадание ли это. "Границы теста попаданий" (HitTestBounds) отражают строгие границы области попадания и используются для оптимизации обхода. Принятое по умолчанию поведение визуала состоит в тестировании ограничивающего прямоугольника содержимого визуализации.

public class Visual: DependencyObject, IDisposable, IVisual
{
// Игнорировать все, что не относится к тестированию на попадание
// HitTestCore реализуется автором визуала для попадания только по его
// содержимому.
protected virtual PointHitTestResult HitTestCore(
PointHitTestParameters point);
protected virtual GeometryHitTestResult HitTestCore(
GeometryHitTestParameters geometry);
protected virtual Rect HitTestBounds {get;}
}
public delegate HitTestFilterBehavior HitTestFilterDelegate(Visual visual);
public delegate HitTestResultBehavior HitTestResultDelegate(HitTestResult result)
public interface IVisual
{
// Игнорировать все, что не относится к тестированию на попадание
// Возвратить самое верхнее попадание визуала
public PointHitTestResult HitTest(Point point);
// HitTest вызывается для инициирования тестирования на попадание для
// дерева визуалов.
void HitTest(
HitTestFilterDelegate filterHitDelegate,
HitTestResultDelegate resultHitDelegate,
HitTestParameters hitTestParams);
}

Тестер попаданий инициирует тест попаданий, переходя к точке попадания или геометрии и к дополнительным параметрам в "параметрах тестирования на попадание" (HitTestParameters). Класс обеспечен, главным образом, для упрощения конструкции и для дальнейшего повышения расширяемости. Особые запросы тестирования на попадание могут быть производными от этого класса для передачи дополнительной информации заинтересованным средствам управления. Каждое средство управления реализует конкретное HitTestCore для точки и геометрии. Ожидается, что средства управления соответствуют параметрам тестирования на попадание при реализации своей логики HitTestCore.

public abstract class HitTestParameters
{
// Общие параметры тестирования на попадание
internal HitTestParameters();
}
public class PointHitTestParameters : HitTestParameters
{
public PointHitTestParameters(Point point);
Point HitPoint {get;}
}
// Возвращает подробную информацию о пересечении двух геометрических объектов.
enum IntersectionDetail
{
// Подробности пересечения пусты или не запрошены.
EmptyOrNotCalculated,
// Первая геометрия полностью внутри второй геометрии.
FullyInside,
// Вторая геометрия полностью содержит первую геометрию.
FullyContains,
// Ребра первой и второй геометрий пересекаются.
Intersects
}
public class GeometryHitTestParameters : HitTestParameters
{
Public GeometryHitTestParameters(
Geometry geometry,
bool computeIntersectionDetail);
public Geometry HitGeometry {get;}
public bool ComputeIntersectionDetail {get;}
}

Средство управления возвращает конкретные данные путем создания производных классов от "результата теста на попадание" (HitTestResult). Например, средство управления текстом может хотеть возвратить попадание позиции символа. PointHitTestResult содержит точку локального координатного пространства. GeometryHitTestResult содержит геометрию локального координатного пространства (исходного теста попаданий). Функции преобразования визуалов могут отображать местоположение попадания в пространство предков.

public abstract class HitTestResult
{
internal HitTestResult(Visual visual);
public Visual Visual {get;}
}
public class PointHitTestResult: HitTestResult
{
public PointHitTestResult(Visual visual, Point point);
public Point Point {get;}
}
public class GeometryHitTestResult: HitTestResult
{
public GeometryHitTestResult(
Visual visual,
Geometry geometry,
IntersectionDetail intersectionDetail);
public Geometry Geometry {get;}
// Это задается только, если GeometryHitTestParameters.IntersectionDetail
public IntersectionDetail IntersectionDetail {get;}
}

Для иллюстрации использования делегатов, рассмотрим тестер попаданий, который хочет первое, самое верхнее попадание с использованием анонимных делегатов:

public Visual HitTest(Visual visual, Point pt)
{
Visual visualHit = null;
visual.HitTest(
new PointHitTestParameters(pt),
null,
HitTestResultDelegate(HitTestResult htr) {
visualHit = htr.Visual;
return HitTestResultBehavior.Stop;
}
);
return visualHit;
}

Другим примером является средство тестирования на попадание (тестер попаданий), которое хочет возвратить все визуалы, на которые были попадания:

public Visual[] HitTest(Visual visual, Point pt)
{
ArrayList visualsHit = new ArrayList();
visual.HitTest(
new PointHitTestParameters(pt),
null,
HitTestResultDelegate(HitTestResult htr) {
visualsHit.Add(htr.Visual);
return HitTestResultBehavior.Continue;
}
);
}

Тестер попаданий использует перечисления для управления фильтрацией тестирования на попадание и результирующим поведением:

public enum HitTestFilterBehavior
{
//
ContinueSkipChildren,
ContinueSkipVisualAndChildren,
ContinueSkip Visual,
Continue,
Stop
}

Перечисление "поведение фильтра теста на попадание" (HitTestFilterBehavior) управляет поведением фильтрации в том, что SkipChildren ("пропустить потомков") предписывает тестировать на попадания этот визуал, но не его дочерние визуалы. SkipVisualAndChildren ("пропустить визуал и потомков") предписывает не тестировать на попадания визуал и любые дочерние визуалы. "Продолжить" (Continue) предписывает тестировать на попадания этот визуал и его дочерние визуалы. "Остановить" (Stop) предписывает не тестировать на попадания никакие визуалы в дереве визуалов и возвратиться к вызывающей стороне.

Перечисление HitTestResultBehavior управляет поведением тестера попаданий:

public enum HitTestResultBehavior
{
Stop,
Continue
}

Stop предписывает возвратиться ко входу теста на попадание, пропустив любые дальнейшие операции фильтра или теста на попадание. Continue предписывает тестирование на попадание для следующего визуала.

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

Автор средства управления записывает логику для попадания, подменяя HitTestCore и производя свое собственное вычисление и/или используя услуги, описанные ниже.

Ниже приведен ряд примеров, которые демонстрируют силу этих услуг.

Первый пример демонстрирует средство управления, имеющее открытое свойство HitRegion, которое представляет область, чувствительную к попаданию, средства управления. Заметим, что область попадания не обязана совпадать с визуализируемым содержимым и может быть оптимизирована некоторыми приложениями. Если область попадания не задана (_hitRegion == null), средство управления предоставляет определять попадание базовым услугам реализации.

public class HitRegionControl : Control // производный от Visual.
{
private Geometry _hitRegion;
public Geometry HitRegion
{
get
{
return _hitRegion;
}
set
{
_hitRegion = value;
}
}
protected virtual PointHitTestResult HitTestCore(PointHitTestParameters
htParams)
{
bool IsHit = (_hitRegion != null) ?
_hitRegion.DoesContain(htParams.Pomt) : IsHitRenderContents(htParams);
return isHit ? new PointHitTestResult(this, htParams.Point): null;
}
protected virtual GeometryHitTestResult HitTestCore(
GeometryHitTestParameters htParams)
{
IntersectionDetail intersectDetail = (_hitRegion != null) ?
_hitRegion.DoesContain(
htParams.Geometry,
htParams.ComputeIntersectionDetail) : HitTestRenderContents(htParams);
return (intersectDetail != IntersectionDetail.Empty) ?
new GeometryHitTestResult(
this,
htParams.Geometry,
intersectDetail) : null;
}
protected virtual Rect HitTestBounds
{
get
{
return (_hitRegion != null) ? _hitRegion.Bounds : GetContentBoundingBox()
}
}
}

Для подмены поведения IsHit следует использовать дополнительные услуги поддержки.

Классы Geometry осуществляют тест на попадание для своей внутренней области:

public abstract class Geometry: Changeable
{
public virtual bool DoesContain(Point point);
public virtual bool DoesContain(Geometry geometry);
public virtual IntersectionDetail DoesContainWithDetail(Geometry geometry);
}

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

public class Visual : DependencyObject, IDisposable, IVisual
{
protected Rect VisualDescendantBounds {get;}
protected Rect VisualContentBounds {get;}
}

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

public class ImageData: ImageSource
{
public virtual bool HitTestImageContents(
HitTestParameters htParams,
Transform transform);
}

АНИМАЦИЯ

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

Графическая анимация достигается связыванием коллекции анимаций с операцией визуализации. Например, метод IDrawingContext.DrawLine берет перо и две концевые точки. Одна из концевых точек может быть связана с коллекцией объектов "точечная анимация" (PointAnimation), в каковом случае линия будет перемещаться с течением времени. Аналогично, с пером может быть связана коллекция объектов "цветовая анимация" (ColorAnimation). В этих случаях, каждая анимация, используемая в операции визуализации, может действовать на отдельном тактовом сигнале, иногда именуемом "временной шкалой". Когда анимированный примитив нарисован, система визуализации заботится о перерисовке сцены через регулярные интервалы. Каждый раз при визуализации кадра вычисляется текущее значение анимаций, используемых в сцене, на основании истекшего времени (в большинстве случаев, измеряемого системным тактовым сигналом), после чего анимированные примитивы перерисовываются.

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

Предусмотрена модель хронирования, в которой хронируемые объекты участвуют в иерархической системе хронирования, где отдельные временные шкалы имеют атрибуты, которые задают их поведение по отношению к их родительской временной шкале или, для временных шкал верхнего уровня, по отношению к временной шкале корневого "документа" (или "страницы" или "кадра"). Атрибуты хронирования являются набором параметров, который задает поведение во времени объекта. Атрибуты хронирования являются исключительно описательными и не имеют состояния среды выполнения. Кроме того, атрибуты хронирования являются неизменяемыми.

Временная шкала является примером сущности хронирования, которая поддерживает состояние среды выполнения согласно набору атрибутов хронирования. Временная шкала задает понятие "сейчас" для хронированного объекта. Дерево хронирования - это структура данных, содержащая набор временных шкал, организованных в иерархическом порядке. Соотношение между временными шкалами задается набором правил интерфейса и атрибутами хронирования, связанными с каждой временной шкалой.

Хронированный объект - это любой объект, который проявляет поведение, изменяющееся со временем. Описание поведения во времени хронированного объекта задано набором атрибутов хронирования, тогда как его состояние хронирования времени выполнения поддерживается одной или несколькими временными шкалами. Анимационная функция - это функция, которая берет базовое значение конкретного типа данных в качестве входа и создает значение того же типа в качестве своего выхода. Анимационная функция может брать или не брать другие неявные или явные входные параметры, например, значение текущего времени временной шкалы. В этом отношении, анимационная функция может не быть постоянной, в том смысле, что один и тот же вход может создавать разные выходы в разные моменты времени.

Модификатор - это объект, который реализует анимационную функцию и используется для модификации значения свойства "элемента" (Element), некоторого другого сложного объекта или параметра для вызова визуализации. Хронированный модификатор - это модификатор, который связан с "временной шкалой" (Timeline), и его анимационная функция явно зависит от состояния времени выполнения этой временной шкалы. Анимация - это хронированный модификатор, который реализует определенный известный набор анимационных функций.

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

Временные шкалы можно рассматривать как секундомеры, которые управляют процессами, изменяющимися со временем, например, воспроизведением видеоклипа или анимацией. Моменты времени указаны в атрибутах временной шкалы по отношению к чему-либо. В большинстве случаев они относятся к родительской временной шкале, но для временных шкал в корне дерева значения относятся к "времени документа", где время документа - это неявная временная шкала, которая начинается при запуске приложения или при перемещении по странице или кадру. Тактовый сигнал во временной шкале открываются двумя путями: как смещение от начальной точки или как отношение продвижения между 0 и 1. Последнее - это просто отношение текущего времени к длительности.

Простейшая временная шкала имеет начальное время и длительность. Например, временная шкала с начальным временем три секунды и длительностью пять секунд "начинается" спустя три секунды после начала отсчета времени t=0 (по умолчанию, момент загрузки приложения) и "оканчивается" пятью секундами позже. В течение этих пяти секунд временная шкала считается "включенной". Если эта временная шкала управляет анимацией, эта анимация изменяется (например, перемещается) в течение этого времени, но является статической до и после. На фиг.29 показана временная шкала с начальным временем три и длительностью пять.

Временную шкалу также можно программировать на повторение ее поведения. Это повторение можно задавать как счет итераций или длительность повторения. Временная шкала проходит столько проходов от начала к концу, сколько необходимо для заполнения необходимого счета или длительности. Если счет повторений не является целочисленным значением, последняя итерация прерывается посередине. На фиг.30 показана временная шкала с началом = 3, длительностью = 5 и длительностью повторения = 17 (это значит, что анимация будет повторяться каждые пять секунд, пока не истекут семнадцать секунд после начального времени, т.е. двадцать секунд).

Начальное время для временной шкалы обычно относится к своей родительской временной шкале (или времени документа), но начальное время может также быть задано относительно начала и конца другой временной шкалы. В такой ситуации каждое начало (или конец) начальной временной шкалы приводит к тому, что соответствующее начало планируется для конечной временной шкалы. На фиг.31 показана временная шкала с начальным временем 3 с после временной шкалы другого.

Когда временная шкала достигает конечной точки, она сразу же "отключается". В этот момент хронированный объект, который управляет, прекращает влиять на представление. Например, если хронированный объект является анимацией, то, когда управляющая временная шкала достигает конечной точки, анимация удаляется, т.е. возвращается к своему базовому значению. Однако бывают случаи, когда желательно, чтобы конечное устойчивое состояние анимации замораживалось на последнем значении. Иными словами, временная шкала проходит от 0 к 1 между начальной и конечной точками, но после конечной точки она остается "включенной" вплоть до 1. Это называется поведением "заполнения". На фиг.32 представлена временная шкала с началом = 3, длительностью = 5 и заполнением = "заморожена" (Freeze).

Время течет линейно от значения продвижения 0 до значения продвижения 1, с точки зрения временной шкалы. Однако соотношение между течением времени внутри временной шкалы и внутри ее предка может отличаться от принятой по умолчанию прямой корреляции в том, что время может быть обращено во временной шкале так, что кажется текущим в обратном направлении, темп течения времени может быть увеличен или уменьшен посредством коэффициента, и/или кривая продвижения может быть задумана так, что вместо того, чтобы продвигаться линейно от 0 до 1, она ускоряется от состояния покоя в начальной точке до максимальной скорости продвижения, после чего замедляется до состояния покоя в конечной точке. Это создает эффект "плавного входа, плавного выхода" для любых анимаций, управляемых этой временной шкалой.

В частности, кривая продвижения/времени линейна по умолчанию. При использовании этой линейной кривой для управления определенными анимациями пользователь ощущает эффект "рывка" в начальной и конечной точках, поскольку анимация начинается и оканчивается внезапно. Для таких случаев временную шкалу можно программировать на ускорение течения времени от состояния покоя до максимального темпа с использованием гладкой кривой ускорения. Аналогично, время можно программировать на замедление до нуля вблизи конечной точки. Эффекты ускорения и замедления задают как процентное соотношение длительности фаз ускорения или замедления. Два значения положительны, и их сумма не превышает единицы. На фиг.33 показана временная шкала с началом = 3, длительностью = 10, ускорением = 0,2 и замедлением = 0,4.

Одна прямая манипуляция временем предусматривает программирование временной шкалы на прохождение от значения продвижения нуль до единицы, а затем обратно до нуля. В этом случае временная шкала активна в течение удвоенного заданного периода, один раз - в течение части "вперед" и еще раз - в течение части "назад". На фиг.34 показана временная шкала с началом = 3, длительностью = 5 и "автореверс" (AutoReverse) = истина.

Видимое течение времени для временной шкалы может быть быстрее или медленнее, чем для ее предка с постоянным коэффициентом. По умолчанию, этот коэффициент равен 1, и это значит, что время во временной шкале и ее предке течет с одинаковой скоростью. Если вместо этого значение больше единицы, то время для временной шкалы течет быстрее, чем для ее предка. Например, коэффициент три заставляет временную шкалу проходить между начальной и конечной точками в три раза быстрее заданной длительности. Напротив, если коэффициент составляет от нуля до единицы, то время течет медленнее. Если коэффициент отрицателен, то время во временной шкале всегда кажется текущим назад по отношению к предку. Заметим, что начальное время само по себе является смещением в системе отсчета этой родительской временной шкалы. В результате, в то время как длительность временной шкалы зависит от коэффициента скорости, начальное время не зависит от него. На фиг.35 показана временная шкала с началом = 3, длительностью = 5 и скоростью = 0,5.

Временные шкалы могут быть организованы в древовидную структуру. Каждый документ, кадр или окно имеет некоторую неявную "корневую" временную шкалу, которую можно рассматривать как представляющую объективное время реального мира. Однако время t=0 для корневой временной шкалы - это время создания этой временной шкалы, т.е. загрузки документа, перехода к кадру или открытия окна.

С учетом иерархической природы системы хронирования имеет смысл рассматривать течение времени как происходящее в одной из трех систем отсчета. Простая система отсчета - это система отсчета отдельной временной шкалы. В этой системе отсчета значение продвижения временной шкалы всегда равно 0 при t=0 и 1 при t=d, где d - простая длительность временной шкалы. Длительность временной шкалы всегда задается в простой системе отсчета. Система отсчета родительской временной шкалы - это простая система отсчета для временной шкалы, которая является предком данной временной шкалы. Например, начальное время временной шкалы всегда задается в системе отсчета родительской временной шкалы. Глобальная система отсчета - это простая система отсчета корневой временной шкалы. В этой системе отсчета время t=5 с наступает спустя пять секунд после создания временной шкалы, и длительность 10 с длится в точности в течение десяти секунд реального мира.

Кроме того, к поддеревьям хронирования применяются различные правила управления хронированием, в том числе, если временная шкала активна, ее родительская временная шкала также должна быть активна. Напротив, если временная шкала не активна, то ни один из ее потомков не может быть активен и ни один не может начаться. Если временная шкала в явном виде приостанавливается (посредством вызова метода ITimingControl.Pause), то ее потомки приостанавливаются в неявном виде. При возобновлении этой временной шкалы любой из ее потомков, который не был приостановлен в явном виде, также возобновляется. При запуске временной шкалы (по любой причине, включая прохождение через точку повтора) ее потомки переустанавливаются.

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

Визуальный предок временной шкалы определяется в неявном виде характером использования временной шкалы. Например, если временная шкала управляет цветовой анимацией, которая, в свою очередь, анимирует кисть, используемую в качестве фона некоторого визуала V, то V является "предком-визуалом" этой временной шкалы. Если с этим визуалом связана принятая по умолчанию временная шкала, эта временная шкала является предком нашей исходной временной шкалы в данном примере. В противном случае рекурсивно проверяется предок-визуал. Корень дерева визуалов всегда неявно связан с корневой временной шкалой, поэтому если визуал находится в дереве визуалов, то любые автоудочеренные временные шкалы в нем гарантированно удочеряются где-то в дереве хронирования. Однако если визуал еще не находится в дереве визуалов, то его временные шкалы остаются вне дерева хронирования, пока визуал не будет вставлен в дерево.

Принятая по умолчанию "корневая" временная шкала также задается визуальным материнством, за исключением того, что в этом случае не обязательно использовать ближайший визуальный предок, который имеет временную шкалу. Напротив, при корневом материнстве временная шкала всегда связывается с наивысшим визуалом в дереве (который может быть объектом "кадр" (Frame) или "окно" (Window), или корневым визуалом, связанным с "менеджером визуалов" (VisualMenager)).

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

Временные шкалы и хронированные объекты совместно используют ряд поведений одновременно. Например, анимацию можно приостановить или возобновить, и список анимаций может быть активным и неактивным. Для поддержания согласованности хронированные объекты реализуют один или несколько интерфейсов, которые позволяют обращаться к методам и свойствам хронирования.

Интерфейс ITimingControl реализуется хронированными объектами, которыми можно управлять во время выполнения:

public interface System.Windows.Media.Ammation.ITimingControl
{
// Атрибуты хронирования
double Acceleration {get; set;}
bool AutoReverse {get; set;}
TimeSyncValue Begin {get; set;}
double Deceleration {get; set;}
Time Duration {get; set;}
TimeSync Value End {get; set;}
TimeEndSync EndSync {get; set;}
TimeFill Fill {get; set;}
TimeFill FillDefault {get; set;}
Timeline ParentTimeline {get; set;}
double RepeatCount {get; set;}
Time RepeatDuration {get; set;}
TimeRestart Restart {get; set;}
TimeRestart RestartDefault {get; set;}
double Speed {get; set;}
// Состояние хронирования времени выполнения
int CurrentRepeat {get;}
Time CurrentTime {get;}
void Disable();
void Enable();
bool IsChanging {get;}
bool IsEnabled {get;}
bool IsForwardProgressing {get;}
bool IsOverridingBaseValue {get;}
bool IsPaused {get;}
bool IsReversed {get;}
double Progress {get;}
// Управление хронированием времени выполнения
void BeginIn(Time offset);
void Disable();
void Enable();
void EndIn(Time offset);
void Pause();
void Resume();
void Seek(Time offset, TimeSeekOrigin origin);
void OverrideSpeed(double speed);
// Извещения о событиях
event EventHandler Begun {add; remove;}
event EventHandler Changed {add; remove;}
event EventHandler Ended {add; remove;}
event EventHandler Paused {add; remove;}
event EventHandler Repeated {add; remove;}
event EventHandler Resumed {add; remove; }
event EventHandler Reversed {add; remove;}
event EventHandler Seeked {add; remove;}
}

В нижеследующей таблице приведена семантика интерфейса ITimingControl:

Метод, свойство или событие Смысл
Acceleration (ускорение) Значение между 0 и 1, представляющее долю простой длительности фазы ускорения времени. Сумма этого атрибута и атрибута "замедление" (Deceleration) не может превышать 1.
AutoReverse (автореверс) Если этот атрибут равен "истина", то временная шкала продвигается от начала к концу, а затем сразу же продвигается от конца к началу. В этом случае временная шкала будет активна в течение удвоенного промежутка времени, заданного атрибутом "длительность" (Duration).
Begin (начало) Время, когда должна начаться эта временная шкала. По умолчанию, это время соотносится с начальным временем родительской временной шкалы, но смещение также может быть задано относительно начального или конечного времени какой-то другой временной шкалы. В последнем случае другая шкала удочеряется той же временной шкалой, что и данная шкала.
BeginIn (начать в) Запускает интерактивное начало в заданный момент времени в будущем или прошлом. Параметр находится в системе отсчета родительской временной шкалы этой временной шкалы. Если родительская временная шкала не активна, этот метод ничего не дает.
Begun (началось) Возбуждается всякий раз, когда объект входит в период, когда его внутреннее состояние непрерывно изменяется.
Changed (изменилось) Возбуждается модификатором всякий раз, когда его внутреннее состояние изменяется.
Ended (окончилось) Возбуждается всякий раз, когда объект выходит из периода, когда его внутреннее состояние непрерывно изменяется.
CurrentRepeat (текущее повторение) Текущая итерация временной шкалы, если она повторяется. Первая итерация - это итерация 1. Если IsOverridingBaseValue равно "ложь", это свойство возвращает 0.
CurrentTime (текущее время) Текущее время, локальное для этой временной шкалы. Если IsOverridingBaseValue равно "ложь", это свойство возвращает Time.Unspecified.
Deceleration (замедление) Значение между 0 и 1 представляющее долю простой длительности фазы замедления времени. Сумма этого атрибута и атрибута "ускорение" (Acceleration) не может превышать 1.
Disable (блокировка) Блокирует эту временную шкалу, эффективно удаляя ее из дерева хронирования.
Duration (длительность) Длительность одного периода от начала до конца.
Enable (разблокировка) Разблокирует эту временную шкалу, эффективно вставляя ее в дерево хронирования. Этот метод ничего не дает, если это автоудочеренная временная шкала и предок по умолчанию не задан.
End (конец) Максимальное конечное время для этой временной шкалы. Если это значение меньше суммы свойств "начало" (Begin) и "длительность" (Duration), то период активации обрезается по этому атрибуту. Кроме того, все, что начинается (запланировано или интерактивно) после времени, указанного этим атрибутом, игнорируется.
EndIn (закончить в) Запускает интерактивное окончание в заданный момент времени в будущем или прошлом. Параметр находится в системе отсчета родительской временной шкалы этой временной шкалы. Если родительская временная шкала не активна, этот метод ничего не дает.
EndSync (синхронизация окончания) Этот атрибут используется для задания неявной длительности временной шкалы, которая используется, если атрибут "длительность" (Duration) не задан в явном виде. Неявная длительность временной шкалы может быть задана хронированным объектом, которым она управляет, или другими временными шкалами, которые могут быть удочерены ею.
Fill (заполнение) Поведение временной шкалы после наступления конечного времени. По умолчанию, временная шкала "включена" только от начала до конца, но если этот атрибут задан равным "заморожена" (Freeze), то временная шкала остается включенной после конечного времени. В этом случае значение продвижения после конечного времени равно тому, которое было в конечное время. Возможные значения: "удалена" (Remove) (глобальное значение по умолчанию), "заморожена", "удержана" (Hold), "переход" (Transition) и "авто" (Auto).
FillDefault (заполнение по умолчанию) Значение по умолчанию атрибута Fill. Если атрибут Fill не задан, то этот атрибут используется для определения поведения заполнения. Кроме того, это значение по умолчанию наследуется временными шкалами, удочеренными этой, если не заданы их собственные атрибуты FillDefault. Возможные значения такие же, как для атрибута Fill.
IsChanging (изменяется?) "Истина", если временная шкала активна, "ложь" в противном случае.
IsEnabled (разблокирована?) "Истина", если временная шкала является частью поддерева хронирования, "ложь" в противном случае. Если это свойство равно "истина", это не гарантирует, что само поддерево, частью которого является эта временная шкала, разблокировано.
IsForwardProgressing (продвигается вперед?) "Истина", если продвижение в этой временной шкале идет от 0 к 1 по сравнению с объективным временем. Это свойство учитывает эффект вложенности в потенциально обращенные временные шкалы. Если IsOverridingBaseValue равно "ложь", это свойство возвращает то же значение, которое возвратила бы родительская временная шкала этой временной шкалы.
IsOverridingBaseValue (подменяет базовое значение?) "Истина", если временная шкала активна или в периоде заполнения.
IsPaused (приостановлена?) "Истина", если временная шкала активна, но в приостановленном состоянии.
IsReversed (обращена?) "Истина", если временная шкала находится в обращенном периоде, что видно из собственной локальной системы отсчета временной шкалы. Это свойство не учитывает эффект вложенности в потенциально обращенные временные шкалы. Если IsOverridingBaseValue равно "ложь", это свойство возвращает "ложь".
ParentTimeline (родительская временная шкала) Временная шкала, которая является предком хронирования этой временной шкалы. Это может быть ссылка на любую другую временную шкалу или одно из двух особых опорных значений: Timeline.VisualParent или Timeline.RootTimeline. Если это свойство задано равным Timeline.VisualParent, то эта временная шкала автоудочеряется после использования временной шкалой, связанной с визуалом, в котором она используется (если визуал не имеет соответствующей временной шкалы, принятой по умолчанию, то родительский визуал рекурсивно проверяется). Если оно задано равным Timeline.RootTimeline, то эта временная шкала автоудочеряется после использования "корнем" дерева хронирования.
Pause (пауза) Приостанавливает эту временную шкалу и все ее дочерние временные шкалы. Если эта временная шкала не активна, этот метод ничего не дает.
Paused (приостановлена) Возбуждается временной шкалой всякий раз, когда она или один из ее потомков приостанавливается.
Progress (продвижение) Текущее значение продвижения временной шкалы. Если IsOverridingBaseValue равно "ложь", это свойство возвращает 0 во всех случаях, возвращаемое значение этого свойства всегда составляет от 0 до 1 включительно.
RepeatCount (счет повторений) Сколько раз следует повторить период от начала до конца. Это может быть дробное значение, а также особое значение float.PositiveInfinity для указания того, что временную шкалу следует повторять всегда. Если этот атрибут и атрибут RepeatDuration заданы, полная длительность активности равна минимум двум.
RepeatDuration (длительность повторения) Продолжительность времени, в течение которого нужно повторять период от начала до конца. Это может предусматривать дробный счет повторений или может быть особым значением Time.Indefinite, чтобы указывать, что временная шкала должна повторяться всегда. Если этот атрибут и атрибут RepeatCount заданы, полная длительность активности равна минимум двум.
Repeated (повторена) Возбуждается временной шкалой всякий раз, когда она повторяет свою простую длительность.
Restart (перезапуск) Поведение временной шкалы при достижении второго (или последующего) начального времени. По умолчанию начальное время прерывает любой активный период и возвращается ко времени t=0 для временной шкалы, но если этот атрибут задан равным "когда не активна", то начальное время, которое будет прерывать активный период, игнорируется. Возможные значения: "всегда" (Always), "когда не активна" (WhenNotActive) и "никогда" (Never).
RestartDefault (перезапуск по умолчанию) Значение по умолчанию для атрибута Restart. Если атрибут Restart не задан, то этот атрибут используется для задания поведения перезапуска. Кроме того, это значение по умолчанию наследуется временными шкалами, удочеренными ею, если их собственные атрибуты RestartDefault установлены. Возможные значения такие же, как для атрибута Restart.
Resume (возобновление) Возобновляет эту временную шкалу и все ее дочерние временные шкалы. Если эта временная шкала не активна и приостановлена, этот метод ничего не дает.
Resumed (возобновлена) Возбуждается временной шкалой при всяком ее возобновлении.
Reversed (обращена) Возбуждается временной шкалой всякий раз при изменении направления времени.
Seek (поиск) Изменяет текущее время для этой временной шкалы, которое может влиять на все дочерние временные шкалы. Если эта временная шкала не активна, этот метод ничего не дает.
Seeked (поиск осуществлен) Возбуждается временной шкалой всякий раз, когда ее время изменяется в результате операции поиска.
Speed (скорость) Относительная скорость, с которой время должно течь для этой временной шкалы, по сравнению с ее родительской временной шкалой. Например, значение 1 означает нормальную скорость, а значение 2 означает, что время истекает вдвое быстрее (и поэтому воспринимаемая длительность оканчивается вдвое раньше, чем задано атрибутом Duration). Это значение может быть отрицательным, в каковом случае время течет назад в этой временной шкале, от конечного времени к начальному, как если бы родительская временная шкала была обращена.

Графические сцены можно анимировать, задавая анимированные параметры для некоторых операций визуализации или добавляя анимации в определенные свойства элемента. Анимация - это функция, которая берет некоторый произвольный набор входов (по меньшей мере, один из которых, в общем случае, является временной шкалой) и создает выход надлежащего типа для передачи операции визуализации. Например, "точечная анимация" (PointAnimation) преобразует значение продвижения временной шкалы в тип значения "точка" (Point). В то же время различные операции визуализации, которые берут одно или несколько значений Point в качестве параметров, могут также принимать PointAnimation вместо Point, в каковом случае анимационная функция оценивается в каждом кадре для вычисления Point для использования в этом кадре.

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

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

Прямая анимация интерполирует значение между начальной и конечной точками. Когда заданы начальная и конечная точки, базовое значение игнорируется в течение времени, когда анимация "включена". Когда анимация "отключена", значение свойства может вернуться к базовому значению. Заметим, что анимация "включена", пока "включена" связанная с ней временная шкала. Поэтому анимацию "от и до" можно заставить постоянно подменять базовое значение, задав атрибут хронирования "заполнение" (Fill) равным "заморожена" (Freeze). На фиг.36 показана точка, анимированная по y с ("от") From=10 и ("до") To=70.

Если заданы только начальная или конечная точка, но не обе, базовое значение свойства используется для значения другой точки. Хотя это кажется избыточным с предыдущим примером, ключевое отличие состоит в том, что в данном случае базовое значение не игнорируется, но составляется с анимацией. Это может создавать интересные эффекты, если базовое значение изменяется (поскольку свойство изменяется другим процессом) или если анимация прицеплена к другой анимации.

Другой способ задания анимационной функции состоит в задании отклонения от базового значения. Это, в принципе, аналогично анимации "от и до", которая интерполируется от базового значения к базовому значению плюс отклонение. Однако в этом случае начальная и конечная точки скомпонованы с базовым значением.

Если временная шкала, связанная с анимацией, задана на повторение, анимация проходит от начала до конца несколько раз. На фиг.37 показана точка, анимированная по y с From=10, ("на") By=60 и ("счетом повторений") RepeatCount=2. Вместо повторения одной и той же траектории в каждой итерации, анимацию можно программировать на накопление эффекта каждой итерации, по существу, компонуя с самой собой. На фиг.38 показана точка, анимированная по y с From=10, By=60, RepeatCount=2 и ("накапливающая?") IsAccumulating = "истина".

Хотя поведение по умолчанию анимации "от и до" состоит в игнорировании базового значения анимированного свойства, это поведение может измениться на аддитивное поведение, где значения "от" и "до" являются отклонениями от базового значения.

Тип Выходное значение
От (From) Значение "от" при t=0 и базовое значение при t=1.
До (To) Базовое значение при t=0 и значение "до" при t=1.
От-До (From-To) Значение "от" при t=0 и значение "до" при t=1.
На (By) Базовое значение при t=0 и сумма базового значения и значения "на" при t=1.
От-На (From-By) Значение "от" при t=0 и сумма значений "от" и "на" при t=1.

При основных анимациях задано выходное значение в начальной и конечной точках, и для вычисления значений между ними используется линейная интерполяция. Для более сложных анимационных функций вместо этого может быть задан список значений. Каждое значение соответствует ключевому кадру. В простом случае эти ключевые кадры имеют место с регулярными интервалами. Анимации также можно программировать на использование шаговых промежутков между ключевыми кадрами. В методе шаговой интерполяции промежуток между каждой парой ключевых кадров пропорционален отношению "расстояния" между двумя ключевыми значениями к "полному расстоянию", покрытому анимацией. Это возможно для анимаций тех типов, для которых имеет смысл понятие "расстояние", например, для плавающих или точечных анимаций. В этом случае интерполяция между ключевыми кадрами линейна. Третья возможность состоит в отсутствии какой-либо анимации, в каковом случае функция выходного значения дискретна. На фиг.39 показана точка, анимированная по y с ("ключевыми значениями") KeyValues=10,90,70 и различными методами интерполяции.

Для дополнительного управления, можно в явном виде задавать время для каждого ключевого кадра. Интерполяция между ключевыми кадрами может быть линейной или дискретной. Ключевые моменты времени заданы в виде процентов полной длительности анимации и должны охватывать весь период. Иными словами, первое ключевое время равно 0, и для линейной интерполяции последнее ключевое время равно 1. На фиг.40 показана точка, анимированная по y с KeyValues=10,90,50 и ("ключевыми моментами времени") KeyTimes=0,.2,1.

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

Нижеследующая таблица содержит список атрибутов, относящихся к анимации, и их смысловое содержание. Этот список является шаблоном, которому подчиняются все объекты анимации. Там, где тип атрибута представляет собой "<ValueType>", реальный объект будет предоставлять атрибут с типом, соответствующим типу анимации. Например, объект ColorAnimation ("цветовая анимация") типизирует эти атрибуты как "Color" ("цвет"). Помимо перечисленных ниже атрибутов объекты анимации поддерживают атрибуты, заданные в интерфейсе ITimingAttributes.

Атрибут Тип Смысл
By (на) <ValueType> Значение отклонения в конце анимации. Значение в начале равно либо значению "от", если задано, либо базовому значению свойства.
From (от) <ValueType> Начальное значение анимации.
InterpolationMethod (метод интерполяции) InterpolationMethod Метод, используемый для интерполяции между ключевыми значениями. Возможные значения: "дискретная" (Discrete), "линейная" (Linear), "шаговая" (Paced) или "сплайновая" (Spline).
KeySplines (ключевые сплайны) KeySplineCollection Набор контрольных точек Безье, связанных со списком KeyTimes, который задает кубическую функцию, которая управляет интервалом шаговой анимации. Этот список может содержать на один элемент меньше, чем список KeyTimes. Этот список используется только, когда атрибут InterpolationMethod задан как Spline.
KeyTimes (ключевые моменты) KeyTimeCollection Список значений времени, используемых для управления шаговой анимацией. Этот список должен содержать то же количество элементов, что и список KeyValues. Список упорядочен по возрастанию значений времени, и первое значение в этом списке должно быть 0, а последнее - 1, если InterpolationMethod не задан как Discrete, в каковом случае последнее значение может быть любым меньшим или равным 1.
KeyValues (ключевые значения) <ValueType> KeyValueCollection Список значений для анимации.
To (до) <ValueType> Значение в конце анимации.

Класс "анимируемый" (Animatable) является производным от класса "изменяемый" (Changeable). Он используется как базовый класс любым объектом или коллекцией объектов, который(ую) можно анимировать или может содержать анимированные значения, например мультимедийным ресурсом. "Модификаторы" (Modifiers), "хронированные модификаторы" (TimeModifiers) и "анимации" (Animations) являются производными от Changeable, а не от Animatable, поскольку их индивидуальные свойства не являются анимируемыми.

public abstract class Animatable : Changeable
{
public abstract bool HasAnimations {get;}
public abstract bool IsAnimating {get;}
public abstract bool IsOverridingBaseValue {get;}
public abstract Animatable GetCurrentValue();
public abstract void SetDefaultParentTimeline(Timeline defaultParentTimeline);
}

Метод, свойство или событие Смысл
HasAnimations (имеет анимации?) "Истина", если объект может изменяться со временем. В общем случае, это свойство равно "истина", если объект присоединен к любым коллекциям анимаций.
IsAnimating (анимируется?) "Истина", если любые из анимаций в объекте изменяются (см. Modifier.IsChanging).
IsOverridingBaseValue (подменяет базовое значение?) "Истина", если любые из анимаций в объекте изменяются или находятся в состоянии заполнения и потому в данный момент активны и изменяют объект.
GetCurrentValue (получить текущее значение) Возвращает объект, который имеет такое же значение, как мгновенное значение этого объекта, но который не изменяется со временем. Если свойство DoesChange равно "ложь", свойство CurrentValue может возвращать сам объект, а не новую копию.
SetDefaultParentTimeline (задать используемую по умолчанию родительскую временную шкалу) Временная шкала, которая является предком для любых автоудочеренных временных шкал. Если это свойство задано, любые автоудочеренные временные шкалы переудочеряются, но новый клон для временных шкал либо для этого объекта не создается.

Классы Modifiers, а, стало быть, и классы TimedModifier и Animation являются производными от Changeable, а не от Animatable ("анимируемого объекта"), поскольку их индивидуальные свойства не должны анимироваться. Это обуславливает тот факт, что программисты не должны иметь возможность анимировать свойство "от" (From) на анимации.

Классы "модификатор" (Modifier) не могут иметь значение свойства StatusOfNextUse, равное "неизменяемое" (Unchangeable). Значение по умолчанию для StatusOfNextUse для "модификаторов" равно ChangeableCopy, однако оно также может быть задано равным ChangeableReference, если пользователь желает повторно использовать Modifier. Если пользователь задает StatusOfNextUse равным ChangeableReference, возникает исключение, если ни для какого присоединенного Modifier не задано свойство "родительская временная шкала" (ParentTimeline). Это предотвращает ситуации наличия конфликтующих унаследованных родительских временных шкал. Неанимированные, неизменяемые ветви Animatable могут иметь значение StatusOfNextUse, равное Unchangeable, и могут быть сделаны неизменяемым после использования.

Такие свойства класса Modifier, как "от" (From), "до" (To) или "на" (By) остаются изменяемыми в течение времени жизни Modifier.

"Модификаторы" являются изменяемыми на протяжении своего времени жизни, поэтому MakeUnchangeable ("сделать неизменяемым") будет вызывать исключение для этих классов. Для Animatable, который в данный момент содержит анимацию, MakeUnchangeable будет вызывать исключение.

Если пользователь подписывается на извещения "изменилось" (Changed) на классе Animatable, пользователь будет получать извещения об изменениях, вызванных либо изменениями свойств, либо по природе анимации. Таким образом, пользователь будет получать извещения об изменениях, когда временные шкалы, связанные с анимациями, используемыми Animatable, являются Seeked или перемещаются вперед, поскольку они присутствуют на каждом представляемом кадре.

В случае независимо анимированных свойств (например, "непрозрачность" (Opacity)) или объектов Animatable (например, SolidColorBrush) извещения Changed, отправленные любому пользователю, который обеспечил обработчик, будут появляться с частотой кадров потока ПИ, а не с частотой кадров составителя. В этом случае не гарантируется, что точное значение анимации является тем, что имеется на экране, хотя значения должны быть близкими.

Если анимация является зависимой или MIL-зависимой, возможно получить значение, которое совпадает с тем, которое будет на экране, хотя в данный момент невозможно сказать, какое извещение соответствует проходу визуализации и потому какое из них отражает значение, близкое к отображаемому. Если дерево хронирования изменяется в течение прохода визуализации, что происходит очень часто, возможно, что пользователь будет получать множественные извещения, и потому еще менее вероятно, что пользователь будет знать, какое из них соответствует окончательному значению на экране.

Модификатор - это объект, который реализует метод GetValue (получить значение), который берет в качестве входа объект, именуемый "базовым значением", определенного типа и возвращает другой объект того же типа, что и вход. Значение выхода зависит как от входа, так и от внутреннего состояния модификатора. В частности, это значит, что при вызове GetValue более одного раза не гарантируется возвращение того же выхода. Графическая анимация происходит, когда метод GetValue модификатора вызывается один раз за кадр, создавая новое значение для каждого кадра.

В общем случае, нет никаких гарантий относительно возвращаемого значения метода GetValue, и всякий раз при вызове метода может возвращаться другое значение. Объекты, потребляющие модификаторы, могут предполагать, что так и есть, и повторно вызывать модификатор, как в следующем примере:

class MyObject
{
private Modifier myModifier;
private object myBaseValue;
public DoSomeWork()
{
object current Value = myModifier.GetValue(myBase Value);
DoSomethingWithCurrentValue(currentValue);
PostCallbackToDoSomeWork(); // приводит к повторному вызову этого метода
}
}

Однако на практике бывают случаи, когда ожидается, что модификатор будет выдавать один и тот же выход при одном и том же входе, в зависимости от внутреннего состояния. Модификатор называют "изменяющимся", когда он находится в периоде, в котором значение GetValue может быть другим при каждом вызове. Он является "неизменяющимся", когда возвращаемое значение GetValue одно и то же при каждом вызове. Если модификатор является "неизменяющимся", пользователь этого модификатора может безопасно кэшировать возвращенное значение метода GetValue и, возможно, избежать повторного и ненужного оценивания метода GetValue, как в следующем примере:

class MyObject
{
private Modifier myModifier;
private object myBase Value;
public Initialize()
{
myModifier.ChangeBegun += new EventHandler(this.OnChangeBegun);
}
public DoSomeWork()
{
object currentValue = myModifier.GetValue(myBase Value);
DoSomethingWithCurrentValue(currentValue);
if(myModifier.IsChanging)
{
PostCallbackToDoSomeWork(); // Делать больше работы быстро
}
else
{
// Ничего не делать, поскольку модификатор не собирается
// изменяться в ближайшее время. Если он начнет изменяться,
// вышеупомянутый делегат перезапустит наш цикл обработки.
}
}
public OnChangeBegun()
{
// Модификатор снова изменяется, поэтому снова начать работать
// по расписанию
PostCallbackToDoSomeWork();
}
}

Реализуется абстрактный класс "модификатор" (Modifier), от которого должны наследоваться модификаторы. Этот класс обеспечивает принятые по умолчанию реализации для всех методов, кроме GetValue и GetUniqueInstance:

public abstract class System. Windows.Media.Animation.Modifier
{
public virtual Timeline ParentTimeline {get; set;}
public virtual bool IsChanging {get;}
public virtual bool IsOverridingBaseValue {get;}
Modifier abstract GetUniqueModifierInstance(Timeline defaultParentTimeline);
object abstract GetValue(object baseValue);
public virtual bool UsesBaseValue {get;}
// Извещения о событиях
public virtual event EventHandler Changed {add; remove;}
}

В нижеследующей таблице приведена семантика класса Modifier:

Метод, свойство или событие Смысл
Changed (изменилось) Возбуждается модификатором всякий раз при изменении внутреннего состояния.
ParentTimeline (родительская временная шкала) Временная шкала, которая является предком любых автоудочеренных временных шкал в этом модификаторе. Если это свойство задано, то любые автоудочеренные временные шкалы в этом модификаторе переудочеряются новой родительской временной шкалой.
GetUniqueInstance (получить уникальный экземпляр) Возвращает экземпляр этого модификатора, который может поддерживать свое собственное состояние времени выполнения отдельно от других экземпляров. Если этот модификатор содержит автоудочеренные временные шкалы, то возвращенный экземпляр имеет эти временные шкалы удочеренными временной шкалой, переданной ему в качестве параметра.
GetValue (получить значение) Вычисляет текущее выходное значение этого модификатора на основании базового значения, переданного ему в качестве аргумента, и внутреннего состояния модификатора. Когда свойство IsOverridingBaseValue равно "ложь", эта функция гарантированно возвращает базовое значение.
IsChanging (изменяется?) "Истина", если модификатор в данный момент изменяется, "ложь", если он находится в периоде отсутствия изменений. Этот флаг лучше всего использовать в сочетании с событиями ChangeBegun ("изменение началось") и ChangeEnded ("изменение закончилось"). Если этот флаг равен "истина", IsOverridingBaseValue также должно быть равно "истина".
IsOverridingBaseValue (подменяет базовое значение?) "Истина", если возвращаемое значение метода GetValue в данный момент подвергается действию модификатора. Когда это значение равно "ложь", GetValue гарантированно возвращает тот же объект, который передан ему в качестве аргумента. Заметим, что модификатор может быть подменяющим базовое значение, но не изменяющим.
UsesBaseValue (использует базовое значение?) "Истина", если возвращаемое значение метода GetValue зависит от базового значения. Если это свойство равно "ложь", это значит, что модификатор вовсе игнорирует базовое значение. Если модификатор находится в списке, это свойство допускает оптимизацию, где в некоторых случаях нужно оценивать только подмножество модификаторов.

Кроме того, реализуется набор классов, зависящих от типа, которые наследуются от Modifier, но предоставляют сохраняющие тип версии методов интерфейса. В нижеследующем примере показан класс FloatModifier:

public abstract class System.Windows.Media.Animation.FloatModifier : Modifier
{
// Методы, зависящие от типа
public sealed override object GetValue(object base Value)
{
return GetValue((float)base Value);
}
public abstract float GetValue(float base Value);
}

Хронированный модификатор - это модификатор, чьим поведением, по меньшей мере, частично управляет объект "временная шкала" (TimeLine). Применяются вышеупомянутые правила модификатора, но дополнительно хронированный модификатор реализует интерфейс ITimingControl для предоставления управления временной шкалы модификатора. Нет никаких абстрактных классов TimeModifier. Вместо этого классы, зависящие от конкретного типа, наследуются от классов Modifier, зависящих от конкретного типа. В нижеприведенном примере показан класс FloatTimedModifier:

public abstract class System.Windows.Media.Animation.FloatTimedModifier :
FloatModifier, ITimingControl
{
protected FloatTimedModifier(FloatTimedModifier example);
// Методы, свойства и события FloatModifier
public override Timeline ParentTimeline {get; set;}
public override bool IsChanging {get;}
public override bool IsOverridingBaseValue {get;}
public override FloatModifier GetUniqueInstance(Timeline defaultParentTimeline);
public override event EventHandler Changed {add; remove;}
// Методы, свойства и события ITimingControl
double Acceleration {get; set;}
bool AutoReverse {get; set;}
TimeSyncValue Begin {get; set;}
double Deceleration {get; set;}
Time Duration {get; set;}
TimeSync Value End {get; set;}
TimeEndSync EndSync {get; set;}
TimeFill Fill {get; set;}
TimeFill FillDefault {get; set;}
Timeline ParentTimeline {get; set;}
double RepeatCount {get; set;}
Time RepeatDuration {get; set;}
TimeRestart Restart {get; set;}
TimeRestart RestartDefault {get; set;}
double Speed {get; set;}
int CurrentRepeat {get;}
Time CurrentTime {get;}
bool IsForwardProgressing {get;}
bool IsPaused {get;}
bool IsReversed {get;}
double Progress {get;}
void BeginIn(Time offset);
void EndIn(Time offset);
void Pause();
void Resume();
void Seek(Time offset, TimeSeekOrigin origin);
event EventHandler ChangeBegun {add; remove;}
event EventHandler ChangeEnded {add; remove;}
event EventHandler Paused {add; remove;}
event EventHandler Repeated {add; remove;}
event EventHandler Resumed {add; remove;}
event EventHandler Reversed add; remove;}
event EventHandler Seeked {add; remove;}
//Данные
protected Timeline Timeline;
}

Заметим, что Modifier и интерфейс ITimingControl имеют некоторые сходные методы, свойства и события. TimedModifier предоставляет для них единую реализацию. TimedModifier свободно реализует ITimingControl, перенаправляя все вызовы управляющей временной шкале, хотя делать это не обязательно. Реализация по умолчанию ITimingControl, обеспеченная реализациями TimedModifier, зависящего от конкретного типа, перенаправляет вызовы управляющей временной шкале.

Анимация - это хронированный модификатор, реализующий конкретную анимационную функцию.

public sealed class System. Windows.Media.Animation.FloatAnimation :
FloatTimedModifier
{
public FloatAnimation(float from, float to, Time duration);
public FloatAnimation(float from, float to, Time duration, TimeFill fill);
public FloatAnimation(float to, Time duration);
public FloatAnimation(float to, Time duration, TimeFill fill);
// Унаследованы все методы, свойства и события FloatTimedModifier,
// плюс добавлено следующее:
public float By {get; set;}
public float From {get; set;}
public InterpolationMethod InterpolationMethod {get; set;}
public bool IsAccumulating {get; set;}
public KeySplineEnumerator KeySplines {get; set;}
public KeyTimeEnumerator KeyTimes {get; set;}
public FloatKeyValueEnumerator Key Values {get; set;}
public float To {get; set;}
}

Коллекция анимаций - это список объектов анимации (наследуемый от <Type>Modifier), где выход метода GetValue из первого объекта используется в качестве параметра базового значения для метода GetValue на втором объекте и т.д. Для гибкости, объекты, содержащиеся в коллекции анимаций, фактически относятся к типу Modifier, зависящему от конкретного типа. Коллекция как целое поддерживает метод GetValue, который выглядит как IModifier.GetValue. Фактически, коллекции анимаций поддерживают большую часть интерфейса IModifier, но они в действительности не реализуют IModifier, поскольку они не поддерживают свойство "UsesBaseValue" ("использует базовое значение?") (это свойство всегда предполагается равным "истина" для коллекции как целого).

public sealed class System.Windows.Media.Animation.FloatAnimationCollection :
ICollection
{
public Timeline DefaultParentTimeline {get; set;}
public bool IsChanging {get;}
public bool IsOverridingBaseValue {get;}
public FloatAnimationCollection GetUniqueInstance(Timeline
defaultParentTimeline);
public float GetValue(float base Value);
// Уведомления о событиях
public event TimeEventHandler ChangeBegun { add; remove;}
public event TimeEventHandler Changed { add; remove; }
public event TimeEventHandler ChangeEnded { add; remove; }
//Поддержка MPE ICollection
public FloatModifier this[int index] {get;}
}

События, инициированные из коллекций анимаций, объединяются.

Анимации пути являются специализацией класса TimedMatrixModifier ("хронированный матричный модификатор"). "Матричный модификатор" (MatrixModifier) можно использовать совместно с "матричным преобразованием" (MatrixTransform). MatrixTransform имеет свойство "матрица" (Matrix) и свойство "матричные анимации" (MatrixAnimations), и, поскольку "анимация пути" (PathAnimation) является MatrixModifier, ее можно использовать в качестве "матричной анимации" (MatrixAnimation).

public sealed class System. Windows.MediaAnimation.PathAnimation :
TimedMatrixModifier
{
public PathGeometry PathGeometry {get; set;}
public bool DoesRotateWithTangent {get; set;}
}

Метод, свойство или событие Смысл
Geometry (геометрия) Это может быть любая геометрия. Для эллипса выбирается соответствующая начальная точка для продвижения 0. Если геометрия имеет много подгеометрий, каждый из их путей будет перемещаться вдоль по очереди, в порядке, в котором они были заданы внутри геометрии.
DoesRotateWithTangent Если свойство задано равным "ложь", объект будет перемещаться вдоль пути геометрии без всякого вращения. Если оно задано равным "истина", объект будет повернут для совмещения с касательной к пути в любом данном месте.

Использование разметки:

<Canvas>
<Canvas.TransformEffect>
<MatrixTransform> <!-- по умолчанию базовое значение Matrix равно
Тождественное преобразование -->
<MatrixTransform.Matrix>
<PathAnimation Begin="0" Duration="10" DoesRotateWithTangent="true"/>
<PathAnimation.Geometry>
<PathGeometry> ... </PathGeometry>
</PathAnimation.Geometry>
</PathAnimation>
</MatrixTransform.Matrix>
</MatrixTransform>
</Canvas.TransformEffet>
</Canvas>

Каждый ресурс, метод или объект, который можно анимировать, подчиняется ряду правил, включая то, которое реализует интерфейс Animatable. Для каждого анимируемого свойства (или параметра), именуемого "Foo" ("нечто"), типа "Bar" ("что-то"), имеется другое свойство (или параметр), именуемое "FooAnimations", типа "BarAnimationCollection". Всякий раз, когда нужна анимация, используются коллекции анимаций. Основные "модификаторы" или объекты Animation не используются напрямую, потому что это препятствует композиции анимации.

Ресурсы можно анимировать путем добавления коллекций анимаций в отдельные свойства. Следующий пример показывает, как создавать "кисть чистого цвета" (SolidColorBrush) с анимированным цветом:

ColorAnimation anim = new ColorAnimation();
animBuilder.From = new Color(1.0f, 0.0f, 0.0f, 0.0f);
animBuilder.Begin = new Time(0);
animBuilder.To = newColor(1.0f, l.0f, 0.0f, 0.0f);
animBuilder.Duration = new Time(1000);
animBuilder.AutoReverse = true;
animBuilder.RepeatDuration = Time.Indefinite;
SolidColorBrush brush = new SolidColorBrush();
brush.Color = new Color(1.0f, 0.0f, 0.0f, 0.0f);
brush.ColorAnimations = anim;

Анимационные ресурсы можно использовать в операциях визуализации или в качестве значений для свойств "элемента" (Element).

Операцию визуализации можно анимировать путем добавления коллекций анимаций в вызовы метода контекста рисования или путем использования анимационных ресурсов. Следующий пример показывает, как проталкивать анимированное значение непрозрачности в контекст рисования:

FloatAnimation anim = new FloatAnimation();
anim.From = 0.0f;
anim.Begin = Time.Immediately;
anim.To= 1.0f;
anim.Duration = new Time(1000);
anim.Fill = TimeFillFreeze;
myDrawingContext.PushOpacity(0.0f, anim);

Элементы можно анимировать путем добавления коллекций анимаций в свойства Element. Следующий пример показывает, как анимировать ширину кнопки в C#:

LengthAnimation anim = new LengthAnimation();
anim.From = new Length(50);
anim.Begin = Time.Immediately;
anim.To = new Length(100);
anim.Duration = new Time( 1000);
anim.Acceleration = 0.2;
anim.Deceleration = 0.2;
anim.Fill = TimeFill.Freeze;
myButton.Width = new Length(50);
myButton.WidthAnimations = anim;

Ниже показан тот же пример в XAML:

<Button ID="myButton" Width="50">
<Button.Width>
<LenthAnimationCollection>
<LengthAnimation
From="50"
Begin="Immediately"
To="100"
Duration="1"
Acceleration="0.2"
Deceleration="0.2"
Fill="Freeze"
/>
</LengthAnimationCollection>
</Button.Width>
</Button>

Всякий раз при использовании анимации (или анимированного ресурса) анимация (или ресурс) клонируется (неглубоким, эффективным способом) для предоставления пункту назначения уникальной, независимо клонируемой временной шкалы. Побочный эффект этого поведения состоит в том, что исходная анимация не является частью визуальной сцены и поэтому не отвечает на управляющие вызовы через интерфейс ITimingControl. Для достижения этого эффекта вызывающий код сначала использует анимацию, а затем считывает анимацию обратно. Считанное обратно значение можно затем кэшировать и использовать для управления хронированием. Нижеследующий пример показывает схему, которой может подчиняться код, предназначенный для управления анимациями:

private FloatAnimation myOpacityAnimation;
public void Initialize()
{
FloatAnimation anim = new FloatAnimation();
// Задать свойство Begin равным Indefinite, поскольку мы хотим начать
// эту анимацию интерактивно, а не автоматически.
anim.Begin = Time.Indefinitely;
anim.From = 1.0f; // Полностью непрозрачна
anim.Duration = new Time(500); // полсекунды
anim.To = 0.5f; // Полупрозрачная
anim.AutoReverse = true;
// Анимировать непрозрачность некоторого элемента, которым мы обладаем
myElement.Opacity = 1.0f;
myElementOpacityAnimations = anim;
// ОШИБКА: следующая строка не имеет предусмотренного результата:
// myOpacityAnimation = animation;
//
// Эта строка кэширует "шаблон" анимации, а не фактическую анимацию,
// которая управляет непрозрачностью элемента.
// Это кэширует правильную анимацию - ту, которая действительно
используется:
myOpacityAnimation = (FloatAnimation)myElement.OpacityAnimations[0];
}
public void OnSomeEvent()
{
// Всякий раз, когда мы обнаруживаем какое-то событие, обеспечить "мигание" элемента
myOpacityAnimation.BeginIn(0);
}

Пользователь может создать новый класс, используя "эффект анимации" (AnimationEffect) в качестве базового класса для реализации AnimationEffect. Пользователю также потребуется создать построитель для своего AnimationEffect.

public abstract class AnimationEffect: Changeable
{
protected AnimationEffect(AnimationEffect effect);
public abstract AnimationEffect GetUniqueInstance(Timeline defaultParent Timeline);
protected void Invalidate();
protected void InvalidatePassive();
protected bool IsInvalid {get;}
protected Element Element {get;}
protected virtual void AttachImpl();
protected virtual void DetachImpl();
protected virtual void PreLayoutReadImpl();
protected virtual void PreLayoutWriteImpl();
protected virtual void PostLayoutReadImpl();
}

Метод, свойство или событие Смысл
Invalidate Пользователь вызывает его, когда хочет, чтобы его AnimationEffect, помещенный в список "эффектов анимации", обрабатывался в течение следующего RenderQueueItem, и хочет удостовериться, что RenderQueueItem запланирован. Список непригодных анимаций повторно задается в начале RenderQueueItem.
InvalidatePassive Пользователь вызывает его, когда хочет, чтобы его AnimationEffect, помещенный в список "эффектов анимации", обрабатывался в течение следующего RenderQueueItem, но не хочет, чтобы RenderQueueItem был запланирован.
IsInvalid Возвращает "истину", если анимация в данный момент находится в списке "эффектов анимации", подлежащих обработке в течение следующего RenderQueueItem. Он может быть равен "истина", поскольку был вызван Invalidate.
Element Это "элемент", к которому присоединен AnimationEffect. Если AnimationEffect присоединен к "элементу", возникнет исключение. Пользователь не должен производить никакой установки, пока не будет вызван OnAttach.
AttachImpl Когда AnimationEffect присоединен к элементу, он автоматически клонируется, и новый клон добавляется в коллекцию "эффектов анимации" на элементе, и вызывается OnAttach. В этот момент будет задано защищенное свойство "элемента" на AnimationEffect. Если пользователь добавил AnimationEffect в коллекцию, вызывать OnAttach будет только новый AnimationEffect. Не гарантируется, что при вызове OnAttach свойства разметки "элемента" будут заданы или все потомки "элемента" будут на месте. AnimationEffect клонируется. Хотя "элемент" можно передавать "эффекту анимации" при вызове каждой функции, его нельзя передавать обработчикам событий для событий от других элементов, которые будут там, где "эффекту анимации" больше всего нужно. "Эффект анимации" может задавать обработчики событий на других элементах, но ему все же нужно будет знать то, что он присвоил этому "элементу".
DetachImpl Будет вызываться на "эффекте анимации" (AnimationEffect) при его отсоединении от "элемента".
PreLayoutReadImpl Будет вызываться на "эффекте анимации" (AnimationEffect), если он забракован, перед тем как мы запустим схему в RenderQueueItem. Это момент, когда "эффект анимации" должен считывать нужные ему значения. Причиной того, что считывание и запись разделены, является то, что считывание предусматривает немедленный запуск компоновки, и, если каждый AnimationEffect считывает и записывает по очереди, это будет замедлять процесс в целом.
PreLayoutWriteImpl Будет вызываться на "эффекте анимации" (AnimationEffect), если он забракован, перед тем как мы запустим компоновку в RenderQueueItem. Хотя мы не гарантируем порядок, в котором будут обрабатываться "эффекты анимации", мы гарантируем, что все забракованные "эффекты анимации" вызовут OnPreLayoutRead прежде, чем это будет вызвано.
PostLayoutReadImpl Будет вызываться на "эффекте анимации" (AnimationEffect), если он забракован, после того как мы запустим компоновку в RenderQueueItem. Если флаг IsAlwaysDirty не установлен, флаг отбраковки на этом "эффекте анимации" будет задан равным "ложь", и он будет удален из списка "эффектов анимации", подлежащих обработке в течение следующего RenderQueueItem. Если "эффект анимации" вызывает SetDirty в этом методе, он будет эффективно оставлять его забракованным в течение следующего RenderQueueItem. Если "эффект анимации" пожелает остаться забракованным, гораздо эффективнее задать флаг IsAlwaysDirty.

ТИПЫ ПРИМИТИВОВ

Базовая единица длины в MIL представляет собой число с плавающей точкой c двойной точностью (double), в результате чего другие типы примитивов и API основаны на числах с плавающей точкой с двойной точностью. В общем случае, эти числа с плавающей точкой с двойной точностью оцениваются как пользовательские единицы, которые первоначально равны 1/96 дюйма. Для цветов каждый из цветовых каналов представляется числом с плавающей точкой, а не числом с плавающей точкой с двойной точностью. Для измерения углов значения чисел с плавающей точкой с двойной точностью выражаются в градусах. Когда число с плавающей точкой или число с плавающей точкой с двойной точностью оценивается как измерение времени, оно выражается в секундах.

Структура Time ("время") представляет конкретный момент времени или промежуток времени. Кроме того, особое значение времени, именуемое "Indefinite" ("неопределенное"), представляет либо бесконечно удаленный в будущее момент времени, либо бесконечно долгий промежуток времени. Значения времени предназначены для использования в системе свойств, поэтому для очистки этого свойства или для явного указания, что свойство не задано, можно использовать особое значение, именуемое "Unspecified" ("незаданное"). Значения времени внутренне хранятся в виде целочисленных счетчиков:

время:
(полное значение часов | частичное значение часов |
значение счетчика времени | особое значение)
полное значение часов:
часы ":" минуты":" секунды ("." дробь)?
частичное значение часов:
минуты ":" секунды ("."дробь)?
значение счетчика времени:
счетчик времени ("."дробь)? (метрика)?
особое значение:
("неопределенное" | "незаданное")
единицы измерения:
"ч" | "мин" | "с" | "мс"
часы:
цифра+
минуты:
2цифры
секунды:
2цифры
дробь:
цифра+
счетчик времени:
цифра+
2цифры:
цифра цифра
цифра:
"0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|9"

Помимо вышеупомянутой грамматики, заметим, что "минуты" и "секунды" необходимо задавать в диапазоне от "00" до "59", чтобы они считались действительными. Кроме того, если формат "значения счетчика времени" используется без единиц, предполагается, что значение выражено в секундах. Ниже приведено несколько примеров значений "времени" и их смысловой нагрузки:

Время Значение
02:30:03 2 часа, 30 минут и 3 секунды
50:00:10,25 50 часов, 10 секунд и 250 миллисекунд
02:33 2 минуты и 33 секунды
00:10.5 10 секунд и 500 миллисекунд
3,2 ч 3 часа и 12 минут
45 мин 45 минут
30с 30 секунд
5,45 мс 5,45 миллисекунд
12,467 12 секунд и 467 миллисекунд
1 д 1 день

Структура "время" (Time) используется для сохранения единичного значения "времени":

public struct System.Windows.Media.Animation.Time : IComparable
{
public Time(int milliseconds);
public bool IsFinite {get;}
public static Time Abs(Time t);
public int CompareTo(Time other);
public override bool Equals(object obj);
public override int GetHashCode();
public static Time Max(Time a, Time b);
public static Time Min(Time a, Time b);
public override string ToString();
public static Time operator +(Time a, Time b);
public static Time operator -(Time t);
public static Time operator -(Time a, Time b);
public static Time operator *(double s, Time t);
public static Time operator *(Time t, double s);
public static Time operator /(Time t, double s);
public static double operator /(Time a, Time b);
public static Time operator %(Time a, Time b);
public static bool operator ==(int n, Time time);
public static bool operator ==(Time time, int n);
public static bool operator ==(Time a, Time b);
public static bool operator !=(int n, Time time);
public static bool operator !=(Time time, int n);
public static bool operator !=(Time a, Time b);
public static bool operator >=(Time a, Time b);
public static bool operator <=(Time a, Time b);
public static bool operator >(Time a, Time b);
public static bool operator <(Time a, Time b);
public static readonly Time Immediately;
public static readonly Time Indefinite;
public static readonly int MaxValue;
public static readonly int MinValue;
public static readonly Time Unspecified;
}

Ниже приведены другие базовые типы, в которых используется следующая система обозначений:

- *: 0 или больше

- +: 1 или больше

- ?: 0 или 1

- {n}: n раз

- (): группирование

- |: разделяет альтернативы

- двойные кавычки окружают литералы

пп:
пробел+
цифра:
"0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
шестнадцатеричная цифра:
digit | "a" | "A" | "b" | "B" | "c" | "C" | "d" | "D" | "e" | "E" | "f" | "F"
цифровая последовательность:
цифры+
знак:
"-"|"+"
экспонента:
("e" | "E") знак? цифровая последовательность
дробная константа:
(цифровая последовательность? "." цифровая последовательность) | (цифровая последовательность ".")
константа с плавающей точкой:
(экспонента дробной константы ?) | (экспонента цифровой последовательности)
целочисленная константа:
цифровая последовательность
запятая:
","
запятая-пп:
(пп запятая? пп*) | (запятая пп*)
число:
(знак? (целочисленная константа | константа с плавающей точкой)) | "незаданное"
число-пп:
число пп*
координата:
число-пп
пара координат:
координата запятая-пп координата
пары координат:
(пара координат запятая-пп )* пара координат
имя файла:
пп* ("'" пригодное имя файла-символ+ "'" | пригодное имя файла-символ-без пустого пространства ) пп*

Синтаксис разметки для цветов:

числовой цвет:
"#" (шестн.цифра{3} | шестн.цифра {4} | шестн.цифра {6} | шестн.цифра {8} )
цвет:
числовой цвет | [a-zA-Z]+

Объект "цвета" (Colors) содержит статические члены, содержащие многие общеизвестные цвета, например красный и синий:

Структура "точка" (Point) описана ниже:

public struct System.Windows.Point
{
public Point(); // инициализируется на 0,0
public Point(double x, double y);
public static Boolean Equals(Point point1, Point point2)
public virtual Boolean Equals(Object o)
public Double X {get; set;}
public Double Y {get; set;}
public virtual Int32 GetHashCode()
public static Point operator+(Point point, Vector vector)
public static Boolean operator==(Point point1, Point point2)
public static Boolean operator!=(Point point1, Point point2)
public static Point operator*(Point point, Matrix matrix)
public static Point operator-(Point point, Vector vector)
public static Vector operator-(Point point1, Point point2)
// Будет брать абсолютные значения X и Y, поскольку Size не может быть
// отрицательным
public static explicit operator Size(Point point) //
public static explicit operator Vector(Point point)
public static Point Add(Point point, Vector vector)
public static Point Multiply(Point point, Matrix matrix)
public Void Offset(Double dx, Double dy)
public static Point Subtract(Point point, Vector vector)
public static Vector Subtract(Point point1, Point point2)
public virtual String ToString()
}

Синтаксис разметки для объекта "точка":

точка:
пара координат

Объект "вектор" (Vector) задан ниже:

public struct System.Windows.Vector
{
public Vector(); // инициализируется на 0,0
public Vector(double x, double y);
public double X {get; set;}
public double Y {get; set;}
public static Boolean Equals(Vector vector1, Vector vector2)
public virtual Boolean Equals(Object obj)
public Double Length {get;}
public Double LengthSquared {get;}
public Double X {get; set;}
public Double Y {get; set;}
public virtual Int32 GetHashCode()
public static Point operator+ (Vector vector, Point point)
public static Vector operator+ (Vector vector1, Vector vector2)
public static Vector operator/ (Vector vector, Double scalar)
public static Boolean operator==(Vector vector1, Vector vector2)
public static Boolean operator!= (Vector vector1, Vector vector2)
// возвращает скалярное произведение: vector1.X*vector2.X + vector1.Y*vector2.Y
public static Double operator*(Vector vector1, Vector vector2)
public static Vector operator* (Double scalar, Vector vector)
public static Vector operator* (Vector vector, Double scalar)
public static Vector operator* (Vector vector, Matrix matrix)
public static Vector operator-(Vector vector1, Vector vector2)
public static Vector operator- (Vector vector)
// Будет брать абсолютные значения X и Y, поскольку Size не может быть
// отрицательны
public static explicit operator Point (Vector vector)
public static explicit operator Size (Vector vector)
public static Vector Subtract(Vector vector1, Vector vector2)
public static Double Multiply(Vector vector1, Vector vector2)
public static Vector Multiply(Double scalar, Vector vector)
public static Vector Multiply(Vector vector, Double scalar)
public static Vector Multiply(Vector vector, Matrix matrix)
public static Vector Divide(Vector vector, Double scalar)
public static Point Add(Vector vector, Point point)
public static Vector Add(Vector vector1, Vector vector2)
public Void Normalize()
// возвращает угол, необходимый для поворота v1 в v2, в градусах
// Будет возвращать значение в диапазоне (-180, 180] градусов
public static Double AngleBetween(Vector vector1, Vector vector2)
public static Double CrossProduct(Vector vector1, Vector vector2)
// возвращает Determinant: vector1.X*vector2.Y - vector1.Y*vector2.X
public static Double Determinant(Vector vector1, Vector vector2)
public virtual String ToString()
}

Синтаксис разметки для объекта "вектор":

размер:
пара координат

Заметим, что размер - это не Vector, и поэтому его нельзя прибавлять, вычитать или преобразовывать. Кроме того, размер не может иметь отрицательную ширину или высоту. При попытке сделать это, будет возникать "исключение, обусловленное аргументами" (ArgumentException).

В отношении прямоугольников, вместо имени Rectangle используется имя Rect, во избежание конфликтов с элементом Rectangle ("прямоугольник"). Хотя возможно задавать отрицательную ширину и высоту, нет стандартного способа нормализовать прямоугольник, и многие операции на прямоугольнике могут давать неочевидные результаты. "Ширину" (Width) и "высоту" (Height) Rect нельзя задавать равными неотрицательным значениям. Если такие значения передаются конструктору, результат будет нормализован. Если Width или Height задать отрицательными, это приведет к ArgumentException.

Rect содержит точки внутри него, а также точки, лежащие на его сторонах. Таким образом, стороны Rect включены. Следовательно, Rect с Width = 0 или Height = 0 не пуст, поскольку он содержит множество точек, находящихся на отрезке одномерной линии, представленном посредством Rect. Если Width и Height равны 0, то Rect содержит одну "точку" (Point) в "положении" (Location). Это значит, что истинно пустой Rect - это особое значение, которое можно присвоить через статическое свойство EmptyRect. Свойства "ширина" и "высота" EmptyRect будут возвращать положительную бесконечность, и ни одно из этих свойств не будет изменяемым. Это сделано для того, чтобы гарантировать отсутствие "пустой" ширины при нормальной высоте или наоборот.

public struct System. Windows.Rect
{
public Rect(); // Задает все значения равными 0
public Rect(Point location, Size size);
public Rect(double x, double y, double width, double height);
// Оба будут нормализовать прямоугольник
public Rect (Point point1, Point point2);
public Rect(Point point, Vector vector);
public static Rect FromLTRB(double left, double top, double right, double bottom);
public static Rect Empty {get;} // возвращает пустой Rect
public bool IsEmpty {get;}
public static Rect Infinite{get;}
public Point Location {get; set;}
public Size Size {get; set;}
public double X {get; set;}
public double Y {get; set;}
public double Width {get; set;}
public double Height {get; set;}
public double Left {get;} // Псевдоним X
public double Top {get;} // Псевдоним Y
public double Right {get;} // X + Width
public double Bottom {get;} // Y + Height
public Point TopLeft {get;}
public Point TopRight {get;}
public Point BottomLeft {get;}
public Point BottomRight {get;}
public Point Center {get;}
public bool Contains(Point point);
public bool Contains(Rect rect);
public bool Contains(double x, double y);
public bool IntersectsWith(Rect rect);
public void Intersect(Rect rect);
public static Rect Intersect(Rect rect1, Rect rect2);
public void Union(Rect rect);
public static Rect Union(Rect rect1, Rect rect2);
public void Union(Point point);
public static Rect Union(Rect rect, Point point);
public void Offset(Vector offset);
public void Offset(double dx, double dy);
public static Rect Offset(Rect rect, Vector offset);
public static Rect Offset(Rect rect, double dx, double dy);
public void Inflate(Size size);
// Если -width > Width*2 или -height > Height*2, the Rect становится Empty
public void Inflate(double width, double height);
public static Rect Inflate(Rect rect, double width, double height);
public static Rect Inflate(Rect rect, Size size);
// Преобразует 4 угла и задает новый прямоугольник, выровненный по оси.
public void Transforrn(Matrix matrix);
public static Rect Transform(Rect rect, Matrix matrix);
public static Boolean Equals(Rect rectangle1, Rect rectangle2)
public virtual Boolean Equals(Object o)
public virtual Int32 GetHashCode()
public static Boolean operator==(Rect rectangle1, Rect rectangle2)
public static Boolean operator!=(Rect rectangle1, Rect rectangle2)
public virtual String ToString()
}

Показано, что прямоугольник (rect) предусматривает ряд методов. IsEmpty возвращает "истину", если экземпляр равен "пустому прямоугольнику" (EmptyRect). Метод FromLTRB, по существу, возвращает прямоугольник, инициализированный посредством (левый, верхний, правый-левый, нижний-верхний). Тогда метод "содержит" (Contains) возвращает "истину", если Rect не равен Empty, p.x >= r.x, p.x <= r.x + r.width, p.y >= r.y и p.y <= r.y + r.height. Это значит, что этот метод возвратит "ложь", если прямоугольник имеет отрицательную ширину или высоту.

IntersectsWith ("пересекается с") возвращает "истину", если ни один Rect не пуст, r1.x <= r2.x + r2.width, r2.x <= r1.x + r1.width, r1.y <= r2.y + r2.height и r2.y <= r1.y + r1.height. Пересечение двух прямоугольников вычисляют путем взятия максимума из размеров "левый" и "верхний" и минимума из размеров "правый" и "нижний". Если какой-либо Rect пуст, возвращается "пустой прямоугольник".

Объединение двух прямоугольников вычисляют путем взятия минимума из размеров "левый" и "верхний" и максимума из размеров "правый" и "нижний". Если какой-либо Rect пуст, возвращается другой Rect. Для метода Offset ("смещение") смещение просто прибавляется к местоположению прямоугольника. Этот метод ничего не дает, если Rect пуст.

Для Inflate (раздувание) величина раздувания просто прибавляется ко всем сторонам. Производятся регулировки, а именно: r.x = r.x - s.width; r.y = r.y - s.height; r.width = r.width + 2 * s.width и r.height = r.height + 2 * s.height. Этот метод ничего не дает, если Rect пуст, поскольку пустой прямоугольник не имеет положения.

Следующий синтаксис разметки задает x, y, ширину и высоту. Следует заметить, что это эквивалентно тому, что делает преобразователь типов System.Drawing.Rectangle.

прямоугольник:
(координата запятая-пп){3} координата | "пустой"

Матрицы для двухмерных вычислений представлены как матрица 3×3. В MIL используется синтаксис векторов-строк. MIL допускает только аффинные преобразования, и поэтому требуется только шесть значений, а не полная матрица 3×3. Они поименованы и определены ниже.

При перемножении матрицы с точкой матрица преобразует точку из новой системы координат (СК) в предыдущую систему координат:

Преобразования могут быть вложены до любого уровня. Всякий раз, когда применяется новое преобразование, оно осуществляется путем умножения слева на текущую матрицу преобразования:

В большинстве мест в API "матрица" (Matrix) напрямую не используется. Вместо этого класс "преобразование" (Transform) поддерживает анимацию глубоким способом.

public struct System. Windows.Media.Matrix
{
// Построение и задание
public Matrix(); // по умолчанию тождественное преобразование
public Matrix(
double m11, double m12,
double m21, double m22,
double offsetX, double offsetY);
// Тождественное преобразование
public static Matrix Identity {get;}
public void SetIdentity();
public bool IsIdentity {get;}
// Поддержка Unset
public static Matrix Unset {get;} // все значения являются NaN
public bool IsSet {get;} // "ложь", если какое-либо NaN
// Математические операции
public void Prepend(Matrix matrix); // "this" становится: матрица * this
public void Append(Matrix matrix); // "this" становится: this * матрица
// Матричные операции
public void Rotate(double angle); // Присоединяет поворот после
public void RotatePrepend(double angle); // Присоединяет поворот перед
public void RotateAt(double angle, Point center); // Присоединяет поворот после
public void RotateAtPrepend(double angle, Point center); // Присоединяет поворот
//перед
public void Scale(Double scaleX, Double scaleY); // Присоединяет масштабирование
//после
public void ScalePrepend(Double scaleX, Double scaleY); // Присоединяет
// масштабирование перед
public void ScaleAt(Double scaleX, Double scaleY, Point point); // Присоединяет
// масштабирование после
public void ScaleAtPrepend(Double scaleX, Double scaleY, Point point);
// Присоединяет масштабирование перед
public void Skew(Double skewX, Double skewY); // Присоединяет перекос
в градусах после
public void SkewPrepend(Double skewX, Double skewY); // Присоединяет
// перекос в градусах перед
public void Translate(Double offsetX, Double offsetY); // Присоединяет
//параллельный перенос после
public void TranslatePrepend(Double offsetX, Double offsetY); // Присоединяет
// параллельный перенос перед
public static operator * (Matrix matrix1, Matrix matrix2);
// Услуги преобразования
public Point Transform(Point point);
public void Transform(Point[] points);
// Поскольку это вектор игнорирует части смещения матрицы
public Vector Transform(Vector vector);
public void TransformVectors(Vector[] vectors);
// Инверсия
public double Determinant {get;}
public bool HasInverse {get;}
public Matrix Inverse {get;} // Вызывает ExceptionArgument
InvalidOperationException, если !HasInverse
// Отдельные члены
public double M11 {get; set;}
public double M12 {get; set;}
public double M21 {get; set;}
public double M22 {get; set;}
public double OffsetX {get; set;}
public double OffsetY {get; set;}
};

Для синтаксиса разметки порядок таков: M11, M12, M21, M22, OffsetX, OffsetY:

матрица:
(координата запятая-пп){5} координата | "Тождественное преобразование"

ТРЕХМЕРНЫЕ ВИЗУАЛЫ И ЭЛЕМЕНТЫ

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

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

Согласно описанному выше визуалы (Visual) в системе MIL представляют дерево двухмерной композиции того, что в конце концов визуализируется. Заметим, что "элементы ПИ" и "средства управления" являются визуалами. Кроме того, визуалы имеют коллекцию дочерних визуалов, которые визуализируются перед (в терминах алгоритма программы рисования) собственным содержимым визуала. Опять же, согласно вышеописанному имеются различные конкретные подклассы визуала, включая RetainedVisual, SurfaceVisual, HwndVisual и т.д. RetainedVisual ("удержанный визуал") представляет набор двухмерных команд рисования, которые пользователь либо создает непосредственно в визуале, либо делает это как результат приема обратного вызова OnRender() от системы схем.

Будет показано, что 3D естественно вписывается в двухмерный визуальный мир. Для этого обеспечивается Retained3DVisual ("удержанный трехмерный визуал"), который, наподобие своего двухмерного аналога, представляет список команд рисования, который принудительно заполняется путем открытия контекста рисования, и визуализируется в этот контекст рисования. Это эффективно приводит к построению графа сцены или метафайла или списка команд в самом визуале.

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

Заметим, что возможность OnRender() по требованию не обеспечена для Retained3DVisual. Эта функция существует для 2D, чтобы помогать управлению объемом данных, отрисованных в двухмерном мире. Наподобие 2D, визуализация происходит посредством DrawingContext ("контекста рисования"), где производятся вызовы "ощущения непосредственного режима". Например, в 2D, в контексте рисования может быть следующее:

DrawingContext ctx =...;
ctx.DrawRectangle(...);
ctx.PushTransform(...);
ctx.DrawGeometry(...);
ctx.PushTransform(...);
ctx.DrawEllipse(...);
ctx.Pop();
ctx.Pop();

Для согласованности с 2D в 3D предусмотрена аналогичная модель, которая выглядит как:

DrawingContext3 ctx =...;
tx.DrawMesh(mesh, material);
ctx.PxishTransforai(transform3);
ctx.DrawMesh(...);
ctx.PushTransform(...);
ctx.DrawMesh(...);
ctx.Pop();
ctx.Pop();

Заметим, что эта модель визуализации одинаково хорошо работает как для трехмерного визуала удержанного режима (где "команды" просто сохраняются), так и для трехмерного визуала непосредственного режима (где визуализация осуществляется напрямую, и камеру не нужно устанавливать над передним планом). Фактически, в случае удержанного режима то, что происходит внутренне в иерархии трехмерного моделирования, строится и удерживается. Альтернативно, в случае непосредственного режима ничего подобного не происходит, команды выдаются непосредственно, и поддерживается стек контекста (например, для преобразований).

Ниже приведен пример программирования с API трехмерного визуала. Этот пример создает Retained3DVisual, получает контекст рисования для визуализации в него, визуализирует в него примитивы и источник света, задает камеру и добавляет визуал к дочерним визуалам средства управления:

// Создать 3-мерный визуал
Retained3DVisual visual3 = new Retained3DVisual();
// Визуализировать в него
using (Drawing3DContext ctx = visual3.Models.RenderOpen())
{
// Визуализировать сетки и источник света в геометрию
ctx.DrawMesh(mesh, material);
ctx.PushTransform(transform3);
ctx.DrawMesh(...);
ctx.PushTransform(secondTransform3);
ctx.AddLight(...);
ctx.DrawMesh(...);
ctx.Pop();
ctx.Pop();
}
// Задать внешние свойства на визуале
visual3.Camera = new ProjectionCamera(...);
// Добавить его к составляющим потомкам некоторого средства управления,
// именуемого myControl
((IVisual)myControl).AddChild(visual3);

Интеграция 2D в 3D является значительной особенностью и позволяет отображать очень интересные сценарии, в которых двумерные пользовательские интерфейсы (ПИ) взаимодействуют в 3D мире, отображаются на трехмерные поверхности и т.д. 2D интегрируется в 3D разными способами. Один способ предусматривает текстуру на трехмерном примитиве. Для текстурирования 3D можно использовать любую двухмерную кисть (Brush) Avalon (а также более сложные спецификации материалов). Особым случаем этого является "кисть визуала" (VisualBrush), которая может хостировать произвольный визуал (а следовательно, и "средства управления" (Control) или все ПИ приложения). Предусмотрены механизмы для разрешения тестирования на попадание и дополнительного теста на попадание в этот визуал для приложений, которые желают делать это, и в некоторых случаях разрешение тестирования на попадание будет производиться так, что эти визуалы могут быть живыми. Заметим, что, поскольку сам визуал не является битовым образом, текстурирование визуала можно реализовать более изощренными способами, например, взяв двухмерную геометрию в визуале и перенеся ее в 3D, таким образом, в качестве текстур используется трехмерная векторная графика, а не двухмерные битовые образы.

Иным способом 2D интегрируется в 3D как команда визуализации на трехмерном контексте. Желательно также иметь возможность просто визуализировать двухмерный визуал, выровненный по экрану и немасштабированный без каких-либо эффектов перспективы, на определенной глубине по z, например, для укладки сферы, двухмерного визуала и куба, в то же время позволяя им совместно пользоваться одним и тем же z-буфером, что позволяет, например, сфере проходить через часть двухмерного ПИ. Это будет предоставлено командой "рисовать визуал" на "трехмерном контексте рисования".

Согласно описанному здесь "анимация" (Animation) должна работать интуитивно, и технология двухмерной анимации непосредственно трансформируема в 3D. Исключением является рационализация временных шкал.

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

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

Для обеспечения декларативного способа построения и использования трехмерных "ресурсов", как в 2D, с помощью кистей, перьев, геометрии, путей и т.д. предусмотрен ряд типов, которые позволяют пользователю конструировать то, что идет в поток 3D-команд, и сконструированный объект можно установить в Retained3DVisual вместо того, чтобы использовать контекст.

Например, вышеприведенный иллюстративный код на основе "трехмерного контекста рисования" (Drawing3DContext) можно переписать следующим образом:

// Создать 3-мерный визуал
Retained3DVisual visual3 = new Retained3DVisual();
visual3.Models.Add(new MeshPrimitive3D(mesh, material));
Model3DCollection innerGroup1 = new Model3DCollection();
innerGroup1.Transform = transform3;
innerGroup1.Add(new MeshPrimitive3D(mesh, material));
Model3DCollection innerGroup2 = new Model3DCollection();
innerGroup2.Transform = secondTransform3;
innerGroup2.Add(new Light(...));
innerGroup2.Add(new MeshPrimitive3D(...));
innerGroup1.Add(innerGroup2);
visual3.Models.Add(innerGroup1);
// Все остальное - как раньше ...
// Задать внешние свойства на визуале
visual3.Camera = new ProjectionCamera(...);
// Добавить его к составляющим потомкам некоторого средства управления,
// именуемого myControl
((IVisual)myControl).AddChild(visual3);

Здесь строится модель, которая затем назначается "удержанному трехмерному визуалу" (Retained3DVisual). Пары PushTransform/Pop заменяются конструкцией Model3DCollection, которая сама имеет преобразования и модели ниже себя. Обеспечение подхода моделирования и принудительного подхода на основе контекста обеспечивает решение для декларативной разметки уровня элементов, перечисления визуалов, эффектов графа сцены и изменяемости содержимого визуалов и осуществляется жизнеспособным способом с точки зрения архитектуры.

Корнем дерева классов моделирования является Model3D, который представляет трехмерную модель, которую можно присоединить к Retained3DVisual. В конечном итоге, источник света, сетки, потоки файлов .Х (так что они могут поступать из файла, ресурса, памяти и т.д.), группы моделей и двухмерные визуалы, размещенные в трехмерном пространстве, - все являются моделями. Таким образом, имеется следующая иерархия:

- Model3D
- Model3DCollection - это коллекция Model3D
- Primitive3D
- MeshPrimitive3D(mesh, material, hitTestID)
- ImportedPrimitive3D(stream, hitTestID) (для файлов .x)
- Источник света (Light)
- Окружающий свет (AmbientLight)
- Отраженный свет (SpecularLight)
- Направленный свет (DirectionalLight)
- Световая точка (PointLight)
- Световое пятно (прожектор) (SpotLight)
- VisualModel3D - имеет Visual и Point3 и hitTestID

Сам класс Model3D поддерживает ряд операций, включая "получение трехмерного ограничивающего прямоугольного параллелепипеда", "получение и задание "преобразования" (Transform) для Model3D", "получение и задание других свойств уровня "узлов"", наподобие режима затенения, и "получение и задание hitTestObject". Заметим, что явное 3D-инициированное тестирование на попадание не предоставляется в трехмерной сцене. Иными словами, не существует API для проецирования произвольно направленного луча в трехмерную сцену, чтобы видеть, что является попаданием. К функциям тестирования на попадание будет осуществляться доступ путем осуществления теста на попадание для двухмерной проекции в Retained3DVisual.

Включение 3D создает проблему, когда приходится рационализировать двухмерную систему координат с помощью трехмерной системы координат. В двухмерном пространстве система координат имеет начало координат в верхнем левом углу, +х вправо и +y вниз.

Трехмерная система координат может быть правосторонней, где +z выходит из экрана, +х - вправо (как и в двухмерной) и +y - вверх (в отличие от двухмерной). Дело в том, что левосторонняя система координат в трехмерной графике имеет тенденцию создавать большое количество нежелательных ошибок программирования. Заметим, что современное оборудование имеет возможность загружать повершинные шейдеры, что позволяет использовать любое необходимое соглашение в отношении системы координат.

Другим аспектом рационализации системы координат является пространство u,v, в котором существуют текстуры. Для пространства u,v нет конкретного принятого по умолчанию соглашения, но модели с "+v, идущей вниз", допускают более прямое отображение двухмерного ПИ посредством текстурирования, чем модели с "+v, идущей вверх" (для тех, где текстуры, скорее всего, появляются сверху вниз). В случае, когда текстурируемая модель не имеет правильно ориентированной сетки u,v, для противодействия можно использовать свойство "преобразование" на кистях материала.

Был проведен ряд экспериментов, чтобы найти наиболее жизнеспособный способ сделать так, чтобы текст выглядел привлекательным в 3D; (заметим, что для этого не обязательно обеспечивать "трехмерный текст" с шириной, высотой и глубиной). С этой целью приложение может строить двухмерные визуалы, содержащие текст (и другие произвольные двухмерные элементы), подлежащие использованию в 3D, гарантировать, что этот визуал передискретизируется (например, 4х-8х), а затем использовать этот визуал в качестве "материала" (Material) в плоскость или любой произвольный трехмерный объект. Если полученное текстурирование является анизотропным, получается привлекательный текст.

Заметим, что все декларативные типы, указанные в этом разделе (например, векторы, точки, источники света, сетки, материалы, примитивы, преобразования и т.д.), легко описать посредством разметки на основе XAML с использованием стандартных механизмов описания типов классов CLR XAML. Типы могут иметь "преобразователи типов" (TypeConverter), но если нет, их можно задавать в разметке посредством стандартных механизмов описания простых и сложных свойств, которые предлагает XAML. Заметим, что спецификации "преобразователя типов" (TypeConverter) предназначены для интерпретации чистостроковых представлений типов по мере их появления в XAML. Так, например, для Vector3D, который используется в ScaleTransform3D, разметка XAML будет:

<ScaleTransform3D scaleVector="1,2,8" />

Заметим, что "ScaleTransform3D" и "scaleVector" анализируются и понимаются общим анализатором XAML, но "1,2,8" поступает на "преобразователь типов", соответствующий Vector3D, и ожидается, что генерируется Vector3D (поскольку это тип свойства "ScaleVector" для ScaleTransform3D). Кроме того, заметим, что хотя и не перечисленные явно для каждого типа, эти типы имеют следующие методы (показаны здесь для Vector3D, но применимы и к другим):

public static bool operator = (Vector3D vector1, Vector3D vector2)
public static bool Equals(Vector3D vector1, Vector3D vector2)
public static bool operator != (Vector3D vector1, Vector3D vector2)
public override bool Equals(object o)
public override int GetHashCode()
public override string ToString()

Кроме того, любому типу, который является производным от Changeable (прямо или косвенно), понадобится иметь на себе метод "public new MyType Copy()". Эти типы примитивов просто существуют в поддержку других типов, описанных в этом разделе. При всякой возможности они зеркально отображают типы примитивов, используемые в 2D, это подобие является целью разработки для этих типов.

Объект Point3D является прямым аналогом типа двухмерной точки System.Windows.Point:

public struct System.Windows.Media3D.Point3D
{
public Point3D(); // инициализируется на 0,0,0
public Point3D(double x, double y, double z);
public double X {get; set;}
public double Y {get; set;}
public double Z {get; set;}
public void Offset(double dx, double dy, double dz);
public static Point3D operator +(Point3D point, Vector3D vector);
public static Point3D operator -(Point3D point, Vector3D vector);
public static Vector3D operator -(Point3D point1, Point3D point2);
public static Point3D operator *(Point3D point, Matrix3D matrix);
public static Point3D operator *(Point3D point, Transform3D transform);
public static explicit operator Vector3D(Point3D point);
// Явное продвижение 3D точки в 4D точку. Координата W принимает значение 1.
public static explicit operator Point4D(Point3D point);
}

координата:
представление из двух чисел
запятая-пп:
одна запятая с любым количеством пробелов до или после
тройка координат:
(координата запятая-пп){2} координата
point3D:
тройка координат

Vector3D - это прямой аналог типа двухмерного вектора System.Windows.Vector:

public struct System.Windows.Media3D.Vector3D
{
public Vector3D0; // инициализируется на 0,0,0
public Vector3D(double x, double y, double z);
public double X {get; set;}
public double Y {get; set;}
public double Z {get; set;}
public double Length {get;}
public double LengthSquared {get;}
public void Normalize(); // придает Vector3D единичную длину
public static Vector3D operator -(Vector3D vector);
public static Vector3D operator +(Vector3D vector1, Vector3D vector2);
public static Vector3D operator -(Vector3D vector1, Vector3D vector2);
public static Point3D operator +(Vector3D vector, Point3D point);
public static Point3D operator -(Vector3D vector, Point3D point);
public static Vector3D operator *(Vector3D vector, double scalar);
public static Vector3D operator *(double scalar, Vector3D vector);
public static Vector3D operator /(Vector3D vector, double scalar);
public static Vector3D operator *(Vector3D vector, Matrix3D matrix);
public static Vector3D operator *(Vector3D vector, Transform3D transform);
// возвращает скалярное произведение: vector1.X*vector2.X + vector1.Y*vector2.Y
public static double DotProduct(Vector3D vector1, Vector3D vector2);
// возвращает вектор, перпендикулярный двум входным векторам, путем
// вычисления векторного произведения,
public static Vector3D CrossProduct(Vector3D vector1, Vector3D vector2);
// возвращает угол, необходимый для поворота v1 в v2, в градусах
// Будет возвращать значение в пределах (-180, 180] градусов
public static double AngleBetween(Vector3D vector1, Vector3D vector2);
public static explicit operator Point3D(Vector3D vector);}
// Явное продвижение 3D вектора в 4D точку. Координата W принимает значение 0.
public static explicit operator Point4D(Vector3D point);
}

point3D:
тройка координат

Point4D добавляет четвертую составляющую, w, к трехмерной точке и используется для преобразования посредством неаффинных Matrix3D. Это не Vector4D, поскольку составляющая 'w', равная 1, преобразуется в Point3D, и составляющая 'w', равная 0, преобразуется в Vector3D:

public struct System. Windows.Media3D.Point4D
{
public Point4D0; // инициализируется на 0,0,0,0
public Point4D(double x, double y, double z, double w);
public double X {get; set;}
public double Y {get; set;}
public double Z {get; set;}
public double W {get; set;}
public static Point4D operator -(Point4D point1, Point4D point2);
public static Point4D operator +(Point4D point1, Point4D point2);
public static Point4D operator *(double scalar, Point4D point);
public static Point4D operator *(Point4D point, double scalar);
public static Point4D operator *(Point4D point, Matrix3D matrix);
public static Point4D operator *(Point4D point, Transform3D transform);
}

point4D:
четверка координат

Кватернионы - это отчетливо трехмерные сущности, которые представляют поворот в трех измерениях. Их сила состоит в способности интерполировать (а, значит, анимировать) между кватернионами для достижения гладкой, надежной интерполяции. Конкретный механизм интерполяции называется сферическая линейная интерполяция.

Кватернионы можно строить, либо напрямую задавая их составляющие (x,y,z,w), либо как представление ось/угол. Первое представление может приводить к ненормализованным кватернионам, для которых определенные операции не имеют смысла (например, выделение оси и угла).

Составляющие "кватерниона" (Quaternion) нельзя задавать сразу после построения "кватерниона", поскольку имеется потенциальная неопределенность в осуществлении этого, например, задание "угла" (Angle) на ненормализованном "кватернионе" не имеет смысла.

public struct System.Windows.Media3D.Quaternion
{
public Quaternion(); // инициализируется на 0,0,0,0
// Допустимы ненормализованные кватернионы
public Quaternion(double x, double y, double z, double w);
// допускает построение по оси и углу
public Quatemion(Vector3D axisOfRotation, double angleInDegrees);
// основные составляющие кватерниона
public double X {get;}
public double Y {get;}
public double Z {get;}
public double W {get;}
// доступ к оси/углу. Будет вызывать исключение, если кватернион
// не нормализован.
public Vector3D Axis {get;}
public double Angle {get;} // в градусах, совсем, как все остальное
// Модуль 1? Только нормализованные кватернионы можно использовать в
// RotateTransform3D.
public bool IsNormalized {get;}
public Quaternion Conjugate(); // возвращает сопряженный кватернион
public Quaternion Inverse(); // возвращает обратный кватернион
public Quaternion Normalize(); // возвращает нормализованный кватернион
public static Quaternion operator +(Quaternion left, Quaternion right);
public static Quaternion operator -(Quaternion left, Quaternion right);
public static Quaternion operator *(Quaternion left, Quaternion right);
// гладко интерполирует между двумя кватернионами
public static Quaternion Slerp(Quaternion left, Quaternion right, double t);
}

кватернион:
четверка координат // представление x,y,z,w
"(" тройка координат ")" координата // представление ось, угол

Matrix3D - это трехмерный аналог System.Windows.Matrix. По аналогии с Matrix большинство API берут не Matrix3D, а Transform3D, которое поддерживает анимацию глубоким способом. Матрицы для трехмерных вычислений представлены матрицей 4×4. В MIL используется синтаксис вектора-строки:

При перемножении матрицы с точкой матрица преобразует точку из новой системы координат в предыдущую систему координат.

Преобразования могут быть вложены до любого уровня. Всякий раз, когда применяется новое преобразование, оно осуществляется путем умножения слева на текущую матрицу преобразования:

public struct System.Windows.Media3D.Matrix3D
{
// Построение и задание
public Matrix(); // по умолчанию тождественное преобразование
public Matrix(
double m11, double m12, double m13, double m14,
double m21, double m22, double m23, double m24,
double m31, double m32, double m33, double m34,
double offsetX, double offsetY, double offsetZ, double m44);
// Тождественное преобразование
public static Matrix3D Identity {get;}
public void SetIdentity();
public bool IsIdentity {get;}
// Математические операции
public void Prepend(Matrix3D matrix); // "this" становится: матрица * this
public void Append(Matrix3D matrix); // "this" становится: this * матрица
// Повороты - версии кватерниона. Если вы хотите поворот ось/угол,
// стройте кватернион вне оси/угла.
public void Rotate(Quaternion quaternion);
public void RotatePrepend(Quaternion quaternion);
public void RotateAt(Quaternion quaternion, Point3D center);
public void RotateAtPrepend(Quaternion quaternion, Point3D center);
public void Scale(Vector3D scalingVector);
public void ScalePrepend(Vector3D scalingVector);
public void ScaleAt(Vector3D scalingVector, Point3D point);
public void ScaleAtPrepend(Vector3D scalingVector, Point3D point);
public void Skew(Vector3D skewVector); // Присоединяет перекос в градусах после.
public void SkewPrepend(Vector3D skewVector);
public void SkewAt(Vector3D skewVector, Point3D point);
public void SkewAtPrepend(Vector3D skewVector, Point3D point);
public void Translate(Vector3D offset); // Присоединяет параллельный перенос после
public void TranslatePrepend(Vector3D offset); // Присоединяет параллельный
// перенос перед.
public static Matrix3D operator * (Matrix3D matrix1, Matrix3D matrix2);
// Услуги преобразования. Те, которые действуют на Vector3D и Point3D
// возникает исключение, если IsAffine == false.
public Point3D Transform(Point3D point);
public void Transform(Point3D[] points);
public Point4D Transform(Point4D point);
public void Transform(Point4D[] points);
// Поскольку это вектор, игнорирует части смещения матрицы.
public Vector3D Transform(Vector3D vector);
public void Transform(Vector3D[] vectors);
// Характеристики матрицы
public bool IsAffine {get;} // "истина", если m{1,2,3} 4 == 0, m44 == 1.
public double Determinant {get;}
public bool HasInverse {get;}
public Matrix3D Inverse {get;} // Выбрасывает InvalidOperationException, если
!HasInverse
// Отдельные члены
public double M11 {get; set;}
public double M12 {get; set;}
public double M13 {get; set;}
public double Ml4 {get; set;}
public double M21 {get; set;}
public double M22 {get; set;}
public double M23 {get; set;}
public double M24 {get; set;}
public double M31 {get; set;}
public double M32 {get; set;}
public double M33 {get; set;}
public double M34 {get; set;}
public double OffsetX {get; set;}
public double OffsetY {get; set;}
public double OffsetZ {get; set;}
public double M44 {get; set;}
};

matrix3D:
(координата запятая-пп ){15} координата | "тождественное преобразование"

Transform3D, как и двухмерное преобразование, является абстрактным базовым классом с конкретными подклассами, представляющими особые типы трехмерного преобразования. Особые подклассы Transform3D также присутствуют в связи с анимацией.

Ниже приведена общая иерархия Transform3D согласно одной реализации:

Transform3D
---- Transform3DCollection
---- AffineTransform3D
-------- TranslateTransform3D
-------- ScaleTransform3D
-------- RotateTransform3D
---- MatrixTransform3D

Корневое Transform3D имеет статические методы для построения особых классов "преобразования" (Transform); (заметим, что это не предоставляет представление Matrix3D, поскольку это Transform может быть шире):

public abstract class System.Windows.Media.Media3D.Transform3D : Animatable
{
internal Transform3D();
public new Transform3D Copy();
// статические помощники для создания общих преобразований
public static MatrixTransfonn3D CreateMatrixTransfonn(Matrix3D matrix);
public static TranslateTransform3D CreateTranslation(Vector3D translation);
public static RotateTransform3D CreateRotation(Vector3D axis, double angle);
public static RotateTransform3D CreateRotation(Vector3D axis, double angle,
Point3D rotationCenter);
public static RotateTransform3D CreateRotation(Quaternion quaternion);
public static RotateTransform3D CreateRotation(Quaternion quaternion,
Point3D rotationCenter);
public static ScaleTransform3D CreateScale(Vector3D scale Vector);
public static ScaleTransform3D CreateScale(Vector3D scaleVector,
Point3D scaleCenter);
public static Transform3D Identity {get;}
// Члены экземпляра
public bool IsAffine {get;}
public Point3D Transform(Point3D point);
public Vector3D Transform(Vector3D vector);
public Point4D Transform(Point4D point);
public void Transform(Point3D[] points);
public void Transform(Vector3D[] vectors);
public void Transform(Point4D[] points);
}

Заметим, что методы Transform(), которые берут Point3D/Vector3D, будут вызывать исключение, если преобразование не является аффинным.

Transform3DCollection будет имитировать TransformCollection, при этом методы Add* модифицируются таким же образом, как вышеописанные методы Create*:

public sealed class System.Windows.Media3D.Transform3DCollection : Transform3D, IList
{
// следовать модели TransformCollection
}

AffineTransform3D - это базовый класс, производными от которого являются конкретные аффинные трехмерные преобразования (параллельный перенос, перекос, поворот, масштабирование), и он открывает доступ для чтения к Matrix3D:

public abstract class System.Windows.Media3D.AffineTransform3D : Transform3D
{
internal AffineTransform3D(); // нерасширяемый
public virtual Matrix3D Value {get;}
}

TranslateTransform3D:

public sealed class System.Windows.Media3D.TranslateTransform3D : AffineTransfonn3D
{
public TranslateTransform3D();
public TranslateTransform3D(Vector3D offset);
public TranslateTransform3D(Vector3D offset,
Vector3DAnimationCollection offsetAnimations);
public new TranslateTransform3D Copy();
[Animations("OffsetAnimations")]
public Vector3D Offset {get; set;}
public Vector3DAnimationCollection OffsetAnimations {get; set;}
public override Matrix3D Value {get;}
}

ScaleTransform3D:

public sealed class System.Windows.Media3D.ScaleTransform3D : AffineTransfonn3D
{
public ScaleTransform3D();
public ScaleTransform3D(Vector3D scale Vector);
public ScaleTransform3D(Vector3D scale Vector, Point3D scaleCenter);
public ScaleTransform3D(Vector3D scale Vector,
Vector3DAnimationCollection scaleVectorAnimations,
Point3D scaleCenter,
Point3DAnimationCollection scaleCenter Animations);
public new ScaleTransfonn3D Copy();
[Animations("ScaleVectorAnimations")]
public Vector3D ScaleVector {get; set;}
public Vector3DAnimationCollection ScaleVectorAnimations {get; set;}
[Animations("ScaleCenterAnimations")]
public Point3D ScaleCenter {get; set;}
public Point3DAnimationCollection ScaleCenterAnimations {get; set;}
public override Matrix3D Value {get;}
}

RotateTransform3D - это больше, чем простое отображение из двухмерного поворота вследствие введения концепции оси для поворота вокруг нее (и, таким образом, использования кватернионов):

public sealed class RotateTransform3D : AffineTransform3D
{
public RotateTransform3D();
public RotateTransform3D(Vector3D axis, double angle);
public RotateTransform3D(Vector3D axis, double angle, Point3D center);
// Кватернионы передаются методам RotateTransform3D, которые нужно
// нормализовать, иначе возникнет исключение.
public RotateTransform3D(Quaternion quaternion);
public RotateTransform3D(Quatemion quaternion, Point3D center);
public RotateTransform3D(
Quaternion quaternion,
QuaternionAnimationCollection quaternionAnimations,
Point3D center,
Point3DAnimationCollection centerAnimations);
public new RotateTransform3D Copy();
// Угол/ось - это просто другая точка зрения на параметр QuaternionRotation.
// Если угол/ось изменяется, QuaternionRotation будет изменяться соответственно,
// и наоборот.
public double Angle {get; set;}
public Vector3D Axis {get; set;}
[Animations("QuaternionRotationAnimations")]
public Quaternion QuaternionRotation {get; set;}
public QuaternionAnimationCollection QuaternionRotationAnimations {get; set;}
[Animations("CenterAnimations")]
public Point3D Center {get; set;}
public Point3DAnimationCollection CenterAnimations {get; set;}
public override Matrix3D Value {get;}
}

Заметим, что здесь анимируемым является только свойство "кватернион" (Quaterion). В общем случае, анимациям оси/углов не свойственно хорошо работать. Лучше анимировать кватернион, и мы может извлекать оси и углы из базового значения кватерниона. Если вы действительно хотите просто анимировать угол относительно фиксированной оси, простой способ его задания состоит в построении двух кватернионов, представляющих эти позиции, и анимации между ними.

MatrixTransform3D может строить Transform3D непосредственно из Matrix3D:

public sealed class System.Windows.Media3D.MatrixTransform3D : Transform3D
{
public MatrixTransform3D();
public MatrixTransform3D(Matrix3D matrix);
public new MatrixTransform3D Copy();
public Matrix3D Value {get; set;}
}

Когда свойство типа Transform3D задано в разметке, система свойств использует соответствующий Transform преобразователь типа для преобразования строкового представления в соответствующий объект, производный от Transform. Анимированные свойства не описываются с использованием этого синтаксиса, однако для описания анимаций можно использовать сложный синтаксис свойств.

Синтаксис моделируется на основе двухмерного преобразования, где <> представляет необязательный параметр:

- matrix(m00 m01 m02 m03 m11 ... m33)
- translate(tx ty tz)
- scale(sx <sy> <sz> <cx> <cy> <cz>)
- Если <sy> или <sz> не заданы, предполагается, что это однородное масштабирование.
- Если <cx> <cy> <cz> заданы, то все они должны быть заданы, и <sx> <sy> в том числе. Они используются для масштабирования центра. Если нет, предполагается, что центр находится в 0,0,0.
- rotate(ax ay az angle <cx> <cy> <cz>)
- ax,ay,az задают ось вращения
- angle - это угол по этой оси
- Если ex, cy, cz не заданы, предполагается, что они 0,0,0.

Ниже приведен пример грамматики:

список преобразований:
пп* преобразования? пп*
преобразования:
преобразование
| преобразование запятая-пп+ преобразования
преобразование:
матрица
| параллельный перенос
| масштабирование
| поворот
| перекос по X
| перекос по Y
матрица:
"матрица" пп* "(" пп*
число запятая-пп
число запятая-пп
... 13 и более раз...
число пп* ")"
параллельный перенос:
"параллельный перенос" пп* "(" пп* число (запятая-пп число запятая-пп число)? пп* ")"
масштабирование:
"масштабирование" пп* "(" пп* число (запятая-пп число запятая-пп число
(запятая-пп число запятая-пп число запятая-пп число)? )? пп* ")"
поворот:
"поворот" пп* "(" пп* число пп* число пп* число пп* число
(запятая-пп число запятая-пп число запятая-пп число)? пп* ")"

Retained3DVisual является производным от Visual и, таким образом, получает его свойства, включая непрозрачность, двухмерное геометрическое усечение, двухмерный режим смешивания, API тестирования на попадание, запрос двухмерных границ и участие в дереве визуалов. Заметим, что все из непрозрачности, усечения, режима смешивания и границ применяются к двухмерной проекции трехмерной сцены.

public class System.Windows.Media.Visuals.Retained3DVisual: Visual
{
public Retained3DVisual();
public Retained3DVisual(UIContext Context);
// Семантика, ориентированная на моделирование. Значение по умолчанию - пустая
// Model3DCollection
public Model3DCollection Models {get; set;}
// Внешние свойства
// Camera - не задается по умолчанию, ошибкой будет не предоставить ее.
public Camera Camera {get; set;}
// ViewPort устанавливает, куда проекция отображается в 2D. По умолчанию 0,0,1,1
[Animation("ViewPortAnimations")]
public Rect ViewPort {get; set;}
public RectAnimationCollection ViewPortAnimations {get; set;}
public Fog Fog {get; set;}
}

Прямоугольник ViewPort устанавливает, куда проекция, определенная комбинацией камера/модели, отображается в двухмерном локальном координатном пространстве. Заметим, что не существует метода Bounds3D на Retained3DVisual. Он доступен как Models.Bounds.

Drawing3DContext очень похож на двухмерный "контекст рисования" (DrawingContext) и доступен из Model3DCollection для Retained3DVisual посредством RenderOpen/RenderAppend. Он напоминает контекст визуализации непосредственного режима, хотя он удерживает команды внутренне.

public class System.Windows.Media3D.Drawing3DContext: IDisposable
{
internal Drawing3DContext(); // не может быть создан открытым образом
// Визуализация
public void DrawMesh(Mesh3D mesh, Material material, object hitTestToken);
// Это для рисования импортированных примитивов наподобие файлов .x
public void DrawImportedPrimitive(ImportedPrimitive3DSource primitiveSource,
object hitTestToken);
public void DrawImportedPrimitive(ImportedPrimtive3DSource primitiveSource,
Material overridingMaterial,
object hitTestToken);
public void DrawVisual(Visual visual, Point3D centerPosition, object hitTestToken);
public void DrawModel(Model3D model);
public void AddLight(Light light);
// Манипуляция стеком
public void PushTransform(Transform3D transform);
public void Pop();
public void Close(); // Также вызывается посредством Dispose();
}

Конкретные детали по семантике этих операций Drawing3DContext описаны ниже со ссылкой на API моделирования, для которого Drawing3DContext является удобным. Например, DrawImportedPrimitive (ImportedPrimitive3DSource primitiveSource, objectHitTestToken) создает ImportedPrimitive3D и добавляет его в накапливающую в данный момент Model3D (которой, в свою очередь, манипулируют методы Push/Pop на контексте).

DrawModel() - это еще одна точка пересечения между миром "контекста" и миром "моделирования", позволяющая "рисовать" Model3D в контекст. Не существует явного "обратного чтения" из Drawing3DContext, поскольку его поддерживает Model3DCollection, и эту коллекцию можно, при необходимости, перечислять.

API моделирования является открытым и защищенным API для этих классов (не показывая унаследованные члены), причем Model3D является абстрактной моделью, из которой все строится:

public abstract class Model3D : Animatable
{
public Transform3D Transform {get; set;} // по умолчанию тождественное
// преобразование
public ShadingMode ShadingMode {get; set;}
public object HitTestToken {get; set;}
public Rect3D Bounds3D {get;} // Границы для этой модели
// одноэлементная "пустая" модель.
public static Model3D EmptyModel3D {get;}
}

Model3DCollection - это то, где комбинация моделей строится и обрабатывается как целое, в необязательном порядке преобразуя или применяя к ним другие атрибуты:

public sealed class Model3DCollection : Model3D, IList
{
public Model3DCoirection(params Model3D[] models);
public Model3DCollection(ICollection collection);
// Семантика Drawing3DContext
public Drawing3DContext RenderOpen();
public Drawing3DContext RenderAppend();
// Интерфейс IList interface (ветвь материала, куда я не собираюсь, плюс...)
public int Add(Model3D model);
public ModeBD this[int index] {get; set;}
public void Insert(...);
//и т.д. ...
}

Заметим, что Model3DCollection также имеет RenderOpen/Append, который возвращает Drawing3DContext. Использование этого контекста изменяет саму ModelCollection. Разница между RenderOpen() и RenderAppend() в том, что RenderOpen() сначала очищает коллекцию. Кроме того, заметим, что в этой реализации на Model3DCollection единомоментно можно открыть только один Drawing3DContext, и, когда он открыт, приложения не могут напрямую осуществлять доступ к содержимому этого Model3DCollection (для чтения или записи.

Источники света относятся к Model3D. Они включают в себя "окружающий" (Ambient), "локальный" (Positional), "направленный" (Directional) и "прожекторный" источники света. Источники света имеют дополнительное свойство являться частью иерархии моделирования и, таким образом, подлежат преобразованиям координатного пространства. На источниках света предусмотрены окружающий, рассеянный и отраженные цвета. Заметим также, что нет источников света, которые иерархически ограничены или ограничены конкретным объемом. Ниже приведена подходящая иерархия источников света:

Model3D
-----Light (абстрактный)
---------AmbientLight (конкретный)
---------DirectionalLight (конкретный)
---------PointLight (конкретный)
------------SpotLight (конкретный)

Базовый класс "источник света" (Light) является абстрактным классом:

public abstract class Light: Model3D
{
internal Light(); // позволяют только открытое создание построение - нет источников света
// третьей стороны
[Animation("AmbientColorAnimations")]
public Color AmbientColor {get; set;}
public ColorAnimationCollection AmbientColorAnimations {get; set;}
[Animation("DifruseColorAnimations")]
public Color DiffuseColor {get; set;}
public ColorAnimationCollection DiffuseColorAnimations {get; set;}
[Animation("SpecularColorAnimations")]
public Color SpecularColor {get; set;}
public ColorAnimationCollection SpecularColorAnimations {get; set;}
}

Окружающие источники света освещают объекты однородно, независимо от их формы:

public sealed class AmbientLight: Light
{
public AmbientLight(Color ambientColor);
}

Направленные источники света не имеют положения в пространстве и проецируют свой свет в определенном направлении, указанном вектором, который задает его:

public sealed class DirectionalLight: Light
{
public DirectionalLight(Color dififuseColor, Vector3D direction);
// обычное использование
[Animation("DirectionAnimations")]
public Vector3D Direction {get; set;}
public Vector3DAnimationCollection DirectionAnimations {get; set;}
}

Направление не нужно нормировать, но оно должно иметь ненулевой модуль.

Локальные источники света имеют положение в пространстве и проецируют свой свет во всех направлениях. Спад света управляется свойствами ослабления и дальности:

[сильное требование на наследование имен, поэтому третьи стороны не могут создавать производные классы...
мы не можем изолировать, поскольку SpotLight является производным от этого...]
public class PointLight: Light
{
public PointLight(Color diffuseColor, Point3D position); // обычное использование
[Animation("PositionAnimations")]
public Point3D Position {get; set;}
public Point3DAnimationCollection PositionAnimations {get; set;}
// Дальность источника света, за пределами которой он не действует.
// Задается локальными координатами.
[Animation("RangeAnimations")]
public double Range {get; set;}
public DoubleAnimationCollection RangeAnimations {get; set;}
// постоянный, линейный и квадратичный коэффициенты ослабления задают, как
// ослабляется источник света между его позицией и значением дальности (Range).
[Animation("ConstantAttenuationAnimations")]
public double ConstantAttenuation {get; set;}
public DoubleAnimationCollection ConstantAttenuationAnimations {get; set;}
[Animation("LinearAttenuationAnimations")]
public double LinearAttenuation {get; set;}
public DoubleAnimationCollection LinearAttenuationAnimations {get; set;}
[Animation("QuadraticAttenuationAnimations")]
public double QuadraticAttenuation {get; set;}
public DoubleAnimationCollection QuadraticAttenuationAnimations {get; set;}
}

Заметим, что в отношении "точечного источника света" (PointLight) должно выполняться сильное требование наследования имен, чтобы "прожекторный источник света" (SpotLight) мог быть от него производным, но не третьи стороны.

SpotLight является производным от PointLight, поскольку имеет позицию, дальность и ослабление, но также добавляется направление и параметры для управления "конусом" света. Для управления "конусом" нужно задать "внешний угол конуса" (outerConeAngle) (за пределами которого ничто не освещается) и "внутренний угол конуса" (innerConeAngle) (внутри которого все полностью освещается). Освещение между внешним и внутренним углами конуса спадает линейно. (Заметим, что имеется "угловое" спадание между краем внутреннего конуса и внешнего конуса и спадание по расстоянию относительно позиции источника света и зависит от ослабления и диапазона.)

public sealed class SpotLight: PointLight
{
public SpotLight(Color color,
Point3D position,
Vector3D direction,
double outerConeAngle,
double innerConeAngle);
[Animation("DirectionAnimations")]
public Vector3D Direction {get; set;}
public Vector3DAnimationCollection DirectionAnimations {get; set;}
[Animation("OuterConeAngleAnimations")]
public double OuterConeAngle {get; set;}
public DoubleAnimationCollection OuterConeAngleAnimations {get; set;}
[Animation("InnerConeAngleAnimations")]
public double InnerConeAngle {get; set;}
public DoubleAnimationCollection InnerConeAngleAnimations {get; set;}
}

Заметим, что, как везде в API MIL, углы заданы в градусах.

Primitive3D являются концевыми вершинами (листьями), которые приводят к визуализации в дереве. Конкретные классы приносят явно заданные сетки, а также импортированные примитивы (файлы .х). Конструктор является внутренним; (расширяемость доступна через MeshPrimitive3D).

public abstract class Primitive3D : Model3D
{
internal Primitive3D(object hitTestToken);
}

MeshPrimitive3D предназначен для моделирования с помощью сетки и материала:

public sealed class MeshPrimitive3D : Primitive3D
{
public MeshPrimitive3D(Mesh3D mesh, Material material, object hitTestToken);
public Mesh3D Mesh {get; set;}
public Material Material {get; set;}
}

Заметим, что MeshPrimitive3D является концевой геометрией и что он содержит, но не сам по себе, Mesh ("сетку"). Это значит, что Mesh может совместно использоваться множеством MeshPrimitive3D, с разными материалами, подлежащими разному тестированию на попадание, без дублирования данных сетки.

ImportedPrimitive3D представляет внешне полученный примитив (потенциально с материалом и анимацией), принесенный и преобразованный в надлежащую внутреннюю форму. Он обрабатывается как жесткая модель. Традиционным примером является .X File (файл .Х) и имеется подкласс ImportedPrimitive3DSource, который явно импортирует файлы .Х.

public sealed class ImportedPrimitive3D : Primitive3D
{
public ImportedTrimitive3D(ImportedPrimitive3DSource primitive,
object hitTestToken);
public ImportedPrimitive3DSource PrimitiveSource {get; set;}
// Позволяет подменять импортированный(е) материал(ы), если таковые имеются.
// Если не задан, это null, и используется встроенный материал.
public Material OverridingMaterial {get; set;}
}

Поскольку ожидается, что файлы .х обычно включены в сцены, для выражения этого поддерживается простой формат "преобразователя типов" (TypeConverter), например:

<ImportedPrimitive3D xfile="myFile.x" />

VisualModel3D берет любой визуал (Visual) (по определению, двухмерный) и помещает его в сцену. При визуализации он будет выровнен по экрану, его размер не будет подвергаться изменению, но он будет в конкретной z-плоскости от камеры. Визуал будет оставаться интерактивным.

public sealed class VisualModel3D : Model3D
{
public VisualModel3D(Visual visual, Point3 CenterPoint, object hitTestToken);
public Visual Visual {get; set;}
public Point3D CenterPoint {get; set;}
}

Визуализация VisualModel3D сначала преобразует "центральную точку" (CenterPoint) в мировые координаты. Затем визуал визуализируется в пиксельный буфер с выравниванием по экрану, причем координата z преобразованной "центральной точки" соответствует месту, где размещен центр визуала. При движении камеры VisualModel3D будет занимать тот же объем пространства экрана и будет обращен к переднему плану и не будет подвергаться действию источника света и т.п. Фиксированной точкой визуала в ходе этого движения камеры по отношению к остальной сцене будет центр визуала, поскольку размещение осуществляется в зависимости от этой точки.

Обеспечиваемый визуал является полностью интерактивным и эффективно "удочеряется" охватывающим его Retained3DVisual, что означает, что заданный визуал можно использовать только один раз в любой VisualModel3D, просто потому, что визуал может иметь только одного родителя. Заметим, что это один из двух механизмов внедрения интерактивных визуалов в 3D. Другой состоит в использовании VisualMaterial и использовании его на примитиве.

DrawMesh и DrawVisual оба берут "объект" hitTestToken. IVisual.HitTest() будет возвращать это при попадании в трехмерный визуал. Затем это используется для разрешения неоднозначности в отношении того, на что происходит попадание в 3D. Заметим, что "отбор путей" здесь не обязателен, поскольку каждый DrawMesh, даже если он получает одну и ту же сетку, может обеспечиваться другим "hitTestToken".

В одной реализации "тестирование на попадание" (HitTesting) реализуется через утилиту RayIntersect ("пересечение лучей"), которая обеспечивает расстояние до попадания, координаты u,v точки попадания, допускает тестирование на попадание за пределами объекта, на который первоначально было попадание, и т.д. Заметим также, что результат тестирования на попадание имеет достаточную информацию, чтобы давать координату u,v примитива, на который было попадание, чтобы позволить перевод в запрос тестирования на попадание, какая бы текстура ни упаковала его. Кроме того, для VisualModel3D, а также использования VisualMaterial тестирование на попадание будет переходить в двухмерный визуал в правильной точке отображения. Это получается благодаря тому, что контексту тестирования на попадание разрешается приходить из внешней двухмерной среды, продолжаться через трехмерную среду и вновь отбираться во вложенной двухмерной среде, потенциально всегда. Конечным результатом этого является то, что при обеспечении этого сквозного прохода пользовательское взаимодействие с реальными двухмерными средствами управления Avalon с отображением текстур работает правильно.

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

public abstract sealed class ImportedPrimitive3DSource
{
// Не допускает внешних производных классов
internal ImportedPrimitive3DSource();
}

Файлы .х поступают в систему в качестве XFile3DSource и используются для построения ImportedPrimitive3D. Он выделяется в отдельный тип данных, что позволяет использовать и совместно использовать его в множественных ImportedPrimitive3D. Надлежащие преобразования и обработка файла .х могут производиться при его построении или могут быть задержаны. Заметим, что файлы .х импортируются как целое, и ни к чему внутри них нельзя обращаться по отдельности.

public sealed class XFile3DSource : ImportedPrimitive3DSource
{
// Данные файла .Х заданы как поток, что позволяет им поступать из
// произвольных источников (файлов, ресурсов, памяти, URL).
public XFile3DSource(IStream xFileStream);
}

Примитив Mesh3D - это непосредственный треугольный примитив (допускающий как индексированное, так и неиндексированное задание), который может быть построен программно. Заметим, что это поддерживает то, что, как ожидается, будет наиболее распространенным использованием примитива (а именно, информация позиции, нормали, цвета и текстуры, причем последние три не обязательны). Сетка также позволяет выбирать, следует ли ее отображать в виде треугольников, линий или точек. Она также поддерживает три топологии для интерпретации индексов, а именно список треугольников, полосу треугольников и веер треугольников.

Для форматов вершин и другой конструкции примитива, которые не поддерживаются непосредственно Mesh3D, можно построить и импортировать файл .x.

public sealed class System.Windows.Media3D.Mesh3D : Animatable
{
public Mesh3D();
// Данные вершин. Нормали, цвета и координаты текстуры все необязательные.
public Point3DCollection Positions {get; set;}
public Vector3DCollection Normals {get; set;} // предполагаются нормированными
public ColorCollection Colors {get; set;}
public ColorCollection SpecularColors {get; set;}
public PointCollection TextureCoordinates {get; set;}
//Данные топологии. Если null, обрабатывать как неиндексированный примитив
public IntegerCollection TriangleIndices {get; set;}
// Тип примитива - по умолчанию = TriangleList
public MeshPrimitiveType MeshPrimitiveType {get; set;}
}

MeshPrimitiveType задан как:

public enum System. Windows.Media3D.MeshPrimitiveType
{
TriangleList,
TriangleStrip,
TriangleFan,
LineList,
LineStrip,
PointList
}

Повершинные данные в Mesh3D делятся на "позиции" (Positions), "нормали" (Normals), "цвета" (Colors) и "текстурные координаты" (TextureCoordinates). Обязательными являются, конечно, только "позиции". Если обеспечены какие-либо другие, они должны иметь точно такую же длину, как коллекция позиций, иначе будет возникать исключение.

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

Коллекция TriangleIndices ("индексы треугольников") имеет члены, которые индексируются в данные вершин для определения повершинной информации для треугольников, составляющих сетку. Эта коллекция интерпретируется на основании заданного MeshPrimitiveType. Для TriangleList ("список треугольников") каждые три элемента в коллекции TriangleIndices задают новый треугольник. Для TriangleFan ("веер треугольников") индексы 0, 1, 2 определяют первый треугольник, затем каждый последующий индекс i определяет новый треугольник, заданный вершинами 0, i, i-1. Для TriangleStrip ("полоса треугольников"), индексы 0, 1, 2 определяют первый треугольник, и каждый последующий индекс i определяет новый треугольник, заданный вершинами i-2, i-1 и i. LineList ("список линий"), LineStrip ("полоса линий") и PointList ("список точек") имеют аналогичные интерпретации, но визуализация осуществляется применительно к линиям и точкам, а не треугольникам.

Если TriangleIndices равна null, то "сетка" (Mesh) интерпретируется как неиндексированный примитив, который эквивалентен TriangleIndices, поддерживающей значения 0,1,...,n-2,n-1 для коллекции Positions ("позиции") длиной n.

После построения Mesh реализация создает оптимальную структуру D3D, которая представляет эту сетку. При этом фактические структуры данных Collection ("коллекция") могут отбрасываться реализацией Mesh во избежание дублирования данных. Последующее обратное считывание сетки, если к нему обращаются посредством некоторого другого механизма (например, обхода иерархии моделей "удержанных трехмерных визуалов" (Retained3DVisual)), будет, скорее всего, реконструировать данные из информации D3D, которая поддерживается на ней, а не удерживать исходные данные.

Класс Mesh является производным от Changeable (через Animatable) и, таким образом, может изменяться. Реализации потребуется вводить наборы в данные вершин и индексов и распространять эти изменения на структуры данных D3D. Явная поддержка декларативных анимаций данных вершин или индексов в сетке отсутствует. Иными словами, например, Point3DAnimationCollection здесь не видны. Это согласуется, например, с двухмерными полилинейными путями.

Как и другие типы, сложный синтаксис свойств XAML можно использовать для задания коллекций, которые определяют Mesh3D. Однако это может быть громоздким и многословным, и, таким образом, чтобы сделать задание более сжатым, обеспечиваются "преобразователи типов" (TypeConverter).

Каждая коллекция, заданная в сетке, берет одну строку чисел, подлежащую анализу и используемую для создания коллекции. Например, "сетку" (Mesh), представляющую индексированную полосу треугольников только с помощью позиций и цветов, можно задать как:

<Mesh3D
meshPrimitiveType="TriangleStrip"
positions="1,2,3, 4,5,6, 7,8,9, 10,11,12, 13,14,15, 16,17,18"
colors="red blue green cyan magenta yellow"
triangleIndices="l,3,4,l,2,3,4,5,6,l,2,4,2"
/>

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

Методы, которые строят Primitive3D, берут "материал" (Material) для задания их внешнего вида. Material - это абстрактный базовый класс с тремя конкретными подклассами: BrushMaterial, VisualMaterial и AdvancedMaterial. BrushMaterial и VisualMaterial являются подклассами другого абстрактного класса, именуемого BasicMaterial. Таким образом:

Material
----BasicMaterial
---------BrushMaterial
---------VisualMateriai
----AdvancedMaterial

Метод BrushMaterial берет одну "кисть" (Brush) и может использоваться для широкого круга эффектов, включая достижение прозрачности (попиксельной либо скалярной), обладание преобразованием текстур (даже анимационной), использование видео-текстур, неявных автогенерированных множественных отображений и т.п. В частности, для текстурирования чистых цветов, изображений, градиентов или даже другого визуала "кисть чистого цвета" (SolidColorBrush), "кисть изображения" (ImageBrush), "градиентная кисть" (GradientBrush) или "кисть визуала" (VisualBrush) будет использоваться для создания BrushMaterial.

Метод VisualMaterial предназначен для построения материала из визуала (Visual). Этот материал будет интерактивным в том смысле, что он будет передаваться в визуал из трехмерного мира, в который он внедрен. Одно различие между ним и BrushMaterial с VisualBrush состоит в том, что BrushMaterial являет неинтерактивным.

Класс AdvancedMaterial, хотя и гораздо сложнее, чем просто использование BrushMaterial или VisualMaterial, обеспечивает дополнительную гибкость.

public abstract class Material: Animatable
{
internal Material(); // не допускает внешнее создание подклассов
public new Material Copy(); // затеняет Changeable.Copy()
public static Material Empty {get;} // одноэлементный материал
}
public abstract class BasicMaterial: Material
{
internal BasicMaterial(); // не допускает внешнее создание подклассов
public new BasicMaterial Copy(); // затеняет Changeable.Copy()
}

Материалы усиливают гибкость и "экономию концепции", благодаря тому что основаны на "кисти" (Brush). Например, не требуется отдельная иерархия "текстур" (Texture), отражающая такие вещи, как видео-текстуры, градиентные текстуры и т.п., поскольку их всех можно задать как "кисти". "Кисти" уже инкапсулируют как альфа-маску, так и скалярные значения непрозрачности, поэтому и то, и другое становится доступным для текстурирования. С "кистями" уже связано двухмерное "преобразование" (Transform), которое в случае текстурирования будет интерпретироваться как преобразование текстуры для преобразования координат u,v в сетке для отображения в текстуру. Кисти являются надлежащим местом для подвешивания стандартных процедурных шейдеров, например шейдера текстуры дерева. Это затем можно использовать в 2D в качестве заливки или пера и в 3D в качестве текстуры. В трехмерном пространстве для процедурных шейдеров не требуется никакой специальной поддержки API.

Заметим, что пиксельные или вершинные шейдеры можно обеспечивать как "стандартные" шейдеры, многие из которых будут параметризованы. Способ обеспечения доступа к ним в API состоит в том, что для шейдеров, которые имеют смысл в двухмерном мире, они будут предоставлены как конкретные подклассы "кисти", причем их параметризация выражается либо через конструкторы на классе, либо как свойства на классе. Затем их можно применять к двухмерным объектам. Шейдеры, которые имеют смысл только в 3D, будут предоставлены как конкретные подклассы "материала" (возможно, как подклассы BasicMaterial), где их можно параметризовать только через их конструктор. Это открытие позволит затем применять шейдеры к трехмерным (где подходит, к двухмерным) сеткам.

Согласно вышеописанному BrushMaterial инкапсулирует "кисть" (Brush). BrushMaterial, примененный к Primitive3D, обрабатывается как текстура. Текстуры будут отображаться напрямую, т.е. двухмерные координаты u,v на отображаемом примитиве будут индексироваться непосредственно в соответствующие координаты x,y на "текстуре" (Texture), измененные преобразованием текстуры. Заметим, что, наподобие других 2D, система координат текстуры начинается с (0,0) в левом верхнем углу с положительным y, указывающим вниз.

"Кисть визуала" (VisualBrush), используемая для "кисти", не будет принимать вход, но будет обновляться в соответствии с любыми анимациями на ней или любыми структурными изменениями, которые случаются с ней. Чтобы использовать "визуал" (Visual) в качестве "материала" (Material) и все же принимать вход, используется VisualMaterial, как описано здесь.

public sealed class BrushMaterial: BasicMaterial
{
public BrushMaterial(Brush brush);
public new BrushMaterial Copy(); // затеняет Material.Copy()
public Brush Brush {get; set;}
// Дополнительное текстурирование конкретных кнопок.
}

Согласно описанному выше VisualMaterial инкапсулирует интерактивный визуал (Visual). Он отличается от BrushMaterial, используемого с визуалом, тем, что визуал остается действующим в своей текстурированной форме. Заметим, что поскольку отображение базируется на сетке u,v, связанной с примитивом, бывают случаи, когда пользовательский ввод не будет иметь полной точности со своим двухмерным аналогом. Например, рассмотрим сценарий захваты мыши, где двухмерное средство управления захватывает мышь (например, для реализации поведения линейки прокрутки). В этих сценариях мыши позволяется отклоняться от линейки прокрутки, но система способна понимать, например, позицию y мыши относительно линейки прокрутки и действовать правильно. В ситуации, когда средство управления текстурируется на примитив с помощью сетки u,v и мышь отклоняется от примитива, в общем случае, произвести такое определение не представляется возможным. В попытках противодействовать этому можно предпринять ряд действий, но в общем случае, все сводится к ограничению отображения в двухмерное пространство. Заметим, что в результате Visual некоторым образом удочеряется корневым Retained3DVisual. В одной реализации неправомерно использовать один "элемент ПИ" (UiElement) в более чем одном "материале" (Material) или использовать VisualMaterial более чем в одном месте, в силу того что инфраструктура базируется на одинарном удочерении средств управления.

public sealed class VisualMaterial : BasicMaterial
{
public VisualMaterial(Visual visual);
public new VisualMaterial Copy(); // затеняет Changeable.Copy()
public Visual Visual {get; set;}
--(необходимо добавить материалы порта просмотра/прямоугольника просмотра для размещения...)
// Дополнительное текстурирование конкретных кнопок.
}

BrushMaterials/VisualMaterials и BumpMaps используются для определения AdvancedMaterial:

public class AdvancedMaterial: Material
{
public AdvancedMaterial();
// Добавляет общие конструкцию.
public new AdvancedMaterial Copy(); // затеняет Changeable.Copy()
public BasicMaterial DiffuseTexture {get; set;}
public BasicMaterial SpecularTexture {get; set;}
public BasicMaterial AmbientTexture {get; set;}
public BasicMaterial EmissiveTexture {get; set;}
[Animations("SpecularPowerAnimations")]
public double SpecularPower {get; set;}
public DoubleAnimationCollection SpecularPowerAnimations {get; set;}
public BumpMap DiffiiseBumpMap {get; set;}
public BumpMap ReflectionBumpMap {get; set;}
public BumpMap RefractionBumpMap {get; set;}
public BrushMaterial ReflectionEnvironmentMap {get; set;}
public BrushMaterial RefractionEnvironmentMap {get; set;}
}

Заметим, что "карты среды" (EnvironmentMap) являются текстурами, которые, как ожидается, существуют в конкретном формате для обеспечения кубического текстурирования. В частности, шесть граней куба кубической карты нужно будет представлять в секциях "кисти" (Brush), связанных с "текстурой" (Texture) (скорее всего, что-либо вроде сетки 3×2 на "кисти"). Заметим, что свойства "окружающий" (Ambient), "рассеянный" (Diffuse) и "отражающий" (Specular) берут BasicMaterial, а не общий Material, поскольку не разрешается задавать их как сами AdvancedMaterials. Кроме того, заметим, что карты среды представляют собой исключительно BrushMaterials, поскольку интерактивность, которую обеспечивает VisualMaterial, не имеет смысл для карт среды.

Карты рельефа представляют собой сетки, которые, наподобие текстур, отображаются на трехмерные примитивы посредством текстурных координат на примитивах. Однако интерполированные данные интерпретируются как возмущения нормалей к поверхности, приводящие к "рельефному" внешнему виду примитива. Для достижения этого отображения рельефа несут информацию, например возмущения нормали, и потенциально другую информацию. Вследствие этого неправильно использовать "кисть" в качестве отображения рельефа. Поэтому можно обеспечить новый класс BumpMap в качестве ImageSource конкретного формата пикселя.

"Материал" (Material) обеспечивает простой "преобразователь типов" (TypeConverter), который позволяет автоматически продвигать строковое задание "кисти" (Brush) в BrushMaterial:

Материал:
... делегат в преобразователь типов кисти ...

Это допускает спецификации такие, как:

<MeshPrimitive3D ... Material="yellow" />
<MeshPrimitive3D ... Material="LinearGradient blue green" />
<MeshPrimitive3D ... Material="HorizontalGradient orange purple" />
<MeshPrimitive3D ... Material="*Resource(myImageResource)" />
Also compound property notation,
<MeshPrimitive3D>
<MeshPrimitive3D.Material>
<LinearGradientBrush>
…………

Можно предусмотреть внешние параметры модели, которые нельзя встраивать на произвольных уровнях в геометрической иерархии. Например, туман можно добавлять к сцене, задавая свойство "туман" (Fog) на Retained3DVisual, например, доступным "туманом" является "пиксельный туман". "Fog" представлен как абстрактный класс, и иерархия показана ниже:

public abstract class Fog : Animatable
{
// только внутренне построимые
internal Fog(Color color);
public new Fog Copy(); // скрывает Changeable.Copy()
[Animation("ColorAnimations")]
public Color Color {get; set;}
public ColorAnimationCollection ColorAnimations {get; set;}
// одноэлементное представление "отсутствия тумана"
public static Fog NoFog {get;}
}
public sealed class LinearFog : Fog
{
public LinearFog(Color color, double fogStart, double fogEnd);
[Animation("FogStartAnimations")]
public double FogStart {get; set;}
public DoubleAnimationCollection FogStartAnimations {get; set;}
[Animation("FogEndAnimations")]
public double FogEnd {get; set;}
public DoubleAnimationCollection FogEndAnimations {get; set;}
}
public sealed class ExponentialFog : Fog
{
public ExponentialFog(Color color, double fogDensity, bool squaredExponent);
[Animation("FogDensityAnimations")]
public double FogDensity {get; set;}
public DoubleAnimationCollection FogDensityAnimations {get; set;}
public bool SquaredExponent {get; set;}
}

fogDensity находится в пределах от 0 до 1 и является нормированным представлением плотности тумана. fogStart и fogEnd содержат z-глубины, заданные в пространстве устройств [0,1], и представляют то, где начинается и оканчивается туман.

"Камера" (Camera) - это механизм, посредством которого трехмерная модель проецируется в двухмерный визуал. Camera сама по себе является абстрактным типом с двумя конкретными подтипами: ProjectionCamera ("проекционная камера") и MatrixCamera ("матричная камера"). ProjectionCamera берет такие хорошо понятные параметры, как Position ("позиция"), LookAtPoint ("смотреть в точку") и FieldOfView ("поле зрения") для построения Camera. MatrixCamera берет Matrix3D, используемую для задания преобразования "мира в устройство".

public abstract class Camera : Animatable
{
// Позволяет строить только внутренне.
internal Camera();
public new Camera Copy(); // скрывает Changeable.Copy()
}

В "удержанном трехмерном визуале" (Retained3DVisual) "камера" (Camera) используется для обеспечения просмотра Model3D, и результирующая проекция отображается в двухмерный "порт просмотра" (ViewPort), установленный на Retained3DVisual. Заметим, что двухмерный ограничивающий прямоугольник для Retained3DVisual будет просто проекцией трехмерного ограничивающего прямоугольного параллелепипеда трехмерной модели, упакованной в его выпуклую, выровненную по осям оболочку, усеченную до усечения, установленного на визуале.

ProjectionCamera - это средство построения камеры из таких параметров, как Position, LookAtPoint и FieldOfView. Она инкапсулирует как перспективные проекции, так и ортографические проекции. На фиг.42 четко показаны важные аспекты ProjectionCamera, а именно просмотр и позиция (где "поле зрения" (FieldOfView) должно быть в горизонтальном направлении).

public class ProjectionCamera : Camera
{
// Общие конструкторы
public ProjectionCamera();
public ProjectionCamera(Point3D position,
Vector3D lookDirection,
Vector3D Up,
double fieldOfView);
public new ProjectionCamera Copy(); // скрывает Changeable.Copy()
// Данные камеры
public bool IsOrthographic {get; set;} // по умолчанию = ложь: перспектива
[Animations("NearPlaneDistanceAnimations")]
public double NearPlaneDistance {get; set;} // по умолчанию = 0
public DoubleAnimationCollection NearPlaneDistanceAnimations {get; set;}
[Animations("FarPlaneDistanceAnimations")]
public double FarPlaneDistance {get; set;} // по умолчанию = бесконечность
public DoubleAnimationCollection FarPlaneDistanceAnimations {get; set;}
[Animations("FieldOfViewAnimations")]
public double FieldOfView {get; set;}
public DoubleAnimationCollection FieldOfViewAnimations {get; set;}
[Animations("PositionAnimations")]
public Point3D Position {get; set;}
public Point3DAnimationCollection PositionAnimations {get; set;}
[Animations("LookDirectionAnimations")]
public Vector3D LookDirection {get; set;}
public Vector3DAnimationCollection LookDirectionAnimations {get; set;}
[Animations("UpAnimations")]
public Vector3D Up {get; set;}
public Vector3DAnimationCollection UpAnimations {get; set;}
}

Заметим, что FieldOfView представляет горизонтальное поле зрения и задано в градусах (как другие углы MIL). Расстояния ближнего и дальнего плана (NearPlaneDistance и FarPlaneDistance представляют расстояния в координатах трехмерного мира от "позиции" (Position) камеры в направлении вектора LookDirection ("направления наблюдения"). NearPlaneDistance по умолчанию равно нулю, а FarPlaneDistance по умолчанию равно бесконечности.

Если после фактического проецирования NearPlaneDistance и FarPlaneDistance по-прежнему равны нулю и бесконечности, соответственно, модель проверяется и ее ограничивающий объем проецируется согласно проекции камеры. Затем проверяется результирующий ограничивающий объем, так что расстояние ближнего плана задается по плоскости ограничивающего объема, перпендикулярной "направлению наблюдения" (LookDirection), ближайшей к позиции камеры. По существу, то же самое делается для дальнего плана, но с использованием самой удаленной плоскости. Это позволяет оптимально использовать разрешение z-буфера, но, тем не менее, отображать всю модель. Если IsOrthographic равно "истина", то используется ортографическая проекция, и FieldOfView игнорируется.

Заметим, что "плоскость проекции", заданная параметрами "проекционной камеры" (ProjectionCamera), затем отображается в прямоугольник "порта просмотра" (ViewPort) на "удержанном трехмерном визуале" (Retained3DVisual) и представляет окончательный переход из трехмерного пространства в двухмерное пространство.

"Матричная камера" (MatrixCamera) как подкласс Camera обеспечивает средство непосредственного задания "матрицы" (Matrix) как проекционного преобразования. Это полезно для приложений, имеющих собственные механизмы вычисления проекционной матрицы.

public class MatrixCamera: Camera
{
// Общие конструкторы
public MatrixCamera();
public MatrixCamera(Matrix3D matrix);
public new MatrixCamera Copy(); // скрывает Changeable.Copy()
// Данные камеры
public Matrix3D Matrix {get; set;} // по умолчанию = // тождественное преобразование
}

Заметим, что значения MinimumZ и MaximumZ непосредственно управляют диапазоном z-буфера. Они рассматриваются после применения Matrix в качестве проекционной матрицы.

Результирующая проекция отображается в прямоугольник "порта просмотра" (ViewPort) на "удержанном трехмерном визуале" (Retained3DVisual) и представляет окончательный переход из трехмерного пространства в двухмерное пространство.

Ниже приведены более полные разметки, показывающие задание всей иерархии Model3D в XAML. Заметим, что некоторая часть синтаксиса может меняться.

В нижеприведенном примере просто создается "модель" (Model) с двумя импортированными файлами .х и преобразованием поворота одного из них (вокруг оси z на 45 градусов) и одним точечным источником белого света, находящимся выше в точке 0,1,0.

<Model3DCollection>
<!-- Здесь дочерние модели идут как потомки --/>
<PointLight position="0,1,0" diffuseColor="white" />
<ImportedPrimitive3D xfile="myFile.x" />
<Model3DCollection transform="rotate(0, 0, 1, 45), scale(2)" >
<ImportedPrimitive3D xfile="mySecondeFile.x" />
</Model3DCollection>
</Model3DCollection>

Этот файл разметки может быть в файле, потоке, ресурсе или любой пригодной сущности. Программа-клиент будет вызывать загрузку этого XAML, и она, в свою очередь, будет строить полную Model3DCollection, подлежащую использованию приложением по желанию.

Следующий пример обеспечивает в явном виде декларированный MeshPrimitive3D путем использования синтаксиса XAML сложного свойства. Сетка будет текстурироваться с помощью "линейного градиента" (LinearGradient) от желтого до красного. В этой сцене также присутствует источник света.

<Model3DCollection>
<!-- Здесь дочерние модели идут как потомки --/>
<PointLight position="0,1,0" diffuseColor="white" />
<MeshPrimitive3D material="LinearGradient yellow red">
<MeshPrimitive3D.Mesh>
<Mesh3D
meshPrimitiveType="TriangleStrip"
positions="1,2,3, 4,5,6, 7,8,9, 10,11,12, 13,14,15, 16,17,18"
normals="... sensible normal vectors ..."
textureCoordinates=".5,.5, 1,1, 0,0, 25,25, .3,.4, .7,.8"
triangleIndices="1,3,4,1,2,3,4,5,6,1,2,4,2"/>
</MeshPrimitive3D.Mesh>
<MeshPrimitive3D>
</Model3DCollection>

В этом примере берется первый файл .х и добавляется в анимацию, заданную в XAML. В этом конкретном примере добавляется однородное масштабирование, которое масштабирует файл .х от 1х к 2.5х за 5 секунд, действует в обратном направлении и повторяет до бесконечности. Используется также ускорение/замедление для плавного начала/плавного завершения этого масштабирования:

<Model3DCollection>
<!-- Здесь дочерние модели идут как потомки --/>
<PointLight position="0,1,0" diffuseColor="white" />
<ImportedPrimitive3D xfile="myFile.x">
<ImportedPrimitive3D.Transform>
<ScaleTransform3D>
<ScaleTransform3D. Scale Vector>
<VectorAnimation
from="1,1,1"
to="2.5,2.5,2.5"
begin="immediately"
duration="5"
autoReverse="true"
repeatDuration="indefinite"
acceleration="0.1"
deceleration="0.1" />
</ScaleTransform3D.ScaleVector>
<ScaleTransform3 D>
</ImportedPrimitive3D.Transform>
</ImportedPrimitive3D>
</Model3DCollection>

В этом примере импортируется файл .х и применяется существующий ПИ в качестве его материала:

<Model3DCollection>
<!-- Здесь дочерние модели идут как потомки --/>
<PointLight position="0,1,0" diffuseColor="white" />
<ImportedPrimitive3D xfile="myFile.x" >
<ImportedPrimitive3D.OverridingMaterial>
<VisualMaterial>
<Button Text="Press Me" OnCtick="button_OnClick" />
</VisualMaterial>
</ImportedPrimitive3D.OverridingMaterial>
</ImportedPrimitive3D>
</Model3DCollection>

XAML поддерживает синтаксис для ссылки на ресурсы. "Ресурсы" в данном контексте - это широкий термин, подразумевающий как раздел "ресурс" в файле XAML, так и загрузку из управляемого ресурса "вилка" ("fork") управляемого модуля. Поддерживаются оба механизма, и на ресурсы можно ссылаться из многих мест, чтобы совместно пользоваться ими. В настоящее время не существует механизма для прямой загрузки собственных ресурсов Win32, однако реализация интерфейса IResourceLoader позволяет делать это.

Хотя это и не относится напрямую к XAML, это относится к тому, как будут строиться приложения, которые совместно используют такие вещи, как сетки и материалы. Заметим, что объекты трехмерного "моделирования" (источник света, Model3DCollection, MeshPrimitive3D, материал, Mesh3D, кисть и т.д.) относятся к типу Changeable. Согласно описанному выше изменяемые значения обеспечивают метод Copy(), который, в принципе, делает глубокую копию, но фактически производит неглубокое клонирование по требованию. Использование этого метода Copy() приводит к оптимальному совместному использованию объектов, в то же время позволяя изменять подчасти этих объектов.

Двухмерные "формы", как то "прямоугольник" (Rectangle), "многоугольник" (Polygon), "эллипс" (Ellipse) и т.д., вводятся в инфраструктуру представления Windows посредством класса "форма" (Shape), который сам по себе является "элементом ПИ" (UiElement). Такие формы, как прямоугольник, можно размещать на "холсте" (Canvas), который сам по себе является "элементом ПИ", предназначенным для использования в качестве контейнера форм. Ниже приведен один пример:

<Canvas Background="black" Top="100" Left="100"Height="600" Width="800">
<Rectangle Top="600" Left="100" Width="100" Height="50"
Fill="red" Stroke="blue" StrokeWidth="10"/>
<Line x1="100" y1="300" x2="300" y2="100" Stroke="green" StrokeWidth="5" />
</Canvas>

В этом случае "холст" (Canvas) является "элементом ПИ" (UiElement), и каждый из "прямоугольника" (Rectangle) и "линии" (Line) является "элементами ПИ" (UiElement). В этом случае все, что содержится в "холсте", является "элементом ПИ".

Однако рассмотрим объединение "путей" (Path) в разметке, которое в данном примере выглядит как:

<Canvas ...>
<Path Fill="Red" Stroke="Blue>
<Path.Data>
<CircleGeometry... />
<RectangleGeometry... />
<PathGeometry .../>
</Path.Data>
</Path>
</Canvas>

В этом случае "геометрия круга" (CircleGeometry), "геометрия прямоугольника" (RectangleGeometry) (т.е. то, что внутри Path.Data) сами по себе не являются "элементами ПИ", но контейнер "путь" - является.

Уровень элементов "трехмерная форма" создает проблему, состоящую в том, что трехмерные характеристики "удержанного трехмерного визуала" (Retained3DVisual) не выходят за пределы самого "визуала" (Visual). Это значит, что никогда не будет взаимопроникновения содержимого двух визуалов по z (например, визуал стрелы не может пронзить визуал яблока). Поскольку "средства управления" (Controls) и "формы" (Shapes) являются производными "визуала" (Visual), это также означает отсутствие поддержки взаимопроникновения "средств управления" или "форм". Они всегда являются двухмерными проекциями своего содержимого.

Для взаимопроникновения трехмерных "объектов" на уровне элементов (Element), а также для выражения этого в разметке предусмотрен элемент Viewport3D, который может содержать сложную трехмерную модель с необходимым взаимопроникновением. Однако он не будет взаимодействовать по z ни с какими другими Viewport3D (и называется Viewport3D, поскольку является двухмерным портом просмотра в трехмерном пространстве).

Viewport3D является производным от того же класса, производным от которого является Shape (либо FrameworkElement, либо Control). Заметим, что Viewport3D не должен быть производным от Shape, поскольку Shape имеет такие свойства, как Fill ("заливка"), Stroke ("штрих") и т.д., которые не имеют смысла для 3D. Таким образом, Viewport3D, Shape и Image являются сестринскими. Viewport3D будет иметь ряд свойств, которые существуют на Shape (Top ("верхний"), Left ("левый"), Width ("ширина"), Height ("высота"), Transform ("преобразование") и Clip ("усечение")). Эти свойства, хотя они не существенны в 3D, имеют смысл для двухмерной проекции 3D, т.е. того, чем является Viewport3D, будучи "элементом ПИ" (UiElement).

Viewport3D также будет иметь связанную с ним "камеру" (Camera) (и будут обеспечены разумные значения по умолчанию), а также свойства, относящиеся ко всей трехмерной сцене, например туман (т.е. свойства, присутствующие также на "удержанном трехмерном визуале" (Retained3DVisual)). Viewport3D также имеет Model3DCollection-значное свойство (которое само является Model3D), которое представляет коллекцию трехмерных объектов, подлежащих представлению через форму. Эта модель также может включать в себя источники света. Все, чему приложение хочет разрешить взаимодействовать по z, будет помещено в эту одну Model3DCollection для одного Viewport3D.

Если камера не задана, будет использоваться камера, принятая по умолчанию, которая обеспечивает проекцию, так что границы Viewport3D, заданные Top, Left, Width, Height, заполняются моделью, ближняя/дальняя плоскости усечения задаются прямо перед и прямо позади модели, камера смотрит по направлению оси z, причем ось y направлена вверх. Если не задано ни одного источника света, неявно используется белый направленный источник света, ориентированный по направлению оси y.

Разметка для Viewport3D аналогична показанной выше для Path, поскольку это пример, когда в разметке для Path заданы не "элементы ПИ" (UiElement). Ниже приведен пример разметки для Viewport3D:

<Canvas ...>
<!-- Здесь идут 2D элементы, и Viewport3D является 2D элементом.-->
<Viewport3D optionalCamera="..." >
<XFilePrimitive path="..." />
<Mesh...>
<BrushMaterial>
<SolidColorBrush color="yellow">
</BrashMaterial>
</Mesh>
<Mesh...>
<VisualMaterial>
<Button.../>
</VisualMaterial>
<Mesh>
<SpotLight.../>
<Model3DCollection Transform="3DTransform">
<Mesh.../>
<Другие модели, сетки, источники света, группы и т.д. ./>
<Model3DCollection>
<Viewport3D>
<!-- Другие 2D элементы -->
<Rectangle ... />
</Canvas>

Внутри тегов <Viewport3D>, Model3DCollection-значный член заполняется из отдельных, указанных разметкой Model3D. Заметим, что здесь между тегами и подклассами Model3D существует взаимно-однозначное отображение. Кроме того, заметим, что вторая "сетка" (Mesh) в этом примере имеет материал "визуала" (Visual), содержащий средство управления, заданное разметкой (в данном случае, кнопка (Button)). Это средство управления будет полностью интерактивным и может задавать OnClick и т.п.

Непосредственно дочерними узлами для Viewport3D являются Model3D, поскольку коллекция "по умолчанию" представляет собой коллекцию "модели". Синтаксис этого можно, в общем случае, изменять, чтобы потребовать задание <Viewport3D.Model> в синтаксисе сложных свойств. Эта разметка следует одному и тому же пути для ресурсов, которые подчиняются стандартной разметке 2D, например, для задания "материала" (Material) и его использования во многих местах. Эта функция обеспечивается общим механизмом ресурсов.

В общем случае, тестирование на попадание осуществляется на уровне "элемента ПИ" (UiElement) или "средства управления" (Control). Однако тестирование на попадание будет ограничиваться Viewport3D как целым, в противном случае, для тестирования на попадание потребуется разделить модели на множественные Viewport3D и тестировать их на попадание. Однако, когда они оказываются в множественных Viewport3D, они уже не являются частью одной и той же трехмерной среды. Чтобы разрешить эту ситуацию, нужно позволить задавать обработчик событий на отдельных членах Model3D в разметке или коде, а также "идентификатор" ("id"). При разрешении тестирования на попадание будет вызваться соответствующий метод (отправителем по-прежнему будет Viewport3D как целое), и идентификатор будет поступать как часть EventArgs ("аргументов события"). Затем приложения должны иметь возможность изолировать свою логику либо в разных обработчиках событий, либо отключив "идентификатор".

Использование VisualMaterial указывает системе, что тестирование на попадание должно переходить в этот внедренный элемент. Система гарантирует, что это действительно происходит. Таким образом, операция тестирования на попадание переходит из 2D в 3D и обратно в 2D. Ниже приведено задание API для Viewport3D:

public class Viewport3D : UIElement // (или Control / FrameworkElement)
{
// Стандартные свойства 2D
public BoxUnit Top {get; set;}
public BoxUnit Left {get; set;}
public BoxUnit Width {get; set;}
public BoxUnit Height {get; set;}
public Transform Transform {get; set;}
public Geometry Clip {get; set;}
// Свойства уровня трехмерной сцены
public Fog Fog {get; set}
public Camera Camera {get; set;} // имеет хорошее значение по умолчанию
// Сама трехмерная модель
public Model3D Model {get; set;}
}

Чтобы делать прозрачность правильно в 3D, примитивы (и, в пределе, отдельные треугольники) сортируются для визуализации от заднего плана к переднему, вследствие чего z-буфер не участвует. Это необходимо, чтобы гарантировать правильное смешивание.

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

Определение приведено ниже:

public class EmbeddingHostVisual: Visual
{
internal EmbeddingHostVisual(Visual hosting Visual); // не может быть создан внешне
public Visual Hosting Visual {get;}
// Все услуги чтения IVisual будет приводить к возникновению исключений.
}

Поскольку соотношение с "предком" EmbeddingHostVisual задано так, чтобы не быть обычным соотношением предок/потомок, это значит, что услуги чтения, наподобие извлечения "преобразования" (Transform) между визуалами, не имеют смысла, поскольку это не однородное отображение. Эти операции обрабатываются в самой трехмерной подсистеме, но заметим, что механизм уникален для Retained3DVisual.

СРЕДЫ

"Данные сред" (MediaData) можно использовать для воспроизведения любого аудио/видео содержимого посредством следующего объекта, также описанного в прилагаемом Приложении:

public System.Windows.Media.MediaData : Changeable, IDisposable, ITimingControl
{
public MediaData (string source);
//
// Свойства MediaData
//
public MediaState State {get;}
public Time MediaDuration {get;}
public bool HasAudio {get;}
public bool Has Video {get;}
public long Width {get;}
public long Height {get;}
public double Volume {get; set;}
public bool Mute {get; set;}
//
// Методы MediaData
//
public void Play();
//
// Changeable
//
public bool IsChangeable {get;} // по умолчанию "истина"
public Changeable Copy();
public void MakeUnchangeable();
public UseStatus StatusOfNextUse {get; set;} // по умолчанию "ложь"
public event EventHandler Changed { add; remove; }
public UIContext UIContext {get;}
//
// Интерфейс IDisposable
//
public void Dispose();
//
// Интерфейс ITimingControl
//
double Acceleration {get; set;}
bool AutoReverse {get; set;}
TimeSyncValue Begin {get; set;}
double Deceleration {get; set;}
Time Duration {get; set;}
TimeSync Value End {get; set;}
TimeEndSync EndSync {get; set;}
TimeFill Fill {get; set;}
TimeFill FillDefault {get; set;}
Timeline ParentTimeline {get; set;}
double RepeatCount {get; set;}
Time RepeatDuration {get; set;}
TimeRestart Restart {get; set;}
TimeRestart RestartDefault {get; set;}
double Speed {get; set;}
int CurrentRepeat {get;}
Time CurrentTime {get;}
bool IsForwardProgressing {get;}
bool IsPaused {get;}
bool IsReversed {get;}
double Progress {get;}
bool IsChanging {get;}
bool IsEnabled {get;}
bool IsOverridingBaseValue {get;}
bool HasChanged {get;}
void Enable();
void Enable(Timeline parentTimeline);
void Disable();
void BeginIn(Time offset);
void EndIn(Time offset);
void Pause();
void Resume();
void Seek(Time offset, TimeSeekOrigin origin);
event EventHandler Begun {add; remove;}
event EventHandler Ended {add; remove;}
event EventHandler Paused {add; remove; }
event EventHandler Repeated {add; remove;}
event EventHandler Resumed {add; remove;}
event EventHandler Reversed {add; remove;}
event EventHandler Seeked {add; remove;}
}

Для аудиоданных предусмотрен объект AudioData:

public System.Windows.Media.AudioData: MediaData
{
public AudioData (string source);
//
// MediaData
//
public MediaState State {get;}
public Time MediaDuration {get;}
public bool HasAudio {get;}
public bool Has Video {get;}
public long Width {get;}
public long Height {get;}
public double Volume {get; set;}
public bool Mute {get; set;}
public void Play();
public void Dispose();
public bool IsChangeable {get;} // по умолчанию "истина"
public Changeable Copy();
public void MakeUnchangeable();
public UseStatus StatusOfNextUse {get; set;} // по умолчанию "ложь"
public event EventHandler Changed {add; remove;}
public UIContext UIContext {get;}
double Acceleration {get; set;}
bool AutoReverse {get; set;}
TimeSyncValue Begin {get; set;}
double Deceleration {get; set;}
Time Duration {get; set;}
TimeSync Value End {get; set;}
TimeEndSync EndSync {get; set;}
TimeFill Fill {get; set;}
TimeFill FillDefault {get; set;}
Timeline ParentTimeline {get; set;}
double RepeatCount {get; set;}
Time RepeatDuration {get; set;}
TimeRestart Restart {get; set;}
TimeRestart RestartDefault {get; set;}
double Speed {get; set;}
int CurrentRepeat {get;}
Time CurrentTime {get;}
bool IsForwardProgressing {get;}
bool IsPaused {get;}
bool IsReversed {get;}
double Progress {get;}
bool IsChanging {get;}
bool IsEnabled {get;}
bool IsOverridingBaseValue {get;}
bool HasChanged {get;}
void Enable();
void Enable(Timeline parentTimeline);
void Disable();
void BeginIn(Time offset);
void EndIn(Time offset);
void Pause();
void Resume();
void Seek(Time offset, TimeSeekOrigin origin);
event EventHandler Begun {add; remove;}
event EventHandler Ended {add; remove;}
event EventHandler Paused {add; remove;}
event EventHandler Repeated {add; remove;}
event EventHandler Resumed {add; remove;}//
event EventHandler Reversed {add; remove;}
event EventHandler Seeked {add; remove;}
}

Для видеоданных предусмотрен объект VideoData:

public System.Windows.Media.VideoData: MediaData
{
public VideoData (string source);
//
// MediaData
//
public MediaState State {get;}
public Time MediaDuration {get;}
public bool HasAudio {get;}
public bool HasVideo {get;}
public long Width {get;}
public long Height {get;}
public double Volume {get; set;}
public bool Mute {get; set;}
public void Play();
public void Dispose();
public bool IsChangeable {get;} // по умолчанию "истина"
public Changeable Copy();
public void MakeUnchangeable();
public UseStatus StatusOfNextUse {get; set;} // по умолчанию "ложь"
public event EventHandler Changed { add; remove; }
public UIContext UIContext {get;}
double Acceleration {get; set;}
bool AutoReverse {get; set;}
bool IsOverridingBaseValue {get;}
bool HasChanged {get;}
void Enable();
void Enable(Timeline parentTimeline);
void Disable();
void BeginIn(Time offset);
void EndIn(Time offset);
void Pause();
void Resume();
void Seek(Time offset, TimeSeekOrigin origin);
event EventHandler Begun {add; remove;}
event EventHandler Ended {add; remove;}
event EventHandler Paused {add; remove;}
event EventHandler Repeated {add; remove;}
event EventHandler Resumed {add; remove;}
event EventHandler Reversed {add; remove;}
event EventHandler Seeked {add; remove;}
}

Также предусмотрен объект Video уровня элементов:

public System.Windows.Controls.Video : FrameworkElement
{
public Video();
//
// Свойства видеоэлемента
//
public VideoData Source {get; set;}
public MediaState MediaState {get;}
public double Volume {get; set;}
public bool Mute {get; set;}
public int NativeVideoHeight {get;}
public int NativeVideoWidth {get;}
public Time MediaDuration {get;}
public TimeSyncValue Begin {get; set;}
public TimeSync Value End {get; set;}
public Time Duration {get; set;}
public double RepeatCount {get; set;}
public Time RepeatDuration {get; set;}
public HorizontalAlignment HorizontalAlignment {get; set;}
public VerticalAlignment VerticalAlignment {get; set;}
public Stretch Stretch {get; set;}
//
// Методы видеоэлемента
//
public void BeginIn(Time offset);
public void EndIn(Time offset);
public void Pause();
public void Resume();
public void Seek(Time offset, TimeSeekOrigin origin);
}

Предусмотрен также объект Audio уровня элементов:

public System.Windows.Controls.Audio: FrameworkElement
{
public Audio();
//
// Свойства аудиоэлемента
//
public AudioData Source {get; set;}
public MediaState MediaState {get;}
public double Volume {get; set;}
public bool Mute {get; set;}
public Time MediaDuration {get;}
public TimeSyncValue Begin {get; set;}
public TimeSync Value End {get; set;}
public Time Duration {get; set;}
public double RepeatCount {get; set;}
public Time RepeatDuration {get; set;}
//
// Методы аудиоэлемента
//
public void BeginIn(Time offset);
public void EndIn(Time offset);
public void Pause();
public void Resume();
public void Seek(Time offset, TimeSeekOrigin origin);
}

ЗАКЛЮЧЕНИЕ

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

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

1. Реализуемый компьютером способ организации данных векторной графики для обработки с получением выходных данных, содержащий этапы, на которых:
принимают функциональный вызов через интерфейс прикладного программирования уровня интеграции сред, при этом уровень интеграции сред находится среди множества уровней в окружении обработки графики и содержит множество типов объектов, причем эти объекты включают в себя представляющий собой средство управления визуальными объектами объект VisualManager, который управляет визуализацией дерева VisualTree визуальных объектов в среде, при этом функциональный вызов содержит связанные с графикой данные;
интерпретируют посредством синтаксического анализатора/транслятора функциональный вызов, содержащий связанные с графикой данные, обуславливая изменение данных в структуре данных графа сцены, причем синтаксический анализатор/транслятор выполнен с возможностью интерпретировать данные языка разметки в собственном формате, прямые кодовые вызовы, кодовые вызовы объектной модели и основывающуюся на XML разметку, и
обуславливают изменение в отображении графики в ответ на изменение данных в графе сцены.

2. Способ по п.1, в котором при обуславливании изменения данных в структуре данных графа сцены вызывают функцию, чтобы инициализировать новый экземпляр класса визуального объекта, не связанного с рисованием.

3. Способ по п.2, дополнительно содержащий этап, на котором принимают функциональный вызов через интерфейс, соответствующий преобразованию, связанному с визуальным объектом.

4. Способ по п.1, в котором при обуславливании изменения данных в структуре данных графа сцены вызывают функцию, чтобы инициализировать новый экземпляр класса визуального объекта рисования.

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

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

7. Способ по п.6, в котором при приеме данных кисти принимают данные, соответствующие чистому цвету.

8. Способ по п.6, в котором при приеме данных кисти принимают данные, соответствующие кисти линейного градиента, и коллекцию ограничителей, содержащую по меньшей мере один ограничитель.

9. Способ по п.6, в котором при приеме данных кисти принимают данные, соответствующие кисти радиального градиента.

10. Способ по п.6, в котором при приеме данных кисти принимают данные, соответствующие изображению.

11. Способ по п.10, дополнительно содержащий этап, на котором принимают функциональный вызов через интерфейс, соответствующий эффекту изображения, для применения к изображению.

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

13. Способ по п.1, в котором при обуславливании изменения данных в структуре данных графа сцены вызывают функцию, связанную с геометрией, для представления эллипса в структуре данных графа сцены.

14. Способ по п.1, в котором при обуславливании изменения данных в структуре данных графа сцены вызывают функцию, связанную с геометрией, для представления прямоугольника в структуре данных графа сцены.

15. Способ по п.1, в котором при обуславливании изменения данных в структуре данных графа сцены вызывают функцию, связанную с геометрией, для представления пути в структуре данных графа сцены.

16. Способ по п.1, в котором при обуславливании изменения данных в структуре данных графа сцены вызывают функцию, связанную с геометрией, для представления линии в структуре данных графа сцены.

17. Способ по п.1, в котором при обуславливании изменения данных в структуре данных графа сцены вызывают функцию, связанную с тестированием визуального объекта на попадание в структуре данных графа сцены.

18. Способ по п.1, в котором при обуславливании изменения данных в структуре данных графа сцены вызывают функцию, связанную с преобразованием координат визуального объекта в структуре данных графа сцены.

19. Способ по п.1, в котором при обуславливании изменения данных в структуре данных графа сцены вызывают функцию, связанную с вычислением ограничивающего прямоугольника визуального объекта в структуре данных графа сцены.

20. Способ по п.1, в котором при обуславливании изменения данных в структуре данных графа сцены вызывают функцию через общий интерфейс к визуальному объекту в структуре данных графа сцены.

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

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

23. Способ по п.1, в котором при обуславливании изменения данных в структуре данных графа сцены вызывают функцию для помещения данных изображения в структуру данных графа сцены.

24. Способ по п.23, в котором при обуславливании изменения данных в структуре данных графа сцены вызывают функцию для помещения объекта эффекта изображения в структуру данных графа сцены, который связан с данными изображения.

25. Способ по п.1, в котором при обуславливании изменения данных в структуре данных графа сцены вызывают функцию для помещения данных, соответствующих тексту, в структуру данных графа сцены.

26. Способ по п.1, в котором при обуславливании изменения данных в структуре данных графа сцены вызывают функцию для обеспечения контекста рисования в ответ на функциональный вызов.

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

28. Способ по п.1, в котором при обуславливании изменения данных в структуре данных графа сцены вызывают функцию для помещения трехмерного визуального объекта в структуру данных графа сцены.

29. Способ по п.28, в котором при обуславливании изменения данных в структуре данных графа сцены задают соответствие двухмерной поверхности трехмерному визуальному объекту.

30. Способ по п.1, в котором при обуславливании изменения данных в структуре данных графа сцены вызывают функцию для помещения данных анимации в структуру данных графа сцены.

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

32. Способ по п.31, в котором средство композиции интерполирует графические данные на основании временной шкалы, чтобы анимировать выходные данные, соответствующие объекту в структуре данных графа сцены.

33. Способ по п.1, в котором при приеме функционального вызова через интерфейс уровня интеграции сред принимают разметку, при этом при обуславливании изменения данных в структуре данных графа сцены выполняют синтаксический разбор этой разметки в вызов к интерфейсу объекта.

34. Способ по п.1, в котором при обуславливании изменения данных в структуре данных графа сцены вызывают функцию для помещения объекта, соответствующего аудио- и/или видеоданным, в структуру данных графа сцены.

35. Способ по п.1, в котором при обуславливании изменения данных в структуре данных графа сцены изменяют изменяемое значение объекта в структуре данных графа сцены.

36. Система для организации данных, относящихся к графике, и/или данных, относящихся к среде, в вычислительном окружении, содержащая структуру данных графа сцены многоуровневой системы для содержания данных, которые можно визуализировать в выходные данные для последующей интеграции выходных данных, которые можно просматривать, интерфейс прикладного программирования уровня интеграции сред для вызова функций для изменения содержимого структуры данных графа сцены, при этом уровень интеграции сред находится среди множества уровней в окружении обработки графики и содержит множество типов объектов, причем эти объекты включают в себя представляющий собой средство управления визуальными объектами объект VisualManager, который управляет визуализацией дерева VisualTree визуальных объектов в среде, и синтаксический анализатор/транслятор, который интерпретирует функциональные вызовы, которыми вызываются упомянутые функции для изменения содержимого структуры данных графа сцены и которые принимаются в упомянутом интерфейсе прикладного программирования, причем синтаксический анализатор/транслятор выполнен с возможностью интерпретировать данные языка разметки в собственном формате, прямые кодовые вызовы, кодовые вызовы объектной модели и основывающуюся на XML разметку.

37. Система по п.36, в которой по меньшей мере одна функция вызывается для помещения дерева визуальных объектов в структуру данных графа сцены.

38. Система по п.37, в которой дерево визуальных объектов содержится в объекте коллекции визуальных объектов.

39. Система по п.36, в которой по меньшей мере одна функция вызывается для помещения визуального объекта в структуру данных графа сцены.

40. Система по п.39, в которой по меньшей мере одна функция вызывается для связывания кисти с визуальным объектом.

41. Система по п.39, в которой по меньшей мере одна функция вызывается для связывания геометрии с визуальным объектом.

42. Система по п.41, в которой геометрия содержит по меньшей мере одно из группы, содержащей геометрию эллипса, геометрию прямоугольника, геометрию линии и геометрию пути.

43. Система по п.39, в которой по меньшей мере одна функция вызывается для связывания преобразования с визуальным объектом.

44. Система по п.43, в которой преобразование содержит преобразование поворота для изменения воспринимаемого угла визуального объекта.

45. Система по п.43, в которой преобразование содержит преобразование масштабирования для изменения воспринимаемого размера визуального объекта.

46. Система по п.43, в которой преобразование содержит преобразование параллельного переноса для изменения воспринимаемого положения визуального объекта.

47. Система по п.43, в которой преобразование содержит преобразование перекоса для изменения воспринимаемого перекоса визуального объекта.

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

49. Система по п.39, в которой по меньшей мере одна функция вызывается для связывания цвета с визуальным объектом.

50. Система по п.39, в которой по меньшей мере одна функция вызывается для связывания данных градиента с визуальным объектом.

51. Система по п.39, в которой по меньшей мере одна функция вызывается для связывания мозаичной кисти с визуальным объектом.

52. Система по п.39, в которой по меньшей мере одна функция вызывается для связывания изображения с визуальным объектом.

53. Система по п.39, в которой по меньшей мере одна функция вызывается для связывания трехмерных данных с визуальным объектом.

54. Система по п.39, в которой по меньшей мере одна функция вызывается для связывания рисования, содержащего примитивы рисования, с визуальным объектом.

55. Система по п.39, в которой по меньшей мере одна функция вызывается для связывания данных аудио- и/или видеосред с визуальным объектом.

56. Система по п.39, в которой по меньшей мере одна функция вызывается для связывания эффекта изображения с визуальным объектом.

57. Система по п.39, в которой по меньшей мере одна функция вызывается для связывания пера с визуальным объектом для описания того, как рисуется контур формы.

58. Система по п.39, в которой по меньшей мере одна функция вызывается для получения контекста рисования, связанного с визуальным объектом.

59. Система по п.39, в которой по меньшей мере одна функция вызывается для связывания данных тестирования на попадание с визуальным объектом.

60. Система по п.39, в которой по меньшей мере одна функция вызывается для связывания прямоугольника с визуальным объектом.

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

62. Система по п.60, в которой по меньшей мере одна функция вызывается для описания того, как содержимое размещается по вертикали в контейнере, соответствующем визуальному объекту.

63. Система по п.60, в которой по меньшей мере одна функция вызывается для описания того, как содержимое размещается по горизонтали в контейнере, соответствующем визуальному объекту.

64. Система для организации данных, относящихся к графике, и/или данных, относящихся к среде, в вычислительном окружении, содержащая:
средство для приема функционального вызова через интерфейс прикладного программирования уровня интеграции сред, при этом уровень интеграции сред находится среди множества уровней в окружении обработки графики и содержит множество типов объектов, причем эти объекты включают в себя представляющий собой средство управления визуальными объектами объект VisualManager, который управляет визуализацией дерева VisualTree визуальных объектов в среде, при этом функциональный вызов содержит связанные с графикой данные;
средство для интерпретации функционального вызова, содержащего связанные с графикой данные, обуславливая изменение данных в структуре данных графа сцены, причем данное средство выполнено с возможностью интерпретировать данные языка разметки в собственном формате, прямые кодовые вызовы, кодовые вызовы объектной модели и основывающуюся на XML разметку, и
средство визуализации для обуславливания изменения в отображении графики в ответ на изменение данных в структуре данных графа сцены.

65. Система по п.64, в которой средство визуализации включает в себя средство композиции низкого уровня для построения кадра для просмотра на основании данных, принятых от средства композиции высокого уровня.

66. Система по п.64, дополнительно содержащая средство анимации, при этом средство композиции высокого уровня предоставляет данные временной шкалы средству композиции низкого уровня для интерполяции внешнего вида видимых данных по, по меньшей мере, двум кадрам для анимирования видимых данных во времени.



 

Похожие патенты:

Изобретение относится к вводу данных в электронные устройства. .

Изобретение относится к медицинской технике и может быть использовано для людей с параличами верхних и нижних конечностей. .

Изобретение относится к печатающей системе, в частности к устройству обеспечения изображения и способу управления этим устройством. .

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

Изобретение относится к электронному обмену сообщениями и, в частности, относится к фильтрации нежелательной электронной почты. .

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

Изобретение относится к мобильным информационным устройствам. .

Изобретение относится к многоплоскостным пользовательским интерфейсам устройств обработки данных компьютерных систем

Изобретение относится к клавиатуре процессорного устройства и, в частности, к дисплейному блоку с клавиатурой, расположенной над экраном компьютерного дисплея

Изобретение относится к области обработки цифровых данных с помощью электрических устройств, а именно к конструкциям указательных устройств, перемещаемых пользователем

Изобретение относится к навигационным портативным системам для установки, в частности, в автомобиле

Изобретение относится к устройствам ввода

Изобретение относится к средствам вывода на печать цифровых данных

Изобретение относится к управлению карманным устройством, обладающим сенсорным экраном, миниатюрной клавиатурой или набором мелких управляющих кнопок, при помощи управляющих устройств стилусообразного типа, а также к конструкциям перчаток и напальчников

Изобретение относится к области компьютерной техники, а именно к модулю, который состоит из телевизионной карты и пульта дистанционного управления

Изобретение относится к совместному использованию прикладных программ вычислительных устройств и, более конкретно, к синхронизированным графическим данным и данным области для систем дистанционной работы с графическими данными
Наверх