rui_orig/viewFilter.go

306 lines
9.3 KiB
Go
Raw Normal View History

2021-09-07 17:36:50 +03:00
package rui
import (
"fmt"
"strings"
)
const (
// Blur is the constant for the "blur" property tag of the ViewFilter interface.
// The "blur" float64 property applies a Gaussian blur. The value of radius defines the value
// of the standard deviation to the Gaussian function, or how many pixels on the screen blend
// into each other, so a larger value will create more blur. The lacuna value for interpolation is 0.
// The parameter is specified as a length in pixels.
Blur = "blur"
// Brightness is the constant for the "brightness" property tag of the ViewFilter interface.
// The "brightness" float64 property applies a linear multiplier to input image, making it appear more
// or less bright. A value of 0% will create an image that is completely black.
// A value of 100% leaves the input unchanged. Other values are linear multipliers on the effect.
// Values of an amount over 100% are allowed, providing brighter results.
Brightness = "brightness"
// Contrast is the constant for the "contrast" property tag of the ViewFilter interface.
// The "contrast" float64 property adjusts the contrast of the input.
// A value of 0% will create an image that is completely black. A value of 100% leaves the input unchanged.
// Values of amount over 100% are allowed, providing results with less contrast.
Contrast = "contrast"
// DropShadow is the constant for the "drop-shadow" property tag of the ViewFilter interface.
// The "drop-shadow" property applies a drop shadow effect to the input image.
// A drop shadow is effectively a blurred, offset version of the input image's alpha mask
// drawn in a particular color, composited below the image.
// Shadow parameters are set using the ViewShadow interface
DropShadow = "drop-shadow"
// Grayscale is the constant for the "grayscale" property tag of the ViewFilter interface.
// The "grayscale" float64 property converts the input image to grayscale.
// The value of amount defines the proportion of the conversion.
// A value of 100% is completely grayscale. A value of 0% leaves the input unchanged.
// Values between 0% and 100% are linear multipliers on the effect.
Grayscale = "grayscale"
// HueRotate is the constant for the "hue-rotate" property tag of the ViewFilter interface.
// The "hue-rotate" AngleUnit property applies a hue rotation on the input image.
// The value of angle defines the number of degrees around the color circle the input samples will be adjusted.
// A value of 0deg leaves the input unchanged. If the angle parameter is missing, a value of 0deg is used.
// Though there is no maximum value, the effect of values above 360deg wraps around.
HueRotate = "hue-rotate"
// Invert is the constant for the "invert" property tag of the ViewFilter interface.
// The "invert" float64 property inverts the samples in the input image.
// The value of amount defines the proportion of the conversion.
// A value of 100% is completely inverted. A value of 0% leaves the input unchanged.
// Values between 0% and 100% are linear multipliers on the effect.
Invert = "invert"
// Saturate is the constant for the "saturate" property tag of the ViewFilter interface.
// The "saturate" float64 property saturates the input image.
// The value of amount defines the proportion of the conversion.
// A value of 0% is completely un-saturated. A value of 100% leaves the input unchanged.
// Other values are linear multipliers on the effect.
// Values of amount over 100% are allowed, providing super-saturated results.
Saturate = "saturate"
// Sepia is the constant for the "sepia" property tag of the ViewFilter interface.
// The "sepia" float64 property converts the input image to sepia.
// The value of amount defines the proportion of the conversion.
// A value of 100% is completely sepia. A value of 0% leaves the input unchanged.
// Values between 0% and 100% are linear multipliers on the effect.
Sepia = "sepia"
//Opacity = "opacity"
)
// ViewFilter defines an applied to a View a graphical effects like blur or color shift.
// Allowable properties are Blur, Brightness, Contrast, DropShadow, Grayscale, HueRotate, Invert, Opacity, Saturate, and Sepia
type ViewFilter interface {
Properties
fmt.Stringer
2022-05-22 12:54:02 +03:00
stringWriter
2021-09-07 17:36:50 +03:00
cssStyle(session Session) string
}
type viewFilter struct {
propertyList
}
// NewViewFilter creates the new ViewFilter
func NewViewFilter(params Params) ViewFilter {
2022-05-05 17:18:08 +03:00
if params != nil {
filter := new(viewFilter)
filter.init()
for tag, value := range params {
filter.Set(tag, value)
}
if len(filter.properties) > 0 {
return filter
}
2021-09-07 17:36:50 +03:00
}
return nil
}
func newViewFilter(obj DataObject) ViewFilter {
filter := new(viewFilter)
filter.init()
for i := 0; i < obj.PropertyCount(); i++ {
if node := obj.Property(i); node != nil {
tag := node.Tag()
switch node.Type() {
case TextNode:
filter.Set(tag, node.Text())
case ObjectNode:
if tag == HueRotate {
// TODO
} else {
ErrorLog(`Invalid value of "` + tag + `"`)
}
default:
ErrorLog(`Invalid value of "` + tag + `"`)
}
}
}
if len(filter.properties) > 0 {
return filter
}
ErrorLog("Empty view filter")
return nil
}
2022-07-26 18:36:00 +03:00
func (filter *viewFilter) Set(tag string, value any) bool {
2021-09-07 17:36:50 +03:00
if value == nil {
filter.Remove(tag)
return true
}
switch strings.ToLower(tag) {
case Blur, Brightness, Contrast, Saturate:
return filter.setFloatProperty(tag, value, 0, 10000)
case Grayscale, Invert, Opacity, Sepia:
return filter.setFloatProperty(tag, value, 0, 100)
case HueRotate:
return filter.setAngleProperty(tag, value)
case DropShadow:
return filter.setShadow(tag, value)
}
ErrorLogF(`"%s" property is not supported by the view filter`, tag)
return false
}
func (filter *viewFilter) String() string {
2022-05-22 12:54:02 +03:00
return runStringWriter(filter)
2021-09-07 17:36:50 +03:00
}
2022-05-22 12:54:02 +03:00
func (filter *viewFilter) writeString(buffer *strings.Builder, indent string) {
buffer.WriteString("filter { ")
comma := false
tags := filter.AllTags()
for _, tag := range tags {
if value, ok := filter.properties[tag]; ok {
if comma {
buffer.WriteString(", ")
}
buffer.WriteString(tag)
buffer.WriteString(" = ")
writePropertyValue(buffer, tag, value, indent)
comma = true
}
2021-09-07 17:36:50 +03:00
}
2022-05-22 12:54:02 +03:00
buffer.WriteString(" }")
2021-09-07 17:36:50 +03:00
}
func (filter *viewFilter) cssStyle(session Session) string {
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
2022-08-18 18:18:36 +03:00
if value, ok := floatTextProperty(filter, Blur, session, 0); ok {
2021-09-07 17:36:50 +03:00
buffer.WriteString(Blur)
buffer.WriteRune('(')
2022-08-18 18:18:36 +03:00
buffer.WriteString(value)
buffer.WriteString("px)")
2021-09-07 17:36:50 +03:00
}
for _, tag := range []string{Brightness, Contrast, Saturate, Grayscale, Invert, Opacity, Sepia} {
2022-08-18 18:18:36 +03:00
if value, ok := floatTextProperty(filter, tag, session, 0); ok {
2021-09-07 17:36:50 +03:00
if buffer.Len() > 0 {
buffer.WriteRune(' ')
}
2022-08-18 18:18:36 +03:00
buffer.WriteString(tag)
buffer.WriteRune('(')
buffer.WriteString(value)
buffer.WriteString("%)")
2021-09-07 17:36:50 +03:00
}
}
if value, ok := angleProperty(filter, HueRotate, session); ok {
if buffer.Len() > 0 {
buffer.WriteRune(' ')
}
buffer.WriteString(HueRotate)
buffer.WriteRune('(')
buffer.WriteString(value.cssString())
buffer.WriteRune(')')
}
var lead string
if buffer.Len() > 0 {
lead = " drop-shadow("
} else {
lead = "drop-shadow("
}
for _, shadow := range getShadows(filter, DropShadow) {
if shadow.cssTextStyle(buffer, session, lead) {
buffer.WriteRune(')')
lead = " drop-shadow("
}
}
return buffer.String()
}
2022-07-26 18:36:00 +03:00
func (style *viewStyle) setFilter(tag string, value any) bool {
2021-09-07 17:36:50 +03:00
switch value := value.(type) {
case ViewFilter:
style.properties[tag] = value
2021-09-07 17:36:50 +03:00
return true
case string:
if obj := NewDataObject(value); obj == nil {
if filter := newViewFilter(obj); filter != nil {
style.properties[tag] = filter
2021-09-07 17:36:50 +03:00
return true
}
}
case DataObject:
if filter := newViewFilter(value); filter != nil {
style.properties[tag] = filter
2021-09-07 17:36:50 +03:00
return true
}
case DataValue:
if value.IsObject() {
if filter := newViewFilter(value.Object()); filter != nil {
style.properties[tag] = filter
2021-09-07 17:36:50 +03:00
return true
}
}
}
notCompatibleType(tag, value)
2021-09-07 17:36:50 +03:00
return false
}
// GetFilter returns a View graphical effects like blur or color shift.
// If the second argument (subviewID) is not specified or it is "" then a top position of the first argument (view) is returned
func GetFilter(view View, subviewID ...string) ViewFilter {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
2021-09-07 17:36:50 +03:00
}
2021-09-07 17:36:50 +03:00
if view != nil {
if value := view.getRaw(Filter); value != nil {
if filter, ok := value.(ViewFilter); ok {
return filter
}
}
if value := valueFromStyle(view, Filter); value != nil {
if filter, ok := value.(ViewFilter); ok {
return filter
}
}
2021-09-07 17:36:50 +03:00
}
return nil
}
// GetBackdropFilter returns the area behind a View graphical effects like blur or color shift.
// If the second argument (subviewID) is not specified or it is "" then a top position of the first argument (view) is returned
func GetBackdropFilter(view View, subviewID ...string) ViewFilter {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
}
if view != nil {
if value := view.getRaw(BackdropFilter); value != nil {
if filter, ok := value.(ViewFilter); ok {
return filter
}
}
if value := valueFromStyle(view, BackdropFilter); value != nil {
if filter, ok := value.(ViewFilter); ok {
return filter
}
}
}
return nil
}