Change color during draw

  • Hi, I'm making the Spritefont Deluxe addon for Construct 2 and I've been trying to make the spritefont support per letter color for quite a long time now. However I can't seem to find a way to do this.

    I tried adding a tint effect to the spritefont and changing its values during runtime. This only changes the values for the next draw and hence affects the whole Spritefont.

    I tried looking at the way the engine draws the canvas, until I finally found the code that draws the object using WebGL effects. It's in layout.js in Layer.DrawGL. There I had the code to "simulate" changing the effect used for glw rendering. However for that I'd need to push a new batch and apply a whole bunch of values that I don't have access to and that would make performance terrible If I needed to push a new batch for each letter or set of letters.

    I tried looking into how I could do this using only Javascript. Right my best bet would be to print the letter texture image on a canvas, then change every white pixel into my color, and then get the new texture image and use that to render.

    While this may work even though I didn't try yet this seems like a very inneficient way of doing it so I really don't know if that's the best way to do this.

    I'm kind of in a corner right now so if anyone has a better idea, I'd be really glad to take it.

  • I'd say for simplicity don't mess with the internals of C2's renderer. It's designed to draw quad and point batches as well as using effects in a way that's best left handled by the editor.

    So If you want to draw in webgl you should be able to call glw.endBatches() or whatever it's called and then use any webgl you want. Just save all states you change and restore them when you're done. The creature2d plugin does this and performance seems to be decent.

    My guess is you'd replace the shader used by default with one that takes a color per quad as a parameter too, so it would multiply the texture with the per quad color to get multiple colors. I'd say first try it out in straight webgl outside of construct to get the concept down. Then you can do it in construct and see how much of the c2 renderer's webgl state you can reuse.

    For rendering it with a html5 canvas you could probably do it by using two canvas'. One you'd draw the text in white to, and the other you'd draw colored rectangles for each letter. Then as a final step you'd use a blend mode to combine the two to make colored text.

    If you're concerned about performance don't use a html5 canvas method when you plan on drawing with webgl. The main issue is the canvas will need to be copied to a webgl texture every frame it changes. This is why C2's text object is slower, as well as my own canvas plugin.

  • Making a text type object using points or quads sounds interesting.

    Painfully complicated, but interesting nonetheless.

  • We are still talking about spritefonts though, which is a quad per letter. Here the question was ways to do a different color per letter. I seem to recall c3 solved this with their updated spritefont object. This is mainly because c3's renderer adds a per quad color multiplier.

    You're probably thinking of geometry based. It's relatively simple to draw a bunch of quads. Although it's probably tedious to design or convert existing fonts over. You'd still want to use some custom webgl to do multiple quads faster and color them.

    Anyways here is a nice list of all the methods of doing text in webgl:

    stackoverflow.com/questions/25956272/better-quality-text-in-webgl

  • A pixel font might work. I'd hate to try to figure out antialiasing for anything else.

  • Hi, thanks R0J0hound for your super helfpul answer.

    Sadly I had already found a way to do this.

    Indeed what I do right now, is I draw the letter on a black canvas the size of the letter, multiply by my color, and draw the letter again in 'destination-in' mode to mask the canvas's content.

    Then to save performance, I cache the texture in an object and if I ever need to redraw that letter with that color, I don't need to redo the process. This is helpful given that in a game the colored parts of the text are often the same words.

    But indeed this is problematic on WebGL because changing an image to a webGL texture had some issues. So I decided to cache the texture as well. So I draw the image, convert it to a texture only once.

    However, this still has some overhead compared to the canvas reder mode.

    I'll need to see if recoding the same process in WebGL will save more perf compared to keeping what I have right now or not.

    Also I don't think breaking C2's batch is a good idea. I have little to no experience in WebGL so I'm probably wrong, but afaik this means that the rendering gets a lot more tedious the more I have letters since I need to create a new batch and break it for each letter. Also I wasn't able to create a new proper batch using the same params C2 uses because I can't access these values, but again, maybe it's due to my inexperience with WebGL

    		function getColoredTexture(inst, image, color, clip, scale, letter) {
    		if(!color || color === 'None') {
    			return image
    		}
    		if (inst.cachedImages !== undefined && inst.cachedImages[letter] !== undefined && inst.cachedImages[letter][color] !== undefined) {
    			return inst.cachedImages[letter][color];
    		}
    		// Create new canvas
    		var charCanvas = createCanvas(clip.w * scale, clip.h * scale)
    		var charContext = charCanvas.getContext("2d");
    		// Draw letter on it
    		charContext.fillStyle = 'black';
    		charContext.fillRect(0, 0, charCanvas.width, charCanvas.height);
    		charContext.drawImage(image,
    			clip.x, clip.y, clip.w, clip.h,
    			0, 0, clip.w * scale, clip.h * scale);
    		// Apply color
    		charContext.globalCompositeOperation = 'multiply';
    		charContext.fillStyle = color;
    		charContext.fillRect(0, 0, clip.w * scale, clip.h * scale);
    		// Restore the transparency
    		charContext.globalCompositeOperation = 'destination-in';
    		charContext.drawImage(image,
    			clip.x, clip.y, clip.w, clip.h,
    			0, 0, clip.w * scale, clip.h * scale);
    		// Restore composite operation
    		charContext.globalCompositeOperation = 'source-over';
    		if (inst.cachedImages === undefined) {
    			inst.cachedImages = {}
    		}
    		if (inst.cachedImages[letter] === undefined) {
    			inst.cachedImages[letter] = {}
    		}
    		inst.cachedImages[letter][color] = charCanvas
    		return charCanvas
    	}
    

    Here's the code and here's how it looks

    cdn.discordapp.com/attachments/183566321156358144/481403367705542659/2018-08-21_11-58-23.gif

    EDIT: Just realized I should probably not recreate a whole new canvas each time. I'll change that.

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • Oh interesting, I didn't know "multiply" was one of the options now. It didn't used to be. I'm not sure if that would cause compatibility issues on some browsers.

    I hadn't thought of creating individual letters of different colors as needed and keeping them for later use. Probably works well when only using a limited amount of colors. If the user ended up trying to do gradual color transitions you may run out of video memory though.

    It's not bad to end a batch in c2's renderer. It does it all the time. All a batch does is it sends multiple quads using the same texture at the same time instead of separately. Besides you'd be doing your own batching of all the letters in your spritefont renderer. The only drawback is if multiple spritefont instances drawn right after each other then they could only be batched individually instead of together, but I say it doesn't matter too much.

    It seemed fun enough to try my hand at webgl again and try using my own shaders instead of working within the limits of c2's shaders and renderer. I must say webgl tutorials seem to add a lot of cruft to their code. It actually seemed fairly straightforward.

    Here I used the browser object to replace the drawGL of one instance to test stuff out. It uses it's own shader program and supplies it's own vertex,uv and color data. The only things I had to save and restore in the c2 renderer was the current shader program and arraybuffer it was using. Otherwise I tried to reuse as little as needed from the renderer, which amounted to just the view matrix.

    dropbox.com/s/52yxvilp81vii0t/custom_webgl3.capx

    Maybe that could be a useful example to use alongside a webgl tutorial.

  • Oh :o

    Nice, thanks. I'll look into this. I'll need to learn some proper webgl first. I didn't know you could get C2's gl by doing glw.gl xD.

    I guess I misread glwrap.js cause I never saw anywhere anything named this.gl, only gl.

    Oh interesting, I didn't know "multiply" was one of the options now. It didn't used to be. I'm not sure if that would cause compatibility issues on some browsers.

    Well it seems like most browsers support it. Every browser except Edge 11 and prior. Edge 12 supports it.

    According to MDN: developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation

    I hadn't thought of creating individual letters of different colors as needed and keeping them for later use. Probably works well when only using a limited amount of colors. If the user ended up trying to do gradual color transitions you may run out of video memory though.

    Yeah it works pretty well in most cases. But indeed it may become inefficient if a user uses too many colors for too many characters. However run out of video memory? How so? Maybe run out of RAM, but VRAM? I'm genuinely asking here. I create the texture on a virtual canvas, and then store it in a JS object. I never actually add the canvas to the document. Would that still use up some video memory?

    It's not bad to end a batch in c2's renderer. It does it all the time. All a batch does is it sends multiple quads using the same texture at the same time instead of separately. Besides you'd be doing your own batching of all the letters in your spritefont renderer. The only drawback is if multiple spritefont instances drawn right after each other then they could only be batched individually instead of together, but I say it doesn't matter too much.

    Yeah ok I kinda get it. I really need to learn webgl, because I didn't know most of that.

    Maybe that could be a useful example to use alongside a webgl tutorial.

    Yes thanks again. I'll look into it and maybe I can use that to make the code more optimized.

  • Even a hidden html5 canvas will use video memory. Internally the browser is using the graphics card for basically any and all rendering.

  • Thanks for the info

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)