Author Topic: How Grayscale Works  (Read 4220 times)

0 Members and 1 Guest are viewing this topic.


  • Guest
How Grayscale Works
« on: July 31, 2011, 05:48:46 pm »
Here's an excerpt from the logs today explaining exactly how and why grayscale actually works on TI calculators.

Code: [Select]
[15:11:42] <+SirCmpwn> so the reason that grayscale works in the first place is that pixels on the screen fade between on and off, but very quickly
[15:12:10] <+SirCmpwn> so if you are fast enough, you can get in the middle of that process and, if you time it right, you can come close to keeping it at a constant faded value
[15:12:22] <+SirCmpwn> this is accomplished by quickly changing a pixel from black to white
[15:12:25] <+SirCmpwn> and back again
[15:12:25] <+OmnomIRC> (O)<DJ_O> False.
[15:12:28] <+SirCmpwn> over and over very quickly
[15:12:30] <+OmnomIRC> (O)<DJ_O> jk
[15:12:55] <+SirCmpwn> Let's say you have an 8x8 sprite
[15:13:10] <+SirCmpwn> no, we'll make it 2x2 to make it easier
[15:13:18] <+OmnomIRC> (O)<ztrumpet> 2x2 O.o
[15:13:33] <+SirCmpwn> it looks like this, where W is white, X is light gray, Y is dark gray, and Z is black
[15:13:35] <+SirCmpwn> WX
[15:13:36] <+SirCmpwn> YZ
[15:14:04] <+SirCmpwn> you cycle through three frames of grayscale
[15:14:29] <+SirCmpwn> so on frame one, it looks like this:
[15:14:38] <+SirCmpwn> where W is white and X is black
[15:14:42] <+SirCmpwn> WX
[15:14:44] <+SirCmpwn> WX
[15:14:48] <+SirCmpwn> frame two:
[15:14:49] <+SirCmpwn> WW
[15:14:51] <+SirCmpwn> XX
[15:14:55] <+SirCmpwn> and frame three:
[15:14:56] <+SirCmpwn> WW
[15:14:57] <+SirCmpwn> XX
[15:15:22] <+OmnomIRC> (O)<ztrumpet> (Sometimes it's different, but that's one example)
[15:15:24] <+SirCmpwn> this way, the light gray pixel is black for 1/3 of the time, and the dark gray pixel is on for 2/3 of the time
[15:15:31] <+SirCmpwn> ztrumpet is right, it is usually a little different
[15:15:51] <+SirCmpwn> the black and white pixels are constantly black or constantly white
[15:15:58] <+OmnomIRC> (O)<DJ_O> it creates diagonal scanlines, moving
[15:16:04] <+SirCmpwn> yes
[15:16:08] <+SirCmpwn> but let's keep this basic
[15:16:22] <+SirCmpwn> everyone follow at this point?
[15:16:28] <+OmnomIRC> (O)<leafiness0> lol storytime
[15:16:29] <+OmnomIRC> (O)<ztrumpet> yup
[15:16:37] <+OmnomIRC> (O)* ztrumpet likes Sir's story
[15:16:50] <+SirCmpwn> now let's look at a single dark gray pixel
[15:16:55] <+SirCmpwn> in extremely slow motion
[15:17:20] <+SirCmpwn> I'll assign it a value from 0-10 based on it's current actual appearance on screen, where 10 is black
[15:17:30] <+OmnomIRC> (O)<DJ_O> yes
[15:17:41] <+SirCmpwn> it changes like this over time: 6, 7, 8, 7, 6, 7, 8, 7, 6, 7, 8, and so on
[15:17:56] <+SirCmpwn> there is, of course, variation on this
[15:18:04] <+OmnomIRC> (O)<ztrumpet> Over a period of three frames to a number?
[15:18:08] <+SirCmpwn> no
[15:18:12] <+SirCmpwn> this isn't frame counts
[15:18:17] <+SirCmpwn> this is in between frames
[15:18:18] <+OmnomIRC> (O)<ztrumpet> Oh, I see
[15:18:22] <+SirCmpwn> let me define it more specifically
[15:18:51] <+SirCmpwn> [sets pixel to black] 6, 7, 8, [sets pixel to white], 7, 6, [sets pixel to black], 7, 8, etc
[15:19:07] <+OmnomIRC> (O)<ztrumpet> Ah, yes
[15:19:18] <+SirCmpwn> there is more or less deviation depending on the quality of grayscale
[15:19:25] <+SirCmpwn> for perfect grayscale, it is a constant 7
[15:19:33] <+SirCmpwn> the more the deviation, the more flicker you notice
[15:19:57] <+SirCmpwn> so imagine two pixels now
[15:20:05] <+SirCmpwn> still with a value of 00-10
[15:20:16] <+SirCmpwn> here's what you want them to look like over time:
[15:20:21] <+SirCmpwn> [dark gray][white]
[15:20:24] <+OmnomIRC> (O)<DJ_O> ah i get it now, i didnt understand the 67876 thing
[15:20:33] <+SirCmpwn> DJ_O good
[15:20:40] <+SirCmpwn> anyone can stop and ask questions, for the record
[15:20:52] <+SirCmpwn> so here's the evolution of that pixel over time:
[15:20:57] <+SirCmpwn> *those pixels
[15:21:05] <+OmnomIRC> (O)* ztrumpet wonders why I've never heard this awesome way of stating it before
[15:21:10] <+OmnomIRC> (O)<ztrumpet> *I've = he's
[15:21:19] <+OmnomIRC> (O)<leafiness0> lol
[15:21:29] <+PixelBoy> (Am I allowed to ask a question about DCS7?)
[15:21:34] <+SirCmpwn> [07][00], [08][00], sets to white, [07][00], [06][00], sets to black, and so on
[15:21:40] <+SirCmpwn> PixelBoy: can it wait
[15:21:41] <+SirCmpwn> ?
[15:22:08] <+SirCmpwn> now imagine if you were to move the dark gray pixel to the right
[15:22:16] <+SirCmpwn> this is where it gets complex
[15:22:26] <+SirCmpwn> the frame before you move it, it looks like [07][00]
[15:22:33] <+SirCmpwn> then you apply the changes to the screen
[15:22:37] <+SirCmpwn> and it starts to do this:
[15:23:17] <+SirCmpwn> [06][01], [05][02], [04][03], [03][04], [02][05], [01][06], [00][07]
[15:23:51] <+SirCmpwn> however, it doesn't actually happen like that, because you're still updating grayscale
[15:24:12] <+SirCmpwn> you never set the left pixel to black again, so it constantly drops. Its evolution is guaranteed to look like this:
[15:24:31] <+SirCmpwn> [07], [06], [05], [04], [03], [02], [01], [00]
[15:24:39] <+SirCmpwn> the right pixel is a different story
[15:24:52] <+SirCmpwn> you keep updating grayscale as it evolves
[15:25:00] <+SirCmpwn> meaning that you flicker it between black and white
[15:25:03] <+SirCmpwn> so it looks like this:
[15:25:06] <+OmnomIRC> (O)<ztrumpet> ...and eventually it just looks like a moving blob
[15:25:18] <+OmnomIRC> (O)<ztrumpet> ;)
[15:25:52] <+SirCmpwn> [00], [01], [02], set to white, [01], set to black. [02], [03], set to white, [02], set to black, [03], [04], set to white, [03], set to black, [04], [05], etc
[15:26:15] <+SirCmpwn> all the way back up to the normal dark gray evolution of [07]-[06]-[07]-[08]
[15:26:37] <+SirCmpwn> so any pixel set from white to gray will have a similar evolution
[15:26:43] <+SirCmpwn> the same applies to black to gray
[15:27:13] <+SirCmpwn> so you can tell that moving a single dark gray pixel will look generally mediocre
[15:27:26] <+SirCmpwn> but imagine if you were doing 4 level grayscale smooth scrolling on the whole screen
[15:27:31] <+SirCmpwn> _every_ pixel is moved
[15:27:45] <+SirCmpwn> not only that, but in our example, the left pixel stayed white
[15:27:52] <+SirCmpwn> but on a full screen tilemap, it probably wonot
[15:27:54] <+SirCmpwn> *wont
[15:28:14] <+SirCmpwn> and if you scroll again before the sequence fully evolves, it gets even worse
[15:28:38] <+OmnomIRC> (O)<ztrumpet> Woah, are you talking about with just one DispGraphrr instead of three?
[15:28:45] <+SirCmpwn> yes
[15:28:47] <+OmnomIRC> (O)<ztrumpet> O.o
[15:28:50] <+SirCmpwn> there is still bad quality with three
[15:28:52] <+OmnomIRC> (O)<ztrumpet> Yeah, don't do that
[15:29:12] <+SirCmpwn> the CPU time that it takes to scroll worsens the situation
[15:29:21] <+SirCmpwn> it takes more time to scroll than to not scroll, obviously
[15:29:29] * +JustCause ([email protected]) Quit (Remote host closed the connection)
[15:29:33] <+SirCmpwn> so your pixel deviation increases
[15:29:42] <+OmnomIRC> (O)<ztrumpet> You almost need four buffers - two to operate on and two to display with
[15:29:48] <+SirCmpwn> at the same time that all of this is happening
[15:29:53] <+SirCmpwn> and you don't really need four buffers
[15:29:55] * leafy ([email protected]) has joined #omnimaga
[15:29:55] * Netbot45 sets mode: +v leafy
[15:30:05] <+SirCmpwn> you already have 3, technically
[15:30:11] <+SirCmpwn> two to operate on, and the screen to display with
[15:30:29] <+OmnomIRC> (O)<ztrumpet> There's another one at E8000 (if you add three bytes of Asm() )
[15:30:41] <+SirCmpwn> you don't need to use any more
[15:31:05] <+SirCmpwn> there is no really good solution to this problem
[15:31:21] <+SirCmpwn> the best ways to fix this is to allow the pixels time to reach their normal evolution after scrolling
[15:31:38] <+SirCmpwn> find something to do between the scrolling and the screen update, but nothing too big
[15:31:43] <+shmibs> so choppier scrolling on more disparate intervals
[15:31:57] <+SirCmpwn> yes
[15:32:18] <+SirCmpwn> to reduce pixel deviation, get as much logic out of the loop as you possibly can
[15:32:29] <+SirCmpwn> DispGraphrr three times in a row for ideal deviation removal
[15:32:49] <+SirCmpwn> and in the rest of your loop, don't even think about redrawing the whole screen every frame
[15:33:05] <+OmnomIRC> (O)<ztrumpet> How many DispGraphrrs do you hav in your entire main loop for Motherload?
[15:33:09] <+SirCmpwn> redraw very sparingly, only on the parts of the screen that change
[15:33:23] <+SirCmpwn> ztrumpet: the loop executes differently every time, but there is at most six per frame
[15:33:32] <+OmnomIRC> (O)<ztrumpet> O.o
[15:33:39] <+OmnomIRC> (O)<ztrumpet> That's some awesome sounding code
[15:33:45] <+leafy> xD
[15:34:00] <+SirCmpwn> I update 3 times every loop
[15:34:11] <+SirCmpwn> on physics frames, I do an early update beforehand
[15:34:11] <+OmnomIRC> (O)<ztrumpet> All in a row?
[15:34:15] <+SirCmpwn> in a row, yes
[15:34:28] <+OmnomIRC> (O)<ztrumpet> physics frames?
[15:34:41] <+SirCmpwn> I split up my frame-to-frame operations three ways
[15:34:49] <+SirCmpwn> every third frame, physics updates
[15:35:02] <+OmnomIRC> (O)<calc84maniac> or you could time the updates with interrupts and do as much processing code you want
[15:35:02] <+OmnomIRC> (O)<ztrumpet> Oh, cool!
[15:35:18] <+SirCmpwn> calc84maniac: _all_ of RAM is tied up
[15:35:32] <+OmnomIRC> (O)<ztrumpet> calc84, that;s how I like it
[15:35:35] <+OmnomIRC> (O)<calc84maniac> okay, I have no idea what we're talking about then :P
[15:35:43] <+SirCmpwn> every third frame, in a different frame than physics, I update scrolling
[15:35:44] <+OmnomIRC> (O)<calc84maniac> I thought you were just talking about grayscale in general
[15:35:46] <+OmnomIRC> (O)<ztrumpet> Motherload
[15:35:51] <+SirCmpwn> ^
[15:35:57] <+SirCmpwn> I have a massive map taking up most of RAM
[15:36:05] <+SirCmpwn> it kinda gets in the way of things
[15:36:08] <+OmnomIRC> (O)<ztrumpet> calc84, Cube Droid has grayscale timed by interrupts
[15:36:27] <+OmnomIRC> (O)<calc84maniac> chip's challenge does too
[15:36:27] <+SirCmpwn> and the map can be modified by the user, and I don't think I want the user modifying my interrupt table ;)
[15:36:32] <+OmnomIRC> (O)<ztrumpet> nice
[15:36:44] <+OmnomIRC> (O)<calc84maniac> since I need 7 updates per frame, heh
[15:36:52] <+OmnomIRC> (O)<ztrumpet> lol
[15:36:56] <+OmnomIRC> (O)<calc84maniac> I don't want to time that manually
[15:37:15] <+SirCmpwn> ztrumpet: the guts of the story is that I have an entirely different main loop on each one of three frames
[15:37:17] <+OmnomIRC> (O)<ztrumpet> thepenguin'd use the crystal timers
[15:37:25] <+SirCmpwn> one of them updates the screen 3 times
[15:37:30] <+SirCmpwn> the other two update the screen 6 times
[15:37:34] <+OmnomIRC> (O)<ztrumpet> Sir, I understand, and it's awesome
[15:37:46] <+SirCmpwn> k
[15:37:51] <+OmnomIRC> (O)<ztrumpet> :D
[15:37:53] <+OmnomIRC> (O)<jimbauwens> ephan
[15:37:55] <+leafy> Oo
[15:37:57] <+SirCmpwn> any questions on the overall grayscale discussion that started this?
[15:38:42] <+SirCmpwn> it's best to just sit there and put some thought into how the screen works and how grayscale works, and you'll eventually understand it if you don't already
« Last Edit: July 31, 2011, 05:49:31 pm by SirCmpwn »

