RoestVrijStaal / grafx2

Automatically exported from code.google.com/p/grafx2
0 stars 0 forks source link

Color cycling #365

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
Animation by shifting the colors of some palette entries.
First requested by jntesteves in issue 31, comment 16.

The effect recently got attention thanks to the slideshow of Mark Ferrari's 
landscapes, made by J Huckaby in HTML+Javascript : 
http://www.effectgames.com/effect/article.psp.html/joe/Old_School_Color_Cycling_
with_HTML5
A very recent interview of Mark J. Ferrari, giving lots of insight on his work 
and tools:
http://www.effectgames.com/effect/article.psp.html/joe/Q_A_with_Mark_J_Ferrari

Some sample IFF images with CRNG chunks are at:
http://www.sitting-ducks.com/~jeff/sachs.html
(Defender of the Crown :-D )

At least these should be importable / viewable in Grafx2. This would make 
Grafx2 one of the few ways to view/edit such images, other than:
* Cosmigo ProMotion (commercial product)
* Werner Randelshofer's ANIMApplet (inline Java) and MultiShow (Java) : 
http://www.randelshofer.ch/blog/archives/191 

A specification of IFF / ILBM is here:
http://www.fine-view.com/jp/labs/doc/ilbm.txt

The minimum feature set we should target:
- Loading any historical file made in Deluxe Paint. (I hope you still have your 
floppy disks around)
- RGB values are all in the palette initially - the animation "only" swaps the 
positions.
- Each cycle is a continuous range, like 4-5-6-7
- Shift is in one direction only: the colors move towards the beginning of the 
palette.
- If several cycles exist, timing of each range is independant from each other.

I just tested Deluxe Paint IV, it seems to allow much more than this, but I 
don't know how it would store the relevant data:
- the range can be non-contiguous and include the same color index more than 
once. I tried a "ping-pong" animation, it somehow works.
- The speed is set using a 1-63 slider. Low is slow, high is fast. Speed is 
possibly 1/slider.
I'm only mentioning it as a reminder that I need to test on Deluxe Paint III, 
and to note one intersting trick of the interface: In DP4, there is only one 
shortcut to start/stop cycling and the ranges are the same that you'd use for 
gradient fill, there is only one additional slider : 0 is non-cycling, any 
other value means that the color will cycle at this speed.

We need to see what is possible about real-time palette changes, in order to 
have working animation while you're drawing. If SDL allows it easily: it will 
probably work, otherwise: animation will be frozen while you're drawing and 
only resume when you release mouse.

Original issue reported on code.google.com by yrizoud on 1 Aug 2010 at 10:14

GoogleCodeExporter commented 8 years ago
Here is a proof-of concept patch that uses an SDL_Thread to change the palette. 
It just changes color 0 from black to red slowly. Seems to work well.

Original comment by pulkoma...@gmail.com on 2 Aug 2010 at 8:36

Attachments:

GoogleCodeExporter commented 8 years ago
I used your POC to make a test where Grafx2 cycles all the ranges defined as 
Gradients (with a fixed speed). It looks very sweet to be able to dynamically 
change these, and see the result instantly. I watched all the Defender of the 
Crown pictures, and the old "Waterfall" picture of Deluxe Paint.The speed is 
impressive, in idle mode the program eats barely more CPU time than usual. (And 
this is still on my Pentium3 600MHz)

I tested DP2E, the MS-DOS version. It is able to handle 16 gradients/cycles per 
image, speed is set by a 1-63 slider again, and the direction of the shift can 
be chosen (not in the ILBM spec).

One annoying thing we'll have to do is to attach all this cycling data to 
history steps, for Undo/Redo for example. This is necessary so old cycle 
settings are not inherited when you Load another image, or when you undo the 
Load, swap to the spare, etc. This will make the cycling data more difficult to 
reach from the other thread, as it will need to be cleanly protected from 
concurrent access (Reading from  Main_backups->Pages->things is not safe at all 
times).
Some windows must freeze the cycling while they're open, and resume it 
afterwards, for ex. the Palette.
The window where you design the cycling colors, on the contrary, must keep it 
going - Changing the speed, especially, must activate the cycling so the user 
sees the result.

