User avatar
Gavinmc42
Posts: 4050
Joined: Wed Aug 28, 2013 3:31 am

Re: My es2gears went black

Mon Feb 04, 2019 2:50 am

Hey I got some of those PB137 years ago, never got around to using them as I still have to get a spare panel.
Might have a look for secondhand refit panels, sometimes going free if you take them away ;)

Sounds a bit chilly there, climate change making it colder?
Down south we have heatwaves, bush fires and power blackouts, up North floods.
We moved up the hill in 2011 to get away from the flood zones at the bottom.
Some how we missed it all this year, winters are warmer so no need for the firewood heater.
Summer needs the aircon for 2 months, down south they lost power for aircon.
Supposed to be cooler down there, crazy weather.

This is the example code that convinced me to drop Linux and go baremetal
https://github.com/ultibohub/Examples/t ... c3000/RPi2
All my temp plotting Pi's use baremetal now.

What language/toolset are you using?
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

User avatar
ab1jx
Posts: 868
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

Re: My es2gears went black

Mon Feb 04, 2019 7:51 am

No real interest in doing baremetal without Linux. I used OpenBSD for about 15 years but Linux has a bigger userbase so it's more refined. A lot of things won't build under OpenBSD, it's like pulling hen's teeth. And I like Firefox, Libre Office, Gimp, I couldn't do baremetal. Even my Zeroes have Raspbian.

Heck no, this is warming up. I remember 20 below several times but not since about the 80s. I don't like the heat, maybe I'll move to Maine. I'm at 1600 feet, no floods here.

OK, finished my framebuffer scroll test;. It even runs without X, but doesn't do anything on my Dell laptop with Debian. All the numbers seem right and it runs for the same 10 seconds, but nothing happens on the screen. My counts were way off before. On a 1920x1080 screen I'm scrolling ~900 lines down ~900 times, the counter shouldn't have been in the inside loop. At the outer level I get about 100 lines/pixels per second. Good enough for a waterfall, I don't want it that fast.

Makefile:

Code: Select all

CC = gcc
st: st.c
        $(CC) -Wall -O3 st.c -o st
 

Save this as st.c:

Code: Select all

/*
  Scrolltest - just flat-out scrolling a screeen area to see how fast it can 
  go.  Uses /dev/fb0 mmaped
  
  The test area that scrolls is dynamically set to screen width / 2, screen
  height * 0.9
*/

#include <unistd.h>
#include <fcntl.h>        /* for fcntl */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>        /* for mmap */
#include <sys/ioctl.h>
#include <linux/fb.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <time.h>

char *fbp; // framebuffer pointer, the whole screen
char *backp; // screen backup area
int sz; // screen size in bytes
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
int tht; // test area height
int twd; // width
int topline, ledge; // screen coords
int datastride, screenstride;
int linecount = 0; // scanlines, 1 pixel high
int secs = 10; // time to run

void restore_screen(void) {
  int i;
  void *copyfrom;
  void *copyto;
  topline = (vinfo.yres - tht) /2;
  copyfrom = backp;
  copyto = fbp + (topline * screenstride) + (ledge * (vinfo.bits_per_pixel / 8));
  for (i=0; i<tht; i++) {
    memcpy(copyto,copyfrom,datastride);
    copyto += screenstride;
    copyfrom += datastride;
  }
}

void down1(void) { // scroll the screen down 1 pixel (every line of it)
  void *copyfrom;
  void *copyto;
  int i;
  copyto = fbp + (tht * screenstride) + (ledge * (vinfo.bits_per_pixel / 8));
  copyfrom = copyto - screenstride; // 1 line up
  for (i=tht; i>topline; i--) {
    memcpy(copyto,copyfrom,datastride);
    copyto -= screenstride;
    copyfrom -= screenstride;
  }
  linecount++;
}

void do_backup(void) { // back up screen area
  int bs = twd * tht * (vinfo.bits_per_pixel / 8); // bytes for test area
  int i;
  topline = (vinfo.yres - tht) /2; // center window vertically
  void *copyfrom;
  void *copyto;
  backp = malloc(bs);
  if (backp == NULL) {
    fprintf(stderr,"Allocating memory for window backup failed\n");
    exit(1);
  }
  copyto = backp;
  copyfrom = fbp + (topline * screenstride) + (ledge * (vinfo.bits_per_pixel / 8));
  for (i=0; i<tht; i++) {
    memcpy(copyto,copyfrom,datastride);
    copyto += datastride;
    copyfrom += screenstride;
  }
}

