Changed EffectWidget to use effect classes

This commit is contained in:
Alexander Taylor 2014-03-19 00:07:00 +00:00
parent 3a064e6922
commit db56261a46
2 changed files with 232 additions and 145 deletions

View File

@ -15,16 +15,34 @@ from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.uix.effectwidget import (effect_monochrome,
effect_red,
effect_blue,
effect_green,
effect_invert,
effect_mix,
effect_flash,
effect_blur_h,
effect_blur_v,
effect_plasma)
# from kivy.uix.effectwidget import (effect_monochrome,
# effect_red,
# effect_blue,
# effect_green,
# effect_invert,
# effect_mix,
# effect_blur_h,
# effect_blur_v,
# effect_postprocessing,
# effect_pixelate,
# effect_waterpaint,
# effect_fxaa,
# effect_plasma)
from kivy.uix.effectwidget import (MonochromeEffect,
InvertEffect,
ScanlinesEffect,
ChannelMixEffect,
ScanlinesEffect,
FXAAEffect,
PixelateEffect,
HorizontalBlurEffect,
VerticalBlurEffect)
class ComparisonWidget(EffectWidget):
pass
class EffectSpinner(Spinner):
pass
@ -33,30 +51,28 @@ class SpinnerRow(BoxLayout):
effectwidget = ObjectProperty()
def update_effectwidget(self, *args):
effects = []
for child in self.children:
for child in self.children[::-1]:
text = child.text
if text == 'none':
pass
if text == 'fxaa':
effects.append(FXAAEffect())
if text == 'monochrome':
effects.append(effect_monochrome)
if text == 'red':
effects.append(effect_red)
if text == 'blue':
effects.append(effect_blue)
if text == 'green':
effects.append(effect_green)
effects.append(MonochromeEffect())
if text == 'invert':
effects.append(effect_invert)
effects.append(InvertEffect())
if text == 'mix':
effects.append(effect_mix)
effects.append(ChannelMixEffect())
if text == 'flash':
effects.append(effect_flash)
effects.append(FlashEffect())
if text == 'blur_h':
effects.append(effect_blur_h)
effects.append(HorizontalBlurEffect())
if text == 'blur_v':
effects.append(effect_blur_v)
if text == 'plasma':
effects.append(effect_plasma)
effects.append(VerticalBlurEffect())
if text == 'postprocessing':
effects.append(ScanlinesEffect())
if text == 'pixelate':
effects.append(PixelateEffect())
if self.effectwidget:
self.effectwidget.effects = effects
@ -65,103 +81,62 @@ example = Builder.load_string('''
BoxLayout:
orientation: 'vertical'
BoxLayout:
EffectWidget:
ComparisonWidget:
id: effect1
Widget:
canvas:
Color:
rgba: 1, 0, 0, 1
Ellipse:
pos: Vector(self.pos) + 0.5*Vector(self.size)
size: 0.4*Vector(self.size)
Color:
rgba: 0, 1, 0.3, 1
Ellipse:
pos: Vector(self.pos) + 0.1*Vector(self.size)
size: 0.6*Vector(self.size)
Color:
rgba: 0.5, 0.5, 0.2, 1
Ellipse:
pos: Vector(self.pos) + Vector([0, 0.6])*Vector(self.size)
size: 0.4*Vector(self.size)
Color:
rgba: 1, 0.8, 0.1, 1
Ellipse:
pos: Vector(self.pos) + Vector([0.5, 0])*Vector(self.size)
size: 0.4*Vector(self.size)
Color:
rgba: 0, 0, 0.8, 1
Line:
points:
[self.x, self.y,
self.x + self.width, self.y + 0.3*self.height,
self.x + 0.2*self.width, self.y + 0.1*self.height,
self.x + 0.85*self.width, self.y + 0.72*self.height,
self.x + 0.31*self.width, self.y + 0.6*self.height,
self.x, self.top]
width: 1
Color:
rgba: 0, 0.9, 0.1, 1
Line:
points:
[self.x + self.width, self.y + self.height,
self.x + 0.35*self.width, self.y + 0.6*self.height,
self.x + 0.7*self.width, self.y + 0.15*self.height,
self.x + 0.2*self.width, self.y + 0.22*self.height,
self.x + 0.3*self.width, self.y + 0.92*self.height]
width: 2
EffectWidget:
ComparisonWidget:
id: effect2
Widget:
canvas:
Color:
rgba: 1, 0, 0, 1
Ellipse:
pos: Vector(self.pos) + 0.5*Vector(self.size)
size: 0.4*Vector(self.size)
Color:
rgba: 0, 1, 0.3, 1
Ellipse:
pos: Vector(self.pos) + 0.1*Vector(self.size)
size: 0.6*Vector(self.size)
Color:
rgba: 0.5, 0.5, 0.2, 1
Ellipse:
pos: Vector(self.pos) + Vector([0, 0.6])*Vector(self.size)
size: 0.4*Vector(self.size)
Color:
rgba: 1, 0.8, 0.1, 1
Ellipse:
pos: Vector(self.pos) + Vector([0.5, 0])*Vector(self.size)
size: 0.4*Vector(self.size)
Color:
rgba: 0, 0, 0.8, 1
Line:
points:
[self.x, self.y,
self.x + self.width, self.y + 0.3*self.height,
self.x + 0.2*self.width, self.y + 0.1*self.height,
self.x + 0.85*self.width, self.y + 0.72*self.height,
self.x + 0.31*self.width, self.y + 0.6*self.height,
self.x, self.top]
width: 1
Color:
rgba: 0, 0.9, 0.1, 1
Line:
points:
[self.x + self.width, self.y + self.height,
self.x + 0.35*self.width, self.y + 0.6*self.height,
self.x + 0.7*self.width, self.y + 0.15*self.height,
self.x + 0.2*self.width, self.y + 0.22*self.height,
self.x + 0.3*self.width, self.y + 0.92*self.height]
width: 2
SpinnerRow:
effectwidget: effect1
text: 'bg effects'
text: 'left effects'
SpinnerRow:
effectwidget: effect2
text: 'scatter effects'
text: 'right effects'
<ComparisonWidget>:
Widget:
canvas:
Color:
rgba: 1, 0, 0, 1
Ellipse:
pos: Vector(self.pos) + 0.5*Vector(self.size)
size: 0.4*Vector(self.size)
Color:
rgba: 0, 1, 0.3, 1
Ellipse:
pos: Vector(self.pos) + 0.1*Vector(self.size)
size: 0.6*Vector(self.size)
Color:
rgba: 0.5, 0.3, 0.8, 1
Ellipse:
pos: Vector(self.pos) + Vector([0, 0.6])*Vector(self.size)
size: 0.4*Vector(self.size)
Color:
rgba: 1, 0.8, 0.1, 1
Ellipse:
pos: Vector(self.pos) + Vector([0.5, 0])*Vector(self.size)
size: 0.4*Vector(self.size)
Color:
rgba: 0, 0, 0.8, 1
Line:
points:
[self.x, self.y,
self.x + self.width, self.y + 0.3*self.height,
self.x + 0.2*self.width, self.y + 0.1*self.height,
self.x + 0.85*self.width, self.y + 0.72*self.height,
self.x + 0.31*self.width, self.y + 0.6*self.height,
self.x, self.top]
width: 1
Color:
rgba: 0, 0.9, 0.1, 1
Line:
points:
[self.x + self.width, self.y + self.height,
self.x + 0.35*self.width, self.y + 0.6*self.height,
self.x + 0.7*self.width, self.y + 0.15*self.height,
self.x + 0.2*self.width, self.y + 0.22*self.height,
self.x + 0.3*self.width, self.y + 0.92*self.height]
width: 2
<SpinnerRow>:
orientation: 'horizontal'
@ -180,17 +155,17 @@ BoxLayout:
<EffectSpinner>:
text: 'none'
values:
['none', 'monochrome',
'invert', 'plasma', 'mix',
'flash', 'blur_h', 'blur_v']
['none', 'fxaa', 'monochrome',
'invert', 'mix',
'blur_h', 'blur_v',
'postprocessing', 'pixelate',]
''')
class EffectApp(App):
def build(self):
return example
EffectApp().run()

