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

191 行
4.9KB

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