Добрый день,
В скором времени выйдет новое крупное обновление 1.18.14.0, в которое войдет новый функционал, который вам однозначно пригодится. В этой статье рассмотрим новые графические возможности по работе с канвой TfgCanvas.
Работа с состоянием канвы
Перед тем, как мы перейдем к новым возможностям, мы обязаны рассмотреть изменения в работе с состоянием канвы. Поскольку эти изменения необходимы для корректной работы с обрезанием изображений по произвольным кривым.
Напомню, что при работе с канвой TfgCanvas разработчик постоянно меняет ее параметры (настройки кисти, матрицы трансформации и т.д.). Поэтому, очень удобно иметь контрольные точки при отрисовке, к которым можно сбросить состояние канвы, чтобы не устанавливать все настройки заново. Для этих целей используются методы SaveState / RestoreState. Метод SaveState возвращал объект состояния, который в дальнейшем нужно было передавать в парный метод RestoreState для восстановления состояния. В новой версии, объект состояния больше не нужно использовать.
Было:
var
State: IFGXSaveState;
State := ACanvas.SaveState;
try
// Задаем параметры отрисовки
// Выполняем отрисовку
finally
ACanvas.RestoreState(State);
end;Стало:
ACanvas.SaveState;
try
// Задаем параметры отрисовки
// Выполняем отрисовку
finally
ACanvas.RestoreState;
end;Теперь при вызове SaveState, канва автоматически сохраняет состояние во внутренний стек, а при вызове RestoreState извлекает его из стека и восстанавливает.
Зачем?
Первая причина: Раньше мы сохраняли только параметры кистей, параметры шрифта, режим смешивания цветов и матрицу преобразования. Это было обусловленно тем, что мы в любой момент могли самостоятельно задать любые параметры канвы. Однако, в данном релизе добавлена операция накладывания произвольной области отсечения, которая является не обратимой операцией (это значит, что заданную область отсечения нельзя убрать парным методом). Единственный способ, сбросить или откатить заданную область отсечения - это использовать нативный механизм работы с состоянием канвы. В связи с этим, мы перешли от нашей реализации к нативной, которая НЕ предусматривает восстановление к произвольному состоянию.
Вторая причина: Чаще всего при выполнении последовательной отрисовки, восстановление канвы требуется не на произвольное состояние, а именно на предыдущее состояние.
{ State 1 }
ACanvas.SaveState; // Save State 1
{ State 2 }
try
// Задаем параметры отрисовки
// Выполняем отрисовку
ACanvas.SaveState; // Save State 2
try
// Другая вложенная отрисовка
finally
ACanvas.RestoreState; // Rollback to State 2
end;
finally
ACanvas.RestoreState; // Rollback to State 1
end;Внимание: Не смотря на то, что старый подход по работе с состоянием сохранился, он может не корректно работать при использовании областей отсечения TfgCanvas.ClipPath.
Обрезание по пути
Одна из самых интересных графических возможностей - это ограничение области отрисовки на канве произвольной фигурой или путем. Накладывание ограничения выполняется при помощи нового метода TfgCanvas.ClipPath.
/// <summary>
/// Выполняет накладывание области отсечения будущих команд отрисовки. Чтобы отменить наложенную область ограничения
/// необходимо использовать методы <c>SaveState</c> / <c>RestoreState</c>. Перед наложение области сохранить состояние
/// канвы, а после завершения работы - восстановить.
/// </summary>
procedure ClipPath(const APath: TfgPath);Рассмотрим пример, где мы хотим нарисовать два овала красного и синего цветов. Красный обрезать по вписанному овалу в область отрисовки, а синий - не обрезать.
Тогда код по отрисовке будет выглядить так:
procedure TFormMain.fgPaintBoxPaint(Sender: TObject; const ACanvas: TfgCanvas);
var
CirclePath: TfgPath;
begin
// Сохраняем состояние, чтобы в будущем убрать область отсечения
ACanvas.SaveState;
CirclePath := TfgPath.Create(nil);
try
// Формируем область отсечения в виде эллипса, вписанного в область отрисовки.
CirclePath.AddArc(fgPaintBox.LocalBounds, 0, 360)
.ClosePath;
ACanvas.ClipPath(CirclePath);
// Рисуем красный круг, он будет обрезан
ACanvas.Fill.Kind := TfgBrushKind.Solid;
ACanvas.Fill.Color := TAlphaColorRec.Red;
ACanvas.FillCircle(TPointF.Create(0, fgPaintBox.Height / 2), fgPaintBox.Height / 2);
finally
CirclePath.Free;
end;
// Сбрасываем область отсечения
ACanvas.RestoreState;
// Рисуем синий круг без ограничений
ACanvas.Fill.Color := TAlphaColorRec.Blue;
ACanvas.FillCircle(TPointF.Create(fgPaintBox.Width, fgPaintBox.Height / 2), fgPaintBox.Height / 2);Где это может пригодиться? Одной из областей применения может стать динамическая отрисовка изображений, обрезанных по произвольной фигуре. Например, вы хотите вывести фотографии или аватарки пользователей в виде круга или какой-нибудь декоративной рамки. Раньше вы могли использовать для этой цели маски. Для этого вам надо было сформировать изображение маски, и затем выполнить отрисовки битмапа с учетом этой маски. Теперь эта операция может выполняться гораздо проще, быстрее и без использования дополнительной памяти.
У нас есть изображение, которое мы хотим вывести в круглом варианте и декорировать белым кольцом. Код может выглядить следующим образом. Фотография находится в менеджере ресурсов.
procedure TFormMain.fgPaintBox1Paint(Sender: TObject; const ACanvas: TfgCanvas);
var
PhotoBitmap: TfgBitmap;
FramePath: TfgPath;
FrameBounds: TRectF;
begin
// Извлекаем изображение
if not TfgAssetsManager.Current.TryGetBitmap(R.Bitmap.PHOTO, PhotoBitmap) then
Exit;
ACanvas.SaveState;
try
// Формируем круглую форму рамки и рисуем битмап
FramePath := TfgPath.Create(nil);
try
FrameBounds := fgPaintBox1.LocalBounds;
FramePath.AddArc(FrameBounds, 0, 360)
.ClosePath;
ACanvas.ClipPath(FramePath);
ACanvas.DrawBitmap(PhotoBitmap, FrameBounds);
finally
FramePath.Free;
end;
finally
ACanvas.RestoreState;
end;
// Декорируем белым кольцом
ACanvas.Stroke.Thickness := 10;
ACanvas.Stroke.Color := TAlphaColorRec.White;
ACanvas.DrawCircle(FrameBounds.CenterPoint, FrameBounds.Width / 2 - 10);
end;Заполнение пути
Есть как минимум два способа заливки кривых фигур по пути TfgPath. По умолчанию, в FGX Native использовался способ заливки правой фигуры. Теперь же вы можете выбирать способ заливки и указывать его через свойство TfgCanvas.FillPathRule. Помимо этого, теперь это свойство так же доступно в компоненте TfgSvgPath.

