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个字符

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