void init(void) {
  int fbfd;  // frame buffer file descriptor
  linecount = 0; // force
  fbfd = open("/dev/fb0", O_RDWR);
  if (fbfd == -1) {
    fprintf(stderr,"Error opening /dev/fb0\n");
    perror("open ");
    exit(1);
  }
  // get the fixed screen information
  if (ioctl (fbfd, FBIOGET_FSCREENINFO, &finfo)) {
    printf("Error reading fixed information.\n");
    exit(2);
  }
  // get variable screen info
  if (ioctl (fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
    fprintf(stderr,"Error reading variable screen info struct.\n");
    exit(3);
  }
  printf("Screen is %u x %u, %u bits/pixel ",vinfo.xres,vinfo.yres,\
    vinfo.bits_per_pixel); // should maybe want byte / pixel, not bits here
  sz = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel;  
  fbp = (char *) mmap(NULL,finfo.smem_len,PROT_READ | PROT_WRITE, MAP_SHARED,fbfd,0);
  if (fbp < 0) { // -1 on error, but it's not normally an int, tricky
    fprintf(stderr,"mmap failed\n");
    perror("mmap ");
    exit(1);
  }
  printf("mapped to 0x%08x\n",(uint32_t) fbp);
  tht = 0.9 * vinfo.yres; // most of the screen height
  twd = 0.5 * vinfo.xres; // half width taken out of the middle
  ledge = (vinfo.xres - twd) / 2; // left margin
  datastride = twd * (vinfo.bits_per_pixel / 8);
  screenstride = vinfo.xres * (vinfo.bits_per_pixel / 8);
  printf("tht: %i twd: %i ledge: %i datastride: %i screenstride: %i\n",\
    tht,twd,ledge,datastride,screenstride);
  close(fbfd); // got a pointer now, don't need fd anymore
}

int main(void) {
  time_t start_t;
  time_t stop_t;
  float persec = 0;
  init();
  do_backup();
  start_t = time(NULL);
  stop_t = start_t + secs;
  while (time(NULL) < stop_t) {
    down1(); // 1 screen height
  }
  restore_screen();
  munmap(fbp,finfo.smem_len);    
  if (backp)
    free(backp);
  persec = (linecount * 1.0) / (secs * 1.0);
  printf("%i lines in %i seconds = %0.3f lines/sec\n",linecount,secs,\
    persec);
  printf("Whee, that was fun!\n");
  return 0;
}
The number of lines keeps changing, not sure why. I see:

Code: Select all

1038 lines in 10 seconds = 103.800 lines/sec
935 lines in 10 seconds = 93.500 lines/sec
1040 lines in 10 seconds = 104.000 lines/sec
992 lines in 10 seconds = 99.200 lines/sec
990 lines in 10 seconds = 99.000 lines/sec
Oh well.

User avatar
Paeryn
Posts: 2747
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: My es2gears went black

Mon Feb 04, 2019 12:55 pm

ab1jx wrote:
Mon Feb 04, 2019 7:51 am
The number of lines keeps changing, not sure why. I see:

Code: Select all

1038 lines in 10 seconds = 103.800 lines/sec
935 lines in 10 seconds = 93.500 lines/sec
1040 lines in 10 seconds = 104.000 lines/sec
992 lines in 10 seconds = 99.200 lines/sec
990 lines in 10 seconds = 99.000 lines/sec
Oh well.
That's probably more down to the timer resolution than anything.
If you start at dead on a second (e.g. 0.0s) then your loop will run for ~10 seconds. If however you start part way through a second (e.g. 0.9s) then your loop will only run for ~9.1s and at ~100 lines per second that accounts for ~90 lines difference.
She who travels light — forgot something.

User avatar
ab1jx
Posts: 868
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

Re: My es2gears went black

Mon Feb 04, 2019 1:51 pm

Paeryn wrote:
Mon Feb 04, 2019 12:55 pm
That's probably more down to the timer resolution than anything.
If you start at dead on a second (e.g. 0.0s) then your loop will run for ~10 seconds. If however you start part way through a second (e.g. 0.9s) then your loop will only run for ~9.1s and at ~100 lines per second that accounts for ~90 lines difference.
Thank you, I realized that once I'd thought about it. The timer was really just for a ballpark number and to have an exit in case the scrolling window obscured the terminal emulator window or it lost focus so ctrl-c wouldn't work. If I did something like
while (time(NULL) < stop_t)

for my inner loop the timing would be better but I'm not sure how much overhead there is. Doing it thousands of times could be expensive. I do tht (972 here) memcpys before I even check the time.

Is there any non-X widget set I can use? I didn't intend it to be that way but I sort of like the fact that it runs without X, just from a command line. I'd like some labels to display frequencies, gains,etc. And buttons, spinners, edit boxes for inputting numbers. I could probably cobble something up, but I'd probably rather not re-invent the wheel. I was doing scrolling to test feasibility of doing the waterfall in an SDR app this way, looks OK.

Hmm, maybe https://github.com/littlevgl/
With the official 7 inch touch screen even: http://www.vk3erw.com/index.php/16-soft ... -littlevgl
Image

Way too complicated, it's like GTK without X. Big learning curve, much baggage.

Actually there seems to have been a GTK 2 for framebuffers https://ha.redhat.com/f/pdf/gtkfb.pdf Runs without X I guess.

It seems to have been a reality, at least for GTK 2. It probably had to be enabled when you built the GTK. Yes, configured with --with-gdktarget=linux-fb by http://maemo.org/api_refs/4.0/gtk/gtk-framebuffer.html That's probably not the standard way to build it.

I also have (installed from the debs):
X11 Direct Graphics Access extension wire protocol
This package provides development headers describing the wire protocol
for the XFree86-DGA extension, which provides direct, framebuffer-like,
graphics access.
Not sure if that's the same as DRI or not, could have been renamed, probably deprecated by now. Package name is x11proto-xf86dga-dev.

Apropos dga gets me:

Code: Select all

XDGA (3)             - Client library for the XFree86-DGA extension.
XDGAChangePixmapMode (3) - Client library for the XFree86-DGA extension.
XDGACloseFramebuffer (3) - Client library for the XFree86-DGA extension.
XDGACopyArea (3)     - Client library for the XFree86-DGA extension.
XDGACopyTransparentArea (3) - Client library for the XFree86-DGA extension.
XDGACreateColormap (3) - Client library for the XFree86-DGA extension.
XDGAFillRectangle (3) - Client library for the XFree86-DGA extension.
XDGAGetViewportStatus (3) - Client library for the XFree86-DGA extension.
XDGAInstallColormap (3) - Client library for the XFree86-DGA extension.
XDGAKeyEventToXKeyEvent (3) - Client library for the XFree86-DGA extension.
XDGAOpenFramebuffer (3) - Client library for the XFree86-DGA extension.
XDGAQueryExtension (3) - Client library for the XFree86-DGA extension.
XDGAQueryModes (3)   - Client library for the XFree86-DGA extension.
XDGAQueryVersion (3) - Client library for the XFree86-DGA extension.
XDGASelectInput (3)  - Client library for the XFree86-DGA extension.
XDGASetClientVersion (3) - Client library for the XFree86-DGA extension.
XDGASetMode (3)      - Client library for the XFree86-DGA extension.
XDGASetViewport (3)  - Client library for the XFree86-DGA extension.
XDGASync (3)         - Client library for the XFree86-DGA extension.
XF86DGA (3)          - Client library for the XFree86-DGA extension.
XFree86-DGA (3)      - Client library for the XFree86-DGA extension.
And, sure enough "THIS IS THE OLD DGA API AND IS OBSOLETE. PLEASE DO NOT USE IT ANYMORE" in /usr/include/X11/extensions/xf86dga1.h

Online DGA man page https://linux.die.net/man/3/xfree86-dga which says
Most of the reasons for the XFree86-DGA extension's existence are now better served in other ways. Further development of this extension is not expected, and it may be deprecated in a future release. The features that continue to be useful will either be provided through other existing mechanisms, or through an extension that address those needs more specifically.
But no idea what the replacement was. Maybe it was DRI.

I have a working framebuffer, I just want a few widgets in it. Do I have to reinvent the wheel?

User avatar
ab1jx
Posts: 868
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

Re: My es2gears went black

Tue Feb 05, 2019 2:05 am

Gavinmc42 wrote:
Mon Feb 04, 2019 2:50 am
Hey I got some of those PB137 years ago, never got around to using them as I still have to get a spare panel.
Might have a look for secondhand refit panels, sometimes going free if you take them away ;)