View File

@ -38,10 +38,12 @@ the widget.
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty, ObjectProperty, ListProperty
from kivy.properties import (StringProperty, ObjectProperty, ListProperty,
NumericProperty)
from kivy.graphics import (RenderContext, Fbo, Color, Rectangle,
Translate, PushMatrix, PopMatrix,
ClearColor, ClearBuffers)
from kivy.event import EventDispatcher
from kivy.base import EventLoop
Builder.load_string('''
@ -133,22 +135,15 @@ vec4 effect(vec4 color, sampler2D texture, vec2 tex_coords, vec2 coords)
effect_mix = '''
vec4 effect(vec4 color, sampler2D texture, vec2 tex_coords, vec2 coords)
{
return vec4(color.z, color.x, color.y, 1.0);
}
'''
effect_flash = '''
vec4 effect(vec4 color, sampler2D texture, vec2 tex_coords, vec2 coords)
{
return color * abs(sin(time));
}
{{
return vec4(color.{}, color.{}, color.{}, 1.0);
}}
'''
effect_blur_h = '''
vec4 effect(vec4 color, sampler2D texture, vec2 tex_coords, vec2 coords)
{
float dt = 0.5 * 1.0 / resolution.x;
{{
float dt = ({} / 4.0) * 1.0 / resolution.x;
vec4 sum = vec4(0.0);
sum += texture2D(texture, vec2(tex_coords.x - 4.0*dt, tex_coords.y)) * 0.05;
sum += texture2D(texture, vec2(tex_coords.x - 3.0*dt, tex_coords.y)) * 0.09;
@ -160,13 +155,13 @@ vec4 effect(vec4 color, sampler2D texture, vec2 tex_coords, vec2 coords)
sum += texture2D(texture, vec2(tex_coords.x + 3.0*dt, tex_coords.y)) * 0.09;
sum += texture2D(texture, vec2(tex_coords.x + 4.0*dt, tex_coords.y)) * 0.05;
return sum;
}
}}
'''
effect_blur_v = '''
vec4 effect(vec4 color, sampler2D texture, vec2 tex_coords, vec2 coords)
{
float dt = 0.5 * 1.0 / resolution.y;
{{
float dt = ({} / 4.0) * 1.0 / resolution.x;
vec4 sum = vec4(0.0);
sum += texture2D(texture, vec2(tex_coords.x, tex_coords.y - 4.0*dt)) * 0.05;
sum += texture2D(texture, vec2(tex_coords.x, tex_coords.y - 3.0*dt)) * 0.09;
@ -178,7 +173,7 @@ vec4 effect(vec4 color, sampler2D texture, vec2 tex_coords, vec2 coords)
sum += texture2D(texture, vec2(tex_coords.x, tex_coords.y + 3.0*dt)) * 0.09;
sum += texture2D(texture, vec2(tex_coords.x, tex_coords.y + 4.0*dt)) * 0.05;
return sum;
}
}}
'''
effect_postprocessing = '''
@ -230,13 +225,13 @@ vec4 effect(vec4 color, sampler2D texture, vec2 tex_coords, vec2 coords)
effect_pixelate = '''
vec4 effect(vec4 vcolor, sampler2D texture, vec2 texcoord, vec2 pixel_coords)
{
vec2 pixelSize = 10.0 / resolution;
{{
vec2 pixelSize = {} / resolution;
vec2 xy = floor(texcoord/pixelSize)*pixelSize + pixelSize/2.0;
return texture2D(texture, xy);
}
}}
'''
effect_waterpaint = '''
@ -358,6 +353,125 @@ vec4 effect( vec4 color, sampler2D buf0, vec2 texCoords, vec2 coords)
'''
class EffectBase(EventDispatcher):
'''The base class for GLSL effects. It simply returns its input.
See module documentation for more details.
'''
glsl = StringProperty(effect_trivial)
class EffectFromFile(EffectBase):
source = StringProperty('')
def on_source(self, instance, value):
if not value:
return
filename = resource_find(self.source)
if filename is None:
return Logger.error('Error reading file {filename}'.
format(filename=self.source))
with open(filename) as fileh:
self.glsl = fileh.read()
class MonochromeEffect(EffectBase):
'''Returns its input colours in monochrome.'''
def __init__(self, *args, **kwargs):
super(MonochromeEffect, self).__init__(*args, **kwargs)
self.glsl = effect_monochrome
class InvertEffect(EffectBase):
'''Inverts the colours in the input.'''
def __init__(self, *args, **kwargs):
super(InvertEffect, self).__init__(*args, **kwargs)
self.glsl = effect_invert
class ScanlinesEffect(EffectBase):
'''Adds scanlines to the input.'''
def __init__(self, *args, **kwargs):
super(ScanlinesEffect, self).__init__(*args, **kwargs)
self.glsl = effect_postprocessing
class ChannelMixEffect(EffectBase):
'''Mixes the color channels of the input according to the order
property. Channels may be arbitrarily rearranged or repeated.'''
order = ListProperty([1, 2, 0])
'''The new sorted order of the rgb channels. Defaults to [1, 2, 0],
corresponding to (g, b, r).'''
def __init__(self, *args, **kwargs):
super(ChannelMixEffect, self).__init__(*args, **kwargs)
self.do_glsl()
def on_size(self, *args):
self.do_glsl()
def do_glsl(self):
letters = [{0: 'x', 1: 'y', 2: 'z'}[i] for i in self.order]
self.glsl = effect_mix.format(*letters)
class PixelateEffect(EffectBase):
'''Pixelates the input according to its
:attr:`~PixelateEffect.pixel_size`'''
pixel_size = NumericProperty(10)
def __init__(self, *args, **kwargs):
super(PixelateEffect, self).__init__(*args, **kwargs)
self.do_glsl()
def on_pixel_size(self, *args):
self.do_glsl()
def do_glsl(self):
self.glsl = effect_pixelate.format(float(self.pixel_size))
class HorizontalBlurEffect(EffectBase):
'''Blurs the input horizontally, with the width given by
:attr:`~HorizontalBlurEffect.size`.'''
size = NumericProperty(4.0)
def __init__(self, *args, **kwargs):
super(HorizontalBlurEffect, self).__init__(*args, **kwargs)
self.do_glsl()
def on_size(self, *args):
self.do_glsl()
def do_glsl(self):
self.glsl = effect_blur_h.format(float(self.size))
class VerticalBlurEffect(EffectBase):
'''Blurs the input vertically, with the width given by
:attr:`~VerticalBlurEffect.width`.'''
size = NumericProperty(4.0)
def __init__(self, *args, **kwargs):
super(VerticalBlurEffect, self).__init__(*args, **kwargs)
self.do_glsl()
def on_size(self, *args):
self.do_glsl()
def do_glsl(self):
self.glsl = effect_blur_h.format(float(self.size))
class FXAAEffect(EffectBase):
'''Applies very simple antialiasing via fxaa.'''
def __init__(self, *args, **kwargs):
super(FXAAEffect, self).__init__(*args, **kwargs)
self.glsl = effect_fxaa
class EffectFbo(Fbo):
def __init__(self, *args, **kwargs):
super(EffectFbo, self).__init__(*args, **kwargs)
@ -472,8 +586,9 @@ class EffectWidget(BoxLayout):
fbo = self.fbo_list[i]
fbo.texture_rectangle.texture = self.fbo_list[i - 1].texture
# Build effect shaders
for effect, fbo in zip(self.effects, self.fbo_list):
fbo.set_fs(shader_header + shader_uniforms + effect +
fbo.set_fs(shader_header + shader_uniforms + effect.glsl +
shader_footer_effect)
self.fbo_list[0].texture_rectangle.texture = self.fbo.texture
@ -509,6 +624,3 @@ class EffectWidget(BoxLayout):
super(EffectWidget, self).clear_widgets(children)
self.canvas = c
class BlurEffectWidget(EffectWidget):
effects = ListProperty([effect_blur_h, effect_blur_v])