diff --git a/data/Tupfile.lua b/data/Tupfile.lua
index ed0e05416e..30dfc5aa15 100644
--- a/data/Tupfile.lua
+++ b/data/Tupfile.lua
@@ -138,6 +138,7 @@ extra_files = {
{"kolibrios/3D/info3ds/TL_SYS_16.PNG", PROGS .. "/develop/info3ds/tl_sys_16.png"},
{"kolibrios/3D/info3ds/TOOLBAR.PNG", PROGS .. "/develop/info3ds/toolbar.png"},
{"kolibrios/3D/info3ds/FONT8X9.BMP", PROGS .. "/fs/kfar/trunk/font8x9.bmp"},
+ {"kolibrios/3D/m2view/", "common/3d/md2view/*"},
{"kolibrios/3D/voxel_editor/VOXEL_EDITOR", PROGS .. "/media/voxel_editor/trunk/voxel_editor"},
{"kolibrios/3D/voxel_editor/CURSORS.PNG", PROGS .. "/media/voxel_editor/trunk/cursors.png"},
{"kolibrios/3D/voxel_editor/CURSORS_GR.PNG", PROGS .. "/media/voxel_editor/trunk/cursors_gr.png"},
@@ -194,6 +195,7 @@ extra_files = {
{"kolibrios/games/phenix", PROGS .. "/games/phenix/trunk/phenix"},
{"kolibrios/games/soko/soko", PROGS .. "/games/soko/trunk/SOKO"},
{"kolibrios/games/soko/", "common/games/soko/*"},
+ {"kolibrios/ggrafx2/", "common/media/grafx2/*"},
{"kolibrios/drivers/ahci/", "common/drivers/ahci/*"},
{"kolibrios/drivers/atikms/", "common/drivers/atikms/*"},
{"kolibrios/drivers/i915/", "common/drivers/i915/*"},
@@ -225,7 +227,7 @@ extra_files = {
{"kolibrios/settings/app_plus.ini", "common/settings/app_plus.ini"},
{"kolibrios/utils/appearance", PROGS .. "/cmm/appearance/appearance.com"},
{"kolibrios/utils/calcplus", PROGS .. "/other/calcplus/calcplus"},
- {"kolibrios/utils/fNav/", "common/File Managers/fNav*"},
+ {"kolibrios/utils/fNav/", "common/File Managers/fNav/*"},
}
if build_type == "rus" then tup.append_table(extra_files, {
{"Docs/cp866/config.txt", build_type .. "/docs/CONFIG.TXT"},
diff --git a/data/common/3d/md2view/md2.cfg b/data/common/3d/md2view/md2.cfg
new file mode 100644
index 0000000000..a8f4df9f9f
--- /dev/null
+++ b/data/common/3d/md2view/md2.cfg
@@ -0,0 +1,15 @@
+# ‘¨â ªá¨á:
+# [/path/]file_mod_1.md2 [[/path/]file_text_1.pcx]
+# ‚ ª ç¥á⢥ ¯ã⨠¯®¤¤¥à¦¨¢ îâáï ¯®¤ª â «®£¨ ⥪ã饣® ª â «®£ à ᯮ«®¦¥¨ï ¢ìà .
+# ।¯®« £ ¥âáï, çâ® ¥á«¨ ¤«ï ä ©« ⥪áâãàë ¥ 㪠§
+# ¯ãâì â® ® à ᯮ«®¦¥ ¢ ⮬ ¦¥ ª â «®£¥ çâ® ¨ ä ©« ¬®¤¥«¨.
+# ।¯®« £ ¥âáï â ª ¦¥ çâ®, ¥á«¨ ¤«ï ä ©« ¬®¤¥«¨ ¥ 㪠§ ¯ãâì â® ® 室¨âáï ¢ ⮬ ¦¥
+# ª â «®£¥ çâ® ¨ ¢ìà.
+
+ /md2_model/Ogros.md2 #¬®¤¥«ì ®àª
+ md2_model/Weapon.md2 #¬®¤¥«ì ®à㦨ï
+
+ #md2_model/TRIS.MD2 DRFREAK.PCX
+
+ #/hd2/1/TEMP/quake_2/QUAKE2/BASEQ2/players/female/tris.md2 athena.pcx
+ #/hd2/1/TEMP/quake_2/QUAKE2/BASEQ2/players/female/w_disrupt.md2 /athena.pcx
diff --git a/data/common/3d/md2view/md2View b/data/common/3d/md2view/md2View
new file mode 100644
index 0000000000..63d2b4f66b
Binary files /dev/null and b/data/common/3d/md2view/md2View differ
diff --git a/data/common/3d/md2view/md2_model/Ogro.txt b/data/common/3d/md2view/md2_model/Ogro.txt
new file mode 100644
index 0000000000..012f5a3381
--- /dev/null
+++ b/data/common/3d/md2view/md2_model/Ogro.txt
@@ -0,0 +1,132 @@
+
+
+10/11/98
+================================================================
+Model Name : Ogro
+installation directory : quake2/baseq2/players/ogro
+
+Model Author : Magarnigal
+ email :mellor@netgazer.net.au
+ homepage :http://magarn.3dpalette.com
+
+
+Skins Author : Ogro_Fix
+ email : ogro_fix@yahoo.com
+ homepage : http://www.fondation.com/pandemonium
+
+
+Additional info : Fix has included a cloth and ogrobase skin that people may use as a base for skinning ogro. The
+ mapping can be a bit confusing, 2d skinners beware.
+
+
+
+Additional skin by : Deranged - (Sharokh)
+ email : deranged@deathsdoor.com
+ homepage : http://www.gibbed.com/thebin
+
+
+
+Ogrific law:
+
+An Ogro is similar to an ogre, but why the O?? In French ogre is pronounced ogrr, not oger, also 'gros' means to be fat/big...so ogre - gros makes Ogro! After all, it's cuter than just ogre :).
+
+The Ogro is smaller than a normal ogre, but just as fat and only slightly less stupid. As opposed to common belief, he is not an evil creature, only killing as is absolutely necessary.
+
+Imagine, if you will, an Ogro in the subway. As he enters the carriage, people become plastered to the walls due to Ogro's size. He's too big! Ahh well, maybe Ogro should just sit on one of these tiny seats...a strange sound can be heard (sproottch). Imagine now, the look of an astonished ogro...huh?!? Standing up, he looks at the seat. OMFO! (oh my fucking ogrod!)! Where a little old grandma had been sitting, now looks more like a pizza! But, I swear, it was a grandma!
+
+This sort of occurrence is common to an Ogro...its in their nature. when you want to talk to an ogro, stay at good distance (better if you call him from behind), as his movements are rather "uncontrolled", and you might cop a smash in the face!
+
+A special diet is required for Ogros. When they're babies (ogrillons), their mum's (ogresses) give them copious amounts of chili con carne, canned beans and the like. The result of this carefully structured diet produces an Ogro capable of immense farting achievements.
+
+The ogro is just full of bad smells. That's not to say he's not clean. An ogro will wash himself often (doesn't fear water as opposed to trolls). The problem lies in his intestinal flora (and fauna). all his orifices (ogrifices?) are used for ogrific spells. (green clouds, etc..). Nothing can be done against this kind of onslaught, except to run or stop breathing.
+
+It's a useful trait to have in the subway. people will often leave the carriage, trying to find the culprit. (Although all that is need it to look at the face of the ogro: he's
+happy, he farted well, and it smells really bad).
+
+
+Ogro's existed long before humans. In fact, one school of thought holds to the theory that humans are a degenerated version of Ogro, more intelligent, but evil.
+
+
+Hidden Ogro's of today include: Obelix, fat boy slim, any Sumo, Amish (braveheart movie), slamfist (small soldiers movie), bud spencer, etc...
+
+
+
+
+
+Additional Credits to : id software, Howzer, Rod,
+
+
+
+================================================================
+* Play Information *
+
+New Sounds : Yes, original sounds from Eric vonRothkirch of 'The Coven', email him
+ at redchurch@uswest.net
+
+CTF Skins : yes
+VWEP Support : yes, standard Id weapons with modified, ogrific skins.
+
+
+* Construction *
+Poly Counts
+Tris.md2 : 670
+weapon.md2 : 122
+
+Vert Counts
+tris.md2 : 358 - anyone actually need this number?
+weapon.md2 : 74
+
+Skin Wastage
+tris.md2 : 1%
+weapon.md2 : 2%
+
+Skin Count : 14
+
+Base : none
+
+
+A background to some of the skins:
+
+Grok: the standard cyberpunk ogro
+
+freedom: this one is dedicated to braveheart.
+
+gib: this one didn't know how to use the ogrific mixer (used to make mammoth steaks), and jumped into it..the mixer did all the work.
+
+slaanesh: this one is an evil ogro, serving the god of lust.
+
+Khorne: this one is even more evil than slaanesh, serving the god of blood and violence.
+
+Nabogro: it's a dwarf (yeah, well, it was), he's a trollslayer. In french nabot means small/dwarfy, so, nabogro went logically.
+
+darkam: hoo, this one is a troll! (yes, he's green, and ogros hate green), but even if he's green it's the best friend of the original, the true ogro.
+
+Igdosh: played with a nuclear missile, thinking it was a cigar...
+
+Gorash: he's evil too, has been created by the doctor ogrenstein. but this is the prototype, the real ogrenstein is under construction for now.
+
+Arboshak: He felt in love with 3CPO and tried to cyborgize himself. but finally preferred R2D2, but too late
+
+
+
+Editors used:-
+
+Modelling/animation : Lightwave 5.5
+
+Converting/clean-up : QME, Q2modeler
+
+Skinning/mapping : NST, Paint shop Pro
+
+
+
+Build/Animation time : longer than expected. 5-10 hours per skin.
+
+
+* How to use this model *
+
+extract to quake2\baseq2\players\ogro run quake2, send us money, frag other players.
+
+
+* Copyright / Permissions *
+
+QUAKE(R) and QUAKE II(R) are registered trademarks of id Software, Inc.
\ No newline at end of file
diff --git a/data/common/3d/md2view/md2_model/Ogros.md2 b/data/common/3d/md2view/md2_model/Ogros.md2
new file mode 100644
index 0000000000..7c29dddb36
Binary files /dev/null and b/data/common/3d/md2view/md2_model/Ogros.md2 differ
diff --git a/data/common/3d/md2view/md2_model/Weapon.md2 b/data/common/3d/md2view/md2_model/Weapon.md2
new file mode 100644
index 0000000000..560facea70
Binary files /dev/null and b/data/common/3d/md2view/md2_model/Weapon.md2 differ
diff --git a/data/common/3d/md2view/md2_model/Weapon.pcx b/data/common/3d/md2view/md2_model/Weapon.pcx
new file mode 100644
index 0000000000..0254648ab7
Binary files /dev/null and b/data/common/3d/md2view/md2_model/Weapon.pcx differ
diff --git a/data/common/3d/md2view/md2_model/igdosh.pcx b/data/common/3d/md2view/md2_model/igdosh.pcx
new file mode 100644
index 0000000000..e105a37162
Binary files /dev/null and b/data/common/3d/md2view/md2_model/igdosh.pcx differ
diff --git a/data/common/3d/md2view/readme.txt b/data/common/3d/md2view/readme.txt
new file mode 100644
index 0000000000..cbc47bde91
--- /dev/null
+++ b/data/common/3d/md2view/readme.txt
@@ -0,0 +1,7 @@
+md2View v0.01 - Âüþâåð md2 ôàéëîâ ìîäåëåé èãðû Quake2.
+Ôàéëû ìîäåëåé è òåêñòóð çàäàþòñÿ â êîíôèãóðàöèîííîì ôàéëå md2.cfg.
+Ïîääåðæèâàþòñÿ òåêñòóðû òîëüêî â ôîðìàòå pcx c 8 áèò ãëóáèíîé öâåòà.
+
+iadn
+http://www.iadn.narod.ru
+iadn@bk.ru
diff --git a/data/common/media/grafx2/fonts/5pxtinyfont.png b/data/common/media/grafx2/fonts/5pxtinyfont.png
new file mode 100644
index 0000000000..c9f4d9b300
Binary files /dev/null and b/data/common/media/grafx2/fonts/5pxtinyfont.png differ
diff --git a/data/common/media/grafx2/fonts/8pxfont.png b/data/common/media/grafx2/fonts/8pxfont.png
new file mode 100644
index 0000000000..76196a7bcf
Binary files /dev/null and b/data/common/media/grafx2/fonts/8pxfont.png differ
diff --git a/data/common/media/grafx2/fonts/GrafX2_Black.gif b/data/common/media/grafx2/fonts/GrafX2_Black.gif
new file mode 100644
index 0000000000..221e3abb60
Binary files /dev/null and b/data/common/media/grafx2/fonts/GrafX2_Black.gif differ
diff --git a/data/common/media/grafx2/fonts/GrafX2_Dark.gif b/data/common/media/grafx2/fonts/GrafX2_Dark.gif
new file mode 100644
index 0000000000..96e4982a55
Binary files /dev/null and b/data/common/media/grafx2/fonts/GrafX2_Dark.gif differ
diff --git a/data/common/media/grafx2/fonts/PF_Arma_5__.png b/data/common/media/grafx2/fonts/PF_Arma_5__.png
new file mode 100644
index 0000000000..9bbc3aff4a
Binary files /dev/null and b/data/common/media/grafx2/fonts/PF_Arma_5__.png differ
diff --git a/data/common/media/grafx2/fonts/PF_Easta_7_.png b/data/common/media/grafx2/fonts/PF_Easta_7_.png
new file mode 100644
index 0000000000..753b49556b
Binary files /dev/null and b/data/common/media/grafx2/fonts/PF_Easta_7_.png differ
diff --git a/data/common/media/grafx2/fonts/PF_Easta_7__.png b/data/common/media/grafx2/fonts/PF_Easta_7__.png
new file mode 100644
index 0000000000..d7ce2a3d78
Binary files /dev/null and b/data/common/media/grafx2/fonts/PF_Easta_7__.png differ
diff --git a/data/common/media/grafx2/fonts/PF_Ronda_7__.png b/data/common/media/grafx2/fonts/PF_Ronda_7__.png
new file mode 100644
index 0000000000..429e5cc130
Binary files /dev/null and b/data/common/media/grafx2/fonts/PF_Ronda_7__.png differ
diff --git a/data/common/media/grafx2/fonts/PF_Tempesta_5.png b/data/common/media/grafx2/fonts/PF_Tempesta_5.png
new file mode 100644
index 0000000000..cec49bdaa4
Binary files /dev/null and b/data/common/media/grafx2/fonts/PF_Tempesta_5.png differ
diff --git a/data/common/media/grafx2/fonts/PF_Tempesta_5_.png b/data/common/media/grafx2/fonts/PF_Tempesta_5_.png
new file mode 100644
index 0000000000..75ee879546
Binary files /dev/null and b/data/common/media/grafx2/fonts/PF_Tempesta_5_.png differ
diff --git a/data/common/media/grafx2/fonts/PF_Tempesta_5__.png b/data/common/media/grafx2/fonts/PF_Tempesta_5__.png
new file mode 100644
index 0000000000..fdd54e185b
Binary files /dev/null and b/data/common/media/grafx2/fonts/PF_Tempesta_5__.png differ
diff --git a/data/common/media/grafx2/fonts/PF_Tempesta_5___.png b/data/common/media/grafx2/fonts/PF_Tempesta_5___.png
new file mode 100644
index 0000000000..c75db671bf
Binary files /dev/null and b/data/common/media/grafx2/fonts/PF_Tempesta_5___.png differ
diff --git a/data/common/media/grafx2/fonts/PF_Tempesta_7.png b/data/common/media/grafx2/fonts/PF_Tempesta_7.png
new file mode 100644
index 0000000000..6db5e0f649
Binary files /dev/null and b/data/common/media/grafx2/fonts/PF_Tempesta_7.png differ
diff --git a/data/common/media/grafx2/fonts/PF_Tempesta_7_.png b/data/common/media/grafx2/fonts/PF_Tempesta_7_.png
new file mode 100644
index 0000000000..fe387ae744
Binary files /dev/null and b/data/common/media/grafx2/fonts/PF_Tempesta_7_.png differ
diff --git a/data/common/media/grafx2/fonts/PF_Tempesta_7__.png b/data/common/media/grafx2/fonts/PF_Tempesta_7__.png
new file mode 100644
index 0000000000..56ab7c7b39
Binary files /dev/null and b/data/common/media/grafx2/fonts/PF_Tempesta_7__.png differ
diff --git a/data/common/media/grafx2/fonts/PF_Tempesta_7___.png b/data/common/media/grafx2/fonts/PF_Tempesta_7___.png
new file mode 100644
index 0000000000..64b77ad193
Binary files /dev/null and b/data/common/media/grafx2/fonts/PF_Tempesta_7___.png differ
diff --git a/data/common/media/grafx2/fonts/PF_Westa_7_.png b/data/common/media/grafx2/fonts/PF_Westa_7_.png
new file mode 100644
index 0000000000..e6c4f95ca1
Binary files /dev/null and b/data/common/media/grafx2/fonts/PF_Westa_7_.png differ
diff --git a/data/common/media/grafx2/fonts/PF_Westa_7__.png b/data/common/media/grafx2/fonts/PF_Westa_7__.png
new file mode 100644
index 0000000000..df086afcff
Binary files /dev/null and b/data/common/media/grafx2/fonts/PF_Westa_7__.png differ
diff --git a/data/common/media/grafx2/fonts/Tuffy.ttf b/data/common/media/grafx2/fonts/Tuffy.ttf
new file mode 100644
index 0000000000..8ea647090f
Binary files /dev/null and b/data/common/media/grafx2/fonts/Tuffy.ttf differ
diff --git a/data/common/media/grafx2/fonts/colorfont.pcx b/data/common/media/grafx2/fonts/colorfont.pcx
new file mode 100644
index 0000000000..d91ab8aff1
Binary files /dev/null and b/data/common/media/grafx2/fonts/colorfont.pcx differ
diff --git a/data/common/media/grafx2/gfx2.gif b/data/common/media/grafx2/gfx2.gif
new file mode 100644
index 0000000000..44c6a7fcb3
Binary files /dev/null and b/data/common/media/grafx2/gfx2.gif differ
diff --git a/data/common/media/grafx2/gfx2.png b/data/common/media/grafx2/gfx2.png
new file mode 100644
index 0000000000..00ea1f6839
Binary files /dev/null and b/data/common/media/grafx2/gfx2.png differ
diff --git a/data/common/media/grafx2/gfx2def.ini b/data/common/media/grafx2/gfx2def.ini
new file mode 100644
index 0000000000..6e84261577
--- /dev/null
+++ b/data/common/media/grafx2/gfx2def.ini
@@ -0,0 +1,420 @@
+###### GrafX2 initialization file ###### Fichier d'initialisation de GrafX2 ##
+# # # #
+# You may modify this file with any # Vous pouvez modifier ce fichier avec #
+# standard ASCII text editor. # n'importe quel éditeur de texte #
+# # ASCII standard. #
+# # #
+# Comments are preceded by ';' or # Les commentaires sont précédés par #
+# '#'. # ';' ou '#'. #
+# # #
+# Options are not case sensitive and # Les options ne sont pas sensibles à #
+# spaces are ignored. # la casse et les espaces sont ignorés.#
+# # #
+# You must not change the order of # Vous ne devez pas changer l'ordre #
+# the sections and their options. # des sections et de leurs options. #
+# You must not delete or put into # Vous ne devez pas effacer ou mettre #
+# comment any section nor option. # en commentaire une section ou option.#
+# # #
+# Each option is preceded by a # Chaque option est précédée par un #
+# comment which explains its meaning. # commentaire qui explique sa fonction.#
+# # #
+##############################################################################
+
+
+
+[MOUSE] # [SOURIS]
+
+ ; The sensitivity of the mouse can | La sensibilité de la souris peut
+ ; take values from 1 to 4. The | prendre des valeurs de 1 à 4. Plus
+ ; smaller values, the faster. | les valeurs sont petites, plus c'est
+ ; This only takes effect in | rapide. Ce paramétrage n'est utilisé
+ ; fullscreen modes. | que dans les modes "plein écran".
+ ; |
+ X_sensitivity = 1 ; (default 1)
+ Y_sensitivity = 1 ; (default 1)
+
+ ; Unused setting, only kept for compatibility.
+ X_correction_factor = 0 ; (default 0)
+ Y_correction_factor = 0 ; (default 0)
+
+ ; Aspect of the main cursor (cross) | Aspect du curseur principal (croix)
+ ; 1: Solid | 1: Solide
+ ; 2: Transparent | 2: Transparent
+ ; 3: Thin (solid) | 3: Fin (solide)
+ Cursor_aspect = 1 ; (default 1)
+
+[MENU] # [MENU]
+
+ ; Colors of the menus (the black | Couleurs des menus (la couleur noire
+ ; and the white colors cannot be | et la couleur blanche ne peuvent pas
+ ; modified). | être modifiées).
+ ; Values are in {Red,Green,Blue} | Les valeurs sont dans l'ordre {Rouge,
+ ; order and are between 0 and 63. | Vert,Bleu} et vont de 0 à 63.
+ Light_color = 42,42,42 ; (default 42,42,42)
+ Dark_color = 27,27,27 ; (default 27,27,27)
+ ;
+ ; Light_color = 24,25,30 ; \_ Nightmare
+ ; Dark_color = 13,14,19 ; /
+ ;
+ ; Light_color = 10,45,28 ; \_ Forest
+ ; Dark_color = 5,27,12 ; /
+ ;
+ ; Light_color = 48,41,26 ; \_ Gold
+ ; Dark_color = 26,22,15 ; /
+ ;
+ ; Light_color = 10,40,55 ; \_ Oceanic
+ ; Dark_color = 10,20,32 ; /
+
+ ; Aspect ratio and size of the | Proportion des menus et de la barre
+ ; menus and the tool-bar. | d'outils.
+ ; Possible values: | Valeurs possibles:
+ ; 0: Do not adapt (pixels are not | 0: Ne pas adapter (les pixels ne sont
+ ; stretched) | pas étirés)
+ ; 1: Adapt the menus and the tool- | 1: Adapter les menus et la barre
+ ; bar according to the resolution| d'outils suivant la résolution
+ ; 2: Slightly adapt the ratio of | 2: Adapter légèrement les proportions
+ ; the menus and tool-bar | des menus et de la barre d'outils
+ ; -1:Do not adapt (like 0) | -1:Ne pas adapter (comme 0)
+ ; -2:Stretch by x2 maximum | -2:Etire au double de taille si possible
+ ; -3:Stretch by x3 maximum | -3:Etire au triple de taille si possible
+ ; -4:Stretch by x4 maximum | -3:Etire au quadruple de taille si
+ ; | possible.
+ Menu_ratio = -2 ; (default -2)
+
+[FILE_SELECTOR] # [SELECTEUR_DE_FICHIERS]
+
+ ; Show hidden files and | Afficher les fichiers et répertoires
+ ; directories (values are 'yes' or | cachés (les valeurs sont 'yes' ou
+ ; 'no'). | 'no').
+ Show_hidden_files = no ; (default 'no')
+ Show_hidden_directories = no ; (default 'no')
+
+ ; Delay before displaying a preview | Délai avant d'afficher une preview
+ ; in file-selectors (in 18.2th of | dans les sélecteurs de fichiers (en
+ ; second). Possible values range | 18.2èmes de seconde) Les valeurs
+ ; from 1 to 256. | possibles vont de 1 à 256.
+ Preview_delay = 8 ; (default 8)
+
+ ; Maximize the preview of the | Maximiser la preview des images pour
+ ; pictures so that it is as big as | qu'elle soit aussi grande que
+ ; possible. If you're not in the | possible.
+ ; same resolution as the picture's | Si vous n'êtes pas dans la même réso-
+ ; one, it can try to correct the | lution que celle de l'image, cela peut
+ ; aspect ratio, but if the picture | essayer de corriger les proportions,
+ ; does not fill the whole screen, | mais si l'image ne prend pas tout
+ ; it can be worse. | l'écran, cela peut être pire.
+ Maximize_preview = no ; (default 'no')
+
+ ; This option is used to place the | Cette option est utilisée pour placer
+ ; selection bar on a filename by | la barre de sélection sur un nom de
+ ; typing its first letters. | fichier en tapant ses 1ères lettres.
+ ; For example, if you want to find | Par exemple, si vous voulez trouver le
+ ; the "PICTURE.PKM" in a directory | fichier "PICTURE.PKM" dans un réper-
+ ; that also contains "PALETTE.PAL", | toire contenant également le fichier
+ ; you'll just have to type P and I. | "PALETTE.PAL", vous n'aurez qu'à taper
+ ; The different values of "FFF" | P puis I.
+ ; indicate if you want to find the | Les different valeurs de "FFF"
+ ; name in both files and directories| indiquent si vous voulez trouvez le nom
+ ; or just in only one of these: | dans les fichiers ET les répertoires ou
+ ; 0: files and directories | simplement dans l'un OU l'autre.
+ ; 1: files only | 0: fichiers et répertoires
+ ; 2: directories only | 1: fichiers seulement
+ ; | 2: répertoires seulement
+ Find_file_fast = 0 ; (default 0)
+
+
+[LOADING] # [CHARGEMENT]
+
+ ; Automatically set the resolution | Passer automatiquement dans la bonne
+ ; when loading a picture. | résolution lors du chargement d'une
+ ; You should set this value to | image.
+ ; 'yes' after disabling the video | Vous devriez définir cette option à
+ ; modes that are not supported by | 'yes' après avoir inhibé les modes
+ ; your video card or monitor. | vidéo qui ne sont pas supportés par
+ ; | votre matériel.
+ Auto_set_resolution = no ; (default 'no')
+
+ ; If the variable above is set to | Si la variable ci-dessus est à 'yes',
+ ; 'yes', this one tells if you want | celle-ci indique si vous voulez
+ ; to set the resolution according | définir la résolution suivant:
+ ; to: | 1: les dimensions de "l'écran
+ ; 1: the internal "original screen" | d'origine" internes à l'image
+ ; dimensions of the picture | 2: les véritables dimensions de
+ ; 2: the actual dimensions of the | l'image
+ ; picture |
+ Set_resolution_according_to = 1 ; (default 1)
+
+ ; If you load a picture with a | Si vous chargez une image ayant une
+ ; palette of less than 256 colors, | palette de moins de 256 couleurs,
+ ; this option defines if you want | cette option indique si vous souhaitez
+ ; to clear the palette or to keep | effacer la palette ou bien conserver
+ ; the colors of the previous | les couleurs de l'image précédente qui
+ ; picture that are over the number | se situent au-delà du nombre de la
+ ; of colors of the new picture. | nouvelle image.
+ ; For example, if you load a | Par exemple, si vous chargez une image
+ ; 32-color picture, the colors 32 | de 32 couleurs, les couleurs 32 à 255
+ ; to 255 will be set to black if | seront passées en noir si cette option
+ ; this option is set to 'yes', or | est à 'yes', ou bien elles resteront
+ ; they will be kept unchanged if | inchangées si elle est à 'no'.
+ ; this option is set to 'no'. |
+ Clear_palette = yes ; (default 'yes')
+
+
+[MISCELLANEOUS] # [DIVERS]
+
+ ; Draw the limits of the picture. | Afficher les limites de l'image
+ Draw_limits = yes ; (default 'yes')
+
+ ; Adjust the brush grabbing in | Ajuster la capture de brosse en mode
+ ; "grid" mode. | "grille".
+ Adjust_brush_pick = yes ; (default 'yes')
+
+ ; Coordinates: | Coordonnées:
+ ; 1: Relative | 1: Relatives
+ ; 2: Absolute | 2: Absolues
+ Coordinates = 1 ; (default 1)
+
+ ; Create a backup file when saving. | Créer un fichier backup lors des
+ ; | sauvegardes.
+ Backup = no ; (default 'no')
+
+ ; Number of pages stored in memory | Nombre de pages stockées en mémoire
+ ; for "undoing". | destinées à annuler les dernières
+ ; Values are between 1 and 99. | modifications. Valeurs entre 1 et 99.
+ Undo_pages = 20 ; (default 20)
+
+ ; Speed of the scroll-bars (in VBLs | Vitesse des barre de défilement (en
+ ; waited) while clicking with the | VBLs attendus) lorsque l'un des
+ ; left or right button of the mouse.| boutons de la souris est enfoncé.
+ ; Values can be between 1 and 255. | Les valeurs sont comprises entre 1 et
+ ; The bigger values, the slower. | 255. Plus elles sont grandes, plus
+ ; | c'est lent.
+ Gauges_scrolling_speed_Left = 10 ; (default 10)
+ Gauges_scrolling_speed_Right = 3 ; (default 3)
+
+ ; Automatically save the configu- | Enregistre automatiquement la configu-
+ ; ration when exiting the program. | ration lorsqu'on quitte le programme.
+ Auto_save = yes ; (default 'yes')
+
+ ; Maximum number of vertices used | Nombre maximum de vertex utilisés dans
+ ; in filled polygons and polyforms, | les polygônes et polyformes pleins, et
+ ; and lasso. Possible values range | le lasso. Les valeurs possibles vont
+ ; from 2 to 16384. | de 2 à 16384.
+ ; Each vertex takes 4 bytes. | Chaque vertex prend 4 octets.
+ Vertices_per_polygon = 1024 ; (default 1024)
+
+ ; Automatically zoom into the | Zoomer automatiquement la zone pointée
+ ; pointed area when you press the | par la souris lorsque vous appuyez sur
+ ; short-key of the Magnifier button | la touche de raccourci de la loupe.
+ ; while being above the picture. |
+ Fast_zoom = yes ; (default 'yes')
+
+ ; Separate the colors in the tool- | Séparer les couleurs dans la barre
+ ; bar by a black squaring. | d'outils par un quadrillage noir.
+ Separate_colors = no ; (default 'no')
+
+ ; Initial value of the feedback for | Valeur initiale du "feedback" pour les
+ ; the drawing modes (cf. docs). | modes de dessin (cf. docs).
+ FX_feedback = yes ; (default 'yes')
+
+ ; When you reduce the palette or | Si vous réduisez la palette ou "zappez"
+ ; "zap" some colors out of it, it is| quelques couleurs, il est possible
+ ; possible that there are not enough| qu'il ne reste pas assez de couleurs
+ ; colors left to draw the menus. | pour afficher les menus. Mettre cette
+ ; Switching the following variable | variable à 'yes' ramènera automatiquent
+ ; on will bring back the colors of | les couleurs du menu s'il reste moins
+ ; the menu if there are less than 4 | de 4 couleurs après une "réduction" ou
+ ; colors left after "reducing" or | un "zapping".
+ ; "zapping". |
+ Safety_colors = yes ; (default 'yes')
+
+ ; Display a message at startup | Afficher un message au démarrage
+ ; telling the version number of the | indiquant le numéro de version du
+ ; program. | programme.
+ Opening_message = yes ; (default 'yes')
+
+ ; Take the Stencil into account when| Prendre le Stencil en compte lorsqu'on
+ ; clearing the image. | efface l'image.
+ Clear_with_stencil = yes ; (default 'yes')
+
+ ; Directly set the discontinuous | Passer automatiquement en mode de
+ ; freehand drawing mode after brush | dessin discontinu après la prise d'une
+ ; grabbing. | brosse.
+ Auto_discontinuous = no ; (default 'no')
+
+ ; Save the screen dimensions in GIF | Sauver les dimensions de l'écran dans
+ ; files. If you want to read these | les fichiers GIF. Si vous voulez lire
+ ; files with Photoshop or Alchemy, | ces fichiers avec Photoshop ou Alchemy,
+ ; and maybe some other programs, you| et peut-être d'autres programmes, vous
+ ; must set this option to 'no'. | devez mettre cette option à 'no'.
+ Save_screen_size_in_GIF = no ; (default 'no')
+
+ ; Automaticaly count the number of | Compter automatiquement le nombre de
+ ; different colors used when opening| couleurs différentes utilisées lors de
+ ; the palette editor window. (Set it| d'ouverture de la fenêtre d'édition de
+ ; to 'no' if you have a slow PC or | la palette. (Mettez-le à 'no' si vous
+ ; if you edit huge pictures) | avez un PC lent ou bien si vous éditez
+ ; | d'énormes images).
+ Auto_nb_colors_used = yes ; (default 'yes')
+
+ ; Default video mode at startup | Mode vidéo par défaut au
+ ; (see the list by running the | démarrage (voir la liste en lançant
+ ; program with argument "/?". | le programme avec l'option "/?".
+ Default_video_mode = window ; (default 'window')
+
+ ; Window dimensions. The program | Dimensions de la fenêtre en mode
+ ; remembers the last window size. | fenêtré.
+ Default_window_size = 640,480 ; (default '640,480')
+
+ ; This setting allows you merge successive mouse movements into a single
+ ; mouse movement. You should only use it if you are using a mouse which
+ ; reports at 200Hz or more, and you experience lag when using discontinuous
+ ; hand-drawing with large brushes (this tool tries to paste the brush and
+ ; update the screen on each new mouse position) In this case, set this to 2
+ ; or more, to ignore some intermediate mouse reports when a more recent one
+ ; is present.
+ ; Note that with a value superior to 1, you lose precision with continuous
+ ; hand-drawing, as intermediate mouse positions are skipped.
+ Merge_movement = 0 ; (default 0)
+
+ ; Number of columns in the palette of the menu bar. Can be any number from
+ ; 1 to 256. If there is not enough room, the program will display less
+ ; columns. But your preference will be kept, and as soon as there is more
+ ; space in the screen, more columns will be shown.
+ ;
+ Palette_Cells_X = 16; (Default 16)
+
+ ; Number of lines in the palette of the menu. Can be any number from
+ ; 1 to 16. The menu can always display the number of lines you request.
+ ;
+ Palette_Cells_Y = 4; (Default 4)
+
+ ; Bookmarked directories. Leave the directory blank for unused ones.
+ ;
+ Bookmark_label =
+ Bookmark_directory =
+
+ Bookmark_label =
+ Bookmark_directory =
+
+ Bookmark_label =
+ Bookmark_directory =
+
+ Bookmark_label =
+ Bookmark_directory =
+
+ ; In the classic layout, the palette in the menu has colors from left to
+ ; right. If you prefer the colors ordered top to bottom, set this option
+ ; to YES.
+ ;
+ Palette_vertical = YES; (Default YES)
+
+ ; The program remembers the last window position, if the
+ ; OS isn't able to do it by itself. (ie: Windows)
+ Window_position = 9999,9999; (Default 9999,9999 which means: NA)
+
+ ; This is the time (in milliseconds) between two clicks for Grafx2 to
+ ; recognize a double-click. Double-click is used mostly in the palette
+ ; area of the menu: double-click a color to open the palette.
+ Double_click_speed = 500; (Default 500)
+
+ ; When you press two digit keys in rapid succession (ex: 3 8), Grafx2
+ ; sets transparency to 38% (instead of 30% then 80%). This setting
+ ; allows you to set the maximum delay between two keypresses for
+ ; GrafX2 to recognize them as a combo.
+ Double_key_speed = 500; (Default 500)
+
+ ; Name of the skinfile you want to | Nom du fichier skin que vous voulez
+ ; use. | utiliser.
+ ; Default : (empty to let the program choose)
+ Skin_file =
+
+ ; Name of the font file (8x8) you | Nom du fichier police de caractère
+ ; want to use. | 8x8 utilisée dans les menus.
+ ; Default : (empty to let the program choose)
+ Font_file =
+
+ ; This determines the color value for the grid. Each pixel of
+ ; the grid will be displayed by XOR-ing the original color with
+ ; the value of this setting.
+ ; For example, if you always paint 16-color images, you can set it
+ ; to 16 so the color of the grid are 16 for 0, 17 for 1, etc.
+ ; Then you can set colors 16-31 as lighter/darker variants
+ ; of your original palette, resulting in a pretty grid !
+ ;
+ ; Valid values are 1 to 255.
+ Grid_XOR_color = 255; (Default 255)
+
+ ; This records the last pixel ratio used, to restore it on start.
+ ; Valid values are from 0 to 7 for: Simple, Wide, Tall, Double,
+ ; Triple, Wide2, Tall2, Quadruple.
+ ;
+ Pixel_ratio = 0; (Default 0)
+
+ ; This records the visibility of toolbars, to restore them on start.
+ ; It's a bitfield, where 1=Status, 2=Layers/Animation, 4=Tools
+ ;
+ Menubars_visible = 255; (Default 255)
+
+ ; This enables a mode where right mouse buttons acts as
+ ; a color picker, with most tools.
+ ;
+ Right_click_colorpick = NO; (Default NO)
+
+ ; When this mode is active, scrolling the view (and the magnifier view)
+ ; affects both the main image and the spare page - as long as they have
+ ; the same dimensions.
+ ;
+ Sync_views = YES; (Default YES)
+
+ ; This setting determines which key inverts the mouse buttons
+ ; when it's held : A left click is then interpreted as a right-click.
+ ; It's especially useful for one-button controllers,
+ ; such as touchscreens and tablets.
+ ; Possible values are 0 (none), 1 (control), 2 (alt)
+ ;
+ Swap_buttons = 1; (Default 1)
+
+ ; Last directory browsed with the script selector.
+ ; Leave blank to initially start in (data directory)/scripts
+ ;
+ Scripts_directory =
+
+ ; When this setting is disabled, and you create a shortcut with a key that
+ ; is already associated to another shortcut, Grafx2 will unset the latter.
+ ; If you enable this mode, Grafx2 will not make such check, so you can design
+ ; shortcuts that trigger several actions at once.
+ ;
+ Allow_multi_shortcuts = no; (Default no)
+
+ ; Determines if the Tilemap tool should identify tiles that are mirrored
+ ; version of other tiles, on the X axis.
+ ;
+ Tilemap_detect_mirrored_x = no; (Default no)
+
+ ; Determines if the Tilemap tool should identify tiles that are mirrored
+ ; version of other tiles, on the Y axis.
+ ;
+ Tilemap_detect_mirrored_y = no; (Default no)
+
+ ; Determines if the Tilemap tool should identify tiles that are reversed
+ ; versions of other tiles (180°).
+ ;
+ Tilemap_detect_mirrored_xy = no; (Default no)
+
+ ; Determines if the Tilemap tool should show the number of distinct tiles
+ ; at the end of analysis.
+ ;
+ Tilemap_count = no; (Default no)
+
+ ; Enables the virtual keyboard when the user enters a textbox.
+ ;
+ ; 0=Auto (guess), 1=ON, 2=OFF
+ Use_virtual_keyboard = 0; (Default 0)
+
+ ; Indicates if new images should by default use layers. The alternative
+ ; is animation frames.
+ ;
+ Default_mode_layers = no; (Default no)
+
+ ; end of configuration
diff --git a/data/common/media/grafx2/grafx2 b/data/common/media/grafx2/grafx2
new file mode 100644
index 0000000000..4209618d1b
Binary files /dev/null and b/data/common/media/grafx2/grafx2 differ
diff --git a/data/common/media/grafx2/scripts/_tst_AAFilter.lua b/data/common/media/grafx2/scripts/_tst_AAFilter.lua
new file mode 100644
index 0000000000..224e1a2860
--- /dev/null
+++ b/data/common/media/grafx2/scripts/_tst_AAFilter.lua
@@ -0,0 +1,30 @@
+-- Apply a kind of AA filter on picture
+
+-- Get the picture size
+w, h = getpicturesize();
+
+-- Here is the filtering matrix
+ matrix = {
+ { 0, -1, 0 },
+ { -1, 5, -1 },
+ { 0, -1, 0 }};
+
+-- Loop trough all the pixels
+-- To make this script simpler we don't handle the picture borders
+-- (the matrix would get pixels outside the picture space)
+-- for var = start_value, end_value, step do ...
+for y = 1, h - 2, 1 do
+ for x = 1, w - 2, 1 do
+ filtered =
+ matrix[1][1] * getbackuppixel(x - 1, y - 1) +
+ matrix[1][2] * getbackuppixel(x , y - 1) +
+ matrix[1][3] * getbackuppixel(x + 1, y - 1) +
+ matrix[2][1] * getbackuppixel(x - 1, y ) +
+ matrix[2][2] * getbackuppixel(x , y ) +
+ matrix[2][3] * getbackuppixel(x + 1, y ) +
+ matrix[3][1] * getbackuppixel(x - 1, y + 1) +
+ matrix[3][2] * getbackuppixel(x , y + 1) +
+ matrix[3][3] * getbackuppixel(x + 1, y + 1);
+ putpicturepixel(x,y,filtered);
+ end
+end
diff --git a/data/common/media/grafx2/scripts/_tst_GradientBrush.lua b/data/common/media/grafx2/scripts/_tst_GradientBrush.lua
new file mode 100644
index 0000000000..a8804d1de7
--- /dev/null
+++ b/data/common/media/grafx2/scripts/_tst_GradientBrush.lua
@@ -0,0 +1,7 @@
+w, h = getbrushsize()
+
+for x = 0, w - 1, 1 do
+ for y = 0, h - 1, 1 do
+ putbrushpixel(x, y, (x+y)%256);
+ end
+end
diff --git a/data/common/media/grafx2/scripts/_tst_Settings.lua b/data/common/media/grafx2/scripts/_tst_Settings.lua
new file mode 100644
index 0000000000..7f5bfe6f31
--- /dev/null
+++ b/data/common/media/grafx2/scripts/_tst_Settings.lua
@@ -0,0 +1,33 @@
+-- Test LUA inputbox
+-- this script tests the inputbox
+
+w, h = getbrushsize()
+--[[
+messagebox(
+ "Forecolor: " .. getforecolor() .. "\n" ..
+ "Backcolor: " .. getbackcolor() .. "\n" ..
+ "Transparent color: " .. gettranscolor() .. "\n" ..
+ "Brush dimensions: " .. w .. "x" .. h
+)
+]]
+
+
+ok, w, h = inputbox("Modify brush",
+ "RGB", 1, 0, 1, -1,
+ "HSV", 0, 0, 1, -1,
+ "HSL", 0, 0, 1, -1,
+ "Width", w, -900.0,900.0, 3,
+ "Height", h, -900.0,900.0, 4,
+ "X Flip", 0, 0, 1, 0,
+ "Y Flip", 0, 0, 1, 0,
+ "Degrees",1, 0, 1, -2,
+ "Radians",0, 0, 1, -2
+);
+if ok == true then
+ messagebox(
+ "w: " .. w .. "\n" ..
+ "h: " .. h .. "\n"
+ )
+end
+
+
diff --git a/data/common/media/grafx2/scripts/_tst_dialog.lua b/data/common/media/grafx2/scripts/_tst_dialog.lua
new file mode 100644
index 0000000000..a49bbdbaa2
--- /dev/null
+++ b/data/common/media/grafx2/scripts/_tst_dialog.lua
@@ -0,0 +1,66 @@
+local counter=0;
+local printcounter = function ()
+ windowprint(10,54, string.format("% .3d", counter));
+end;
+
+windowopen(100,150, "Dialogtest");
+windowbutton(6, 18, 54, 14, "Close", 27); -- 1, shortcut=ESC
+windowrepeatbutton(6, 38, 14, 14, "+"); -- 2
+windowrepeatbutton(26, 38, 14, 14, "-"); -- 3
+windowbutton(6, 70, 54, 14, "Help"); -- 4
+windowinput(6, 88, 10);
+printcounter();
+
+repeat
+ local button, button2, key = windowdodialog();
+
+ if button == 2 then -- "+"
+ counter=counter+1;
+ printcounter();
+ end
+ if button == 3 then -- "-"
+ counter=counter-1;
+ printcounter();
+ end
+ if button == 4 then -- "Help"
+ messagebox("Help screen");
+ end
+until key == 27 or button == 1;
+windowclose();
+
+
+-- messagebox(tostring(button) .. " " .. tostring(button2));
+
+
+---- Open_window(149,118,"Grid");
+-- Display_cursor();
+-- Hide_cursor();
+---- Close_window();
+---- Update_window_area(0,0,Window_width, Window_height);
+---- clicked_button=Window_clicked_button();
+--
+-- -- standard button
+---- Window_set_normal_button(12,92,51,14,"Cancel",0,1,KEY_ESC); -- 1
+-- -- repeatable button (while held)
+---- Window_set_repeatable_button(202,43,13,11,"-",0,1,SDLK_LAST); -- 8
+-- -- text input
+-- Window_set_input_button(29,24,3); -- 3
+-- Window_input_content(input_x_button,str);
+-- Readline(31,26,str,3,INPUT_TYPE_INTEGER);
+--
+-- -- dropdown
+-- Window_set_dropdown_button(216, 158, 84,14,84,"Preset...", 0,0,1,RIGHT_SIDE|LEFT_SIDE,1);
+-- Window_dropdown_clear_items(Button);
+-- Window_dropdown_add_item(Button,0,"Set");
+--
+-- -- vertical scroller
+-- mix_scroller = Window_set_scroller_button(31,20,84,256,1,Main_backups->Pages->Gradients->Range[Current_gradient].Mix);
+-- Window_draw_slider(mix_scroller);
+--
+-- -- display
+---- Print_in_window(11,26, "X:",MC_Dark,MC_Light);
+---- Print_in_window_limited(Button->Pos_X+3+10,Button->Pos_Y+2,Config.Bookmark_label[bookmark_number],8,MC_Black,MC_Light);
+-- Window_display_frame_in( 6, 21,110, 52);
+-- Window_display_frame(6,17,130,37);
+-- Window_rectangle(panel->Pos_X, panel->Pos_Y, panel->Width, panel->Height+1, MC_Light);
+
diff --git a/data/common/media/grafx2/scripts/_tst_dialog2.lua b/data/common/media/grafx2/scripts/_tst_dialog2.lua
new file mode 100644
index 0000000000..de7708ea65
--- /dev/null
+++ b/data/common/media/grafx2/scripts/_tst_dialog2.lua
@@ -0,0 +1,33 @@
+--
+-- test of GUI library
+--
+run("libs/gui.lua")
+
+local counter = gui.label{x=10, y=54, value=0, format="% .3d"}
+local form = gui.dialog{
+ title="Dialogtest",
+ w=100,
+ h=150,
+ counter,
+ gui.button{ label="+",
+ x=6, y=38, w=14, h=14, repeatable=true, click=function()
+ counter.value=counter.value+1;
+ counter:render();
+ end},
+ gui.button{ label="-",
+ x=26, y=38, w=14, h=14, repeatable=true, click=function()
+ counter.value=counter.value-1;
+ counter:render();
+ end},
+ gui.button{ label="Help",
+ x=6, y=70, w=54, h=14, click=function()
+ messagebox("Help screen");
+ end},
+ gui.button{ label="Close",
+ x=6, y=18, w=54, h=14, key=27, click=function()
+ return true; -- causes closing
+ end},
+}
+
+form:run()
+
diff --git a/data/common/media/grafx2/scripts/libs/gui.lua b/data/common/media/grafx2/scripts/libs/gui.lua
new file mode 100644
index 0000000000..00b48364a7
--- /dev/null
+++ b/data/common/media/grafx2/scripts/libs/gui.lua
@@ -0,0 +1,111 @@
+--
+-- Event-driven GUI library
+--
+--
+
+gui = {
+
+--
+-- dialog() --
+--
+dialog = function(args)
+ local dia = {
+ title = args.title,
+ w = args.w,
+ h = args.h,
+ --
+ widgets = {},
+ -- an indexed array, starting at 1. Used for calling the relevant
+ -- callback when a numbered control is clicked.
+ callbacks = {},
+
+ --
+ -- dialog.run() --
+ --
+ run = function(this)
+ windowopen(this.w,this.h, this.title or "");
+ -- examine all elements
+ for _,widget in ipairs(this.widgets) do
+ widget:render()
+ end
+
+ repeat
+ local button, button2, key = windowdodialog();
+
+ if button > 0 then
+ local c = this.callbacks[button]
+ -- run the callback and stop the form if it returns true
+ if c ~= nil and c(this) then
+ break;
+ end
+ end
+ until key == 27;
+ windowclose();
+ end
+ }
+ local id = 1;
+ -- examine all elements
+ for _,value in ipairs(args) do
+ -- all arguments that are tables are assumed to be widgets
+ if type(value)=="table" then
+ table.insert(dia.widgets, value)
+ -- clickable widgets take up an auto-numbered id
+ if (value.click) then
+ dia.callbacks[id] = value.click
+ id=id+1
+ end
+ end
+ end
+ return dia;
+end,
+
+--
+-- button() --
+--
+button = function(args)
+ local but = {
+ x = args.x,
+ y = args.y,
+ w = args.w,
+ h = args.h,
+ key = args.key,
+ label = args.label,
+ click = args.click or donothing,
+ render = args.repeatable and function(this)
+ windowrepeatbutton(this.x, this.y, this.w, this.h, this.label, this.key or -1);
+ end
+ or function(this)
+ windowbutton(this.x, this.y, this.w, this.h, this.label, this.key or -1);
+ end
+ }
+ return but;
+end,
+
+--
+-- label() --
+--
+label = function(args)
+ local fld = {
+ x = args.x,
+ y = args.y,
+ value = args.value,
+ format = args.format,
+ fg = args.fg or 0,
+ bg = args.bg or 2,
+ render = function(this)
+ if type(this.format) then
+ windowprint(this.x, this.y, string.format(this.format, this.value), this.fg, this.bg);
+ else
+ windowprint(this.x, this.y, this.value, this.fg, this.bg);
+ end
+ end
+ }
+ return fld;
+end,
+
+
+-- "do nothing" function. Used as default callback
+donothing = function(this)
+end
+
+}
diff --git a/data/common/media/grafx2/scripts/samples_2.4/brush/ApplyColor.lua b/data/common/media/grafx2/scripts/samples_2.4/brush/ApplyColor.lua
new file mode 100644
index 0000000000..39ea024942
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/brush/ApplyColor.lua
@@ -0,0 +1,129 @@
+--BRUSH Remap: Apply PenColor
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+
+-- Copyright 2010 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+run("../libs/dawnbringer_lib.lua")
+
+OK,tin,clz,fade,amt,brikeep,falloff,nobg,nopen,briweight = inputbox("Apply PenColor 2 Brush",
+
+ "1. Tint", 1, 0,1,-1,
+ "2. Colorize", 0, 0,1,-1,
+ "BG->FG color Fade", 0, 0,1,0,
+ "AMOUNT % (0-100)", 100, 0,100,0,
+ "Preserve Brightness", 1, 0,1,0,
+ "Bri/Dark FallOff", 1, 0,1,0,
+ "Exclude Background", 1,0,1,0,
+ "Exclude PenColor", 0,0,1,0,
+ "ColMatch Bri-Weight %", 25, 0,100,0
+);
+
+
+if OK == true then
+
+ function cap(v) return math.min(255,math.max(v,0)); end
+
+ w, h = getbrushsize()
+
+
+ fg = getforecolor()
+ bg = getbackcolor()
+ fR,fG,fB = getcolor(fg)
+ bR,bG,bB = getcolor(bg)
+
+ pal = db.fixPalette(db.makePalList(256))
+ if nobg == 1 then
+ pal = db.stripIndexFromPalList(pal,bg) -- Remove background color from pallist
+ end
+ if nopen == 1 then
+ pal = db.stripIndexFromPalList(pal,fg) -- Remove Pencolor from pallist
+ end
+
+
+ amtA = amt / 100
+ amtR = 1 - amtA
+
+ -- Normalize Pen Color
+ lev = (fR+fG+fB)/3
+ fR = fR - lev
+ fG = fG - lev
+ fB = fB - lev
+
+ ---------------------------------------------------
+ -- Colorize (Colourant) (just apply colorbalance)
+ -- Tint (make grayscale and apply colorbalance)
+ --
+ -- I think it should be the other way around since colorize is the process of adding color to B&W film...
+ -- But this is the what Brilliance and others call it
+ --
+ if clz == 1 or tin == 1 then
+ cols = {}
+ for n = 0, 255, 1 do
+
+ r,g,b = getcolor(n)
+ a = db.getBrightness(r,g,b)
+
+
+ mR,mG,mB = fR,fG,fB
+
+ -- Fade between bg & fg pencolor across dark-bright
+ if fade == 1 then
+ lf = a / 255
+ lr = 1 - lf
+ mR = bR*lr + fR*lf
+ mG = bG*lr + fG*lf
+ mB = bB*lr + fB*lf
+ lev = (mR+mG+mB)/3
+ mR = mR - lev
+ mG = mG - lev
+ mB = mB - lev
+ end
+
+ fr,fg,fb = mR,mG,mB
+
+
+ if brikeep == 1 then
+ -- Loose Brightness preservation (ex: applying full red to dark colors)
+ brin = db.getBrightness(cap(r+mR),cap(g+mG),cap(b+mB))
+ itot = brin - a
+ fr = mR - itot
+ fg = mG - itot
+ fb = mB - itot
+ end
+
+ -- Falloff (Effect weakens at dark and bright colors)
+ if falloff == 1 then
+ fo = 1 - math.abs((a - 127.5)/127.5)^2
+ fr = fr * fo
+ fg = fg * fo
+ fb = fb * fo
+ end
+
+ if tin == 1 then
+ --cols[n+1] = matchcolor((a+fr)*amtA + r*amtR, (a+fg)*amtA + g*amtR, (a+fb)*amtA + b*amtR)
+ cols[n+1] = db.getBestPalMatchHYBRID({(a+fr)*amtA+r*amtR, (a+fg)*amtA + g*amtR, (a+fb)*amtA + b*amtR},pal,briweight / 100,true)
+ end
+ if clz == 1 then
+ --cols[n+1] = matchcolor((r+fr)*amtA + r*amtR, (g+fg)*amtA + g*amtR, (b+fb)*amtA + b*amtR)
+ cols[n+1] = db.getBestPalMatchHYBRID({(r+fr)*amtA+r*amtR, (g+fg)*amtA + g*amtR, (b+fb)*amtA + b*amtR},pal,briweight / 100,true)
+ end
+ end
+
+ if nobg == 1 then cols[getbackcolor()+1] = getbackcolor(); end
+
+ for x = 0, w - 1, 1 do
+ for y = 0, h - 1, 1 do
+ putbrushpixel(x, y, cols[getbrushpixel(x,y) + 1]);
+ end
+ end
+end;
+-- eof Colorize & Tint
+--------------------------------------------------------
+
+end -- OK
diff --git a/data/common/media/grafx2/scripts/samples_2.4/brush/Fisheye.lua b/data/common/media/grafx2/scripts/samples_2.4/brush/Fisheye.lua
new file mode 100644
index 0000000000..ef4871603a
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/brush/Fisheye.lua
@@ -0,0 +1,31 @@
+--BRUSH Distortion: FishEye
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+
+-- Copyright 2010 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+-- This script was adopted from Evalion, a Javascript codecrafting/imageprocessing project
+--http://goto.glocalnet.net/richard_fhager/evalion/evalion.html
+
+w, h = getbrushsize()
+
+
+for y = 0, h - 1, 1 do
+ for x = 0, w - 1, 1 do
+
+ ox = x / w;
+ oy = y / h;
+ v = (math.cos((ox-0.5)*math.pi)*math.cos((oy-0.5)*math.pi))*0.85;
+ ox = (1 + ox - (ox-0.5)*v) % 1;
+ oy = (1 + oy - (oy-0.5)*v) % 1;
+
+ c = getbrushbackuppixel(math.floor(ox*w),math.floor(oy*h));
+ putbrushpixel(x, y, c);
+ end
+end
+
diff --git a/data/common/media/grafx2/scripts/samples_2.4/brush/GrayscaleAvg.lua b/data/common/media/grafx2/scripts/samples_2.4/brush/GrayscaleAvg.lua
new file mode 100644
index 0000000000..3146ab7be7
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/brush/GrayscaleAvg.lua
@@ -0,0 +1,24 @@
+--BRUSH Remap: Grayscale (average)
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+
+-- Copyright 2010 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+w, h = getbrushsize()
+
+for x = 0, w - 1, 1 do
+ for y = 0, h - 1, 1 do
+
+ r, g, b = getcolor(getbrushpixel(x,y))
+
+ a = (r+g+b)/3
+
+ putbrushpixel(x, y, matchcolor(a,a,a));
+
+ end
+end
diff --git a/data/common/media/grafx2/scripts/samples_2.4/brush/GrayscaleDesat.lua b/data/common/media/grafx2/scripts/samples_2.4/brush/GrayscaleDesat.lua
new file mode 100644
index 0000000000..80db6264c8
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/brush/GrayscaleDesat.lua
@@ -0,0 +1,36 @@
+--BRUSH Remap: Grayscale (desaturate)
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+
+-- Copyright 2010 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+-- This script was adopted from Evalion, a Javascript codecrafting/imageprocessing project
+--http://goto.glocalnet.net/richard_fhager/evalion/evalion.html
+
+
+percent = 100
+
+--
+function desaturate(percent,r,g,b) -- V1.0 by Richard Fhager
+ p = percent / 100
+ a = (math.min(math.max(r,g,b),255) + math.max(math.min(r,g,b),0)) * 0.5 * p
+ r = r + (a-r*p)
+ g = g + (a-g*p)
+ b = b + (a-b*p)
+ return r,g,b
+end
+--
+
+
+w, h = getbrushsize()
+
+for x = 0, w - 1, 1 do
+ for y = 0, h - 1, 1 do
+ putbrushpixel(x, y, matchcolor(desaturate(percent,getcolor(getbrushpixel(x,y)))));
+ end
+end
diff --git a/data/common/media/grafx2/scripts/samples_2.4/brush/Halfsmooth.lua b/data/common/media/grafx2/scripts/samples_2.4/brush/Halfsmooth.lua
new file mode 100644
index 0000000000..68e2f1d387
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/brush/Halfsmooth.lua
@@ -0,0 +1,34 @@
+--BRUSH: Halfsize with smoothscaling
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+
+-- Copyright 2010 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+w, h = getbrushsize()
+
+setbrushsize(math.floor(w/2),math.floor(h/2))
+
+for x = 0, w - 1, 2 do
+ for y = 0, h - 1, 2 do
+ r1,g1,b1 = getcolor(getbrushbackuppixel(x,y));
+ r2,g2,b2 = getcolor(getbrushbackuppixel(x+1,y));
+ r3,g3,b3 = getcolor(getbrushbackuppixel(x,y+1));
+ r4,g4,b4 = getcolor(getbrushbackuppixel(x+1,y+1));
+
+ r = (r1 + r2 + r3 + r4 ) / 4;
+ g = (g1 + g2 + g3 + g4 ) / 4;
+ b = (b1 + b2 + b3 + b4 ) / 4;
+
+ c = matchcolor(r,g,b);
+
+ putbrushpixel(x/2, y/2, c);
+
+ end
+end
+
+
diff --git a/data/common/media/grafx2/scripts/samples_2.4/brush/Waves.lua b/data/common/media/grafx2/scripts/samples_2.4/brush/Waves.lua
new file mode 100644
index 0000000000..14e4e0e610
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/brush/Waves.lua
@@ -0,0 +1,42 @@
+--BRUSH Distortion: Waves v1.0
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+
+-- Copyright 2010 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+-- This script was adopted from Evalion, a Javascript codecrafting/imageprocessing project
+-- http://goto.glocalnet.net/richard_fhager/evalion/evalion.html
+
+
+--frq = 2
+--amp = 0.3
+
+-- Adjust power of frequency & amplitude
+frq_adj = 2
+amp_adj = 0.02
+
+ok,frq,amp = inputbox("Settings",
+ "Frequency 1-10", 3, 1,10,0,
+ "Amplitude 1-10", 3, 1,10,0
+);
+
+w, h = getbrushsize()
+
+for y = 0, h - 1, 1 do
+ for x = 0, w - 1, 1 do
+
+ ox = x / w;
+ oy = y / h;
+ ox = (1 + ox + math.sin(oy*math.pi*frq*frq_adj)*amp*amp_adj) % 1;
+
+ c = getbrushbackuppixel(math.floor(ox*w),y);
+ putbrushpixel(x, y, c);
+
+ end
+end
+
diff --git a/data/common/media/grafx2/scripts/samples_2.4/codenetsend.lua b/data/common/media/grafx2/scripts/samples_2.4/codenetsend.lua
new file mode 100644
index 0000000000..8fae1ff755
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/codenetsend.lua
@@ -0,0 +1,17 @@
+-- Codenet send for grafx2 2.4 and up, by Michael Ilsaas.
+-- Sends the latest saved picture. Set the IP address of the C64 in the line below.
+ip = "192.168.0.64"; -- <-- IP address of the C64.
+fn, fp = getfilename();
+picfile = assert(io.open(fp .. '/' .. fn ,"r"));
+picsize = picfile:seek("end")
+picfile:close();
+if picsize == 9000 or picsize == 9002 then -- check for hires
+ os.execute("echo AQgLCAoAnjIwNjIAAAAArQDdKfwJAo0A3ak4jRjQqQiNFtCpO40R0KkAjSDQogC9DA6dAGC9DA+dAGG9DBCdAGK9DBGdAGO9DBKdAGS9DBOdAGW9DBSdAGa9DBWdAGe9DBadAGi9DBedAGm9DBidAGq9DBmdAGu9DBqdAGy9DBudAG29DBydAG69DB2dAG/o4ADQm6IAvQwenQBwvQwfnQBxvQwgnQByvQwhnQBzvQwinQB0vQwjnQB1vQwknQB2vQwlnQB3vQwmnQB4vQwnnQB5vQwonQB6vQwpnQB7vQwqnQB8vQwrnQB9vQwsnQB+vQwtnQB/6OAA0JuiAL1MLZ0ATL1MLp0ATb1ML50ATr1MMJ0AT+jgANDjvUUJnQDAvUUKnQDBvUULnQDCvUUMnQDDvUUNnQDEvUUOnQDF6OAA0NdMAMCpwI00A6mojTUDqQCNNgOpQI03A60B3gkBjQHeor+gxCBbwxAJqQagwqkITLnESKkYoMJoqQ14IP7DEPtYIOH/0ANMucR4IP7DMPGtRAPJCNDqrUUD8ArJBtDhIA/DTDzArUYDyUXQ1K1MAym/DU0D0MqtTwPJEfA2yQHwCKkCIKzCTDzArVoDyQjQIKkAjVoDGK1cA2kIjVwDkAruXQPQBe5cA/D2IDjCIJnCTDzArVwDyRnQB61dA8k+8AipAyCswkw8wK1iA8nK0OCtYwPJH9DZrWUDSKkBjWUDqQCNSAONXgONYAOF+oX7qSCNSQOpGI1fA6kRjWEDrVoDrlwDjloDjVwDrVsDrl0DjlsDjV0DGKISpft9UwOF+6X6fVIDhfrKyhDukAjm+9AE5vrw+KX6Sf+NYAOl+0n/jWEDTl8DIDjCIJnCrWYDhf2tZwOF/K5oA6xpA2jJBPAPyQXwPskG8GvJB/B6TDzAziDQpQFIKfgJA4UBrmkDoAC5agOR/MjK0PdohQHuINAYmGX8ha6FLaX9aQCFr4UuTDzAqTOFAZhIivASrWoDoACR/MiR/MjQ+Ob9ytDzaKrwC61qA6AAkfzIytD6qTeFAUw8wKX80ALG/cb8pf1IpfxIqQhMucSiCL35wZ1AA8oQ96kDSKk/SKkITLnEIFmmIDOlTK6nQkNERk5PIFJSLU5FVCBGT1VORC4NAFJSLU5FVCBGT1VORC4gQ1M4OTAwQSBSRVZJU0lPTiAAogO9UgOdVgO9NAOdUgPKEPGpgI1OA6kAjVADjVEDhfqF+xiiEqX7fUcDhful+n1GA4X6ysoQ7pAI5vvQBOb68Pil+kn/jVADpftJ/41RA6IFvT4DnTgDvb/EnT4DyhDxYBitSQNpDqqtSANpANAEikxpxGBIohy9RgOdXgPKEPeiB6kAnVoDytD6hfqF+41IA6k0jUkDqQGNTwOpA41aA2iNWwMYoh6l+31bA4X7pfp9WgOF+srKEO6QCOb70ATm+vD4pfpJ/41cA6X7Sf+NXQMgOMJMmcKiB71Tw91GA9A5yhD1ogO9NAPdXgPQLMoQ9akCjU0Dogm9TgOdWAO9v8SdTgPKEPGiA700A51UA8oQ9yCHwqk8TGnEYAABCAAGBAABhvyE/SC5wxABYEip/4X6oACYSBhprCDtw4ol+oX6mCX6hfpoqMjAA9DnpfrJ/9AaoACx/KrImEix/KhoSEoJrCDcw2ioyMAG0OipgqIFoA8g3MOpiaLToAAg3MNoYKkAIO3D4A7QF8Bj0BOpASDtw4rQC8ALsAeYOOkHkAFgqf9gCo0C3qkAKo0D3o4E3owF3mAKjQLeqQAqjQPergTerAXeYKmSIO3DmCl/0AOp/2CtCd6tCN6sCd6uCN6Y0CngyLAlikoIqvAToACtCN6ZOAPIrQnemTgDyMrQ7yiQBq0I3pk4A6kAYIpImPAOogCtCN6tCd7o0PeI0PRoqvAMrQjeyvAGrQneytD0qf9gosmODN6iAI4N3o0O3o4P3kipkiDtw5gpf/ASrQnerQjerAnergjeIEPETHrEqZwg7cOYKQHw2mhKaQCqoAC5OAONCN7IuTgDjQneyMrQ72CpAI0g0GAAgBCxMhg= | base64 -d > /tmp/picview.prg");
+else
+ os.execute("echo AQgLCAoAnjIwNjIAAAAArQDdKfwJAo0A3ak4jRjQqRiNFtCpO40R0K0/NY0h0KkAjSDQogC9Lw6dAGC9Lw+dAGG9LxCdAGK9LxGdAGO9LxKdAGS9LxOdAGW9LxSdAGa9LxWdAGe9LxadAGi9LxedAGm9LxidAGq9LxmdAGu9LxqdAGy9LxudAG29LxydAG69Lx2dAG/o4ADQm6IAvS8enQBwvS8fnQBxvS8gnQByvS8hnQBzvS8inQB0vS8jnQB1vS8knQB2vS8lnQB3vS8mnQB4vS8nnQB5vS8onQB6vS8pnQB7vS8qnQB8vS8rnQB9vS8snQB+vS8tnQB/6OAA0JuiAL1vLZ0ATL1vLp0ATb1vL50ATr1vMJ0AT+jgANDjvVcxnQDYvVcynQDZvVcznQDavVc0nQDb6OAA0OO9aAmdAMC9aAqdAMG9aAudAMK9aAydAMO9aA2dAMS9aA6dAMXo4ADQ10wAwKnAjTQDqaiNNQOpAI02A6lAjTcDrQHeCQGNAd6iv6DEIFvDEAmpBqDCqQhMucRIqRigwmipDXgg/sMQ+1gg4f/QA0y5xHgg/sMw8a1EA8kI0OqtRQPwCskG0OEgD8NMPMCtRgPJRdDUrUwDKb8NTQPQyq1PA8kR8DbJAfAIqQIgrMJMPMCtWgPJCNAgqQCNWgMYrVwDaQiNXAOQCu5dA9AF7lwD8PYgOMIgmcJMPMCtXAPJGdAHrV0DyT7wCKkDIKzCTDzArWIDycrQ4K1jA8kf0NmtZQNIqQGNZQOpAI1IA41eA41gA4X6hfupII1JA6kYjV8DqRGNYQOtWgOuXAOOWgONXAOtWwOuXQOOWwONXQMYohKl+31TA4X7pfp9UgOF+srKEO6QCOb70ATm+vD4pfpJ/41gA6X7Sf+NYQNOXwMgOMIgmcKtZgOF/a1nA4X8rmgDrGkDaMkE8A/JBfA+yQbwa8kH8HpMPMDOINClAUgp+AkDhQGuaQOgALlqA5H8yMrQ92iFAe4g0BiYZfyFroUtpf1pAIWvhS5MPMCpM4UBmEiK8BKtagOgAJH8yJH8yND45v3K0PNoqvALrWoDoACR/MjK0PqpN4UBTDzApfzQAsb9xvyl/Uil/EipCEy5xKIIvfnBnUADyhD3qQNIqT9IqQhMucQgWaYgM6VMrqdCQ0RGTk8gUlItTkVUIEZPVU5ELg0AUlItTkVUIEZPVU5ELiBDUzg5MDBBIFJFVklTSU9OIACiA71SA51WA700A51SA8oQ8amAjU4DqQCNUAONUQOF+oX7GKISpft9RwOF+6X6fUYDhfrKyhDukAjm+9AE5vrw+KX6Sf+NUAOl+0n/jVEDogW9PgOdOAO9v8SdPgPKEPFgGK1JA2kOqq1IA2kA0ASKTGnEYEiiHL1GA51eA8oQ96IHqQCdWgPK0PqF+oX7jUgDqTSNSQOpAY1PA6kDjVoDaI1bAxiiHqX7fVsDhful+n1aA4X6ysoQ7pAI5vvQBOb68Pil+kn/jVwDpftJ/41dAyA4wkyZwqIHvVPD3UYD0DnKEPWiA700A91eA9AsyhD1qQKNTQOiCb1OA51YA72/xJ1OA8oQ8aIDvTQDnVQDyhD3IIfCqTxMacRgAAEIAAYEAAGG/IT9ILnDEAFgSKn/hfqgAJhIGGmsIO3DiiX6hfqYJfqF+mioyMAD0Oel+sn/0BqgALH8qsiYSLH8qGhISgmsINzDaKjIwAbQ6KmCogWgDyDcw6mJotOgACDcw2hgqQAg7cPgDtAXwGPQE6kBIO3DitALwAuwB5g46QeQAWCp/2AKjQLeqQAqjQPejgTejAXeYAqNAt6pACqNA96uBN6sBd5gqZIg7cOYKX/QA6n/YK0J3q0I3qwJ3q4I3pjQKeDIsCWKSgiq8BOgAK0I3pk4A8itCd6ZOAPIytDvKJAGrQjemTgDqQBgikiY8A6iAK0I3q0J3ujQ94jQ9Giq8AytCN7K8AatCd7K0PSp/2CiyY4M3qIAjg3ejQ7ejg/eSKmSIO3DmCl/8BKtCd6tCN6sCd6uCN4gQ8RMesSpnCDtw5gpAfDaaEppAKqgALk4A40I3si5OAONCd7IytDvYKkAjSDQYACAELEyGA== | base64 -d > /tmp/picview.prg");
+end
+if picsize == 9000 or picsize == 10001 then -- check for loadaddress, add two bytes if not found
+ os.execute('echo a >> /tmp/picview.prg');
+end
+os.execute('cat '.. fp .. '/' .. fn .. ' >> /tmp/picview.prg'); -- append pic to c64 binary
+os.execute("codenet -x /tmp/picview.prg -n "..ip); -- send file to c64
diff --git a/data/common/media/grafx2/scripts/samples_2.4/demo/3DPalette.lua b/data/common/media/grafx2/scripts/samples_2.4/demo/3DPalette.lua
new file mode 100644
index 0000000000..8e4b6a8ad1
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/demo/3DPalette.lua
@@ -0,0 +1,460 @@
+--3D-Palette viewer V0.72 (HSL-models added, 3D-World added, Pen-color only cycles thru unique colors, InputBox)
+--by Richard 'Dawnbringer' Fhager
+
+-- Mouse: Rotate Cube (Stops animation)
+-- Arrow-keys: Move Cube (in 3D world)
+-- F1: Start/Stop animation
+-- F2: Reset
+-- F3: Increase Color-Size
+-- F4: Decrease Color-Size
+-- F5: (Wip) Cycle thru selected PenColor (Note that only unique colors are displayed)
+-- F9: RGB-space model
+--F10: HSL-space model
+--F11: HSLcubic-space model
+-- "+" (Num): Zoom In
+-- "-" (Num): Zoom Out
+-- Esc: Exit script
+
+-- Drawing updated, rectangle missing, Sep11
+
+run("../libs/dawnbringer_lib.lua")
+
+
+BRIDIAG_SHOW = 1 -- Show brightness/Grayscale diagonal (1 = on, 0 = off)
+ANIM = 1 -- Animation (1 = on, 0 = off)
+BOX_DRK = 8 -- Darkest color used for box (0-255)
+BOX_BRI = 112 -- Brightest color used for box (0-255)
+COLSIZE_BASE = 26 -- Colors base size (value to adjusted by palette-size, with 2 cols maxsize is v / 1.23)
+
+--
+OK,RGB,HSL,HSLC,BOX_BRI,COLSIZE_BASE,SET800x600 = inputbox("3D-Palette Viewer Settings",
+
+ "1. RGB space [F9]", 1, 0,1,-1,
+ "2. HSL space [F10]", 0, 0,1,-1,
+ "3. HSL-cubic space [F11]",0, 0,1,-1,
+ "Box Brightness (16-255)", BOX_BRI, 16,255,0,
+ "Col Size (1-100) [F3/F4]", COLSIZE_BASE, 1,100,0,
+ "Set Screen to 800x600", 1,0,1,0
+
+);
+--
+
+if OK then
+
+ if SET800x600 == 1 then setpicturesize(800,600); end
+
+ SPACE = "rgb"
+ FORM = "cube"
+ if HSL == 1 then
+ SPACE = "hsl"
+ FORM = "cylinder"
+ end
+ if HSLC == 1 then
+ SPACE = "hsl_cubic"
+ FORM = "cube"
+ end
+
+
+pal = db.fixPalette(db.makePalList(256))
+
+FG = getforecolor()
+BG = getbackcolor()
+
+palcol = FG
+
+--
+function initColors(space)
+ for n = 1, #pal, 1 do
+ c = pal[n];
+ if space == "rgb" then
+ cols[n] = {c[1]/128-1,c[2]/128-1,c[3]/128-1,c[4]};
+ end
+ if space == "hsl_cubic" then
+ cols[n] = {}
+ cols[n][1] = (db.getHUE(c[1],c[2],c[3],0) / 6.0 * 255) / 128 - 1
+ cols[n][2] = (db.getSaturation(c[1],c[2],c[3])) / 128 - 1
+ cols[n][3] = (db.getLightness(c[1],c[2],c[3])) / 128 - 1
+ cols[n][4] = c[4]
+ end
+ if space == "hsl" then
+ cols[n] = {}
+ hue = db.getHUE(c[1],c[2],c[3],0) / 6.0 * math.pi*2
+ rad = db.getSaturation(c[1],c[2],c[3]) / 256
+ cols[n][1] = math.cos(hue) * rad
+ cols[n][2] = math.sin(hue) * rad
+ cols[n][3] = (db.getLightness(c[1],c[2],c[3])) / 128 - 1
+ cols[n][4] = c[4]
+ end
+ end
+end
+--
+
+cols = {} -- Make points of palette colors
+colz = {} -- To hold calculated points
+initColors(SPACE)
+
+
+function initPointsAndLines(form,bridiag)
+ if form == "cube" then
+ pts = {{-1,1,-1},{1,1,-1},{1,-1,-1},{-1,-1,-1}, -- The box
+ {-1,1, 1},{1,1, 1},{1,-1, 1},{-1,-1, 1}}
+ lin = {{1,2},{2,3},{3,4},{4,1},{5,6},{6,7},{7,8},{8,5},{1,5},{2,6},{3,7},{4,8}} -- Box Lines
+ if bridiag == 1 then lin[13] = {4,6}; end
+ end
+ if form == "cylinder" then
+ p = 28
+ pts = {}
+ lin = {}
+ for n = 1, p, 1 do
+ x = math.cos(math.pi*2 / p * (n-1))
+ y = math.sin(math.pi*2 / p * (n-1))
+ pts[n] = {x,y,-1}
+ lin[n] = {n,1 + (n%p)}
+ pts[n + p] = {x,y,1}
+ lin[n + p] = {n+p,p + 1 + (n%p)}
+ end
+ lin[p*2+1] = {1,p+1} -- Red (0 degrees)
+ lin[p*2+2] = {p+1,p+1+math.ceil(p/2)} -- Lightness end (needs an even # of points to work)
+ end
+end
+
+boxp = {} -- To hold the calculated points
+initPointsAndLines(FORM,BRIDIAG_SHOW)
+
+w,h = getpicturesize()
+CX,CY = w/2, h/2
+
+
+
+function initAndReset()
+ XANG, YANG, ZANG, ZOOM, COLSIZE_ADJ, XD, YD, WORLD_X, WORLD_Y, ZSELECT = 0,0,0,0,0,0,0,0,0,0
+end
+
+initAndReset()
+
+SIZE = math.min(w,h)/4
+DIST = 5 -- Distance perspective modifier, ~5 is nominal, more means "less 3D"
+
+CMAXSIZE = math.floor(COLSIZE_BASE / ((#pal)^0.3))
+--CMAXSIZE = 8
+CMINSIZE = 1 -- Negative values are ok. Color are never smaller than 1 pix
+
+BOX_LINE_DIV = 20 -- Number of colors/segments that a box-line can be divided into (depth)
+BOX_DIV_MULT = BOX_LINE_DIV / (math.sqrt(3)*2)
+
+-- Box depth colors
+box_div = {}
+for n = 0, BOX_LINE_DIV-1, 1 do
+ c = BOX_DRK + (BOX_BRI / (BOX_LINE_DIV - 1)) * n
+ --box_div[BOX_LINE_DIV - n] = matchcolor(c,c,c)
+ box_div[BOX_LINE_DIV - n] = db.getBestPalMatchHYBRID({c,c,c},pal,0.5,true)
+end
+
+--BOX_COL = matchcolor(80,80,80)
+BKG_COL = matchcolor(0,0,0)
+--CUR_COL = matchcolor(112,112,112)
+
+
+ function rotate3D(x,y,z,Xsin,Ysin,Zsin,Xcos,Ycos,Zcos) -- PrecCalced cos&sin for speed
+
+ local x1,x2,x3,y1,y2,y3,f,xp,yp
+
+ x1 = x
+ y1 = y * Xcos + z * Xsin
+ z1 = z * Xcos - y * Xsin
+
+ x2 = x1 * Ycos - z1 * Ysin
+ y2 = y1
+ z2 = x1 * Ysin + z1 * Ycos
+
+ x3 = x2 * Zcos - y2 * Zsin
+ y3 = x2 * Zsin + y2 * Zcos
+ z3 = z2
+
+ return x3,y3,z3
+ end
+
+ function do3D(x,y,z,zoom,dist,Xsin,Ysin,Zsin,Xcos,Ycos,Zcos) -- PrecCalced cos&sin for speed
+
+ local x1,x2,x3,y1,y2,y3,f,xp,yp
+
+ x1 = x
+ y1 = y * Xcos + z * Xsin
+ z1 = z * Xcos - y * Xsin
+
+ x2 = x1 * Ycos - z1 * Ysin
+ y2 = y1
+ z2 = x1 * Ysin + z1 * Ycos
+
+ x3 = x2 * Zcos - y2 * Zsin
+ y3 = x2 * Zsin + y2 * Zcos
+ z3 = z2
+
+ f = dist/(z3 + dist + zoom)
+ xp = x3 * f
+ yp = y3 * f
+
+ return xp,yp,z3
+ end
+
+
+function draw3Dline(x1,y1,z1,x2,y2,z2,div,mult,depthlist)
+ local s,xt,yt,xd,yd,zd,xf,yf
+ xd = (x2 - x1) / div
+ yd = (y2 - y1) / div
+ zd = (z2 - z1) / div
+ xf,yf = x1,y1
+
+ for s = 1, div, 1 do
+ -- Depth assumes a 1-Box (z ranges from -sq(3) to sq(3))
+ depth = math.floor(1 + (z1+zd*s + 1.732) * mult)
+ xt = x1 + xd*s -- + math.random()*8
+ yt = y1 + yd*s -- + math.random()*8
+ c = depthlist[depth]
+ if c == null then c = 1; end -- Something isn't perfect, error is super rare but this controls it
+ --db.line(xf,yf,xt,yt,c)
+ drawline(xf,yf,xt,yt,c)
+ xf = xt
+ yf = yt
+ end
+end
+
+function killinertia()
+ XD = 0
+ YD = 0
+end
+
+ -- If using 1-box, z is -sq(3) to sq(3)
+ minz = math.sqrt(3)
+ totz = minz * 2
+ maxrad = CMAXSIZE - CMINSIZE
+
+--q = 0
+--delay = 4
+--move = 0.03
+
+while 1 < 2 do
+
+ -- Time-for-space-wiggle...or somekindof attempt
+ --WORLD_X = -move
+ --q = (q + 1) % delay
+ --if q < delay/2 then WORLD_X = move; end
+
+ clearpicture(BKG_COL)
+
+ Xsin = math.sin(XANG); Xcos = math.cos(XANG)
+ Ysin = math.sin(YANG); Ycos = math.cos(YANG)
+ Zsin = math.sin(ZANG); Zcos = math.cos(ZANG)
+
+ -- Rotate Box points
+ for n = 1, #pts, 1 do
+ p = pts[n]
+ x,y,z = p[1],p[2],p[3]
+ XP,YP,zp = rotate3D(x,y,z,Xsin,Ysin,Zsin,Xcos,Ycos,Zcos)
+ boxp[n] = {XP,YP,zp}
+ end
+
+ -- Rotate Colors in palette
+ for n = 1, #cols, 1 do
+ p = cols[n]
+ x,y,z,c = p[1],p[2],p[3],p[4]
+ XP,YP,zp = rotate3D(x,y,z,Xsin,Ysin,Zsin,Xcos,Ycos,Zcos)
+ colz[n] = {XP,YP,zp,c}
+ end
+
+ ------------------------------------
+ -- Control world
+ ------------------------------------
+
+ -- Calculate points anew
+
+ -- Worldize Box points
+ for n = 1, #boxp, 1 do
+ s = SIZE
+ v = boxp[n]
+ x = v[1] + WORLD_X
+ y = v[2] + WORLD_Y
+ z = v[3]
+ f = DIST/(z + DIST + ZOOM)
+ XP = CX + x * f * s
+ YP = CY + y * f * s
+ boxp[n] = {XP,YP,z}
+ end
+
+ -- Worldize Colors in palette
+ for n = 1, #colz, 1 do
+ s = SIZE
+ v = colz[n]
+ x = v[1] + WORLD_X
+ y = v[2] + WORLD_Y
+ z = v[3]
+ c = v[4]
+ f = DIST/(z + DIST + ZOOM)
+ XP = CX + x * f * s
+ YP = CY + y * f * s
+ colz[n] = {XP,YP,z,c}
+ end
+
+
+-------------------------------------
+-------------------------------------
+
+ -- Brightness Diagonal
+ --if BRIDIAG_SHOW == 1 then
+ -- p1 = boxp[4]
+ -- p2 = boxp[6]
+ -- x1,y1,z1 = p1[1],p1[2],p1[3]
+ -- x2,y2,z2 = p2[1],p2[2],p2[3]
+ -- draw3Dline(x1,y1,z1,x2,y2,z2,BOX_LINE_DIV,BOX_DIV_MULT,box_div)
+ --end
+
+ --c1 = math.min(FG,BG)
+ --c2 = math.max(FG,BG)
+ --p = colz[26]
+ --XP1,YP1,zp1,c1 = p[1],p[2],p[3],p[4]
+ --for n = #colz, 1, -1 do
+ -- p = colz[27]
+ -- XP2,YP2,zp2,c2 = p[1],p[2],p[3],p[4]
+ -- drawline(XP1,YP1,XP2,YP2,c1)
+ --end
+
+ -- sort on z
+ db.sorti(colz,3)
+
+ -- Draw colors
+ for n = #colz, 1, -1 do
+ p = colz[n]
+ XP,YP,zp,c = p[1],p[2],p[3],p[4]
+
+ radius = CMINSIZE + maxrad - (zp+minz) / totz * maxrad
+ dorad = math.floor(radius - ZOOM*2 + COLSIZE_ADJ)
+
+ if dorad >= 1 then
+ --db.drawCircle(XP,YP,dorad,c)
+ drawdisk(XP,YP,dorad,c)
+ --db.drawRectangle(XP,YP,dorad,dorad,c)
+ else putpicturepixel(XP,YP,c)
+ end
+
+ if c == FG or c == BG then
+ sz = math.max(3,dorad + 3)
+ if c == BKG_COL then v = (c+128) % 255; c = matchcolor(v,v,v); end
+ db.drawRectangleLine(XP-sz,YP-sz,sz*2,sz*2,c)
+ end
+
+ end -- colz
+
+
+
+ -- Draw box
+ for n = 1, #lin, 1 do
+
+ l = lin[n]
+ p1 = boxp[l[1]]
+ p2 = boxp[l[2]]
+ x1,y1,z1 = p1[1],p1[2],p1[3]
+ x2,y2,z2 = p2[1],p2[2],p2[3]
+ draw3Dline(x1,y1,z1,x2,y2,z2,BOX_LINE_DIV,BOX_DIV_MULT,box_div)
+
+ end -- eof box
+
+ --updatescreen(); if (waitbreak(0.00)==1) then return; end
+
+ repeat
+
+ old_key = key;
+ old_mouse_x = mouse_x;
+ old_mouse_y = mouse_y;
+ old_mouse_b = mouse_b;
+
+ updatescreen()
+
+ moved, key, mouse_x, mouse_y, mouse_b = waitinput(0)
+
+ if mouse_b == 1 then ANIM = 0; end
+
+ if (key==27) then
+ return;
+ end
+
+ if (key==282) then ANIM = (ANIM+1) % 2; end -- F1: Stop/Start Animation
+ if (key==283) then initAndReset(); end -- F2: Reset all values
+ if (key==284) then COLSIZE_ADJ = COLSIZE_ADJ + 0.5; end -- F3
+ if (key==285) then COLSIZE_ADJ = COLSIZE_ADJ - 0.5; end -- F4
+
+ --messagebox(key)
+
+ if (key==286) then
+ --FG = (FG + 1) % 255;
+ palcol = (palcol + 1) % #pal
+ FG = pal[palcol+1][4]
+ setforecolor(FG);
+ setcolor(0,getcolor(0)) -- Force update of palette until setforecolor() is fixed
+ end -- F5
+
+ if (key==290) then -- F9
+ initColors("rgb")
+ initPointsAndLines("cube",BRIDIAG_SHOW)
+ end
+ if (key==291) then -- F10
+ initColors("hsl")
+ initPointsAndLines("cylinder", 0) -- Bridiag won't show even if turned on, it's only for cube
+ end
+ if (key==292) then -- F11
+ initColors("hsl_cubic")
+ initPointsAndLines("cube",BRIDIAG_SHOW)
+ end
+
+ if (key==269) then ZOOM = ZOOM + 0.1; end
+ if (key==270) then ZOOM = ZOOM - 0.1; end
+
+ if (key==32) then
+ ZSELECT = (ZSELECT + math.pi/2) % (2*math.pi);
+ --YANG = ((YANG - math.pi/2) % (math.pi*2));
+ --XANG = ((XANG + math.pi/2) % (math.pi*2));
+
+
+ YANG = ((YANG + math.pi/2) % (math.pi*2));
+ XANG = ((XANG + math.pi/2) % (math.pi*2));
+ YANG = ((YANG - math.pi/2) % (math.pi*2));
+ end -- Rotate Z 90 Degrees
+
+ SPEED = math.pi / 100
+
+
+ if (key==273) then WORLD_Y = WORLD_Y - 0.05; killinertia(); end
+ if (key==274) then WORLD_Y = WORLD_Y + 0.05; killinertia(); end
+
+ if (key==276) then WORLD_X = WORLD_X - 0.05; killinertia(); end
+ if (key==275) then WORLD_X = WORLD_X + 0.05; killinertia(); end
+
+ until ((mouse_b == 1 and (old_mouse_x~=mouse_x or old_mouse_y~=mouse_y)) or key~=0 or ANIM==1 or math.abs(XD)>0.01 or math.abs(YD)>0.01);
+
+ if ANIM == 0 then
+ if (mouse_b==1 and (old_mouse_x~=mouse_x or old_mouse_y~=mouse_y)) then -- Inertia
+ XD = (mouse_y - old_mouse_y)*0.005
+ YD = (mouse_x - old_mouse_x)*0.005
+ else
+ XD = XD*0.92
+ YD = YD*0.92
+ end
+ XANG = ((XANG - XD) % (math.pi*2));
+ YANG = ((YANG + YD) % (math.pi*2));
+ ZANG = ZSELECT
+ end
+
+ if ANIM == 1 then
+ XANG = (XANG + math.pi/300) % (math.pi*2)
+ YANG = (YANG + math.pi/500) % (math.pi*2)
+ ZANG = (ZANG + math.pi/1000) % (math.pi*2)
+ end
+
+ --XANG = ((CY-mouse_y) / 200 % (math.pi*2));
+ --YANG = ((mouse_x - CX) / 200 % (math.pi*2));
+ --ZANG = 0
+
+ statusmessage("x"..math.floor(XANG*57.3).."° y"..math.floor(YANG*57.3).."° z"..math.floor(ZANG*57.3).."° Zm: "..math.floor(-ZOOM*10).." ")
+
+end
+
+end -- OK
diff --git a/data/common/media/grafx2/scripts/samples_2.4/demo/Ellipse.lua b/data/common/media/grafx2/scripts/samples_2.4/demo/Ellipse.lua
new file mode 100644
index 0000000000..f24df21c5c
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/demo/Ellipse.lua
@@ -0,0 +1,68 @@
+--PICTURE scene: Ellipse update-demo (anim)
+--Demonstrates 'interactive' features.
+--by Richard Fhager
+
+-- Copyright 2011 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+
+--
+-- rot: Rotation in degrees
+-- stp: Step is # of line segments (more is "better")
+-- a & b are axis-radius
+function ellipse2(x,y,a,b,stp,rot,col)
+ local n,m=math,rad,al,sa,ca,sb,cb,ox,oy,x1,y1,ast
+ m = math; rad = m.pi/180; ast = rad * 360/stp;
+ sb = m.sin(-rot * rad); cb = m.cos(-rot * rad)
+ for n = 0, stp, 1 do
+ ox = x1; oy = y1;
+ sa = m.sin(ast*n) * b; ca = m.cos(ast*n) * a
+ x1 = x + ca * cb - sa * sb
+ y1 = y + ca * sb + sa * cb
+ if (n > 0) then drawline(ox,oy,x1,y1,col); end
+ end
+end
+--
+
+setpicturesize(300,300)
+setcolor(0,96,96,96)
+setcolor(1,255,255,128)
+
+r1 = 100
+r2 = 50
+rt = 0
+
+frames = 100
+
+
+while (1 < 2) do
+
+ r1t = 10 + math.random() * 140
+ r2t = 10 + math.random() * 140
+ rtt = math.random() * 360
+
+for n = 0, frames-1, 1 do
+ clearpicture(0)
+
+ f2 = n / frames
+ f1 = 1 - f2
+
+ r1a = r1*f1 + r1t*f2
+ r2a = r2*f1 + r2t*f2
+ rta = rt*f1 + rtt*f2
+
+ -- x, y, r1, r2, stp, rot, col
+ ellipse2(150, 150, r1a, r2a, 50, rta, 1)
+
+ statusmessage('press ESC to stop')
+ updatescreen();if (waitbreak(0)==1) then return end
+
+end
+
+ r1,r2,rt = r1a,r2a,rta
+
+end
diff --git a/data/common/media/grafx2/scripts/samples_2.4/demo/FlipPicture.lua b/data/common/media/grafx2/scripts/samples_2.4/demo/FlipPicture.lua
new file mode 100644
index 0000000000..3c1a4d62c8
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/demo/FlipPicture.lua
@@ -0,0 +1,17 @@
+-- flip picture - Copyright 2010 Paulo Silva
+-- This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. See
+w,h=getpicturesize();
+ok,flipx,flipy=inputbox("flip picture","flip x",1,0,1,-1,"flip y",0,0,1,-1);
+if ok==true then
+ if flipx==1 then
+ for y=0,h-1,1 do
+ for x=0,w/2,1 do
+ c1=getpicturepixel(x,y);c2=getpicturepixel(w-x-1,y)
+ putpicturepixel(x,y,c2);putpicturepixel(w-x-1,y,c1)
+ end;end
+ else
+ for y=0,h/2,1 do
+ for x=0,w-1,1 do
+ c1=getpicturepixel(x,y);c2=getpicturepixel(x,h-y-1)
+ putpicturepixel(x,y,c2);putpicturepixel(x,h-y-1,c1)
+ end;end;end;end
diff --git a/data/common/media/grafx2/scripts/samples_2.4/demo/SierpinskyCarpet.lua b/data/common/media/grafx2/scripts/samples_2.4/demo/SierpinskyCarpet.lua
new file mode 100644
index 0000000000..21d786eb6e
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/demo/SierpinskyCarpet.lua
@@ -0,0 +1,59 @@
+--PICTURE: Pattern - Sierpinsky carpet v1.0
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+-- Email: dawnbringer@hem.utfors.se
+-- MSN: annassar@hotmail.com
+--
+-- Copyright 2010 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+-- This script was adopted from Evalion, a Javascript codecrafting/imageprocessing project
+-- http://goto.glocalnet.net/richard_fhager/evalion/evalion.html
+--
+
+frac = {{1,1,1},{1,0,1},{1,1,1}}
+
+iter = 6
+
+
+--
+function pattern(x,y,p,n,i) -- Fractal Pattern V1.0 by Richard Fhager (mod allows for wrapping)
+ py = #p
+ px = #p[1]
+ while ((p[1+math.abs(math.floor(y*py))%py][1+math.abs(math.floor(x*px))%px]) == 1 and n
+
+-- This script was adopted from Evalion, a Javascript codecrafting/imageprocessing project
+-- http://goto.glocalnet.net/richard_fhager/evalion/evalion.html
+--
+
+frac = {{1,1},{1,0}}
+
+iter = 15
+
+--
+function pattern(x,y,p,n,i) -- Fractal Pattern V1.0 by Richard Fhager (mod allows for wrapping)
+ py = #p
+ px = #p[1]
+ while ((p[1+math.abs(math.floor(y*py))%py][1+math.abs(math.floor(x*px))%px]) == 1 and n
+
+-- This script was adopted from Evalion, a Javascript codecrafting/imageprocessing project
+--http://goto.glocalnet.net/richard_fhager/evalion/evalion.html
+
+
+
+w, h = getbrushsize()
+if (w<64 or h<64) then
+ setbrushsize(64,64)
+ w=64
+ h=64
+end
+
+for y = 0, h - 1, 1 do
+ for x = 0, w - 1, 1 do
+
+ -- Fractionalize image dimensions
+ ox = x / w;
+ oy = y / h;
+
+ -- Ball
+ Xr = ox-0.5; Yr = oy-0.5;
+ W = (1 - 2*math.sqrt(Xr*Xr + Yr*Yr));
+
+ -- 'FishEye' distortion / Fake 3D
+ F = (math.cos((ox-0.5)*math.pi)*math.cos((oy-0.5)*math.pi))*0.65;
+ ox = ox - (ox-0.5)*F;
+ oy = oy - (oy-0.5)*F;
+
+ -- Checkers
+ V = ((math.floor(0.25+ox*10)+math.floor(1+oy*10)) % 2) * 255 * W;
+
+ -- Specularities
+ SPEC1 = math.max(0,(1-5*math.sqrt((ox-0.45)*(ox-0.45)+(oy-0.45)*(oy-0.45)))*112);
+ SPEC2 = math.max(0,(1-15*math.sqrt((ox-0.49)*(ox-0.49)+(oy-0.48)*(oy-0.48)))*255);
+
+ r = W * 255 + SPEC1 + SPEC2
+ g = V + SPEC1 + SPEC2
+ b = V + SPEC1 + SPEC2
+
+ putbrushpixel(x, y, matchcolor(r,g,b));
+
+ end
+end
diff --git a/data/common/media/grafx2/scripts/samples_2.4/demo/brush/ColorSphere.lua b/data/common/media/grafx2/scripts/samples_2.4/demo/brush/ColorSphere.lua
new file mode 100644
index 0000000000..7cadb85b7c
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/demo/brush/ColorSphere.lua
@@ -0,0 +1,38 @@
+--BRUSH Scene: Sphere of pencolor v1.0
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+
+-- Copyright 2010 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+-- This script was adopted from Evalion, a Javascript codecrafting/imageprocessing project
+--http://goto.glocalnet.net/richard_fhager/evalion/evalion.html
+
+
+w, h = getbrushsize()
+
+rp,gp,bp = getcolor(getforecolor())
+
+for y = 0, h - 1, 1 do
+ for x = 0, w - 1, 1 do
+
+ -- Fractionalize image dimensions
+ ox = x / w;
+ oy = y / h;
+
+ -- Sphere
+ X = 0.5; Y = 0.5; Rd = 0.5
+ a = math.sqrt(math.max(0,Rd*Rd - ((X-ox)*(X-ox)+(Y-oy)*(Y-oy)))) * 1/Rd
+
+ r = rp * a
+ g = gp * a
+ b = bp * a
+
+ putbrushpixel(x, y, matchcolor(r,g,b));
+
+ end
+end
diff --git a/data/common/media/grafx2/scripts/samples_2.4/demo/brush/FindAA.lua b/data/common/media/grafx2/scripts/samples_2.4/demo/brush/FindAA.lua
new file mode 100644
index 0000000000..03695a029a
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/demo/brush/FindAA.lua
@@ -0,0 +1,100 @@
+--BRUSH: Find AA-colors from pencolors
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+
+-- Copyright 2010 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+cellw = 8
+cellh = 4
+colors = 256
+
+setbrushsize(cellw * 3, cellh * 3)
+
+
+--
+function makePalList(cols)
+ pal = {}
+ for n = 0, cols-1, 1 do
+ r,g,b = getcolor(n)
+ pal[n+1] = {r,g,b}
+ end
+ return pal
+end
+--
+
+--
+function getBestPalMatchHYBRID(rgb,pal,briweight)
+ local diff,diffC,diffB,best,bestcol,cols,n,c,r,g,b,p,obri,pbri
+ cols = #pal
+ bestcol = 0
+ best = 9e99
+
+ r = rgb[1]
+ g = rgb[2]
+ b = rgb[3]
+
+ obri = math.pow(r*9,2)+math.pow(g*16,2)+math.pow(b*8,2)
+
+ for n=0, cols-1, 1 do
+ p = pal[n+1]
+ pbri = math.pow(p[1]*9,2)+math.pow(p[2]*16,2)+math.pow(p[3]*8,2)
+ diffB = math.abs(obri - pbri)
+
+
+ diffC = (math.pow(r-p[1],2)+math.pow(g-p[2],2)+math.pow(b-p[3],2)) * 400
+ --diff = diffB + diffC
+ diff = briweight * (diffB - diffC) + diffC
+ if diff <= best then bestcol = n; best = diff; end
+ end
+
+ return bestcol
+end
+--
+
+--
+function drawRectangle(x1,y1,w,h,c)
+ for y = y1, y1+h, 1 do
+ for x = x1, x1+w, 1 do
+ putbrushpixel(x,y,c);
+ end
+ end
+end
+--
+
+
+
+palList = makePalList(colors)
+
+cf = getforecolor()
+cb = getbackcolor()
+rf,gf,bf = getcolor(cf)
+rb,gb,bb = getcolor(cb)
+
+ra = (rf + rb) / 2
+ga = (gf + gb) / 2
+ba = (bf + bb) / 2
+
+rgb1 = {ra,ga,ba}
+c1 = getBestPalMatchHYBRID(rgb1,palList,0.0)
+c2 = getBestPalMatchHYBRID(rgb1,palList,0.75)
+c3 = getBestPalMatchHYBRID(rgb1,palList,0.99)
+
+q = {{cf,c1,cb},
+ {cf,c2,cb},
+ {cf,c3,cb}}
+
+
+for y = 0, #q-1, 1 do
+ for x = 0, #q[1]-1, 1 do
+
+ drawRectangle(x*cellw,y*cellh,cellw,cellh,q[y+1][x+1])
+
+ end
+end
+
+
diff --git a/data/common/media/grafx2/scripts/samples_2.4/demo/brush/Mandelbrot.lua b/data/common/media/grafx2/scripts/samples_2.4/demo/brush/Mandelbrot.lua
new file mode 100644
index 0000000000..d93b8f6b45
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/demo/brush/Mandelbrot.lua
@@ -0,0 +1,64 @@
+--BRUSH Scene: Mandelbrot fractal v0.5
+--
+--Draws a Mandelbrot fractal in the current brush.
+--
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+
+-- Copyright 2010 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+-- This script was adopted from Evalion, a Javascript codecrafting/imageprocessing project
+--http://goto.glocalnet.net/richard_fhager/evalion/evalion.html
+
+
+colors = 64
+
+x0 = -1.7
+x1 = 0.7
+ym = 0
+iter = 64
+
+
+ok, x0, x1, ym, iter = inputbox("Fractal data",
+ "X0", x0, -2, 2,4,
+ "X1", x1, -2, 2,4,
+ "midY", ym, -2, 2,4,
+ "Iter", iter, 1, 2048,0
+);
+
+-- -0.831116819,-0.831116815,0.2292112435,192
+
+
+function mandel(x,y,l,r,o,i) -- pos. as fraction of 1, left coord, right coord, y coord, iterations
+
+ local w,s,a,p,q,n,v,w
+
+ s=math.abs(r-l);
+
+ a = l + s*x;
+ p = a;
+ b = o - s*(y-0.5);
+ q = b;
+ n = 1;
+ v = 0;
+ w = 0;
+
+ while (v+w<4 and n 0 then s = 1; end
+ if v < 0 then s = -1; end
+ return s
+end
+--
+
+function db.distance(ax,ay,bx,by) return math.sqrt((ax-bx)^2 + (ay-by)^2); end
+
+--
+function db.rotation (rot_ang,hub_x,hub_y,x,y) -- Rotate coordinates x & y relative hub
+ local new_ang,dist,m,xd,yd,v; m = math
+ xd=hub_x-x;
+ yd=hub_y-y;
+ if (not(xd==0 and yd==0)) then
+ v = -90; if xd < 0 then v = 90; end
+ new_ang = m.atan(yd/xd) - (v+rot_ang) * m.pi/180;
+ dist = m.sqrt(xd*xd+yd*yd);
+ x = hub_x - m.sin(new_ang)*dist;
+ y = hub_y + m.cos(new_ang)*dist;
+ end
+ return math.floor(x),math.floor(y) -- For drawing purposes
+end
+--
+
+--
+function db.recursiveSum(div,rlev) -- divisons per recursion,recursion levels
+ local s,i,m
+ s,m = 1,1
+ for i = 1, rlev, 1 do
+ m = m*div
+ s = s + m
+ end
+ return s
+end
+--
+
+--
+-- ... eof Custom Math Functions ...
+--
+
+-- *************************************
+-- *** Fractional Scenery ***
+-- *************************************
+
+--
+function db.setSceneryPalette()
+ db.colorCigarr(10,28,true) -- 250 colors
+ setcolor(250, 208,48,48)
+ setcolor(251, 48,208,48)
+ setcolor(252, 48,48,208)
+ setcolor(253, 224,224,64)
+ setcolor(254, 224,64,224)
+ setcolor(255, 64,224,224)
+end
+--
+
+--
+function db.star(xf,yf,sx,sy,rgb,haz,out,lum)
+ local n,c,dist; c={}
+ dist = haz + out * math.sqrt((xf-sx)^2+(yf-sy)^2);
+ for n = 1, 3, 1 do c[n] = (rgb[n] * lum) / dist; end
+ return c;
+end
+--
+
+--
+function db.zoom(xf,yf,zoom,panx,pany) -- Zoom and Pan in a fractional coord-system
+ xf = (xf-0.5)/zoom + 0.5 + panx;
+ yf = (yf-0.5)/zoom + 0.5 + pany;
+ return xf,yf
+end
+--
+
+--
+function db.rotationFrac(rot_ang,hub_x,hub_y,x,y) -- Rotate coordinates x & y relative hub
+ local new_ang,dist,m,xd,yd,v; m = math
+ xd=hub_x-x;
+ yd=hub_y-y;
+ if (not(xd==0 and yd==0)) then
+ v = -90; if xd < 0 then v = 90; end
+ new_ang = m.atan(yd/xd) - (v+rot_ang) * m.pi/180;
+ dist = m.sqrt(xd*xd+yd*yd);
+ x = hub_x - m.sin(new_ang)*dist;
+ y = hub_y + m.cos(new_ang)*dist;
+ end
+ return x,y
+end
+--
+
+--
+function db.twirl(x,y,arms,trot,tpow,tang)
+ local b,ang,vx,vy,vr,m,deg,tw
+ m=math; deg=math.pi/180; tw=.5;
+ if (not(x==.5 and y==.5)) then
+ ang = m.atan((.5-y)/(.5-x));
+ b = 0; if (x>.5) then b = m.pi; end
+ vx = .5-x; vy = .5-y; vr = m.pow(m.sqrt(vx*vx+vy*vy),tpow);
+ tw = .5+m.sin(-tang*deg+vr*trot+(ang + b)*arms)*.5;
+ end
+ return tw;
+end
+--
+
+--- Alpha filters
+--
+function db.alpha1(x,y,amp) -- Coord, Amplify: 0..n
+ local p,a,xh,yh,m
+ xh=0.5-x; yh=0.5-y; m = math
+ p = m.pow(xh*xh+yh*yh,0.7);
+ a = m.cos(32*m.pi*p)*m.sin(8*m.pi*(xh+yh));
+ return 1 + (a * amp)
+end
+--
+
+--
+-- ... eof Fractional Scenery ...
+--
+
+-- *************************************
+-- *** Custom Array Functions ***
+-- *************************************
+--
+-- Ok, I don't know Lua that well (still unsure about some scopes & refs etc.)
+-- And some features may not be active in Grafx2. So, some of the follwing functions
+-- may exist in Lua/Grafx2...but since I'm not sure if and how they work - I'll prefer
+-- to add a set of my own of known performance.
+
+--
+function db.newArrayInit(xs,val)
+ local x,ary; ary = {}
+ for x = 1, xs, 1 do
+ ary[x] = val
+ end
+ return ary
+end
+--
+
+
+--
+function db.newArrayInit2Dim(xs,ys,val)
+ local x,y,ary; ary = {}
+ for y = 1, ys, 1 do
+ ary[y] = {}
+ for x = 1, xs, 1 do
+ ary[y][x] = val
+ end
+ end
+ return ary
+end
+--
+
+--
+-- Merge two arrays into a NEW one: array_c = db.newArrayMerge(array_b,array_b)
+--
+function db.newArrayMerge(a,b)
+ local n,ary; ary = {}
+ for n = 1, #a, 1 do
+ ary[n] = a[n]
+ end
+ for n = 1, #b, 1 do
+ ary[n+#a] = b[n]
+ end
+ return ary
+end
+--
+
+--
+-- Generate a copy of an array with a new value added Last
+--
+function db.newArrayInsertLast(a,val)
+ local n,ary; ary = {}
+ for n = 1, #a, 1 do
+ ary[n] = a[n]
+ end
+ ary[#a+1] = val
+ return ary
+end
+--
+
+--
+-- Generate a copy of an array with a new value added First
+--
+function db.newArrayInsertFirst(a,val)
+ local n,ary; ary = {}
+ ary[1] = val
+ for n = 2, #a+1, 1 do
+ ary[n] = a[n-1]
+ end
+ return ary
+end
+--
+
+--
+function db.ary2txt(ary) -- One & two dimensions supported [a,b] -> "a,b". [[a,b],[c,d]] -> "a-b, c-d"
+ local t,n,m,v
+ t = ""
+ for n = 1, #ary, 1 do
+ if type(ary[n]) == "table" then
+ t = t..ary[n][1]
+ for m = 2, #ary[n], 1 do
+ t = t.."-"..ary[n][m]
+ end
+ else t = t..ary[n];
+ end
+ t = t..", "
+ end
+ return t
+end
+--
+
+--
+-- *** Array data manipulation ***
+--
+
+--
+-- InsertionSort Array, this is chaos...I'm confused and stomped...don't understand how Lua works...
+-- ...sorting seem be to ok but this code is ugly...
+-- Sort LO-HI
+--
+-- Screwed up or confused thing here I think, perhaps lo-hi/hi-lo. This is working lo-hi but the code
+-- looks like hi-lo...edit this some day
+--
+function db.sorti(d,idx)
+ local a,j,tmp,l,e
+ l = #d
+
+ for a=2, l, 1 do
+ tmp = d[a];
+ e = a
+ for j=a, 2, -1 do
+ e = j
+ if d[j-1][idx] > tmp[idx] then d[j] = d[j-1]; e = j-1; else break; end;
+ end;
+ d[e] = tmp; -- WHY THE F**K CAN'T YOU READ j HERE!?! STUPID ASSUCKING LANGUAGE
+
+ end;
+ --return d
+end
+--
+
+--
+function db.shuffle(list)
+ local i,n,t
+ for n = #list, 2, -1 do
+ i = 1+math.floor(math.random() * n)
+ t = list[n]; list[n] = list[i]; list[i] = t
+ end
+end
+--
+
+--
+-- ... eof Custom Array Functions ...
+--
+
+
+-- *************************************
+-- *** Misc. Logical Operations ***
+-- *************************************
+
+--
+-- palList [r,g,b,palindex] is expected only to contain unique colors
+-- index = -1 --> index of list
+--
+function db.makeIndexList(list,index)
+ local n,ilist
+ ilist = {}
+ for n = 1, #list, 1 do
+ if (index > 0) then ilist[n] = list[n][index]; end
+ if (index == -1) then ilist[n] = n; end
+ end
+ return ilist
+end
+--
+
+--
+-- Return a list of all possible (non-same) pairs from the entries in a list
+-- [a,b,c] --> [[a,b],[a,c],[b,c]]
+-- (All entries are treated as unique. i.e it's only the INDEX that counts)
+-- mode = 0: Only unique pairs (m = (n^2 - n)/2), [a,b] --> [[a,b]]
+-- mode = 1: All pairs, i.e mirror versions as well. (m = n^2 - n), [a,b] --> [[a,b], [b,a]]
+--
+function db.pairsFromList(list,mode)
+ local a,b,l,n,pairs
+ pairs = {}
+ l = #list
+ n = 1
+ for a = 1, l, 1 do
+ for b = a+1, l, 1 do
+ pairs[n] = {list[a],list[b]}; n = n + 1
+ if mode == 1 then pairs[n] = {list[b],list[a]}; n = n + 1; end
+ end
+ end
+ return pairs
+end
+--
+
+function db.valueInArray(ary,val)
+ local n,res
+ res = false
+ for n = 1, #ary, 1 do
+ if ary[n] == val then res = true; break; end
+ end
+ return res
+end
+
+-- RAMP specific
+
+-- Remove initial pair (palList) colors from pallist
+function db.initiateRamp(pair,pallist,pal_index)
+ local n,found,plist
+ plist = {}
+
+ found = 1
+ for n = 1, #pallist, 1 do
+ if db.valueInArray(pair,pallist[n]) == false then
+ plist[found] = pallist[n]; found = found + 1;
+ end
+ end
+
+ pair[pal_index] = plist -- ex: ["pal"]
+
+ return pair -- Is now a 2 color RAMP
+end
+--
+
+-- Remove new col entry from ramp's pallist and add it to the ramp, returns an updated ramp
+-- RampList = [1,2] ["pal"] = palList = [3,4,5], addindex = 3
+-- --> [1,2,3] palList = [4,5]
+function db.updateRamp(ramp,addindex,pal_index)
+ local n,found,pallist,plist,newramp
+ plist = {}
+ pallist = ramp[pal_index]
+
+ -- New palList without added color to IndexList
+ found = 1
+ for n = 1, #pallist, 1 do
+ if pallist[n] ~= addindex then
+ plist[found] = pallist[n]; found = found + 1;
+ end
+ end
+
+ newramp = db.newArrayInsertLast(ramplist,addindex)
+ newramp[pal_index] = plist
+
+ return rlist
+end
+
+--
+-- Returns a list of all inital ramps from color pairs
+--
+-- Weeds out bad pairs, attaches remaining palette colors and the first rgb-vector
+--
+--
+function db.initiateRampList(pairs,pallist,pal_index,vec_index,min,maxmult,rw,gw,bw)
+ local n,ramplist,newpairs,accept,dist,c1,c2,max,rD,gD,bD
+ ramplist = {}
+ max = min + (142 / math.sqrt(#pallist)) * maxmult -- min ex: 8-12
+ accept = 0
+
+ for n = 1, #pairs, 1 do
+ c1 = pallist[pairs[n][1]]
+ c2 = pallist[pairs[n][2]]
+ rD = c2[1] - c1[1]
+ gD = c2[2] - c1[2]
+ bD = c2[3] - c1[3]
+ dist = math.sqrt( (rw*rD)^2 + (gw*gD)^2 + (bw*bD)^2 )
+
+ if dist >= min and dist <= max then
+ accept = accept + 1; ramplist[accept] = db.initiateRamp(pairs[n],pallist,pal_index);
+ ramplist[accept][vec_index] = {rD, gD, bD, dist}; -- Add first color vector, ONLY KEEP DISTANCE?
+ end
+ end
+
+ return ramplist
+end
+
+
+function db.findRampExpansionColors(ramp)
+ local clist
+ clist = {}
+ -- Calculate vectors here?
+ return clist
+end
+
+
+function db.findRAMPS(min_len, max_len)
+ local i,n,c,pallist,ramp,ramplist,pairs,spairs,palindex,vecindex,found,donelist,newlist,dones
+ local colorlist
+ palindex = "pal"
+ vecindex = "vector"
+ pallist = db.fixPalette(db.makePalList(256), 0)
+ pairs = db.pairsFromList(db.makeIndexList(pallist,-1), 0)
+ ramplist = db.initiateRampList(pairs,pallist,palindex,vecindex, 8,0.75, 0.26,0.55,0.19)
+
+ -- MIN_LEN = 5
+ -- MAX_LEN = 10
+
+ -- Split Ramp-build into two parts:
+ -- 1. Build ramps >= MIN_LEN, NONE added to 'Done'
+ -- 2. Run til no more ramps can be expanded or reaches MAX_LEN, ALL ramps added to 'Done'
+
+ for i = 1, (min_len - 2), 1 do -- Assuming 2 for inital pairs (2 color ramps)
+ newlist = {}
+ found = 0
+ for n = 1, #ramplist, 1 do
+ ramp = ramplist[n]
+ colorlist = db.findRampExpansionColors(ramp) -- Colors that can split the current ramp into new expanded ramps
+ for c = 1, #colorlist, 1 do
+ found = found + 1; newlist[found] = db.updateRamp(ramp,colorlist[c],palindex); -- Ramp is expanded by 1 color
+ end
+ end
+ ramplist = newlist
+ end
+
+
+ donelist = {}; dones = 0
+
+ repeat
+ newlist = {}
+ found = 0
+ for n = 1, #ramplist, 1 do
+ ramp = ramplist[n]
+ if true == false then
+ found = found + 1; newlist[found] = db.updateRamp(ramp,color,palindex);
+ else
+ dones = dones + 1; donelist[dones] = ramp;
+ end
+ end
+ --ramplist = newlist
+ until found == 0
+
+ return #pairs.." - "..#ramplist
+end
+
+--
+-- ... eof Misc. Logical Operations ...
+--
+
+
+-- ***************************************
+-- *** General RGB-Color Modifications ***
+-- ***************************************
+
+
+--
+function db.makeComplementaryColor(r,g,b,brikeeplev) -- Lev: 0 = Normal, 1 = Loose, 2 = Strict
+
+ local bri_o,bri_n,bdiff
+
+ function cap(v) return math.max(0,math.min(v,255)); end
+
+ bri_o = db.getBrightness(r,g,b)
+ r,g,b = db.shiftHUE(r,g,b,180)
+
+ if brikeeplev > 0 then
+
+ for n = 0, brikeeplev*3-1, 1 do -- Must iterate to reduce brightness error
+ bri_n = db.getBrightness(r,g,b)
+ bdiff = (bri_o - bri_n) / 2 * brikeeplev
+ r = cap(r + bdiff)
+ g = cap(g + bdiff)
+ b = cap(b + bdiff)
+ end
+
+ end
+
+ return r,g,b
+
+end
+--
+
+
+-- *** Color balance ***
+--
+-- bri_flag: Preserve brightness
+-- loose_flag: Loose preservation restrictions for brightness and balance
+--
+-- Jeez, was this a tricky sucker; color-balance is just adding and capping...
+-- but trying color-balance with preserved perceptual brightness is a different monster...
+-- ...so bad I could only solve it by iterative error correction.
+--
+function db.ColorBalance(r,g,b,rd,gd,bd,bri_flag,loose_flag) -- preserve brightness
+ local rw,gw,bw,ri,gi,bi,itot,rni,gni,bni,ro,go,bo,ovscale,lev,count,rt,gt,bt,rf,gf,bf,bri
+
+ -- Dawn 3.0, [0.26,0.55,0.19], 0-255 bri-colorscale adjust = 1.56905
+ rw,gw,bw = 0.26, 0.55, 0.19
+
+ function cap(v) return math.min(255,math.max(v,0)); end
+
+ bri = db.getBrightness(r,g,b)
+
+
+ -- Loose brightness & balance preservation, a good compromise.
+ if bri_flag == true and loose_flag == true then
+
+ lev = (rd + gd + bd) / 3
+ rd = rd - lev
+ gd = gd - lev
+ bd = bd - lev
+
+ brin = db.getBrightness(cap(r+rd),cap(g+gd),cap(b+bd))
+ itot = brin - bri
+ rd = rd - itot
+ gd = gd - itot
+ bd = bd - itot
+
+ end
+
+
+ if bri_flag == true and loose_flag == false then
+
+ itot = 255
+ count = 0
+
+ -- Normalize (Yup, it's right only to normalize once first..cont.norm. will have some counter-effect)
+ lev = (rd + gd + bd) / 3
+ rd = rd - lev
+ gd = gd - lev
+ bd = bd - lev
+
+ repeat
+
+ --messagebox("Norm:"..rd..", "..gd..", "..bd)
+
+ -- Calculate total brightness change
+ -- Note: Perceptual Brightness is exponential, and can't be delta-adjusted for anything other than greyscales.
+ -- Although the formula for the new brightness corrected normalization level can can be derived...
+ -- ...it doesn't do much good since the bigger problem is overflow outside the 0-255 boundary.
+ -- As for now, I see no other means to solve this issue than with iterative error-correction.
+
+ rt = r+rd
+ gt = g+gd
+ bt = b+bd
+
+ itot = 9e99
+ rni = rd
+ gni = gd
+ bni = bd
+
+ -- We can get brightness of negative values etc. So bri-correction is put on hold until values are scaled down
+ if (rt>=0 and gt>=0 and bt>=0) and (rt<256 and gt<256 and bt<256) then
+ brin = db.getBrightness(rt,gt,bt)
+ itot = brin - bri
+ --messagebox("Bri Diff: "..itot)
+ -- Brightness adjusted balance
+ rni = rd - itot
+ gni = gd - itot
+ bni = bd - itot
+ end
+
+ --messagebox("Bri Adj Bal:"..rni..", "..gni..", "..bni)
+
+ -- Apply balance to find overflow (as fraction of the channel change)
+ ro = math.max( math.max((r + rni)-255,0), math.abs(math.min((r + rni),0)) ) / math.max(math.abs(rni),1)
+ go = math.max( math.max((g + gni)-255,0), math.abs(math.min((g + gni),0)) ) / math.max(math.abs(gni),1)
+ bo = math.max( math.max((b + bni)-255,0), math.abs(math.min((b + bni),0)) ) / math.max(math.abs(bni),1)
+
+ ovscale = 1 - math.max(ro,go,bo)
+
+ -- Scaling balances might be logically incorrect (as they can be seen as constant differences)
+ -- But scaling DOWN is quite harmless and I don't see how it could be done otherwise...
+ -- ex: +10 red, +5 blue: Scale x2 = +20 red, +10 blue -> More red over blue than ordered, a contrast behaviour.
+ -- +10 red, +5 blue: Scale x0.5 = +5 red, +2.5 blue -> Less of everything, but a part of the order. Harmless?
+ --
+ rd = rni * ovscale
+ gd = gni * ovscale
+ bd = bni * ovscale
+
+ count = count + 1
+
+ --messagebox("Final bal:"..rd..", "..gd..", "..bd)
+
+ until math.abs(itot) < 1 or count > 5
+
+ end
+
+ rf = r + rd
+ gf = g + gd
+ bf = b + bd
+
+ --messagebox("Result color:"..rf..", "..gf..", "..bf)
+
+ return rf,gf,bf
+end
+--
+
+
+
+--
+-- bri_flag: Preserve brightness
+-- cap_flag: Cap new color at 0-255, has a desaturating effect for large values.
+--
+function db.ColorBalanceXXX(r,g,b,rd,gd,bd,bri_flag,cap_flag) -- preserve brightness
+ local rf,gf,bf
+
+ if cap_flag == true then
+ rd = math.min(255,math.max(0, r+rd)) - r
+ gd = math.min(255,math.max(0, g+gd)) - g
+ bd = math.min(255,math.max(0, b+bd)) - b
+ end
+
+ local rw,gw,bw,ri,gi,bi,itot,rni,gni,bni,ro,go,bo,ovscale
+
+
+ -- Dawn 3.0, [0.26,0.55,0.19], 0-255 bri-colorscale adjust = 1.56905
+ rw,gw,bw = 0.26, 0.55, 0.19
+
+ if bri_flag == true then
+
+ -- Calculate total brightness change
+ --ri = rd * rw
+ --gi = gd * gw
+ --bi = bd * bw
+ --itot = math.sqrt(ri^2 + gi^2 + bi^2)
+
+ bri = db.getBrightness(r,g,b)
+ brin = db.getBrightness(r+rd,g+gd,b+bd)
+ itot = brin - bri
+
+
+ -- Normalized and Brightness adjusted balance
+ rni = rd - itot
+ gni = gd - itot
+ bni = bd - itot
+
+ -- Apply balance to find overflow (as fraction of the channel change)
+ ro = math.max( math.max((r + rni)-255,0), math.abs(math.min((r + rni),0)) ) / math.max(math.abs(rni),1)
+ go = math.max( math.max((g + gni)-255,0), math.abs(math.min((g + gni),0)) ) / math.max(math.abs(gni),1)
+ bo = math.max( math.max((b + bni)-255,0), math.abs(math.min((b + bni),0)) ) / math.max(math.abs(bni),1)
+
+ ovscale = 1 - math.max(ro,go,bo)
+
+ rd = rni * ovscale
+ gd = gni * ovscale
+ bd = bni * ovscale
+
+ end
+
+ rf = r + rd
+ gf = g + gd
+ bf = b + bd
+
+ return rf,gf,bf
+end
+--
+
+--
+function db.getContrast(ch) -- Channel, returns fraction -1..0..1, negative for ch < 127.5
+ --return math.abs((ch / 127.5) - 1)
+ return (ch / 127.5) - 1
+end
+--
+
+--
+function db.getAvgContrast(r,g,b)
+ return (math.abs(db.getContrast(r)) + math.abs(db.getContrast(g)) + math.abs(db.getContrast(b))) / 3
+end
+--
+
+--
+-- Mode = 0: Proportional - all colors reach max contrast at 100%
+--
+-- Mode = 1: Linear - percentage simply added
+--
+function db.changeContrastOLD(r,g,b,prc,mode)
+
+ local m,rd,gd,bd,rv,gv,bv,rc,gc,bc,base,sign
+
+ base = 1; sign = 1
+ if prc < 0 then base = 0; sign = -1; end -- decontrast
+
+ m = prc / 100 * sign
+
+ -- mode 0
+ rc = db.getContrast(r)
+ rd = (base - math.abs(rc)) * m * db.sign(rc)
+ rv = (rc+rd+1) * 127.5
+
+ gc = db.getContrast(g)
+ gd = (base - math.abs(gc)) * m * db.sign(gc)
+ gv = (gc+gd+1) * 127.5
+
+ bc = db.getContrast(b)
+ bd = (base - math.abs(bc)) * m * db.sign(bc)
+ bv = (bc+bd+1) * 127.5
+
+ return rv,gv,bv
+
+end
+--
+
+function db.changeContrast(r,g,b,prc) -- Photoshop style
+
+ local m,rd,gd,bd,rv,gv,bv,rc,gc,bc
+
+ m = 1 + math.pow((255 / 100 * prc),3) / (255*255)
+
+ -- decontrast
+ if prc < 0 then
+ m = 1 - math.abs(prc)/100
+ end
+
+ rc = db.getContrast(r)
+ rd = rc * m
+ rv = (rd+1) * 127.5
+
+ gc = db.getContrast(g)
+ gd = gc * m
+ gv = (gd+1) * 127.5
+
+ bc = db.getContrast(b)
+ bd = bc * m
+ bv = (bd+1) * 127.5
+
+ return rv,gv,bv
+
+end
+
+
+
+--
+function db.getBrightness(r,g,b) -- 0-255
+ local bri
+ --bri = (r+g+b)/3
+ --bri = r*0.3 + g*0.59 + b*0.11 -- Luma Y'601
+ --bri = math.sqrt((r*0.3)^2 + (g*0.59)^2 + (b*0.11)^2) -- Luma Y'601
+ --bri = r*0.245 + g*0.575 + b*0.18 -- Dawn 2.0
+
+ bri = math.sqrt((r*0.26)^2 + (g*0.55)^2 + (b*0.19)^2) * 1.56905 -- Dawn 3.0
+ return bri
+end
+--
+
+
+--
+-- Note on desaturation: These functions are all junk, the only way to desaturate
+-- is to fade a color into it's corresponding greyscale.
+--
+
+--
+function db.desaturate(percent,r,g,b) -- V1.0 by Richard Fhager
+ local a,p
+ p = percent / 100
+ a = (math.min(math.max(r,g,b),255) + math.max(math.min(r,g,b),0)) * 0.5 * p
+ r = r + (a-r*p) -- a+r*(1-p)
+ g = g + (a-g*p)
+ b = b + (a-b*p)
+ return r,g,b
+end
+--
+
+--
+function db.desaturateA(percent,c) -- array version
+ local r,g,b,a
+ r = c[1]
+ g = c[2]
+ b = c[3]
+ p = percent / 100
+ a = (math.min(math.max(r,g,b),255) + math.max(math.min(r,g,b),0)) * 0.5 * p
+ r = r + (a-r*p)
+ g = g + (a-g*p)
+ b = b + (a-b*p)
+ return {r,g,b}
+end
+--
+
+--
+function db.desatAVG(desat,c) -- Desaturation, simpe average
+ r = c[1]
+ g = c[2]
+ b = c[3]
+ p = desat / 100
+ a = (r+g+b)/3
+ r = r + p*(a-r)
+ g = g + p*(a-g)
+ b = b + p*(a-b)
+ return {r,g,b}
+end
+--
+
+
+--
+function db.getSaturation(r,g,b) -- HSL
+ local M,m,c,s,l
+ M = math.max(r,g,b)
+ m = math.min(r,g,b)
+ c = (M - m)/255
+ s = 0
+ if c ~= 0 then
+ --l = (0.3*r + 0.59*g + 0.11*b)/255 -- HSLuma: Y'601
+ l = (M+m)/510 -- This produces a quite "correct looking" divison of saturation
+ if l <= 0.5 then s = c / (2*l); end
+ if l > 0.5 then s = c / (2-2*l); end
+ end
+ return math.min(255,s * 255)
+end
+--
+
+--
+function db.getTrueSaturationX(r,g,b) -- Distance from grayscale axis. Not HSV/HSL
+ local sat,bri
+ bri = (r+g+b) / 3
+ sat = math.min(255, math.sqrt((r-bri)^2 + (g-bri)^2 + (b-bri)^2) * 1.224744875)
+ return sat
+end
+--
+
+-- WIP. Trying to find a more natural model for estimating Saturation
+-- Current: (HSL + True) / 2
+function db.getAppSaturation(r,g,b)
+ return math.min(255, (db.getSaturation(r,g,b) + db.getTrueSaturationX(r,g,b)) / 2)
+end
+--
+
+--
+function db.saturate(percent,r,g,b)
+ local a,m,p,mc
+ a = (math.min(math.max(r,g,b),255) + math.max(math.min(r,g,b),0)) * 0.5
+ m = math.min(255-math.max(r,g,b), math.min(r,g,b))
+ p = percent * (m / 100)
+ mc = math.max((r-a),(g-a),(b-a)) -- Can this be derived elsewhere?
+ if mc ~= 0 then
+ r = r + (r-a) * p / mc
+ g = g + (g-a) * p / mc
+ b = b + (b-a) * p / mc
+ end
+ return r,g,b
+end
+--
+
+--
+-- Super Saturate: Better than Photoshop etc.
+--
+-- Higher than 100% power is ok
+--
+function db.saturateAdv(percent,r,g,b,brikeeplev,greydamp) -- brikeep = 0 - 2
+ local a,m,p,mc,bri_o,bri_n,bdiff,mx,mi,adj,q,n
+ function cap(v) return math.max(0,math.min(v,255)); end
+ mx = math.max(r,g,b)
+ mi = math.min(r,g,b)
+ bri_o = db.getBrightness(r,g,b)
+ a = (math.min(mx,255) + math.max(mi,0)) * 0.5
+ m = math.min(255-mx, mi)
+ p = percent * (m / 100)
+ mc = math.max((r-a),(g-a),(b-a)) -- Can this be derived elsewhere?
+ if mc ~= 0 and m ~= 0 then
+ adj = math.min(1,(mx - mi) / m) -- Reduce effect on low saturation
+ if greydamp == false then adj = 1; end
+ q = p / mc * adj
+ r = cap( r + (r-a) * q )
+ g = cap( g + (g-a) * q )
+ b = cap( b + (b-a) * q )
+ end
+ for n = 0, brikeeplev*2, 1 do -- Must iterate to reduce brightness error
+ bri_n = db.getBrightness(r,g,b)
+ bdiff = (bri_o - bri_n) / 2 * brikeeplev
+ r = cap(r + bdiff)
+ g = cap(g + bdiff)
+ b = cap(b + bdiff)
+ end
+ return r,g,b
+end
+--
+
+
+--
+-- Lightness: Darken / Brighten color (Argument and returnvalue is a rgb-list)
+-- Rate of change is inversely proportional to the distance of the max/min.
+-- i.e. all colors/channels will reach max/min at the same time (at 0 or 100 %)
+-- (As opposed to 'Brightness' where all channels are changed by a constant value)
+--
+function db.lightness(percent,c)
+ local v,r,g,b,p
+ r = c[1]
+ g = c[2]
+ b = c[3]
+ p = math.abs(percent/100)
+ v = 255
+ if percent < 0 then v = 0; end
+ r = r + (v - r)*p
+ g = g + (v - g)*p
+ b = b + (v - b)*p
+ return {r,g,b}
+end
+--
+
+--
+function db.changeLightness(r,g,b,percent)
+ local v
+ v = db.lightness(percent,{r,g,b})
+ return v[1],v[2],v[3]
+end
+--
+
+--
+function db.getLightness(r,g,b) -- HSL bi-hexcone
+ return (math.max(r,g,b) + math.min(r,g,b)) / 2
+end
+--
+
+--
+function db.shiftHUE(r,g,b,deg) -- V1.3 R.Fhager 2007, (Heavily derived code, hehe...)
+ local c,h,mi,mx,d,s,p,i,f,q,t
+ c = {g,b,r}
+ mi = math.min(r,g,b)
+ mx = math.max(r,g,b); v = mx;
+ d = mx - mi;
+ s = 0; if mx ~= 0 then s = d/mx; end
+ p = 1; if g ~= mx then p = 2; if b ~= mx then p = 0; end; end
+
+ if s~=0 then
+ h=(deg/60+(6+p*2+(c[1+p]-c[1+(p+1)%3])/d))%6;
+ i=math.floor(h);
+ f=h-i;
+ p=v*(1-s);
+ q=v*(1-s*f);
+ t=v*(1-s*(1-f));
+ c={v,q,p,p,t,v}
+ r = c[1+i]
+ g = c[1+(i+4)%6]
+ b = c[1+(i+2)%6]
+ end
+
+ return r,g,b
+end
+--
+
+--
+function db.getHUE(r,g,b,greytol) -- 0-6 (6.5 = Greyscale), mult. with 60 for degrees
+ -- 1 Color diff is roughly detected by Tolerance = 0.0078125 (Tol. incr. with lightness etc.)
+ local c,h,mi,mx,d,s,p,i,f,q,t
+ c = {g,b,r}
+ mi = math.min(r,g,b)
+ mx = math.max(r,g,b); v = mx;
+ d = mx - mi;
+ s = 0; if mx ~= 0 then s = d/mx; end
+ p = 1; if g ~= mx then p = 2; if b ~= mx then p = 0; end; end
+
+ h = 6.5 -- for custom graphical purposes
+ if s>greytol then -- can't use >=
+ h=(6+p*2+(c[1+p]-c[1+(p+1)%3])/d)%6;
+ end
+
+ return h
+end
+--
+
+--
+-- ... eof RGB color modifications ...
+--
+
+
+
+-- ****************************************
+-- *** Custom Color / Palette functions ***
+-- ****************************************
+
+
+--# of Unique colors in palette:
+--#db.fixPalette(db.makePalList(256))
+
+--# of Colors in Image:
+--#db.makePalListFromHistogram(db.makeHistogram())
+
+--# of Unique colors in Image:
+--#db.fixPalette(db.makePalListFromHistogram(db.makeHistogram()))
+
+--
+function db.rgbcap(r,g,b,mx,mi)
+ local m = math
+ return m.max(mi,m.min(r,mx)), m.max(mi,m.min(g,mx)), m.max(mi,m.min(b,mx))
+end
+--
+
+--
+function db.rgbcapInt(r,g,b,mx,mi)
+ local m = math
+ return m.floor(m.max(mi,m.min(r,mx))), m.floor(m.max(mi,m.min(g,mx))), m.floor(m.max(mi,m.min(b,mx)))
+end
+--
+
+
+--
+function db.makePalList(cols)
+ local pal,n,r,g,b
+ pal = {}
+ for n = 0, cols-1, 1 do
+ r,g,b = getcolor(n)
+ pal[n+1] = {r,g,b,n}
+ end
+ return pal
+end
+--
+
+--
+function db.makeSparePalList(cols)
+ local pal,n,r,g,b
+ pal = {}
+ for n = 0, cols-1, 1 do
+ r,g,b = getsparecolor(n)
+ pal[n+1] = {r,g,b,n}
+ end
+ return pal
+end
+--
+
+
+--
+-- Use to remove the black colors (marks unused colors) from palette-list
+-- if it's known that no black color exists in the image.
+function db.stripBlackFromPalList(pallist)
+ local i,u,c,dummy; i = 257 -- Do 'nothing' If using a full 256 col palette with no blacks
+ for u = 1, #pallist, 1 do
+ c = pallist[u]
+ if (c[1]+c[2]+c[3]) == 0 then i = u; end
+ end
+ dummy = table.remove(pallist,i)
+ return pallist
+end
+--
+
+--
+function db.stripIndexFromPalList(pallist,colindex)
+ local i,u,c,dummy
+ for u = 1, #pallist, 1 do
+ c = pallist[u]
+ if c[4] == colindex then i = u; end
+ end
+ dummy = table.remove(pallist,i)
+ return pallist
+end
+--
+
+--
+function db.addHSBtoPalette(pallist)
+ local n,hue,sat,rgb
+ for n=1, #pallist, 1 do
+ rgb = pallist[n]
+ pallist[n][5] = db.getHUE(rgb[1],rgb[2],rgb[3],0)
+ pallist[n][6] = db.getSaturation(rgb[1],rgb[2],rgb[3])
+ pallist[n][7] = db.getBrightness(rgb[1],rgb[2],rgb[3])
+ end
+ return pallist -- {r,g,b,n,bri,hue,sat}
+end
+--
+
+--
+function db.makePalListRange(start,ends)
+ local pal,n,r,g,b,a
+ pal = {}
+ a = 1
+ for n = start, ends, 1 do
+ r,g,b = getcolor(n)
+ pal[a] = {r,g,b,n}; a = a + 1;
+ end
+ return pal
+end
+--
+
+
+--
+function db.makePalListShade(cols,sha) -- Convert colors to less bits, colorcube operations etc.
+ local pal,n,r,g,b,mf,div
+ mf = math.floor
+ div = 256 / sha
+ pal = {}
+ for n = 0, cols-1, 1 do
+ r,g,b = getcolor(n)
+ pal[n+1] = {mf(r/div),mf(g/div),mf(b/div),n}
+ end
+ return pal
+end
+--
+--
+function db.makePalListShadeSPARE(cols,sha) -- Convert colors to less bits, colorcube operations etc.
+ local pal,n,r,g,b,mf,div
+ mf = math.floor
+ div = 256 / sha
+ pal = {}
+ for n = 0, cols-1, 1 do
+ r,g,b = getsparecolor(n)
+ pal[n+1] = {mf(r/div),mf(g/div),mf(b/div),n}
+ end
+ return pal
+end
+--
+
+
+
+--
+function db.getColorDistance_weight(r1,g1,b1,r2,g2,b2,rw,gw,bw)
+ return math.sqrt( (rw*(r1-r2))^2 + (gw*(g1-g2))^2 + (bw*(b1-b2))^2 )
+end
+--
+
+--
+-- Since brightness is exponential, each channel may work as a "star" drowning the color
+-- of a lesser channel. This algorithm is an approximation to adjust distances for this phenomenon.
+-- Ex: Adding 32 red to black is visually obvious, but adding 64 red to full green is almost
+-- impossible to detect by the naked eye.
+--
+-- However this isn't a complete solution so we may weigh in brightness as well...
+--
+-- If cv = 0 (0..1) then prox acts as ordinary perceptual colordistance
+-- if bri = 1 (0..1) then distance is only that of brightness
+function db.getColorDistanceProx(r1,g1,b1,r2,g2,b2,rw,gw,bw,normalizer, cv,briweight)
+ local rp1,gp1,bp1,rp2,gp2,bp2,v,m1,m2,prox,bdiff; v = 2*255*255
+ m1 = math.max(r1,g1,b1)
+ m2 = math.max(r2,g2,b2)
+ rp1 = 1 - math.sqrt((r1-m1)^2 / v) * cv
+ gp1 = 1 - math.sqrt((g1-m1)^2 / v) * cv
+ bp1 = 1 - math.sqrt((b1-m1)^2 / v) * cv
+
+ rp2 = 1 - math.sqrt((r2-m2)^2 / v) * cv
+ gp2 = 1 - math.sqrt((g2-m2)^2 / v) * cv
+ bp2 = 1 - math.sqrt((b2-m2)^2 / v) * cv
+
+ bdiff = math.abs(db.getBrightness(r1,g1,b1) - db.getBrightness(r2,g2,b2)) -- weights are hardcoded in function
+ prox = math.sqrt( (rw*(r1*rp1-r2*rp2))^2 + (gw*(g1*gp1-g2*gp2))^2 + (bw*(b1*bp1-b2*bp2))^2 ) * normalizer
+
+ return prox * (1-briweight) + bdiff * briweight
+end
+--
+
+--
+function db.getBestPalMatch(r,g,b,pal,index_flag) -- pal = [r,g,b,palindex], index_flag -> return palindex if pal is sorted or reduced
+ local diff,best,bestcol,cols,n,c,p
+ cols = #pal
+ bestcol = -1
+ best = 9e99
+
+ for n=1, cols, 1 do
+ p = pal[n]
+ diff = db.getColorDistance_weight(r,g,b,p[1],p[2],p[3],0.26,0.55,0.19) * 1.569
+ if diff < best then bestcol = n; best = diff; end
+ end
+
+ if index_flag == true then
+ bestcol = pal[bestcol][4] + 1
+ end
+
+ return bestcol-1 -- palList index start at 1, image-palette at 0
+end
+--
+
+
+-- Normally this function will return the (image)palette index of best color
+-- ...but if the palette has been sorted with 'fixPalette' it will return the index
+-- of the custom palList, setting index_flag will convert this value to image-palette index
+--
+-- HYBRID means the colormatch is a combo of color and (perceptual)brightness
+--
+--
+function db.getBestPalMatchHYBRID(rgb,pal,briweight,index_flag) -- Now correctly balanced
+ local diff,diffC,diffB,best,bestcol,cols,n,c,r,g,b,p,obri,pbri
+ cols = #pal
+ bestcol = -1
+ best = 9e99
+
+ --messagebox(briweight)
+
+ -- Note: Not secured against negative values (this algorithm is SLOW, we cannot afford it)
+ r = rgb[1]
+ g = rgb[2]
+ b = rgb[3]
+
+ obri = db.getBrightness(r,g,b) -- 0-255
+
+ for n=1, cols, 1 do
+ p = pal[n]
+ pbri = db.getBrightness(p[1],p[2],p[3])
+ diffB = math.abs(obri - pbri)
+ -- we need to normalize the distance by the weights
+ diffC = db.getColorDistance_weight(r,g,b,p[1],p[2],p[3],0.26,0.55,0.19) * 1.569
+
+ diff = briweight * (diffB - diffC) + diffC
+ if diff < best then bestcol = n; best = diff; end
+ end
+
+ if index_flag == true then
+ bestcol = pal[bestcol][4] + 1 -- Since we detract 1 on return, God Lua is stupid
+ end
+
+ return bestcol-1 -- palList index start at 1, image-palette at 0
+end
+--
+
+
+
+--
+-- Special version of Hybrid-remapping for mixPalette list
+--
+-- mixpal: {score,col#1,col#2,dist,rm,gm,bm, c1_r,c1_g,c1_b, c2_r,c2_g,c2_b}
+--
+-- returns: {col#1,col#2} (index of palette)
+--
+function db.getBestPalMatchHybridMIX(rgb,mixpal,briweight,mixreduction)
+ local diff,diffC,diffB,best,bestcol,cols,n,c,r,g,b,p,obri,pbri, distmult
+ cols = #mixpal
+ bestcol = -1
+ best = 9e99
+
+ -- We will simply add the the distance to the mix with the distance between the mixcolors and
+ -- employ a user tolerance to much the latter will matter.
+ --distmult = 255 / 9.56 / 100 * mixreduction -- 16 shades
+ distmult = 1.56902 / 100 * mixreduction -- 24-bit, Dawn3.0 colormodel
+
+ -- Note: Not secured against negative values (this algorithm is SLOW, we cannot afford it)
+ r = rgb[1]
+ g = rgb[2]
+ b = rgb[3]
+
+ obri = db.getBrightness(r,g,b) -- 0-255
+
+ for n=1, cols, 1 do
+ p = mixpal[n]
+ --pbri = db.getBrightness(p[5],p[6],p[7])
+
+ -- *** DawnBringer's exponetial color brightness dither resolution phenomena theorem ***
+ -- Bri = color value ^ 2
+ -- Two adjacent pixels displayed with "normal high resolution" will NOT have the perceptual
+ -- brightness of the resulting mixcolor. The brightness lies closer to that of the brightest pixel.
+ -- Bri[(C1+C2)/2] = SQRT( (C1bri^2 + C2bri^2) / 2 )
+ -- (Brightness according to Dawn-model: bri = SQRT( (r*.26)^2 + (g*.55)^2 + (b*.19)^2 ) )
+
+ pbri = math.sqrt((db.getBrightness(p[8],p[9],p[10])^2 + db.getBrightness(p[11],p[12],p[13])^2) / 2)
+
+ diffB = math.abs(obri - pbri)
+ -- we need to normalize the distance by the weights
+ diffC = db.getColorDistance_weight(r,g,b,p[5],p[6],p[7],0.26,0.55,0.19) * 1.569 + p[4]*distmult
+
+ diff = briweight * (diffB - diffC) + diffC
+ if diff <= best then bestcol = n; best = diff; end
+ end
+
+ return {mixpal[bestcol][2], mixpal[bestcol][3]}
+--return {mixpal[bestcol][2], 0}
+
+
+
+end
+--
+
+
+
+--
+function db.matchcolorHSB(h,s,b,pallist,index_flag)
+ --
+ -- why don't we just convert HSB-diagram to RGB and do normal colormatching?
+ -- Not the same...
+ --
+ local n,c,best,bestcol,pb,ph,ps,diff,huediff,huecorr,hue_adj,sat_adj,bri_adj
+ bestcol = -1
+ best = 9e99
+
+ -- higher adjust means more impact (higher hue gives more interpolation )
+ hue_adj = 4
+ sat_adj = 0.075
+ bri_adj = 2
+
+ huecorr = 255 / 6 -- Our Hue goes from 0.0 - 5.999
+
+ for n=1, #pallist, 1 do
+ c = pallist[n]
+ ph = c[5]
+ ps = c[6]
+ pb = c[7]
+
+ huediff = math.abs(h-ph*huecorr)
+ if huediff > 127 then huediff = huediff - (huediff % 127) * 2; end
+
+ --if ph == 6.5 then huediff = 0; end
+
+ -- With less saturation, exact hue becomes less important and brightness more usefull
+ -- This allows for greyscales and low saturation colors to work smoothly.
+ huediff = huediff * (ps /255)
+
+ diff = hue_adj*huediff^2 + (s-ps)^2 * sat_adj + (b-pb)^2 * bri_adj
+
+ if diff <= best then bestcol = n; best = diff; end
+ end
+
+ if index_flag == true then
+ bestcol = pallist[bestcol][4] + 1 -- Since we detract 1 on return, God Lua is stupid
+ end
+
+ return bestcol-1
+
+end
+--
+
+--
+-- Used by PaletteAnalysis.lua, FindRamps(), MixColors() etc.
+-- Assigns is used by ApplySpare script
+--
+function db.fixPalette(pal,sortflag) -- Arrange palette & only keep unique colors
+
+ local n,l,rgb,i,unique,bri,hue,sat,ulist,indexpal,newpal,dtot
+ ulist = {}
+ indexpal = {}
+ newpal = {}
+ local doubles,assign
+ doubles = {}; assign = {}
+
+ l = #pal
+
+ unique = 1 -- ok, see how stupid lua is
+ dtot = 0
+ for n=1, l, 1 do
+ rgb = pal[n]; -- actually rgbn
+ i = 1 + rgb[1] * 65536 + rgb[2] * 256 + rgb[3];
+ bri = db.getBrightness(rgb[1],rgb[2],rgb[3])
+ if indexpal[i] == nil then
+ indexpal[i] = rgb; ulist[unique] = {i,bri}; unique = unique+1;
+ assign[rgb[4]+1] = rgb[4] -- really n, but can we be sure?
+ else
+ doubles[rgb[4]] = true; -- Mark as double (This is wrong; starts at 0...but col 0 cannot be a double so...)
+ dtot = dtot + 1
+ assign[rgb[4]+1] = indexpal[i][4] -- Reassign removed color
+ end
+ end
+
+ -- sort ulist
+ if sortflag == 1 then db.sorti(ulist,2); end -- sort by brightness
+
+ l = #ulist
+ for n=1, l, 1 do
+ newpal[n] = indexpal[ulist[n][1]]
+ end
+
+ newpal["assigns"] = assign -- Complete list of image color assigns (removed cols will point to 1st occurence)
+ newpal["doubles"] = doubles
+ newpal.double_total = dtot
+
+ --messagebox("unique colors", unique-1)
+
+ return newpal
+
+end
+--
+
+--
+function db.drawColorspace12bit(x,y,cols,size)
+ local r,g,b,c,rows,row,col,s16,rx,ry,xx,yy
+ s16 = size*16
+ rows = math.floor(16/cols)
+
+ for g = 0, 15, 1 do
+ col = g % cols
+ row = math.floor(g / cols)
+ for r = 0, 15, 1 do
+ for b = 0, 15, 1 do
+ c = matchcolor(r*17,g*17,b*17)
+ xx = x+col*s16+r*size
+ yy = y+row*s16+b*size
+ for ry = 0, size-1, 1 do
+ for rx = 0, size-1, 1 do
+ putpicturepixel(xx+rx,yy+ry,c)
+ end;end
+ end
+ end
+ end
+end
+--
+
+--
+function db.drawHSBdiagram(pallist,posx,posy,width,height,size,sat)
+ --db.addHSBtoPalette(palList)
+ local x,y,c
+ for y = 0, height-1, 1 do
+ for x = 0, width-1, 1 do
+ hue = 255/width * x
+ bri = 255/height * y
+ c = db.matchcolorHSB(hue,sat,bri,pallist,true)
+ db.drawRectangle(posx + x*size, posy + y*size,size,size, c)
+ end
+ end
+end
+--
+
+--
+function db.polarHSBdiagram(ox,oy,radius,pol,brilev,huelev,saturation,dark2bright_flag)
+
+local pal,bstep,bstep2,hstep,hstep2,bri,hue,sx,sy,cx,cy,x,y,p1,p2,c
+
+pal = db.addHSBtoPalette(db.fixPalette(db.makePalList(256)))
+
+bstep = radius / (brilev + pol)
+bstep2 = bstep / 2
+hstep = -360 / huelev
+hstep2 = hstep / 2
+
+c = 255; if dark2bright_flag then c = 0; end
+drawdisk(ox,oy,math.ceil(pol*bstep),matchcolor(c,c,c))
+
+for y=pol, brilev+pol-1,1 do
+
+ bri = (brilev - y + pol) * (256 / brilev)
+
+ if dark2bright_flag then
+ bri = (brilev - (brilev - y + pol)) * (256 / brilev)
+ end
+
+ for x=0, huelev-1,1 do
+
+ hue = x * (360 / huelev) * 255/360
+
+ c = db.matchcolorHSB(hue,saturation,bri,pal,true)
+
+ sx = ox
+ sy = oy - y*bstep
+
+ cx,cy = db.rotationFrac(x*hstep,ox,oy,sx,sy)
+
+ x1,y1 = db.rotation(x*hstep-hstep2,ox,oy,ox, sy-bstep2)
+ x2,y2 = db.rotation(x*hstep+hstep2,ox,oy,ox, sy-bstep2)
+ x3,y3 = db.rotation(x*hstep-hstep2,ox,oy,ox, sy+bstep2)
+ x4,y4 = db.rotation(x*hstep+hstep2,ox,oy,ox, sy+bstep2)
+
+ p1 = {{x1,y1},{x2,y2},{x3,y3}}
+ p2 = {{x3,y3},{x4,y4},{x2,y2}}
+
+ db.fillTriangle(p1,c,0,true,false) -- triangle, fillcol, linecol, fill, wire
+ db.fillTriangle(p2,c,0,true,false)
+
+ end
+ updatescreen(); if (waitbreak(0)==1) then return; end
+end
+
+end -- polarHSB
+--
+
+
+--
+-- Histograms, remapping etc.
+--
+
+--
+function db.makeHistogram()
+ local n,y,x,c,w,h,list; list = {}
+ w, h = getpicturesize()
+ for n = 1, 256, 1 do list[n] = 0; end
+ for y = 0, h - 1, 1 do
+ for x = 0, w - 1, 1 do
+ c = getpicturepixel(x,y)
+ list[c+1] = list[c+1] + 1
+ end
+ end
+ return list
+end
+--
+
+--
+function db.makeHistogramIndexed() -- With color index so it can be sorted etc.
+ local n,y,x,c,w,h,r,g,b,list; list = {}
+ w, h = getpicturesize()
+ for n = 1, 256, 1 do
+ r,g,b = getcolor(n-1)
+ list[n] = {0,n-1,r,g,b};
+ end
+ for y = 0, h - 1, 1 do
+ for x = 0, w - 1, 1 do
+ c = getpicturepixel(x,y)
+ list[c+1][1] = list[c+1][1] + 1
+ end
+ end
+ return list
+end
+--
+
+--
+function db.makeSpareHistogram()
+ local n,y,x,c,w,h,list; list = {}
+ w, h = getsparepicturesize()
+ --w,h = 512,360
+ for n = 1, 256, 1 do list[n] = 0; end
+ for y = 0, h - 1, 1 do
+ for x = 0, w - 1, 1 do
+ c = getsparepicturepixel(x,y)
+ list[c+1] = list[c+1] + 1
+ end
+ end
+ return list
+end
+--
+
+
+--
+-- Makes a palette-list from only the colors (histogram) that occurs in the image
+-- Assumes image/palette has not changed since histogram was created
+function db.makePalListFromHistogram(hist)
+ local n,r,g,b,list,count
+ list = {}
+ count = 1
+ for n = 1, #hist, 1 do
+ if hist[n] > 0 then
+ r,g,b = getcolor(n-1)
+ list[count] = {r,g,b,n-1}
+ count = count + 1
+ end
+ end
+ return list
+end
+--
+
+function db.makePalListFromSpareHistogram(hist)
+ local n,r,g,b,list,count
+ list = {}
+ count = 1
+ for n = 1, #hist, 1 do
+ if hist[n] > 0 then
+ r,g,b = getsparecolor(n-1)
+ list[count] = {r,g,b,n-1}
+ count = count + 1
+ end
+ end
+ return list
+end
+--
+
+
+--
+function db.remap(org) -- Working with a remap-list there's no need of reading backuppixel
+ --messagebox("Remapping")
+ local x,y,c,i,w,h,s,f,col
+ f = getpicturepixel
+ s = false
+ w, h = getpicturesize()
+ for y = 0, h - 1, 1 do
+ for x = 0, w - 1, 1 do
+ c = f(x,y)
+ i = org[c+1]
+ if i == null then i = matchcolor(getbackupcolor(getbackuppixel(x,y))); s = true; col = c; end -- Find color for a removed double
+ putpicturepixel(x,y,i)
+ end
+ end
+ if s then messagebox("Remapping: Not all image colors were found in remap-list (re-assign), probably due to duplicate removal. Matchcolor was used, ex: col# "..col);
+ end
+end
+--
+
+--
+-- Same as db.remap but no comments
+--
+function db.remapImage(colassignlist) -- assignment list is optional
+ local x,y,c,w,i,h,assign
+ assign = false
+ if colassignlist ~= null then assign = true; end
+ w,h = getpicturesize()
+ for y = 0, h-1, 1 do
+ for x = 0, w-1, 1 do
+ c = getbackuppixel(x,y)
+ i = null; if assign then i = colassignlist[c+1]; end
+ if not assign or i == null then
+ i = matchcolor(getbackupcolor(c))
+ end
+ putpicturepixel(x,y,i)
+ end
+ end
+end
+--
+
+--
+-- Palette DeCluster: Color-reduction by fusing similar colors into new ones, using a desired tolerance.
+-- This is a method similar to Median-Cut, but more surgical.
+--
+-- pallist: Palette list {r,g,b,palette_index}
+-- hist: Histogram {color 0 pixels, color 1 pixels...etc} always a full 256 color list
+-- crad: Cluster radius treshold in % of distance between black & white
+-- A value of 0 will only remove identical colors
+-- A value of 3-4 will usally fuse redundant colors without causing notice
+-- prot_pow: (0..10) Protect common colors in histogram. Distances are increased by ocurrence.
+-- Also gives protection to fused colors even if not using histogram (combined nominal weights)
+-- pixels: Pixels in image (so protection can be calculated)
+-- rw,gw,bw: Color weights (rw+gw+bw = 1, 0.33,0.33,0.33 is nominal)
+--
+-- Returns:
+-- a new (c)palette list {r,g,b,{original palette_indices},fused flag, histogram_weight}
+-- a remap list (org) [image color + 1] = remap color (in the new palette)
+function db.deCluster(pallist, hist, crad, prot_pow, pixels, rw,gw,bw)
+
+ --messagebox(pixels)
+
+ local u,c,a,i,o,j,n,c1,c2,r,g,b,r1,g1,b1,r2,g2,b2,wt,rt,gt,bt,tot,pd
+ local worst,wtot,maxdist,maxDist,distfrac,clusterExists,clustVal,count,crad1
+ local cList,cPalList,clusterList,fuseCol,orgcols,newPalList,org
+
+ maxdist = math.sqrt(rw*rw*65025 + gw*gw*65025 + bw*bw*65025)
+ distfrac = 100 / maxdist
+
+ -- Let's just make a slightly more suitable format of the pallist (List for original color(s))
+ cPalList = {}
+ for u = 1, #pallist, 1 do
+ c = pallist[u]
+ cPalList[u] = {c[1],c[2],c[3],{c[4]},false,hist[c[4]+1]} -- r,g,b,{original colors},fuse_marker,histogram_weight
+ end
+
+ --table.insert(cPalList,{255,255,0,{257},false,1})
+
+ clusterExists = true
+ while clusterExists do
+ clusterExists = false
+ clusterList = {}
+
+ crad1 = crad + 1 -- avoid divison by zero
+ worst = 9999
+ for a = 1, #cPalList, 1 do
+ c1 = cPalList[a]
+ r1,g1,b1 = c1[1],c1[2],c1[3]
+ wtot = c1[6]
+ cList = {a}
+ maxDist = 0
+ for b = 1, #cPalList, 1 do
+ if (b ~= a) then
+ c2 = cPalList[b]
+ r2,g2,b2 = c2[1],c2[2],c2[3]
+ wt = c2[6]
+ pd = math.pow((1 + wt / pixels), prot_pow) -- Protection, increase distance
+ dist = db.getColorDistance_weight(r1,g1,b1,r2,g2,b2,rw,gw,bw) * distfrac * pd
+ if dist <= crad then
+ wtot = wtot + wt
+ table.insert(cList,b)
+ maxDist = math.max(dist,maxDist)
+ end
+ end
+ end -- b
+ if #cList > 1 then
+ clustVal = maxDist / (crad1 * #cList) * (wtot / #cList)
+ if clustVal < worst then
+ worst = clustVal
+ clusterList = cList
+ end
+ end
+ end -- a
+
+ --t = db.ary2txt(clusterList)
+ --messagebox("Worst cluster is "..t)
+
+ -- Fuse
+ if #clusterList > 1 then
+ clusterExists = true -- Run another iteration and look for more clusters
+ fuseCol = {0,0,0,{}}
+ rt,gt,bt,tot = 0,0,0,0
+ for n = 1, #clusterList, 1 do
+ i = clusterList[n]
+ c = cPalList[i]
+ --o = c[4][1] -- Original color (always #1 in list since fused colors can't re-fuse)
+ o = c[4] -- Original color list
+ --if c[5] == true then messagebox("Re-Fusing..."); end
+ r,g,b = c[1],c[2],c[3]
+ --wt = hist[o+1] -- Org. colors are 0-255
+ wt = c[6]
+ rt = rt + r * wt
+ gt = gt + g * wt
+ bt = bt + b * wt
+ tot = tot + wt
+ cPalList[i] = -1 -- Erase color
+ --table.insert(fuseCol[4],o)
+ orgcols = fuseCol[4]
+ for j = 1, #o, 1 do
+ table.insert(orgcols,o[j])
+ end
+ fuseCol[4] = orgcols
+ end
+
+ rt = rt / tot
+ gt = gt / tot
+ bt = bt / tot
+ fuseCol[1] = rt
+ fuseCol[2] = gt
+ fuseCol[3] = bt
+ fuseCol[5] = true -- fusecol marker
+ fuseCol[6] = tot
+ table.insert(cPalList,fuseCol)
+ --messagebox(#clusterList.." Colors was fused, resulting in "..rt..", "..gt..", "..bt)
+ newPalList = {}
+ for n = 1, #cPalList, 1 do
+ if cPalList[n] ~= -1 then
+ table.insert(newPalList,cPalList[n])
+ --newPalList = db.newArrayInsertLast(newPalList,cPalList[n])
+ end
+ end
+ cPalList = newPalList
+ --messagebox("Pal length: "..#cPalList)
+ statusmessage("DC - Image colors: "..#cPalList.." "); waitbreak(0)
+ end -- fuse
+
+ end -- while
+
+ -- Create remap-list
+ org = {}
+ count = 0
+ for u = 1, #cPalList, 1 do
+ c = cPalList[u]
+ for n = 1, #c[4], 1 do
+ i = c[4][n]
+ org[i+1] = count -- quick way to remap without matchcolor
+ end
+ count = count + 1
+ end
+
+ return org,cPalList
+
+end; -- decluster
+
+
+
+-- ------------- MEDIAN CUT V1.0 ------------
+--
+-- 256 color Palette Lua-version (converted from Evalion JS-script)
+--
+-- by Richard 'DawnBringer' Fhager
+--
+--
+-- pal: [[r,g,b,i]] Pallist
+-- cnum: Target number of colors in reduced palette
+-- (step:) 1.. Pixel picks for processing, 1 = all pixels in image, best and slowest. 2 = 25% of pixels
+-- qual: Flag Qualitative color selection (Normal mode)
+-- quant: Flag Quantative color/pixel selection (Histogram) 100% mean that it count as much as quality
+-- (One of or both qual/quant must be selected)
+-- rgbw: [3] RGB-weights []. Weigh the color channels. ex: [1,1.333,0.75]
+-- bits: 1-8 Bits used for each color channel in palette
+-- quantpow: 0..1
+--
+-- return: A palette! A list of [r,g,b] values
+--
+-- NOTE: Quantity will act as a counterforce to altered colorspace (weights)...
+-- Ex: if Green is considered bigger, it will be split into more blocks
+-- but each blocks will have less colors and thus less quantity.
+--
+-- Perceptual colorspace (rgb-weights) will generally produce the best quality of palettes, but if
+-- It's desirable to preserve stronger colors, esp. in small palettes,
+-- it can be good to just use nominal space: 1,1,1
+--
+-- Histogram may be useful to assign more colors to an object that already covers most of the image,
+-- however; if the object of interest is small in relation to a large (unimportant) background, it's
+-- usually best not to have any histogram at all. Histogram will dampen strong infrequent colors.
+
+
+function db.medianCut(pal,cnum,qual,quant,rgbw,bits,quantpow)
+ local n,x,y,xs,ys,rgb,blocklist,blocks
+ local len,res,chan,diff,maxdiff,maxblock,split
+ local qualnorm, quantnorm
+
+ -- Normalize 256 for quality/quantity relationship
+ qualnorm = 1 / math.sqrt(rgbw[1]^2 + rgbw[2]^2 + rgbw[3]^2)
+ quantnorm = 256 / #pal
+
+ blocklist = {}
+ blocklist[1] = {}; blocks = 1
+
+ for n=1, #pal, 1 do
+ blocklist[1][n] = pal[n];
+ end
+
+ analyzeBlock(blocklist[1],qual,quant,rgbw,qualnorm,quantnorm,quantpow)
+
+ failsafe = 0
+ while (blocks < cnum and failsafe < 256) do
+ failsafe = failsafe + 1
+ maxdiff = -1
+ maxblock = -1
+ for n=1, blocks, 1 do
+ diff = blocklist[n].diff
+ if (diff > maxdiff) then maxdiff = diff; maxblock = n; end -- maxchan is stored as .chan in block
+ end
+ split = splitBlock(blocklist,maxblock,qual,quant,rgbw,qualnorm,quantnorm,quantpow)
+ --if (split == false){ alert("Only found " + blocks + " (24-bit) colors!"); break; }
+ blocks = #blocklist
+ --status.value = "MC: " +blocks
+ end -- while
+
+ return blocks2Palette(blocklist,bits)
+
+end
+--
+
+--
+function blocks2Palette(blocklist,bits)
+ local n,r,g,b,c,pal,block,rgb,blen,M,dB,cB,rf,gf,bf
+
+ M = math
+ pal = {}
+
+ --bits = 1
+ dB = M.pow(2,8-bits)
+ cB = M.ceil(255 / (M.pow(2,bits) - 1))
+
+ for n=1, #blocklist, 1 do
+ block = blocklist[n]
+ r,g,b = 0,0,0
+ blen = #block
+ for c=1, blen, 1 do
+ rgb = block[c]
+ r = r + rgb[1]
+ g = g + rgb[2]
+ b = b + rgb[3]
+ end
+
+ rf = M.floor(M.min(255,M.max(0,M.floor(r/blen))) / dB) * cB
+ gf = M.floor(M.min(255,M.max(0,M.floor(g/blen))) / dB) * cB
+ bf = M.floor(M.min(255,M.max(0,M.floor(b/blen))) / dB) * cB
+
+ pal[n] = {rf, gf, bf, 0} -- col is avg. of all colors in block (index is set (to 0) for compatibility)
+ end -- blocklist
+
+ return pal
+end
+--
+
+--
+function analyzeBlock(block,qual,quant,rgbw,qualnorm,quantnorm,quantpow)
+ local r,g,b,n,rmin,gmin,bmin,rmax,gmax,bmax,rdif,gdif,bdif,chan,d,median,diff
+ local len,Mm,Mx,rgb,kv,qu
+
+ Mx,Mm = math.max, math.min
+ len = #block
+
+ rmin,gmin,bmin = 255,255,255
+ rmax,gmax,bmax = 0,0,0
+
+ for n=1, len, 1 do
+ rgb = block[n]
+ r = rgb[1] * rgbw[1]
+ g = rgb[2] * rgbw[2]
+ b = rgb[3] * rgbw[3]
+ --if (!isNaN(r) and !isNaN(g) and !isNaN(b)) then -- Ignore any erroneous data
+ rmin = Mm(rmin,r)
+ gmin = Mm(gmin,g)
+ bmin = Mm(bmin,b)
+ rmax = Mx(rmax,r)
+ gmax = Mx(gmax,g)
+ bmax = Mx(bmax,b)
+ --end
+ end
+
+ rdif = (rmax - rmin) -- * rgbw[1]
+ gdif = (gmax - gmin) -- * rgbw[2]
+ bdif = (bmax - bmin) -- * rgbw[3]
+
+ d = {{rmin,rdif,rmax},{gmin,gdif,gmax},{bmin,bdif,bmax}}
+
+ chan = 1 -- Widest channel
+ if (gdif > rdif) then chan = 2; end
+ if (bdif > rdif and bdif > gdif) then chan = 3; end
+
+ -- Ok, this is the average of the max/min value rather than an actual median
+ -- I guess this will fill the colorspace more uniformly and perhaps select extremes to a greater extent?
+ -- Which is better?
+ --median = d[chan][1] + d[chan][2] / 2 -- OLD same as median with nominal weights
+
+ median = (d[chan][1] + d[chan][3]) / 2
+
+ -- quantity and quality are normalized to 256 (256 is the total of colors in the set for quantity)
+ -- Note that, regardless of forumla, quality (distance) must always be greater in any block than quantity (colors/pixels)
+ -- Coz a block may contain many of only 1 unique color, thus rendering it impossible to split if selected.
+ kv = 1
+ qu = 1
+ if (quant) then kv = 1 + len*quantnorm*quantpow; end
+ if (qual) then qu = d[chan][2] * qualnorm; end
+ diff = qu + qu*kv^2.5
+
+ block.chan = chan
+ block.diff = diff
+ block.median = median
+
+ return {chan,diff,median,len}
+
+end
+--
+
+function splitBlock(blocklist,maxblock,qual,quant,rgbw,qualnorm,quantnorm,quantpow)
+ local n,cmax,median,blockA,blockB,len,cB,block,rgb,res
+
+ blockA,blockB = {},{}
+ block = blocklist[maxblock]
+
+ res = true
+
+ chan = block.chan
+ median = block.median
+
+ cB = blocklist[maxblock] -- maxblock starts at 1 when called so it should not hava a +1
+ len = #cB
+
+ for n=1, len, 1 do
+ rgb = cB[n]
+ --if (rgb[chan] >= median) then blockA.push(rgb); end
+ --if (rgb[chan] < median) then blockB.push(rgb); end
+ if (rgb[chan]*rgbw[chan] >= median) then table.insert(blockA,rgb); end
+ if (rgb[chan]*rgbw[chan] < median) then table.insert(blockB,rgb); end
+ end
+
+ blocklist[maxblock] = blockA -- Can't be empty right?
+ analyzeBlock(blocklist[maxblock],qual,quant,rgbw,qualnorm,quantnorm,quantpow)
+
+ if (#blockB > 0) then
+ table.insert(blocklist,blockB)
+ analyzeBlock(blocklist[#blocklist],qual,quant,rgbw,qualnorm,quantnorm,quantpow) -- no -1 on blocklist
+ else
+ res = false
+ end
+
+ return res -- false = no split
+end
+
+------------ eof MEDIAN CUT --------------------------
+
+
+-- ------------- MEDIAN REDUX V1.0 ------------
+--
+-- Divide space by greatest distance of any two colors (rather than MC-method of any given channel)
+-- Basically it allows colorspace to be sliced at any angles rather than the "boxing" of MC.
+--
+--
+-- by Richard 'DawnBringer' Fhager
+--
+--
+-- pal: [[r,g,b,i,h]] Pallist (h = histogram/pixelcount)
+-- cnum: Target number of colors in reduced palette
+-- (step:) 1.. Pixel picks for processing, 1 = all pixels in image, best and slowest. 2 = 25% of pixels
+-- qual: Flag Qualitative color selection (Normal mode)
+-- quant: Flag Quantative color/pixel selection (Histogram) 100% mean that it count as much as quality
+-- (One of or both qual/quant must be selected)
+-- rgbw: [3] RGB-weights []. Weigh the color channels. ex: [0.26, 0.55, 0.19]
+-- bits: 1-8 Bits used for each color channel in palette
+-- quantpow: 0..1 Quantity vs Quality (put weight into histogram/pixelcount)
+-- briweight: 0..1 Brightness distance weight in colordistance
+-- proxweight: 0..1 Primary Proximity distance weight in colordistance (ColorTheory-WIP: compensate for brightness of individual channels, the "extra power" of primary colors)
+--
+-- return: A palette! A list of [r,g,b] values
+--
+-- NOTE: Quantity will act as a counterforce to altered colorspace (weights)...
+-- Ex: if Green is considered bigger, it will be split into more blocks
+-- but each blocks will have less colors and thus less quantity.
+--
+-- Perceptual colorspace (rgb-weights) will generally produce the best quality of palettes, but if
+-- It's desirable to preserve stronger colors, esp. in small palettes,
+-- it can be good to just use nominal space: 0.33, 0.33, 0.33
+--
+-- Histogram may be useful to assign more colors to an object that already covers most of the image,
+-- however; if the object of interest is small in relation to a large (unimportant) background, it's
+-- usually best not to have any histogram at all. Histogram will dampen strong infrequent colors.
+
+
+function db.medianRedux(pal,cnum,qual,quant,rgbw,bits,quantpow, briweight, proxweight) -- pal[r,g,b,i,pixelcount]
+ local n,x,y,xs,ys,rgb,blocklist,blocks
+ local len,res,chan,diff,maxdiff,maxblock,split
+ local qualnorm, quantnorm,count
+
+ blocklist = {}
+ blocklist[1] = {}; blocks = 1
+
+ count = 0
+ for n=1, #pal, 1 do
+ blocklist[1][n] = pal[n];
+ count = count + pal[n][5]
+ end
+
+ -- Normalize 256 for quality/quantity relationship
+ qualnorm = 1 / math.sqrt(rgbw[1]^2 + rgbw[2]^2 + rgbw[3]^2)
+ quantnorm = 256 / count
+
+
+ -- Dist table
+ statusmessage("MR: Making Distance Table..."); updatescreen(); if (waitbreak(0)==1) then return; end
+ local dy,c,r1,g1,b1,i1,i2
+ dt = {}
+ for n=1, #pal, 1 do
+ c = pal[n]
+ r1,g1,b1,i1 = c[1],c[2],c[3],c[4]
+ dt[i1+1] = {}
+ for m=1, #pal, 1 do
+ dt[i1+1][pal[m][4]+1] = db.getColorDistanceProx(r1,g1,b1,pal[m][1],pal[m][2],pal[m][3],rgbw[1],rgbw[2],rgbw[3],qualnorm, proxweight, briweight) -- pri/bri
+ end
+ end
+ --
+
+ statusmessage("MR: Analyzing Block 1..."); updatescreen(); if (waitbreak(0)==1) then return; end
+ r_analyzeBlock(dt,blocklist[1],qual,quant,rgbw,qualnorm,quantnorm,quantpow)
+
+ statusmessage("MR: Analyzing Blocks..."); updatescreen(); if (waitbreak(0)==1) then return; end
+ failsafe = 0
+ while (blocks < cnum and failsafe < 256) do
+ failsafe = failsafe + 1
+ maxdiff = -1
+ maxblock = -1
+ for n=1, blocks, 1 do
+ diff = blocklist[n].diff
+ if (diff > maxdiff) then maxdiff = diff; maxblock = n; end -- maxchan is stored as .chan in block
+ end
+ split = r_splitBlock(dt,blocklist,maxblock,qual,quant,rgbw,qualnorm,quantnorm,quantpow)
+ if (split == false) then messagebox("Only found "..blocks.." (24-bit) colors!"); break; end
+ blocks = #blocklist
+ statusmessage("MR: "..blocks); updatescreen(); if (waitbreak(0)==1) then return; end
+ end -- while
+
+ return r_blocks2Palette(blocklist,bits)
+
+end
+--
+
+--
+function r_blocks2Palette(blocklist,bits)
+ local n,r,g,b,c,pal,block,rgb,blen,M,dB,cB,rf,gf,bf
+
+ M = math
+ pal = {}
+
+ --bits = 1
+ dB = M.pow(2,8-bits)
+ cB = M.ceil(255 / (M.pow(2,bits) - 1))
+
+ for n=1, #blocklist, 1 do
+ block = blocklist[n]
+ r,g,b = 0,0,0
+ blen = #block
+ for c=1, blen, 1 do
+ rgb = block[c]
+ r = r + rgb[1]
+ g = g + rgb[2]
+ b = b + rgb[3]
+ end
+
+ rf = M.floor(M.min(255,M.max(0,M.floor(r/blen))) / dB) * cB
+ gf = M.floor(M.min(255,M.max(0,M.floor(g/blen))) / dB) * cB
+ bf = M.floor(M.min(255,M.max(0,M.floor(b/blen))) / dB) * cB
+
+ pal[n] = {rf, gf, bf} -- col is avg. of all colors in block
+ end -- blocklist
+
+ return pal
+end
+--
+
+--
+function r_analyzeBlock(dt,block,qual,quant,rgbw,qualnorm,quantnorm,quantpow)
+ local r,g,b,n,m,rmin,gmin,bmin,rmax,gmax,bmax,rdif,gdif,bdif,chan,d,median,diff
+ local len,Mm,Mx,rgb,kv,qu
+ local maxdist,dist,r1,g1,b1,r2,g2,b2,c1,c2,count
+
+ Mx,Mm = math.max, math.min
+ len = #block
+
+ rmin,gmin,bmin = 255,255,255
+ rmax,gmax,bmax = 0,0,0
+
+ maxdist,c1,c2,count = 0,-1,-1,0
+
+ for n=1, len, 1 do
+ rgb1 = block[n]
+ count = count + rgb1[5] -- pixelcount for color
+ for m=n+1, len, 1 do
+ rgb2 = block[m]
+ --dist = db.getColorDistanceProx(r1,g1,b1,r2,g2,b2,0.26,0.55,0.19,1.569, 0.1, 0.25) -- pri/bri
+ dist = dt[rgb1[4]+1][rgb2[4]+1]
+
+ if dist > maxdist then
+ maxdist = dist
+ c1 = rgb1[4]+1
+ c2 = rgb2[4]+1
+ end
+
+ end
+ end
+
+ -- quantity and quality are normalized to 256 (256 is the total of colors in the set for quantity)
+ -- Note that, regardless of forumla, quality (distance) must always be greater in any block than quantity (colors/pixels)
+ -- Coz a block may contain many of only 1 unique color, thus rendering it impossible to split if selected.
+ kv = 1
+ qu = 1
+ if (quant) then kv = math.pow(1 + count*quantnorm*quantpow, 0.5); end
+ if (qual) then qu = maxdist * qualnorm; end
+ diff = qu*(1-quantpow) + qu*kv
+
+ block.chan = -1
+ block.diff = diff
+ block.median = -1
+ block.c1 = c1
+ block.c2 = c2
+
+ return {diff,len}
+
+end
+--
+
+function r_splitBlock(dt,blocklist,maxblock,qual,quant,rgbw,qualnorm,quantnorm,quantpow)
+ local n,cmax,median,blockA,blockB,len,cB,block,rgb,res
+ local c1,c2,dist1,dist2,medr,medg,medb,r1,g1,b1,r2,g2,b2,rgb1,rgb2
+
+ blockA,blockB = {},{}
+ block = blocklist[maxblock]
+
+ res = true
+
+ --chan = block.chan
+ --median = block.median
+ c1 = block.c1
+ c2 = block.c2
+
+ --rgb1 = block[c1]
+ --r1,g1,b1 = rgb1[1],rgb1[2],rgb1[3]
+ --rgb2 = block[c2]
+ --r2,g2,b2 = rgb2[1],rgb2[2],rgb2[3]
+ --medr = (r1+r2)/2
+ --medg = (g1+g2)/2
+ --medb = (b1+b2)/2
+
+ cB = blocklist[maxblock] -- maxblock starts at 1 when called so it should not hava a +1
+ len = #cB
+
+ if len < 2 then return false; end
+
+ for n=1, len, 1 do
+ rgb = cB[n]
+
+ dist1 = dt[rgb[4]+1][c1]
+ dist2 = dt[rgb[4]+1][c2]
+
+ if (dist1 <= dist2)
+ then table.insert(blockA,rgb);
+ end
+
+ if (dist1 > dist2) then
+ table.insert(blockB,rgb);
+ end
+ end
+
+ blocklist[maxblock] = blockA -- Can't be empty right?
+ r_analyzeBlock(dt,blocklist[maxblock],qual,quant,rgbw,qualnorm,quantnorm,quantpow)
+
+ if (#blockB > 0) then
+ table.insert(blocklist,blockB)
+ r_analyzeBlock(dt,blocklist[#blocklist],qual,quant,rgbw,qualnorm,quantnorm,quantpow) -- no -1 on blocklist
+ else
+ res = false
+ end
+
+ return res -- false = no split
+end
+
+------------ eof MEDIAN REDUX --------------------------
+
+
+
+
+--
+-- ... eof Custom Color / Palette functions ...
+--
+
+
+-- *****************************
+-- *** Custom Draw functions ***
+-- *****************************
+
+--
+function db.line(x1,y1,x2,y2,c) -- Coords should be integers or broken lines are possible
+ local n,st,m,xd,yd; m = math
+ st = m.max(1,m.abs(x2-x1),m.abs(y2-y1));
+ xd = (x2-x1) / st
+ yd = (y2-y1) / st
+ for n = 0, st, 1 do
+ putpicturepixel(m.floor(x1 + n*xd), m.floor(y1 + n*yd), c );
+ end
+end
+--
+
+--
+function db.lineTransp(x1,y1,x2,y2,c,amt) -- amt: 0-1, 1 = Full color
+ local n,st,m,x,y,r,g,b,r1,g1,b1,c2,org; m = math
+ org = 1 - amt
+ st = m.max(1,m.abs(x2-x1),m.abs(y2-y1));
+ for n = 0, st, 1 do
+ x = m.floor(x1+n*(x2-x1)/st)
+ y = m.floor(y1+n*(y2-y1)/st)
+ r,g,b = getcolor(getpicturepixel(x,y))
+ r1,g1,b1 = getcolor(c)
+ c2 = matchcolor(r1*amt+r*org, g1*amt+g*org, b1*amt+b*org)
+ putpicturepixel(x, y, c2 );
+ end
+end
+--
+
+--
+function db.drawBrushRectangle(x1,y1,w,h,c)
+ local x,y
+ for y = y1, y1+h-1, 1 do
+ for x = x1, x1+w-1, 1 do
+ putbrushpixel(x,y,c);
+ end
+ end
+end
+--
+
+--
+function db.drawRectangle(x1,y1,w,h,c)
+ local x,y
+ for y = y1, y1+h-1, 1 do
+ for x = x1, x1+w-1, 1 do
+ putpicturepixel(x,y,c);
+ end
+ end
+end
+--
+
+--
+function db.drawRectangleNeg(x1,y1,w,h,c)
+ local x,y,xs,ys
+ xs = db.sign(w)
+ ys = db.sign(h)
+ if xs == 0 then xs = 1; end
+ if ys == 0 then ys = 1; end
+ for y = y1, y1+h-1, ys do
+ for x = x1, x1+w-1, xs do
+ putpicturepixel(x,y,c);
+ end
+ end
+end
+--
+
+--
+function db.drawRectangleLine(x,y,w,h,c)
+ w = w-1
+ h = h-1
+ db.line(x,y,x+w,y,c)
+ db.line(x,y,x,y+h,c)
+ db.line(x,y+h,x+w,y+h,c)
+ db.line(x+w,y,x+w,y+h,c)
+end
+--
+
+
+--
+function db.drawRectangleMix(x1,y1,w,h,c1,c2)
+ local x,y,c,n
+ c = {c1,c2}
+ n = 0
+ for y = y1, y1+h-1, 1 do
+ n = n + 1
+ for x = x1, x1+w-1, 1 do
+ n = n + 1
+ putpicturepixel(x,y,c[n%2+1]);
+ end
+ end
+end
+--
+
+--
+function db.drawCircle(x1,y1,r,c) -- ok, lottsa weird adjustments here, can probably be optimized...
+ local x,y,d,r5,r25,r2,xr5,yr5
+ r5,r25,r2,xr5,yr5 = r+0.5,r-0.25,r*2, x1-r-0.5, y1-r-0.5
+ for y = 0, r2, 1 do
+ for x = 0, r2, 1 do
+ d = math.sqrt((x-r5)^2 + (y-r5)^2)
+ if d < r25 then putpicturepixel(x + xr5, y + yr5,c); end
+ end
+ end
+end
+--
+
+--
+function db.drawBrushCircle(x1,y1,r,c) -- ok, lottsa weird adjustments here, can probably be optimized...
+ local x,y,d
+ for y = 0, r*2, 1 do
+ for x = 0, r*2, 1 do
+ d = math.sqrt((x-r-0.5)^2 + (y-r-0.5)^2)
+ if d < r-0.25 then putbrushpixel(x1+x-r-0.5,y1+y-r-0.5,c); end
+ end
+ end
+end
+--
+
+--
+-- Rotation in degrees
+-- Step is # of line segments (more is "better")
+-- a & b are axis-radius
+function db.ellipse2(x,y,a,b,stp,rot,col)
+ local n,m=math,rad,al,sa,ca,sb,cb,ox,oy,x1,y1,ast
+ m = math; rad = m.pi/180; ast = rad * 360/stp;
+ sb = m.sin(-rot * rad); cb = m.cos(-rot * rad)
+ for n = 0, stp, 1 do
+ ox = x1; oy = y1;
+ sa = m.sin(ast*n) * b; ca = m.cos(ast*n) * a
+ x1 = x + ca * cb - sa * sb
+ y1 = y + ca * sb + sa * cb
+ --if (n > 0) then db.line(ox,oy,x1,y1,col); end
+ if (n > 0) then drawline(ox,oy,x1,y1,col); end
+ end
+end
+--
+
+
+
+--[[
+var ER = 0.3
+var DR = 0.15
+
+ellipse(0.5*xx,0.5*yy,DR*xx,6,Math.PI*0)
+
+function ellipse(x,y,r,stp,rot){
+ var n,deg=360,m=Math,rad=Math.PI/180,rn
+ var ox,oy,x1,y1,x2,y2,d1,r1 = ER * xx
+
+ for (n=0; n<=deg; n+=stp){
+
+ ox = x2; oy = y2, rn = rad * n
+ d1 = rn - rot
+ x1 = x + m.sin(d1) * r
+ y1 = y + m.cos(d1) * r
+
+ x2 = x1 + m.sin(-rn) * r1
+ y2 = y1 + m.cos(-rn) * r1
+ if (n > 0){ line_rgb(MX,[0,0,0],0,ox,oy,x2,y2) }
+ }
+}
+
+}
+
+ellipse2(0.5*xx,0.5*yy,15,8,200,22,[0,0,0],0.5)
+
+function ellipse2(x,y,a,b,stp,rot,rgb,transp){
+ var n,m=Math,rad=m.PI/180,al,sa,ca,sb,cb,ox,oy,x1,y1
+ sb = m.sin(-rot * rad); cb = m.cos(-rot * rad)
+ for (n=0; n<=stp; n++){
+ ox = x1; oy = y1; al = rad * 360/stp * n
+ sa = m.sin(al) * b; ca = m.cos(al) * a
+ x1 = x + ca * cb - sa * sb
+ y1 = y + ca * sb + sa * cb
+ if (n > 0){ line_rgb(MX,rgb,transp,ox,oy,x1,y1) }
+ }
+}
+
+
+]]
+
+
+
+function db.obliqueCube(side,x,y,r,g,b,bri,cols)
+ local n,c,depth,x1,y1,x2,y2,f
+
+ asscols = false
+ if cols >= 0 and cols<250 then
+ asscols = true
+ c = cols; setcolor(cols,r,g,b); cols = cols + 1
+ cP50 = cols; q = bri*0.5; setcolor(cols,r+q,g+q,b+q); cols = cols + 1;
+ cP75 = cols; q = bri*0.75; setcolor(cols,r+q,g+q,b+q); cols = cols + 1;
+ cM50 = cols; q = -bri*0.5; setcolor(cols,r+q,g+q,b+q); cols = cols + 1;
+ cM100= cols; q = -bri; setcolor(cols,r+q,g+q,b+q); cols = cols + 1;
+ end
+
+ f = matchcolor
+ if asscols == false then
+ c = f(r,g,b)
+ cP50 = f(r+bri*0.5,g+bri*0.5,b+bri*0.5)
+ cP75 = f(r+bri*0.75,g+bri*0.75,b+bri*0.75)
+ cM50 = f(r-bri*0.5,g-bri*0.5,b-bri*0.5)
+ cM100 = f(r-bri,g-bri,b-bri)
+ end
+
+
+ depth = math.floor(side / 2)
+
+ for n = 0, depth-1, 1 do
+ db.line(x+side+n,y-1-n,x+side+n,y+side-n-1,cM50)
+ --drawline(x+side+n,y-1-n,x+side+n,y+side-n-1,cM50)
+ end
+
+ for n = 0, depth-1, 1 do
+ db.line(x+n,y-1-n,x+side+n-1,y-1-n,cP50)
+ --drawline(x+n,y-1-n,x+side+n-1,y-1-n,cP50)
+ end
+
+ -- /
+ --
+ --db.line(x+side,y-1,x+side+depth-1,y-depth,c)
+
+ -- Smoothing & Shade
+
+ --
+ -- /
+ --db.line(x+side,y+side-1,x+side+depth-1,y+side-depth,cM100)
+
+ --db.line(x,y,x+side-2,y,cP75)
+ --db.line(x,y,x,y+side-2,cP75)
+
+ db.drawRectangle(x,y,side,side,c)
+
+ return cols
+
+end
+
+
+function db.obliqueCubeBRI(side,x,y,r,g,b,bri,pallist,briweight,index_flag)
+ local n,c,depth,x1,y1,x2,y2
+
+ --f = db.getBestPalMatchHYBRID
+ c = db.getBestPalMatchHYBRID({r,g,b}, pallist, briweight, index_flag)
+ cP50 = db.getBestPalMatchHYBRID({r+bri*0.5,g+bri*0.5,b+bri*0.5}, pallist, briweight, index_flag)
+ cP75 = db.getBestPalMatchHYBRID({r+bri*0.75,g+bri*0.75,b+bri*0.75}, pallist, briweight, index_flag)
+ cM50 = db.getBestPalMatchHYBRID({r-bri*0.5,g-bri*0.5,b-bri*0.5}, pallist, briweight, index_flag)
+ cM100 = db.getBestPalMatchHYBRID({r-bri,g-bri,b-bri}, pallist, briweight, index_flag)
+
+ depth = math.floor(side / 2)
+
+ db.drawRectangle(x,y,side,side,c)
+
+ for n = 0, depth-1, 1 do
+ db.line(x+side+n,y-1-n,x+side+n,y+side-n-1,cM50)
+ --drawline(x+side+n,y-1-n,x+side+n,y+side-n-1,cM50)
+ end
+
+ for n = 0, depth-1, 1 do
+ db.line(x+n,y-1-n,x+side+n-1,y-1-n,cP50)
+ --drawline(x+n,y-1-n,x+side+n-1,y-1-n,cP50)
+ end
+
+ -- /
+ --
+ db.line(x+side,y-1,x+side+depth-1,y-depth,c)
+ --drawline(x+side,y-1,x+side+depth-1,y-depth,c)
+
+
+ -- Smoothing & Shade
+
+ --
+ -- /
+ --db.line(x+side,y+side-1,x+side+depth-1,y+side-depth,cM100)
+
+ --db.line(x,y,x+side-2,y,cP75)
+ --db.line(x,y,x,y+side-2,cP75)
+
+
+end
+
+
+--
+function db.fillTriangle(p,fcol,lcol,fill,wire) -- p = list of 3 points
+
+ local n,x,y,x1,x2,y1,y2,xf,yf,len,mr
+
+ mr = math.floor
+
+ -- Convert to screen/matrix-coordinates
+ --if (mode == 'percent') then xf = xx / 100; yf = yy / 100; end
+ --if (mode == 'fraction') then xf = xx; yf = yy; end
+ --if (mode ~= 'absolute') then screenilizeTriangle(p,xf,yf); end
+
+ if (fill) then
+ local Ax,Ay,Bx,By,Cx,Cy,xd,a,b,yc,ABdy,BCdy,ABix,BCix,ACix
+
+ xd = {}
+
+ --sort(p,1) -- Find top and middle y-point
+ db.sorti(p,2)
+
+ Ay = p[1][2]; Ax = p[1][1]
+ By = p[2][2]; Bx = p[2][1]
+ Cy = p[3][2]; Cx = p[3][1]
+
+ ABdy = By - Ay
+ BCdy = Cy - By
+ ABix = (Bx - Ax) / ABdy
+ BCix = (Cx - Bx) / BCdy
+ ACix = (Cx - Ax) / (Cy - Ay)
+
+ a=1; b=2;
+ if (ACix < ABix) then a=2; b=1; end
+ for y = 0, ABdy-1, 1 do -- Upper -1
+ xd[a] = mr(Ax + ABix * y)
+ xd[b] = mr(Ax + ACix * y)
+ yc = y+Ay;
+ for x=xd[1], xd[2], 1 do
+ putpicturepixel(x,yc,fcol)
+ end
+ end
+
+ a=1; b=2;
+ if (BCix < ACix) then a=2; b=1; end
+ for y = 0, BCdy, 1 do -- Lower
+ xd[a] = mr(Cx - BCix * y);
+ xd[b] = mr(Cx - ACix * y)
+ yc = Cy-y;
+ for x = xd[1], xd[2], 1 do
+ putpicturepixel(x,yc,fcol)
+ end
+ end
+
+ end -- eof fill
+
+ if (wire) then
+ for n = 0, 2, 1 do -- Outline
+ x1 = p[n+1][1]; y1 = p[n+1][2]
+ x2 = p[1 + (n+1) % 3][1]; y2 = p[1 + (n+1) % 3][2]
+ --db.line(x1,y1,x2,y2,lcol)
+ drawline(x1,y1,x2,y2,lcol)
+ end
+ end
+
+end -- eof fillTriangle
+--
+
+--
+-- ... eof Custom Draw functions ...
+--
+
+
+-- ******************************
+-- *** Filters & Convolutions ***
+-- ******************************
+
+
+function db.applyConvolution2Pic(convmx,divisor,bias,neg,amt)
+ local r,g,b,mx,my,cx,cy,mxh,myh,mp,rb,gb,bb,xx,yy,x,y,w,h,div,n1,n2,amtr,ro,go,bo
+
+ n1 = 1
+ n2 = bias
+ if neg == 1 then
+ n1 = -1
+ n2 = 255 + bias
+ end
+
+ amtr = 1 - amt
+ w, h = getpicturesize()
+ cy = #convmx
+ cx = #convmx[1]
+ mxh = math.floor(cx / 2) + 1
+ myh = math.floor(cy / 2) + 1
+
+ for y = 0, h-1, 1 do
+ for x = 0, w-1, 1 do
+ r,g,b = 0,0,0
+ ro,go,bo = getcolor(getbackuppixel(x,y))
+ div = divisor
+ for my = 1, cy, 1 do
+ for mx = 1, cx, 1 do
+ xp = mx-mxh
+ yp = my-myh
+ mp = convmx[my][mx]
+ xx = x + xp
+ yy = y + yp
+ if yy>=0 and yy=0 and xx 0 and n 0 and n0) then
+ x = x*px - fx
+ y = y*py - fy
+ nfrac = nfrac + spfrac
+ end
+ n = n+1
+ end
+ --return 1 - n/i;
+ return 1 - nfrac/i
+end
+--
+
+
+--
+function db.mandel(x,y,l,r,o,i) -- pos. as fraction of 1, left coord, right coord, y coord, iterations
+
+ local w,s,a,p,q,n,v,w
+
+ s=math.abs(r-l);
+
+ a = l + s*x;
+ p = a;
+ b = o - s*(y-0.5);
+ q = b;
+ n = 1;
+ v = 0;
+ w = 0;
+
+ while (v+w<4 and n weakest then weakest = w; weak_i = {z,y,x}; end
+ end
+
+ end;end;end
+ return weak_i[1],weak_i[2],weak_i[3]
+end
+--
+
+--
+--
+-- Nearest color version: void is selected by the point that has the greatest distance
+-- to the nearest color. Higher value means greater void.
+--
+function db.addColor2Cube(cube,sha,r,g,b,rw,gw,bw)
+ local star,x,y,z,d,rd,gd,bd,cu1,cu2
+ star = 0
+ cube[r+1][g+1][b+1] = {false, star}
+ for z = 0, sha-1, 1 do
+ rd = (rw*(z-r))^2
+ cu2 = cube[z+1]
+ for y = 0, sha-1, 1 do
+ gd = (gw*(y-g))^2
+ cu1 = cu2[y+1]
+ for x = 0, sha-1, 1 do
+
+ d = rd + gd + (bw*(x-b))^2
+
+ --cube[z+1][y+1][x+1][2] = math.min(d, cube[z+1][y+1][x+1][2]) -- Don't add, use nearest color
+
+ cu1[x+1][2] = math.min(d, cu1[x+1][2])
+
+ end;end;end
+end
+--
+
+-- Should be same as original, but not 100% verified. Using a rgb+1 trick to speed up handling
+--
+function db.addColor2Cube_test(cube,sha,r,g,b,rw,gw,bw)
+ local star,x,y,z,d,rd,gd,bd,cu1,cu2
+ star = 0
+ r = r+1; g = g+1; b = b+1
+ cube[r][g][b] = {false, star}
+ for z = 1, sha, 1 do
+ rd = (rw*(z-r))^2
+ cu2 = cube[z]
+ for y = 1, sha, 1 do
+ gd = (gw*(y-g))^2
+ cu1 = cu2[y]
+ for x = 1, sha, 1 do
+ cu1[x][2] = math.min(rd+gd+(bw*(x-b))^2, cu1[x][2])
+ end;end;end
+end
+--
+
+
+
+-- Create new allowed colorlines in colorspace (ramps from which colors can be picked)
+function db.enableRangeColorsInCube(cube,sha,r1,g1,b1,r2,g2,b2)
+
+ local div,r,g,b,n,rs,gs,bs
+ div = 256 / sha
+ rs = (r2 - r1) / sha / div
+ gs = (g2 - g1) / sha / div
+ bs = (b2 - b1) / sha / div
+
+ for n = 0, sha-1, 1 do
+
+ r = math.floor(r1/div + rs * n)
+ g = math.floor(g1/div + gs * n)
+ b = math.floor(b1/div + bs * n)
+
+ cube[r+1][g+1][b+1][1] = true
+
+ end
+end
+--
+
+
+function db.colorCigarr(shades,radius,fill_flag)
+ local s,rad,radsq,step,shalf,bas,cols,found,x,y,z,bri,con,d,n
+ radius = radius / 100
+ step = math.floor(255 / (shades-1))
+ shalf = math.floor(shades / 2)
+ s = shades - 1
+ rad = math.floor(shades / 2 * radius)
+ radsq = rad^2
+
+ bas = 0
+ cols = {}
+ found = 0
+
+ for z = 0, s, 1 do
+ for y = 0, s, 1 do
+ for x = 0, s, 1 do
+
+ --0.26,0.55,0.19
+ bri = (x + y + z ) / 3
+ --bri = math.sqrt(((x*0.26)^2 + (y*0.55)^2 + (z*0.19)^2)) * 1.5609
+ con = math.floor((shades - math.abs(bri - shalf)*2) * radius)
+
+ d = math.floor(math.sqrt((bri-x)^2 + (bri-y)^2 + (bri-z)^2))
+ --d = math.floor(math.sqrt(((bri-x)*0.26)^2 + ((bri-y)*0.55)^2 + ((bri-z)*0.19)^2)) * 1.5609
+
+ -- Filled cigarr: Less or Equal, cigarr shell: Equal
+ if d == con or (d < con and fill_flag) then
+ found = found + 1
+ r = bas + x * step
+ g = bas + y * step
+ b = bas + z * step
+ cols[found] = {r,g,b}
+ end
+
+ end; end; end
+
+ --messagebox("Colors found: "..found.."\n\n".."Run AnalyzePalette to examine")
+
+ for n = 0, 255, 1 do
+ if n < found then
+ c = cols[n+1]
+ setcolor(n,c[1],c[2],c[3])
+ else
+ setcolor(n,0,0,0)
+ end
+ end
+end -- eof colorcigarr
+
+
+--
+-- ... eof Color Cube ...
+--
+
+
+
+-- COLORMIX --
+--
+-- Returns a list of mixcolors palette entries, that are ranked by by quality & usefulness
+--
+-- This whole junk my partly locked on 16 shades (4096 colors/ 12bit palette precision) so don't use anything else...
+--
+--
+function db.colormixAnalysis(sha,spare_flag,cust_dist) -- Interface
+ local shades,pallist,ilist,custom_max_distance
+
+ shades = sha -- 16 is good
+ --messagebox(shades)
+
+ custom_max_distance = -1
+ if cust_dist ~= null then
+ custom_max_distance = cust_dist -- in %
+ end
+
+ if spare_flag == true then -- No shades here for now
+ --pallist = db.makePalListShadeSPARE(256,shades) -- 16 shades so Colorcube processes is possible
+ pallist = db.makeSparePalList(256)
+ pallist = db.fixPalette(pallist,0) -- Remove doubles, No need to sort?
+ ilist = db.makeIndexList(pallist, -1) -- -1, use list order as index
+ else
+ pallist = db.makePalListShade(256,shades) -- 16 shades so Colorcube processes is possible
+ pallist = db.fixPalette(pallist,0) -- Remove doubles, No need to sort?
+ ilist = db.makeIndexList(pallist, -1) -- -1, use list order as index
+ end
+
+ if shades > 0 then
+ return db.colormixAnalysisEXT(shades,pallist,ilist,custom_max_distance) -- max distance in %
+ end
+ if shades == -1 then
+ return db.colormixAnalysisEXTnoshade(pallist,ilist,custom_max_distance) -- max distance in %
+ end
+end
+--
+--
+function db.colormixAnalysisEXT(SHADES,pallist,ilist,custom_max_distance) -- Shades, most number of mixes returned
+ local n,m,c1,c2,pairs,cube,rm,gm,bm
+ local mix,total,found,dist,void,ideal,mini,maxi,bestmix,bestscore
+
+ --messagebox("will now make pairs")
+
+ pairs = db.pairsFromList(ilist,0) -- 0 for unique pairs only, pairs are entries in pallist
+
+ --messagebox(#pairs.." will now add colors to cube")
+
+ cube = db.initColorCube(SHADES,{true,9999})
+ for n = 1, #pallist, 1 do
+ c1 = pallist[n]
+ db.addColor2Cube_test(cube,SHADES,c1[1],c1[2],c1[3],0.26,0.55,0.19)
+ end
+
+ -- these values are adjusted for a 12bit palette (0-15) and perceptual weight where r+g+b = 1.0
+ -- Ideal distance = 2.5 Green steps = 1.375
+ -- Minimum distance = 1 Green step = 0.55
+
+ --messagebox("colorcube done")
+
+ VACT = 1
+ DACT = 1
+
+ total = 9.56 -- Max distance possible with 16 shades
+ ideal = 0.45 -- 1 step = 0.637
+ mini = 0.35
+ maxi = ideal + (total - ideal) / math.max(1, #pallist / 16)
+ if custom_max_distance ~= -1 then
+ maxi = total * (custom_max_distance / 100)
+ end
+ mix = {}
+ --mix[1] = {9e99,0,0,9e99,0,0,0}
+ bestmix = -1
+ bestscore = 9e99
+ found = 0
+ for n = 1, #pairs, 1 do
+
+ c1 = pallist[pairs[n][1]]
+ c2 = pallist[pairs[n][2]]
+ --0.26,0.55,0.19
+ dist = db.getColorDistance_weight(c1[1],c1[2],c1[3],c2[1],c2[2],c2[3],0.26,0.55,0.19) -- Not normalized
+
+ rm = math.floor((c1[1]+c2[1])/2)
+ gm = math.floor((c1[2]+c2[2])/2)
+ bm = math.floor((c1[3]+c2[3])/2)
+
+ -- Mix color adjustment (perhaps less than perfect, but probably good enough)
+ mixbri = db.getBrightness(rm,gm,bm)
+ truebri = math.sqrt((db.getBrightness(c1[1],c1[2],c1[3])^2 + db.getBrightness(c2[1],c2[2],c2[3])^2) / 2)
+ diff = truebri - mixbri
+ rm = math.max(0,math.min(15,math.floor(rm + diff)))
+ gm = math.max(0,math.min(15,math.floor(gm + diff)))
+ bm = math.max(0,math.min(15,math.floor(bm + diff)))
+ newbri = db.getBrightness(rm,gm,bm)
+ delta = math.abs(newbri - truebri)
+ --if delta > 0.9 then
+ -- messagebox(pallist[pairs[n][1]][4]..", "..pallist[pairs[n][2]][4].." delta = "..delta)
+ --end
+ --
+
+ --rm = math.floor(math.sqrt((c1[1]^2 + c2[1]^2) / 2))
+ --gm = math.floor(math.sqrt((c1[2]^2 + c2[2]^2) / 2))
+ --bm = math.floor(math.sqrt((c1[3]^2 + c2[3]^2) / 2))
+
+ void = cube[rm+1][gm+1][bm+1][2]
+
+ if dist >= mini and dist <= maxi then
+ found = found + 1
+ score = ((1+DACT*(dist - ideal)^2) / (1+void*VACT)) -- Lowest is best
+ mix[found] = {score,pallist[pairs[n][1]][4],pallist[pairs[n][2]][4],dist,rm*SHADES,gm*SHADES,bm*SHADES,c1[1]*SHADES,c1[2]*SHADES,c1[3]*SHADES,c2[1]*SHADES,c2[2]*SHADES,c2[3]*SHADES} -- mix holds palette entry
+ if score < bestscore then bestscore = score; bestmix = found; end
+ end
+
+ end
+
+
+ if true == false then
+ -- 2nd pass, add bestmix to colorspace. This reduces many similar mixes.
+ m = mix[bestmix]
+ db.addColor2Cube(cube,SHADES,m[5],m[6],m[7],0.26,0.55,0.19)
+ for n = 1, #mix, 1 do
+ if n ~= bestmix then
+ m = mix[n]
+ dist = m[4]
+ void = cube[m[5]+1][m[6]+1][m[7]+1][2]
+ score = ((1+DACT*(dist - ideal)^2) / (1+void*VACT))
+ m[1] = score
+ end
+ end
+ end
+
+ c1,c2 = -1,-1
+ if found > 0 then
+ db.sorti(mix,1)
+ best = mix[1]
+ c1 = best[2]
+ c2 = best[3]
+ end
+
+ --return found,c1,c2
+ return mix,found,c1,c2
+end
+--
+
+
+--
+-- Mixcolor without colorcube - no scoring or sorting, 24bit colors, faster...
+--
+function db.colormixAnalysisEXTnoshade(pallist,ilist,custom_max_distance)
+ local n,m,c1,c2,pairs,cube,rm,gm,bm
+ local mix,total,found,dist,void,ideal,mini,maxi,bestmix,bestscore
+
+ pairs = db.pairsFromList(ilist,0) -- 0 for unique pairs only, pairs are entries in pallist
+
+ total = 162.53 -- Max distance possible with 24-bit palette ad Dawn3.0 color weights 162.53
+ ideal = 0
+ mini = 0
+ maxi = ideal + (total - ideal) / math.max(1, #pallist / 16)
+
+ if custom_max_distance ~= -1 then
+ maxi = total * (custom_max_distance / 100)
+ end
+
+ statusmessage("Mixcol Analysis ("..#pairs.." pairs) ");
+ updatescreen(); if (waitbreak(0)==1) then return; end
+
+ mix = {}
+ found = 0
+ for n = 1, #pairs, 1 do
+ c1 = pallist[pairs[n][1]]
+ c2 = pallist[pairs[n][2]]
+ --0.26,0.55,0.19
+ dist = db.getColorDistance_weight(c1[1],c1[2],c1[3],c2[1],c2[2],c2[3],0.26,0.55,0.19) -- Not normalized
+
+ rm = math.floor((c1[1]+c2[1])/2)
+ gm = math.floor((c1[2]+c2[2])/2)
+ bm = math.floor((c1[3]+c2[3])/2)
+
+ -- Mix color adjustment
+ mixbri = db.getBrightness(rm,gm,bm)
+ truebri = math.sqrt((db.getBrightness(c1[1],c1[2],c1[3])^2 + db.getBrightness(c2[1],c2[2],c2[3])^2) / 2)
+ diff = truebri - mixbri
+ rm = math.max(0,math.min(255,math.floor(rm + diff)))
+ gm = math.max(0,math.min(255,math.floor(gm + diff)))
+ bm = math.max(0,math.min(255,math.floor(bm + diff)))
+ newbri = db.getBrightness(rm,gm,bm)
+ delta = math.abs(newbri - truebri)
+ --if delta > 0.9 then
+ -- messagebox(pallist[pairs[n][1]][4]..", "..pallist[pairs[n][2]][4].." delta = "..delta)
+ --end
+ --
+
+ if dist >= mini and dist <= maxi then
+ found = found + 1
+ score = 1
+ mix[found] = {score,pallist[pairs[n][1]][4],pallist[pairs[n][2]][4],dist,rm,gm,bm,c1[1],c1[2],c1[3],c2[1],c2[2],c2[3]} -- mix holds palette entry
+ end
+
+ end
+
+ --messagebox(#mix)
+
+ return mix,found,-1,-1
+end
+--
+
+
+
+-- Fuse a palettelist into an extended mix-anlysis list
+function db.fusePALandMIX(pal,mix,max_score,max_dist)
+ local n,c,mixlist,tot,score,dist,c1,c2,rm,gm,bm
+
+ mixlist = {}
+ tot = 0
+
+ -- {r,g,b,n}
+ for n = 1, #pal, 1 do
+ tot = tot + 1
+ c = pal[n]
+ mixlist[tot] = {0,c[4],c[4],0,c[1],c[2],c[3],c[1],c[2],c[3],c[1],c[2],c[3]}
+ end
+
+ -- {score,col#1,col#2,dist,rm,gm,bm} low score is best
+ for n = 1, #mix, 1 do
+ score = mix[n][1]
+ dist = mix[n][4]
+ if score <= max_score and dist <= max_dist then
+ tot = tot + 1
+ mixlist[tot] = mix[n]
+ end
+ end
+
+ return mixlist
+end
+--
+
+
+-- ********************************************
+-- *** L-system (fractal curves & "plants") ***
+-- ********************************************
+--
+
+--
+function db.Lsys_makeData(a)
+ local n,i; i = {}
+ for n = 1, #a, 1 do i[a[n][2]] = a[n]; end
+ return i
+end
+--
+
+--
+function db.Lsys_makeSet(seed,iter,data)
+ local s,n,i,nset,set
+ set = seed
+ for n = 1, iter, 1 do
+ nset = ''
+ for i = 1, #set, 1 do
+ s = string.sub(set,i,i)
+ nset = nset..data[s][3]
+ end
+ set = nset
+ end
+ return set
+end
+--
+
+function db.Lsys_draw(set,data,cx,cy,size,rot,rgb,rng,transp, speed)
+ local p,M,DEG,l,n,d,i,v,q,c,tx,ty,posx,posy,dval,col,w,h,s,cl,count
+
+ if speed == nil then speed = 50; end -- speed is drawing operations per update
+
+ function ang(d) return (d % 360 + 360) * DEG; end
+
+ w,h = getpicturesize()
+
+ p = 0
+ M = math
+ DEG = math.pi/180
+ l = #set
+
+ posx={}; posy={}; dval={}
+
+ if (rgb == null) then rgb = {0,0,0}; end
+ if (transp == null) then transp = 0; end
+ col = db.newArrayMerge(rgb,{})
+ q = 255 / l
+
+ count = 0
+ for n = 1, l, 1 do
+ s = string.sub(set,n,n)
+ d = data[s]
+ i = d[1]
+ v = d[4]
+
+ --messagebox(i)
+
+ if (i == 'Left') then rot = rot - v; end
+ if (i == 'Right') then rot = rot + v; end
+
+ if (i == 'Save') then p=p+1; posx[p] = cx; posy[p] = cy; dval[p] = rot; end
+ if (i == 'Load') then cx = posx[p]; cy = posy[p]; rot = dval[p]; p=p-1; end
+
+ if (i == 'Draw') then
+ tx = cx + M.sin(ang(rot)) * size
+ ty = cy + -M.cos(ang(rot)) * size
+ for c = 1, 3, 1 do
+ if (rng[c] > 0) then col[c] = rgb[c] + (n * q) * rng[c]; end
+ if (rng[c] < 0) then col[c] = rgb[c] + (n * q) * rng[c]; end
+ end
+
+ cl = matchcolor(col[1],col[2],col[3])
+ --putpicturepixel(cx*w,cy*h,cl);
+ --db.line(cx*w,cy*h,tx*w,ty*h,cl)
+ db.lineTransp(cx*w,cy*h,tx*w,ty*h,cl,transp)
+
+ cx = tx; cy = ty
+ end
+ count = count + 1
+ if count == speed then count = 0; updatescreen(); if (waitbreak(0)==1) then return end; end
+ end
+
+ return {cx,cy,rot}
+end -- draw
+
+
+--
+-- eof L-system
+--
+---------------------------------------------------------------------------------------
+
+
+
+-- ********************************************
+-- *** COMPLEX SPECIAL FUNCTIONS ***
+-- ********************************************
+--
+
+---------------------------------------------------------------------------------------
+--
+-- Render engine for mathscenes (Full Floyd-Steinberg dither etc)
+--
+function db.fsrender(f,pal,ditherprc,xdith,ydith,percep,xonly, ord_bri,ord_hue,bri_change,hue_change,BRIWEIGHT, wd,ht,ofx,ofy) -- f is function
+
+local w,h,i,j,c,x,y,r,g,b,d,fl,v,v1,v2,vt,vt1,vt2,dither,m,mathfunc,dpow,fsdiv,ord,d1a,d1b,briweight
+local d1,d2,o1,o2,ox,oy
+
+ -- percep is no longer used, matchcolor2 is always active, but the code is kept if there's ever a need to
+ -- study the effect of perceptual colorspaces versus matchcolor
+
+ if ord_bri == null then ord_bri = 0; end
+ if ord_hue == null then ord_hue = 0; end
+ if bri_change == null then bri_change = 0; end
+ if hue_change == null then hue_change = 0; end
+ if BRIWEIGHT == null then BRIWEIGHT = 0; end
+
+ briweight = BRIWEIGHT / 100
+
+ ord = {{0,4,1,5},
+ {6,2,7,3},
+ {1,5,0,4},
+ {7,3,6,2}}
+
+
+ --i = ((ord[y % 4 + 1][x % 4 + 1])*28.444 - 99.55556)/100 * 16
+
+ function hue(r,g,b,deg)
+ local i,brin,diff,brio,r2,g2,b2
+ r2,g2,b2 = db.shiftHUE(r,g,b,deg)
+ brio = db.getBrightness(r,g,b)
+ for i = 0, 5, 1 do -- 6 iterations, fairly strict brightness preservation
+ brin = db.getBrightness(r2,g2,b2)
+ diff = brin - brio
+ r2,g2,b2 = db.rgbcap(r2-diff, g2-diff, b2-diff, 255,0)
+ end
+ return r2,g2,b2
+ end
+
+
+
+fsdiv = 16
+if xonly == 1 then fsdiv = 7; end -- Only horizontal dither
+
+dither = 0; if ditherprc > 0 then dither = 1; end
+
+-- When using standard error-diffusion brightness-matching is not really compatible
+--matchfunc = matchcolor2
+--if dither == 1 then matchfunc = matchcolor; end
+
+dpow = ditherprc / 100
+
+
+if wd == null then
+ w,h = getpicturesize()
+ else w = wd; h = ht
+end
+
+if ofx == null then
+ ox,oy = 0,0
+ else ox = ofx; oy = ofy
+end
+
+
+function cap(v)
+ return math.min(255,math.max(0,v))
+ end
+
+
+
+ --
+ fl = {}
+ fl[1] = {}
+ fl[2] = {}
+ i = 1
+ j = 2
+ --
+
+ -- Read the first 2 lines
+ v1 = ydith/2 + 0%2 * -ydith
+ v2 = ydith/2 + 1%2 * -ydith
+ for x = 0, w - 1, 1 do
+ d1a,d1b = 0,0
+ if ord_bri > 0 then
+ o1 = ord[0 % 4 + 1][x % 4 + 1]
+ d1a = (o1*28.444 - 99.55556)/100 * ord_bri
+ o2 = ord[1 % 4 + 1][(x+2) % 4 + 1] -- +2 To get it in right sequence for some reason
+ d1b = (o2*28.444 - 99.55556)/100 * ord_bri
+ end
+ -- We skip Hue-ordering for now
+ vt1 = v1 + xdith/2 + (x+math.floor(0/2))%2 * -xdith + d1a
+ vt2 = v2 + xdith/2 + (x+math.floor(1))%2 * -xdith + d1b -- Ok, not sure why 1/2 doesn't make for a nice pattern so we just use 1
+ r,g,b = f(x, 0, w, h)
+ fl[i][x] = {cap(r+vt1),cap(g+vt1),cap(b+vt1)}
+ r,g,b = f(x, 1, w, h)
+ fl[j][x] = {cap(r+vt2),cap(g+vt2),cap(b+vt2)}
+ end
+
+for y = 0, h-1, 1 do
+ for x = 0, w-1, 1 do
+
+ o = fl[i][x]
+ r = o[1] + bri_change
+ g = o[2] + bri_change
+ b = o[3] + bri_change
+
+ if hue_change ~= 0 then
+ r,g,b = hue(r,g,b,hue_change)
+ end
+
+
+ --if percep == 0 then c = matchfunc(r,g,b); end
+ --if percep == 1 then
+ -- --c = db.getBestPalMatchHYBRID({r,g,b},pal,0,true)
+ --c = matchcolor2(r,g,b,briweight)
+ --end
+
+ c = matchcolor2(r,g,b,briweight)
+
+ putpicturepixel(x+ox,y+oy,c)
+
+ if dither == 1 then
+ if x>1 and x 0 then
+ o = ord[y % 4 + 1][x % 4 + 1]
+ d1 = (o*28.444 - 99.55556)/100 * ord_bri
+ end
+
+ vt = v + xdith/2 + (x+math.floor(y/2))%2 * -xdith + d1
+ r,g,b = f(x, y+2, w, h)
+
+ if ord_hue > 0 then
+ o = ord[y % 4 + 1][x % 4 + 1]
+ d2 = (((o + 3.5) % 7) / 7 - 0.5) * ord_hue
+ r,g,b = hue(r,g,b,d2)
+ end
+
+ fl[j][x] = {cap(r+vt),cap(g+vt),cap(b+vt)}
+ end
+ updatescreen(); if (waitbreak(0)==1) then return; end
+end
+
+
+end
+-------------------------------------------------------------------------------
+
+--
+-- ROTATE Image or Brush
+--
+-- target: 1 = Brush, 2 = Picture, 3 = Brush-to-Picture
+-- rot: Rotation in degrees
+-- mode: 1 = Simple, 2 = Cosine Interpolation, 2 = BiLinear Interpolation
+-- spritemode: 0 = Off, 1 = On (Only match adjacent colors, use with Bilinear-Ip. for good result)
+-- resize: 0 = No, 1 = Yes (Resize Image/Brush to fit all gfx, otherwise clip)
+-- update: 0 = No, 1 = Yes (Update screen while drawing)
+-- xoffset: For use with Brush-to-Picture operations
+-- yoffset: For use with Brush-to-Picture operations
+--
+function db.doRotation(target,rot,mode,spritemode,resize,update, xoffset,yoffset)
+
+ local trg,f,w,h,x,y,r,g,b,c,hub_x,hub_y,x1,y1,x2,y2,x3,y3,x4,y4,dX,dY,dXs,dYs,ox,oy,mx,my,xp,yp,pal,func
+
+ function donothing(n)
+ end
+
+func = {
+ {getsize=getbrushsize, setsize=setbrushsize, clear=donothing, get=getbrushbackuppixel, put=putbrushpixel},
+ {getsize=getpicturesize, setsize=setpicturesize, clear=clearpicture, get=getbackuppixel, put=putpicturepixel},
+ {getsize=getbrushsize, setsize=donothing, clear=donothing, get=getbrushbackuppixel, put=putpicturepixel}
+}
+trg = func[target]
+
+--
+function bilinear(ox,oy,w,h,func,mode)
+ local xp1,xp2,yp1,yp2,r1,r2,r3,r4,g1,g2,g3,g4,b1,b2,b3,b4,r,g,b, c1,c2,c3,c4,pal,adjx,adjy
+ xp2 = ox - math.floor(ox)
+ yp2 = oy - math.floor(oy)
+
+ if mode == 1 then -- Cosinus curve (rather than linear), slightly sharper result (probably same as Photoshop)
+ xp2 = 1 - (math.cos(xp2 * math.pi) + 1)/2
+ yp2 = 1 - (math.cos(yp2 * math.pi) + 1)/2
+ end
+
+ xp1 = 1 - xp2
+ yp1 = 1 - yp2
+
+ c1 = func(math.floor(ox),math.floor(oy));
+ c2 = func(math.ceil(ox),math.floor(oy));
+ c3 = func(math.floor(ox),math.ceil(oy));
+ c4 = func(math.ceil(ox),math.ceil(oy));
+
+ r1,g1,b1 = getcolor(c1);
+ r2,g2,b2 = getcolor(c2);
+ r3,g3,b3 = getcolor(c3);
+ r4,g4,b4 = getcolor(c4);
+
+ pal = {{r1,g1,b1,c1},{r2,g2,b2,c2},{r3,g3,b3,c3},{r4,g4,b4,c4}} -- for SpriteMode ColorMatching
+
+ r = (r1*xp1 + r2*xp2)*yp1 + (r3*xp1 + r4*xp2)*yp2;
+ g = (g1*xp1 + g2*xp2)*yp1 + (g3*xp1 + g4*xp2)*yp2;
+ b = (b1*xp1 + b2*xp2)*yp1 + (b3*xp1 + b4*xp2)*yp2;
+
+ return r,g,b, pal
+end
+--
+
+ f = db.rotationFrac
+ w,h = trg.getsize()
+ hub_x = w / 2 - 0.5 -- Rotates 90,180 perfectly, not 45
+ hub_y = h / 2 - 0.5
+ --hub_x = w / 2
+ --hub_y = h / 2
+ x1,y1 = f (-rot,hub_x,hub_y,0,0) -- Rot is negative coz we read destination and write to source
+ x2,y2 = f (-rot,hub_x,hub_y,w-1,0)
+ x3,y3 = f (-rot,hub_x,hub_y,0,h-1)
+ x4,y4 = f (-rot,hub_x,hub_y,w-1,h-1)
+ dX = (x2 - x1) / w
+ dY = (y2 - y1) / w
+ dXs = (x4 - x2) / h
+ dYs = (y3 - y1) / h
+
+ adjx,adjy = 0,0
+ ox,oy = 0,0
+ if resize == 1 then
+ mx = math.ceil(math.max(math.abs(x1-hub_x),math.abs(x3-hub_x))) * 2 + 2
+ my = math.ceil(math.max(math.abs(y1-hub_y),math.abs(y3-hub_y))) * 2 + 2
+ if target == 3 then -- Center gfx at Brush-to-Picture
+ adjx = -mx/2
+ adjy = -my/2
+ end
+ ox = (mx - w) / 2
+ oy = (my - h) / 2
+ trg.setsize(mx,my)
+ end
+
+ trg.clear(0)
+
+ for y = -oy, h-1+oy, 1 do
+ RE,GE,BE = 0,0,0
+ for x = -ox, w-1+ox, 1 do
+
+ xp = x1 + dX * x + dXs * y
+ yp = y1 + dY * x + dYs * y
+
+ if mode == 2 or mode == 3 then
+ r,g,b,pal = bilinear(xp,yp,w,h,trg.get, mode_co)
+ if spritemode == 1 then
+ c = db.getBestPalMatchHYBRID({r+RE,g+GE,b+BE},pal,0.65,true) -- Brightness do very little in general with 4 set colors
+ else c = matchcolor2(r+RE,g+GE,b+BE)
+ end
+ else c = trg.get(xp,yp)
+ end
+
+ --rn,gn,bn = getcolor(c)
+ --RE = (r - rn)*0.5
+ --GE = (g - gn)*0.5
+ --BE = (b - bn)*0.5
+
+ trg.put(x+ox+xoffset+adjx,y+oy+yoffset+adjy, c)
+
+ end
+ if update == 1 then
+ statusmessage("Working... %"..math.floor(((y+oy) / (h-1+2*oy))*100))
+ updatescreen(); if (waitbreak(0)==1) then return; end
+ end
+ end
+
+end; -- doRotation
+
+-------------------------------------------------------------------------------
+
+--
+-- PARTICLE v1.0
+--
+-- Draw Sphere or Disc to any target with gradients that may fade to background
+--
+-- type: "sphere" - volmetric planet-like disc
+-- "disc" - plain disc
+-- mode: "blend" - mix graphics with background color
+-- "add" - add graphics to background color
+-- wd,ht: - Max Width/Height of drawing area, i.e. screen size (needed if drawing to an array-buffer)
+-- sx,sy: - drawing coordinates (center)
+-- xrad,yrad: - x & y radii
+-- rgba1: - rgb+alpha array of center color: {r,g,b,a}, alpha is 0..1 where 1 is no transparency, Extreme rgb-values allowed
+-- rgba2: - rgb+alpha array of edge color: {r,g,b,a}, alpha is 0..1 where 1 is no transparency, Extreme rgb-values allowed
+-- update_flag: - Display rendering option (and add break feature)
+-- f_get: - Get pixel function: use getpicturepixel if reading from image (set null for image default)
+-- f_put: - Put pixel function: use putpicturepixel if drawing to image (set null for image default)
+-- f_recur: - Optional custom control-function for recursion (set null if not used)
+-- recur_count - Recursion depth counter, for use in combination with a custom function (f_recur), 0 as default
+--
+-- Ex: particle("sphere","add", w,h, w/2,h/2, 40,40, {500,400,255, 0.8},{0,-150,-175, 0.0}, true, null, null, null, 0)
+--
+function db.particle(type,mode,wd,ht,sx,sy,xrad,yrad,rgba1,rgba2, update_flag, f_get, f_put, f_recur, recur_count)
+
+ local x,y,rev,dix,diy,r3,g3,b3,px,py,alpha,ralpha,add,q,rgb,rgb1,rgb2,rgb3,n,def_get,def_put
+
+ function def_get(x,y)
+ local r,g,b
+ r,g,b = getcolor(getpicturepixel(x,y));
+ return {r,g,b}
+ end
+ function def_put(x,y,r,g,b) putpicturepixel(x,y, matchcolor2(r,g,b,0.65)); end
+ if f_get == null then f_get = def_get; end
+ if f_put == null then f_put = def_put; end
+
+ q = {[true] = 1, [false] = 0}
+
+ rgb,rgb1,rgb2 = {},{},{}
+
+ if mode == 'blend' then
+ add = 1
+ end
+
+ if mode == 'add' then
+ add = 0
+ end
+
+ dix = xrad*2
+ diy = yrad*2
+
+ for y = 0, diy, 1 do
+ py = sy+y-yrad; oy = y / diy;
+ if (py >= 0 and py < ht) then
+
+ for x = 0, dix, 1 do
+ px = sx+x-xrad; ox = x / dix;
+ if (px >= 0 and px < wd) then
+
+ if type == 'sphere' then -- Sphere
+ a = math.sqrt(math.max(0,0.25 - ((0.5-ox)^2+(0.5-oy)^2))) * 2
+ end
+
+ if type == 'disc' then -- Disc
+ a = 1-math.sqrt((0.5-ox)^2+(0.5-oy)^2)*2
+ end
+
+ if a>0 then
+
+ rev = 1-a
+ rgb3 = f_get(px,py)
+ alpha = rgba1[4] * a + rgba2[4] * rev
+ ralpha = 1 - alpha * add
+
+ for n = 1, 3, 1 do
+ rgb1[n] = q[rgba1[n]==-1]*rgb3[n] + q[rgba1[n]~=-1]*rgba1[n] -- Fade from background?
+ rgb2[n] = q[rgba2[n]==-1]*rgb3[n] + q[rgba2[n]~=-1]*rgba2[n] -- Fade to background?
+ rgb[n] = (rgb1[n] * a + rgb2[n] * rev) * alpha + rgb3[n] * ralpha
+ end
+
+ f_put(px, py, rgb[1],rgb[2],rgb[3]);
+
+ end
+
+ end -- if x is good
+ end -- x
+ if update_flag then updatescreen(); if (waitbreak(0)==1) then return; end; end
+ end -- if y is good
+ end -- y
+
+ if f_recur ~= null then -- recursion
+ f_recur(type,mode,wd,ht,sx,sy,xrad,yrad,rgba1,rgba2, update_flag, f_get, f_put, f_recur, recur_count);
+ updatescreen(); if (waitbreak(0)==1) then return; end;
+ end
+
+end
+-- eof PARTICLE
+
+
+ --
+ -- MedianCut a larger palette-list from a MathScene to produce a high-quality BriSorted palette for the final render/colormatching
+ --
+ function db.makeSamplePal(w,h,colors,frend)
+ local n,x,y,r,g,b,pal
+ n,pal = 0,{}
+ for y = 0, h, math.ceil(h/63) do
+ for x = 0, w, math.ceil(w/63) do
+ r,g,b = frend(x,y,w,h)
+ n = n+1
+ r,g,b = db.rgbcapInt(r,g,b,255,0)
+ pal[n] = {r,g,b,0}
+ end;end
+ return db.fixPalette(db.medianCut(pal, colors, true, false, {0.26,0.55,0.19}, 8, 0),1) -- pal, cols, qual, quant, weights, bits, quantpower
+ end
+ --
+
+--
+-- Backdrop/Gradient Render (May be written to a matrix for rendering with db.fsrender)
+--
+ function db.backdrop(p0,p1,p2,p3,fput,ip_mode) -- points:{x,y,r,g,b}, IpMode "linear" is default
+
+ local x,y,ox,oy,xr,yr,r,g,b,ax,ay,w,h
+
+ ax,ay = p0[1],p0[2]
+
+ w = p1[1] - p0[1]
+ h = p2[2] - p0[2]
+
+ for y = 0, h, 1 do -- +1 to fill screen with FS-render
+
+ oy = y/h
+ if ip_mode == "cosine" then oy = 1 - (math.cos(oy * math.pi) + 1)/2; end
+ yr = 1 - oy
+
+ for x = 0, w, 1 do
+
+ ox = x/w
+ if ip_mode == "cosine" then ox = 1 - (math.cos(ox * math.pi) + 1)/2; end
+ xr = 1 - ox
+
+ r = (p0[3]*xr + p1[3]*ox)*yr + (p2[3]*xr + p3[3]*ox)*oy;
+ g = (p0[4]*xr + p1[4]*ox)*yr + (p2[4]*xr + p3[4]*ox)*oy;
+ b = (p0[5]*xr + p1[5]*ox)*yr + (p2[5]*xr + p3[5]*ox)*oy;
+
+ fput(x+ax,y+ay,r,g,b)
+
+ end;end
+
+ end
+-- eof backdrop
+
+
+
+--
+-- SPLINES --
+--
+function db.splinePoint(x0,y0,x1,y1,x2,y2,points,point)
+ local x,y,sx1,sy1,sx2,sy2,f
+
+ f = point * 1 / points
+
+ sx1 = x0*(1-f) + x1*f
+ sy1 = y0*(1-f) + y1*f
+
+ sx2 = x1*(1-f) + x2*f
+ sy2 = y1*(1-f) + y2*f
+
+ x = sx1 * (1-f) + sx2 * f
+ y = sy1 * (1-f) + sy2 * f
+
+ return x,y
+end
+--
+
+
+-- zx = 2*x1 - (x0+x2)/2
+--
+function db.drawSplineSegment(x0,y0,x1,y1,x2,y2,x3,y3,points,col) -- Does spline segment p1-p2
+ local n,x,y,sx1,sy1,sx2,sy2,mid,zx1,zy1,zx2,zy2,fx,fy
+
+ mid = math.floor(points / 2)
+ -- Extended Bezier points
+ zx1 = 2*x1 - (x0+x2)/2
+ zy1 = 2*y1 - (y0+y2)/2
+ zx2 = 2*x2 - (x1+x3)/2
+ zy2 = 2*y2 - (y1+y3)/2
+
+ fx,fy = x1,y1 -- Segment to be drawn (0),1 - 2,(3)
+ for n = 0, mid, 1 do
+
+ f = n * 1 / points * 2
+
+ sx1,sy1 = db.splinePoint(x0,y0,zx1,zy1,x2,y2,mid*2, mid + n)
+ sx2,sy2 = db.splinePoint(x1,y1,zx2,zy2,x3,y3,mid*2, n)
+
+ x = sx1 * (1-f) + sx2 * f
+ y = sy1 * (1-f) + sy2 * f
+
+ --putpicturepixel(x,y,col)
+ db.line(fx,fy,x,y,col)
+ fx,fy = x,y
+
+ end
+
+end
+--
+
+-- eof Splines
diff --git a/data/common/media/grafx2/scripts/samples_2.4/libs/memory.lua b/data/common/media/grafx2/scripts/samples_2.4/libs/memory.lua
new file mode 100644
index 0000000000..f650e873f3
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/libs/memory.lua
@@ -0,0 +1,138 @@
+-- Persistence library:
+-- Memorize data for current function
+-- memory.save(tab) and tab=memory.load()
+--
+-- The data will be stored in file called
+-- .dat
+-- in the lua directory
+--
+-- Example 1:
+--
+-- -- Load initial values or set defaults
+-- arg = memory.load({picX=320,picY=200,scale=0})
+-- -- Run an inputbox
+-- OK,arg.picX,arg.picY,arg.scale = inputbox("Image Size")",
+-- "Width", arg.picX, 1,2048,0,
+-- "Height", arg.picY, 1,2048,0,
+-- "Scale", arg.scale, 0,1,0);
+-- if OK == true then
+-- -- Save the selected values
+-- memory.save(arg)
+-- end
+
+-- Example 2:
+--
+-- -- Load initial values or set defaults
+-- arg = memory.load({x=320,y=200,scale=0})
+-- picX=arg.x
+-- picY=arg.y
+-- scale=arg.scale
+-- -- Run an inputbox
+-- OK,picX,picY,scale = inputbox("Image Size")",
+-- "Width", picX, 1,2048,0,
+-- "Height", picY, 1,2048,0,
+-- "Scale", scale, 0,1,0);
+-- if OK == true then
+-- -- Save the selected values
+-- memory.save({x=picX,y=picY,scale=scale})
+-- end
+
+
+memory =
+{
+ serialize = function(o)
+ if type(o) == "number" then
+ return tostring(o)
+ elseif type(o) == "string" then
+ return string.format("%q", o)
+ --elseif type(o) == "table" then
+ -- io.write("{\n")
+ -- for k,v in pairs(o) do
+ -- io.write(" ", k, " = ")
+ -- memory.serialize(v)
+ -- io.write(",\n")
+ -- end
+ -- io.write("}\n")
+ else
+ error("cannot serialize a " .. type(o))
+ end
+ end;
+
+ -- Return a string identifying the calling function.
+ -- Pass 1 for parent, 2 for grandparent etc.
+ callername = function(level)
+ local w
+ local last_slash
+ local info = debug.getinfo(level+1,"Sn")
+ local caller=tostring(info.name)
+ -- Function name if possible
+ if (caller~="nil") then
+ return caller
+ end
+ -- Otherwise, get file name, without extension
+
+ -- Get part after directory name
+ last_slash=0
+ while true do
+ local pos = string.find(info.source, "/", last_slash+1)
+ if (pos==nil) then break end
+ last_slash=pos
+ end
+ while true do
+ local pos = string.find(info.source, "\\", last_slash+1)
+ if (pos==nil) then break end
+ last_slash=pos
+ end
+
+ caller=string.sub(info.source, last_slash+1)
+
+ -- Remove file extension
+ if (string.sub(caller,-4, -1)==".lua") then
+ caller=string.sub(caller, 1, -5)
+ end
+ return caller
+ end;
+
+ -- Memorize some parameters.
+ save = function(o)
+ local caller=memory.callername(2)
+ --for k, v in pairs(o) do
+ -- messagebox(tostring(k))
+ -- messagebox(tostring(v))
+ --end
+ local f, e = io.open(caller..".dat", "w");
+ if (f ~= nil) then
+ f:write("Entry {\n")
+ for k, v in pairs(o) do
+ if (type(v)=="number") then
+ f:write(" "..k.."="..memory["serialize"](v)..",\n")
+ end
+ end
+ f:write("}\n")
+ f:close()
+ end
+ end;
+
+
+ -- Recover some saved parameters.
+ load = function(o)
+ local caller=memory.callername(2)
+ local i
+
+ function Entry (b)
+ -- Adds (or replaces) values in arg with those from b
+ for k, v in pairs(b) do
+ o[k]=v
+ end
+ end
+ local f = (loadfile(caller..".dat"))
+ if (f ~= nil) then
+ f()
+ end
+
+ return o
+ end;
+
+}
+
+return memory
diff --git a/data/common/media/grafx2/scripts/samples_2.4/palette/Desaturate.lua b/data/common/media/grafx2/scripts/samples_2.4/palette/Desaturate.lua
new file mode 100644
index 0000000000..91470de211
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/palette/Desaturate.lua
@@ -0,0 +1,40 @@
+--PALETTE Adjust: Desaturate v1.1
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+
+-- Copyright 2010 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+-- This script was adopted from Evalion, a Javascript codecrafting/imageprocessing project
+-- http://goto.glocalnet.net/richard_fhager/evalion/evalion.html
+
+
+-- Note: Negative values will work as INCREASED saturation, but I'm not sure if this function is 100% correct
+
+
+--percent = 25
+
+OK,percent = inputbox("Desaturate Palette","Percent %", 25, 0,100,0);
+
+--
+function desaturate(percent,r,g,b) -- V1.0 by Richard Fhager
+ p = percent / 100
+ a = (math.min(math.max(r,g,b),255) + math.max(math.min(r,g,b),0)) * 0.5 * p
+ r = r + (a-r*p)
+ g = g + (a-g*p)
+ b = b + (a-b*p)
+ return r,g,b
+end
+--
+
+if OK == true then
+
+ for c = 0, 255, 1 do
+ setcolor(c, desaturate(percent,getcolor(c)))
+ end
+
+end
\ No newline at end of file
diff --git a/data/common/media/grafx2/scripts/samples_2.4/palette/ExpandColors.lua b/data/common/media/grafx2/scripts/samples_2.4/palette/ExpandColors.lua
new file mode 100644
index 0000000000..d3e64359c7
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/palette/ExpandColors.lua
@@ -0,0 +1,171 @@
+--PALETTE: Expand Colors v1.0
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+-- Email: dawnbringer@hem.utfors.se
+-- MSN: annassar@hotmail.com
+--
+-- Copyright 2010 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+
+-- Continously fill the greatest void in the area of the color-cube enclosed by (or along ramps of) initial colors
+-- This algorithm will create lines of allowed colors (all ranges) in 3d colorspace and the pick
+-- new colors from the most void areas (on any line). Almost like a Median-cut in reverse.
+--
+-- Rather than filling the colorcube symmetrically it adds intermediate colors to the existing ones.
+--
+-- Running this script on the C64 16-color palette might be educational
+--
+--
+-- Source cols#, Expand to #,
+-- Ex: 15-31 means that palette colors 0-15 is expanded to 16 new colors placed at slots 16-31
+--
+-- Spread mode: OFF - New colors will conform to the contrast & saturation of original colors
+-- (new colors will stay on the ramps possible from the original colors)
+--
+-- ON - New colors will expand their variance by each new addition (mostly notable when adding many new colors)
+-- Will add range lines/ramps to all new colors from old ones, but keep within max/min values of the
+-- original colors. 15-bit mode will dampen the spread towards extreme colors (if starting with low contrast)
+--
+-- 15-bit colors: Higher color-resolution, 32768 possible colors rather than the 4096 of 12bit. Slower but perhaps better.
+--
+
+SHADES = 16 -- Going 24bit will probably be too slow and steal too much memory, so start with 12bit (4096 colors) for now
+
+ini = 0
+exp = 255
+
+OK,ini,exp,linemode,fbit = inputbox("Expand Colors (0-255):",
+ "Source Cols #: 1-254", 15, 1,254,0,
+ "Expand to #: 2-255", 31, 2,255,0,
+ "Spread mode", 0, 0,1,0,
+ "15-bit colors (slow)", 0, 0,1,0
+);
+
+if (fbit == 1) then SHADES = 32; end
+
+
+
+function initColorCube(sha)
+ ary = {}
+ for z = 0, sha-1, 1 do
+ ary[z+1] = {}
+ for y = 0, sha-1, 1 do
+ ary[z+1][y+1] = {}
+ for x = 0, sha-1, 1 do
+ ary[z+1][y+1][x+1] = {false,0}
+ end
+ end
+ end
+ return ary
+end
+
+-- Gravity model (think of colors as stars of equal mass/brightness in a 3d space)
+function addColor2Cube(cube,sha,r,g,b)
+ star = 1000000
+ fade = 1000
+
+ cube[r+1][g+1][b+1] = {false,star}
+
+ for z = 0, sha-1, 1 do
+ for y = 0, sha-1, 1 do
+ for x = 0, sha-1, 1 do
+
+ d = fade / ( (x-b)^2 + (y-g)^2 + (z-r)^2 )
+
+ cube[z+1][y+1][x+1][2] = cube[z+1][y+1][x+1][2] + d
+
+ end;end;end
+end
+
+
+-- Create new allowed colorlines in colorspace (ramps from which colors can be picked)
+function enableRangeColorsInCube(cube,sha,r1,g1,b1,r2,g2,b2)
+
+ local div,r,g,b
+ div = 256 / sha
+ rs = (r2 - r1) / sha / div
+ gs = (g2 - g1) / sha / div
+ bs = (b2 - b1) / sha / div
+
+ for n = 0, sha-1, 1 do
+
+ r = math.floor(r1/div + rs * n)
+ g = math.floor(g1/div + gs * n)
+ b = math.floor(b1/div + bs * n)
+
+ cube[r+1][g+1][b+1][1] = true
+
+ end
+end
+
+
+function findVoid(cube,sha)
+ weakest = 999999999999
+ weak_i = {-1,-1,-1}
+ for z = 0, sha-1, 1 do
+ for y = 0, sha-1, 1 do
+ for x = 0, sha-1, 1 do
+
+ c = cube[z+1][y+1][x+1]
+ if c[1] == true then
+ w = c[2]
+ if w <= weakest then weakest = w; weak_i = {z,y,x}; end
+ end
+
+ end;end;end
+ return weak_i[1],weak_i[2],weak_i[3]
+end
+
+--
+
+if OK == true then
+
+ cube = initColorCube(SHADES)
+
+ -- Define allowed colorspace
+ for y = 0, ini-1, 1 do
+ r1,g1,b1 = getcolor(y)
+ for x = y+1, ini, 1 do
+ r2,g2,b2 = getcolor(x)
+ enableRangeColorsInCube(cube,SHADES,r1,g1,b1,r2,g2,b2)
+ end
+ end
+
+ div = 256 / SHADES
+
+ -- Fill cube with initial colors
+ for n = 0, ini, 1 do
+ r,g,b = getcolor(n)
+ addColor2Cube(cube,SHADES,math.floor(r/div),math.floor(g/div),math.floor(b/div))
+ end
+
+
+ for n = ini+1, exp, 1 do
+ r,g,b = findVoid(cube,SHADES)
+
+ if (r == -1) then messagebox("Report:","No more colors can be found, exit at "..n); break; end
+
+ mult = 255 / (SHADES - 1)
+ setcolor(n, r*mult,g*mult,b*mult)
+
+ if linemode == 1 then
+ -- Add lines from new color to all old
+ for x = 0, n-1, 1 do
+ r2,g2,b2 = getcolor(x)
+ enableRangeColorsInCube(cube,SHADES,r*mult,g*mult,b*mult,r2,g2,b2) -- uses 24bit values rgb
+ end
+ end
+
+ addColor2Cube(cube,SHADES,r,g,b) -- rgb is in 'shade' format here
+
+ end
+
+end
+
+
+
diff --git a/data/common/media/grafx2/scripts/samples_2.4/palette/FillColorCube.lua b/data/common/media/grafx2/scripts/samples_2.4/palette/FillColorCube.lua
new file mode 100644
index 0000000000..c4996f4a10
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/palette/FillColorCube.lua
@@ -0,0 +1,105 @@
+--PALETTE: Fill ColorCube voids v1.0
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+-- Email: dawnbringer@hem.utfors.se
+-- MSN: annassar@hotmail.com
+--
+-- Copyright 2010 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+--
+-- Create a palette by continously filling the greatest void in the RGB color-cube
+--
+
+
+SHADES = 16 -- Going 24bit will probably be too slow and steal too much memory, so we're 12bit (4096 colors) for now
+
+ini = 0
+exp = 255
+
+OK,ini,exp = inputbox("Fill Palette Color voids",
+ "From/Keep #: 0-254", 0, 0,254,0,
+ "Replace to #: 1-255", 31, 1,255,0
+);
+
+
+function initColorCube(sha)
+ ary = {}
+ for z = 0, sha-1, 1 do
+ ary[z+1] = {}
+ for y = 0, sha-1, 1 do
+ ary[z+1][y+1] = {}
+ end
+ end
+ return ary
+end
+
+
+function addColor2Cube(cube,sha,r,g,b) -- Gravity model
+ star = 1000000
+ fade = 1000
+
+ cube[r+1][g+1][b+1] = star
+ for z = 0, sha-1, 1 do
+ for y = 0, sha-1, 1 do
+ for x = 0, sha-1, 1 do
+
+ d = fade / ( (x-b)^2 + (y-g)^2 + (z-r)^2 )
+
+ if cube[z+1][y+1][x+1] ~= nil then
+ cube[z+1][y+1][x+1] = cube[z+1][y+1][x+1] + d
+ else
+ cube[z+1][y+1][x+1] = d
+ end
+
+ end;end;end
+end
+
+
+function findVoid(cube,sha)
+ weakest = 999999999999
+ weak_i = {-1,-1,-1}
+ for z = 0, sha-1, 1 do
+ for y = 0, sha-1, 1 do
+ for x = 0, sha-1, 1 do
+
+ w = cube[z+1][y+1][x+1]
+ if w <= weakest then weakest = w; weak_i = {z,y,x}; end
+
+ end;end;end
+ return weak_i[1],weak_i[2],weak_i[3]
+end
+
+--
+
+if OK == true then
+
+ cube = initColorCube(SHADES)
+ -- Fill cube with initial colors
+ for n = 0, ini-1, 1 do
+ r,g,b = getcolor(n)
+ div = SHADES
+ addColor2Cube(cube,SHADES,math.floor(r/div),math.floor(g/div),math.floor(b/div))
+ end
+
+ if ini == 0 then -- With no inital color, some inital data must be added to the colorcube.
+ addColor2Cube(cube,SHADES,0,0,0)
+ setcolor(0, 0,0,0)
+ ini = ini + 1
+ end
+
+ for n = ini, exp, 1 do
+ r,g,b = findVoid(cube,SHADES)
+ mult = 255 / (SHADES - 1)
+ setcolor(n, r*mult,g*mult,b*mult)
+ addColor2Cube(cube,SHADES,r,g,b)
+ end
+
+end
+
+
+
diff --git a/data/common/media/grafx2/scripts/samples_2.4/palette/InvertedRGB.lua b/data/common/media/grafx2/scripts/samples_2.4/palette/InvertedRGB.lua
new file mode 100644
index 0000000000..4a8f58383d
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/palette/InvertedRGB.lua
@@ -0,0 +1,27 @@
+--PALETTE Modify: Inverted RGB
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+
+-- Copyright 2010 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+-- This script was adopted from Evalion, a Javascript codecrafting/imageprocessing project
+-- http://goto.glocalnet.net/richard_fhager/evalion/evalion.html
+
+
+
+for c = 0, 255, 1 do
+
+ r,g,b = getcolor(c)
+
+ r2 = (g+b)/2
+ g2 = (r+b)/2
+ b2 = (r+g)/2
+
+ setcolor(c, r2,g2,b2)
+
+end
diff --git a/data/common/media/grafx2/scripts/samples_2.4/palette/Set3bit.lua b/data/common/media/grafx2/scripts/samples_2.4/palette/Set3bit.lua
new file mode 100644
index 0000000000..21112a5d65
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/palette/Set3bit.lua
@@ -0,0 +1,39 @@
+--PALETTE Set: 3 Bit (8 Primaries)
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+
+-- Copyright 2010 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+
+-- Generate palette of all colors possible with a given number of shades for each channel
+-- 2 shades = 1 bit / channel = 3 bit palette = 2^3 colors = 8 colors
+-- 4 shades = 2 bit / channel = 6 bit palette = 2^6 colors = 64 colors
+
+-- Channel shades (shades = 2 ^ bit-depth)
+shades = 2
+
+mult = 255 / (shades-1)
+
+
+colors = {}
+col = 0
+for r = 0, shades-1, 1 do
+ for g = 0, shades-1, 1 do
+ for b = 0, shades-1, 1 do
+ col = col + 1
+ colors[col] = { r*mult, g*mult, b*mult }
+ end
+ end
+end
+
+
+for c = 1, #colors, 1 do
+
+ setcolor(c-1,colors[c][1],colors[c][2],colors[c][3])
+
+end
diff --git a/data/common/media/grafx2/scripts/samples_2.4/palette/Set6bit.lua b/data/common/media/grafx2/scripts/samples_2.4/palette/Set6bit.lua
new file mode 100644
index 0000000000..ad7a24cb92
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/palette/Set6bit.lua
@@ -0,0 +1,39 @@
+--PALETTE Set: Full 6 Bit (64 colors)
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+
+-- Copyright 2010 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+
+-- Generate palette of all colors possible with a given number of shades for each channel
+-- 2 shades = 1 bit / channel = 3 bit palette = 2^3 colors = 8 colors
+-- 4 shades = 2 bit / channel = 6 bit palette = 2^6 colors = 64 colors
+
+-- Channel shades (shades = 2 ^ bit-depth)
+shades = 4
+
+mult = 255 / (shades-1)
+
+
+colors = {}
+col = 0
+for r = 0, shades-1, 1 do
+ for g = 0, shades-1, 1 do
+ for b = 0, shades-1, 1 do
+ col = col + 1
+ colors[col] = { r*mult, g*mult, b*mult }
+ end
+ end
+end
+
+
+for c = 1, #colors, 1 do
+
+ setcolor(c-1,colors[c][1],colors[c][2],colors[c][3])
+
+end
diff --git a/data/common/media/grafx2/scripts/samples_2.4/palette/SetC64Palette.lua b/data/common/media/grafx2/scripts/samples_2.4/palette/SetC64Palette.lua
new file mode 100644
index 0000000000..26a38cf0ff
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/palette/SetC64Palette.lua
@@ -0,0 +1,50 @@
+--PALETTE Set: C64 Palette (16 colors)
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+
+-- Copyright 2010 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+
+OK,clean = inputbox("C64 Palette:", "Remove old palette", 0, 0,1,0
+);
+
+
+
+ colors = {{0, 0, 0}, -- 0 Black
+ {62, 49,162}, -- 1 D.Blue
+ {87, 66, 0}, -- 2 Brown
+ {140, 62, 52}, -- 3 D.Red
+ {84, 84, 84}, -- 4 D.Grey
+ {141, 72,179}, -- 5 Purple
+ {144, 95, 37}, -- 6 Orange
+ {124,112,218}, -- 7 B.Blue
+ {128,128,128}, -- 8 Grey
+ {104,169, 65}, -- 9 Green
+ {187,119,109}, -- 10 B.Red
+ {122,191,199}, -- 11 Cyan
+ {171,171,171}, -- 12 B.Grey
+ {208,220,113}, -- 13 Yellow
+ {172,234,136}, -- 14 B.Green
+ {255,255,255} -- 15 White
+ }
+
+
+if OK == true then
+
+ for c = 1, #colors, 1 do
+ setcolor(c-1,colors[c][1],colors[c][2],colors[c][3])
+ end
+
+
+ if clean == 1 then
+ for c = #colors+1, 256, 1 do
+ setcolor(c-1,0,0,0)
+ end
+ end
+
+end
\ No newline at end of file
diff --git a/data/common/media/grafx2/scripts/samples_2.4/palette/ShiftHue.lua b/data/common/media/grafx2/scripts/samples_2.4/palette/ShiftHue.lua
new file mode 100644
index 0000000000..986e98cf3a
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/palette/ShiftHue.lua
@@ -0,0 +1,55 @@
+--PALETTE Adjust: Shift Hue v0.9
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+
+-- Copyright 2010 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+-- This script was adopted from Evalion, a Javascript codecrafting/imageprocessing project
+-- http://goto.glocalnet.net/richard_fhager/evalion/evalion.html
+
+
+--Shift_degrees = 45
+
+OK,Shift_degrees = inputbox("Shift Hue v0.9","Degrees", 45, 0,360,3);
+
+
+--
+function shiftHUE(r,g,b,deg) -- V1.3 R.Fhager 2007, adopted from Evalion
+ local c,h,mi,mx,d,s,p,i,f,q,t
+ c = {g,b,r}
+ mi = math.min(r,g,b)
+ mx = math.max(r,g,b); v = mx;
+ d = mx - mi;
+ s = 0; if mx ~= 0 then s = d/mx; end
+ p = 1; if g ~= mx then p = 2; if b ~= mx then p = 0; end; end
+
+ if s~=0 then
+ h=(deg/60+(6+p*2+(c[1+p]-c[1+(p+1)%3])/d))%6;
+ i=math.floor(h);
+ f=h-i;
+ p=v*(1-s);
+ q=v*(1-s*f);
+ t=v*(1-s*(1-f));
+ c={v,q,p,p,t,v}
+ r = c[1+i]
+ g = c[1+(i+4)%6]
+ b = c[1+(i+2)%6]
+ end
+
+ return r,g,b
+end
+--
+
+if OK == true then
+
+ for c = 0, 255, 1 do
+ r,g,b = getcolor(c)
+ setcolor(c, shiftHUE(r,g,b,Shift_degrees))
+ end
+
+end
\ No newline at end of file
diff --git a/data/common/media/grafx2/scripts/samples_2.4/picture/CellColourReducer.lua b/data/common/media/grafx2/scripts/samples_2.4/picture/CellColourReducer.lua
new file mode 100644
index 0000000000..3f97a8c096
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/picture/CellColourReducer.lua
@@ -0,0 +1,37 @@
+-- cell colour reducer - jan'11, from Paulo Silva, with help from people from GrafX2 google group (DawnBringer, Adrien Destugues (PulkoMandy), and Yves Rizoud)
+-- This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. See
+w,h=getpicturesize()
+ok,xcell,ycell=inputbox("Modify cell pixel size","xcell",8,1,16,4,"ycell",8,1,16,4);
+if ok==true then
+ function grayscaleindexed(c)
+ r,g,b=getcolor(c);return math.floor((b*11+r*30+g*59)/100);end
+ celcnt={};for n=0,255,1 do celcnt[n+1]=0;end -- Arraycounter must have initial value
+ for y1=0,h-1,ycell do
+ for x1=0,w-1,xcell do
+ for i=0,255,1 do
+ celcnt[i+1]=0;end
+ for y2=0,ycell-1,1 do
+ for x2=0,xcell-1,1 do
+ x=x1+x2;y=y1+y2;u=getpicturepixel(x,y)
+ celcnt[u+1]=celcnt[u+1]+(1000*xcell*ycell)+math.random(0,950);end;end
+ ikattr=0;paattr=0;ikcnt=0;pacnt=0
+ for i=0,255,1 do
+ if ikcntgrayscaleindexed(paattr) then tmpr=ikattr;ikattr=paattr;paattr=tmpr;end
+ wmid=math.floor((grayscaleindexed(paattr)+grayscaleindexed(ikattr))/2)
+ for y2=0,ycell-1,1 do
+ for x2=0,xcell-1,1 do
+ x=x1+x2;y=y1+y2;u=getpicturepixel(x,y)
+ if u==ikattr then
+ idou=ikattr
+ elseif u==paattr then
+ idou=paattr
+ else
+ idou=ikattr
+ if grayscaleindexed(u)>wmid then idou=paattr;end
+ end
+ putpicturepixel(x,y,idou)
+ end;end;end;end;end
diff --git a/data/common/media/grafx2/scripts/samples_2.4/picture/DrawGridIsometric.lua b/data/common/media/grafx2/scripts/samples_2.4/picture/DrawGridIsometric.lua
new file mode 100644
index 0000000000..1dd304dbe9
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/picture/DrawGridIsometric.lua
@@ -0,0 +1,13 @@
+-- Draw isometric grid - Copyright 2010 Paulo Silva
+-- This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. See
+w,h=getpicturesize();
+ok,gsiz,ik=inputbox("draw isometric grid","size",16,0,128,5,"colour",1,0,255,6);
+if ok==true then
+ for y=0,h-1,gsiz do
+ for x=0,w-1,1 do
+ putpicturepixel(x,y+(x/2)%gsiz,ik);
+ end;end
+ for y=0,h-1,gsiz do
+ for x=0,w-1,1 do
+ putpicturepixel(x+((gsiz/2)-1),y+(gsiz-1)-((x/2)%gsiz),ik);
+ end;end;end
diff --git a/data/common/media/grafx2/scripts/samples_2.4/picture/DrawGridOrthogonal_RGB.lua b/data/common/media/grafx2/scripts/samples_2.4/picture/DrawGridOrthogonal_RGB.lua
new file mode 100644
index 0000000000..729243fcac
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/picture/DrawGridOrthogonal_RGB.lua
@@ -0,0 +1,14 @@
+-- draw grid - rgb (matchcolor) - Copyright 2010 Paulo Silva
+-- This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. See
+w,h=getpicturesize()
+ok,xsiz,ysiz,r,g,b=inputbox("draw grid - rgb (matchcolor)","x size",8,1,64,5,"y size",8,1,64,6,"r",128,0,255,6,"g",128,0,255,6,"b",128,0,255,6);
+if ok==true then
+ c=matchcolor(r,g,b)
+ for y=0,h-1,1 do
+ for x=0,w-1,xsiz do
+ putpicturepixel(x,y,c);
+ end;end
+ for y=0,h-1,ysiz do
+ for x=0,w-1,1 do
+ putpicturepixel(x,y,c);
+ end;end;end
diff --git a/data/common/media/grafx2/scripts/samples_2.4/picture/DrawgridOrthogonal_Index.lua b/data/common/media/grafx2/scripts/samples_2.4/picture/DrawgridOrthogonal_Index.lua
new file mode 100644
index 0000000000..7f4aafcd47
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/picture/DrawgridOrthogonal_Index.lua
@@ -0,0 +1,11 @@
+-- draw grid - indexed colour - Copyright 2010 Paulo Silva
+-- This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. See
+w,h=getpicturesize();
+ok,xsiz,ysiz,c=inputbox("draw grid - indexed colour)","x size",8,1,64,5,"y size",8,1,64,6,"colour id",0,0,255,6);
+if ok==true then
+ for y=0,h-1,1 do
+ for x=0,w-1,xsiz do
+ putpicturepixel(x,y,c);end;end
+ for y=0,h-1,ysiz do
+ for x=0,w-1,1 do
+ putpicturepixel(x,y,c);end;end;end
diff --git a/data/common/media/grafx2/scripts/samples_2.4/picture/GlassGridFilter.lua b/data/common/media/grafx2/scripts/samples_2.4/picture/GlassGridFilter.lua
new file mode 100644
index 0000000000..3aa7e22e3f
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/picture/GlassGridFilter.lua
@@ -0,0 +1,12 @@
+-- Glass grid filter - Copyright 2010 Paulo Silva
+-- This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. See
+w,h=getpicturesize();
+ok,xsiz,ysiz=inputbox("message","xsize",8,0,64,5,"ysize",8,0,64,6);
+if ok==true then
+ for y1=0,h-1,xsiz do
+ for x1=0,w-1,ysiz do
+ for y2=0,(ysiz/2)-1,1 do
+ for x2=0,xsiz-1,1 do
+ c1=getpicturepixel(x1+x2,y1+y2);c2=getpicturepixel(x1+(xsiz-1)-x2,y1+(ysiz-1)-y2)
+ putpicturepixel(x1+x2,y1+y2,c2);putpicturepixel(x1+(xsiz-1)-x2,y1+(ysiz-1)-y2,c1)
+ end;end;end;end;end
diff --git a/data/common/media/grafx2/scripts/samples_2.4/picture/PaletteToPicture.lua b/data/common/media/grafx2/scripts/samples_2.4/picture/PaletteToPicture.lua
new file mode 100644
index 0000000000..a645fd3475
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/picture/PaletteToPicture.lua
@@ -0,0 +1,11 @@
+-- palette to picture - Copyright 2010 Paulo Silva
+-- This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. See
+w,h=getpicturesize();
+ok,xsiz,ysiz=inputbox("palette to picture","x size",8,1,16,5,"y size",8,1,16,6);
+if ok==true then
+ for y1=0,7,1 do
+ for x1=0,31,1 do
+ for y2=0,ysiz-1,1 do
+ for x2=0,xsiz-1,1 do
+ putpicturepixel(x1*xsiz+x2,y1*ysiz+y2,y1+x1*8)
+ end;end;end;end;end
diff --git a/data/common/media/grafx2/scripts/samples_2.4/picture/Pic2isometric.lua b/data/common/media/grafx2/scripts/samples_2.4/picture/Pic2isometric.lua
new file mode 100644
index 0000000000..8002b816b0
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/picture/Pic2isometric.lua
@@ -0,0 +1,87 @@
+--PICTURE (part of): 2 Isometric v0.1b
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+-- Email: dawnbringer@hem.utfors.se
+-- MSN: annassar@hotmail.com
+--
+-- Copyright 2010 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+
+-- Color 0 is assumed to be the background
+--
+
+iso = {{0, 0, 1, 1, 1, 1, 0, 0},
+ {1, 1, 1, 1, 1, 1, 1, 1},
+ {2, 2, 1, 1, 1, 1, 3, 3},
+ {2, 2, 2, 2, 3, 3, 3, 3},
+ {2, 2, 2, 2, 3, 3, 3, 3},
+ {2, 2, 2, 2, 3, 3, 3, 3},
+ {0, 0, 2, 2, 3, 3, 0, 0}}
+
+isowidth = 8
+isoheight = 7
+
+xoff = 0.5
+yoff = 0
+
+xstep = 4
+ystep = 2
+zstep = 4
+
+-- Part of screen from top-left (4 = 1/4)
+xsize = 5
+ysize = 4
+
+
+w, h = getpicturesize()
+
+xo = math.floor(w * xoff)
+
+-- just don't render more than can be fittted right now
+w = math.floor(w / xsize)
+h = math.floor(h / ysize)
+
+
+
+for y = 0, h - 1, 1 do
+ for x = 0, w - 1, 1 do
+
+ isox = x * xstep - y * xstep
+ isoy = y * ystep + x * ystep
+
+ cb = getbackuppixel(x,y)
+
+ --
+ if cb ~= 0 then
+
+ r,g,b = getbackupcolor(cb);
+ c1 = matchcolor(r,g,b);
+ c2 = matchcolor(r+64, g+64, b+64);
+ c3 = matchcolor(r-64, g-64, b-64);
+
+ cols = {0,c1,c2,c3}
+
+ for iy = 1, isoheight, 1 do
+ for ix = 1, isowidth, 1 do
+
+ i = iso[iy][ix]
+ c = cols[i+1]
+ if i ~= 0 then putpicturepixel(xo + isox+ix-1, isoy+iy-1, c); end
+
+ end
+ end
+
+ end
+ --
+
+ end
+end
+
+
+
+
diff --git a/data/common/media/grafx2/scripts/samples_2.4/picture/Rainbow-Dark2Bright.lua b/data/common/media/grafx2/scripts/samples_2.4/picture/Rainbow-Dark2Bright.lua
new file mode 100644
index 0000000000..35b0247bae
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/picture/Rainbow-Dark2Bright.lua
@@ -0,0 +1,36 @@
+--PICTURE: Rainbow - Dark to Bright v1.1
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+-- Email: dawnbringer@hem.utfors.se
+-- MSN: annassar@hotmail.com
+--
+
+
+--dofile("dawnbringer_lib.lua")
+run("../libs/dawnbringer_lib.lua")
+--> db.shiftHUE(r,g,b, deg)
+
+w, h = getpicturesize()
+
+for y = 0, h - 1, 1 do
+ for x = 0, w - 1, 1 do
+
+ -- Fractionalize image dimensions
+ ox = x / w;
+ oy = y / h;
+
+ r = 255 * math.sin(oy * 2)
+ g = (oy-0.5)*512 * oy
+ b = (oy-0.5)*512 * oy
+
+ r, g, b = db.shiftHUE(r,g,b,ox * 360);
+
+ c = matchcolor(r,g,b)
+
+ putpicturepixel(x, y, c);
+
+ end
+ updatescreen(); if (waitbreak(0)==1) then return; end
+end
+
+
diff --git a/data/common/media/grafx2/scripts/samples_2.4/picture/RemapImage2RGB.lua b/data/common/media/grafx2/scripts/samples_2.4/picture/RemapImage2RGB.lua
new file mode 100644
index 0000000000..1d87bea631
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/picture/RemapImage2RGB.lua
@@ -0,0 +1,50 @@
+--SCENE: Remap pic to RGB, diag.dith
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+
+-- Copyright 2010 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+-- Set Palette (to a predefined one)
+
+colors = {{ 0, 0, 0},
+ {255, 0, 0},
+ { 0,255, 0},
+ { 0, 0,255}
+ }
+
+
+chm = {1,0,0}
+
+for c = 1, #colors, 1 do
+ setcolor(c-1,colors[c][1],colors[c][2],colors[c][3])
+end
+
+for c = #colors, 255, 1 do
+ setcolor(c,0,0,0)
+end
+
+
+
+w, h = getpicturesize()
+
+for y = 0, h - 1, 1 do
+
+ for x = 0, w - 1, 1 do
+
+ r,g,b = getbackupcolor(getbackuppixel(x,y));
+
+ rn = r * chm[1+(y+0+x)%3]
+ gn = g * chm[1+(y+1+x)%3]
+ bn = b * chm[1+(y+2+x)%3]
+
+ n = matchcolor(rn,gn,bn);
+
+ putpicturepixel(x, y, n);
+
+ end
+end
diff --git a/data/common/media/grafx2/scripts/samples_2.4/picture/RemapImage2RGB_ed.lua b/data/common/media/grafx2/scripts/samples_2.4/picture/RemapImage2RGB_ed.lua
new file mode 100644
index 0000000000..f59048ecee
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/picture/RemapImage2RGB_ed.lua
@@ -0,0 +1,69 @@
+--SCENE: Remap pic 2 RGB, 1lineED-dith. (Same line simple error-diffusion dither)
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+
+-- Copyright 2010 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+
+power = 0.615
+
+c1 = 0.8 -- Error weight (white is green)
+c2 = 0.2 -- RGB weight (white is r+g+b)
+
+-- Set Palette (to a predefined one)
+
+colors = {{ 0, 0, 0},
+ {255, 0, 0},
+ { 0,255, 0},
+ { 0, 0,255}
+ }
+
+
+chm = {1,0,0}
+
+for c = 1, #colors, 1 do
+ setcolor(c-1,colors[c][1],colors[c][2],colors[c][3])
+end
+
+for c = #colors, 255, 1 do
+ setcolor(c,0,0,0)
+end
+
+
+
+w, h = getpicturesize()
+
+for y = 0, h - 1, 1 do
+
+ re = 0
+ ge = 0
+ be = 0
+
+ for x = (y%2), w - 1, 1 do
+
+ r,g,b = getbackupcolor(getbackuppixel(x,y));
+
+ rn = re * c1 + r * chm[1+(y+0+x)%3] * c2
+ gn = ge * c1 + g * chm[1+(y+1+x)%3] * c2
+ bn = be * c1 + b * chm[1+(y+2+x)%3] * c2
+
+ n = matchcolor(rn,gn,bn);
+
+ putpicturepixel(x, y, n);
+
+
+ rn,gn,bn = getcolor(getpicturepixel(x,y));
+
+ re = (re + (r - rn)) * power
+ ge = (ge + (g - gn)) * power
+ be = (be + (b - bn)) * power
+
+
+
+ end
+end
diff --git a/data/common/media/grafx2/scripts/samples_2.4/picture/RemapImageTo3bitPal.lua b/data/common/media/grafx2/scripts/samples_2.4/picture/RemapImageTo3bitPal.lua
new file mode 100644
index 0000000000..20a71599d6
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/picture/RemapImageTo3bitPal.lua
@@ -0,0 +1,80 @@
+--SCENE: Remap pic to 3bit, LineEDdith. (Same line simple error-diffusion dither)
+--by Richard Fhager
+--http://hem.fyristorg.com/dawnbringer/
+
+-- Copyright 2010 Richard Fhager
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+--
+-- Just a demonstration.
+--
+
+
+
+power = 0.6
+
+-- Channel shades (shades = 2 ^ bit-depth)
+shades = 2
+
+mult = 255 / (shades-1)
+
+
+colors = {}
+col = 0
+for r = 0, shades-1, 1 do
+ for g = 0, shades-1, 1 do
+ for b = 0, shades-1, 1 do
+ col = col + 1
+ colors[col] = { r*mult, g*mult, b*mult }
+ end
+ end
+end
+
+
+for c = 1, #colors, 1 do
+
+ setcolor(c-1,colors[c][1],colors[c][2],colors[c][3])
+
+end
+
+
+for c = #colors, 255, 1 do
+ setcolor(c,0,0,0)
+end
+
+
+
+w, h = getpicturesize()
+
+for y = 0, h - 1, 1 do
+
+ re = 0
+ ge = 0
+ be = 0
+
+ for x = (y%2), w - 1, 1 do
+
+ r,g,b = getbackupcolor(getbackuppixel(x,y));
+
+ rn = re + r
+ gn = ge + g
+ bn = be + b
+
+ n = matchcolor(rn,gn,bn);
+
+ putpicturepixel(x, y, n);
+
+
+ rn,gn,bn = getcolor(getpicturepixel(x,y));
+
+ re = (re + (r - rn)) * power
+ ge = (ge + (g - gn)) * power
+ be = (be + (b - bn)) * power
+
+
+ end
+end
diff --git a/data/common/media/grafx2/scripts/samples_2.4/picture/Tiler.lua b/data/common/media/grafx2/scripts/samples_2.4/picture/Tiler.lua
new file mode 100644
index 0000000000..760dd42e79
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/picture/Tiler.lua
@@ -0,0 +1,112 @@
+--Picture Tiler by Adrien Destugues
+--Extract unique tiles from the spare page
+--to the main one. Main page is erased.
+--
+-- Copyright 2011 Adrien Destugues
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; version 2
+-- of the License. See
+
+-- Copy palette from spare to main
+-- TODO
+
+-- Grid size
+-- TODO : get it from GrafX2
+xgrid = 16;
+ygrid = 16;
+
+-- picture size
+w, h = getpicturesize();
+
+-- We may need less if there are duplicates
+setsparepicturesize(xgrid, w*h/xgrid);
+
+tileid = 0;
+
+-- blit part of the spare to picture
+function blitpicturetospare(srcx, srcy, dstx, dsty, width, height)
+ local x,y;
+ for y = 0, height - 1, 1 do
+ for x = 0, width - 1, 1 do
+ putsparepicturepixel(dstx+x, dsty+y, getpicturepixel(srcx + x, srcy + y));
+ end
+ end
+end
+
+function comparesparewithpicture(srcx, srcy, dstx, dsty, width, height)
+ local x,y,color
+ for y = 0, height - 1, 1 do
+ for x = 0, width - 1, 1 do
+ color = getsparepicturepixel(srcx + x, srcy + y);
+ if color ~= getpicturepixel(dstx+x, dsty+y) then
+ -- they are different
+ return false;
+ end
+ end
+ end
+ -- they are identical
+ return true;
+end
+
+-- compute checksum of a picture area
+-- it may not be unique, we use it as a key for an hashmap
+function checksum(srcx, srcy, width, height)
+ local sum,x,y
+ sum = 0;
+ for y = 0, height - 1, 1 do
+ for x = 0, width - 1, 1 do
+ sum = sum + getpicturepixel(srcx+x, srcy+y);
+ end
+ end
+
+ return sum;
+end
+
+tilemap = {}
+
+-- foreach tile
+for y = 0, h-1, ygrid do
+ for x = 0, w - 1, xgrid do
+ -- existing one ?
+ csum = checksum(x,y,xgrid,ygrid);
+ if tilemap[csum] ~= nil then
+ -- potential match
+ -- Find matching tileid
+ found = false;
+ for id in pairs(tilemap[csum]) do
+ -- is it a match ?
+ if comparesparewithpicture(x,y,0,id*ygrid, xgrid, ygrid) then
+ -- found it !
+ tilemap[csum][id] = tilemap[csum][id] + 1;
+ found = true;
+ break;
+ end
+ end
+ -- Add tile anyway if needed
+ if not found then
+ desty = tileid * ygrid;
+ blitpicturetospare(x, y, 0, desty, xgrid, ygrid);
+
+ -- add it to the tilemap
+ tilemap[csum][tileid] = 1;
+ -- give it a tile id
+ tileid = tileid + 1;
+ end
+ else
+ -- Copy to spare
+ desty = tileid * ygrid;
+ blitpicturetospare(x, y, 0, desty, xgrid, ygrid);
+
+ -- add it to the tilemap
+ tilemap[csum] = {}
+ tilemap[csum][tileid] = 1;
+ -- give it a tile id
+ tileid = tileid + 1;
+ end
+ end
+end
+
+setsparepicturesize(xgrid, (tileid-1)*ygrid)
+--updatescreen();
diff --git a/data/common/media/grafx2/scripts/samples_2.4/picture/XBitColourXpaceFromPalette.lua b/data/common/media/grafx2/scripts/samples_2.4/picture/XBitColourXpaceFromPalette.lua
new file mode 100644
index 0000000000..444e62fa8c
--- /dev/null
+++ b/data/common/media/grafx2/scripts/samples_2.4/picture/XBitColourXpaceFromPalette.lua
@@ -0,0 +1,12 @@
+-- Copyright 2010 Paulo Silva
+-- This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. See
+w,h=getpicturesize();
+ok,bitd=inputbox("colourspace from palette","bitdepth:",4,1,8,5);
+if ok==true then
+ bitd3=(2^bitd);bitd8=(2^(math.floor(bitd/2)));bitd9=(2^((math.floor((bitd-1)/2))+1))
+ for y1=0,(bitd8-1),1 do
+ for x1=0,(bitd9-1),1 do
+ for y2=0,(bitd3-1),1 do
+ for x2=0,(bitd3-1),1 do
+ putpicturepixel(x1*bitd3+x2,y1*bitd3+y2,matchcolor((y2*255)/(bitd3-1),((y1*8+x1)*255)/(bitd3-1),(x2*255)/(bitd3-1)))
+ end;end;end;end;end
diff --git a/data/common/media/grafx2/skins/font_Classic.png b/data/common/media/grafx2/skins/font_Classic.png
new file mode 100644
index 0000000000..56379f68e8
Binary files /dev/null and b/data/common/media/grafx2/skins/font_Classic.png differ
diff --git a/data/common/media/grafx2/skins/font_DPaint.png b/data/common/media/grafx2/skins/font_DPaint.png
new file mode 100644
index 0000000000..cbc90df313
Binary files /dev/null and b/data/common/media/grafx2/skins/font_DPaint.png differ
diff --git a/data/common/media/grafx2/skins/font_Fairlight.png b/data/common/media/grafx2/skins/font_Fairlight.png
new file mode 100644
index 0000000000..d892df6f01
Binary files /dev/null and b/data/common/media/grafx2/skins/font_Fairlight.png differ
diff --git a/data/common/media/grafx2/skins/font_Fun.png b/data/common/media/grafx2/skins/font_Fun.png
new file mode 100644
index 0000000000..3eea4a3eb5
Binary files /dev/null and b/data/common/media/grafx2/skins/font_Fun.png differ
diff --git a/data/common/media/grafx2/skins/font_Melon.png b/data/common/media/grafx2/skins/font_Melon.png
new file mode 100644
index 0000000000..56e6c54f7b
Binary files /dev/null and b/data/common/media/grafx2/skins/font_Melon.png differ
diff --git a/data/common/media/grafx2/skins/font_Seen.png b/data/common/media/grafx2/skins/font_Seen.png
new file mode 100644
index 0000000000..253bd05a84
Binary files /dev/null and b/data/common/media/grafx2/skins/font_Seen.png differ
diff --git a/data/common/media/grafx2/skins/skin_Aurora.png b/data/common/media/grafx2/skins/skin_Aurora.png
new file mode 100644
index 0000000000..adf1257316
Binary files /dev/null and b/data/common/media/grafx2/skins/skin_Aurora.png differ
diff --git a/data/common/media/grafx2/skins/skin_Clax2.gif b/data/common/media/grafx2/skins/skin_Clax2.gif
new file mode 100644
index 0000000000..6c27d3b35a
Binary files /dev/null and b/data/common/media/grafx2/skins/skin_Clax2.gif differ
diff --git a/data/common/media/grafx2/skins/skin_Clax3.gif b/data/common/media/grafx2/skins/skin_Clax3.gif
new file mode 100644
index 0000000000..ffefeaf341
Binary files /dev/null and b/data/common/media/grafx2/skins/skin_Clax3.gif differ
diff --git a/data/common/media/grafx2/skins/skin_Clax4.gif b/data/common/media/grafx2/skins/skin_Clax4.gif
new file mode 100644
index 0000000000..294f97c8a2
Binary files /dev/null and b/data/common/media/grafx2/skins/skin_Clax4.gif differ
diff --git a/data/common/media/grafx2/skins/skin_DPaint.png b/data/common/media/grafx2/skins/skin_DPaint.png
new file mode 100644
index 0000000000..495572ee90
Binary files /dev/null and b/data/common/media/grafx2/skins/skin_DPaint.png differ
diff --git a/data/common/media/grafx2/skins/skin_classic.png b/data/common/media/grafx2/skins/skin_classic.png
new file mode 100644
index 0000000000..a0cc86acf2
Binary files /dev/null and b/data/common/media/grafx2/skins/skin_classic.png differ
diff --git a/data/common/media/grafx2/skins/skin_modern.png b/data/common/media/grafx2/skins/skin_modern.png
new file mode 100644
index 0000000000..66ec408334
Binary files /dev/null and b/data/common/media/grafx2/skins/skin_modern.png differ
diff --git a/data/common/media/grafx2/skins/skin_scenish.png b/data/common/media/grafx2/skins/skin_scenish.png
new file mode 100644
index 0000000000..2280211767
Binary files /dev/null and b/data/common/media/grafx2/skins/skin_scenish.png differ
diff --git a/data/common/settings/app_plus.ini b/data/common/settings/app_plus.ini
index 20c1454b74..8bf4130b98 100644
--- a/data/common/settings/app_plus.ini
+++ b/data/common/settings/app_plus.ini
@@ -7,6 +7,7 @@ window_title=KolibriOS Additional Software
[Media]
zSea=/kolibrios/media/zsea/zsea,46
FPlay=/kolibrios/media/fplay,40
+GrafX2=/kolibrios/grafx2/grafx2,48
[3D Tools]
Info3DsPro=/kolibrios/3d/info3ds/info3ds,75
@@ -15,8 +16,9 @@ VoxelEditor=/kolibrios/3d/voxel_editor/voxel_editor,30
[3D Demos]
3dsHeart=/kolibrios/3d/3dsheart,78
+M2View=/kolibrios/3d/m2view/m2view,78
[Other]
Calc+=/kolibrios/utils/calcplus,4
Life=/kolibrios/demos/life2,13
-fNav=/kolibrios/unils/fNav.kex,45
+fNav=/kolibrios/utils/fNav.kex,45