What language/toolset are you using?
Language/toolset is just plain GCC in retirement. Used to do a lot of Pascal/Delphi. For work I had to learn Java, then VBscript, then something else ugly, I forget what. Javascript's not bad. I've also done Fortran, 80x86 assembly, RPG, BASIC. Graphics languages REGIS, HPGL, Postscript, Borland graphics https://en.wikipedia.org/wiki/Borland_G ... _Interface and some built into BASICs: c64, Apple2, etc. HTML but not much CSS.

Panels have gotten a lot cheaper than 10 years ago, used to be almost $5/watt now sometimes $1/watt. And more common which helps the price. Home Depot finally has them https://www.homedepot.com/s/?search=off ... ar%20panel Walmart has some decent battery deals, I order a lot from them online, their nearest store is an hour away. I got I think this panel for $100 a year ago https://www.homedepot.com/p/Renogy-100- ... /301449131

Hot water solar panels free, I can believe that. That silicon oil they usually contain is about impossible to get rid of so once you unsolder the plumbing you can't clean it up enough to solder it again. Maybe lye would work, there's no solvent that touches it. Maybe something recent.

User avatar
Gavinmc42
Posts: 4050
Joined: Wed Aug 28, 2013 3:31 am

Re: My es2gears went black

Tue Feb 05, 2019 3:28 am

Price per KW/hr keeps going up here, around 0.30AUD now, that's not including all the service charges.
So people are taking out their old small 1-1.5KW systems and replacing them with 5KW+ and battery walls.
Means the old 160-200W panels can be got for $30-50 each, but they get snapped up pretty quick.
I have a working framebuffer, I just want a few widgets in it. Do I have to reinvent the wheel?
Yep or use QT which is another learning curve.
I wasted months looking at GUI's, too big, too slow, too confusing, not accelerated.
Once I stopped and actually played with AJ Stark OpenVG code it was simpler than I expected.
Kicked myself wasting time of that other stuff.

Mind you I still need to mix framebuffer, OMXplayer and OpenVG in a way I can understand and remember.
Something to do with layers, that I got right by accident :oops:
Going to take me a few more years to understand this stuff.
Then I start again when Pi4 comes out?

Jan Newmarch and Andrew from Melbourne required reading.
https://www.apress.com/gp/book/9781484224717
https://github.com/AndrewFromMelbourne

Lots of tutorials give you trianlge code, big deal, show me code for a GUI.
A real demo application etc.

You might want to try Netbeans and JavaFX it comes with examples I found very useful.
The old Oracle one was accelerated, not sure about the new open versions.
Too slow on the old Pi's but new 2-3's it is ok.
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

User avatar
ab1jx
Posts: 868
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

Re: My es2gears went black

Tue Feb 05, 2019 4:57 am

These little ARM chips can execute Java bytecode directly I think, it's built into their instruction set. Most of what happens in millions of Android phones is in Java because there's no real speed penalty like with old style interpreted Java bytecode. The compiler doesn't actually make an executable, it just makes bytecode. Like UCSD Pascal on an Apple II just compiled into P-code, still needed another step before it could be run. That may not have been true with the Pi 1.

Around here there are a lot of high voltage panels being installed that tie into the grid and sell power back to the power company. I think if you work the tax breaks/credits right they don't cost much. Like Germany. But I don't think it's feasible to feed them into batteries. Most grid-tied people with solar still have no power in power outages, unless they got creative setting up switching.

Framebuffer I like because it's just memory-mapped video. Like doing a poke on a memory address and having something appear on the screen. Change the address and it moves, you can work out addresses for things like corners. I open() /dev/fb0 then mmap() it, then I've got a pointer to the upper left screen corner. It's not accelerated but it's just writing to memory so it's quick. Except when you scroll like a waterfall it's thousand of writes.

A couple years ago I saw a video clip by Peter Onion on here of his SDR he wrote on a Pi and he said:
Not quite. In OpenGLES you write small bits of C like code called "shaders",
these are compiled into GPU code. They then run on the GPU to process vertex
and pixel information. All that happens on the GPU.

I did the waterfall in OpenGLES by using a texture that wraps, updating only
one line of the texture and changing the Y coordinate in the texture for the
top line. Only needed to transfer one line of data into the GPU for each
line scrolled. Very efficient compared with ARM based memmoves in
framebuffers !
I have mental blocks against learning Python and C++, gave up a couple years ago (like 2nd semester calculus). QT needs C++ I think (also FLTK). Clunky GTK doesn't. A big problem with learning OpenGLES is figuring out where to start and not be studying the wrong thing. So much documentation in Linux is obsolete or the wrong thing.

This lab does some interesting stuff https://wearables.unisa.edu.au/ I originally stumbled across something by this guy http://www.tinmith.net/wayne/ who graduated from there. He does a lot in OpenGL, a lot of his machine vision stuff. Cameras connected by Firewire, computers analyzing motion not as bitmaps but as motion vectors. http://www.tinmith.net/ Works for Google now.

Image

User avatar
Gavinmc42
Posts: 4050
Joined: Wed Aug 28, 2013 3:31 am

Re: My es2gears went black

Tue Feb 05, 2019 7:07 am

