A pixel art painter geared specifically at NES pixel art. Includes export for .chr binary file as well as palette and namespace data.
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

227 lines
7.4KB

  1. import GlobalEvents from "/app/js/common/EventCaller.js";
  2. import NESPalette from "/app/js/models/NESPalette.js";
  3. const ATTRIB_NESIDX = "nesidx";
  4. // The two attributes below MUST BOTH be in the element.
  5. const ATTRIB_PALIDX = "pidx"; // This is the palette index (0 - 3 (Tiles) 4 - 7 (Sprites))
  6. const ATTRIB_COLIDX = "cidx"; // This is the color index in the selected palette (0 - 3)
  7. const CLASS_BTN_ACTIVE = "pure-button-active";
  8. const SELECT_ID_PALETTEMODE = "palette-mode";
  9. var Active_Palette_Index = 0;
  10. var Active_Color_Index = 0;
  11. function InvertRGB(hex){
  12. var h = (255 - parseInt(hex, 16)).toString(16);
  13. return (h.length < 2) ? "0" + h : h;
  14. }
  15. function InvertColor(chex, bw){
  16. bw = (bw === true);
  17. if (chex.indexOf("#") === 0){
  18. chex = chex.slice(1);
  19. }
  20. if (chex.length === 3){
  21. chex = chex[0] + chex[0] + chex[1] + chex[1] + chex[2] + chex[2];
  22. }
  23. if (chex.length !== 6){
  24. throw new ValueError("Hex color expected to be 3 or 6 characters long.");
  25. }
  26. if (bw) {
  27. var r = parseInt(chex.slice(0, 2), 16);
  28. var g = parseInt(chex.slice(2, 4), 16);
  29. var b = parseInt(chex.slice(4, 6), 16);
  30. // http://stackoverflow.com/a/3943023/112731
  31. return (r * 0.299 + g * 0.587 + b * 0.114) > 186
  32. ? '#000000' : '#FFFFFF';
  33. }
  34. return "#" + InvertRGB(chex.slice(0, 2)) + InvertRGB(chex.slice(2, 4)) + InvertRGB(chex.slice(4, 6));
  35. }
  36. function GetPaletteIndexes(el){
  37. if (el.hasAttribute(ATTRIB_PALIDX) && el.hasAttribute(ATTRIB_COLIDX)){
  38. var pi = el.getAttribute(ATTRIB_PALIDX);
  39. if (!isNaN(pi))
  40. pi = parseInt(pi);
  41. else
  42. pi = -1;
  43. var ci = el.getAttribute(ATTRIB_COLIDX);
  44. if (!isNaN(ci))
  45. ci = parseInt(ci);
  46. else
  47. ci = -1;
  48. if (pi >= 0 && pi < 4 && ci >= 0 && ci < 4){
  49. return {pi:pi, ci:ci};
  50. }
  51. }
  52. return null;
  53. }
  54. function SetPaletteElStyle(el, c){
  55. el.style["background-color"] = c;
  56. el.style.color = InvertColor(c);
  57. }
  58. function SetColorPaletteEls(mode, pal){
  59. var elist = document.querySelectorAll("[" + ATTRIB_PALIDX + "]");
  60. elist.forEach(function(el){
  61. var i = GetPaletteIndexes(el);
  62. if (i !== null){
  63. SetPaletteElStyle(el, pal.get_palette_color((mode*4) + i.pi, i.ci));
  64. }
  65. });
  66. }
  67. function FindAndColorPalette(mode, pi, ci, pal){
  68. if ((mode == 0 && pi < 4) || (mode == 1 && pi >= 4)){
  69. var el = document.querySelector("[" + ATTRIB_PALIDX +"='" + (pi%4) + "']" +
  70. "[" + ATTRIB_COLIDX + "='" + ci + "']");
  71. if (el){
  72. SetPaletteElStyle(el, pal.get_palette_color(pi, ci));
  73. }
  74. }
  75. }
  76. class CTRLPalettes{
  77. constructor(){
  78. this.__NESPalette = null;
  79. this.__activePaletteEl = null;
  80. this.__mode = 0; // 0 = Tile palette mode | 1 = Sprite palette mode.
  81. var self = this;
  82. // ------------------------------------------------------------------------------------
  83. // Defining hooks for the main system palette interactions.
  84. // ------------------------------------------------------------------------------------
  85. var handle_syspalette_clicked = function(event){
  86. if (self.__activePaletteEl !== null && this.hasAttribute(ATTRIB_NESIDX)){
  87. var idx = parseInt(this.getAttribute(ATTRIB_NESIDX), 16);
  88. if (idx >= 0 && idx < NESPalette.SystemColor.length){
  89. var i = GetPaletteIndexes(self.__activePaletteEl);
  90. if (self.__palette !== null && i !== null){
  91. self.__NESPalette.set_palette_syscolor_index(i.pi + ((self.__mode === 1) ? 4 : 0), i.ci, idx);
  92. SetPaletteElStyle(self.__activePaletteEl, NESPalette.SystemColor[idx]);
  93. }
  94. }
  95. }
  96. };
  97. var elist = document.querySelectorAll("[" + ATTRIB_NESIDX + "]");
  98. elist.forEach(function(el){
  99. var idx = parseInt(el.getAttribute(ATTRIB_NESIDX), 16);
  100. if (idx >= 0 && idx < NESPalette.SystemColor.length){
  101. SetPaletteElStyle(el, NESPalette.SystemColor[idx]);
  102. el.addEventListener("click", handle_syspalette_clicked);
  103. }
  104. });
  105. // ------------------------------------------------------------------------------------
  106. // Defining hooks for the drawing palette interactions.
  107. // ------------------------------------------------------------------------------------
  108. var handle_palcolor_clicked = function(event){
  109. if (this.hasAttribute(ATTRIB_PALIDX) && this.hasAttribute(ATTRIB_COLIDX)){
  110. if (this !== self.__activePaletteEl){
  111. var i = GetPaletteIndexes(this);
  112. if (i !== null){
  113. if (self.__activePaletteEl !== null){
  114. self.__activePaletteEl.classList.remove(CLASS_BTN_ACTIVE);
  115. }
  116. this.classList.add(CLASS_BTN_ACTIVE);
  117. self.__activePaletteEl = this;
  118. GlobalEvents.emit("active_palette_color", i.pi, i.ci);
  119. }
  120. }
  121. }
  122. };
  123. var elist = document.querySelectorAll("[" + ATTRIB_PALIDX + "]");
  124. elist.forEach(function(el){
  125. if (el.hasAttribute(ATTRIB_PALIDX) && el.hasAttribute(ATTRIB_COLIDX)){
  126. el.addEventListener("click", handle_palcolor_clicked);
  127. }
  128. });
  129. // ------------------------------------------------------------------------------------
  130. // Defining hooks for palette mode swapping
  131. // ------------------------------------------------------------------------------------
  132. var el = document.querySelector("#" + SELECT_ID_PALETTEMODE);
  133. if (el){
  134. el.addEventListener("change", function(event){
  135. switch(this.value){
  136. case "sprite":
  137. self.__mode = 1; break;
  138. case "tile":
  139. self.__mode = 0; break;
  140. }
  141. SetColorPaletteEls(self.__mode, self.__NESPalette);
  142. });
  143. }
  144. // ------------------------------------------------------------------------------------
  145. // Setting some hooks to watch for some global events.
  146. // ------------------------------------------------------------------------------------
  147. var handle_set_app_palette = function(p){
  148. if (p instanceof NESPalette){
  149. self.palette = p;
  150. }
  151. }
  152. GlobalEvents.listen("set_app_palette", handle_set_app_palette);
  153. var handle_palettemode = (function(mode){
  154. if (mode === 0 || mode ===1){
  155. this.__mode = mode;
  156. SetColorPaletteEls(this.__mode, this.__NESPalette);
  157. }
  158. }).bind(this);
  159. GlobalEvents.listen("set_palette_mode", handle_palettemode);
  160. }
  161. get palette(){
  162. return this.__NESPalette;
  163. }
  164. set palette(p){
  165. if (!(p instanceof NESPalette)){
  166. throw new TypeError("Expected NESPalette object instance.");
  167. }
  168. var self = this;
  169. var handle_palettes_changed = function(event){
  170. if (self.__NESPalette !== null){
  171. if (event.type == "ALL"){
  172. SetColorPaletteEls(self.__mode, self.__NESPalette);
  173. } else {
  174. FindAndColorPalette(self.__mode, event.pindex, event.cindex, self.__NESPalette);
  175. }
  176. }
  177. }
  178. // Disconnect listener from old palette and connect it to new palette.
  179. if (this.__NESPalette !== p){
  180. if (this.__NESPalette !== null){
  181. this.__NESPalette.unlisten("palettes_changed", handle_palettes_changed);
  182. }
  183. this.__NESPalette = p;
  184. this.__NESPalette.listen("palettes_changed", handle_palettes_changed);
  185. }
  186. var elist = document.querySelectorAll("[" + ATTRIB_PALIDX + "]");
  187. elist.forEach((function(el){
  188. if (el.hasAttribute(ATTRIB_COLIDX)){
  189. var i = GetPaletteIndexes(el);
  190. if (i !== null){
  191. SetPaletteElStyle(el, p.get_palette_color((this.__mode * 4) + i.pi, i.ci));
  192. }
  193. }
  194. }).bind(this));
  195. }
  196. }
  197. const instance = new CTRLPalettes();
  198. export default instance;