kenray Posted May 7, 2014 Share Posted May 7, 2014 I very much appreciate the new BitmapData.setHSL method in 2.0.4, and it seems great for *saturating* a bitmap by increasing the value above 0 towards 1... but is there a way do *desaturate* a bitmap so that it goes grayscale? Adobe Fireworks (for example) has a range of -100 to 100, where -100 saturation is fully desaturated grayscale. I was hoping that setHSL would support a -1 to 1 range (instead of its current 0 to 1 range), and in fact when I tried some negative values it led to odd results <grin>. What I'm trying to do is to turn a bitmap from its normal saturation to a grayscale version *over time*. Although I can use something like the new processPixelRGB method to iterate over each pixel with a callback, that gets really slow. So I was hoping to be able to do a dozen or so timed executions of adjusting the setHSL's saturation value to fully desaturate a bitmap over a second or two. So I'd like to vote for either a -1 to 1 range for H, S and L or in the meantime, is there some way to efficiently grayscale a bitmap over time? (oh, and it needs to work in both WebGL and Canvas... ) Thanks! Link to comment Share on other sites More sharing options...
rich Posted May 8, 2014 Share Posted May 8, 2014 To desaturate just do this:bmd.shiftHSL(null, -1.0, null); Link to comment Share on other sites More sharing options...
kenray Posted May 8, 2014 Author Share Posted May 8, 2014 To desaturate just do this:bmd.shiftHSL(null, -1.0, null); Perfect! Thanks! Link to comment Share on other sites More sharing options...
kenray Posted May 9, 2014 Author Share Posted May 9, 2014 Actually, I discovered that while bmd.shiftHSL works great to switch to grayscale immediately, the problem with desaturating over time is that when bmd.shiftHSL is run, it permanently alters the original bitmap data. What I was hoping to do was to (for example) loop 10 times through from 0 to -1, stepping by -0.1 to gradually desaturate it (so the saturation level would go 90%, 80%, 70%, etc.), but since it permanently alters the bitmap data, each loop accelerates the desaturation (90%, 72%, 50%, 30%, etc.), and of course once its fully desaturated, it can't re-saturate over time. So what I'm trying to accomplish is a sprite that's clicked desaturates to grayscale in like 2000ms, and when clicked again, it re-saturates back to its original level in 2000ms. Any ideas on how to accomplish this? Link to comment Share on other sites More sharing options...
rich Posted May 9, 2014 Share Posted May 9, 2014 In your loop: 1) BitmapData.draw (or drawSprite maybe?) to paste the original image in2) BitmapData.update()3) BitmapData.shiftHSL(value)4) inc value5) loop Link to comment Share on other sites More sharing options...
kenray Posted May 9, 2014 Author Share Posted May 9, 2014 I just tried that and it looks as though it is stacking multiple copies of the image on top of itself each way through the loop - I used an example of a large version of the Adobe PDF document icon (because it has antialiasing around the edges), and here's what I get: Original: After running loop:However seeing that, I put in a bmd.cls() before the bmd.draw() and it worked! So for others who might be interested, here's the code that worked to desaturate and resaturate a sprite over time (2000ms each way):// Requires Phaser 2.0.4+// Assumes you've preloaded a 100x200 image with the key 'MyImage' in the cache// and created the sprite called 'MySprite' at location 500,500; to make it easier// I added a property to MySprite that held a reference to the bitmapData object, // another to hold the current saturation value, and a third to hold the name of // the sprite itself for later use// Preload phase:game.load.image('MyImage','path/to/myimage.png');// Create phase:var bmd = game.add.bitmapData(100, 200);var Rect = new Phaser.Rectangle(0, 0, 100, 200);bmd.copyPixels('MyImage', Rect, 0, 0);bmd.update();MySprite = game.add.sprite(500, 500, bmd);MySprite._bitmap = bmd;MySprite._name = 'MyImage';MySprite._saturation = 0;// Global functions:function Desaturate(pSprite, pDuration) { var tMSPerStep = 100; // I didn't want any step to take longer than 100ms if (pSprite._saturation > -1.0) { if (pDuration <= 100) { pSprite._bitmap.shiftHSL(null, -1.0, null); pSprite._saturation = -1.0; } else { var tNumSteps = Math.floor(pDuration/tMSPerStep) + 1; var tCurrStep = 1; var tMethod = "desaturate"; ChangeSaturation(pSprite, tMethod, tCurrStep, tNumSteps, tMSPerStep); } }}function Resaturate(pSprite, pDuration) { var tMSPerStep = 100; // I didn't want any step to take longer than 100ms if (pSprite._saturation <= -1.0) { if (pDuration <= 100) { pSprite._bitmap.copyPixels(pSprite.draw(pSprite._name, 0, 0); pSprite._saturation = 0; } else { var tNumSteps = Math.floor(pDuration/tMSPerStep) + 1; var tCurrStep = 1; var tMethod = "resaturate"; ChangeSaturation(pSprite, tMethod, tCurrStep, tNumSteps, tMSPerStep); } }}function ChangeSaturation(pSprite, pMethod, pCurrStep, pNumSteps, pMSPerStep) { var tSaturation; var tBaseSaturation = (pCurrStep / pNumSteps); if (pCurrStep <= pNumSteps) { if (pMethod == "desaturate") { tSaturation = -1 * tBaseSaturation; } else { tSaturation = -1 * (1 - tBaseSaturation); } pSprite._bitmap.cls(); pSprite._bitmap.draw(pSprite._name, 0, 0); pSprite._bitmap.update(); pSprite._bitmap.shiftHSL(null, tSaturation, null); pSprite._saturation = tSaturation; pCurrStep += 1; game.time.events.add(pMSPerStep, ChangeSaturation, this, pSprite, pMethod, pCurrStep, pNumSteps, pMSPerStep); }}// Use:Desaturate(MySprite, 2000);Resaturate(MySprite, 2000);Thanks for all your help, Rich! Link to comment Share on other sites More sharing options...
rich Posted May 9, 2014 Share Posted May 9, 2014 Oh yeah.. cls too Link to comment Share on other sites More sharing options...
kenray Posted May 9, 2014 Author Share Posted May 9, 2014 Rich, although this is working beautifully in Chrome, it fails in Safari... are the new bitmap routines WebGL-specific? If so, do you have a suggestion for doing this in Safari via Canvas? Here's my simple test page: http://www.sonsothunder.com/test/grayscale/grayscaleTest.html (all the JS is in the page) Link to comment Share on other sites More sharing options...
rich Posted May 9, 2014 Share Posted May 9, 2014 Nothing BitmapData does uses WebGL. Link to comment Share on other sites More sharing options...
jpdev Posted May 9, 2014 Share Posted May 9, 2014 Just for fun, have a look here: http://browsershots.org/http://www.sonsothunder.com/test/grayscale/grayscaleTest.html (Don't know how long this is valid - it's a page that opens up a url on many different systems and creates screenshots for you.Many system get the grayscale right, some put out color image, some choke on the js ) Link to comment Share on other sites More sharing options...
kenray Posted May 9, 2014 Author Share Posted May 9, 2014 I *thought* it didn't use WebGL (which is why I liked it in the first place!). Do you have any idea why Safari's not rendering the image at http://www.sonsothunder.com/test/grayscale/grayscaleTest.html in grayscale? If you go to that page in Chrome, it's grayscale, but in Safari it's not. Here's the complete JS for the page:var Phaser,game;var FillBMD;var tWidth = 209;var tHeight = 255;var tName = 'arnold';game = new Phaser.Game(800, 600, Phaser.CANVAS, '');var MainState = function(game){ this.game = game;};MainState.prototype = { preload: function() { game.load.image('arnold', 'assets/arnold-rest.png'); }, create: function() { FillBMD = game.add.bitmapData(tWidth, tHeight); var Rect = new Phaser.Rectangle(0, 0, tWidth, tHeight); FillBMD.copyPixels(tName, Rect, 0, 0); FillBMD.update(); FillBMD.shiftHSL(null, -1.0, null); var tFillSprite = game.add.sprite(200, 200, FillBMD); }, update: function() { }, render: function() { }}game.state.add('mainstate', MainState);game.state.start('mainstate');BTW, I tried Phaser.AUTO too and got the same thing... Link to comment Share on other sites More sharing options...
kenray Posted May 9, 2014 Author Share Posted May 9, 2014 Thanks to the browser shots (thanks, jpdev!) and a bit of research, it looks like it's WebKit-related. Luakit, Rekonq, and Safari (at least) all render my test in color, but Chrome, Firefox, etc. render it grayscale... So now that it looks like the why is answered, the remaining question for you, Rich, is: is there anything that can be done about it? Or is it something that is just "the way it is"? Link to comment Share on other sites More sharing options...
Recommended Posts