Offline Geekboy1011

  • The Oneironaut
  • Donator
  • LV11 Super Veteran (Next: 3000)
  • ***********
  • Posts: 2031
  • Rating: +119/-2
  • Dream that Awakening dream
    • View Profile
Re: How Grayscale Works
« Reply #1 on: July 31, 2011, 07:37:21 pm »
Holy TextWALL.

still a interesting read and a nice way of doing it in axe...would have never even attempted that my self X.x

Offline mrmprog

  • LV7 Elite (Next: 700)
  • *******
  • Posts: 559
  • Rating: +35/-1
    • View Profile
Re: How Grayscale Works
« Reply #2 on: August 01, 2011, 12:27:52 am »
So to get better grayscale, you can do DispGraphrr multiple times?


  • Guest
Re: How Grayscale Works
« Reply #3 on: August 01, 2011, 12:28:51 am »
You have to play around with it a little.  One complete cycle with Axe grayscale is three times.

Offline calcdude84se

  • Needs Motivation
  • LV11 Super Veteran (Next: 3000)
  • ***********
  • Posts: 2272
  • Rating: +78/-13
  • Wondering where their free time went...
    • View Profile
Re: How Grayscale Works
« Reply #4 on: August 01, 2011, 12:29:55 am »
You mean 4-level, right? For 3-level two cycles are sufficient, if I am not mistaken.
"People think computers will keep them from making mistakes. They're wrong. With computers you make mistakes faster."
-Adam Osborne
Spoiler For "PartesOS links":
I'll put it online when it does something.


  • Guest
