Текстуры

77

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

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

Чтобы многократно повторить изображение в виде мозаики, понадобится установить свойство ImageSource (для идентификации исходного изображения, которое должно быть повторено), а также свойства Viewport, ViewportUnits и TileMode. Последние три свойства определяют размер мозаики и способ ее размещения.

Для установки размера каждого фрагмента мозаики применяется свойство Viewport. Чтобы использовать фрагменты пропорционального размера, значение ViewportUnits должно быть установлено в RelativeToBoundingBox (по умолчанию так и есть). Затем размер фрагмента определяется в пропорциональной координатной системе от 0 до 1 по каждому измерению. Другими словами, мозаика, имеющая координаты левого верхнего угла (0,0) и правого нижнего (1,1), покрывает всю область заполнения. Для получения мозаичной структуры необходимо определить Viewport меньшего размера, чем общий размер площади заполнения, как показано ниже:

<ImageBrush ImageSource="tile.png" TileMode="Tile"
Viewport="0,0 0.5,0.5"></ImageBrush>

Это создает прямоугольник Viewbox, который начинается в левом верхнем углу области заполнения (0,0) и простирается до середины (0.5, 0.5). В результате область заполнения всегда будет покрыта четырьмя фрагментами мозаики, независимо от его размера. Такое поведение хорошо тем, что исключает опасность того, что часть мозаики придется "ломать" на границе фигуры. (Разумеется, это не касается случая, когда используется ImageBrush для заполнения нерегулярной области.)

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

Данное поведение можно изменить, модифицировав свойство Stretch (которое по умолчанию имеет значение Fill). Значение None гарантирует, что фрагменты мозаики не будут деформированы и сохранят свои первоначальные пропорции. Однако если заполняемая область не имеет форму квадрата, между фрагментами появятся пробелы. Этот вариант показан в третьей части рисунке:

<Grid Margin="5">
    <Grid.RowDefinitions>
      <RowDefinition></RowDefinition>
      <RowDefinition></RowDefinition>
      <RowDefinition></RowDefinition>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto"></ColumnDefinition>
      <ColumnDefinition></ColumnDefinition>
    </Grid.ColumnDefinitions>

    <TextBlock Margin="3">Fixed Tiles</TextBlock>
    <Rectangle Grid.Column="1" Stroke="Black">
      <Rectangle.Fill>
        <ImageBrush ImageSource="tile.jpg" TileMode="Tile"
                    ViewportUnits="Absolute" Viewport="0 0 37 37"></ImageBrush>
      </Rectangle.Fill>
    </Rectangle>

    <TextBlock Grid.Row="1" Margin="3">Proportional Tiles</TextBlock>
    <Rectangle Grid.Row="1" Grid.Column="1" Stroke="Black">
      <Rectangle.Fill>
        <ImageBrush ImageSource="tile.jpg" TileMode="Tile"
                    Viewport="0 0 0.2 0.2"></ImageBrush>
      </Rectangle.Fill>
    </Rectangle>

    <TextBlock Grid.Row="2" Margin="3">
      Proportional Tiles<LineBreak></LineBreak>(no stretch)
    </TextBlock>
    <Rectangle Grid.Row="2" Grid.Column="1" Stroke="Black">
      <Rectangle.Fill>
        <ImageBrush ImageSource="tile.jpg" TileMode="Tile" Stretch="None"
                    Viewport="0 0 0.2 0.2"></ImageBrush>
      </Rectangle.Fill>
    </Rectangle>

  </Grid>
Мозаичные текстуры
Деформация мозаичных структур при изменении размеров окна

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

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

Альтернативное решение состоит в определении размера мозаики в абсолютных координатах, на основе размера исходного изображения. Чтобы предпринять такой шаг, ViewportUnits устанавливается в Absolute (вместо RelativeToBoundBox). Ниже показан пример определения размеров элемента мозаики в 32x32 с началом в левом верхнем углу:

<ImageBrush ImageSource="tile.jpg" TileMode="Tile"
   ViewportUnits="Absolute" Viewport="0, 0 32, 32"></ImageBrush>

Такой тип мозаичной структуры показан в первом прямоугольнике на вышеуказанном рисунке. Его недостаток в том, что высота и ширина области заполнения должны делиться на 32. В противном случае получатся неполные фрагменты по краям. В случае применения ImageBrush для заполнения элемента изменяемого размера решить эту проблему невозможно и придется смириться с тем, что края фрагментов мозаики не всегда совпадут с краями заполняемой области.

Все рассмотренные до сих пор мозаичные структуры использовали значение Tile свойства TileMode. Это можно изменить, задав альтернативное расположение фрагментов. Ниже перечислены возможные варианты:

Tile

Копирует изображение по всей доступной области

FlipX

Копирует изображение, переворачивая каждый второй столбец вокруг вертикальной оси

FlipY

Копирует изображение, переворачивая каждый второй столбец вокруг горизонтальной оси

FlipXY

Копирует изображение, переворачивая каждый второй столбец вокруг вертикальной и горизонтальной осей

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

<Grid>
  <UniformGrid>
    <Rectangle Stroke="Black">
      <Rectangle.Fill>
        <ImageBrush ImageSource="tile.jpg" TileMode="Tile" 
                    ViewportUnits="Absolute" Viewport="0 0 37 37"></ImageBrush>
      </Rectangle.Fill>
    </Rectangle>
    <Rectangle Stroke="Black">
      <Rectangle.Fill>
        <ImageBrush ImageSource="tile.jpg" TileMode="FlipX"
                    ViewportUnits="Absolute" Viewport="0 0 37 37"></ImageBrush>
      </Rectangle.Fill>
    </Rectangle>
    <Rectangle Stroke="Black">
      <Rectangle.Fill>
        <ImageBrush ImageSource="tile.jpg" TileMode="FlipY"
                    ViewportUnits="Absolute" Viewport="0 0 37 37"></ImageBrush>
      </Rectangle.Fill>
    </Rectangle>
    <Rectangle Stroke="Black">
      <Rectangle.Fill>
        <ImageBrush ImageSource="tile.jpg" TileMode="FlipXY"
                    ViewportUnits="Absolute" Viewport="0 0 37 37"></ImageBrush>
      </Rectangle.Fill>
    </Rectangle>
  </UniformGrid>
    <UniformGrid>
      <UniformGrid.Resources>
        <Style TargetType="{x:Type TextBlock}">
          <Setter Property="HorizontalAlignment" Value="Center"></Setter>
          <Setter Property="VerticalAlignment" Value="Bottom"></Setter>
          <Setter Property="FontSize" Value="25"></Setter>
          <Setter Property="FontWeight" Value="Bold"></Setter>
          <Setter Property="Margin" Value="3"></Setter>
        </Style>
      </UniformGrid.Resources>
      <TextBlock>Tile</TextBlock>
      <TextBlock>FlipX</TextBlock>
      <TextBlock>FlipY</TextBlock>
      <TextBlock>FlipXY</TextBlock>
    </UniformGrid>
  </Grid>
Модификация свойства TileMode
Пройди тесты
Лучший чат для C# программистов