So all in all, lots of small details but I don't see any serious roadblocks.

About file formats, we can always import IFF/ILBM. We must decide which formats 
to save the cycling information.
IFF is possible if the cycles are within DeluxePaint limits (ie: continuous, no 
more than 16...)
GIF is possible with a custom-made extension block
PNG is possible with a custom data block.

Original comment by yrizoud on 2 Aug 2010 at 10:55

GoogleCodeExporter commented 8 years ago
The function I used for my test:
http://pastebin.com/miSxSew3

Original comment by yrizoud on 2 Aug 2010 at 10:58

GoogleCodeExporter commented 8 years ago
Found a better IFF/ILBM specification here, that describes which bit performs 
reverse cycling: http://netghost.narod.ru/gff/vendspec/iff/iff.txt

The formula for rate is : freq = rate * 60 / 1384
The DP2E speed slider is very weird, slider is rate/78, with some rounding 
errors between loading and saving that I can't really explain (its 64, save, 
re-load, it's 63). Anyway, DP2E allows speeds between 0.2856Hz and 18.28Hz, in 
0.2856Hz increments. We should only find files with this range of speeds and 
this granularity, even though the standard would allow rates between 0.00366Hz 
and 240Hz, in 0.00366Hz increments.

Original comment by yrizoud on 4 Aug 2010 at 12:26

GoogleCodeExporter commented 8 years ago

Original comment by pulkoma...@gmail.com on 9 Aug 2010 at 9:46

GoogleCodeExporter commented 8 years ago
I think it's a good idea to add a link to the sumary of the IFF format on the 
GFF (Encyclopedia of graphics file formats). There are links for the Spec 
Yrizoud posted before and sample images too.

http://netghost.narod.ru/gff/graphics/summary/iff.htm

Although, I opened some of these samples on an hex editor and they look like 
very basic ILBM files with no color cycling. They have a CAMG chunk too.

All the amiga .IFF files I have seen have an ILBM chunk on offset 0x8, right 
before BMHD. The .LBM files saved by GrafX2 have an PBM chunk instead, that I 
think is the same as Deluxe Paint. I don't know if it makes any difference or 
point that the image uses a different standard or version or something. Joseph 
Huckaby's lbm2json tool completely ignores what is writen on this offset, 
jumping right to BMHD. Anyway, I think what Deluxe Paint do is the right think 
to do for most uses anyway.

Original comment by jntesteves on 9 Aug 2010 at 6:27

GoogleCodeExporter commented 8 years ago
This summary is incomplete for my purpose, so I linked the other resources 
instead.
I had tested all those samples: Venus is 8bit and loads correctly in Grafx2, 
but the image looks very weird (same in XnView), others are 24bit RGB files and 
don't load correctly in Grafx2.

About the chunk "ILBM" vs "PBM ", Grafx2 accepts only those two values, and has 
different loading code for the two formats. It saves with "PBM ", the comment 
saying "Planar BitMap".
Deluxe Paint 2E on MS-DOS can read both ILBM and PBM variants, but saves PBM 
with 256 colors - just like Grafx2.

I can't easily test with files on Deluxe Paint 3 :/ I thought Grafx2 produced 
compatible files (as long as less than 32 colors were used), but now I doubt 
DP3 will read a PBM file.

Original comment by yrizoud on 9 Aug 2010 at 7:39

GoogleCodeExporter commented 8 years ago
I realize that my answer sounded harsh, it was not my intention. I was rather 
bitter because many things are undocumented, old documents are false, and it's 
very difficult to understand why Deluxe Paint sets some of the reserved bytes.

To recapitulate:
Images like those of the Amiga era were made on Deluxe Paint III. They are 
ILBM, almost always 16 or 32 colors, but they could also be 64-color with some 
palette restriction (EHB) or full 4096-color with severe pixel restrictions 
(HAM). About color cycling, I'm using images from "Defender of the Crown" as 
samples, since I already know how they work.
I also found the Waterfall from DP3 sample disk.
All the files I found had 4 ranges, with a maximum of 2 of them cycling. 
(non-cycling ranges are only useful during drawing, they indicate the available 
gradients)