Around here there are a lot of high voltage panels being installed that tie into the grid and sell power back to the power company. I think if you work the tax breaks/credits right they don't Cost much.
Yep, we got ours installed in time to get $0.50 feed in tariff, but after 5 years the service charges have increased to about the same as what we used to pay for the bill :(
Even swaping to LED lighting the power bill is creeping up again.

New installs get $0.08 feed in so people installed bigger sizes upto 5KW and add batteries.
They tend to use more power in the day time becuase they are not paying for it.
New Tesla Power Wall 2 now can provide power when the grid goes down, only the first few hundred have been install?

Not sure about Java byte code on the Pi3 and the 1176 does do it, I think.
Ah, understand Peter's one line waterfall, just move pointer instead of rewriting it?
A big problem with learning OpenGLES is figuring out where to start and not be studying the wrong thing. So much documentation in Linux is obsolete or the wrong thing.
Yep modern stuff does not work and old stuff is Android.

Nice link to AR wearable, got some guys working on that here, during Open Days here my kid was chasing AR Dinosaurs
MS are supposed to be releasing a lower cost system, current ones are very expensive.

Do anything intresting in AR/VR/OpenCL most end up at Google, Amazon, Intel, IBM, MS etc.
Then IP disappears or dies ;)
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

User avatar
ab1jx
Posts: 868
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

Re: My es2gears went black

Tue Feb 05, 2019 5:18 pm

If you have the batteries and inverter and all that I would think 3 KW worth of panels would do unless you have electric heat. Not sure about A/C. Newer panels are slightly more efficient but still only about 13% I think. That's why the heat collecting ones are good, much more efficient. I used to hang out in the forums at https://www.fieldlines.com/ I built a few small windmills but nothing with speed regulators and they all destroyed themselves in high winds. We get a lot of wind in the winter, not summer. I spent most of 2 months carving this thing too. This was 2008.
combined_800.jpg
combined_800.jpg (77.82 KiB) Viewed 1187 times
Carved based on a spreadsheet from https://scoraigwind.com/books/ My interpretation is that the pitch of the blade varies depending on the radius so the outside is almost flat because it can hit speeds of 200 MPH, in the center it doesn't turn that fast so the pitch is steeper. All parts of the blade contribute equally, and it can only turn 1 speed at a time. It has lift like an airplane wing.

I looked at some datasheets, Jazelle is what they call the Java bytecode thing in the ARM chips.

I wonder if that guy isn't working on the self-driving cars at Google. All that experience processing camera video.

Almost no hits when I searched for wearable on this board. And a Pi running on lithium batteries is tiny too. And there are cameras that plug into the board.

Move a pointer, it's not that simple. If I wanted to take over the whole screen I could just memcpy the whole bottom part down. But I want the app to run in a window, which means I have to ignore stuff outside the window. So skip a bunch of pixels, copy a bunch, skip a bunch, repeat. It probably won't help much but I can precompute all the pointers and store them in an array of structs. I'll use them over and over. I'd be more apt to study GPU assemly language than OpenlGL I think. But there aren't enough hours in a day.

User avatar
Paeryn
Posts: 2747
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: My es2gears went black

Tue Feb 05, 2019 10:29 pm

ab1jx wrote:
Tue Feb 05, 2019 5:18 pm
I looked at some datasheets, Jazelle is what they call the Java bytecode thing in the ARM chips.
Jazelle was a bit of a weird thing, they kept supporting it but as far as I'm aware a lot of processors (especially newer ones) only had limited hardware support, the rest of the bytecodes just trapped into normal arm (or maybe thumb) code for handling in software. Plus I don't think there was any public documentation of how the JVM state was held internally or which bytecodes were implemented in hardware. I never found anything when I looked years ago.
ab1jx wrote:
Tue Feb 05, 2019 5:18 pm
Move a pointer, it's not that simple. If I wanted to take over the whole screen I could just memcpy the whole bottom part down. But I want the app to run in a window, which means I have to ignore stuff outside the window. So skip a bunch of pixels, copy a bunch, skip a bunch, repeat. It probably won't help much but I can precompute all the pointers and store them in an array of structs. I'll use them over and over. I'd be more apt to study GPU assemly language than OpenlGL I think. But there aren't enough hours in a day.
You could probably do it fairly easily using Dispmanx to create a window (not to be confused with an X11 window) above the framebuffer for your waterfall, as long as you don't mind that such a window would be "invisible" to the framebuffer or X11. That way the hardware does the composition as it generates the video display and the window's contents never actually get copied to the famebuffer.
She who travels light — forgot something.

User avatar
Gavinmc42
Posts: 4050
Joined: Wed Aug 28, 2013 3:31 am

Re: My es2gears went black

Wed Feb 06, 2019 12:16 am

You could probably do it fairly easily using Dispmanx to create a window (not to be confused with an X11 window) above the framebuffer for your waterfall, as long as you don't mind that such a window would be "invisible" to the framebuffer or X11. That way the hardware does the composition as it generates the video display and the window's contents never actually get copied to the famebuffer.
That explains a few things, I was using OpenVG to make a LCARS UI but could not do a screenshot.
The Screenshot software only grabs the framebuffer?

Dispmanx is extremely well documented, not ;)
Time for me to play with Andrew from Melbourne's code to understand it.
I don't get the layers and EGL/Dispmanx stuff yet.
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

User avatar
Paeryn
Posts: 2747
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: My es2gears went black

Wed Feb 06, 2019 1:36 am

Gavinmc42 wrote:
Wed Feb 06, 2019 12:16 am
You could probably do it fairly easily using Dispmanx to create a window (not to be confused with an X11 window) above the framebuffer for your waterfall, as long as you don't mind that such a window would be "invisible" to the framebuffer or X11. That way the hardware does the composition as it generates the video display and the window's contents never actually get copied to the famebuffer.
That explains a few things, I was using OpenVG to make a LCARS UI but could not do a screenshot.
The Screenshot software only grabs the framebuffer?

