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

189 行
4.8KB

  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. return window.btoa(b);
  77. }
  78. set base64(s){
  79. var b = window.atob(s);
  80. var len = b.length;
  81. if (b.length !== 16){
  82. throw new Error("Base64 string contains invalid byte count.");
  83. }
  84. var bytes = new Uint8Array(b.length);
  85. for (var i=0; i < b.length; i++){
  86. bytes[i] = b.charCodeAt(i);
  87. }
  88. this.__data = bytes;
  89. }
  90. get paletteIndex(){return this.__paletteIndex;}
  91. set paletteIndex(pi){
  92. if (!Utils.isInt(pi))
  93. throw new TypeError("Palette index expected to be an integer.");
  94. if (pi < 0 || pi >= 4){
  95. throw new RangeError("Palette index out of bounds.");
  96. }
  97. this.__paletteIndex = pi;
  98. }
  99. setPixelIndex(x, y, ci){
  100. if (x < 0 || x >= 8 || y < 0 || y >= 8){
  101. throw new ValueError("Coordinates out of bounds.");
  102. }
  103. if (ci < 0 || ci >= 4){
  104. throw new ValueError("Color index out of bounds.");
  105. }
  106. SetDataArrayColor(this.__data, x, y, ci);
  107. return this;
  108. }
  109. getPixelIndex(x, y){
  110. if (x < 0 || x >= 8 || y < 0 || y >= 8){
  111. throw new ValueError("Coordinates out of bounds.");
  112. }
  113. return GetDataArrayColor(this.__data, x, y);
  114. }
  115. flip(flag){
  116. if (flag >= 1 && flag <= 3){
  117. var newData = new Uint8Array(16);
  118. for (var x = 0; x < 8; x++){
  119. for (var y = 0; y < 8; y++){
  120. var ci = GetDataArrayColor(this.__data, x, y);
  121. SetDataArrayColor(
  122. newData,
  123. (flag == 1 || flag == 3) ? 7 - x: x,
  124. (flag == 2 || flag == 3) ? 7 - y: y,
  125. ci
  126. );
  127. //console.log(newData);
  128. //newData[r[0]] = 2;
  129. //newData[r[0]] = r[1];
  130. }
  131. }
  132. //console.log(newData);
  133. this.__data = newData;
  134. }
  135. return this;
  136. }
  137. clone(){
  138. var t = new NESTile();
  139. t.base64 = this.base64;
  140. return t;
  141. }
  142. isEq(tile){
  143. if (!(tile instanceof NESTile)){
  144. throw new TypeError("Expected NESTile instance.");
  145. }
  146. var b64 = this.base64;
  147. if (tile.base64 === b64){
  148. return 0;
  149. }
  150. var tc = tile.clone().flip(1); // Flip horizontal.
  151. if (tc.base64 === b64){
  152. return 1;
  153. }
  154. tc.flip(3); // Flip horizontal AND verticle. Net effect is the same as tile.clone().flip(2) ... Flip Verticle
  155. if (tc.base64 === b64){
  156. return 2;
  157. }
  158. tc.flip(1); // Flip horizontal again. Net effect is the same as tile.clone().flip(3) ... flip H & V
  159. if (tc.base64 === b64){
  160. return 3;
  161. }
  162. return -1;
  163. }
  164. }