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文字以内のものにしてください。

295 行
7.8KB

  1. import {EventCaller} from "/app/js/common/EventCaller.js"
  2. /**
  3. * Object for manipulating the eight NES palettes.
  4. * @extends EventCaller
  5. */
  6. export default class NESPalette extends EventCaller{
  7. constructor(){
  8. super();
  9. this.__BGColor = 63; // Index to the background color ALL palettes MUST share.
  10. this.__palette = [
  11. // Tile/Background Palettes
  12. 0,0,0,
  13. 0,0,0,
  14. 0,0,0,
  15. 0,0,0,
  16. // Sprite Palettes
  17. 0,0,0,
  18. 0,0,0,
  19. 0,0,0,
  20. 0,0,0
  21. ];
  22. }
  23. get obj(){
  24. return ([this.__BGColor]).concat(this.__palette);
  25. }
  26. set obj(d){
  27. if (!(d instanceof Array) || d.length !== 25)
  28. throw new TypeError("Invalid Object or value range.");
  29. try {
  30. this.set_palette(d);
  31. } catch (e) {
  32. throw e;
  33. }
  34. }
  35. get json(){
  36. return JSON.stringify(this.obj);
  37. }
  38. set json(j){
  39. try{
  40. this.obj = JSON.parse(j);
  41. } catch (e) {
  42. throw e;
  43. }
  44. }
  45. /**
  46. * Sets one or all of the eight color palettes to the values given. By default, function
  47. * assumes the given array is for all eight palettes (or 25 total color indexes, 3 per palette
  48. * and 1 background/transparency color used by ALL palettes).
  49. * If a single palette is being set, the array must only contain 3 entries.
  50. * @param {Array} apci - Array of color indexes to store into the palette(s)
  51. * @param {number} [p=8] - Zero-based index of the palette being set. Any value outside the range of 0 - 7 will set ALL palettes.
  52. * @returns {this}
  53. */
  54. set_palette(apci, p=8){
  55. var StoreColorValue = (i, v) => {
  56. if (typeof(v) == 'number'){
  57. if (i >= 0)
  58. this.__palette[i] = v
  59. else
  60. this.__BGColor = v;
  61. } else if (typeof(v) == 'string' && v.length == 2){
  62. var c = parseInt(v, 16);
  63. if (!isNaN(c)){
  64. if (i >= 0)
  65. this.__palette[i] = c;
  66. else
  67. this.__BGColor = c;
  68. }
  69. }
  70. };
  71. if (typeof(p) != 'number')
  72. throw new TypeError("First argument expected to be a number.");
  73. if (!(apci instanceof Array))
  74. throw new TypeError("Expected an array of color index values.");
  75. if (p < 0 || p >= 8){ // Setting ALL palettes!
  76. if (apci.length != 25)
  77. throw new RangeError("Color array must contain 25 color values to fill all palettes.");
  78. StoreColorValue(-1, apci[0]);
  79. for (var i=0; i < 24; i++){
  80. StoreColorValue(i, apci[i+1]);
  81. }
  82. } else { // Setting a specific palette.
  83. if (apci.length != 3)
  84. throw new RangeError("Color array must contain three color values.");
  85. p *= 3;
  86. for (var i=0; i < 4; i++){
  87. StoreColorValue(p+i, apci[i]);
  88. }
  89. }
  90. this.emit("palettes_changed", {type:"ALL"});
  91. return this;
  92. }
  93. /**
  94. * Sets a palette's color index value to a given system color index.
  95. * NOTE: Setting palette color index 0 for ANY palette changes that index for ALL palettes.
  96. * @param {number} p - The index of the palette being set.
  97. * @param {number} pci - The palette color index (0 - 3) to set.
  98. * @param {number} sci - The system color index (0 - 63) value to set to.
  99. * @returns {this}
  100. */
  101. set_palette_syscolor_index(p, pci, sci){
  102. if (typeof(p) != 'number' || typeof(pci) != 'number' || typeof(sci) != 'number')
  103. throw new TypeError("Palette, palette color, and system color index expected to be numbers.");
  104. if (p < 0 || p >= 8){
  105. throw new RangeError("Palette index is out of bounds.");
  106. }
  107. if (pci < 0 || pci >= 4){
  108. throw new RangeError("Palette color index is out of bounds.");
  109. }
  110. if (typeof(sci) == "string" && sci.length == 2)
  111. sci = parseInt(sci, 16);
  112. if (isNaN(sci))
  113. throw new TypeError("System Color Index expected to be a number of hex value string.");
  114. if (sci < 0 || sci >= 64){
  115. throw new RangeError("System color index is out of bounds.");
  116. }
  117. if (pci == 0){
  118. this.__BGColor = sci;
  119. this.emit("palettes_changed", {type:"ALL", cindex:0});
  120. } else {
  121. this.__palette[(p*3) + (pci-1)] = sci;
  122. this.emit("palettes_changed", {type:(p < 4) ? "TILE" : "SPRITE", pindex:p, cindex:pci});
  123. }
  124. return this;
  125. }
  126. /**
  127. * Returns the system color index at the given palette color index.
  128. * @param {number} p - The index (0 - 7) of the palette.
  129. * @param {number} pci - The palette color index (0 - 3).
  130. * @param {boolean} [ashex=false] - If true, will return the index as a two character hex string.
  131. * @returns {number} - The index of the system color used.
  132. */
  133. get_palette_syscolor_index(p, pci, ashex=false){
  134. if (typeof(p) != 'number' || typeof(pci) != 'number')
  135. throw new TypeError("Palette and color index expected to be numbers.");
  136. if (p < 0 || p >= 8){
  137. throw new RangeError("Palette index is out of bounds.");
  138. }
  139. if (pci < 0 || pci >= 4){
  140. throw new RangeError("Palette color index is out of bounds.");
  141. }
  142. var i = (pci === 0) ? this.__BGColor : this.__palette[(p*3)+(pci-1)];
  143. if (ashex){
  144. i = i.toString(16);
  145. i = ((i.length < 2) ? "0" : "") + i;
  146. }
  147. return i;
  148. }
  149. /**
  150. * Returns a hex string color value used by the NES system at the index stored at the given
  151. * palette color index.
  152. * @param {number} p - The index (0 - 7) of the palette.
  153. * @param {number} pci - The palette color index (0 - 3).
  154. * @returns {string}
  155. */
  156. get_palette_color(p, pci){
  157. if (typeof(p) != 'number' || typeof(pci) != 'number')
  158. throw new TypeError("Palette and color index expected to be numbers.");
  159. if (p < 0 || p >= 8){
  160. throw new RangeError("Palette index is out of bounds.");
  161. }
  162. if (pci < 0 || pci >= 4){
  163. throw new RangeError("Palette color index is out of bounds.");
  164. }
  165. return NESPalette.SystemColor[this.get_palette_syscolor_index(p, pci)];
  166. }
  167. /**
  168. * Generates a small 6502 assembly block string containing the current palette data.
  169. * @param {string} [memname="PaletteData"] The label named under which to store the data.
  170. * @returns {string}
  171. */
  172. to_asm(memname="PaletteData"){
  173. var NumToHex=function(n){
  174. var h = n.toString(16);
  175. if (h.length %2)
  176. h = '0' + h;
  177. return '$' + h;
  178. };
  179. var BGHex = NumToHex(this.__BGColor);
  180. var s = memname + ":\n\t.db ";
  181. // Storing background palette data.
  182. for (var i=0; i < 12; i++){
  183. if (i % 3 == 0)
  184. s += ((i == 0) ? "" : " ") + BGHex;
  185. s += " " + NumToHex(this.__palette[i]);
  186. }
  187. s += "\t; Background palette data.\n\t.db ";
  188. // Storing foreground palette data.
  189. for (var i=12; i < 24; i++){
  190. if (i % 3 == 0)
  191. s += ((i == 12) ? "" : " ") + BGHex;
  192. s += " " + NumToHex(this.__palette[i]);
  193. }
  194. s += "\t; Foreground palette data.";
  195. return s;
  196. }
  197. }
  198. // NES Palette color information comes from the following site...
  199. // http://www.thealmightyguru.com/Games/Hacking/Wiki/index.php/NES_Palette
  200. /**
  201. * Hex string color values representing the NES system palette.
  202. */
  203. NESPalette.SystemColor = [
  204. "#7C7C7C",
  205. "#0000FC",
  206. "#0000BC",
  207. "#4428BC",
  208. "#940084",
  209. "#A80020",
  210. "#A81000",
  211. "#881400",
  212. "#503000",
  213. "#007800",
  214. "#006800",
  215. "#005800",
  216. "#004058",
  217. "#000000",
  218. "#000000",
  219. "#000000",
  220. "#BCBCBC",
  221. "#0078F8",
  222. "#0058F8",
  223. "#6844FC",
  224. "#D800CC",
  225. "#E40058",
  226. "#F83800",
  227. "#E45C10",
  228. "#AC7C00",
  229. "#00B800",
  230. "#00A800",
  231. "#00A844",
  232. "#008888",
  233. "#000000",
  234. "#000000",
  235. "#000000",
  236. "#F8F8F8",
  237. "#3CBCFC",
  238. "#6888FC",
  239. "#9878F8",
  240. "#F878F8",
  241. "#F85898",
  242. "#F87858",
  243. "#FCA044",
  244. "#F8B800",
  245. "#B8F818",
  246. "#58D854",
  247. "#58F898",
  248. "#00E8D8",
  249. "#787878",
  250. "#000000",
  251. "#000000",
  252. "#FCFCFC",
  253. "#A4E4FC",
  254. "#B8B8F8",
  255. "#D8B8F8",
  256. "#F8B8F8",
  257. "#F8A4C0",
  258. "#F0D0B0",
  259. "#FCE0A8",
  260. "#F8D878",
  261. "#D8F878",
  262. "#B8F8B8",
  263. "#B8F8D8",
  264. "#00FCFC",
  265. "#F8D8F8",
  266. "#000000",
  267. "#000000"
  268. ];
  269. NESPalette.Default = [
  270. "#080808",
  271. "#343434",
  272. "#a2a2a2",
  273. "#efefef",
  274. "#666666" // Out of bounds color.
  275. ];