Dispmanx is extremely well documented, not ;)
Time for me to play with Andrew from Melbourne's code to understand it.
I don't get the layers and EGL/Dispmanx stuff yet.
;-) Dispmanx isn't too bad, just reading the C header files tells you a lot and a few things can be gotten from the userland example code in /opt/vc/src/ most of it is fairly intuitive, very little that needs a try-it-and-see-what-happens approach. The main one I'm not sure on is the range of values that layers can take, the framebuffer has a layer of -127 I think, and I've used up to 255 - not that having that many layers would work I think, you're limited at least by the how much time the HVS has to fetch a line (or lines) for each layer and generate the visible line to display and how much memory the HSV has to work with.

There's a function vc_dispmanx_snapshot() that renders what you see into a bitmap resource which you can then read.

Taken from what I use for screen grabs with OpenVG,

Code: Select all

// Take a copy of an area of the entire screen ready for saving
__attribute__((visibility("hidden")))
char *grabScreen(STATE_T *state) {
        uint32_t width = state->screen_width;
        uint32_t height = state->screen_height;
        uint32_t pitch = state->screen_pitch;
        int32_t vc_err;
	char *screen_buffer = malloc((size_t) (pitch * height));
	if (screen_buffer != NULL) {
                DISPMANX_RESOURCE_HANDLE_T screenshot;
                screenshot = vc_dispmanx_resource_create(VC_IMAGE_RGBA32, width, height, (uint32_t*)&vc_err);
                if (screenshot != 0) {
                        vc_err = vc_dispmanx_snapshot(state->dmx_display, screenshot, 0);
                        if (vc_err == 0) {
                                VC_RECT_T area;
                                vc_dispmanx_rect_set(&area, 0, 0, width, height);
                                vc_err = vc_dispmanx_resource_read_data(screenshot, &area, screen_buffer, pitch);
                                if (vc_err != 0) {
                                        fprintf(stderr, "ERROR %s:%d libshapes failed to read vc_resource.\n", __FUNCTION__, __LINE__);
                                }
                        }
                        else {
                                fprintf(stderr, "ERROR %s:%d libshapes failed to take vg_snapshot.\n", __FUNCTION__, __LINE__);
                        }
                        vc_dispmanx_resource_delete(screenshot);
                }
                else {
                        fprintf(stderr, "ERROR: %s:%d libshapes failed to create vc_resource\n", __FUNCTION__, __LINE__);
                        vc_err = -1;
                }
                if (vc_err != 0) {
                        free(screen_buffer);
                        screen_buffer = NULL;
                }
        }
        else {
                fprintf(stderr, "ERROR: %s:%d libshapes failed to allocate memory for screenshot.\n", __FUNCTION__, __LINE__);
        }
	return screen_buffer;
}
She who travels light — forgot something.

User avatar
ab1jx
Posts: 868
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

Re: My es2gears went black

Wed Feb 06, 2019 3:47 am

Paeryn wrote:
Wed Feb 06, 2019 1:36 am
framebuffer for your waterfall, as long as you don't mind that such a window would be "invisible" to the framebuffer or X11. That way the hardware does the composition as it generates the video display and the window's contents never actually get copied to the famebuffer.
For some reason I expected originally to get a second framebuffer on top of the first, like if I were using an LCD display. Then it would go away when my app ended. Sounds like that's what dispmanx would do. But I'm sort of trying to keep this portable to other machines with framebuffers, I have a Rock64 to test on. Isn't dispmanx from Broadom? But my scroll test doesn't work on my Dell laptop, I see no change in the screen at all. It has a /dev/fb0 and fbset looks like it's active. Double buffered maybe? Anyway I don't have a problem backing up the screen and putting it back when I'm done.

Separate issue but same program: I thought I could maybe speed up the scrolling of the waterfall by pre-calculating 2 arrays of pointers to feed to memcpy instead of doing it on the fly. I need to do it thousands of times and the pixel locations won't change. But I don't know how many there will be, that depends on the screen size, so I tried to use dynamic arrays.

So I do

Code: Select all

void *copyto; // for scrolling waterfall, arrays of pointers
void *copyfrom;
// then once I know the screen size I do (stores is the number of pointers I want)
copyto = calloc(stores,sizeof(void *));  // with error checking, not showing that
copyfrom = calloc(stores,sizeof(void *));
// then I set up some initial fixed points (also void *)
copy_to = (void *) fbp + ((appheight - bmarg) * screenstride) + appstride;
copy_from = (void *) copy_to - screenstride; // 1 line up
// and then try to fill my arrays:
for (i=0; i<stores; i++) {
  copyto[i] = (void *) copy_to;
  copyfrom[i] = (void *) copy_from;
  copy_to -= screenstride;
  copy_from -= screenstride;
}
But I get:

Code: Select all

fbsdr.c: In function 'fbinit':
fbsdr.c:110:11: warning: dereferencing 'void *' pointer
     copyto[i] = (void *) copy_to;
           ^
fbsdr.c:110:5: error: invalid use of void expression
     copyto[i] = (void *) copy_to;
     ^~~~~~
fbsdr.c:111:13: warning: dereferencing 'void *' pointer
     copyfrom[i] = (void *) copy_from;
             ^
fbsdr.c:111:5: error: invalid use of void expression
     copyfrom[i] = (void *) copy_from;
I'm not trying to dereference anything. If I change the definition of copyto and copyfrom from void *to char * I get a slightly different error from GCC. I want to make 2 parallel arrays of hundreds of pointers to locations in the framebuffer. I can't find a situation quite like this on Stack Exchange/Stack Overflow or by Googling (alright, Ducky). This is GCC 6.3. copyto and copy_to are different: copy_to is just a void *, same with copy_from. copyto and copyfrom are the arrays.

I think what it's doing is detaching the void from the *. It sweeps the void under the carpet as meaning there's no return value from the expression, then it applies the * as a dereference operator to what comes next on the line.

Oh well, I'll skip the arrays for now at least. If I set copy_to to the address of the bottom left corner of the app, all the other points should be just a series of -= screenstride. Then I copy appstride bytes, do -= screenstride on both source and destination for the memcpy, repeat as needed.

User avatar
ab1jx
Posts: 868
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

Re: My es2gears went black

Wed Feb 06, 2019 6:03 pm