Детали о двух способах заливки доступны здесь.
Внимание: Данная настройка пока не поддерживается в дизайнере, так как не поддерживается на уровне FMX, которая используется для реализации в дизайнере.
Штрихи кисти
Теперь вы можете использовать свои варианты штрихов при отрисовки кривых. На уровне кисти TfgStrokeBrush доступен новый вид линии - Dash = Custom. Параметры штриха задаются через новый метод TfgStrokeBrush.SetCustomDash.
procedure TfgStrokeBrush.SetCustomDash(const ADash: TDashArray; const AOffset: Single);ADash- это массив парных значений. Первое - это длина штриха, второе - длина пробела.
SVG
В этом релизе расширена поддержка параметров SVG, а именно добавлена поддержка следующих атрибутов: stroke-dasharray, stroke-dashoffset, stroke-linecap, stroke-linejoin, fill-rule.
Редактор градиента
Мы добавили дизайнер для управления параметрами градиента. Новое окно доступно через кнопку, встроенную в свойство типа TfgGradient.
Данный дизайнер позволяет, управлять практически всеми параметрами градиента:
Добавлять и удалять точки градиента. Чтобы добавить точку, нужно нажать в свободное место на нижнем компоненте с отображением градиента. Чтобы удалить, точку надо утянуть за пределы компонента.
Менять тип градиента - линейный или радиальный
Менять направление градиента при помощи точек на правой панели просмотра градиента.
Сбросить параметры градиента до значений по умолчанию.
Итог
В следующей статье нас ждет обзор новой функции перетаскивания компонентов Drag & Drop.
Recommended Comments