A pixel art painter geared specifically at NES pixel art. Includes export for .chr binary file as well as palette and namespace data.
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

227 行
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;