There isn't really a good reason to restore the framebuffer, it's mostly a courtesy to the user. If you just drag a window over the affected area afterwards X generates expose events and repaints the area good as new.
black_1024x768.jpg
black_1024x768.jpg (96.83 KiB) Viewed 1104 times
Here I was backing up the area to memory and I added a bzero() to zero out that area in the framebuffer after I'd copied it to aid in visualizing it. My calculations were right, there's a black 1024x768 rectangle centered in a 1920x1080 screen. Carry on.

User avatar
Paeryn
Posts: 2747
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: My es2gears went black

Thu Feb 07, 2019 1:20 am

ab1jx wrote:
Wed Feb 06, 2019 3:47 am
So I do

Code: Select all

void *copyto; // for scrolling waterfall, arrays of pointers
void *copyfrom;
// then once I know the screen size I do (stores is the number of pointers I want)
copyto = calloc(stores,sizeof(void *));  // with error checking, not showing that
copyfrom = calloc(stores,sizeof(void *));
// then I set up some initial fixed points (also void *)
copy_to = (void *) fbp + ((appheight - bmarg) * screenstride) + appstride;
copy_from = (void *) copy_to - screenstride; // 1 line up
// and then try to fill my arrays:
for (i=0; i<stores; i++) {
  copyto[i] = (void *) copy_to;
  copyfrom[i] = (void *) copy_from;
  copy_to -= screenstride;
  copy_from -= screenstride;
}
But I get:

Code: Select all

fbsdr.c: In function 'fbinit':
fbsdr.c:110:11: warning: dereferencing 'void *' pointer
     copyto[i] = (void *) copy_to;
           ^
fbsdr.c:110:5: error: invalid use of void expression
     copyto[i] = (void *) copy_to;
     ^~~~~~
fbsdr.c:111:13: warning: dereferencing 'void *' pointer
     copyfrom[i] = (void *) copy_from;
             ^
fbsdr.c:111:5: error: invalid use of void expression
     copyfrom[i] = (void *) copy_from;
I'm not trying to dereference anything. If I change the definition of copyto and copyfrom from void *to char * I get a slightly different error from GCC. I want to make 2 parallel arrays of hundreds of pointers to locations in the framebuffer. I can't find a situation quite like this on Stack Exchange/Stack Overflow or by Googling (alright, Ducky). This is GCC 6.3. copyto and copy_to are different: copy_to is just a void *, same with copy_from. copyto and copyfrom are the arrays.
You were attempting to dereference a void*. You declared copyto as being a pointer to void, then you used it as an array by assigning a value to the ith element from it, the array access needs to dereference copyto so you end up with (

Code: Select all

void *copyto = calloc(sizes, sizeof(void *));
copyto[i] = (void *)copy_to;
// The line above is roughly expanded to
// *(copyto + i * sizeof(void)) = copy_to;
As you can see you've asked to write to an address that cannot be computed (void doesn't have a size) and it can't write anything (again, void doesn't have a size, it doesn't exist as concrete type).

It looks like you meant to have copyto be a void**, that could be used as an array of pointers to void.
She who travels light — forgot something.

User avatar
ab1jx
Posts: 868
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

Re: My es2gears went black

Thu Feb 07, 2019 2:29 am