Most old specifications of IFF / ILBM refer to the above format.

Mark Ferrari's incredible landscapes were made on DP2E. There is only one 
format there: 256-color, PBM files. Such files (cycling or not) are the ones 
made on the PC platform; I expect we can find more in the archives of the 
demoscene. No real specification exists, but we can study how DP2E writes 
files, it still runs perfectly on Windows (well, on 98).

Grafx2 loads all these formats, though I have some serious doubts about HAM. 
When you save as "lbm" it saves in PBM format with 256-color palette, just like 
DP2E.

I've checked, and DP2E can load ILBM files as well, but the saving is always 
PBM. It also always saves 16 ranges, even if many of them are unused.

--> We can only find a maximum of 16 cycling ranges in an image. No need to 
support more in Grafx2

Original comment by yrizoud on 10 Aug 2010 at 1:09

GoogleCodeExporter commented 8 years ago
It's all logical. Both versions of DP save the pixel data as it is in memory. 
The amiga use planes, while the PC has a linear video ram format.

As for the ranges, I'm not sure what one could do with more than 16 ranges 
anyway. I think it's rare that the whole palette gets cycled, and you need 
multiple colors in the shade so that the effect works.

Note : it also works for Amstrad CPC's pictures, here we have 16 ranges of 16 
colors each. But no one uses them anyway :)

Original comment by pulkoma...@gmail.com on 10 Aug 2010 at 8:11

GoogleCodeExporter commented 8 years ago
Implemented and working in r1574. It behaves identically to Deluxe paint III 
(Amiga) and 2E (PC) : In the settings for gradients, there is a slider for 
cycle speed. Every color range that has a non-zero speed will cycle.

Saving as ILBM saves only the cycling ranges: those with more than one color, 
and a non-zero speed. Similarly, loading an ILBM file skips those that don't 
follow these rules; (Deluxe paint would save even non-cycling ranges as 
settings.)

I know it looks not-very-ambitious compared to all complicated things we could 
think of, but I preferred getting something out while the HTML5 experiment is 
still fresh - and we're compatible with Deluxe Paint files as input and output, 
so this looks very functional.

The classic usage for cycling is a scrolling, but I realized there's more: If 
all colors of a range are unused except one, the range will describe the color 
changes of a single ink. So you can do any pulsating effect. A more complicated 
usage: With 256 colors split in 16 ranges, you can make a 16-color picture that 
passes through 16 different palettes over time.

I disabled cycling while a window is open, because it's almost impossible to 
edit the palette, for example. Like in Deluxe Paint, the cycling temporarily 
resumes while you hold the speed slider, so you get immediate feedback when you 
change the speed.

Note that you can have several cycles that share some colors. When this 
happens, the colors from range #1 that are not overwritten by range #2 cycle on 
the whole range, while the colors of range #2 cycle over range #2. I'm having 
difficulty checking if Deluxe Paint behaves this way, but honestly I don't see 
how they could do any other way.

TODO:
The gradient screen needs to be made better-looking. I think I may need to 
implement a horizontal slider for it. A "Cycle on/off" button must be added 
anyway. Mouse wheel should browse the 16 ranges.

A shortcut for this button must be added. And the cycling must be off by 
default, I think.

Save the cycling in GIF too (?)

Save the cycling in PNG too (?)

The system is a bit hackish, because the loaded cycles overwrite (some of) the 
gradient settings; the gradient settings are saved without speed so the colors 
don't cycle when you start grafx2 later; (not necessarily a bad thing); and if 
you 'reload' the config it overwrites the current image's cycles. Need to think 
about this.

Original comment by yrizoud on 16 Aug 2010 at 4:09