Re: How Grayscale Works
« Reply #5 on: August 01, 2011, 12:48:28 am »
Correct.  I just never bother with 3 level XD

Offline TIfanx1999

  • ಠ_ಠ ( ͡° ͜ʖ ͡°)
  • CoT Emeritus
  • LV13 Extreme Addict (Next: 9001)
  • *
  • Posts: 6173
  • Rating: +191/-9
    • View Profile
Re: How Grayscale Works
« Reply #6 on: August 01, 2011, 12:03:50 pm »
Thank you for posting this. I missed the IRC conversation, and I was curious as to how you handled grayscale in Motherload, since you said you had come up with your own method. =)


  • Guest
Re: How Grayscale Works
« Reply #7 on: August 02, 2011, 01:15:14 am »
Sure thing.  Glad it helped.
I only recall saying that I spent some time fine tuning grayscale, instead of rolling my own ;)

Offline DJ Omnimaga

  • Clacualters are teh gr33t
  • CoT Emeritus
  • LV15 Omnimagician (Next: --)
  • *
  • Posts: 55936
  • Rating: +3153/-232
  • CodeWalrus founder & retired Omnimaga founder
    • View Profile
    • DJ Omnimaga Music
Re: How Grayscale Works
« Reply #8 on: August 02, 2011, 01:33:44 am »
Cool Sir! Thanks for posting this. Hopefully it clears things up for many people. :)
« Last Edit: August 02, 2011, 01:43:03 am by DJ_O »