Paeryn wrote:
Thu Feb 07, 2019 1:20 am
[
You were attempting to dereference a void*. You declared copyto as being a pointer to void, then you used it as an array by assigning a value to the ith element from it, the array access needs to dereference copyto so you end up with t looks like you meant to have copyto be a void**, that could be used as an array of pointers to void.
Yeah, I should have done void ** I guess, I wasn't trying to dereference but to store the pointer in an array of pointers. Not sure it's worthwhile since my calculations in the loop are only something like

Code: Select all

for (i=0; i<stores; i++) {
   copy_to -= screenstride;
   copy_from -= screenstride;
   memcpy(copy_to,copy_from,appstride);
}
I'm skipping the array for now, not that hard to put it in later. The best thing would be to do it in GPU assembly language but it would take me a while to learn enough to do that. Pretty simple operation though. But I'll be doing it a lot. I'll try to get it going and profile it and see.

User avatar
Gavinmc42
Posts: 4050
Joined: Wed Aug 28, 2013 3:31 am

Re: My es2gears went black

Sun Feb 10, 2019 12:24 am

A link to this was posted on another post.
http://www.aholme.co.uk/GPU_FFT/Main.htm
For SDR FFTs are important, so it is worth looking at just because it has a good explanation.
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

User avatar
ab1jx
Posts: 868
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

Re: My es2gears went black

Sun Feb 10, 2019 2:58 am

Thank you, that should come with hello_fft or at least a link to it. I've read some of Herman Hermitage's stuff. I have directories marked QPULib, rpi-playground, vc4-gpu-tools, vc4asm, videocoreiv-qpu. Much of it crashes on a Pi 3B because I guess it dates back to a Pi 1 and an address was different. I haven't messed with a lot, part of my objective is to keep this portable to non-Pi machines. I use fftwf for FFTs so far.

In the links section at the end of that is Peter Onion's SDR on you-tube https://youtu.be/0IZxzm4R1bo which I'd seen but misplaced my copy of. Peter said that a good test of your FFT was to feed in a simple sine wave as digitized points and you should get out just 1 or 2 peaks because it's all 1 frequency. His display is fast, I'd forgotten that. Anyway thanks to youtube-dl I have a copy again. An old screenshot of my xlib version looks like:
waterfall1_332.jpg
waterfall1_332.jpg (75.11 KiB) Viewed 1014 times
There are problems with it, including the waterfall, which is why I'm replacing the GUI with a framebuffer version. I don't know what the signal was, it's from last September, looks like cell phone or TV just because it's a wide peak. I'm working strictly with RTL2832 dongles as the RF part because that's what I have, 3 of them. When I tried to copy the RF section of my program into the framebuffer version it keeps telling me that USB device is busy. Usually that's because you have DVB drivers for the dongle, which I don't, and they're blacklisted and all that. I can run the old program or rtl_test and they work, the new version doesn't.

But whilst in the middle of that we had an 8-hour power outage. Now the Debian on my laptop is complaining about some LILO keytable file. The first problem is that the partition needs to have fsck run on it but it doesn't boot far enough to run /forcefsck. And there's no fsck available in the rescue mode of the CD I installed it from, just a busybox. So I just downloaded a live DVD image,I'll try that.

User avatar
ab1jx
Posts: 868
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

Re: My es2gears went black

Mon Feb 11, 2019 3:33 am

I found my bug the old fashioned way, by thinking about it. I hadn't touched this project in most of a year. I knew I needed to init the dongle so I stuck that in and it worked. Then I have a function that starts up my threads so I stuck that in and boom. The thread init starts up the rf function which calls the dongle init. So I was trying to open the dongle twice, the second time it was busy doing what I told it to

User avatar
Gavinmc42
Posts: 4050
Joined: Wed Aug 28, 2013 3:31 am

Re: My es2gears went black

Mon Feb 11, 2019 4:24 am

You have me wondering how hard is it to turn the Pi's RF combo chips into channel scanners.
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

User avatar
ab1jx
Posts: 868
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

Re: My es2gears went black

Mon Feb 11, 2019 11:38 pm

Gavinmc42 wrote:
Mon Feb 11, 2019 4:24 am
You have me wondering how hard is it to turn the Pi's RF combo chips into channel scanners.
Aw, just get your basic $20 dongle like https://www.nooelec.com/store/sdr/sdr-r ... r820t.html I bought a cheaper Chinese knockoff for US $12 which sort of works. The biggest shortcoming that comes to mind is the stability of the crystal, it's not bad for VHF FM etc. but if you use an upconverter on it and try to listen to HF SSB it needs frequent retuning.

At https://www.nooelec.com/store/sdr.html it lists more versions. The tcxo one should drift less. This one: https://www.nooelec.com/store/sdr.html covers
Frequency capability approximately 65MHz-2300MHz, with small frequency gap near 1100MHz
for $36. Another could be a built-in bias tee for driving a preamp, neither of these seems to have that.

User avatar
ab1jx
Posts: 868
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

Re: My es2gears went black

Wed Feb 13, 2019 1:58 am

I gotta stop playing and get back to work. I ran across somebody's Python demo of how to use a Pi framebuffer doing color gradients and thought well, I always wanted to play with gradients too, but in C. For each frame 3 screen edges are selected at random, then a point in a random position om each edge. The screen is filled with pixels, each having its RGB values set by its distance from those 3 points. Which means some calculations but nothing like doing SDR. Only 1 thread, 3 square roots taken for each pixel. The timing, well:

Code: Select all

upstairs# ./grad1
Screen is 1920 x 1080, 4 bytes/pixel
Drew 41472000 pixels in 36 seconds or 1152000.000000 pixels/second.
1.80 seconds per screenfull
thumb2.jpg
thumb2.jpg (16.92 KiB) Viewed 948 times

Code: Select all

/*
  gradients - goofing around.
  
  Each screenful 3 points are picked at random positions on the top, bottom,
  left, right sides.  Every pixel on the screen has its RGB value set by its
  distance from those 3 points.
  
  Compile with: gcc -Wall -g grad1.c -o grad1 -lm
  
*/

#define RUNS 20 // number of screenfuls

#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>        /* for mmap */
#include <sys/ioctl.h>
#include <linux/fb.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <time.h>

char *fbp; // pointer to framebuffer (upper left corner)
char *backp; // screen backup area
uint32_t screenbytes; // screen size in bytes (authoritative)
uint32_t screenstride; // bytes for each scanline of full screen
uint32_t yres; // vertical screen resolution
uint32_t xres; // horizontal screen resolution
uint32_t bpp; // bytes per pixel
uint32_t bitsperpixel; // sometimes you need bits, sometimes bytes
uint32_t speccolor = 0x01f5f600;  // Tektronix scope blue pixel at 32 bpp
float maxdist;

struct point {
  uint16_t x;
  uint16_t y;
};

struct point points[3];

void pickpoints(void) { // pick the 3 points on the axes
  int skip;
  skip = random()/(RAND_MAX/4.0); // pick 0-3  
  switch (skip) {
    case 0 : // skip top
      points[0].x = 0;  // left
      points[0].y = (random()/(RAND_MAX * 1.0)) * yres;
      points[1].y = 0; // top
      points[1].x = (random()/(RAND_MAX*1.0)) * xres;
      points[2].y = yres-1; // right
      points[2].x = (random()/(RAND_MAX*1.0)) * xres;
      break;
    case 1 :  // skip bottom
      points[0].x = 0;  // left
      points[0].y = (random()/(RAND_MAX * 1.0)) * yres;
      points[1].y = 0;  // top
      points[1].x = (random()/(RAND_MAX*1.0)) * xres;
      points[2].y = yres-1;
      points[2].x = (random()/(RAND_MAX*1.0)) * xres;
      break;
    case 2: // skip left
      points[0].x = xres - 1; // right
      points[0].y = (random()/(RAND_MAX * 1.0)) * yres;
      points[1].y = 0;  // top
      points[1].x = (random()/(RAND_MAX*1.0)) * xres;
      points[2].y = 0;  // bottom
      points[2].x = (random()/(RAND_MAX*1.0)) * xres;
      break;
    case 3:  // skip right
      points[0].x = 0;  // left
      points[0].y = (random()/(RAND_MAX * 1.0)) * yres;
      points[1].x = (random()/(RAND_MAX*1.0)) * xres;
      points[1].y = 0; // top
      points[2].x = (random()/(RAND_MAX*1.0)) * xres;
      points[2].y = yres-1; // bottom
  }; // end switch
}

// returns a 0-255 value depending on x,y distance from points[pt]
unsigned char dist(int x, int y, int pt) {
  unsigned char ret = 0;
  float dist = sqrt(((x-points[pt].x)*(x-points[pt].x)) + \
   ((y-points[pt].y) * (y-points[pt].y))); // geometric distance formula
  ret = (dist/maxdist) * 256;
  return ret;
}

void dograd(void) {
  int x,y;
  unsigned char r,g,b;
  for (y=0; y<yres; y++) {
    for (x=0; x<xres; x++) {
      r = dist(x,y,0);
      g = dist(x,y,1);
      b = dist(x,y,2);
      fbp[(y * xres * 4) + (x * 4)] = r;
      fbp[(y * xres * 4) + (x * 4) + 1] = g;
      fbp[(y * xres * 4) + (x * 4) + 2] = b;
    } // for x
  } // for y
}

void restore_screen(void) {  // copy back
  memcpy(fbp,backp,screenbytes);
}

void fbinit(void) { // set up framebuffer, back up screen area
  struct fb_var_screeninfo vinfo; // fetched with an ioctl
  struct fb_fix_screeninfo finfo; // this has smem_len: screen bytes
  int fbfd;  // frame buffer file descriptor
  fbfd = open("/dev/fb0", O_RDWR);
  if (fbfd == -1) {
    fprintf(stderr,"Error opening /dev/fb0\n");
    perror("open ");
    exit(1);
  }
  // get the fixed screen information
  if (ioctl (fbfd, FBIOGET_FSCREENINFO, &finfo)) {
    printf("Error reading fixed information.\n");
    exit(2);
  }
  // get variable screen info
  // each struct (FSCREENINFO and VSCREENINFO) has unique and useful numbers
  if (ioctl (fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
    fprintf(stderr,"Error reading variable screen info struct.\n");
    exit(3);
  }
  xres = vinfo.xres;
  yres = vinfo.yres;
  bpp = vinfo.bits_per_pixel / 8;  
  bitsperpixel = vinfo.bits_per_pixel;
  printf("Screen is %u x %u, %u bytes/pixel\n",xres,yres,bpp);
  screenstride = finfo.line_length;
  screenbytes = finfo.smem_len;
  backp = malloc(screenbytes);
  if (backp == NULL) {
    fprintf(stderr,"Can't malloc %u bytes to back up screen\n",screenbytes);
    exit(1);
  }
  fbp = (char *) mmap(NULL,finfo.smem_len,PROT_READ | PROT_WRITE, MAP_SHARED,fbfd,0);
  if (fbp < 0) { // returns (void *) -1 on error
    fprintf(stderr,"mmap failed\n");
    perror("mmap ");
    exit(1);
  }
  close(fbfd); // don't need anymore
  maxdist = sqrt((xres * xres) + (yres * yres));
  memcpy(backp,fbp,screenbytes); // back up screen
} // end fbinit

int main(void) {
  int q;
  float pps;
  float sps;
  uint32_t pix;
  time_t start_t = time(NULL), done_t;
  srandom(time(NULL)); // seed random generator
  fbinit();
  for (q=0; q<RUNS; q++) {  
    pickpoints(); // set the 3 points to measure from
    dograd(); // do gradients
//    sleep(3); // for photo pause
  }
  restore_screen();
  free(backp);
  munmap(fbp,screenbytes);
  done_t = time(NULL);
  pix = RUNS * xres * yres;
  pps = pix / (done_t - start_t);
  printf("Drew %u pixels in %li seconds or %f pixels/second.\n",pix,\
    (done_t - start_t), pps);
  sps = ((done_t - start_t)*1.0)/(RUNS * 1.0);
  printf("%0.2f seconds per screenful\n",sps);
  return 0;
}
No libraries, no dependencies, doesn't need X but will run with it.

User avatar
Gavinmc42
Posts: 4050
Joined: Wed Aug 28, 2013 3:31 am

Re: My es2gears went black

Wed Feb 13, 2019 3:36 am

Yep, the fun of doing graphics without X11 ;)
Don't know why everyone insists on multi windowing, multi user, multi tscking OS's.
One user, one task, one window is much easier.
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

User avatar
ab1jx
Posts: 868
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

Re: My es2gears went black

Wed Feb 13, 2019 4:23 am

Gavinmc42 wrote:
Wed Feb 13, 2019 3:36 am
Yep, the fun of doing graphics without X11 ;)
Don't know why everyone insists on multi windowing, multi user, multi tscking OS's.
One user, one task, one window is much easier.
Well mostly it just turned out that way. My SDR program has 3 threads already and I haven't even gotten to demodulation. 1 just deals with getting data from the dongle and decimating it, another puts stuff through FFTW, another displays. They have queues between them and handshaking, I've got to get back into it. This was just fun. But it's an approximate test and practice too, if I have 1 thread just drawing I think that will be about right. The dongle puts out about 2 million samples/second, I use about every 64th one last time I worked on it. There are these little fudge factors to try to get the speed of the different sections about matched. Like the number of points I put through the FFT.