GoogleCodeExporter commented 8 years ago
Re: saving cycling info in PNG:
I think we could just save an ancillary 'cCYC' chunk (I'm suggesting cCYC to 
follow the lead of http://r0k.us/graphics/png16Tech.html#colorCycling) in 
exactly the same format as the ILBM color cycling saving.

GIF seems to require a bit more care, but we might be able to do the same thing.

Original comment by 00a...@gmail.com on 17 Aug 2010 at 12:16

GoogleCodeExporter commented 8 years ago
Ok about cCYC.

After some testing, I realize I also need to make separate cycling data for the 
Main page and the Spare page.

Original comment by yrizoud on 17 Aug 2010 at 12:18

GoogleCodeExporter commented 8 years ago
Sample image to test various ways to use the effects: glowing goo, various 
blinking lights, rotating gear, conveyor belt, and my favorite, the flowing 
water.

Original comment by yrizoud on 17 Aug 2010 at 4:32

Attachments:

GoogleCodeExporter commented 8 years ago
I load the above file. It does one step of color cycling and then immediately 
crashes:

grafx2: xcb_io.c:140: dequeue_pending_request: Assertion `req == 
dpy->xcb->pending_requests' failed.
fish: Job 1, './bin/grafx2 ' terminated by signal SIGABRT (Abort)

making a color cycling range using the gradients dialog and then adjusting the 
speed to be >0 , results in a similar crash (again, after some cycling has 
already shown up).

OS+package details:

Arch Linux
SDL 1.2.14
libxcb 1.6
xorg-server 1.8.1

Original comment by 00a...@gmail.com on 18 Aug 2010 at 2:37

GoogleCodeExporter commented 8 years ago
Looks like a threading problem, xlib doesn't seem to accept the way we update 
the screen from 2 threads without any mutual exclusion.
On Windows, once every 5 minutes I get some display problems when opening 
screens (missing refresh or palette that isn't completely reset), but they 
never cause any crash.
I have an idea of how to change the cycling system so it no longer requires 
threading.

Original comment by yrizoud on 18 Aug 2010 at 8:59

GoogleCodeExporter commented 8 years ago
00ai99 : Please try r1581, I attach a tgz to this post. It's incomplete, for 
example the colors are not visually put back in the right slots when you open 
the palette, otherwise it should totally eliminate the unstability. I'll 
continue tomorrow.

Original comment by yrizoud on 19 Aug 2010 at 12:38

Attachments:

GoogleCodeExporter commented 8 years ago
oh thanks yrizoud (though I just updated from SVN, since it was quicker.). That 
works perfectly OK for me, no crashes :)
I did notice though that perhaps the code that finds a best-matching color 
should prefer colors that are not being cycled.. as with the lbm you provided, 
some area of the palette surrounds flashes red every so often :)

Original comment by 00a...@gmail.com on 19 Aug 2010 at 2:47

GoogleCodeExporter commented 8 years ago
btw, isn't it possible to have the cycling thread only flag which gradients 
need to be cycled, rather than doing the cycling? Then in the main loop,you'd 
have to wake up MAX_HZ times per second, do any pending cycling and unflag 
those gradients.
This is AFAIK the 'standard' way to do such things. We only really want cycling 
on the main editing screen, right? Not any subordinate dialogs.

Original comment by 00a...@gmail.com on 19 Aug 2010 at 2:56

GoogleCodeExporter commented 8 years ago
00ai99: About best-matching colors, you mean the colors chosen for menu? Indeed 
I must try to avoid it, even if I know that in many cases, there won't be 
enough colors remaining.
About the implementation of timing, the 16 cycling ranges can have different 
speeds that are not multiples of each others, so I couldn't rely on a fixed 
frequency. Instead, I opted for a "delta time" system: Get current time, 
compute which positions the gradients should be according to this time, and if 
any of them is not the same as the last screen update, an update of the palette 
and screen is required. This whole system must be called frequently enough to 
get smooth movement, but not too often in order to limit CPU cost.
We can even easily add the interpolation system described by J Huckaby - but 
it's optional, because it will force a full screen update at least 50 times per 
second.

Original comment by yrizoud on 19 Aug 2010 at 10:00

GoogleCodeExporter commented 8 years ago
I'd add a thread for each cycling gradient, updating a struct with the info, 
and let main actually set the palette when there is time for that.
The struct would of course need locking with a mutex, because of all those 
threads writiing to it.

Original comment by pulkoma...@gmail.com on 19 Aug 2010 at 12:35

GoogleCodeExporter commented 8 years ago
...seriously? You'd make 16 extra threads increment counters, instead of the 
single line of code:
  new_offset=(now-start)/(int)(1000.0/(Gradient_array[i].Speed*0.2856)) % len;
(input.c:943)

Original comment by yrizoud on 19 Aug 2010 at 1:35

GoogleCodeExporter commented 8 years ago
Depends on what we want. Threads that sleep don't cost anything. If we start to 
add colorblending and such stuff it may be good to have separate threads when 
we use very slow cycling.

On the other hand, maybe I just spent too much time coding for HAiku and start 
seeing threads everywhere :)

Original comment by pulkoma...@gmail.com on 19 Aug 2010 at 8:20

GoogleCodeExporter commented 8 years ago
If you just HAVE to use a thread somewhere, you can make Grafx2 still 
responsive while color reduction is under way: Delegate worker thread to 
perform the conversion, while main thread receives all events and can redraw 
mouse cursor.

Original comment by yrizoud on 19 Aug 2010 at 9:38

GoogleCodeExporter commented 8 years ago
Yes, I was thinking about that too. Once I get some free time to dig into it 
and do it properly (you don't want to fix all the bugs, do you ?)

Original comment by pulkoma...@gmail.com on 20 Aug 2010 at 12:02

GoogleCodeExporter commented 8 years ago
[deleted comment]
GoogleCodeExporter commented 8 years ago

Original comment by pulkoma...@gmail.com on 22 Aug 2010 at 1:39

GoogleCodeExporter commented 8 years ago
By r1598, gradient screen is much improved. "Cycle on/off" button is there, 
mouse wheel browses the 16 ranges, keyboard shortcut is there (default: 
Control+Tilde), helpfile is up-to-date, cycling is off by default.

Cycling data is not saved in GIF or PNG, but I'm not sure if I should do it.
Still need to separate cycling data so the Main and spare images have their 
own. Functions of "Copy to spare" should transfer cycling data when you choose 
to copy palette.

Original comment by yrizoud on 30 Aug 2010 at 12:09

GoogleCodeExporter commented 8 years ago
I need cycling in gif/png :)
It's the only way to have cycling+layers...

Original comment by pulkoma...@gmail.com on 30 Aug 2010 at 9:03

GoogleCodeExporter commented 8 years ago
GIF was done (some time ago)
PNG will need to use some black magic formulas like:
For read:  png_set_read_user_chunk_fn(),
For write: png_set_unknown_chunks()

Original comment by yrizoud on 18 Jan 2011 at 8:11

GoogleCodeExporter commented 8 years ago
TODO:
- save/load cycling data in PNG format
- on program startup, when loading default palette from skin: load cycling data 
from skin too. Then update all skins so that they have good ranges according to 
their palettes.

Original comment by yrizoud on 8 Feb 2011 at 10:52

GoogleCodeExporter commented 8 years ago
Almost done in r1715.
Works veeery well on startup, it picks up the gradient settings from your 
selected skin, and puts it in the "noname" pictures that you're editing by 
default.

Still has problem when loading an image from command-line, gradient is black 
until you open the grad settings window. Anyway, issue is probably larger than 
that, because now there are many causes for changing gradient settings (load, 
undo, redo, swap) and yet the Load_gradient_data() is only called by the 
settings window.

Original comment by yrizoud on 9 Feb 2011 at 1:11

GoogleCodeExporter commented 8 years ago
Solved now.

Original comment by yrizoud on 16 Feb 2011 at 5:39

GoogleCodeExporter commented 8 years ago
A technical reminder, since I had to search this information today :
Internally, Grafx2 stores Speed as the "speed slider" value, same as Deluxe 
Paint.
0 = stopped
1 = IFF 78 = 0.2856 Hz freq = 3501 ms delay
64 = IFF 4991 = 18.28 Hz freq = 55 ms delay
70 = IFF 5461 = 20 Hz freq = 50 ms delay
Above is not usable in Deluxe Paint 3 (?)
105 = IFF 8192 = 30 Hz freq = 33.3 ms delay
Above is not usable in Grafx2, slider only goes up to 105
210 = IFF 16384 = 60 Hz freq = 16.6 ms delay

Original comment by yrizoud on 30 Aug 2013 at 5:40