Yehuda Katz Posted October 11, 2017 Share Posted October 11, 2017 Hello everyone, is there any special limitation for using filters? I can see from example on Phaser web site that this code should work but in my app it does not work this.game.load.image('logo', 'assets/images/preload_bg.png'); this.game.load.script('gray', 'https://cdn.rawgit.com/photonstorm/phaser/master/v2/filters/Gray.js'); var logo = this.game.add.sprite(this.game.world.centerX, this.game.world.centerY, 'logo'); logo.anchor.setTo(0.5, 0.5); var gray = this.game.add.filter('Gray'); logo.filters = [gray]; The sprite is added but it's not affected by filter at all... I checked: that Phaser.Filter.Gray exists, gray object is really filer object and logo has it within filters property. What else I can do to find issue? Thanks in advance Link to comment Share on other sites More sharing options...
3man7 Posted October 11, 2017 Share Posted October 11, 2017 I did a quick test and the code works for me. Are there any errors on console from your app? Yehuda Katz 1 Link to comment Share on other sites More sharing options...
samid737 Posted October 11, 2017 Share Posted October 11, 2017 (edited) Filters work in WEBGL mode that could be the issue. Try grey.update() below logo.filters= [gray]. Edit: looking back , the key must match: this.game.add.filter('gray'): Edited October 12, 2017 by samid737 Key Yehuda Katz 1 Link to comment Share on other sites More sharing options...
Yehuda Katz Posted October 12, 2017 Author Share Posted October 12, 2017 16 hours ago, 3man7 said: Are there any errors on console from your app? That's the most weird, no errors... I even followed @samid737 suggestion and replaced 'Gray' with 'gray' and got error message: TypeError: c.Filter[a] is not a constructor Also, do you test with JS5 or JS6? I am thinking may be those filters are not compatible with JS6 and I should rewrite them? Here is full code: export default class Achievements extends Phaser.State { preload() { this.game.load.image('logo', 'assets/images/phaser2.png'); this.game.load.script('gray', 'https://cdn.rawgit.com/photonstorm/phaser/master/v2/filters/Gray.js'); } create() { var logo = this.game.add.sprite(this.game.world.centerX, this.game.world.centerY, 'logo'); logo.anchor.setTo(0.5, 0.5); var gray = this.game.add.filter('Gray'); logo.filters = [gray]; gray.update() } update() { } } As you can see I even tried to use original phaser2.png file, to be sure that my png file is not not an issue Thanks and waiting for your suggestions Link to comment Share on other sites More sharing options...
Yehuda Katz Posted October 12, 2017 Author Share Posted October 12, 2017 I found the reason, the problem is here: game = new Phaser.Game(480, 800, Phaser.CANVAS, 'game'); The problem is that I cannot replace Phaser.CANVAS with Phaser.AUTO because it causes blinking when game is run on Android Any suggestion how to fix that or why filter is not working with CANVAS? p.s. Just in case if anyone needs Gray filter rewritten into JS6 which can be included into project: export default class FilterGray extends Phaser.Filter { constructor(game) { super(game); this.uniforms.gray = { type: '1f', value: 1.0 }; this.fragmentSrc = [ "precision mediump float;", "varying vec2 vTextureCoord;", "varying vec4 vColor;", "uniform sampler2D uSampler;", "uniform float gray;", "void main(void) {", "gl_FragColor = texture2D(uSampler, vTextureCoord);", "gl_FragColor.rgb = mix(gl_FragColor.rgb, vec3(0.2126 * gl_FragColor.r + 0.7152 * gl_FragColor.g + 0.0722 * gl_FragColor.b), gray);", "}" ]; } set gray(value) { this.uniforms.gray.value = value; } get gray() { return this.uniforms.gray.value; } } user is as simple as that: this.filter_gray = new FilterGray(this.game); icon.filters = [this.filter_gray]; Link to comment Share on other sites More sharing options...
3man7 Posted October 12, 2017 Share Posted October 12, 2017 Well, like @samid737 said, the issue is that you are using CANVAS. In the testing I've used Phaser 2.6.2 and Phaser.AUTO. I am not entirely sure if there are filters that can be used with CANVAS as they are meant to work with WEBGL only. Refer to:https://github.com/photonstorm/phaser/issues/2223http://phaser.io/docs/2.6.2/Phaser.Filter.html Quote @rich: Filters are basically Shaders, and shaders are WebGL only I'm afraid. I have updated the docs to reflect this. Quote The vast majority of filters (including all of those that ship with Phaser) use fragment shaders, and therefore only work in WebGL and are not supported by Canvas at all. Yehuda Katz 1 Link to comment Share on other sites More sharing options...
Yehuda Katz Posted October 20, 2017 Author Share Posted October 20, 2017 @3man7 I decided to give up with using filters as they drop frame rate to 15-20 from 60 on my PC (I can imagine what will happen on tablet or mobile devises). I have only 80 different sprites created from PNG file and moving them around (scrolling) glitches the screen. Does anyone have have any idea how to fix that or simply do one time dirty work in Photoshop and give up with filters? Link to comment Share on other sites More sharing options...
3man7 Posted October 20, 2017 Share Posted October 20, 2017 5 hours ago, Yehuda Katz said: @3man7 I decided to give up with using filters as they drop frame rate to 15-20 from 60 on my PC (I can imagine what will happen on tablet or mobile devises). I have only 80 different sprites created from PNG file and moving them around (scrolling) glitches the screen. Does anyone have have any idea how to fix that or simply do one time dirty work in Photoshop and give up with filters? The filter option will definitely have an impact on the performance. I would personally apply the filter to the batch of images in Photoshop using 'Action'. Here's a good tutorial: youtube.com/watch?v=lcZp2h5WXdA Don't forget to backup your original images! This method only applies if your sprites will remain grayscale the entire project. If you want to transition from color to grayscale and back on color then I would advice against this method and instead try to find a fix for the code. Sorry that I cannot help you enough with the filter code as I haven't used them before. Yehuda Katz 1 Link to comment Share on other sites More sharing options...
Yehuda Katz Posted October 20, 2017 Author Share Posted October 20, 2017 @3man7 Nope, I need only gray scale image to display locked levels on level selection menu. I thought may be Phaser can somehow do the job once, cache that gray scale image and use it with every update... instead of simply doing whole filter job all the time =) Once I will have free time, I will try write some code which will cache that gray scale image of image and share the code here (in case of success) Link to comment Share on other sites More sharing options...
3man7 Posted October 20, 2017 Share Posted October 20, 2017 17 minutes ago, Yehuda Katz said: @3man7 Nope, I need only gray scale image to display locked levels on level selection menu. I thought may be Phaser can somehow do the job once, cache that gray scale image and use it with every update... instead of simply doing whole filter job all the time =) Once I will have free time, I will try write some code which will cache that gray scale image of image and share the code here (in case of success) Ohh I get it. What I would've done instead is this: create a black box with the transparency of 60% and save it as .png. After that just duplicate the .png image with 'for loop', put them in a group and place them over the colored levels (aka unlocked). Then kill() the image specific to the levels unlocked. It won't be grayscale but it's a similar effect. In the end it's up to you Link to comment Share on other sites More sharing options...
samme Posted October 20, 2017 Share Posted October 20, 2017 If you're using the Canvas renderer, the LUMINOSITY blendMode (against black) is similar to grayscale. Link to comment Share on other sites More sharing options...
Yehuda Katz Posted October 21, 2017 Author Share Posted October 21, 2017 (edited) @3man7 thanks, I used similar trick for gradient transparency to hide part of scrolling level map at the top and bottom. I simply created PNG file with gradient transparency and put it over =) I will probably do my own class but with next update =) @samme what if I will simply do scan line of sprite's context and cache result, will not it be much faster? BTW you asked me to share solution for centering the text, here it is (I just finished and it works awesome): _text_true_size(object) { var cache_key = object.text + JSON.stringify(object.style); if ('true_size_cache' in object && object.true_size_cache[cache_key]) return object.true_size_cache[cache_key]; var true_size_data = {left: -Infinity, top: -Infinity, right: Infinity, bottom: Infinity, width: Infinity, height: Infinity}; var width = object.canvas.width, height = object.canvas.height; var image_data = object.context.getImageData(0, 0, width, height).data; var line_index = 0, canvas_line_width = width * 4, i, j, stop; var pixel_color = 0, background_color = image_data[0] + image_data[1] + image_data[2] + image_data[3]; stop = false; for (i=0; i<height; i++) { for (j=0; j<canvas_line_width; j+=4) { pixel_color = image_data[line_index + j] + image_data[line_index + j + 1] + image_data[line_index + j + 2] + image_data[line_index + j + 3]; if (pixel_color != background_color) { true_size_data.top = i; stop = true; break; } } if (stop) break; line_index += canvas_line_width; } line_index = image_data.length - canvas_line_width; stop = false; for (i=height; i>0; i--) { for (j=0; j<canvas_line_width; j+=4) { pixel_color = image_data[line_index + j] + image_data[line_index + j + 1] + image_data[line_index + j + 2] + image_data[line_index + j + 3]; if (pixel_color != background_color) { true_size_data.bottom = height - i; stop = true; break; } } if (stop) break; line_index -= canvas_line_width; } stop = false; for (i=0; i<canvas_line_width; i+=4) { line_index = 0; for (j=0; j<height; j++) { pixel_color = image_data[line_index + i] + image_data[line_index + i + 1] + image_data[line_index + i + 2] + image_data[line_index + i + 3]; if (pixel_color != background_color) { true_size_data.left = Math.floor(i / 4); stop = true; break; } line_index += canvas_line_width; } if (stop) break; } stop = false; for (i=canvas_line_width; i>3; i-=4) { line_index = 0; for (j=0; j<height; j++) { pixel_color = image_data[line_index + i] + image_data[line_index + i + 1] + image_data[line_index + i + 2] + image_data[line_index + i + 3]; if (pixel_color != background_color) { true_size_data.right = width - Math.floor(i / 4); stop = true; break; } line_index += canvas_line_width; } if (stop) break; } true_size_data.width = width - true_size_data.left - true_size_data.right; true_size_data.height = height - true_size_data.top - true_size_data.bottom; object.true_size_cache = {}; object.true_size_cache[cache_key] = true_size_data; return object.true_size_cache[cache_key]; } It assumes that top left pixel is the background color. Actually in 99% it's transparent but I decided to support rare cases too. It also uses compares sum of color (4 bytes) to speed up the search. The result is cached so next call should be instant. Edited October 22, 2017 by Yehuda Katz there was a bug in calculating right and bottom margin values; cache key should be bases on text + style value Link to comment Share on other sites More sharing options...
Yehuda Katz Posted October 21, 2017 Author Share Posted October 21, 2017 Oh and here is the sample usage: var true_size = this._text_true_size(object); var offset_x = (object.width / 2) - (true_size.left + (true_size.width / 2)); var offset_y = (object.height / 2) - (true_size.top + (true_size.height / 2)); var rect = new Phaser.Rectangle(object.x, object.y, object.bounds.width, object.bounds.height); object.alignIn(rect, align, offset_x, offset_y); Currently I am working on a code to reduce font size automatically so it could fit the bounds and in case it cannot fit then it should auto. short. words =) p.s. The only downside of such method is that you should call alignIn method every time you change the text. I tried to overwrite 'text' setter but without any success. I am not sure how to do that correctly in JS6 (with methods it's simply super.method_name()) Link to comment Share on other sites More sharing options...
samid737 Posted October 22, 2017 Share Posted October 22, 2017 You can also grayscale sprites manually using processPixelRGB: Yehuda Katz 1 Link to comment Share on other sites More sharing options...
Yehuda Katz Posted October 22, 2017 Author Share Posted October 22, 2017 @samid737 what about processPixelRGB method, is it significantly slower than scan line? As far as I now, JS works faster with one dimensional arrays... when I got inside of processPixelRGB method, I can see that it does many calculations and conversions. However, if that's just 10-50msec then I would prefer cleaner (readable) code with processPixelRGB over faster getImageData Link to comment Share on other sites More sharing options...
samme Posted October 22, 2017 Share Posted October 22, 2017 20 hours ago, Yehuda Katz said: what if I will simply do scan line of sprite's context and cache result, will not it be much faster? CANVAS blendMode is just globalCompositeOperation so it really might be faster. But wouldn't it definitely be easier? Yehuda Katz 1 Link to comment Share on other sites More sharing options...
samid737 Posted October 22, 2017 Share Posted October 22, 2017 ProcessPixelRGB does some work by calling internal functions, so it might be slower than using getImageData. If you run the code once I don't think execution time will matter too much. Im also not familiar with scan line, is it part of the canvas API? Yehuda Katz 1 Link to comment Share on other sites More sharing options...
Yehuda Katz Posted October 22, 2017 Author Share Posted October 22, 2017 @samme I am not sure if it will work for my particular case (my question was simplified) but I like clipping path, which may come up handy for weird clip masks, I should bookmark that page, thanks! @samid737 The getImageData() method seems to be native HTML5 method as it works with canvas context (the text actually has both canvas and context public properties). Probably it would be nice to have small wrapper function to quickly access line and get each pixel as array/object (in win32 development, working with canvas is hundreds slower than bitmap and bitmaps are another hundreds slower than scan line, which is something similar what getImageData() method returns). I just finished auto_fit_bounds method and should have time to give a shot for a custom gray scale converter (I will update topic if I have success) Link to comment Share on other sites More sharing options...
Yehuda Katz Posted November 5, 2017 Author Share Posted November 5, 2017 @samme it took me some time to finish all small tasks from todo and today I tested globalCompositeOperation. It works just awesome (and very fast too): var icon_bmd_source = new Phaser.BitmapData(this.game, '', 120, 120); icon_bmd_source.draw(new Phaser.Sprite(this.game, 0, 0, 'default', 'level_icon_1')); var icon_bmd_overlay = new Phaser.BitmapData(this.game, '', 120, 120); icon_bmd_overlay.draw(new Phaser.Sprite(this.game, 0, 0, 'default', 'level_disable_overlay')); var icon_bmd_result = new Phaser.BitmapData(this.game, '', 120, 120); icon_bmd_result.context.drawImage(icon_bmd_source.canvas, 0, 0); icon_bmd_result.context.globalCompositeOperation = 'source-in'; icon_bmd_result.context.drawImage(icon_bmd_overlay.canvas, 0, 0); var sprite = new Phaser.Sprite(this.game, 50, 100, icon_bmd_result); this.game.add.existing(sprite); The whole idea is that I created filled rectangle (if needed it can be partially transparent) from which I use only those pixels, which are not transparent in source =) However, I have no clue why I cannot reach canvas or context of Sprite... while I was able to do that with Phaser.Text object. Link to comment Share on other sites More sharing options...
Yehuda Katz Posted November 6, 2017 Author Share Posted November 6, 2017 So here is the final version which combines any two sprite and returns BitmapData which later can be used to update existing sprite via: my_sprite.loadTexture(this._combine_sprite(icon_sprite, overlay_sprite, 'source-in')); _combine_sprite(first_sprite, second_sprite, combine_mode) { var first_bmd = new Phaser.BitmapData(this.game, '', first_sprite.width, first_sprite.height); first_bmd.draw(first_sprite); var second_bmd = new Phaser.BitmapData(this.game, '', second_sprite.width, second_sprite.height); second_bmd.draw(second_sprite); var result_bmd = new Phaser.BitmapData(this.game, '', first_sprite.width, first_sprite.height); result_bmd.context.drawImage(first_bmd.canvas, 0, 0); result_bmd.context.globalCompositeOperation = combine_mode; result_bmd.context.drawImage(second_bmd.canvas, 0, 0); return result_bmd; } p.s. I decided to pass overlay sprite because there will be many icons and there is no reason to create overlay sprite every time I need to update icon. Instead I created one overlay sprite and use it many times for all icons I have Link to comment Share on other sites More sharing options...
Recommended Posts