But I like the fact that it's in a window and Firefox is in another. That's not going to work very well with a framebuffer, which is why I'd like to find a more X-friendly way to do this, like so it only takes up 1 pane in my pager. If I could go through DRI that might do it. If I tell X I'm doing OpenGL but just use the framebuffer pointer it gives to OpenGL then maybe.

User avatar
ab1jx
Posts: 868
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

Re: My es2gears went black

Thu Feb 14, 2019 12:46 am

Hee-hee, ya gotta try this:

Near the end of fbinit() change this line:
maxdist = sqrt((xres * xres) + (yres * yres));
to
maxdist = sqrt((xres * xres) + (yres * yres))/7.0;

I wasn't happy with the muted colors. They happen because I want to scale the resulting distances from the screen edges to every pixel position. So I used a fraction of the maximum possible which is maxdist = sqrt((xres * xres) + (yres * yres)); But that's bigger than is likely to ever happen so I tried a /2 on it. Now most of the distances are bigger than maxdist, so not only are there more vivid colors, there are hard lines every time they wrap. So there are circles, sort of. I've made that divisor a random between 2 and 33 which changes every frame but that's a more complicated change. I'm also working on having it run until a keypress, then I'll post that. And probably adding a screenshot capability.

They come out like these:
Image
If you look at one of the simple ones, imagine where the centers of the circles are. That's the random point on an axis that the distances are measured from to set the RGB colors. In the more complicated one at the top the divisor on maxdist was bigger, and at some point the distance is an unsigned byte, so it wraps. OK, it's going into 24 bit color with 8 bits each for RGB. I like the effect. But even in the first one you can still see where the 3 points were.
Attachments
test3.jpg
test3.jpg (44.68 KiB) Viewed 889 times

Return to “Troubleshooting”