Text Drop Shadow

Hey there,

I really love Corona - it has a fast turn around time, and actually I mainly use it for Business app’s.

However I’v hid a snag, I need to do a Drop Shadow effect on my text in the app.

I use a .ttf font, and not really sure how to do this best, without having to buy a 3. Party plugin.

Any ideas, or input, would be greatly appreciated.

Best Wishes

/Thomas 

Hey thomas,

this related topic might help you:

https://forums.coronalabs.com/topic/55722-can-you-apply-a-filter-effect-to-a-displaynewtext-display-object/

or you can just create a second text object with the same text and a shadow color and offset it a bit…

I second the two text object solution (as per gb8), but I’d love to hear about any other creative solutions and results, so please share if you come up with a better way.

-Ed

Corona’s display.newEmbossedText() Lua function does exactly this.  It renders the text twice.  The back text is rendered first in a contrasting color (dark if your text is bright; bright if your text is dark) translated 1 content point in the bottom-right direction.  Your top text is then rendered on top.  Our internal implementation has a performance advantage because we use the same text mask for both the front and back text.

   https://docs.coronalabs.com/api/library/display/newEmbossedText.html

Hey Joshua,
thank you so much for the reply.

I already tried the display.newEmbosssedText function - however, I can’t really seam to “move the Shadow”, so it dispays a bit more as a drop Shadow, rather then the Embossed effect. Is there a way to increase the translation from 1 point to more - perhaps both on x and y axis?

Best Wishes

/Thomas

>> Is there a way to increase the translation from 1 point to more

Unfortunately no. For that, you’ll have to render the text twice as Ed suggested and do it yourself.

here is an old link from  " That’s So Panda " ,  that might be helpful to you

http://thatssopanda.com/corona-sdk-tutorials/adding-drop-shadow-to-text-objects/

here is an old link from  " That’s So Panda " ,  that might be helpful to you

http://thatssopanda.com/corona-sdk-tutorials/adding-drop-shadow-to-text-objects/

The cool thing about the two copies of the text is you can now apply a blur filter to the drop shadow to get softer shadows.

I made a drop text shadow function a while back, it’s not perfect but it allows you to offset the x/y position of the shadow among other things. The only “catch” is that it returns a single group containing both objects (for easy repositioning later), so if you want to update the string you need to call the :setText function I’ve included:

function display.newShadowText(params) local group = display.newGroup() local parent = params.parent or nil--display group to insert the text into local txt = params.text or ""--the string to display local width = params.width or 0--width of the textbox, 0 for unlimited local height = params.height or 0--height of the textbox, 0 for unlimited local x = params.x or 0--x position local y = params.y or 0--y position local font = params.font or native.systemFont--font name local fontSize = params.fontSize or 50--font size local align = params.align or "left"--alignment left/right/center local shadowColor = params.shadowColor or {0, 0, 0, 0.4}--colour of the drop shadow local textColor = params.textColor or {1}--colour of the main text local xOffset = params.xOffset or 0--how much to offset the dropshadow's x position relative to the main text object local yOffset = params.yOffset or -fontSize / 15 --same for the y position local shadowOff = params.shadowOff or false --if you don't want to show a shadow local anchorX = params.anchorX or 0.5 --anchor points local anchorY = params.anchorY or 0.5 --offset text on OS as needed, since some fonts get displayed differently on different OS if system.getInfo("platformName") == "iPhone OS" then --y = y + 5 elseif system.getInfo("platformName") == "Android" then --y = y + 5 end local s = display.newText({ text = txt, width = width, height = height, x = 0, y = 0, font = font, align = align, fontSize = fontSize, }) s:setFillColor( unpack(shadowColor) ) group:insert(s) local t = display.newText({ text = txt, width = width, height = height, x = xOffset, y = yOffset, font = font, align = align, fontSize = fontSize, }) t:setFillColor( unpack(textColor) ) group:insert(t) group.x, group.y = x + (group.width \* 0.5) - (group.width \* anchorX), y + (group.height \* 0.5) - (group.height \* anchorY) group.s = s group.t = t if shadowOff then s.isVisible = false end if parent then parent:insert(group) end function group:setText(str, shouldMove) self.s.text = str self.t.text = str if shouldMove == true or shouldMove == nil then group.x, group.y = x + (group.width \* 0.5) - (group.width \* anchorX), y + (group.height \* 0.5) - (group.height \* anchorY) end end function group:getText() return self.t.text end function group:setShadowColor(...) if arg then if type(arg[1]) == "table" then self.s:setFillColor( unpack(arg[1]) ) elseif type(arg[1]) == "number" then self.s:setFillColor( unpack(arg) ) end end end function group:setFillColor(...) if arg then if type(arg[1]) == "table" then self.t:setFillColor( unpack(arg[1]) ) elseif type(arg[1]) == "number" then self.t:setFillColor( unpack(arg) ) end end end return group end

