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

202 行
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.__palette = null;
  32. this.__paletteIndex = 0;
  33. this.__data = new Uint8Array(16);
  34. }
  35. get pixels(){
  36. return new Proxy(this, {
  37. get: function(obj, prop){
  38. console.log(prop);
  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 palette(){return this.__palette;}
  91. set palette(p){
  92. if (p !== null && !(p instanceof NESPalette)){
  93. throw new TypeError("Expected NESPalette instance or null.");
  94. }
  95. this.__palette = p;
  96. }
  97. setPixelIndex(x, y, ci){
  98. if (x < 0 || x >= 8 || y < 0 || y >= 8){
  99. throw new ValueError("Coordinates out of bounds.");
  100. }
  101. if (ci < 0 || ci >= 4){
  102. throw new ValueError("Color index out of bounds.");
  103. }
  104. SetDataArrayColor(this.__data, x, y, ci);
  105. return this;
  106. }
  107. getPixelIndex(x, y){
  108. if (x < 0 || x >= 8 || y < 0 || y >= 8){
  109. throw new ValueError("Coordinates out of bounds.");
  110. }
  111. return GetDataArrayColor(this.__data, x, y);
  112. }
  113. getPixel(x, y){
  114. var ci = 0;
  115. try {
  116. ci = this.getPixelIndex(x, y);
  117. } catch (e) {
  118. throw e;
  119. }
  120. if (this.__palette !== null){
  121. return this.__palette.get_palette_color(this.__paletteIndex, ci);
  122. }
  123. switch(ci){
  124. case 1:
  125. return "#555555";
  126. case 2:
  127. return "#AAAAAA";
  128. case 3:
  129. return "#FFFFFF";
  130. }
  131. return 0;
  132. }
  133. flip(flag){
  134. if (flag >= 1 && flag <= 3){
  135. var newData = new Uint8Array(16);
  136. for (var x = 0; x < 8; x++){
  137. for (var y = 0; y < 8; y++){
  138. var ci = GetDataArrayColor(this.__data, x, y);
  139. SetDataArrayColor(
  140. newData,
  141. (flag == 1 || flag == 3) ? 7 - x: x,
  142. (flag == 2 || flag == 3) ? 7 - y: y,
  143. ci
  144. );
  145. //console.log(newData);
  146. //newData[r[0]] = 2;
  147. //newData[r[0]] = r[1];
  148. }
  149. }
  150. //console.log(newData);
  151. this.__data = newData;
  152. }
  153. return this;
  154. }
  155. clone(){
  156. var t = new NESTile();
  157. t.base64 = this.base64;
  158. return t;
  159. }
  160. isEq(tile){
  161. if (!(tile instanceof NESTile)){
  162. throw new TypeError("Expected NESTile instance.");
  163. }
  164. var b64 = this.base64;
  165. if (tile.base64 === b64){
  166. return 0;
  167. }
  168. if (tile.clone().flip(1).base64 === b64){
  169. return 1;
  170. }
  171. if (tile.clone().flip(2).base64 === b64){
  172. return 2;
  173. }
  174. if (tile.clone().flip(3).base64 === b64){
  175. return 3;
  176. }
  177. return -1;
  178. }
  179. }