how can create dynamic gradient?

We want to use dynamic gradient in our roku app. Is there any way to create it?

Please help.

Not nicely, no. There’s no css-style gradient support.

You would need to create a roBitmap, and then write rectangles to represent the gradient.

Then write it to tmp:

Then pass the uri out to the poster node (or wherever needs to read it)

w = 1920
h = 1080
bm = CreateObject("roBitmap", {width: w, height: h, AlphaEnable: false})
bm.DrawRect(10, 10, w-20, h-20, &H8080)
bm.Finish()
ba = bm.GetPng(0, 0, w, h)
ba.WriteFile("tmp:/test.png")

Use this but it doesn’t look like gradient.

Here’s a function you can use to generate vertical and horizontal linear gradients. Keep in mind, this function takes over 1000ms on my streaming stick 4k for a 1920x1080 image, so use it sparingly.

function GenerateGradient(width as integer, height as integer, startRgba as longinteger, endRgba as longinteger, lineDirection = "vertical")
  bitmap = CreateObject("roBitmap", { width: width, height: height, alphaEnable: true })

  'draw vertical lines from left to right
  if lineDirection = "vertical" then
    numSteps# = width
    'draw horizontal lines from top to bottom
  else
    numSteps# = height
  end if

  ' separate rgba values to compute the step between each gradient band
  red# = (startRgba and &hFF000000&) >> 24
  redEnd# = (endRgba and &hFF000000&) >> 24
  redStep# = (redEnd# - red#) / numSteps#

  blue# = (startRgba and &h00FF0000&) >> 16
  blueEnd# = (endRgba and &h00FF0000&) >> 16
  blueStep# = (blueEnd# - blue#) / numSteps#

  green# = (startRgba and &h0000FF00&) >> 8
  greenEnd# = (endRgba and &h0000FF00&) >> 8
  greenStep# = (greenEnd# - green#) / numSteps#

  alpha# = startRgba and &h000000FF&
  alphaEnd# = endRgba and &h000000FF&
  alphaStep# = (alphaEnd# - alpha#) / numSteps#

  for i = 0 to numSteps#
    red# += redStep#
    blue# += blueStep#
    green# += greenStep#
    alpha# += alphaStep#

    'merge rgb into single int
    color& = (Int(red#) << 24) + (Int(blue#) << 16) + (Int(green#) << <li-emoji id="lia_smiling-face-with-sunglasses" title=":smiling_face_with_sunglasses:"></li-emoji> + (Int(alpha#))

    if lineDirection = "vertical" then
      'draw a line at (i, 0) for the full height of the image
      bitmap.DrawRect(i, 0, 1, height, color&)
    else
      'draw a line at (0, i) for the full width of the image
      bitmap.DrawRect(0, i, width, 1, color&)
    end if
  end for

  bitmap.Finish()

  png = bitmap.GetPng(0, 0, width, height)
  uri = "tmp:/linear-gradient-" + str(width) + "x" + str(height) + "-0x" + stri(startRgba, 16) + "-0x" + stri(endRgba, 16) + "-" + lineDirection + ".png"
  png.WriteFile(uri)
  return uri
end function

And you can call it like this:

verticalGradientUrl = GenerateGradient(1920, 1080, &h00000000, &hFF0000FF, "vertical")
horizontalGradientUrl = GenerateGradient(1920, 1080, &h00000000, &hFF0000FF, "horizontal")

The function has not been optimized, meaning it will draw a line for every pixel in the gradient, even if several lines would have the exact same color. It could be optimized to draw a rectangle that is as big as the height/width of the color band.

If you’re in control of the poster size, then you should consider generating a gradient that’s 1 pixel in the opposing direction. (i.e. 1920x1 or 1x1080), then you can stretch the poster to the size you need in that opposing direction.

HI TwitchBronBron,

I tried it many times but it does not work for me.

Oh, sorry, I had a bug in my code. I was missing the << 8 for the green channel. I updated my sample snippet, it should work now.

I also just uploaded a full working example of this to the RokuCommunity samples repository, so feel free to pull that down and sideload it to see it working in action.

https://github.com/rokucommunity/sample-projects/tree/master/gradient

Thank you!! It’s working.

it works fine here!

just to improve, is there any way to create the gradient by passing angle degrees?

I’m sure there is, but that gets a lot harder! The current method just walks left-to-right or top-to-bottom and writes pixels. By using an angle, you’d need to calculate the positions of each pixel along the angle plane and then walk all the boxes that way…probably dealing with when the pixels fall off the visible dimensions.

If you get ambitious enough to implement that, feel free to open a PR in this sample project and I’ll update my code snippet: https://github.com/rokucommunity/sample-projects/tree/master/gradient

When I tried to use these following color it does not work. Is there any limitation to use it? please suggest?
&H00000000, &H00800000, &H00008000, &H00808000, &H00808080, &H00FF0000, &H0000FF00, &H00FFFF00, &H00FF00FF

Can you be more specific? What does “it does not work” mean?

When I used color that was mentioned in previous comment in the this function GenerateGradient(1920, 1080, &h00000000, &hFF0000FF, “horizontal”), the gradient is not showing these colors.

Let me show my example.

I need to implement a background label with gradient image.

First I implement a new component who just generate an image and by pass some args and returns an uri

Obs: Ignore GroupBase. it is a generic class just to add to any group node some util handlers. In your case just extend group.

<?xml version="1.0" encoding="utf-8" ?>

<!--********** Copyright 2016 Roku Corp.  All Rights Reserved. **********-->

<component name="GradientImage" extends="GroupBase" >

    <script type="text/brightscript" uri="pkg:/components/GradientImage/GradientImage.brs" />

</component>

in GradientImage.brs I implement as example above with tiny changes

function getLinearGradientUri(width as Integer, height as Integer, startRgba as Integer, endRgba as Integer, lineDirection as String) as String

  bitmap = CreateObject("roBitmap", { width: width, height: height, alphaEnable: true })

  'draw vertical lines from left to right
  if lineDirection = "vertical" then
    numSteps# = width
    'draw horizontal lines from top to bottom
  else
    numSteps# = height
  end if

  ' separate rgba values to compute the step between each gradient band
  red# = (startRgba and &hFF000000&) >> 24
  redEnd# = (endRgba and &hFF000000&) >> 24
  redStep# = (redEnd# - red#) / numSteps#

  blue# = (startRgba and &h00FF0000&) >> 16
  blueEnd# = (endRgba and &h00FF0000&) >> 16
  blueStep# = (blueEnd# - blue#) / numSteps#

  green# = (startRgba and &h0000FF00&) >> 8
  greenEnd# = (endRgba and &h0000FF00&) >> 8
  greenStep# = (greenEnd# - green#) / numSteps#

  alpha# = startRgba and &h000000FF&
  alphaEnd# = endRgba and &h000000FF&
  alphaStep# = (alphaEnd# - alpha#) / numSteps#

  for i = 0 to numSteps#
    red# += redStep#
    blue# += blueStep#
    green# += greenStep#
    alpha# += alphaStep#

    'merge rgb into single int
    color& = (Int(red#) << 24) + (Int(blue#) << 16) + (Int(green#) << 8) + (Int(alpha#))

    if lineDirection = "vertical" then
      'draw a line at (i, 0) for the full height of the image
      bitmap.DrawRect(i, 0, 1, height, color&)
    else
      'draw a line at (0, i) for the full width of the image
      bitmap.DrawRect(0, i, width, 1, color&)
    end if
  end for

  bitmap.Finish()

  png = bitmap.GetPng(0, 0, width, height)
  uri = "tmp:/linear-gradient-" + str(width) + "x" + str(height) + "-0x" + stri(startRgba, 16) + "-0x" + stri(endRgba, 16) + "-" + lineDirection + ".png"
  png.WriteFile(uri)

  return uri
end function

Then, To do a Label I implement a component who use GradientImage and wrap this and pass some util args.

In this case I use to create a background

<?xml version="1.0" encoding="utf-8" ?>

<!--********** Copyright 2016 Roku Corp.  All Rights Reserved. **********-->

<component name="GradientLabel" extends="GradientImage" >

    <interface>
        <field id="width" type="Int" />
        <field id="height" type="Int"  />
        <field id="paddingTop" value="2" type="Int" />
        <field id="paddingRight" value="10" type="Int" />
        <field id="paddingBottom" value="2" type="Int" />
        <field id="paddingLeft" value="10" type="Int" />
        <field id="startRgba" type="LongInteger" />
        <field id="endRgba" type="LongInteger"  />
        <field id="lineDirection" type="String" value="vertical" />
        <field id="size" type="String" value="medium" />
        <field id="text" type="String" onChange="onContentChanged" />
    </interface>

    <script type="text/brightscript" uri="pkg:/components/GradientLabel/GradientLabel.brs" />

    <children>

        <Poster id="gradient" />

        <Label
            id="label"
            horizAlign="center"
            vertAlign="center"
        />

    </children>

</component>

In GradientLabel.brs

Feel free to do as you want. In my case I did to make an label under text

The most important part is this

m.gradient.uri = getLinearGradientUri(m.top.width, m.top.height, m.top.startRgba, m.top.endRgba, "vertical")
sub init()
    m.Gradient = m.top.findNode("gradient")
    m.Label = m.top.findNode("label")

    m.Label.color = getColor("WHITE")
    m.Label.observeField("text", "onLabelChanged")
end sub

sub onContentChanged()
    m.Label.font = getFontSize(m.top.size)
    m.Label.text = m.top.text
end sub

sub onLabelChanged(event)
    node = event.getRoSGNode()
    rect = node.boundingRect()
    setSize(rect)
    node.width = m.top.width
    node.height = m.top.height

    m.Gradient.translation = [
        - m.top.paddingRight
        - m.top.paddingTop
    ]

    m.Gradient.width = m.top.width + m.top.paddingRight + m.top.paddingLeft
    m.Gradient.height = m.top.height + m.top.paddingTop + m.top.paddingBottom

    m.gradient.uri = getLinearGradientUri(m.top.width, m.top.height, m.top.startRgba, m.top.endRgba, "vertical")
end sub

function getFontSize(size as String)
    if size = "xsmall" then return getFont("OPENSANS_BOLD10")
    if size = "small" then return getFont("OPENSANS_BOLD12")
    if size = "medium" then return getFont("OPENSANS_BOLD14")
    if size = "large" then return getFont("OPENSANS_BOLD18")
    if size = "xlarge" then return getFont("OPENSANS_BOLD20")

    return getFont("OPENSANS_BOLD14")

end function

sub setSize(rect)
    setWidth(rect.width)
    setHeight(rect.height)
end sub

sub setWidth(width)
    if width > m.top.width then m.top.width = width
end sub

sub setHeight(height)
    if height > m.top.height then m.top.height = height
end sub

Using the component GradientLabel

<GradientLabel
    size="large"
    paddingTop="7"
    paddingBottom="7"
    paddingRight="0"
    paddingLeft="0"
/>

I checked the color is not working

&H00000000,&h00800000,&h00008000,&h00808000,&h00FF0000,&h0000FF00,&h00FFFF00

@PrahladFusioni all of your colors have the alpha channel set to 00. Which means all your colors will be fully transparent.

&H00000000
red=0, green=0, blue=0, alpha=0

&h00800000
red=0, green=80, blue=0, alpha=0

etc…

If I take your first two colors (&H00000000, &h00800000) but make them fully solid (&H000000FF, &h008000FF) then the gradient works fine. Here is the code:

m.gradientPoster.uri = GenerateGradient(1920, 1080, &H000000FF, &h008000FF, "vertical")