It takes a table of parameters as it’s only argument. The params all have defaults so you only need to set the ones that you need to customise.

Here’s a sample of it in use:

local myGroup = display.newGroup() local bg = display.newRect(myGroup, display.contentWidth \* 0.5, display.contentHeight \* 0.5, display.contentWidth, display.contentHeight) bg:setFillColor(0.8, 0.8, 1) local myText = display.newShadowText({ parent = myGroup, text = "show this string with a shadow please", width = display.contentWidth \* 0.5, height = 0, x = display.contentWidth \* 0.5, y = display.contentHeight \* 0.5, font = "someFontName", align = "center", fontSize = 75, xOffset = 0, yOffset = -5, anchorX = 0.5, shadowColor = {0, 0, 0, 0.8} }) local strings = { "hello", "world", "foo", "bar" } local function update() --can pass in a table or just numbers --myText:setFillColor( {math.random(), math.random(), math.random()} ) myText:setFillColor( math.random(), math.random(), math.random() ) --update the text myText:setText(strings[math.random(1, #strings)]) end timer.performWithDelay( 1000, update, -1 )

I was thinking about doing a tutorial on drop shadows or writing up a simple function for it. But I started thinking about the problem of not being able to simply update .text and there is a solution to it. See this tutorial:

https://coronalabs.com/blog/2012/05/01/tutorial-property-callbacks/

Basically you can use a feature of meta tables that lets you set properties like:

myDropText.text = “Hello World”

and it actually triggers a call back function in your object that will take that value and you can use it to update the front text and shadow text.

Rob

Good shout Rob. I made my function a few years ago before I knew anything about meta tables/methods, this way is much simpler (well once you’ve set it up it is).

I’ve hit a small snag though, I’ve tried converting my method to use property callbacks, but in the process this has stopped working (where “parent” is a display group):

local group = display.newGroup() group = proxy.get\_proxy\_for( group ) if parent then parent:insert(group) --this is now broken end

Is there something I’ve forgotten that would cause the proxy to stop insert from working. The error is:

bad argument #-2 to 'insert' (Proxy expected, got nil)

I think, based on a very quick glance, that you’d have to call as “parent:insert(group.raw)” given the way that proxy is written.

aside:  the proxy approach is useful when you just need to intercept getters/setters (for example, doing custom things on a display object in response to a transition call) but will fail if you also need to still pass an “isA/instanceOf” -type test.  that is, your “group” no longer IS A display object, instead it’s the proxy wrapper – whereas “group.raw” IS A display object.

even further aside: there’s another somewhat similar approach of metatable chaining that can preserve the isA() original object yet still allow you to intercept getters/setters like a proxy, but it’s a bit much to cover in a forum post, mentioned only for completeness.

Hey thomas,

this related topic might help you:

https://forums.coronalabs.com/topic/55722-can-you-apply-a-filter-effect-to-a-displaynewtext-display-object/

or you can just create a second text object with the same text and a shadow color and offset it a bit…

I second the two text object solution (as per gb8), but I’d love to hear about any other creative solutions and results, so please share if you come up with a better way.

-Ed

Corona’s display.newEmbossedText() Lua function does exactly this.  It renders the text twice.  The back text is rendered first in a contrasting color (dark if your text is bright; bright if your text is dark) translated 1 content point in the bottom-right direction.  Your top text is then rendered on top.  Our internal implementation has a performance advantage because we use the same text mask for both the front and back text.

   https://docs.coronalabs.com/api/library/display/newEmbossedText.html

Hey Joshua,
thank you so much for the reply.

I already tried the display.newEmbosssedText function - however, I can’t really seam to “move the Shadow”, so it dispays a bit more as a drop Shadow, rather then the Embossed effect. Is there a way to increase the translation from 1 point to more - perhaps both on x and y axis?

Best Wishes

/Thomas

>> Is there a way to increase the translation from 1 point to more

Unfortunately no. For that, you’ll have to render the text twice as Ed suggested and do it yourself.