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

233 行
6.0KB

  1. import Utils from "/app/js/common/Utils.js";
  2. import ISurface from "/app/js/ifaces/ISurface.js";
  3. import NESTile from "/app/js/models/NESTile.js";
  4. import NESPalette from "/app/js/models/NESPalette.js";
  5. function LRIdx2TileIdxCo(index){
  6. var res = {
  7. lid: 0,
  8. index: 0,
  9. x: 0,
  10. y: 0
  11. };
  12. var x = Math.floor(index % 256);
  13. var y = Math.floor(index / 256);
  14. if (x < 128){
  15. res.index = (Math.floor(y/8) * 16) + Math.floor(x / 8);
  16. } else {
  17. res.index = (Math.floor(y/8) * 16) + Math.floor((x - 128) / 8);
  18. res.lid = 1;
  19. }
  20. res.x = x % 8;
  21. res.y = y % 8;
  22. return res;
  23. }
  24. export default class NESBank extends ISurface{
  25. constructor(){
  26. super();
  27. this.__LP = []; // Left Patterns (Sprites)
  28. this.__RP = []; // Right Patterns (Backgrounds)
  29. for (var i=0; i < 256; i++){
  30. this.__LP.push(new NESTile());
  31. this.__RP.push(new NESTile());
  32. }
  33. this.__palette = null;
  34. }
  35. get json(){
  36. JSON.stringify({
  37. LP: this.__LP.map(x=>x.base64),
  38. RP: this.__RP.map(x=>x.base64)
  39. });
  40. }
  41. get chr(){
  42. var buff = new Uint8Array(8192);
  43. var offset = 0;
  44. this.__LP.forEach(function(i){
  45. buff.set(i.chr, offset);
  46. offset += 16;
  47. });
  48. this.__RP.forEach(function(i){
  49. buff.set(i.chr, offset);
  50. offset += 16;
  51. });
  52. return buff;
  53. }
  54. get palette(){return this.__palette;}
  55. set palette(p){
  56. if (p !== null && !(p instanceof NESPalette))
  57. throw new TypeError("Expected null or NESPalette object.");
  58. if (p !== this.__palette){
  59. this.__palette = p;
  60. }
  61. }
  62. get width(){return 256;}
  63. get height(){return 128;}
  64. get length(){return this.width * this.height;}
  65. get coloridx(){
  66. return new Proxy(this, {
  67. get:function(obj, prop){
  68. var len = (obj.__LP.length * 8) + (obj.__RP.length * 8);
  69. if (prop === "length")
  70. return len;
  71. if (!Utils.isInt(prop))
  72. throw new TypeError("Expected integer index.");
  73. prop = parseInt(prop);
  74. if (prop < 0 || prop >= len)
  75. return NESPalette.Default[4];
  76. var res = LRIdx2TileIdxCo(prop);
  77. var list = (res.lid === 0) ? obj.__LP : obj.__RP;
  78. return list[res.index].getPixelIndex(res.x, res.y);
  79. },
  80. set:function(obj, prop, value){
  81. if (!Utils.isInt(prop))
  82. throw new TypeError("Expected integer index.");
  83. if (!Utils.isInt(value))
  84. throw new TypeError("Color expected to be integer.");
  85. prop = parseInt(prop);
  86. value = parseInt(value);
  87. if (prop < 0 || prop >= len)
  88. throw new RangeError("Index out of bounds.");
  89. if (value < 0 || value >= 4)
  90. throw new RangeError("Color index out of bounds.");
  91. var res = LRIdx2TileIdxCo(prop);
  92. var list = (res.lid === 0) ? obj.__LP : obj.__RP;
  93. list[res.index].setPixelIndex(res.x, res.y, value);
  94. return true;
  95. }
  96. });
  97. }
  98. get lp(){
  99. return new Proxy(this, {
  100. get: function(obj, prop){
  101. if (prop === "length")
  102. return obj.__LP.length;
  103. if (!Utils.isInt(prop))
  104. throw new TypeError("Expected integer index.");
  105. prop = parseInt(prop);
  106. if (prop < 0 || prop >= 256)
  107. throw new RangeError("Index out of bounds.");
  108. return obj.__LP[prop];
  109. },
  110. set: function(obj, prop, value){
  111. if (!Utils.isInt(prop))
  112. throw new TypeError("Expected integer index.");
  113. if (!(value instanceof NESTile))
  114. throw new TypeError("Can only assign NESTile objects.");
  115. prop = parseInt(prop);
  116. if (prop < 0 || prop >= 256)
  117. throw new RangeError("Index out of bounds.");
  118. obj.__LP[prop].copy(value);
  119. return true;
  120. }
  121. });
  122. }
  123. get rp(){
  124. return new Proxy(this, {
  125. get: function(obj, prop){
  126. if (prop === "length")
  127. return obj.__RP.length;
  128. if (!Utils.isInt(prop))
  129. throw new TypeError("Expected integer index.");
  130. prop = parseInt(prop);
  131. if (prop < 0 || prop >= 256)
  132. throw new RangeError("Index out of bounds.");
  133. return obj.__RP[prop];
  134. },
  135. set: function(obj, prop, value){
  136. if (!Utils.isInt(prop))
  137. throw new TypeError("Expected integer index.");
  138. if (!(value instanceof NESTile))
  139. throw new TypeError("Can only assign NESTile objects.");
  140. prop = parseInt(prop);
  141. if (prop < 0 || prop >= 256)
  142. throw new RangeError("Index out of bounds.");
  143. obj.__RP[prop].copy(value);
  144. return true;
  145. }
  146. });
  147. }
  148. copy(b){
  149. if (!(b instanceof NESBank))
  150. throw new TypeError("Expected NESBank object.");
  151. for (var i=0; i < 256; i++){
  152. this.lp[i] = b.lp[i];
  153. this.rp[i] = b.rp[i];
  154. }
  155. return this;
  156. }
  157. clone(){
  158. return (new NESBank()).copy(this);
  159. }
  160. getColor(x,y){
  161. if (x < 0 || x >= 256 || y < 0 || y >= 128)
  162. return this.__default_pi[4];
  163. var res = LRIdx2TileIdxCo((y*256)+x);
  164. var list = (res.lid === 0) ? this.__LP : this.__RP;
  165. var pi = list[res.index].paletteIndex;
  166. var ci = list[res.index].getPixelIndex(res.x, res.y);
  167. if (this.__palette !== null){
  168. return this.__palette.get_palette_color(pi, ci);
  169. }
  170. return NESPalette.Default[ci];
  171. }
  172. getColorIndex(x, y){
  173. if (x < 0 || x >= 256 || y < 0 || y >= 128)
  174. return {pi: -1, ci:-1};
  175. var res = LRIdx2TileIdxCo((y*256)+x);
  176. var list = (res.lid === 0) ? this.__LP : this.__RP;
  177. return {
  178. pi: list[res.index].paletteIndex,
  179. ci: list[res.index].getPixelIndex(res.x, res.y)
  180. };
  181. }
  182. setColorIndex(x, y, ci, pi){
  183. if (x < 0 || x >= 256 || y < 0 || y > 128)
  184. throw new RangeError("Coordinates out of bounds.");
  185. if (!Utils.isInt(pi))
  186. pi = -1;
  187. if (!Utils.isInt(ci))
  188. ci = 0;
  189. if (pi < 0){
  190. this.coloridx[(y*256)+x] = ci;
  191. } else {
  192. var res = LRIdx2TileIdxCo((y*256)+x);
  193. var list = (res.lid === 0) ? this.__LP : this.__RP;
  194. list[res.index].paletteIndex = pi;
  195. list[res.index].setPixelIndex(res.x, res.y, ci);
  196. }
  197. return this;
  198. }
  199. }