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个字符

247 行
5.7KB

  1. import ISurface from "/app/js/ifaces/ISurface.js";
  2. import NESPalette from "/app/js/models/NESPalette.js";
  3. class NESTileSurface extends ISurface{
  4. constructor(tile, palette, palindex){
  5. super();
  6. this.__tile = tile;
  7. this.__palette = (palette instanceof NESPalette) ? palette : null;
  8. this.__pi = (palindex >= 0 && palindex < 8) ? palindex : -1;
  9. }
  10. get width(){return 8;}
  11. get height(){return 8;}
  12. get length(){return 64;}
  13. getColor(x, y){
  14. var ci = this.__tile.getPixelIndex(x, y);
  15. if (this.__palette !== null){
  16. return this.__palette.get_palette_color(this.__pi, ci);
  17. }
  18. return NESPalette.Default(ci);
  19. }
  20. getColorIndex(x, y){
  21. return {pi:this.__pi, ci:this.__tile.getPixelIndex(x,y)};
  22. }
  23. }
  24. function clear(ctx, color, cw, ch){
  25. if (typeof(cw) !== 'number'){
  26. cw = (Math.floor(ctx.canvas.clientWidth) > 0) ?
  27. Math.floor(ctx.canvas.clientWidth) :
  28. Math.floor(ctx.canvas.width);
  29. }
  30. if (typeof(ch) !== 'number'){
  31. ch = (Math.floor(ctx.canvas.clientHeight) > 0) ?
  32. Math.floor(ctx.canvas.clientHeight) :
  33. Math.floor(ctx.canvas.height);
  34. }
  35. ctx.save();
  36. ctx.fillStyle = color;
  37. ctx.fillRect(0, 0, cw, ch);
  38. ctx.restore();
  39. }
  40. function render(surf, sx, sy, sw, sh, scale, ctx, dx, dy, palcolored){
  41. if (!(surf instanceof ISurface)){
  42. console.log("WARNING: Cannot render non-ISurface object.");
  43. return;
  44. }
  45. if (sx + sw > surf.width || sy + sh > surf.height){
  46. console.log("WARNING: Cannot render. Region is out of bounds.");
  47. return;
  48. }
  49. palcolored = (palcolored === true);
  50. var cw = (Math.floor(ctx.canvas.clientWidth) > 0) ?
  51. Math.floor(ctx.canvas.clientWidth) :
  52. Math.floor(ctx.canvas.width);
  53. var ch = (Math.floor(ctx.canvas.clientHeight) > 0) ?
  54. Math.floor(ctx.canvas.clientHeight) :
  55. Math.floor(ctx.canvas.height);
  56. if (cw <= 0 || ch <= 0){return;}
  57. clear(ctx, NESPalette.Default(4));
  58. ctx.save();
  59. var ctximg = ctx.getImageData(0, 0, cw, ch);
  60. var idat = ctximg.data;
  61. var PutPixel = (i,j,s,c) => {
  62. i = Math.round(i);
  63. j = Math.round(j);
  64. s = Math.ceil(s);
  65. var r = parseInt(c.substring(1, 3), 16);
  66. var g = parseInt(c.substring(3, 5), 16);
  67. var b = parseInt(c.substring(5, 7), 16);
  68. for (var y=j; y < j+s; y++){
  69. for (var x=i; x < i+s; x++){
  70. if (x >= 0 && x < cw && y >= 0 && y < ch){
  71. var index = (y*cw*4) + (x*4);
  72. idat[index] = r;
  73. idat[index+1] = g;
  74. idat[index+2] = b;
  75. idat[index+3] = 255;
  76. }
  77. }
  78. }
  79. };
  80. for (let j=sy; j < sy + sh; j++){
  81. var y = (j*scale) + dy;
  82. for (let i=sx; i < sx + sw; i++){
  83. var x = (i*scale) + dx;
  84. if (x >= 0 && x < cw && y >= 0 && y < ch){
  85. var color = NESPalette.Default(4);
  86. if (palcolored){
  87. color = surf.getColor(i, j);
  88. } else {
  89. var pinfo = surf.getColorIndex(i, j);
  90. color = (pinfo.ci >= 0) ? NESPalette.Default(pinfo.ci) : NESPalette.Default(4);
  91. }
  92. PutPixel(x,y,scale,color);
  93. }
  94. }
  95. }
  96. ctx.putImageData(ctximg, 0, 0);
  97. ctx.restore();
  98. }
  99. function renderToFit(surf, ctx, palcolored){
  100. if (!(surf instanceof ISurface)){
  101. console.log("WARNING: Cannot render non-ISurface object.");
  102. return;
  103. }
  104. palcolored = (palcolored === true);
  105. var cw = (Math.floor(ctx.canvas.clientWidth) > 0) ?
  106. Math.floor(ctx.canvas.clientWidth) :
  107. Math.floor(ctx.canvas.width);
  108. var ch = (Math.floor(ctx.canvas.clientHeight) > 0) ?
  109. Math.floor(ctx.canvas.clientHeight) :
  110. Math.floor(ctx.canvas.height);
  111. if (cw <= 0 || ch <= 0){return;}
  112. var scale = Math.min(
  113. cw/surf.width,
  114. ch/surf.height
  115. );
  116. var offX = Math.floor((cw - (surf.width*scale)) * 0.5);
  117. var offY = Math.floor((ch - (surf.height*scale)) * 0.5);
  118. render(surf, 0, 0, surf.width, surf.height, scale, ctx, offX, offY, palcolored);
  119. }
  120. function renderGridNxN(ctx, gsize, sw, sh, scale, offset, color){
  121. // Draw grid.
  122. if (scale > 0.5){
  123. ctx.save();
  124. ctx.strokeStyle = color;
  125. var w = sw * scale;
  126. var h = sh * scale;
  127. var length = Math.max(sw, sh);
  128. for (var i=0; i < length; i += gsize){
  129. var x = (i*scale) + offset[0];
  130. var y = (i*scale) + offset[1];
  131. if (i < sw){
  132. ctx.beginPath();
  133. ctx.moveTo(x, offset[1]);
  134. ctx.lineTo(x, offset[1] + h);
  135. ctx.stroke();
  136. ctx.closePath();
  137. }
  138. if (i < sh){
  139. ctx.beginPath();
  140. ctx.moveTo(offset[0], y);
  141. ctx.lineTo(offset[0] + w, y);
  142. ctx.stroke();
  143. ctx.closePath();
  144. }
  145. }
  146. ctx.restore();
  147. }
  148. }
  149. function renderGridMxN(ctx, gw, gh, sw, sh, scale, offset, color){
  150. if (scale > 0.5){
  151. ctx.save();
  152. ctx.strokeStyle = color;
  153. var w = sw * scale;
  154. var h = sh * scale;
  155. var length = Math.max(sw, sh);
  156. for (let i=0; i < sw; i += gw){
  157. var x = (i*scale) + offset[0];
  158. //var y = (i*scale) + offset[1];
  159. if (i < sw){
  160. ctx.beginPath();
  161. ctx.moveTo(x, offset[1]);
  162. ctx.lineTo(x, offset[1] + h);
  163. ctx.stroke();
  164. ctx.closePath();
  165. }
  166. }
  167. for (let i=0; i < sh; i += gh){
  168. var y = (i * scale) + offset[1];
  169. if (i < sh){
  170. ctx.beginPath();
  171. ctx.moveTo(offset[0], y);
  172. ctx.lineTo(offset[0] + w, y);
  173. ctx.stroke();
  174. ctx.closePath();
  175. }
  176. }
  177. ctx.restore();
  178. }
  179. }
  180. function renderGridMajorMinor(ctx, mjsize, mnsize, sw, sh, scale, offset, mjcolor, mncolor){
  181. if (scale > 0.5){
  182. renderGridNxN(ctx, mnsize, sw, sh, scale, offset, mncolor);
  183. renderGridNxN(ctx, mjsize, sw, sh, scale, offset, mjcolor);
  184. }
  185. }
  186. export default {
  187. clear: clear,
  188. render: render,
  189. renderToFit: renderToFit,
  190. renderGridNxN: renderGridNxN,
  191. renderGridMxN: renderGridMxN,
  192. renderGridMajorMinor: renderGridMajorMinor,
  193. NESTileSurface: NESTileSurface
  194. };