Код документа: RU2360275C2
ОБЛАСТЬ ТЕХНИКИ, К КОТОРОЙ ОТНОСИТСЯ ИЗОБРЕТЕНИЕ
Изобретение, в общем случае, относится к компьютерным системам и, в частности, к обработке графической и иной видеоинформации для отображения в компьютерных системах.
ПРЕДШЕСТВУЮЩИЙ УРОВЕНЬ ТЕХНИКИ
Традиционная модель непосредственного режима для доступа к графике в компьютерных системах достигла своего предела, в частности, потому, что быстродействие памяти и шины не соответствует развитию главных процессоров и/или графических процессоров. В общем случае, современная модель (например, 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) обычно объявляется вверху файла разметки следующим образом:
Визуал - это контейнер для графического содержимого и множества потомков. Различные свойства визуала можно использовать для управления поведением визуализации визуала. Например, благодаря заданию отсечения на визуале содержимое визуала усекается до заданной формы. Другие свойства представляют собой "преобразование", "режим смешивания", "непрозрачность", "показ" и т.д. Всеми этими свойствами можно управлять через свойства 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), которая позволяет пользователю осуществлять такие операции, как добавление, удаление, вставка и т.д. на множестве потомков. Ниже показан пример "коллекции визуалов":
Порядок визуалов в "коллекции визуалов" определяет порядок визуализации визуалов, т.е. визуалы визуализируются от самого низкого индекса к самому высокому индексу от заднего плана к переднему плану (порядок раскраски).
"Посреднический визуал" (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().
Заметим, что операции проталкивания могут быть произвольным образом выложены, пока операции выталкивания соответствуют проталкиванию. Например, справедливо следующее:
"Геометрия" (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)) для визуализации дерева визуалов на устройстве:
Производный визуал может делать обратный вызов пользователю в ходе вызова RenderContents.
На фиг.13 представлена иерархия классов "визуал" в одной реализации. В ходе обратных вызовов, которые имеют место как часть прохода визуализации (содержащих обратный вызов на IRetainedVisual.Render или обратный вызов на PaintingVisual.RenderCore), дерево визуалов "фиксируется" из соображений производительности. Эта фиксация осуществляется для любого контекста, т.е. никакой контекст не может изменить дерево, когда оно зафиксировано, вне зависимости от того, какому дереву визуалов принадлежит визуал. Когда дерево зафиксировано, потомки визуала не могут изменяться, и содержимое других визуалов нельзя изменить никаким способом (например, Open, 3D модель задания корня и т.д.), "преобразование", "непрозрачность", "усечение", "режим смешивания" или "эффект" на визуале нельзя устанавливать, тестирование на попадание не работает, а также не работает операция получения информации границ.
Функциональные возможности на визуале предоставляются через интерфейс IVisual, что позволяет делать функциональные возможности открытыми, в то же время защищая объектную модель. Ниже показан интерфейс Ivisual в одной иллюстративной реализации:
Визуал содержит содержимое визуализации и коллекцию потомков. Ряд свойств, в том числе преобразование, усечение, непрозрачность, режим смешивания и т.д., можно использовать для управления фактической визуализацией дерева визуалов. Заметим, что визуалу не требуется одновременно иметь содержимое и потомков. В одной реализации содержимое визуализации и коллекцию потомков можно создавать по требованию для оптимизации использования памяти. API визуала (Visual) позволяет пользователю делать его производным от Visual и конкретизировать его.
Преобразования потомков осуществляются посредством преобразования на свойстве "дети" (Children) типа "коллекция визуалов" (VisualCollection).
Услуги чтения преобразования обеспечивают методы, которые позволяют пользователям получать матрицу (Matrix), представляющую составное преобразование из одной системы координат в другую:
Методы TransformToAncestor и TransformToDescendant более эффективны, но требуют, чтобы вызывающая сторона знала соотношение между двумя визуалами. Более общие методы TransformTo/FromVisual находят общего предка и вычисляют преобразование к этому визуалу. Заметим, что они могут обуславливать обновление блоков кэш-памяти и вызовы OnRender на произвольных визуалах. Если визуалы не связаны, или встречается вырожденное преобразование, вырывается исключение.
Предусмотрено также вычисление границ:
VisualDescendantBounds возвращает объединение ограничивающих прямоугольников содержимого для потомков текущего визуала, но не включающих в себя содержимое текущего визуала. VisualContentBounds возвращает ограничивающий прямоугольник для содержимого текущего визуала.
Свойство Opacity ("непрозрачность") (например, число с плавающей точкой с двойной точностью (double)) задает необязательное "значение непрозрачности" для применения к визуалу, при композиции в его родителя. По умолчанию, это значение равно 1,0, при котором содержимое появляется с полной "непрозрачностью". Заметим, что поскольку это значение перемножается любыми другими данными "непрозрачности" в подграфе, "непрозрачность" 1,0 ничего не меняет. "Непрозрачность" 0,0 обуславливает прозрачность всего содержимого, значение 0,25 обуславливает непрозрачность, составляющую двадцать пять процентов от его номинального значения и т.д. Непрозрачность применяется до "режима смешивания" (BlendMode).
"Режим смешивания" - это свойство, которое задает необязательный "режим смешивания" для применения к содержимому подграфа и пункту назначения при композиции этого визуала. По умолчанию, это значение равно BlendMode.Normal, что осуществляет композицию со знанием альфа-канала в пункт назначения. В случае задания этого свойства равным некоторому другому значению, будет осуществляться композиция с содержимым визуала в качестве источника и содержимым конечного пункта в качестве пункта назначения. Это применяется после применения свойства "непрозрачность".
Базовый класс Visual представляет интерфейсы для особенностей, которые являются общими для визуалов:
ContainerVisual является непосредственно производным от класса Visual и продвигает защищенные свойства так, чтобы они были открытыми. Это позволяет пользователю создавать контейнерность визуала без необходимости создавать новый производный класс.
RetainedVisual - это визуал, который вводит "поток удержанных команд", который можно использовать для рисования:
Поток команд можно использовать в режиме "по требованию" (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.
Предусмотрен также "визуал раскраски" (PaintingVisual):
В то время как "удержанный визуал" добавляет поток удержанных команд, "визуал раскраски", по существу, поддерживается поверхностью. Система может виртуализовать поверхность и все же сохранять команды визуализации, покуда удовлетворяются требования производительности. По этой причине поверхность не доступна пользователю.
Одно отличие "визуала раскраски" от "визуала рисования" состоит в том, что методы обеспечивают "статический контекст рисования" (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.
Управление менеджера окон находится вне этого объекта, однако его можно объединить с "контекстом окна" WindowContext, придав свойствам (например, размеру) значения чтения/записи и обеспечив дополнительные свойства, например положение, заголовок окна и т.д. Заметим, что "размер" представляет размер окна в физических единицах (1/96 дюйма). Это не размер пикселя. Заметим, что могут быть ситуации, когда данные, визуализированные в окне, теряются по какой-то причине, например переключение видеорежима или переключение от локальной консоли на сеанс сервера удаленного терминала.
"Визуализаторы визуала" и "менеджеры визуалов" являются другими объектами, отвечающими за визуализацию дерева визуалов в конечный пункт визуализации. Визуализатор визуала обеспечивает простую модель "одной вспышки", которая визуализирует в среду, тогда как менеджеры визуалов устанавливают удержанную связь между деревом визуалов и пунктом назначения, на который они визуализируются. Так поддерживается "инкрементальная" визуализация в среду.
Ниже приведен пример того, как выглядит "визуализатор визуала" в одной реализации:
Экземпляр этого класса нельзя создавать открытым образом, ввиду отсутствия среды "по умолчанию". Визуализатор визуала также является объектом ContextAffinity.
Предусмотрено свойство "цвет фона" (BackgroundColor):
Это принятый по умолчанию цвет фона менеджера визуалов, который может быть по умолчанию прозрачным для менеджеров визуалов. Однако некоторые среды (например, визуализация в традиционные HWnds) не могут поддерживать попиксельную прозрачность, и, таким образом, каждый менеджер визуалов может задавать свое собственное значение по умолчанию для этого свойства. Большинство приложений будут игнорировать это свойство и, например, могут быть установлены на цвет фона системного окна или прозрачными.
Свойство "корневой визуал" (RootVisual) идентифицирует корневой визуал для визуализации:
Он задан по умолчанию равным null. Когда свойство "корневой визуал" равен null, менеджер визуалов рисует цвет фона в среду.
Свойство "размер корня" RootSize возвращает, в виртуальных единицах, размер конечного пункта визуализации. Например, для менеджера визуалов, поддерживаемого окном, это будет клиентским размером окна:
Предусмотрена также информация разрешения:
Каждая среда должна иметь разрешение устройства, даже если не поддерживается пикселями. Например, при печати, даже при захвате в метафайл, информацию разрешения нужно сделать доступной через "менеджер визуалов", чтобы можно было оптимизировать содержимое для этого разрешения. Заметим, что в этом случае, захвата метафайла, можно использовать относительно высокое принятое по умолчанию разрешение, в то же время позволяя пользователю непосредственно конфигурировать разрешение.
Исходное преобразование "мира в устройство", заданное для "корневого визуала" делает так, что одна единица в этом визуале равна 1/96 дюйма на устройстве. Например, при наличии "менеджера визуала экрана" (ScreenVisualManager), который поддерживается устройством, имеющим 192 точек на дюйм, то исходное преобразование должно быть задано так, что одна единица в системе координат для "корневого визуала" должна быть равна двум единицам на устройстве. В этом случае, ResoultionInformation.PixelSize будет возвращать (0,5, 0,5), чтобы указывать, что каждый пиксель равен 1/48 дюйма на стороне.
WindowVisualManager - это основной способ рисования на экране. Он управляет визуализацией дерева визуалов в "контекст окна" (WindowContext):
КОНТЕКСТ РИСОВАНИЯ
API DrawingContext ("контекст рисования") представляет привычную для специалистов в данной области модель программирования "на основе контекста", позволяющую конструировать содержимое визуала, которое заполняет визуал или визуализируется в "данные изображения" (ImageData). В этом разделе описаны классы "контекст рисования" (DrawingContext), а также классы и точки входа, необходимые для получения "контекста рисования" и перечисления содержимого визуала в "удержанном визуале"/"визуале рисования".
Приложения не строят непосредственно "контекст рисования", и представленные версии "контекста рисования" являются абстрактными классами. Существует несколько способов получения "контекста рисования" для помещения туда содержимого визуала. Они включают в себя RetainedVisual.RenderOpen() или RetainedVisual.RenderAppend(), каждая из которых возвращает "контекст рисования" для выдачи в него команд. Другие способы включают в себя IRetainedRender.Render(), DrawingVisual.RenderOpen(), DrawingVisual.RenderAppend() и PaintingVisual.PaintingOpen() или PaintingVisual.PaintingAppend() (хотя "визуалы раскраски" не обрабатывают анимации). "Данные изображения" (ImageData) (или подкласс "данных изображения") имеет механизм возврата "контекста рисования" для визуализации на поверхность битового образа фиксированного разрешения. "Данные изображения" также не обрабатывают анимации.
Ниже приведен API "контекста рисования"
Большинство методов объекта "контекст рисования" самоочевидны для специалистов в данной области, однако следует заметить, что "контекст рисования" является 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:
В базовом классе DrawingContextWalker предусмотрен метод WalkContent:
Пользователи создают его подклассы и, по желанию, реализуют все абстрактные методы. Пользователи могут создавать экземпляр объекта и вызывать на нем WalkContent. Затем WalkContent осуществляет обратный вызов соответствующих методов по мере обхода визуала. Реализация любого из этих методов может остановить обход, при желании, путем вызова защищенного метода StopWalking(). Заметим, что незаконно начинать обход, когда "контекст рисования" на визуале открыт для визуализации в него.
Опции определяют, как совершается обход:
Если установлено 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.
В целях основного использования изменяемый объект включает в себя следующие свойство и методы:
Свойство IsChangeable указывает, можно ли изменить изменяемый объект в зависимости от его текущего значения. Например, попытка задать свойство непрозрачности кисти будет успешна только, если этот объект кисти имеет свойство IsChangeable, равное "истина". В противном случае возникнет исключение. При конструировании изменяемые объекты имеют свойство IsChangeable, равное "истина" по умолчанию, и потому являются непосредственно изменяемыми.
Согласно фиг.14 запросы 1402 принимаются, например, посредством функциональных вызовов, исходящих от прикладной программы, которые адресованы изменяемым классам 1404. В общем случае, обработчик 1406 запросов, включающий в себя конечный автомат 1408, обрабатывает запросы и поддерживает данные состояния и объекта через поддерживающую структуру данных 1410, клонируя структуру данных в клонированную копию 1412 с соответствующим состоянием свойств, в зависимости от текущего состояния свойств, как описано ниже. Исключения 1414 могут возникать, например, в случае запроса на неразрешенный переход из текущего состояния свойств. Состояния свойств описаны ниже со ссылкой на фиг.15-17.
Следует заметить, что функциональные вызовы, адресованные изменяемым классам, можно обрабатывать прямо или косвенно. Например, обработчик 1406 запросов на фиг.14 может включать в себя набор API, который обеспечивает интерфейсы к конечному автомату. Альтернативно, обработчик 1406 запросов может содержать код посреднического программного обеспечения, который преобразует запросы, принятые в одной операционной системе, в вызовы API, обрабатываемые другой операционной системой. Таким образом, запросы в данном использовании через обработчик запросов "обуславливают" запрашиваемое поведение, независимо от того, где происходит фактическая обработка посредством конечного автомата или где обеспечены структуры данных и классы.
Таким образом, (помимо других механизмов) приложения могут конструировать изменяемые объекты посредством "нового" запроса, задавать значения в них, использовать их, продолжать задавать значения в них и продолжать использовать их.
Ниже приведен пример того, как приложение создает кисть чистого цвета (scb), изменяет кисть, чтобы она имела чистый цвет (красный), и использует кисть для окрашивания фона кнопки в красный цвет:
Понятие "использования" значения имеет особый смысл, т.е. рассматриваются только значения, подлежащие использованию при определенных условиях. Эти условия включают в себя: когда значение задано в свойство системы свойств, когда значение используется в качестве подобъекта в более сложном изменяемом объекте и когда значение используется в команде DrawingContext и т.п. Заметим, что расширители системы могут легко задавать другие экземпляры использования изменяемого объекта, который квалифицируется как "использование" и изменяет изменяемое состояние объекта.
Когда значение используется в одном из этих квалифицированных видов использования, с точки зрения пользовательской модели, делается его клон, и этот клон имеет свое свойство IsChangeable, заданное равным "ложь". Заметим, что в действительности клон не обязательно создается, а когда используется, не обязательно глубок (в иерархии объектов, как объясняется ниже). Тем не менее, с точки зрения модели следует считать, что клон делают, и, таким образом, используемое здесь понятие "клон" охватывает клон, который фактически создан, клон, созданный частично, и/или клон, логически созданный с точки зрения модели, даже если его не обязательно создавать. Клон - это то, что фактически используется, и, по умолчанию, клон нельзя изменять.
Как показано выше, изменяемый объект также содержит методы, включающие в себя Copy() и MakeUnchangeable(). Явный вызов метода Copy() создает копию изменяемого объекта, причем свойство IsChangeable копии задано равным "истина". Этот вызов не изменяет объект, на котором вызывается этот метод. Метод MakeUnchangeable() можно вызывать на любом изменяемом объекте и изменяет свойство IsChangeable, чтобы сделать его ложным (если уже не ложное, в каковом случае вызов не имеет эффекта).
Вышеозначенные механизмы обеспечивают схему для замены свойства. Для изменения изменяемого объекта значение свойства IsChangeable должно быть "истина". Поскольку квалифицированное использование объекта создает клон, который не является изменяемым, этот объект нужно копировать посредством метода Copy(), изменять и снова использовать. Это эффективно заменяет исходный объект, который присутствовал, новым объектом, который является измененной копией оригинала. Примеры этого изложены ниже. Заметим, что используемый изменяемый объект - это то, что было использовано, и, таким образом, по вышеприведенному определению не является изменяемым, поскольку свойство IsChangeable устанавливается равным "ложь" после использования. Таким образом, изменяемый объект не изменяется, но вместо этого изменяемый объект копируется и заменяется. Заметим, что в случае изменяемых объектов имеется только один набор типов, что, в общем случае, гораздо желательнее с точки зрения программирования. Кроме того, истинная изменчивость обеспечивается дополнительными свойствами, как описано ниже.
Согласно описанному выше создание кисти, ее изменение и ее использование является непосредственным. Пример простого использования в операции рисования приведен ниже:
Выполнение вышеуказанных команд рисует один красный прямоугольник и один зеленый прямоугольник. Заметим, что 'scb', в сущности, клонируется после каждого его использования.
Более сложная конструкция, использующая кисть линейного градиента (lgb), в которой цвет изменяется (например, линейно) от одного ограничителя к другому, приведена ниже:
Здесь процесс состоит в построении значений (GradientStops) и использовании их в определениях более сложных значений.
Рассмотрим другой пример, направленный на изменение непрозрачности (которая может изменяться от нуля до единицы) фона кнопки (Btn) до 0,4. В этом конкретном использовании фон (Background) копируется в объект со свойством IsChangeable, установленным на значение "истина", фон изменяется и устанавливается обратно.
Заметим, что присвоение "Bm.Background" в последней строке отделяет любое унаследованное значение или значение ведомости свойств, которое могло поступить.
Изменения, которые глубже в иерархии объектов, не выглядят для пользователя отличными от изменений, которые расположены менее глубоко:
Заметим, что Copy() нужно вызывать только на объекте верхнего уровня, а не на отдельных "ограничителях градиента" (GradientStops). Это потому, что система должна гарантировать, что подобъекты объекта со свойством IsChangeable, равным "истина", сами по себе устанавливаются с IsChangeable, равным "истина", когда к ним осуществляют доступ.
На фиг.15 показана диаграмма состояний, демонстрирующая состояния изменяемого объекта в основном использовании, начинающаяся со свойства IsChangeable, равного "истина", при новом создании. В общем случае, сплошные стрелки показывают состояния объекта, переходящего из текущего состояния в конечное состояние, в то время как любая пунктирная стрелка представляет операцию, которая оставляет объект в неизменном виде, но создает новый объект в конечном состоянии. В этой диаграмме состояний имеются два состояния, и переходы происходят при вызове либо Copy(), либо MakeUnchangeable(), и когда объект используется способом, квалифицируемым как использование, что описано выше. Заметим, что вызов метода Copy() из любого состояния приводит к новому значению с его свойством IsChangeable, заданным равным"истина", тогда как вызов MakeUnchangeable() приводит к конечному значению, причем IsChangeable задано равным "ложь".
Вышеприведенное описание представляет прямую, самосогласованную модель, которая описывает основное использование с только двумя состояниями, методы Copy() и MakeUnchangeable() и понятие "использования" значения Changeable. Однако в отношении изменений вышеприведенные примеры изменения основаны на концепции замены, т.е. копирования существующего элемента, изменения его на месте и копирования его обратно. Это предполагает резервирование памяти для выполнения (которое может быть потенциально значимым, в зависимости от того, насколько глубоко должно быть произведено изменение и насколько широк объект сам по себе для мелких клонов), а также дополнительную нагрузку на программиста для поддержания некоторого механизма отыскания пути для изменения атрибута.
Согласно аспекту настоящего изобретения для добавления поддержки концепции истинной изменчивости значений к модели добавляется еще одно свойство, именуемое StatusOfNextUse типа UseStatus. Заметим, что основная проблема, препятствующая изменчивости в модели единичного свойства, состоит в том, что квалифицированное использование значения безусловно приводит к результирующему значению со свойством IsChangeable, равным "ложь". Свойство StatusOfNextUse разрешает эту проблему.
По умолчанию, свойство StatusOfNextUse равно UseStatus.Unchangeable, но может быть задано равным UseStatus.ChangeableCopy, за счет чего использование значения, на которое оно установлено, приведет к созданию объекта клона, который имеет свойство IsChangeable, заданное равным "истина". В результате значение объекта можно изменять на месте без какого-либо дополнительного резервирования памяти для выполнения.
Кроме того, поскольку значения при использовании могут изменяться в этой модели, предусмотрено извещение, когда происходят такие изменения, через простое событие изменения. Кроме того, поскольку объект уже не является неизменным, сродство к контексту обеспечивается посредством члена UIContext. Заметим, что, когда объект является изменяемым, он имеет значение null. В противном случае он принадлежит UIContext, в котором он был создан. Результирующее определение класса Changeable будет:
Вышеприведенный пример простой, неглубокой изменчивости описывал требования к изменению непрозрачности на "кисти" (Brush), с кодом, который должен действовать всякий раз, когда нужно изменять непрозрачность. Напротив, с механизмом изменчивости на основе свойства StatusOfNextUse, сначала сам Btn.Background имеет значение свойства IsChangeable, равное "истина":
Выше использовалось (в квалифицированном использовании) значение с StatusOfNextUse равным UseStatus.ChangeableCopy, так что результат сам является изменяемым. После установки программист может производить нужные изменения, как в нижеследующем примере:
Программист может производить такие изменения так часто, как он хочет, и изменение будет происходить напрямую, без какого-либо выделения объекта при последующих настройках.
Заметим, что вышеприведенный пример не описывает, как Btn.Background появляется в первом месте, и, таким образом, его копию нужно делать посредством метода Copy(). В ситуации преднамеренной изменчивости, когда программист хочет создать фон, подлежащий изменению, лучше всего сделать это непосредственно, как в следующем примере:
При этом программист может задавать непрозрачность (Btn.Background.Opacity = ...) всякий раз, когда это необходимо, поскольку кисть первоначально была создана как StatusOfNextUse, равный UseStatus.ChangeableCopy.
Следует заметить, что использование модели на основе замены, а не модели на основе изменения не представляет большой трудности ввиду вышеприведенных примеров. Причина в том, что изменения производятся на первом уровне, и может не оказаться запретительно дорогим всегда заменять вместо того, чтобы изменять. Действительно, это техника пригодна, когда требуется лишь ограниченная изменчивость. Однако, когда изменения производятся над значениями глубже в объекте, модель изменения явно имеет преимущество.
В качестве примера такой более глубокой изменчивости рассмотрим LinearGradientBrush (lgb), где программист повторно хочет изменить свет седьмого ограничителя (lgb.Stops[6]). Программист может использовать те же команды, которые описаны выше, чтобы установить изменяемую версию в Btn.Background:
Затем программист может повторно производить нужные изменения:
Программист может также однократно обратиться к переменной "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 теперь выглядит как:
Когда изменяемый объект (Changeable), который имеет StatusOfNextUse, заданное равным UseStatus.ChangeableReference, используется (квалифицированным образом), это значение уже не копируется. Вместо этого, раздается ссылка на существующее значение, и последующие изменения этого значения (или любых ранее или затем розданных ссылок) будут влиять на результат использования. Иными словами, изменяемое значение теперь совместно используется с потенциально любым количеством использований.
Ниже приведен пример использования уровня элементов:
В вышеприведенном примере простая операция рисования была описана с двумя генерированными прямоугольниками, одним красным и одним голубым:
Это желаемое поведение. Однако, если программист вместо этого пожелает, чтобы кисть совместно использовалась, можно использовать следующие команды:
Здесь оба прямоугольника зеленые. Если позднее цвет изменяется, например 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), например, чтобы задать по умолчанию изменяемые значения.
Обращаясь к объяснению работы настоящего изобретения, настоящее изобретение обеспечивает значительные преимущества, когда имеет дело с глубокими свойствами объекта, что называют "пунктир вниз". Например, рассмотрим следующее:
Глубокий доступ в объект геометрии 'g' - это пример того, что называют "пунктир вниз". Заметим, что доступ к свойствам (геометриям, [12], фигурам, [2], отрезкам, [0] и точкам) вызывают получатели, а не установщики свойств; [17] это единственный доступ к свойству, который приводит к вызову установщика. Языки программирования, в общем случае, не могут различать между доступом к свойству с целью задания значения свойства глубже вниз и доступом для считывания значения глубже вниз.
Когда "пунктир вниз" начинается с неизменяемого объекта, осуществляется доступ к переменной локального члена. Пример включает в себя доступ к элементу, который не был сделан изменяемым путем использования свойства "ChangeableValue" ("изменяемое значение").
Когда свойство происходит из изменяемого объекта, результирующее значение также является изменяемым, так что его можно изменять. Для этого получатель свойств на родителе возвращает подобъект напрямую, если уже изменяемый, или делает неглубокий клон одного подобъекта, задает его в локальном члене и возвращает этот клон. Заметим, что эти атрибуты делают вышеописанный код, после прогона первый раз и выделения и присваивания неглубоких клонов, свободным, т.е. не требующим резервирование памяти для выполнения.
Аспект настоящего изобретения состоит в том, что неглубокое клонирование по требованию осуществляется только тогда, когда необходимо. Это максимизирует совместное использование, минимизирует резервирование памяти для выполнения, допускает изменения без резервирования памяти для выполнения и не продвигает концепцию клонирования в пользовательскую модель. Это приобретает особую важность в более глубоких деревьях и при работе в трех измерениях. Для этого метод Copy() обеспечивает иллюзию глубокой копии, но в действительности сначала делает только неглубокую копию, а затем медленно делает более глубокие копии по мере необходимости. Такой "пунктир вниз" обеспечивает значительное повышение производительности.
Согласно аспекту настоящего изобретения другое свойство на изменяемом объекте (в общем случае, невидимом для приложений) состоит в том, что изменяемый объект имеет событие "изменился" (Changed) (типа EventHandler). Когда свойство "изменяемого объекта" (Changeable) изменяется, вызывается "изменяемый делегат" на этом изменяемом объекте, с изменяющимся изменяемым объектом в качестве отправителя. Действие делания неглубокого клона посредством "пунктира вниз" проталкивает обработчики события Changed вниз в мелкий клон. Это позволяет последующим изменениям появляться на более глубоких элементах и запускать надлежащие обработчики событий. Заметим, что также существует событие Changed, так что клиенты, отличные от системы свойств, могут использовать эту систему и регистрироваться на извещения.
Изменения обработчика события Changed распространяются вниз на подобъекты. Кроме того, изменения изменяемого объекта, которое само по себе задействует другие изменяемые объекты (например, добавляет изменяемый подобъект в изменяемый объект, удаляет его и т.д.), приводят к тому, что обработчики событий, содержащих изменяемые объекты, удаляются из старых и проталкиваются в новые рекурсивно.
На фиг.18-23 показано, как действует неглубокое клонирование и "пунктир вниз", на основании следующего кода в качестве примера:
Как показано на фиг.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:
Заметим, что это идентично общему свойству, участвующему в системе свойств. Это потому, что WriteLocal будет производить особую обработку для глубоких свойств, которые являются производными от класса Changeable.
Реализатор изменяемого типа нуждается в однострочной преамбуле и однострочном постскриптуме на всем, что изменяет изменяемый объект (например, свойства). Также простая, однострочная преамбула нужна на объектах, которые обращаются к состоянию изменяемого объекта (например, получателях свойств). Нужны реализации CloneCore(), MakeUnchangeableCore(), PropagateEventHandlerCore(), PropagateEventHandlers() (заметим, что только последние три нужны для типов, которые имеют другие "изменяемые объекты" в качестве свойств), и также необходим упаковщик с сохранением типов для Copy().
Нижеприведенные примеры происходят из справочного прототипа, включающего в себя (искусственный) пример "ограничителя градиента" (GradientStop), который является простым изменяемым типом (который упрощен в том, что ни один из его подтипов не является изменяемым). Заметим, что на практике очень немногие изменяемые типы будут таким образом упрощены, поскольку все, что содержит коллекцию анимаций (саму по себе изменяемую), будет более сложным:
Ниже приведен пример "кисти линейного градиента" (LinearGradientBrush), которая является более сложным изменяемым типом (поскольку некоторые из его подтипов, а именно "ограничители градиента" (GradientStop), сами по себе являются изменяемыми):
Изменяемая объектная модель делится на открытую часть и часть, которую видят расширители и хостеры. Однако, заметим вновь, что эти вещи являются прямыми для написателя компонентов, который использует эти типы.
Заметим, что действие квалифицированного использования изменяемого объекта, опирающееся только на его 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. Заметим, что "пунктир вниз" будет снова делать подэлементы изменяемыми.
Однако при наличии "ссылки на изменяемый объект" операция клонирования должна выполняться эффективно, однако ссылка поддерживается для пути "изменяемого объекта" вниз к "ссылке на изменяемый объект". Это необходимо, чтобы при наличии извещения от "ссылки на изменяемый объект" для всего, что хостируется, вызывались правильные обработчики.
Рассмотрим следующий пример:
Здесь создается "кисть линейного градиента" в виде коллекции его "ограничителей" (Stop) и единичного "ограничителя" (Stop) и делается "ссылкой на изменяемый объект" (ChangeableReference). Кисть можно использовать во многих местах, и изменение в ChangeableReference GradientStop должно влиять на обе кисти.
ПРИМЕРЫ (ДЕЙСТВИТЕЛЬНЫЕ И НЕДЕЙСТВИТЕЛЬНЫЕ) ИСПОЛЬЗОВАНИЯ ИЗМЕНЯЕМЫХ ОБЪЕКТОВ
В нижеследующем разделе изложена сущность использования объектов и манипулирования ими, каковые являются производными от класса "изменяемый объект" (Changeable), благодаря чему такие объекты, как кисти, перья и анимации, изменяются под управлением программиста. Классы, являющиеся производными от Changeable, моделируют изменчивость путем автоматического построения своей неизменяемой версии при использовании в квалифицированном использовании. Согласно описанному выше Changeable рассматривается как используемый в квалифицированном использовании, когда объект установлен в свойство "системы свойств", используемое как подобъект в сложном объекте класса Changeable или используемое в команде "контекст рисования" (DrawingContext).
При разработке приложений с такими объектами объекты системы графики и сред, в общем случае, создаются, задаются, используются, после чего никогда не изменяются. Например, чтобы задать фон "кнопки", программист может использовать "кисть чистого цвета" (SolidColorBrush), который является производным от Changeable; но программист никогда не может вновь изменить фон кнопки в ходе выполнения приложения. Рассмотрим один пример:
При таком использовании Changeable ведет себя как тип значения, например, Rect ("прямоугольник") или Color ("цвет"). Changeable копируется в свой пункт назначения, и изменения к оригиналу не влияют на изменения используемого значения. Однако бывают ситуации, когда программисту может понадобиться изменить объект после того, как он был использован. Например, предположим, что программист хочет изменить цвет кнопки в предыдущем коде после того, как пользователь щелкает по ней.
Структура Changeable существует, чтобы удовлетворять потребность в различных ситуациях, например вышеописанной. В общем случае, Changeable - это значение, которое может или не может быть изменяемым, что определяется значением свойства IsChangeable. Попытка изменить значение, когда IsChangeable равно "ложь", приводит к исключению. Кроме того, объекты Changeable, которые можно изменять, порождают события "изменился" (Changed), когда они изменяются или когда изменяется любой из их членов. Таким образом, при работе с изменяемыми объектами (Changeable) важно понимать, когда Changeable "используется" в квалифицированном использовании.
По умолчанию, когда изменяемый объект используется в квалифицированном использовании, создается неизменяемая копия, и эта копия фактически используется. Копия имеет значение IsChangeable, равное "ложь". Следующий код обуславливает возникновение исключения, поскольку код пытается изменить неизменяемую копию myBrush, которая использовалась для задания фона кнопки:
Изменение исходного изменяемого объекта не обновляет копии:
Чтобы изменить фон кнопки в этом примере, программист повторно присваивает измененную кисть свойству фона кнопки:
Программист также может использовать метод "копирование" (Copy) для извлечения изменяемой копии используемого изменяемого объекта. Извлеченная копия по-прежнему повторно присваивается обратно свойству, чтобы иметь действие:
Поскольку это не идеальное поведение Changeable во всех ситуациях, например, если программист хочет изменить используемую версию (рабочую копию) Changeable, класс Changeable позволяет программисту задавать, как он ведет себя при использовании, обеспечивая свойства StatusOfNextUse.
StatusOfNextUse предусматривает три варианта поведения Changeable при использовании:
Unchangeable ("неизменяемый"): поведение по умолчанию, показанное в примерах в предыдущем разделе. Когда используется объект Changeable, он создает неизменяемую копию самого себя, которая используется вместо исходного объекта. Программист может продолжать изменять исходный объект; исходная версия (сделанная копия) не подвержена изменениям, вносимым в исходный объект, и не может быть изменена. Для изменения используемой версии используется метод "копирование" для получения изменяемой версии, эта версия обновляется, и новая версия заменяет используемую версию.
ChangeableCopy ("изменяемая копия"): когда используется объект Changeable, он создает изменяемую копию самого себя, которая используется вместо исходного объекта. Программист может продолжать изменять исходный объект; исходная версия (сделанная копия) не подвержена изменениям, вносимым в исходный объект, но также является изменяемой. Используемая версия имеет StatusOfNextUse, равный Unchangeable.
ChangeableReference ("ссылка на изменяемый объект"): когда используется объект Changeable, он обеспечивает ссылку на самого себя. Программист может продолжать изменять исходный объект; изменения исходного объекта влияют на используемую версию - они являются одним и тем же объектом.
ChangeableCopy изменяет поведение Changeable так, что при использовании он создает изменяемую копию самого себя, а не неизменяемую копию (как в случае, когда по умолчанию задано Unchangeable. Нижеследующие код (показанный ранее) вызывает исключение, поскольку свойство StatusOfNextUse для myBrush по умолчанию задано равным Unchangeable:
Если же свойство StatusOfNextUse кисти задано равным ChangeableCopy, код работает, как предусмотрено:
Задание ChangeableCopy также сохраняет любые подобъекты главного объекта изменяемыми. В следующем примере "кисти линейного градиента" (LinearGradientBrush) придается StatusOfNextUse, равный ChangeableCopy. В результате "кисть линейного градиента" и ее подобъекты остаются изменяемыми после того, как их использовали; программисту не нужно задавать свойство StatusOfNextUse любых объектов Changeable, содержащихся в объекте, в этом примере, GradientStop:
При использовании объектов Changeable с StatusOfNextUse равным ChangeableCopy, программист также может удержать описатель используемой версии Changeable и использовать эту ссылку для изменения объекта. В следующем примере ссылка на используемую LinearGradientBrush извлекается и используется для изменения фона кнопки:
Задание ChangeableReference изменяет поведение Changeable, так что он обеспечивает ссылку на самого себя при использовании. Программист может продолжать изменять исходный объект; изменения исходного объекта влияют на используемую версию, поскольку они являются одним и тем же объектом. Ниже приведен пример:
КИСТЬ И ПЕРО
Кисть - это объект, который представляет метод заливки плоскости. Помимо способности заливать плоскость абсолютным способом, кисти уровня интеграции сред также способны приспосабливаться к заливке плоскости в зависимости от размера объекта, который они заливают. Примеры типов кисти включают в себя "кисть чистого цвета" (SolidColorBrush), "кисть визуала" (VisualBrush) (которая может ссылаться на визуал), "кисть рисования" (DrawingBrush) (которая может ссылаться на ресурс векторной графики), "линейного градиента" (LinearGradient), "радиального градиента" (RadialGradient), "кисть изображения" (ImageBrush) и "кисть девятиячеечной сетки" (NineGridBrush). Принятые по умолчанию значения свойств кисти указаны ниже и, в общем случае, являются значениями, которые не приводят ни к какому действию. Таким образом, цвета, по умолчанию, заданы прозрачными и т.д. Также коллекция анимаций, по умолчанию, задана равной null.
Согласно вышеупомянутому определенные объекты кисти имеют представление о том, как они связаны с системой координат, когда они используются, и представление о том, как они связаны с ограничивающим прямоугольником геометрии, с которой они используются. Этот размер зависит от формы объекта, который заливает кисть.
Базовый класс "кисть" (Brush) имеет "преобразование" (Transform), общую непрозрачность и режим смешивания:
Объекты Brush (и другие объектные ресурсы в векторной графике и API MIL) являются "изменяемыми" (Changeable) и записываемыми после того, как созданы, и подчиняются общей схеме Changeable в отношении того, как они ведут себя после использования в квалифицированном использовании.
Кисти (за исключением VisualBrush и DrawingBrush) имеют простой синтаксис для использования в разметке, однако этот простой синтаксис не позволяет осуществлять доступ ко всем свойствам и поведению. Если программисту будет нужен более обширный доступ, чем обеспечивает простой синтаксис, программисту потребуется использовать сложный синтаксис. Заметим, что во избежание избыточности здесь задокументирован только простой синтаксис, поскольку сложный синтаксис подчиняется тому же шаблону, что и другие классы CLR.
Ниже приведены типы кисти с простым синтаксисом для разметки в данной реализации:
Многие из типов кисти используют систему координат для задания некоторых из их параметров. Эту систему координат можно задать по отношению к простому ограничивающему прямоугольнику геометрии, с которым используется кисть, или она может быть абсолютной и интерпретироваться в координатном пространстве, которое активно во время использования кисти. Это называется соответственно режимом RelativeToBoundingBox ("относительно ограничивающего прямоугольника" и режимом Absolute ("абсолютный").
SolidColorBrush заливает плоскость чистым цветом. При наличии альфа-компонента цвета, он объединяется путем умножения с соответствующим атрибутом непрозрачности в Brush.
Поскольку это простой тип (т.е. ни одно из его свойств не является Changeable), единственным защищенным методом, который нужно реализовать, является CloneCore(). Кроме того, поскольку нет комбинации значений, которые делают этот объект недействительным, нет необходимости обеспечивать метод ValidateObjectState(). Эти методы и другие родственные методы описаны в прилагаемом Приложении.
Простой синтаксис для разметки для SolidColorBrush:
Класс "кисти" (Brushes) содержит статические свойства для экземпляров SolidColorBrush, которые открыты. Каждый задается равным значению цвета одного и того же имени. Заметим, что, поскольку это стандартизованные кисти, их значения IsChangeable заданы равными "ложь" (например, реализационные вызовы MakeUnchangeable() после построения).
Ниже приведены некоторые стандартные цвета:
Рисование градиентов осуществляется путем задания ограничителей градиента. Эти ограничители градиента указывают цвета вдоль некоторого рода прогрессии. В настоящее время поддерживаются два типа градиентов, а именно линейный и радиальный градиенты. Рисование градиента осуществляется посредством интерполяций между ограничителями градиента в заданном цветовом пространстве.
Градиенты образованы списком ограничителей градиента. Каждый из этих ограничителей градиента содержит цвет (в который включено значение альфа) и смещение. Если не задано никаких ограничителей градиента, кисть рисуется как прозрачная (как если не задано никакой кисти). Если задан только один ограничитель градиента, кисть рисуется как чистый цвет с одним заданным цветом. Рассматриваются любые ограничители градиента со смещениями в диапазоне от нуля до единицы (0,0... 1,0) совместно с наибольшим ограничителем в диапазоне (-∞...0,0] и наименьшим ограничителем в диапазоне [1,0...+∞). Если набор рассматриваемых ограничителей включает в себя ограничитель, находящийся вне диапазона от нуля до единицы, выводится неявный ограничитель в нуле (и/или единице), который представляет интерполированный цвет, который имел бы место в этом ограничителе. Кроме того, если два или более ограничителя заданы с одним и тем же смещением, на этом смещении происходит жесткий (а не интерполированный) переход. Порядок добавления ограничителей определяет поведение при этом смещении; первый добавляемый ограничитель - это эффективный цвет до этого смещения, последний добавляемый ограничитель - это эффективный цвет после этого ограничителя, и любые дополнительные ограничители при этом смещении игнорируются.
Этот класс является Changeable, как и другие классы ресурсов:
Аналогично SolidColorBrush, это имеет вложенные Changeable в коллекциях анимаций.
Перечисление GradientSpreadMethod задает, как нужно рисовать градиент вне заданного вектора или пространства. Имеются три возможных значения, включая Pad ("набивка"), в которой концевые цвета (первый и последний) используются для заливки оставшегося пространства, Reflect ("отражение"), в которой ограничители повторяются по порядку, пока пространство не будет залито. Значение по умолчанию для свойств этого типа равно Pad:
На фиг.24 показаны некоторые примеры GradientSpreadMethod (хотя в градации серого, а не в цвете). Каждая форма имеет линейный градиент, идущий от белого к серому. Сплошная линия представляет вектор градиента.
Перечисление ColorInterpolationMode задает режим интерполяции для цветов в градиенте. Имеются две опции: PhysicallyLinearGamma ("физически линейная гамма") и PerceptuallyLinearGamma (перцептивно линейная гамма").
Это абстрактный базовый класс.
"Линейный градиент" (LinearGradient) задает кисть линейного градиента вдоль вектора. Отдельные ограничители задают ограничители цветов вдоль этого вектора.
Простой синтаксис разметки для "кисти линейного градиента" (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.
Разметка для "радиального градиента" (RadialGradient) позволяет задавать "радиальный градиент" с двумя ограничителями цвета при смещениях 0 и 1 соответственно. По умолчанию, используется MappingMode, который является RelativeToBoundingBox, поскольку являются радиусами, заданными по умолчанию, 0,5:
TileBrush "мозаичная кисть" - это абстрактный базовый класс, который содержит логические средства для описания мозаики и средство, с помощью которого эта мозаика должна заливать область. Подклассы "TileBrush" содержат содержимое и логически задают способ заливки бесконечной плоскости.
Перечисление Stretch ("растяжение") используется для описания того, как ViewBox ("прямоугольник просмотра") (начальное координатное пространство) отображается в ViewPort ("порт просмотра") (конечное координатное пространство). Это используется в TileBrush:
На фиг.26 представлены примеры растяжения. В этих примерах содержимое выравнивается по верхнему/левому краю.
Перечисление TileMode ("режим мозаики") используется для описания того, заливается ли и как заливается пространство "элементами мозаичного изображения" (Tile). TileBrush задает, где находится базовый Tile (определяется посредством ViewPort). Остальное пространство заливается на основании значения TileMode.
На фиг.27 представлены примеры TileMode. В каждом примере элемент мозаичного изображения, расположенный в верхнем левом углу, является базовым элементом мозаичного изображения. Эти примеры представляют None, Tile, FlipX, FlipY и FlipXY.
Перечисление VerticalAlignment используется для описания того, как размещается содержимое в контейнере по вертикали:
Перечисление HorizontalAlignment используется для описания того, как размещается содержимое в контейнере по горизонтали.
Свойства TileBrush выделяют прямоугольный участок бесконечной плоскости, подлежащий мозаичной заливке (ViewBox), и описывают конечный прямоугольник (ViewPort), который будет базовым Tile в заливаемой области. Оставшаяся конечная область будет залита на основании свойства TileMode, которое управляет тем, дублируется ли исходный элемент мозаичного изображения и как он дублируется для заливки остального пространства:
Содержимое 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), содержимое которой задано визуалом. Эту кисть можно использовать для создания сложных схем или ее можно использовать для рисования дополнительных копий содержимого других частей сцены.
Как было отмечено, VisualBrush не имеет простого синтаксиса разметки, хотя ее можно описывать посредством сложного синтаксиса.
"Кисть рисования" (DrawingBrush) является "мозаичной кистью" (TileBrush), содержимое которой задано "рисованием" (Drawing). Эту кисть можно использовать для создания сложных схем, которые были созданы посредством "контекста рисования" (DrawingContext).
Как было отмечено, DrawingBrush не имеет простого синтаксиса разметки, хотя ее можно описать посредством сложного синтаксиса.
"Кисть изображения" (ImageBrush) является "мозаичной кистью" (TileBrush), содержимое которой задается посредством "источника изображения" (ImageSource). Эту кисть можно использовать для заливки пространства изображением.
Простой синтаксис разметки для ImageBrush:
кисть изображения:
"изображение" uri-изображения
"Кисть видео" (VideoBrush) является TileBrush, содержимое которой задано "видеоданными" (VideoData). Эту кисть можно использовать для заливки пространства посредством "видео" (Video).
Простой синтаксис разметки для VideoBrush:
Кисть видео:
"видео" uri-видео
"Кисть девятиячеечной сетки" (NineGridBrush) - это кисть, которая всегда заливает ограничивающий прямоугольник объекта изображением своего содержимого, и растяжение изображения не выполняется посредством исключительно визуального масштабирования. Источник изображения делится на девять прямоугольников четырьмя границами (отсюда название "девятиячеечная сетка"). Содержимое изображения в каждой из этих девяти областей масштабируется в 0, 1 или 2 измерениях, пока они не заполнят ограничивающий прямоугольник объекта. Измерения, в которых масштабируется каждая секция, можно видеть на этой схеме: фиг.28 представляет концепцию "девятиячеечной сетки", показывая девять ячеек, заданных верхней, левой, нижней и правой границами. Стрелки в каждом квадрате сетки показывают измерение(я), в которых их содержимое будет растянуто, чтобы соответствовать размеру ViewPort.
Помимо областей девятиячеечной сетки, изображенных выше, имеется необязательная "десятая" ячейка. Она принимает форму дополнительного изображения, которое центрировано в ViewPort и не масштабировано. Ее можно использовать вместо формы в центре кнопки и т.д. Эта "десятая ячейка" называется глифом и предоставляется свойством GlyphImageSource:
Заметим, что члены границы отсчитываются от края изображения в пикселях изображения.
Простой синтаксис разметки для NineGridBrush:
Кисть-девятиячеечной-сетки:
"девятиячеечная сетка" uri-изображения целое целое целое целое [uri-глифового-изображения]
Четыре целых числа - это значения LeftBorder (левой границы), RightBorder (правой границы), TopBorder (верхней границы) и BottomBorder (нижней границы) соответственно. Конечный URI для десятой ячейки или глифа является необязательным.
"Перо" (Pen) - это объект, который берет "кисть" (Brush) и другие параметры, которые описывают, как оконтуривать пространство/геометрию. В принципе, "перо" описывает, как создавать область контура из "геометрии" (Geometry). Создается новая область, которая базируется на краях Geometry, толщины (Thickness) пера, PenLineJoin, PenLineCap и т.д. После создания области она заливается Brush.
PenLineCap определяет, как рисовать концы штриховой линии:
PenDashCap определяет, как рисовать концы каждого штриха в пунктирной штриховой линии:
PenLineJoin определяет, как рисовать соединения при вычерчивании линии:
Класс DashArrays содержит статические свойства, которые обеспечивают доступ к общим, широко известным стилям штриха:
ЦВЕТ
Архитектура цвета построена на некоторых общих принципах, включая то, что "цвет" (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.
Нижеприведенный пример создает градиент с использованием этих четырех механизмов:
Первый цвет фона задается как шестнадцатеричное число (#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 "цветового контекста", поскольку оно является другой концепцией и в большинстве случаев обрабатывается иначе.
Заметим, что получение цветового значения системы цветовых объектов ПИ может зависеть от контекста, как и другие, более развитые, типы тем, и должны быть собраны с другими метриками системы с помощью модели приложения/API тем.
Заметим, что значения scRGB могут варьироваться ниже 0,0 и выше 1,0 для поддержки расширенных динамических диапазонов и гамм.
Переопределения операторов для объектов Color зависят от контекста, поскольку смешивание и добавление цветов зависит от цветового пространства. Например, пространства яркости RGB являются аддитивными и линейными, так что обычные математические операции являются довольно интуитивными, но пространства светлоты RGB, так же как пространства CMYK, не являются одновременно линейными и аддитивными, вследствие чего эти операции приводят к разным визуальным эффектам. Кроме того, большинство операций Color могут приводить к значениям за пределами нужной гаммы и, таким образом, требуют компенсации отображения гаммы. Это может быть так же просто, как фиксация низкого качества, или может быть значительно более изощренным.
Можно предусмотреть те или иные перегрузки операторов анимации, если они ограничены конкретно "цветовым контекстом" scRGB, поскольку scRGB основано на физическом свете и смешивает линейно и аддитивно. CMYK, sRGB и другие цветовые пространства имеют очень разные модели смешивания.
Аналогичные методы при использовании в "многоканальных" цветах можно также использовать для поддержки HSB, YCC и YUV и аналогичных цветовых пространств, которые тесно связаны с sRGB или scRGB.
Значения с плавающей точкой в формах 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.
Geometry - это абстрактный базовый класс.
Чтобы задать преобразование, применяемое к геометрии, задают свойство "преобразование" (Transform). "Коллекция геометрий" (GeometryCollection) - это коллекция множества объектов Geometry, которые были скомбинированы с использованием конкретных булевых операций в их заданной области. Этот объект позволяет легче строить визуальные комбинации объектов Geometry, чем возможно с использованием исключительно объектов PathFigure в "геометрии пути" (PathGeometry).
Перечисление комбинированного режима направляет комбинацию геометрической области в коллекции. Булевы операции объединения (Union), исключающего ИЛИ (XOR), пересечения (Intersect) являются коммутативными и поэтому применяются к геометриям независимо от порядка. Операции дополнения (Complement) и исключения (Exclude) не являются коммутативными и потому заданы между первой геометрией и отдельной оставшейся геометрией. Иными словами, комбинация исключений для {g1, g2, g3} будет применяться как ((g1 исключить g2) и (g1 исключить g3)). Дополнение указывает, что существующая область заменяется результатом удаления существующей области из новой области. Другими словами, существующая область исключается из новой области. Исключение указывает, что существующая область заменяется результатом удаления новой области из существующей области. Другими словами, новая область исключается из существующей области. Пересечение относится к комбинированию областей путем взятия их пересечения, объединение относится к комбинированию областей путем взятия объединения их обеих, и исключающее ИЛИ относится к комбинированию областей путем взятия только площадей, охватываемых одной или другой областью, но не обеими:
GetOptimizedGeometry() свертывает коллекцию геометрии, где возможно, результатом не обязательно является GeometryCollection. Это может включать в себя такие оптимизации, как комбинирование геометрии примыкающих прямоугольников в геометрию одного прямоугольника, осуществление булевой операции между геометрией примыкающих путей для создания геометрии нового пути или выравнивание GeometryCollection с одним и тем же комбинированным режимом. В случаях, когда геометрия используется во многих разных контекстах, это обеспечивает повышение производительности при обработке и хранении.
Ниже приведен пример разметки, в которой используется GeometryCollection:
"Геометрия пути" (PathGeometry) - это коллекция объектов "фигура пути" (PathFigure). Каждый из объектов PathFigure состоит из одного или нескольких объектов "отрезок пути" (PathSegment), которые фактически задают их форму. Залитая область PathGeometry задается взятием содержащихся PathFigure, имеющих свойство "залито" (Filled) равное "истине", и применения "правила заливки" (FillRule) для определения охватываемой области. На фиг.13 представлены соотношения объектов PathGeometry.
Перечисление FillRule задает, как комбинируются пересекающиеся области объектов "фигура" (Figure), содержащихся в Geometry для формирования результирующей области Geometry:
Правило NonZero определяет "нахождение внутри" для точки на холсте, проводя луч из этой точки в бесконечность в любом направлении с последующей проверкой мест, где отрезок формы пересекает луч. Начиная со счета нуль, добавляют единицу всякий раз, когда "отрезок" пересекает луч слева направо, и вычитают единицу всякий раз, когда отрезок пути пересекает луч справа налево. Если после подсчета пересечений результат оказывается равным нулю, значит точка находится вне пути. В противном случае она находится внутри.
Правило EvenOdd определяет "нахождение внутри" для точки на холсте, проводя луч из этой точки в бесконечность в любом направлении и подсчитывая количество "отрезков" пути из данной формы, которые пересекают луч. Если число нечетное, значит точка находится внутри; если четное, значит точка находится снаружи.
Для преобразования других типов геометрии в геометрию пути для включения с другими фигурами используется метод "добавление геометрии" (AddGeometry). Он добавляет фигуру, которая геометрически совпадает со входной геометрией. Для неанимированной геометрии совпадение является точным, тогда как анимированная геометрия может быть с потерями при преобразовании. Причина для преобразования с потерями состоит в том, что анимированные параметры входной геометрии не соответствуют форме, которая подходит к отрезку.
Не гарантируется, что перечисление и структура результирующей PathGeometry точно соответствуют входной геометрии.
Коллекция фигур является коллекцией объектов PathFigure и основного содержимого, задающего PathGeometry:
PathFigure - это подразделение Geometry, которое задает коллекцию отрезков. Эта коллекция отрезков является единой связной последовательностью двухмерных объектов "отрезок пути" (PathSegment). PathFigure может быть замкнутой формой определенной площади или связной последовательностью "отрезков" (Segment), которые задают кривую, но не замкнутую область. Класс PathFigure включает в себя ряд удобных функций, упрощающих построение PathFigure из явных вызовов метода ArcTo/LineTo/(и других), не требующих объекта PathSegment. Явный вызов AddSegment ("добавление отрезка") можно использовать для добавления составного "отрезка" (Segment).
Фигуре требуется начальная точка, поскольку каждый отрезок поддерживает непрерывность по отношению к последней добавленной точке. Чтобы задать начальную точку, вызывают 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:
PathSegment представляет фрагмент очертания PathFigure. Простые прямолинейные отрезки, отрезки эллиптической дуги, отрезки кубического сплайна Безье и отрезки квадратного сплайна Безье могут объединяться друг с другом для формирования PathFigure.
Каждый из объектов PathSegment, которые добавлены в PathFigure, также имеют свойство PathSegmentIsStroked. Если это свойство PathSegment задано равным "истина", то конкретный PathSegment будет вносить вклад в оконтуренную область PathGeometry при визуализации с помощью Pen. Это также применяется к тестированию на попадание и к явному "расширена" (Widen) "геометрии пути" (PathGeometry). Конкретное поведение при переключении между оконтуренными и неоконтуренными отрезками PathSegment в PathFigure такое же, как если бы Pen было задано штриховым, в том, что надлежащие концы штрихов будут применяться к концам PathSegment.
Нижеследующий пример демонстрирует разметку, которая использует PathGeometry, PathFigure и PathSegment:
"Геометрия прямоугольника" (RectangleGeometry) задает прямоугольник или прямоугольный геометрический объект с закругленными углами. Радиус Х и радиус Y относятся к радиальной длине, выровненной относительно оси, закругленных углов:
Следующий пример демонстрирует разметку, которая использует RectangleGeometry:
"Геометрия эллипса" (EllipseGeometry) определяет эллиптическую область, заданную радиальными длинами Х и Y, выровненными относительно оси:
Следующий пример демонстрирует разметку, которая использует EllipseGeometry:
"Геометрия линии" (LineGeometry) задает отрезок линии между двумя точками и потому не содержит никакой залитой области:
Следующий пример демонстрирует разметку, которая использует LineGeometry:
ПОСТРОЕНИЕИЗОБРАЖЕНИЯ
"Источник изображения" (ImageSource) - это абстрактный класс, содержащий основной компоновочный блок для конвейера построения изображения. ImageSource, в принципе, представляет единичный, постоянный набор пикселей с определенным размером и разрешением. Например, ImageSource может быть единичным кадром в файле изображения, который мог бы обеспечить декодер (Decoder), или может быть результатом преобразования, которое действует в отношении определенного собственного ImageSource. ImageSource не является группой кадров или анимацией. ImageSource является изменяемым не потому, что его собственные свойства могут изменяться, но потому, что свойства его подклассов потенциально могут изменяться.
По соображениям производительности "источники изображения" поддерживают неуправляемый доступ к изображению с использованием интерфейса источника битового образа IMIL (IMILBitmapSource). Если подкласс ImageSource не обеспечивает его, то это делает базовый класс ImageSource (используя класс-упаковщик).
"Данные изображения" (ImageData) - это подкласс "источника изображения" (ImageSource). ImageData реализует ImageSource для нескольких разных источников изображений:
ImageData обеспечивает услуги, включающие в себя кэширование декодированного изображения в системной памяти, обрезку изображения до заданного начального прямоугольника (в кэш) и задание размеров изображения в соответствии с указанными шириной и высотой декодирования (в кэш). Для сценариев декодирования изображения ImageData позволяет задавать, какой декодер использовать, или автоматически обнаруживать кодек, на основании входного потока и типа MIME. ImageData не поддерживает загрузку изображения непосредственно из URI. Загрузчик следует использовать для отображения URI в поток, который можно использовать для построения ImageData. После построения ImageData его единственными изменяемыми свойствами являются его внедренное уменьшенное изображение, его метаданные и его пиксельные данные. Другие свойства считаются неизменяемыми.
Пиксельные данные ImageData можно изменять, в том числе одним из двух путей: (1) получая "контекст рисования" (DrawingContext) для ImageData и используя команды через "контекст рисования" для рисования на изображении или (2) используя ImageData как пункт назначения (RenderTarget) для "менеджера визуалов" (VisualManager) и выдавая команду визуализировать дерево визуалов (сцену) в ImageData. В любом случае рисование осуществляется в изображение в памяти. Изменяется только кэшированное изображение памяти - файл изображения сам по себе не затрагивается (пока ImageData не будет позднее закодирован в файл изображения с использованием "кодера изображений" (ImageEncoder).
ImageDecoder ("декодер изображений") - это абстрактный класс, который обеспечивает базовый класс для декодеров. Он обеспечивает способ определения количества кадров в изображении и перечисления (или индексирования) кадров. Как отмечено выше, каждый кадр изображения является "источником изображения" (ImageSource). Добавленные кодеки создают объект "данные изображения" (ImageData) для каждого запрашиваемого кадра. Встроенные кодеки могут использовать разные подклассы для возвращения ImageSource для каждого кадра. ImageDecoder является не самим ImageSource, но контейнером для одного или нескольких "источников изображения". Заметим, что каждый кадр изображения потенциально может иметь разные атрибуты (разный размер, разное разрешение и т.д.).
MIL предусматривает ряд встроенных декодеров, в том числе ImageDecoderBmp.cs, ImageDecoderGif.cs, ImageDecoderIcon.cs, ImageDecoderJpeg.cs, ImageDecoderPng.cs, ImageDecoderTiff.cs и ImageDecoderWmp.cs. Каждый из них реализует ImageDecoder и единый конструктор, который использует System.IO.Stream для инициализации декодера, как в следующем примере:
"Кодер изображения" (ImageEncoder) - эта коллекция "источников изображения" (кадров изображения), каждый из которых имеет собственные метаданные и уменьшенное изображение. Также могут иметь место глобальные уменьшенные изображения и метаданные, связанные со всем набором кадров. Также можно выбирать кодек для обеспечения поддержки свойств кодирования, используемых для определения того, как кодировать изображение. Коллекцию кадров можно сохранять (кодировать) в любое количество заданных потоков (по одному). Коллекцию можно очищать и затем наполнять другой коллекцией и снова сохранять.
MIL предусматривает ряд встроенных кодеров, в том числе ImageEncoderBmp.cs, ImageEncoderGif.cs, ImageEncoderJpeg.cs, ImageEncoderPng.cs, ImageEncoderTiff.cs и ImageEncoderWmp.cs.
ImageSizeOptions ("опции размеров изображения") используются для задания размеров уменьшенных изображений и размеров кэшированных изображений. Опции включают в себя ширину, высоту, сохраняется ли форматное соотношение исходного изображения, и угол поворота (кратный 90 градусам).
Ниже приведено определение формата пикселя для изображений и пиксельных поверхностей:
Каждый кодек (ImageEncoder и ImageDecoder) требуется для обеспечения CodecInfo, которая дает информацию о кодеке, обеспечивает методы создания экземпляров для соответствующего кодера/декодера и обеспечивает метод, который определяет, согласуется ли кодек с обеспечиваемым "фильтром кодеков" (CodecFilter).
MIL предусматривает встроенные объекты CodecInfo, включая CodecInfoBmp.cs, CodechInfoGif.cs, CodecmInfoIcon.cs, CodecInfoJpeg.cs, CodecInfoPng.cs, CodechInfoTiff.cs и CodecInfoWmp.cs.
"Фильтр кодеков" (CodecFilter) используется перечислителем кодеков для перечисления кодеков на основании заданных критериев. Критерии, которые не заданы, игнорируются при поиске подходящего кодека. Например, если MimeType ("тип MIME") не задан, рассматриваются кодеки с любым типом MIME.
Когда перечислитель построен (его ctor являются внутренними), задается "фильтр кодеков" (CodecFilter). Этот фильтр используется для определения того, какие кодеки перечислять. Перечисляются только те, которые согласуются с фильтром (если существуют).
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 является абстрактным базовым классом:
"Коллекция преобразований" (TransformCollection) - это коллекция объектов Transform, значение которой соответствует матричному умножению отдельных значений Transform. Значение составлено слева направо, согласуясь с первым и последним элементами коллекции:
"Преобразование поворота" (RotateTransform) задает поворот на угол относительно конкретной центральной точки (по умолчанию, 0,0). Угол задан в градусах. Статическое матричное представление для угла поворота вокруг точки x,y имеет следующий вид.
"Преобразование параллельного переноса" (TranslateTransform) задает перенос вдоль оси в направлении x и y. Статическое матричное представление параллельного переноса со сдвигом dx,dy выглядит следующим образом.
"Преобразование масштабирования" (ScaleTransform) задает масштабирование в направлении x и y относительно центральной точки (по умолчанию 0,0). Статическое матричное представление масштабирования sx, sy относительно точки x,y выглядит следующим образом.
"Преобразование перекоса" (SkewTransform) задает перекос на угол в градусах в направлении x и y. Угол перекоса измеряется в градусах. Статическое матричное представление перекоса под углом выглядит следующим образом.
"Матричное преобразование" (MatrixTransform) задает преобразование через его математическое представление:
Когда свойство типа Transform задано в разметке, система свойств использует преобразователь типа Transform для преобразования представления строки в надлежащий производный объект Transform. В настоящее время не существует способа описания анимированных свойств с использованием этого синтаксиса.
Синтаксис находится в векторной графике, и соответствующая конструкция Transform кратко изложена ниже, причем параметры, обозначенные "<>" представляют необязательные параметры:
- matrix(m11 m12 m21 m22 offsetX offset Y)
- AddMatrix(m11, m12, m21, m22, offsetX, offsetY)
- translate(tx
- AddTranslate(tx, ty).
- Если не задано, предполагается равным 0.
- scale(sx
- AddScale(sx, sy).
- Если не задано, предполагается таким же, как sx.
- rotate(angle
- AddRotate(angle, Point(cx,cy)).
- Если cx, cy не заданы, предполагаются равными 0,0.
- skewX(angle)
- AddSkew(angle, 0)
- skewY(angle)
- AddSkew(0, angle)
ЭФФЕКТЫ
Эффекты обеспечивают средство изменения содержимого визуала способом, ориентированным на визуализацию. Например, "эффекты изображения" (ImageEffects) (эффекты битового образа на растровой основе) действуют в отношении основанного на изображении, полностью скомпонованного представления части сцены. Эффекты разбиваются на различные типы, включающие в себя ImageEffects ("эффекты изображения"), BlendModes ("режимы смешивания") и VectorEffects ("векторные эффекты").
ImageEffects можно использовать в сцене удержанного режима, применяя их к подрафу или "элементу" (Element), или их можно использовать в автономном конвейере изображений. В общем случае, "эффект изображения" (ImageEffect) имеет нуль или более входов и нуль или более выходов, которые относятся к типу ImageSource. В конвейере изображений непосредственного режима не нужен выход, поскольку ImageEffect может выявлять другие свойства, которые описывают атрибуты входа. Например, ImageEffect может выдавать информацию гистограммы цветов или информацию обнаружения грани. В сцене удержанного режима есть дополнительный мета-вход, который обеспечивает доступ к визуализируемому содержимому подграфа, к которому применяется эффект.
"Режимы смешивания" (BlendModes) являются особой формой эффектов на основе изображения. Их можно применить к сцене удержанного режима, в общем случае, таким же образом, как "эффекты изображения" (ImageEffects). Таким образом, имеется свойство "элемента" (Element) ("BlendMode"), а также свойство BlendMode на визуале, метод PushBlendMode на IDrawingContext и свойство BlendMode на Brush. Режимы смешивания осуществляют объединение начального и конечного цветов при композиции источника. Примером "режимов смешивания" являются "умножение" (Multiply), "сложение" (Add) и т.д. "Векторные эффекты" (VectorEffects) являются другим типом эффектов.
Как описано в обзоре, "режимы смешивания" описывают операцию, которая управляет тем, как изображение составляется в другое изображение или в сцену. "Режимы смешивания" можно применять к сцене и к кистям. Каждый "режим смешивания" (BlendMode) описывает, как комбинировать начальный пиксель и конечный пиксель, и применяется к каждому составляемому пикселю. "Режимы смешивания" применяются после масштабирования или иного преобразования источника или после применения любых эффектов (включая "непрозрачность" (Opacity)). Заметим, что при применении операций BlendMode источник и пункт назначения находятся в формате с предварительным умножением на альфа. Чтобы задать "режим смешивания", программист может использовать один из "режимов смешивания", заданных в статическом классе "режимы смешивания", или может в явном виде задавать начальный и конечный коэффициенты. Поскольку в одной реализации коэффициенты не являются расширяемыми и они не имеют параметров, они представляются перечислением:
ТЕСТИРОВАНИЕ НА ПОПАДАНИЕ
Тестирование на попадание используется для выборки визуалов в сцене. Некоторые сценарии высокого уровня включают в себя выделение типа лассо и выделение типа резиновой ленты, перемещение с помощью клавиатуры (используемое для нахождения следующего элемента для переключения фокуса), определение фокуса мыши в дереве элементов, выделение перекрывающихся элементов с помощью прозрачности (например, изображений), тестирование на попадание типа "мысленный пузырь" и выделение символа, на который имеется попадание, в тексте.
В общем случае, тестирование на попадание обеспечивает согласованность по ядру, инфраструктуре и средствам управления и действует, начиная с вершины дерева средств управления, и возвращает средство управления или набор средств управления посредством точки или геометрии. Средство управления может указывать наличие или отсутствие попадания на него с помощью услуг поддержки, включающих в себя визуализированную геометрию, ограничивающий прямоугольник, внеполосную геометрию (область попадания), непрозрачность или маску изображения и свою собственную логику. Средство управления может возвращать в случае попадания конкретные данные, относящиеся к попаданию (например, линию, позицию символа и т.д.).
Механизм тестирования на попадание может эффективно фильтровать результаты тестирования на попадание. Кроме того, механизм тестирования на попадание обеспечивает гибкость для расширения на другие типы визуалов и разрешения вниз до подпримитивов в визуале, например, одним примером этого является "удержанный трехмерный визуал" (Retained3DVisual).
Обход тестирования на попадание является глубоким обходом дерева визуалов справа налево. Существуют три участника, тестер попаданий, обходчик и средство управления/визуал. Тестер попаданий реализует два обратных вызова, один - для управления обходчиком, и другой - для раннего окончания обхода на определенных визуалах, на которые имеется попадание. Средство управления реализует виртуальный метод для определения того, что является попаданием. Обходчик является фиксированной частью системы и обходит дерево визуалов на основании поведения обратного вызова, по существу, опрашивая каждое средство управления, было ли попадание на средство управления. О попаданиях сообщается посредством обратного вызова в порядке по z-координате, сверху вниз.
Таким образом, внутренне, тестирование на попадание содержит обход дерева визуалов. Снижаясь, тестер попаданий просматривает фильтрацию применительно к соотношениям уровня элементов, например холста с формами или фиксируемой панели с внутренним холстом. При обнаружении попадания тестер попаданий может либо продолжить обработку последующих попаданий (если таковые имеются), либо остановиться.
Логика последовательности операций с точки зрения обходчика попаданий проиллюстрирована в следующем псевдокоде:
Тестер попаданий использует открытые методы для инициирования теста попаданий и обеспечивает делегатов для управления поведением. Поведение по умолчанию состоит в тестировании всех визуалов и возвращении первого попадания. Если не выдается никакого результирующего делегата, возникает исключение.
Средство управления принимает решение по своей логике тестирования на попадание, подменяя HitTestCore ("ядро теста на попадания") для точки и геометрии. При инициировании теста попаданий внутренний обходчик дерева визуалов вызывает HitTestCore, в сущности, спрашивая средство управления, попадание ли это. "Границы теста попаданий" (HitTestBounds) отражают строгие границы области попадания и используются для оптимизации обхода. Принятое по умолчанию поведение визуала состоит в тестировании ограничивающего прямоугольника содержимого визуализации.
Тестер попаданий инициирует тест попаданий, переходя к точке попадания или геометрии и к дополнительным параметрам в "параметрах тестирования на попадание" (HitTestParameters). Класс обеспечен, главным образом, для упрощения конструкции и для дальнейшего повышения расширяемости. Особые запросы тестирования на попадание могут быть производными от этого класса для передачи дополнительной информации заинтересованным средствам управления. Каждое средство управления реализует конкретное HitTestCore для точки и геометрии. Ожидается, что средства управления соответствуют параметрам тестирования на попадание при реализации своей логики HitTestCore.
Средство управления возвращает конкретные данные путем создания производных классов от "результата теста на попадание" (HitTestResult). Например, средство управления текстом может хотеть возвратить попадание позиции символа. PointHitTestResult содержит точку локального координатного пространства. GeometryHitTestResult содержит геометрию локального координатного пространства (исходного теста попаданий). Функции преобразования визуалов могут отображать местоположение попадания в пространство предков.
Для иллюстрации использования делегатов, рассмотрим тестер попаданий, который хочет первое, самое верхнее попадание с использованием анонимных делегатов:
Другим примером является средство тестирования на попадание (тестер попаданий), которое хочет возвратить все визуалы, на которые были попадания:
Тестер попаданий использует перечисления для управления фильтрацией тестирования на попадание и результирующим поведением:
Перечисление "поведение фильтра теста на попадание" (HitTestFilterBehavior) управляет поведением фильтрации в том, что SkipChildren ("пропустить потомков") предписывает тестировать на попадания этот визуал, но не его дочерние визуалы. SkipVisualAndChildren ("пропустить визуал и потомков") предписывает не тестировать на попадания визуал и любые дочерние визуалы. "Продолжить" (Continue) предписывает тестировать на попадания этот визуал и его дочерние визуалы. "Остановить" (Stop) предписывает не тестировать на попадания никакие визуалы в дереве визуалов и возвратиться к вызывающей стороне.
Перечисление HitTestResultBehavior управляет поведением тестера попаданий:
Stop предписывает возвратиться ко входу теста на попадание, пропустив любые дальнейшие операции фильтра или теста на попадание. Continue предписывает тестирование на попадание для следующего визуала.
Хотя идентификаторы теста на попадание можно использовать для маркировки конкретного содержимого для положительной идентификации попадания, производительность была низкой, поскольку такая модель разбивала поток визуализации, добавляла служебную нагрузку обхода и была трудно управляемой при тестировании на попадание. Благодаря объединению элемента и визуала в унифицированный тип, основным уровнем разбиения является сам визуал, и средства управления могут структурировать сами себя для получения уровня разбиения, который им нужен.
Автор средства управления записывает логику для попадания, подменяя HitTestCore и производя свое собственное вычисление и/или используя услуги, описанные ниже.
Ниже приведен ряд примеров, которые демонстрируют силу этих услуг.
Первый пример демонстрирует средство управления, имеющее открытое свойство HitRegion, которое представляет область, чувствительную к попаданию, средства управления. Заметим, что область попадания не обязана совпадать с визуализируемым содержимым и может быть оптимизирована некоторыми приложениями. Если область попадания не задана (_hitRegion == null), средство управления предоставляет определять попадание базовым услугам реализации.
Для подмены поведения IsHit следует использовать дополнительные услуги поддержки.
Классы Geometry осуществляют тест на попадание для своей внутренней области:
Визуал обеспечивает защищенные функции для теста на попадание в отношении визуализируемого содержимого (своего собственного). При удержании визуала происходит запуск проверки содержимого. Этот помощник проверяет поток команд рисования, сохраненный на визуале, по одной команде, тестируя на попадание точку или геометрию для каждой, с визуализируемой геометрией.
Код будет возвращать, если пиксель изображения в точке выше порога альфа. Точка находится в пространстве визуалов, и преобразование применяется к пространству устройств, где происходит тест на основе пикселей.
АНИМАЦИЯ
Система анимации состоит из двух главных наборов компонентов, а именно средства управления хронированием и набора объектов анимации. Средства хронирования - это услуга, которая может использоваться любыми объектами, которые проявляют поведение, зависящее от времени, главными примерами являются анимации и объекты аудио- или видеосред. Объекты анимации реализуют набор функций, которые отображают временные диапазоны в другие типы данных, которые затем используются как входы в другие объекты более высокого уровня.
Графическая анимация достигается связыванием коллекции анимаций с операцией визуализации. Например, метод 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 реализуется хронированными объектами, которыми можно управлять во время выполнения:
В нижеследующей таблице приведена семантика интерфейса ITimingControl:
Графические сцены можно анимировать, задавая анимированные параметры для некоторых операций визуализации или добавляя анимации в определенные свойства элемента. Анимация - это функция, которая берет некоторый произвольный набор входов (по меньшей мере, один из которых, в общем случае, является временной шкалой) и создает выход надлежащего типа для передачи операции визуализации. Например, "точечная анимация" (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 = "истина".
Хотя поведение по умолчанию анимации "от и до" состоит в игнорировании базового значения анимированного свойства, это поведение может измениться на аддитивное поведение, где значения "от" и "до" являются отклонениями от базового значения.
При основных анимациях задано выходное значение в начальной и конечной точках, и для вычисления значений между ними используется линейная интерполяция. Для более сложных анимационных функций вместо этого может быть задан список значений. Каждое значение соответствует ключевому кадру. В простом случае эти ключевые кадры имеют место с регулярными интервалами. Анимации также можно программировать на использование шаговых промежутков между ключевыми кадрами. В методе шаговой интерполяции промежуток между каждой парой ключевых кадров пропорционален отношению "расстояния" между двумя ключевыми значениями к "полному расстоянию", покрытому анимацией. Это возможно для анимаций тех типов, для которых имеет смысл понятие "расстояние", например, для плавающих или точечных анимаций. В этом случае интерполяция между ключевыми кадрами линейна. Третья возможность состоит в отсутствии какой-либо анимации, в каковом случае функция выходного значения дискретна. На фиг.39 показана точка, анимированная по y с ("ключевыми значениями") KeyValues=10,90,70 и различными методами интерполяции.
Для дополнительного управления, можно в явном виде задавать время для каждого ключевого кадра. Интерполяция между ключевыми кадрами может быть линейной или дискретной. Ключевые моменты времени заданы в виде процентов полной длительности анимации и должны охватывать весь период. Иными словами, первое ключевое время равно 0, и для линейной интерполяции последнее ключевое время равно 1. На фиг.40 показана точка, анимированная по y с KeyValues=10,90,50 и ("ключевыми моментами времени") KeyTimes=0,.2,1.
Для дополнительного управления интерполяцией можно использовать набор кубических кривых Безье для описания временной кривой, используемой для анимации. Кривая Безье, визуализируемая на экране, не должна вводить в заблуждение; эта кривая используется для изменения формы кривой хронирования, но значения ключевых кадров по-прежнему интерполируются линейно по значению продвижения. Этот метод сплайновой интерполяции добавляет фильтр, который преобразует линейное значение продвижения 0-1, обеспечиваемое временной шкалой, связанной с анимацией, в нелинейную кривую продвижения 0-1.
Нижеследующая таблица содержит список атрибутов, относящихся к анимации, и их смысловое содержание. Этот список является шаблоном, которому подчиняются все объекты анимации. Там, где тип атрибута представляет собой "
Класс "анимируемый" (Animatable) является производным от класса "изменяемый" (Changeable). Он используется как базовый класс любым объектом или коллекцией объектов, который(ую) можно анимировать или может содержать анимированные значения, например мультимедийным ресурсом. "Модификаторы" (Modifiers), "хронированные модификаторы" (TimeModifiers) и "анимации" (Animations) являются производными от Changeable, а не от Animatable, поскольку их индивидуальные свойства не являются анимируемыми.
Классы 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, и всякий раз при вызове метода может возвращаться другое значение. Объекты, потребляющие модификаторы, могут предполагать, что так и есть, и повторно вызывать модификатор, как в следующем примере:
Однако на практике бывают случаи, когда ожидается, что модификатор будет выдавать один и тот же выход при одном и том же входе, в зависимости от внутреннего состояния. Модификатор называют "изменяющимся", когда он находится в периоде, в котором значение GetValue может быть другим при каждом вызове. Он является "неизменяющимся", когда возвращаемое значение GetValue одно и то же при каждом вызове. Если модификатор является "неизменяющимся", пользователь этого модификатора может безопасно кэшировать возвращенное значение метода GetValue и, возможно, избежать повторного и ненужного оценивания метода GetValue, как в следующем примере:
Реализуется абстрактный класс "модификатор" (Modifier), от которого должны наследоваться модификаторы. Этот класс обеспечивает принятые по умолчанию реализации для всех методов, кроме GetValue и GetUniqueInstance:
В нижеследующей таблице приведена семантика класса Modifier:
Кроме того, реализуется набор классов, зависящих от типа, которые наследуются от Modifier, но предоставляют сохраняющие тип версии методов интерфейса. В нижеследующем примере показан класс FloatModifier:
Хронированный модификатор - это модификатор, чьим поведением, по меньшей мере, частично управляет объект "временная шкала" (TimeLine). Применяются вышеупомянутые правила модификатора, но дополнительно хронированный модификатор реализует интерфейс ITimingControl для предоставления управления временной шкалы модификатора. Нет никаких абстрактных классов TimeModifier. Вместо этого классы, зависящие от конкретного типа, наследуются от классов Modifier, зависящих от конкретного типа. В нижеприведенном примере показан класс FloatTimedModifier:
Заметим, что Modifier и интерфейс ITimingControl имеют некоторые сходные методы, свойства и события. TimedModifier предоставляет для них единую реализацию. TimedModifier свободно реализует ITimingControl, перенаправляя все вызовы управляющей временной шкале, хотя делать это не обязательно. Реализация по умолчанию ITimingControl, обеспеченная реализациями TimedModifier, зависящего от конкретного типа, перенаправляет вызовы управляющей временной шкале.
Анимация - это хронированный модификатор, реализующий конкретную анимационную функцию.
Коллекция анимаций - это список объектов анимации (наследуемый от
События, инициированные из коллекций анимаций, объединяются.
Анимации пути являются специализацией класса TimedMatrixModifier ("хронированный матричный модификатор"). "Матричный модификатор" (MatrixModifier) можно использовать совместно с "матричным преобразованием" (MatrixTransform). MatrixTransform имеет свойство "матрица" (Matrix) и свойство "матричные анимации" (MatrixAnimations), и, поскольку "анимация пути" (PathAnimation) является MatrixModifier, ее можно использовать в качестве "матричной анимации" (MatrixAnimation).
Использование разметки:
Каждый ресурс, метод или объект, который можно анимировать, подчиняется ряду правил, включая то, которое реализует интерфейс Animatable. Для каждого анимируемого свойства (или параметра), именуемого "Foo" ("нечто"), типа "Bar" ("что-то"), имеется другое свойство (или параметр), именуемое "FooAnimations", типа "BarAnimationCollection". Всякий раз, когда нужна анимация, используются коллекции анимаций. Основные "модификаторы" или объекты Animation не используются напрямую, потому что это препятствует композиции анимации.
Ресурсы можно анимировать путем добавления коллекций анимаций в отдельные свойства. Следующий пример показывает, как создавать "кисть чистого цвета" (SolidColorBrush) с анимированным цветом:
Анимационные ресурсы можно использовать в операциях визуализации или в качестве значений для свойств "элемента" (Element).
Операцию визуализации можно анимировать путем добавления коллекций анимаций в вызовы метода контекста рисования или путем использования анимационных ресурсов. Следующий пример показывает, как проталкивать анимированное значение непрозрачности в контекст рисования:
Элементы можно анимировать путем добавления коллекций анимаций в свойства Element. Следующий пример показывает, как анимировать ширину кнопки в C#:
Ниже показан тот же пример в XAML:
Всякий раз при использовании анимации (или анимированного ресурса) анимация (или ресурс) клонируется (неглубоким, эффективным способом) для предоставления пункту назначения уникальной, независимо клонируемой временной шкалы. Побочный эффект этого поведения состоит в том, что исходная анимация не является частью визуальной сцены и поэтому не отвечает на управляющие вызовы через интерфейс ITimingControl. Для достижения этого эффекта вызывающий код сначала использует анимацию, а затем считывает анимацию обратно. Считанное обратно значение можно затем кэшировать и использовать для управления хронированием. Нижеследующий пример показывает схему, которой может подчиняться код, предназначенный для управления анимациями:
Пользователь может создать новый класс, используя "эффект анимации" (AnimationEffect) в качестве базового класса для реализации AnimationEffect. Пользователю также потребуется создать построитель для своего AnimationEffect.
ТИПЫ ПРИМИТИВОВ
Базовая единица длины в MIL представляет собой число с плавающей точкой c двойной точностью (double), в результате чего другие типы примитивов и API основаны на числах с плавающей точкой с двойной точностью. В общем случае, эти числа с плавающей точкой с двойной точностью оцениваются как пользовательские единицы, которые первоначально равны 1/96 дюйма. Для цветов каждый из цветовых каналов представляется числом с плавающей точкой, а не числом с плавающей точкой с двойной точностью. Для измерения углов значения чисел с плавающей точкой с двойной точностью выражаются в градусах. Когда число с плавающей точкой или число с плавающей точкой с двойной точностью оценивается как измерение времени, оно выражается в секундах.
Структура Time ("время") представляет конкретный момент времени или промежуток времени. Кроме того, особое значение времени, именуемое "Indefinite" ("неопределенное"), представляет либо бесконечно удаленный в будущее момент времени, либо бесконечно долгий промежуток времени. Значения времени предназначены для использования в системе свойств, поэтому для очистки этого свойства или для явного указания, что свойство не задано, можно использовать особое значение, именуемое "Unspecified" ("незаданное"). Значения времени внутренне хранятся в виде целочисленных счетчиков:
Помимо вышеупомянутой грамматики, заметим, что "минуты" и "секунды" необходимо задавать в диапазоне от "00" до "59", чтобы они считались действительными. Кроме того, если формат "значения счетчика времени" используется без единиц, предполагается, что значение выражено в секундах. Ниже приведено несколько примеров значений "времени" и их смысловой нагрузки:
Структура "время" (Time) используется для сохранения единичного значения "времени":
Ниже приведены другие базовые типы, в которых используется следующая система обозначений:
- *: 0 или больше
- +: 1 или больше
- ?: 0 или 1
- {n}: n раз
- (): группирование
- |: разделяет альтернативы
- двойные кавычки окружают литералы
Синтаксис разметки для цветов:
Объект "цвета" (Colors) содержит статические члены, содержащие многие общеизвестные цвета, например красный и синий:
Структура "точка" (Point) описана ниже:
Синтаксис разметки для объекта "точка":
Объект "вектор" (Vector) задан ниже:
Синтаксис разметки для объекта "вектор":
Заметим, что размер - это не 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 будут возвращать положительную бесконечность, и ни одно из этих свойств не будет изменяемым. Это сделано для того, чтобы гарантировать отсутствие "пустой" ширины при нормальной высоте или наоборот.
Показано, что прямоугольник (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. В MIL используется синтаксис векторов-строк. MIL допускает только аффинные преобразования, и поэтому требуется только шесть значений, а не полная матрица 3×3. Они поименованы и определены ниже.
При перемножении матрицы с точкой матрица преобразует точку из новой системы координат (СК) в предыдущую систему координат:
Преобразования могут быть вложены до любого уровня. Всякий раз, когда применяется новое преобразование, оно осуществляется путем умножения слева на текущую матрицу преобразования:
В большинстве мест в API "матрица" (Matrix) напрямую не используется. Вместо этого класс "преобразование" (Transform) поддерживает анимацию глубоким способом.
Для синтаксиса разметки порядок таков: M11, M12, M21, M22, OffsetX, OffsetY:
ТРЕХМЕРНЫЕ ВИЗУАЛЫ И ЭЛЕМЕНТЫ
Этот раздел посвящен трехмерным эффектам, главным образом, для обеспечения прямых трехмерных эффектов через уровень интеграции сред для использования в приложениях. Такие эффекты объединяются с удаленным доступом, печатью, композицией рабочего стола и четким образом участвуют в архитектуре MIL и элементной модели и с составляемостью сред по MIL. Например, деловая визуализация имеет некоторые требования к трехмерному представлению и интерактивности. Это может потребовать разрешения тестирования на попадание в тысячи или десятки тысяч объектов и будет приводить к интерактивным визуализациям, которые могут выглядеть наподобие изображения, показанного на фиг.41.
Описанные здесь признаки, в общем случае, относятся к визуализации и взаимодействию времени выполнения, а не к функциям, которые обеспечиваются посредством инструментальных средств моделирования. Функции моделирования, которые обеспечены, в большей степени нацелены на ситуации, когда анимацию или конструкцию времени выполнения нужно открыть времени выполнения. Например, может быть обеспечена экструзия трехмерного текста, даже если это в действительности особенность моделирования, когда приложениям потребуется привязка текста к данным, для чего требуется, чтобы это была особенность времени выполнения.
Согласно описанному выше визуалы (Visual) в системе MIL представляют дерево двухмерной композиции того, что в конце концов визуализируется. Заметим, что "элементы ПИ" и "средства управления" являются визуалами. Кроме того, визуалы имеют коллекцию дочерних визуалов, которые визуализируются перед (в терминах алгоритма программы рисования) собственным содержимым визуала. Опять же, согласно вышеописанному имеются различные конкретные подклассы визуала, включая RetainedVisual, SurfaceVisual, HwndVisual и т.д. RetainedVisual ("удержанный визуал") представляет набор двухмерных команд рисования, которые пользователь либо создает непосредственно в визуале, либо делает это как результат приема обратного вызова OnRender() от системы схем.
Будет показано, что 3D естественно вписывается в двухмерный визуальный мир. Для этого обеспечивается Retained3DVisual ("удержанный трехмерный визуал"), который, наподобие своего двухмерного аналога, представляет список команд рисования, который принудительно заполняется путем открытия контекста рисования, и визуализируется в этот контекст рисования. Это эффективно приводит к построению графа сцены или метафайла или списка команд в самом визуале.
Retained3DVisual по существу является набором трехмерных данных (команд визуализации/графа сцены/метафайла), включающих в себя источник света, камеру для задания двухмерной проекции этой сцены, прямоугольный двухмерный порт просмотра в локальном координатном пространстве для отображения проекции и другие внешние параметры, например переключатели сглаживания, переключатели тумана и т.п.
Заметим, что возможность OnRender() по требованию не обеспечена для Retained3DVisual. Эта функция существует для 2D, чтобы помогать управлению объемом данных, отрисованных в двухмерном мире. Наподобие 2D, визуализация происходит посредством DrawingContext ("контекста рисования"), где производятся вызовы "ощущения непосредственного режима". Например, в 2D, в контексте рисования может быть следующее:
Для согласованности с 2D в 3D предусмотрена аналогичная модель, которая выглядит как:
Заметим, что эта модель визуализации одинаково хорошо работает как для трехмерного визуала удержанного режима (где "команды" просто сохраняются), так и для трехмерного визуала непосредственного режима (где визуализация осуществляется напрямую, и камеру не нужно устанавливать над передним планом). Фактически, в случае удержанного режима то, что происходит внутренне в иерархии трехмерного моделирования, строится и удерживается. Альтернативно, в случае непосредственного режима ничего подобного не происходит, команды выдаются непосредственно, и поддерживается стек контекста (например, для преобразований).
Ниже приведен пример программирования с API трехмерного визуала. Этот пример создает Retained3DVisual, получает контекст рисования для визуализации в него, визуализирует в него примитивы и источник света, задает камеру и добавляет визуал к дочерним визуалам средства управления:
Интеграция 2D в 3D является значительной особенностью и позволяет отображать очень интересные сценарии, в которых двумерные пользовательские интерфейсы (ПИ) взаимодействуют в 3D мире, отображаются на трехмерные поверхности и т.д. 2D интегрируется в 3D разными способами. Один способ предусматривает текстуру на трехмерном примитиве. Для текстурирования 3D можно использовать любую двухмерную кисть (Brush) Avalon (а также более сложные спецификации материалов). Особым случаем этого является "кисть визуала" (VisualBrush), которая может хостировать произвольный визуал (а следовательно, и "средства управления" (Control) или все ПИ приложения). Предусмотрены механизмы для разрешения тестирования на попадание и дополнительного теста на попадание в этот визуал для приложений, которые желают делать это, и в некоторых случаях разрешение тестирования на попадание будет производиться так, что эти визуалы могут быть живыми. Заметим, что, поскольку сам визуал не является битовым образом, текстурирование визуала можно реализовать более изощренными способами, например, взяв двухмерную геометрию в визуале и перенеся ее в 3D, таким образом, в качестве текстур используется трехмерная векторная графика, а не двухмерные битовые образы.
Иным способом 2D интегрируется в 3D как команда визуализации на трехмерном контексте. Желательно также иметь возможность просто визуализировать двухмерный визуал, выровненный по экрану и немасштабированный без каких-либо эффектов перспективы, на определенной глубине по z, например, для укладки сферы, двухмерного визуала и куба, в то же время позволяя им совместно пользоваться одним и тем же z-буфером, что позволяет, например, сфере проходить через часть двухмерного ПИ. Это будет предоставлено командой "рисовать визуал" на "трехмерном контексте рисования".
Согласно описанному здесь "анимация" (Animation) должна работать интуитивно, и технология двухмерной анимации непосредственно трансформируема в 3D. Исключением является рационализация временных шкал.
Выше показан стиль использования принудительной визуализации, где команды рисования выдаются контексту. Это не декларативное использование, и, таким образом, этот принудительный подход непригоден для декларативной разметки.
Поскольку вышеописанный механизм не предусматривает явного внешнего построения трехмерной модели (хотя внутренне это происходит), программист не может обращаться к трехмерной модели или перечислять ее содержимое. В результате не оказывается места для записи "эффектов", которые берут модель в качестве входа и генерируют новую модель.
Для обеспечения декларативного способа построения и использования трехмерных "ресурсов", как в 2D, с помощью кистей, перьев, геометрии, путей и т.д. предусмотрен ряд типов, которые позволяют пользователю конструировать то, что идет в поток 3D-команд, и сконструированный объект можно установить в Retained3DVisual вместо того, чтобы использовать контекст.
Например, вышеприведенный иллюстративный код на основе "трехмерного контекста рисования" (Drawing3DContext) можно переписать следующим образом:
Здесь строится модель, которая затем назначается "удержанному трехмерному визуалу" (Retained3DVisual). Пары PushTransform/Pop заменяются конструкцией Model3DCollection, которая сама имеет преобразования и модели ниже себя. Обеспечение подхода моделирования и принудительного подхода на основе контекста обеспечивает решение для декларативной разметки уровня элементов, перечисления визуалов, эффектов графа сцены и изменяемости содержимого визуалов и осуществляется жизнеспособным способом с точки зрения архитектуры.
Корнем дерева классов моделирования является Model3D, который представляет трехмерную модель, которую можно присоединить к Retained3DVisual. В конечном итоге, источник света, сетки, потоки файлов .Х (так что они могут поступать из файла, ресурса, памяти и т.д.), группы моделей и двухмерные визуалы, размещенные в трехмерном пространстве, - все являются моделями. Таким образом, имеется следующая иерархия:
Сам класс 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" анализируются и понимаются общим анализатором XAML, но "1,2,8" поступает на "преобразователь типов", соответствующий Vector3D, и ожидается, что генерируется Vector3D (поскольку это тип свойства "ScaleVector" для ScaleTransform3D). Кроме того, заметим, что хотя и не перечисленные явно для каждого типа, эти типы имеют следующие методы (показаны здесь для Vector3D, но применимы и к другим):
Кроме того, любому типу, который является производным от Changeable (прямо или косвенно), понадобится иметь на себе метод "public new MyType Copy()". Эти типы примитивов просто существуют в поддержку других типов, описанных в этом разделе. При всякой возможности они зеркально отображают типы примитивов, используемые в 2D, это подобие является целью разработки для этих типов.
Объект Point3D является прямым аналогом типа двухмерной точки System.Windows.Point:
Vector3D - это прямой аналог типа двухмерного вектора System.Windows.Vector:
Point4D добавляет четвертую составляющую, w, к трехмерной точке и используется для преобразования посредством неаффинных Matrix3D. Это не Vector4D, поскольку составляющая 'w', равная 1, преобразуется в Point3D, и составляющая 'w', равная 0, преобразуется в Vector3D:
Кватернионы - это отчетливо трехмерные сущности, которые представляют поворот в трех измерениях. Их сила состоит в способности интерполировать (а, значит, анимировать) между кватернионами для достижения гладкой, надежной интерполяции. Конкретный механизм интерполяции называется сферическая линейная интерполяция.
Кватернионы можно строить, либо напрямую задавая их составляющие (x,y,z,w), либо как представление ось/угол. Первое представление может приводить к ненормализованным кватернионам, для которых определенные операции не имеют смысла (например, выделение оси и угла).
Составляющие "кватерниона" (Quaternion) нельзя задавать сразу после построения "кватерниона", поскольку имеется потенциальная неопределенность в осуществлении этого, например, задание "угла" (Angle) на ненормализованном "кватернионе" не имеет смысла.
Matrix3D - это трехмерный аналог System.Windows.Matrix. По аналогии с Matrix большинство API берут не Matrix3D, а Transform3D, которое поддерживает анимацию глубоким способом. Матрицы для трехмерных вычислений представлены матрицей 4×4. В MIL используется синтаксис вектора-строки:
При перемножении матрицы с точкой матрица преобразует точку из новой системы координат в предыдущую систему координат.
Преобразования могут быть вложены до любого уровня. Всякий раз, когда применяется новое преобразование, оно осуществляется путем умножения слева на текущую матрицу преобразования:
Transform3D, как и двухмерное преобразование, является абстрактным базовым классом с конкретными подклассами, представляющими особые типы трехмерного преобразования. Особые подклассы Transform3D также присутствуют в связи с анимацией.
Ниже приведена общая иерархия Transform3D согласно одной реализации:
Корневое Transform3D имеет статические методы для построения особых классов "преобразования" (Transform); (заметим, что это не предоставляет представление Matrix3D, поскольку это Transform может быть шире):
Заметим, что методы Transform(), которые берут Point3D/Vector3D, будут вызывать исключение, если преобразование не является аффинным.
Transform3DCollection будет имитировать TransformCollection, при этом методы Add* модифицируются таким же образом, как вышеописанные методы Create*:
AffineTransform3D - это базовый класс, производными от которого являются конкретные аффинные трехмерные преобразования (параллельный перенос, перекос, поворот, масштабирование), и он открывает доступ для чтения к Matrix3D:
TranslateTransform3D:
ScaleTransform3D:
RotateTransform3D - это больше, чем простое отображение из двухмерного поворота вследствие введения концепции оси для поворота вокруг нее (и, таким образом, использования кватернионов):
Заметим, что здесь анимируемым является только свойство "кватернион" (Quaterion). В общем случае, анимациям оси/углов не свойственно хорошо работать. Лучше анимировать кватернион, и мы может извлекать оси и углы из базового значения кватерниона. Если вы действительно хотите просто анимировать угол относительно фиксированной оси, простой способ его задания состоит в построении двух кватернионов, представляющих эти позиции, и анимации между ними.
MatrixTransform3D может строить Transform3D непосредственно из Matrix3D:
Когда свойство типа Transform3D задано в разметке, система свойств использует соответствующий Transform преобразователь типа для преобразования строкового представления в соответствующий объект, производный от Transform. Анимированные свойства не описываются с использованием этого синтаксиса, однако для описания анимаций можно использовать сложный синтаксис свойств.
Синтаксис моделируется на основе двухмерного преобразования, где <> представляет необязательный параметр:
Ниже приведен пример грамматики:
Retained3DVisual является производным от Visual и, таким образом, получает его свойства, включая непрозрачность, двухмерное геометрическое усечение, двухмерный режим смешивания, API тестирования на попадание, запрос двухмерных границ и участие в дереве визуалов. Заметим, что все из непрозрачности, усечения, режима смешивания и границ применяются к двухмерной проекции трехмерной сцены.
Прямоугольник ViewPort устанавливает, куда проекция, определенная комбинацией камера/модели, отображается в двухмерном локальном координатном пространстве. Заметим, что не существует метода Bounds3D на Retained3DVisual. Он доступен как Models.Bounds.
Drawing3DContext очень похож на двухмерный "контекст рисования" (DrawingContext) и доступен из Model3DCollection для Retained3DVisual посредством RenderOpen/RenderAppend. Он напоминает контекст визуализации непосредственного режима, хотя он удерживает команды внутренне.
Конкретные детали по семантике этих операций Drawing3DContext описаны ниже со ссылкой на API моделирования, для которого Drawing3DContext является удобным. Например, DrawImportedPrimitive (ImportedPrimitive3DSource primitiveSource, objectHitTestToken) создает ImportedPrimitive3D и добавляет его в накапливающую в данный момент Model3D (которой, в свою очередь, манипулируют методы Push/Pop на контексте).
DrawModel() - это еще одна точка пересечения между миром "контекста" и миром "моделирования", позволяющая "рисовать" Model3D в контекст. Не существует явного "обратного чтения" из Drawing3DContext, поскольку его поддерживает Model3DCollection, и эту коллекцию можно, при необходимости, перечислять.
API моделирования является открытым и защищенным API для этих классов (не показывая унаследованные члены), причем Model3D является абстрактной моделью, из которой все строится:
Model3DCollection - это то, где комбинация моделей строится и обрабатывается как целое, в необязательном порядке преобразуя или применяя к ним другие атрибуты:
Заметим, что Model3DCollection также имеет RenderOpen/Append, который возвращает Drawing3DContext. Использование этого контекста изменяет саму ModelCollection. Разница между RenderOpen() и RenderAppend() в том, что RenderOpen() сначала очищает коллекцию. Кроме того, заметим, что в этой реализации на Model3DCollection единомоментно можно открыть только один Drawing3DContext, и, когда он открыт, приложения не могут напрямую осуществлять доступ к содержимому этого Model3DCollection (для чтения или записи.
Источники света относятся к Model3D. Они включают в себя "окружающий" (Ambient), "локальный" (Positional), "направленный" (Directional) и "прожекторный" источники света. Источники света имеют дополнительное свойство являться частью иерархии моделирования и, таким образом, подлежат преобразованиям координатного пространства. На источниках света предусмотрены окружающий, рассеянный и отраженные цвета. Заметим также, что нет источников света, которые иерархически ограничены или ограничены конкретным объемом. Ниже приведена подходящая иерархия источников света:
Базовый класс "источник света" (Light) является абстрактным классом:
Окружающие источники света освещают объекты однородно, независимо от их формы:
Направленные источники света не имеют положения в пространстве и проецируют свой свет в определенном направлении, указанном вектором, который задает его:
Направление не нужно нормировать, но оно должно иметь ненулевой модуль.
Локальные источники света имеют положение в пространстве и проецируют свой свет во всех направлениях. Спад света управляется свойствами ослабления и дальности:
Заметим, что в отношении "точечного источника света" (PointLight) должно выполняться сильное требование наследования имен, чтобы "прожекторный источник света" (SpotLight) мог быть от него производным, но не третьи стороны.
SpotLight является производным от PointLight, поскольку имеет позицию, дальность и ослабление, но также добавляется направление и параметры для управления "конусом" света. Для управления "конусом" нужно задать "внешний угол конуса" (outerConeAngle) (за пределами которого ничто не освещается) и "внутренний угол конуса" (innerConeAngle) (внутри которого все полностью освещается). Освещение между внешним и внутренним углами конуса спадает линейно. (Заметим, что имеется "угловое" спадание между краем внутреннего конуса и внешнего конуса и спадание по расстоянию относительно позиции источника света и зависит от ослабления и диапазона.)
Заметим, что, как везде в API MIL, углы заданы в градусах.
Primitive3D являются концевыми вершинами (листьями), которые приводят к визуализации в дереве. Конкретные классы приносят явно заданные сетки, а также импортированные примитивы (файлы .х). Конструктор является внутренним; (расширяемость доступна через MeshPrimitive3D).
MeshPrimitive3D предназначен для моделирования с помощью сетки и материала:
Заметим, что MeshPrimitive3D является концевой геометрией и что он содержит, но не сам по себе, Mesh ("сетку"). Это значит, что Mesh может совместно использоваться множеством MeshPrimitive3D, с разными материалами, подлежащими разному тестированию на попадание, без дублирования данных сетки.
ImportedPrimitive3D представляет внешне полученный примитив (потенциально с материалом и анимацией), принесенный и преобразованный в надлежащую внутреннюю форму. Он обрабатывается как жесткая модель. Традиционным примером является .X File (файл .Х) и имеется подкласс ImportedPrimitive3DSource, который явно импортирует файлы .Х.
Поскольку ожидается, что файлы .х обычно включены в сцены, для выражения этого поддерживается простой формат "преобразователя типов" (TypeConverter), например:
VisualModel3D берет любой визуал (Visual) (по определению, двухмерный) и помещает его в сцену. При визуализации он будет выровнен по экрану, его размер не будет подвергаться изменению, но он будет в конкретной z-плоскости от камеры. Визуал будет оставаться интерактивным.
Визуализация 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 будет из формата файла .х.
Файлы .х поступают в систему в качестве XFile3DSource и используются для построения ImportedPrimitive3D. Он выделяется в отдельный тип данных, что позволяет использовать и совместно использовать его в множественных ImportedPrimitive3D. Надлежащие преобразования и обработка файла .х могут производиться при его построении или могут быть задержаны. Заметим, что файлы .х импортируются как целое, и ни к чему внутри них нельзя обращаться по отдельности.
Примитив Mesh3D - это непосредственный треугольный примитив (допускающий как индексированное, так и неиндексированное задание), который может быть построен программно. Заметим, что это поддерживает то, что, как ожидается, будет наиболее распространенным использованием примитива (а именно, информация позиции, нормали, цвета и текстуры, причем последние три не обязательны). Сетка также позволяет выбирать, следует ли ее отображать в виде треугольников, линий или точек. Она также поддерживает три топологии для интерпретации индексов, а именно список треугольников, полосу треугольников и веер треугольников.
Для форматов вершин и другой конструкции примитива, которые не поддерживаются непосредственно Mesh3D, можно построить и импортировать файл .x.
MeshPrimitiveType задан как:
Повершинные данные в 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), представляющую индексированную полосу треугольников только с помощью позиций и цветов, можно задать как:
Конечно, любые из них могут быть представлены гораздо более многословно в сложном синтаксисе свойств.
Методы, которые строят Primitive3D, берут "материал" (Material) для задания их внешнего вида. Material - это абстрактный базовый класс с тремя конкретными подклассами: BrushMaterial, VisualMaterial и AdvancedMaterial. BrushMaterial и VisualMaterial являются подклассами другого абстрактного класса, именуемого BasicMaterial. Таким образом:
Метод BrushMaterial берет одну "кисть" (Brush) и может использоваться для широкого круга эффектов, включая достижение прозрачности (попиксельной либо скалярной), обладание преобразованием текстур (даже анимационной), использование видео-текстур, неявных автогенерированных множественных отображений и т.п. В частности, для текстурирования чистых цветов, изображений, градиентов или даже другого визуала "кисть чистого цвета" (SolidColorBrush), "кисть изображения" (ImageBrush), "градиентная кисть" (GradientBrush) или "кисть визуала" (VisualBrush) будет использоваться для создания BrushMaterial.
Метод VisualMaterial предназначен для построения материала из визуала (Visual). Этот материал будет интерактивным в том смысле, что он будет передаваться в визуал из трехмерного мира, в который он внедрен. Одно различие между ним и BrushMaterial с VisualBrush состоит в том, что BrushMaterial являет неинтерактивным.
Класс AdvancedMaterial, хотя и гораздо сложнее, чем просто использование BrushMaterial или VisualMaterial, обеспечивает дополнительную гибкость.
Материалы усиливают гибкость и "экономию концепции", благодаря тому что основаны на "кисти" (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, как описано здесь.
Согласно описанному выше VisualMaterial инкапсулирует интерактивный визуал (Visual). Он отличается от BrushMaterial, используемого с визуалом, тем, что визуал остается действующим в своей текстурированной форме. Заметим, что поскольку отображение базируется на сетке u,v, связанной с примитивом, бывают случаи, когда пользовательский ввод не будет иметь полной точности со своим двухмерным аналогом. Например, рассмотрим сценарий захваты мыши, где двухмерное средство управления захватывает мышь (например, для реализации поведения линейки прокрутки). В этих сценариях мыши позволяется отклоняться от линейки прокрутки, но система способна понимать, например, позицию y мыши относительно линейки прокрутки и действовать правильно. В ситуации, когда средство управления текстурируется на примитив с помощью сетки u,v и мышь отклоняется от примитива, в общем случае, произвести такое определение не представляется возможным. В попытках противодействовать этому можно предпринять ряд действий, но в общем случае, все сводится к ограничению отображения в двухмерное пространство. Заметим, что в результате Visual некоторым образом удочеряется корневым Retained3DVisual. В одной реализации неправомерно использовать один "элемент ПИ" (UiElement) в более чем одном "материале" (Material) или использовать VisualMaterial более чем в одном месте, в силу того что инфраструктура базируется на одинарном удочерении средств управления.
BrushMaterials/VisualMaterials и BumpMaps используются для определения AdvancedMaterial:
Заметим, что "карты среды" (EnvironmentMap) являются текстурами, которые, как ожидается, существуют в конкретном формате для обеспечения кубического текстурирования. В частности, шесть граней куба кубической карты нужно будет представлять в секциях "кисти" (Brush), связанных с "текстурой" (Texture) (скорее всего, что-либо вроде сетки 3×2 на "кисти"). Заметим, что свойства "окружающий" (Ambient), "рассеянный" (Diffuse) и "отражающий" (Specular) берут BasicMaterial, а не общий Material, поскольку не разрешается задавать их как сами AdvancedMaterials. Кроме того, заметим, что карты среды представляют собой исключительно BrushMaterials, поскольку интерактивность, которую обеспечивает VisualMaterial, не имеет смысл для карт среды.
Карты рельефа представляют собой сетки, которые, наподобие текстур, отображаются на трехмерные примитивы посредством текстурных координат на примитивах. Однако интерполированные данные интерпретируются как возмущения нормалей к поверхности, приводящие к "рельефному" внешнему виду примитива. Для достижения этого отображения рельефа несут информацию, например возмущения нормали, и потенциально другую информацию. Вследствие этого неправильно использовать "кисть" в качестве отображения рельефа. Поэтому можно обеспечить новый класс BumpMap в качестве ImageSource конкретного формата пикселя.
"Материал" (Material) обеспечивает простой "преобразователь типов" (TypeConverter), который позволяет автоматически продвигать строковое задание "кисти" (Brush) в BrushMaterial:
Это допускает спецификации такие, как:
Можно предусмотреть внешние параметры модели, которые нельзя встраивать на произвольных уровнях в геометрической иерархии. Например, туман можно добавлять к сцене, задавая свойство "туман" (Fog) на Retained3DVisual, например, доступным "туманом" является "пиксельный туман". "Fog" представлен как абстрактный класс, и иерархия показана ниже:
fogDensity находится в пределах от 0 до 1 и является нормированным представлением плотности тумана. fogStart и fogEnd содержат z-глубины, заданные в пространстве устройств [0,1], и представляют то, где начинается и оканчивается туман.
"Камера" (Camera) - это механизм, посредством которого трехмерная модель проецируется в двухмерный визуал. Camera сама по себе является абстрактным типом с двумя конкретными подтипами: ProjectionCamera ("проекционная камера") и MatrixCamera ("матричная камера"). ProjectionCamera берет такие хорошо понятные параметры, как Position ("позиция"), LookAtPoint ("смотреть в точку") и FieldOfView ("поле зрения") для построения Camera. MatrixCamera берет Matrix3D, используемую для задания преобразования "мира в устройство".
В "удержанном трехмерном визуале" (Retained3DVisual) "камера" (Camera) используется для обеспечения просмотра Model3D, и результирующая проекция отображается в двухмерный "порт просмотра" (ViewPort), установленный на Retained3DVisual. Заметим, что двухмерный ограничивающий прямоугольник для Retained3DVisual будет просто проекцией трехмерного ограничивающего прямоугольного параллелепипеда трехмерной модели, упакованной в его выпуклую, выровненную по осям оболочку, усеченную до усечения, установленного на визуале.
ProjectionCamera - это средство построения камеры из таких параметров, как Position, LookAtPoint и FieldOfView. Она инкапсулирует как перспективные проекции, так и ортографические проекции. На фиг.42 четко показаны важные аспекты ProjectionCamera, а именно просмотр и позиция (где "поле зрения" (FieldOfView) должно быть в горизонтальном направлении).
Заметим, что FieldOfView представляет горизонтальное поле зрения и задано в градусах (как другие углы MIL). Расстояния ближнего и дальнего плана (NearPlaneDistance и FarPlaneDistance представляют расстояния в координатах трехмерного мира от "позиции" (Position) камеры в направлении вектора LookDirection ("направления наблюдения"). NearPlaneDistance по умолчанию равно нулю, а FarPlaneDistance по умолчанию равно бесконечности.
Если после фактического проецирования NearPlaneDistance и FarPlaneDistance по-прежнему равны нулю и бесконечности, соответственно, модель проверяется и ее ограничивающий объем проецируется согласно проекции камеры. Затем проверяется результирующий ограничивающий объем, так что расстояние ближнего плана задается по плоскости ограничивающего объема, перпендикулярной "направлению наблюдения" (LookDirection), ближайшей к позиции камеры. По существу, то же самое делается для дальнего плана, но с использованием самой удаленной плоскости. Это позволяет оптимально использовать разрешение z-буфера, но, тем не менее, отображать всю модель. Если IsOrthographic равно "истина", то используется ортографическая проекция, и FieldOfView игнорируется.
Заметим, что "плоскость проекции", заданная параметрами "проекционной камеры" (ProjectionCamera), затем отображается в прямоугольник "порта просмотра" (ViewPort) на "удержанном трехмерном визуале" (Retained3DVisual) и представляет окончательный переход из трехмерного пространства в двухмерное пространство.
"Матричная камера" (MatrixCamera) как подкласс Camera обеспечивает средство непосредственного задания "матрицы" (Matrix) как проекционного преобразования. Это полезно для приложений, имеющих собственные механизмы вычисления проекционной матрицы.
Заметим, что значения MinimumZ и MaximumZ непосредственно управляют диапазоном z-буфера. Они рассматриваются после применения Matrix в качестве проекционной матрицы.
Результирующая проекция отображается в прямоугольник "порта просмотра" (ViewPort) на "удержанном трехмерном визуале" (Retained3DVisual) и представляет окончательный переход из трехмерного пространства в двухмерное пространство.
Ниже приведены более полные разметки, показывающие задание всей иерархии Model3D в XAML. Заметим, что некоторая часть синтаксиса может меняться.
В нижеприведенном примере просто создается "модель" (Model) с двумя импортированными файлами .х и преобразованием поворота одного из них (вокруг оси z на 45 градусов) и одним точечным источником белого света, находящимся выше в точке 0,1,0.
Внутри тегов
Непосредственно дочерними узлами для Viewport3D являются Model3D, поскольку коллекция "по умолчанию" представляет собой коллекцию "модели". Синтаксис этого можно, в общем случае, изменять, чтобы потребовать задание
В общем случае, тестирование на попадание осуществляется на уровне "элемента ПИ" (UiElement) или "средства управления" (Control). Однако тестирование на попадание будет ограничиваться Viewport3D как целым, в противном случае, для тестирования на попадание потребуется разделить модели на множественные Viewport3D и тестировать их на попадание. Однако, когда они оказываются в множественных Viewport3D, они уже не являются частью одной и той же трехмерной среды. Чтобы разрешить эту ситуацию, нужно позволить задавать обработчик событий на отдельных членах Model3D в разметке или коде, а также "идентификатор" ("id"). При разрешении тестирования на попадание будет вызваться соответствующий метод (отправителем по-прежнему будет Viewport3D как целое), и идентификатор будет поступать как часть EventArgs ("аргументов события"). Затем приложения должны иметь возможность изолировать свою логику либо в разных обработчиках событий, либо отключив "идентификатор".
Использование VisualMaterial указывает системе, что тестирование на попадание должно переходить в этот внедренный элемент. Система гарантирует, что это действительно происходит. Таким образом, операция тестирования на попадание переходит из 2D в 3D и обратно в 2D. Ниже приведено задание API для Viewport3D:
Чтобы делать прозрачность правильно в 3D, примитивы (и, в пределе, отдельные треугольники) сортируются для визуализации от заднего плана к переднему, вследствие чего z-буфер не участвует. Это необходимо, чтобы гарантировать правильное смешивание.
Предусмотрен также EmbeddingHostVisual. Это подкласс Visual, который не может быть построен внешними сторонами и может быть построен только внутренне. EmbeddingHostVisual содержит Visual, который представляет, что заданный визуал используется как "внедряющий хост" для некоторого соотношения между визуалами, отличного от соотношения предок/потомок. Двухмерные визуалы, внедренные в трехмерную сцену, являются другим примером этого. Эффекты могут быть еще одним примером одного соотношения между визуалами, отличного от соотношения предок/потомок.
Определение приведено ниже:
Поскольку соотношение с "предком" EmbeddingHostVisual задано так, чтобы не быть обычным соотношением предок/потомок, это значит, что услуги чтения, наподобие извлечения "преобразования" (Transform) между визуалами, не имеют смысла, поскольку это не однородное отображение. Эти операции обрабатываются в самой трехмерной подсистеме, но заметим, что механизм уникален для Retained3DVisual.
СРЕДЫ
"Данные сред" (MediaData) можно использовать для воспроизведения любого аудио/видео содержимого посредством следующего объекта, также описанного в прилагаемом Приложении:
Для аудиоданных предусмотрен объект AudioData:
Для видеоданных предусмотрен объект VideoData:
Также предусмотрен объект Video уровня элементов:
Предусмотрен также объект Audio уровня элементов:
ЗАКЛЮЧЕНИЕ
Из предыдущего подробного описания следует, что обеспечен уровень интеграции сред, содержащий интерфейсы и объектную модель, которые обеспечивают программный код с возможностью взаимодействия с графом сцены. Система, способ и объектная модель предусматривают прямое использование и в то же время мощность, гибкость и расширяемость.
Хотя изобретение предусматривает различные модификации и альтернативные конструкции, определенные иллюстрируемые варианты его осуществления показаны в чертежах и подробно описаны выше. Однако следует понимать, что не предполагается ограничить изобретение конкретными раскрытыми формами, но напротив, предполагается охватить все модификации, альтернативные конструкции и эквиваленты, отвечающие сущности и объему изобретения.
Изобретение относится к способам обработки графической и иной видеоинформации для отображения в компьютерных системах. Техническим результатом изобретения является усовершенствование графической модели, которая позволяет пользоваться многочисленными особенностями и возможностями обработки графики и эффективно выводить сложные графические и аудиовизуальные данные. Технический результат достигается благодаря тому, что уровень интеграции сред, включающий в себя интерфейс прикладного программирования (API) и объектную модель, позволяет разработчикам программного кода согласованно взаимодействовать со структурой данных графа сцены для вывода графики. Посредством интерфейсов программный код добавляет дочерние визуальные объекты к другим визуальным объектам для построения иерархического графа сцены, записывает списки команд, например данные геометрии, данные изображения, данные анимации и другие данные для вывода, и может задавать на визуальных объектах свойства преобразования, усечения и непрозрачности. Уровень интеграции сред и API позволяют программистам выполнять композиционные эффекты в своих приложениях напрямую, в то же время усиливая блок обработки графики таким образом, чтобы не оказывать неблагоприятное воздействие на нормальную производительность приложений. Многоуровневая система включает в себя возможность объединения различных типов сред (например, двумерной графики, трехмерной графики, видео, аудио, текста и построения изображения) и их гладкой и бесстыковой анимации. 3 н. и 63 з.п. ф-лы, 42 ил.
Компьютерное устройство для выполнения прикладных программ
Комментарии