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

200 行
4.8KB

  1. import NESPalette from "/app/js/models/NESPalette.js";
  2. function BitMask(offset){
  3. switch(offset){
  4. case 0:
  5. return 63; // Mask '00111111'
  6. case 1:
  7. return 207; // Mask '11001111'
  8. case 2:
  9. return 243; // Mask '11110011'
  10. }
  11. return 252; // Mask '11111100'
  12. }
  13. function SetDataArrayColor(arr, x, y, ci){
  14. var index = (y*8)+x;
  15. var dindex = Math.floor(index*0.25);
  16. var bitoffset = (index % 4);
  17. arr[dindex] = (arr[dindex] & BitMask(bitoffset)) ^ (ci << ((3 - bitoffset)*2));
  18. //if (dindex === 1){
  19. // console.log("index: ", dindex, " | value: ", arr[dindex], " | (x,y): (", x, ",", y, ") | Bit Offset: ", bitoffset, "Color: ", ci);
  20. //}
  21. }
  22. function GetDataArrayColor(arr, x, y){
  23. var index = (y*8)+x;
  24. var dindex = Math.floor(index*0.25);
  25. var bitoffset = 6 - ((index % 4) * 2);
  26. return (arr[dindex] & (3 << bitoffset)) >> bitoffset;
  27. }
  28. export default class NESTile{
  29. constructor(){
  30. this.__palette = null;
  31. this.__paletteIndex = 0;
  32. this.__data = new Uint8Array(16);
  33. }
  34. get pixels(){
  35. return new Proxy(this, {
  36. get: function(obj, prop){
  37. if (!Number.isInt(prop))
  38. throw new TypeError("Expected integer index.");
  39. if (prop < 0 || prop >= 64)
  40. throw new RangeError("Index out of bounds.");
  41. var dindex = Math.floor(prop*0.25);
  42. var bitoffset = 6 - ((prop % 4) * 2);
  43. return (obj.__data[dindex] & (3 << bitoffset)) >> bitoffset;
  44. },
  45. set: function(obj, prop, value){
  46. if (!Number.isInt(prop))
  47. throw new TypeError("Expected integer index.");
  48. if (!Number.isInt(value))
  49. throw new TypeError("Color index expected to be integer.");
  50. if (prop < 0 || prop >= 64)
  51. throw new RangeError("Index out of bounds.");
  52. if (value < 0 || value >= 4)
  53. throw new RangeError("Color index out of bounds.");
  54. var dindex = Math.floor(index*0.25);
  55. var bitoffset = (index % 4);
  56. obj.__data[dindex] = (obj.__data[dindex] & BitMask(bitoffset)) ^ (ci << ((3 - bitoffset)*2));
  57. }
  58. });
  59. }
  60. get dataArray(){
  61. var d = [];
  62. for (var x = 0; x < 8; x++){
  63. for (var y = 0; y < 8; y++){
  64. d.push(this.getPixelIndex(x, y));
  65. }
  66. }
  67. return d;
  68. }
  69. get base64(){
  70. var b = ""
  71. for (var i = 0; i < this.__data.length; i++) {
  72. b += String.fromCharCode(this.__data[i]);
  73. }
  74. return window.btoa(b);
  75. }
  76. set base64(s){
  77. var b = window.atob(s);
  78. var len = b.length;
  79. if (b.length !== 16){
  80. throw new Error("Base64 string contains invalid byte count.");
  81. }
  82. var bytes = new Uint8Array(b.length);
  83. for (var i=0; i < b.length; i++){
  84. bytes[i] = b.charCodeAt(i);
  85. }
  86. this.__data = bytes;
  87. }
  88. get palette(){return this.__palette;}
  89. set palette(p){
  90. if (p !== null && !(p instanceof NESPalette)){
  91. throw new TypeError("Expected NESPalette instance or null.");
  92. }
  93. this.__palette = p;
  94. }
  95. setPixelIndex(x, y, ci){
  96. if (x < 0 || x >= 8 || y < 0 || y >= 8){
  97. throw new ValueError("Coordinates out of bounds.");
  98. }
  99. if (ci < 0 || ci >= 4){
  100. throw new ValueError("Color index out of bounds.");
  101. }
  102. SetDataArrayColor(this.__data, x, y, ci);
  103. return this;
  104. }
  105. getPixelIndex(x, y){
  106. if (x < 0 || x >= 8 || y < 0 || y >= 8){
  107. throw new ValueError("Coordinates out of bounds.");
  108. }
  109. return GetDataArrayColor(this.__data, x, y);
  110. }
  111. getPixel(x, y){
  112. var ci = 0;
  113. try {
  114. ci = this.getPixelIndex(x, y);
  115. } catch (e) {
  116. throw e;
  117. }
  118. if (this.__palette !== null){
  119. return this.__palette.get_palette_color(this.__paletteIndex, ci);
  120. }
  121. switch(ci){
  122. case 1:
  123. return "#555555";
  124. case 2:
  125. return "#AAAAAA";
  126. case 3:
  127. return "#FFFFFF";
  128. }
  129. return 0;
  130. }
  131. flip(flag){
  132. if (flag >= 1 && flag <= 3){
  133. var newData = new Uint8Array(16);
  134. for (var x = 0; x < 8; x++){
  135. for (var y = 0; y < 8; y++){
  136. var ci = GetDataArrayColor(this.__data, x, y);
  137. SetDataArrayColor(
  138. newData,
  139. (flag == 1 || flag == 3) ? 7 - x: x,
  140. (flag == 2 || flag == 3) ? 7 - y: y,
  141. ci
  142. );
  143. //console.log(newData);
  144. //newData[r[0]] = 2;
  145. //newData[r[0]] = r[1];
  146. }
  147. }
  148. //console.log(newData);
  149. this.__data = newData;
  150. }
  151. return this;
  152. }
  153. clone(){
  154. var t = new NESTile();
  155. t.base64 = this.base64;
  156. return t;
  157. }
  158. isEq(tile){
  159. if (!(tile instanceof NESTile)){
  160. throw new TypeError("Expected NESTile instance.");
  161. }
  162. var b64 = this.base64;
  163. if (tile.base64 === b64){
  164. return 0;
  165. }
  166. if (tile.clone().flip(1).base64 === b64){
  167. return 1;
  168. }
  169. if (tile.clone().flip(2).base64 === b64){
  170. return 2;
  171. }
  172. if (tile.clone().flip(3).base64 === b64){
  173. return 3;
  174. }
  175. return -1;
  176. }
  177. }