Consider this demo from David Nuon:
http://zunostudios.com/demos/css32014-demos/filters.html
As David notices in his post:
You'll notice that the more the sliders are to the right, the less responsive the page becomes.
And that is true. After I altered the image, I saw how my CPU began to work a lot.
What I could not answer to myself is WHY after all the css modifications the page is so unresponsive. Like all the animations are 2fps.
If the job is done why it keeps working?
Edit: With the help of xengravity I could see that maybe after all the filters are to the right it seems that the GPU does the following at an insane rate:
1.- takes the original image.
2.- modifies it (math calculations, blabla, etc).
But always starting from the original image. Maybe thats why it seems all so slow....
Edit: Added demo into a snippet for future posterity
var update_filter = function () {
var styles = [
'grayscale( ' + parseInt($('#grayscale').val()) * .01 + ')',
'blur( ' + $('#blur').val() + 'px)',
'sepia( ' + $('#sepia').val() + '%)',
'brightness( ' + parseInt($('#brightness').val()) * .01 + ')',
'contrast( ' + $('#contrast').val() * .1 + ')',
'hue-rotate( ' + $('#hue-rotate').val() * 3.6 + 'deg)',
'invert( ' + $('#invert').val() + '%)',
'saturate( ' + parseInt($('#saturate').val()) * .1 + ')',
'opacity( ' + parseInt($('#opacity').val()) * .01 + ')',
'drop-shadow( ' + (function (n) {
return '0px ' +
'0px ' +
n + 'px ' +
'black)'; }
)($('#drop-shadow').val()) ,
];
var styles = '-webkit-filter:\n' + styles.map(function (e) { return '\t' + e;} ).join('\n') + ';';
$('#image').attr('style', styles);
$('#code').val(styles);
};
$('#reset').click( function () {
$('#grayscale').val( $('#grayscale').data('default') );
$('#blur').val( $('#blur').data('default') );
$('#sepia').val( $('#sepia').data('default') );
$('#brightness').val( $('#brightness').data('default') );
$('#contrast').val( $('#contrast').data('default') );
$('#hue-rotate').val( $('#hue-rotate').data('default') );
$('#invert').val( $('#invert').data('default') );
$('#saturate').val( $('#saturate').data('default') );
$('#opacity').val( $('#opacity').data('default') );
$('#drop-shadow').val( $('#drop-shadow').data('default') );
update_filter();
});
$( 'input[type="range"]').change(update_filter );
update_filter();
body, html {
background: #fff;
}
.wrapper {
width: 800px;
height: 400px;
background: #fff;
border-radius: 5px;
position: relative;
margin: 60px auto 0 auto;
}
.controls {
background: #ddd;
width: 250px;
position: absolute;
right: 0;
bottom: 0;
top: 0;
}
.image {
background: url(transparency.png);
width: 550px;
position: absolute;
left: 0;
bottom: 0;
top: 0;
}
.controls {
padding: 0 0 0 0;
}
.controls li {
list-style: none;
margin: 0;
padding: 5px 15px;
border-bottom: 1px solid #aaa;
}
.controls li span {
font-size: 13px;
}
.controls li span::after {
content: '()';
}
.controls li input {
color: #333;
float: right;
}
#code {
position: absolute;
left:0;
right: 0;
bottom: -155px;
border:0;
font-family: monospace;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<div class="wrapper">
<div class="image">
<img id="image" src="http://i.imgur.com/WdIGZ1t.png" alt="">
</div>
<div class="controls">
<ul class="controls"><li>
<span>blur</span>
<input type="range" id="blur" min="0" max="100" value="0" data-default="0">
</li>
<li>
<span>grayscale</span>
<input type="range" id="grayscale" min="0" max="100" value="0" data-default="0">
</li>
<li>
<span>drop-shadow</span>
<input type="range" id="drop-shadow" min="0" max="100" value="0" data-default="0">
</li>
<li>
<span>sepia</span>
<input type="range" id="sepia" min="0" max="100" value="0" data-default="0">
</li>
<li>
<span>brightness</span>
<input type="range" id="brightness" min="0" max="100" value="100" data-default="100">
</li>
<li>
<span>contrast</span>
<input type="range" id="contrast" min="0" max="100" value="10" data-default="10">
</li>
<li>
<span>hue-rotate</span>
<input type="range" id="hue-rotate" min="0" max="100" value="0" data-default="0">
</li>
<li>
<span>invert</span>
<input type="range" id="invert" min="0" max="100" value="0" data-default="0">
</li>
<li>
<span>saturate</span>
<input type="range" id="saturate" min="0" max="100" value="10" data-default="10">
</li>
<li>
<span>opacity</span>
<input type="range" id="opacity" min="0" max="100" value="100" data-default="100">
</li>
<li><button id="reset">Reset</button></li>
</div>
<textarea id="code" cols="20" rows="11"></textarea>
</div>
The filter CSS property applies graphical effects like blur or color shift to an element. Filters are commonly used to adjust the rendering of images, backgrounds, and borders. Included in the CSS standard are several functions that achieve predefined effects.
filter: drop-shadow(0 0 0.75rem crimson); A drop shadow is effectively a blurred, offset version of the input image's alpha mask, drawn in a specific color and composited below the image. Note: This function is somewhat similar to the box-shadow property.
Use the "filter" property in CSS to create blur effects on elements.
To set image brightness in CSS, use filter brightness(%). Remember, the value 0 makes the image black, 100% is for original image and default. Rest, you can set any value of your choice, but values above 100% would make the image brighter.
The main problem with this "app" is painting. And if you apply some filter effects, or even go crazy with the slider, you are actually killing CPU with computationally expensive operations which are perfectly intended for GPU. It is very good at this stuff. If you use timeline tool and trace your app you will see massive green bars which indicates painting and painting is happening on the main thread(CPU). What you need to do is to manually promote elements that use these filter effects to a layer. You can do that with some hacks like transform: translateZ(0);
or new will-change: transform;
and after that you will see massive improvement in responsiveness of your app. But on mobile devices generation 2012 and below you can forget good performance, this is just too much for them.
Try to add this code through the dev tools and observe the result, if it is not faster let me know ?
img#image {
transform: translateZ(0); /*for older browsers*/
will-change: transform;
}
You'll notice that the only time the performance is hit hard is when you either use blur or drop-shadow. If you play with any of the other filters there is very little cpu usage or any indication of a performance hit.
The first thing to understand is that not all filters are created equal.
Most filters modify pixels on a 1:1 level which is a relatively straight forward calculation.
However when you introduce anything with a "blur" or "shadow" it may no longer be a 1:1 calculation.
Say you use the radius parameter and set it to 5px. You now need to look at 5x the amount of pixels to calculate what the blur should look like.
When you compound this with other filters on top of this the calculation required increases.
Modern browsers are able to utilize the GPU to help with these calculations to make the process faster but you'll notice devices with weak GPU's suffering.
So to answer your question specifically. The CPU isn't running after the filter is applied, it's actually still in the process of calculating! When you put in crazy values like the example allows you to (e.g. a radius of 100px) you can imagine how intensive that calculation will be on the CPU.
Here is a quick guideline on performance for CSS filters:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With