キケッツ掲示板のリポジトリです. https://bbs.kekec.wiki
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

paint.js 25 KiB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047
  1. const colourCode = {'#000000': '黒',
  2. '#0000ff': 'ブルー',
  3. '#00ff00': 'ライム',
  4. '#00ffff': '青',
  5. '#ff0000': '赤',
  6. '#ff00ff': 'マジェンタ',
  7. '#ffff00': 'イェロゥ',
  8. '#ffffff': '白',
  9. '#f1f1f1': '白',
  10. '#a0a0a0': '銀ねず',
  11. '#868686': 'ねずみ色',
  12. '#797979': '灰色',
  13. '#313131': '墨',
  14. '#ed542a': '金赤'};
  15. const canvasArea = document.getElementById ('canvas-area');
  16. const canvasRoot = document.getElementById ('canvas');
  17. const canvasRootCtx = canvasRoot.getContext ('2d');
  18. const canvas = []; // 各レイァごとの Canvas 要素
  19. const canvasCtx = []; // 各レイァごとの Canvas コンテクスト
  20. let layerMax = 3; // レイァの最大値
  21. let layerName = ['下', '中', '上']; // レイァ名
  22. let canvasWidth = canvasRoot.width; // Canvas 幅
  23. let canvasHeight = canvasRoot.height; // Canvas 高さ
  24. // canvas[0] = document.getElementById ('canvas');
  25. // canvas[1] = document.getElementById ('canvas1');
  26. // canvas[2] = document.getElementById ('canvas2');
  27. for (let i = 0; i < layerMax; ++i)
  28. {
  29. // 各レイァにたぃし,要素代入
  30. canvas[i] = document.createElement ('canvas');
  31. canvas[i].width = canvasWidth;
  32. canvas[i].height = canvasHeight;
  33. // 各レイァにたぃし,コンテクスト代入
  34. canvasCtx[i] = canvas[i].getContext ('2d');
  35. canvasCtx[i].willReadFrequently = true;
  36. }
  37. const canvasPerfect = document.getElementById ('canvas-perfect');
  38. // レイァ合成用作業 Canvas
  39. const userName = document.getElementById ('user-name'); // 投稿者名
  40. const password = document.getElementById ('password'); // 削除用パスワード
  41. const message = document.getElementById ('message'); // 投稿メシジ
  42. const buttonNew = document.getElementById ('new');
  43. // 新規作成ボタン
  44. const buttonSend = document.getElementById ('send');
  45. // 送信ボタン
  46. const buttonSave = document.getElementById ('save');
  47. // ダウンロード・ボタン
  48. const buttonChangeSize = document.getElementById ('change-size');
  49. // サイズ変更
  50. const buttonUndo = document.getElementById ('undo');
  51. // 元に戻す
  52. const buttonRedo = document.getElementById ('redo');
  53. // やり直し
  54. // const formColour = document.getElementById ('colour'); // 色フォーム
  55. const formSize = document.getElementById ('size'); // 太さフォーム
  56. // const radioColour = formColour.colour; // 選択中の色
  57. let colour = 'black';
  58. const radioSize = formSize.size; // 選択中の太さ
  59. const pen = document.getElementById ('pen'); // ペン
  60. const rubber = document.getElementById ('rubber'); // 消ゴム
  61. const bucket = document.getElementById ('bucket'); // 塗りつぶし
  62. const layer = document.getElementById ('layer').layer; // レイァ
  63. const layerAdd = document.getElementById ('layer').add; // レイァ追加
  64. const layerDel = document.getElementById ('layer').del; // レイァ削除
  65. const layerUp = document.getElementById ('layer').up; // レイァ上へ
  66. const layerDown = document.getElementById ('layer').down; // レイァ下へ
  67. const layerSep = document.getElementById ('layer').sep; // レイァ分け
  68. const delId = document.getElementById ('del-id');
  69. const delPass = document.getElementById ('del-pass');
  70. const deleteButton = document.getElementById ('delete');
  71. const changeSizeWindow = document.getElementById ('modal');
  72. const changeWidth = document.getElementById ('change-width');
  73. const changeHeight = document.getElementById ('change-height');
  74. let imageUrl = (getCookie ('backup') == null) ? '' : getCookie ('backup');
  75. let uniqId = (imageUrl == '') ? uniqueId () : imageUrl.split ('.')[0];
  76. if (imageUrl != '')
  77. {
  78. let imgData = new Image ();
  79. imgData.addEventListener ('load',
  80. function ()
  81. {
  82. canvasWidth = canvasRoot.width = canvasPerfect.width = imgData.width;
  83. canvasHeight = canvasRoot.height = canvasPerfect.height = imgData.height;
  84. canvasArea.style.width = canvasWidth + 'px';
  85. canvasArea.style.height = canvasHeight + 'px';
  86. for (let i = 0; i < layerMax; ++i)
  87. {
  88. canvas[i].width = canvasWidth;
  89. canvas[i].height = canvasHeight;
  90. }
  91. canvasCtx[0].drawImage (imgData, 0, 0, canvasWidth, canvasHeight);
  92. reDraw ();
  93. }, false);
  94. imgData.src = `//miteruzo.ml/kekec/bbs/draft/${imageUrl}`;
  95. }
  96. let nowDraw = false; // 描画フラグ
  97. let lastX; // 直前の X 座標
  98. let lastY; // 直前の Y 座標
  99. let zoomX; // 定義上の Canvas 幅と実際のそれとの比率
  100. let zoomY; // 定義上の Canvas 高さと実際のそれとの比率
  101. let scaleFactor = 1;
  102. let startDistance = 0;
  103. let lastDistance = null;
  104. // undo/redo のための変数
  105. let history = []; // フレィムごとの履歴
  106. let count = 0; // フレィムごとの履歴の数
  107. let historySt = []; // 一筆ごとの履歴
  108. let countSt = 0; // 一筆ごとの履歴の数
  109. let countMax = 0; // 履歴の最大値(やり直し用)
  110. let img = new Image (); // 取込用画像オブジェクト
  111. let held = false;
  112. /*
  113. * サーヴァに送信する.
  114. * url:フェッチ先,param:JSON パラメタ
  115. *
  116. * 戻り値は,なし.
  117. */
  118. async function
  119. SendServer (url, param)
  120. {
  121. fetch (url, param).then (
  122. (response) => response.json ()).then (function
  123. (json)
  124. {
  125. if (json.status)
  126. return 'success';
  127. else
  128. return 'miss';
  129. }).catch (
  130. (error) => 'error');
  131. }
  132. /*
  133. * Canvas から画像を取得する.
  134. * id:要素オブジェクト
  135. *
  136. * 戻り値は,Promise オブジェクト.
  137. */
  138. function
  139. getImageFromCanvas (id)
  140. {
  141. return new Promise (
  142. function (resolve, reject)
  143. {
  144. const w2 = new Image (); // 作業用画像オブジェクト
  145. w2.onload = () => resolve (w2);
  146. w2.onerror = (e) => reject (e);
  147. w2.src = id.toDataURL ('image/png');
  148. });
  149. }
  150. /*
  151. * Canvas を合成する.
  152. *
  153. * 戻り値は,なし.
  154. */
  155. async function
  156. concatCanvas ()
  157. {
  158. canvasPerfect.getContext ('2d').fillStyle = 'white';
  159. canvasPerfect.getContext ('2d').fillRect (0, 0, canvasWidth, canvasHeight);
  160. // Canvas 合成
  161. for (let i = 0; i < layerMax; ++i)
  162. {
  163. const w = await getImageFromCanvas (canvas[i]);
  164. canvasPerfect.getContext ('2d').drawImage (w, 0, 0, canvasWidth, canvasHeight);
  165. }
  166. }
  167. /*
  168. * サーヴァに Canvas 画像を送信する.
  169. * backup:ドラフト・フラグ
  170. *
  171. * 戻り値は,なし.
  172. */
  173. async function
  174. SendToServer (backup)
  175. {
  176. await concatCanvas ();
  177. let image = canvasPerfect.toDataURL ('image/png');
  178. let param = {method: 'POST',
  179. headers: {'Content-Type': 'application/json; charset=utf-8'},
  180. body: JSON.stringify ({data: image}),
  181. mode: 'cors'};
  182. if (backup)
  183. await SendServer (`/modules/backup.php?id=${uniqId}`, param);
  184. else
  185. {
  186. // SendServer (`upload.php?name=${userName.value}&pass=${password.value}&msg=${message.value}`, param);
  187. await SendServer (`/modules/upload.php${window.location.search}&name=${userName.value}&pass=${password.value}&held=${held ? 1 : 0}&uniqid=${uniqId}`, param);
  188. }
  189. }
  190. /*
  191. * 定義上の Canvas サイズと実際のそれとの比率を取得する.
  192. *
  193. * 戻り値は,なし.
  194. */
  195. function
  196. GetZoom ()
  197. {
  198. zoomX = canvasWidth / canvasRootCtx.canvas.clientWidth;
  199. zoomY = canvasHeight / canvasRootCtx.canvas.clientHeight;
  200. }
  201. /*
  202. * ローカル画像 files を取込む.
  203. *
  204. * 戻り値は,なし.
  205. */
  206. function
  207. LoadFile (files)
  208. {
  209. let reader = new FileReader (); // ファイル読込ダイアログ
  210. // ファイルが読込まれた場合
  211. reader.onload = function
  212. (evt)
  213. {
  214. // 画像が読込まれた場合
  215. img.onload = function
  216. ()
  217. {
  218. canvasCtx[layer.value].drawImage (img, 0, 0, canvasWidth, canvasHeight);
  219. held = true;
  220. };
  221. img.src = evt.target.result;
  222. // 履歴を更新
  223. historySt[countSt] = count;
  224. ++countSt;
  225. countMax = countSt;
  226. history[count] = {type: 'load', layer: layer.value};
  227. ++count;
  228. };
  229. reader.readAsDataURL (files[0]); // 選択された最初のファイルを指定
  230. }
  231. GetZoom ();
  232. NewCanvas ();
  233. /*
  234. * ペンの座標および履歴を記録し,ペン移動中の処理に移る.
  235. * クリックまたはタップ開始と同時に実行する.
  236. *
  237. * 戻り値は,なし.
  238. */
  239. function
  240. Down (evt)
  241. {
  242. let mousePos = getMousePosition (canvasRoot, evt); // マウス座標
  243. if (bucket.checked)
  244. {
  245. bucketArea (parseInt (mousePos.x, 10), parseInt (mousePos.y, 10), colour,
  246. layer.value, false);
  247. }
  248. else
  249. {
  250. // 座標を記録
  251. lastX = mousePos.x + 1;
  252. lastY = mousePos.y;
  253. nowDraw = true;
  254. // 履歴を更新
  255. historySt[countSt] = count;
  256. ++countSt;
  257. countMax = countSt;
  258. Drawing (evt);
  259. }
  260. }
  261. /*
  262. * ペン移動中の処理を行ふ.
  263. *
  264. * 戻り値は,なし.
  265. */
  266. function
  267. Move (evt) /* 筆移動なうみ */
  268. {
  269. Drawing (evt);
  270. }
  271. async function
  272. Up () /* 筆離したお (^∇^)/ */
  273. {
  274. nowDraw = false;
  275. await SendToServer (true);
  276. historySt[countSt] = count;
  277. }
  278. canvasRoot.addEventListener ('mousedown',
  279. function (evt)
  280. {
  281. Down (evt);
  282. });
  283. canvasRoot.addEventListener ('mousemove',
  284. function (evt)
  285. {
  286. Move (evt);
  287. });
  288. canvasRoot.addEventListener ('mouseup',
  289. function ()
  290. {
  291. Up ();
  292. });
  293. canvasRoot.addEventListener ('touchstart',
  294. function (evt)
  295. {
  296. Down (evt.changedTouches[0]);
  297. });
  298. canvasRoot.addEventListener ('touchmove',
  299. function (evt)
  300. {
  301. evt.preventDefault ();
  302. Move (evt.changedTouches[0]);
  303. });
  304. canvasRoot.addEventListener ('touchend',
  305. function ()
  306. {
  307. Up ();
  308. });
  309. canvasRoot.addEventListener ('touchcancel',
  310. function ()
  311. {
  312. Up ();
  313. });
  314. addEventListener ('resize',
  315. function ()
  316. {
  317. GetZoom ();
  318. });
  319. buttonNew.addEventListener ('click',
  320. function ()
  321. {
  322. if (confirm ('作成中の絵を初期化しても大丈夫ですか.'))
  323. NewCanvas ();
  324. });
  325. buttonSend.addEventListener ('click',
  326. async function ()
  327. {
  328. const searchParams = new URLSearchParams (window.location.search);
  329. await SendToServer (false);
  330. document.getElementsByTagName ('body')[0].style.marginTop = 'auto';
  331. document.getElementsByTagName ('body')[0].style.marginBottom = 'auto';
  332. document.getElementsByTagName ('body')[0].innerHTML = '<div style="font-size: 500%; position: fixed; top: 50%; left: 50%; transform: translateX(-50%) translateY(-50%); white-space: nowrap; margin: auto; text-align: center">待ってね.</div>';
  333. window.setTimeout (
  334. function ()
  335. {
  336. window.location.href = `./?thread=${searchParams.get ('thread')}&sort=${
  337. searchParams.get ('sort')}`;
  338. }, 2000);
  339. });
  340. buttonSave.addEventListener ('click',
  341. function ()
  342. {
  343. let download = document.createElement ('a');
  344. concatCanvas ();
  345. download.href = canvasPerfect.toDataURL ('image/png');
  346. download.download = 'canvas.png';
  347. document.body.appendChild (download);
  348. download.click ();
  349. document.body.removeChild (download);
  350. });
  351. buttonChangeSize.addEventListener ('click',
  352. function ()
  353. {
  354. changeWidth.value = canvasWidth;
  355. changeHeight.value = canvasHeight;
  356. changeSizeWindow.style.display = 'block';
  357. });
  358. buttonUndo.addEventListener ('click',
  359. function ()
  360. {
  361. if (countSt > 0)
  362. {
  363. const _layer = Number (layer.value);
  364. --countSt;
  365. count = historySt[countSt];
  366. while (layerMax != 3)
  367. {
  368. if (layerMax > 3)
  369. delLayer (0, true);
  370. else
  371. addLayer (0, true);
  372. }
  373. ClearCanvas ();
  374. for (let i = 0; i < count; ++i)
  375. Trace (i);
  376. layer.value = Math.max (0, Math.min (_layer, layerMax - 1));
  377. }
  378. reDraw ();
  379. });
  380. buttonRedo.addEventListener ('click',
  381. function ()
  382. {
  383. if (countSt < countMax)
  384. {
  385. const _layer = Number (layer.value);
  386. ++countSt;
  387. count = historySt[countSt];
  388. for (let i = historySt[countSt - 1]; i < count; ++i)
  389. Trace (i);
  390. layer.value = Math.max (0, Math.min (_layer, layerMax - 1));
  391. }
  392. reDraw ();
  393. });
  394. pen.addEventListener ('click',
  395. function ()
  396. {
  397. for (let i = 0; i < layerMax; ++i)
  398. canvasCtx[i].globalCompositeOperation = 'source-over';
  399. })
  400. rubber.addEventListener ('click',
  401. function ()
  402. {
  403. for (let i = 0; i < layerMax; ++i)
  404. canvasCtx[i].globalCompositeOperation = 'destination-out';
  405. });
  406. layerAdd.addEventListener ('click',
  407. function ()
  408. {
  409. addLayer (Number (layer.value) + 1, false);
  410. });
  411. layerDel.addEventListener ('click',
  412. function ()
  413. {
  414. if (layerMax > 1)
  415. delLayer (Number (layer.value), false);
  416. else
  417. window.alert ('消せるレイアがありません.');
  418. });
  419. layerUp.addEventListener ('click',
  420. function ()
  421. {
  422. if (layer.value < layerMax - 1)
  423. changeLayer (Number (layer.value), Number (layer.value) + 1, false);
  424. });
  425. layerDown.addEventListener ('click',
  426. function ()
  427. {
  428. if (layer.value > 0)
  429. changeLayer (Number (layer.value), Number (layer.value) - 1, false);
  430. });
  431. function
  432. Trace (his)
  433. {
  434. switch (history[his].type)
  435. {
  436. case 'draw':
  437. canvasCtx[history[his].layer].globalCompositeOperation = (
  438. ((history[his].mode == 'rubber')
  439. ? 'destination-out'
  440. : 'source-over'));
  441. if (history[his].mode == 'bucket')
  442. {
  443. bucketArea (history[his].start.x, history[his].start.y,
  444. history[his].colour, history[his].layer, true);
  445. }
  446. else
  447. {
  448. canvasCtx[history[his].layer].lineWidth = history[his].size;
  449. canvasCtx[history[his].layer].strokeStyle = history[his].colour;
  450. canvasCtx[history[his].layer].beginPath ();
  451. canvasCtx[history[his].layer].moveTo (history[his].start.x,
  452. history[his].start.y);
  453. canvasCtx[history[his].layer].lineTo (history[his].end.x,
  454. history[his].end.y);
  455. canvasCtx[history[his].layer].closePath ();
  456. canvasCtx[history[his].layer].stroke ();
  457. }
  458. break;
  459. case 'load':
  460. canvasCtx[history[his].layer].drawImage (
  461. img, 0, 0, canvasWidth, canvasHeight);
  462. break;
  463. case 'addLayer':
  464. addLayer (history[his].layer, true);
  465. break;
  466. case 'delLayer':
  467. delLayer (history[his].layer, true);
  468. break;
  469. case 'changeLayer':
  470. changeLayer (history[his].from, history[his].to, true);
  471. break;
  472. }
  473. }
  474. function
  475. getMousePosition (canvas, evt)
  476. {
  477. let rect = canvas.getBoundingClientRect ();
  478. return {x: (evt.clientX - rect.left) * zoomX,
  479. y: (evt.clientY - rect.top) * zoomY};
  480. }
  481. function
  482. ClearCanvas ()
  483. {
  484. for (let i = 0; i < layerMax; ++i)
  485. canvasCtx[i].clearRect (0, 0, canvasWidth, canvasHeight);
  486. reDraw ();
  487. }
  488. function
  489. NewCanvas ()
  490. {
  491. ClearCanvas ();
  492. count = 0;
  493. history = [];
  494. countSt = 0;
  495. historySt = [];
  496. countMax = 0;
  497. }
  498. function
  499. Drawing (evt)
  500. {
  501. if (nowDraw)
  502. {
  503. let mousePos = getMousePosition (canvasRoot, evt);
  504. let nowX = mousePos.x;
  505. let nowY = mousePos.y;
  506. if ((nowX == lastX) && (nowY == lastY))
  507. --nowX;
  508. if (radioSize.value == 0)
  509. {
  510. canvasCtx[layer.value].lineWidth = document.getElementById (
  511. 'size-free').value;
  512. }
  513. else
  514. canvasCtx[layer.value].lineWidth = radioSize.value;
  515. canvasCtx[layer.value].strokeStyle = colour;
  516. canvasCtx[layer.value].beginPath ();
  517. canvasCtx[layer.value].moveTo (lastX, lastY);
  518. canvasCtx[layer.value].lineTo (nowX, nowY);
  519. canvasCtx[layer.value].closePath ();
  520. canvasCtx[layer.value].stroke ();
  521. reDraw ();
  522. history[count] = {
  523. type: 'draw',
  524. start: {x: lastX, y: lastY},
  525. end: {x: nowX, y: nowY},
  526. colour: canvasCtx[layer.value].strokeStyle,
  527. size: canvasCtx[layer.value].lineWidth,
  528. layer: layer.value,
  529. mode: ((canvasCtx[layer.value].globalCompositeOperation
  530. == 'destination-out')
  531. ? 'rubber'
  532. : (bucket.checked ? 'bucket' : 'pen'))};
  533. ++count;
  534. lastX = mousePos.x;
  535. lastY = mousePos.y;
  536. }
  537. }
  538. function
  539. changeColour (rgb)
  540. {
  541. const colourPicker = document.getElementById ('colour-picker');
  542. let cps = colourPicker.getAttribute ('cmanCPat').split (',');
  543. let clCode;
  544. cps[0] = 'def_color:cns=' + rgbTo16 (rgb).substr (0, 7);
  545. clCode = rgbTo16 (rgb).substr (0, 7);
  546. if (clCode in colourCode)
  547. clCode = colourCode[clCode];
  548. document.getElementById ('irorororo').innerHTML = clCode;
  549. colourPicker.setAttribute ('cmanCPat', cps.join ());
  550. colour = rgb;
  551. }
  552. function
  553. rgbTo16 (col)
  554. {
  555. return "#" + col.match(/\d+/g).map(function(a){return ("0" + parseInt(a).toString(16)).slice(-2)}).join("");
  556. }
  557. function
  558. bucketArea (px, py, cl, lyr, fromHistory)
  559. {
  560. console.log ([px, py]);
  561. let cwnt = 0;
  562. let r, g, b, a;
  563. let r_, g_, b_, a_;
  564. [r, g, b, a] = canvasCtx[lyr].getImageData (px, py, 1, 1).data;
  565. let dx = new Array (canvasWidth * canvasHeight);
  566. let dy = new Array (canvasWidth * canvasHeight);
  567. let dxy = new Array (canvasHeight);
  568. for (let i = 0; i < canvasHeight; ++i)
  569. dxy[i] = new Array (canvasWidth).fill (0);
  570. dx[0] = px;
  571. dy[0] = py;
  572. dxy[py][px] = 1;
  573. ++cwnt;
  574. for (let i = 0; true; ++i)
  575. {
  576. let x = dx[i];
  577. let y = dy[i];
  578. if (x + 1 < canvasWidth)
  579. {
  580. if (dxy[y][x + 1] == 0)
  581. {
  582. dxy[y][x + 1] = 1;
  583. [r_, g_, b_, a_] = canvasCtx[lyr].getImageData (
  584. x + 1, y, 1, 1).data;
  585. if ((r_ == r) && (g_ == g) && (b_ == b) && (a_ == a))
  586. {
  587. dx[cwnt] = x + 1;
  588. dy[cwnt] = y;
  589. ++cwnt;
  590. }
  591. }
  592. }
  593. if (0 <= x - 1)
  594. {
  595. if (dxy[y][x - 1] == 0)
  596. {
  597. dxy[y][x - 1] = 1;
  598. [r_, g_, b_, a_] = canvasCtx[lyr].getImageData (
  599. x - 1, y, 1, 1).data;
  600. if ((r_ == r) && (g_ == g) && (b_ == b) && (a_ == a))
  601. {
  602. dx[cwnt] = x - 1;
  603. dy[cwnt] = y;
  604. ++cwnt;
  605. }
  606. }
  607. }
  608. if (y + 1 < canvasHeight)
  609. {
  610. if (dxy[y + 1][x] == 0)
  611. {
  612. dxy[y + 1][x] = 1;
  613. [r_, g_, b_, a_] = canvasCtx[lyr].getImageData (
  614. x, y + 1, 1, 1).data;
  615. if ((r_ == r) && (g_ == g) && (b_ == b) && (a_ == a))
  616. {
  617. dx[cwnt] = x;
  618. dy[cwnt] = y + 1;
  619. ++cwnt;
  620. }
  621. }
  622. }
  623. if (y - 1 >= 0)
  624. {
  625. if (dxy[y - 1][x] == 0)
  626. {
  627. dxy[y - 1][x] = 1;
  628. [r_, g_, b_, a_] = canvasCtx[lyr].getImageData (
  629. x, y - 1, 1, 1).data;
  630. if ((r_ == r) && (g_ == g) && (b_ == b) && (a_ == a))
  631. {
  632. dx[cwnt] = x;
  633. dy[cwnt] = y - 1;
  634. ++cwnt;
  635. }
  636. }
  637. }
  638. if (i == cwnt - 1)
  639. break;
  640. }
  641. canvasCtx[lyr].fillStyle = cl;
  642. for (let i = 0; i < cwnt; ++i)
  643. canvasCtx[lyr].fillRect (dx[i], dy[i], 1, 1);
  644. reDraw ();
  645. if (!(fromHistory))
  646. {
  647. // 履歴を更新
  648. historySt[countSt] = count;
  649. ++countSt;
  650. countMax = countSt;
  651. history[count] = {
  652. type: 'draw',
  653. start: {x: px, y: py},
  654. colour: canvasCtx[lyr].fillStyle,
  655. layer: lyr,
  656. mode: 'bucket'};
  657. ++count;
  658. }
  659. }
  660. function
  661. getCookie (nameOfCookie)
  662. {
  663. const r = document.cookie.split (';');
  664. let f = null;
  665. r.forEach (
  666. function (element)
  667. {
  668. const content = element.split ('=');
  669. if (content[0] == nameOfCookie)
  670. f = content[1];
  671. });
  672. return f;
  673. }
  674. function
  675. uniqueId (digits)
  676. {
  677. var strong = typeof digits !== 'undefined' ? digits : 1000;
  678. return Date.now().toString(16) + Math.floor (
  679. (strong * Math.random ())).toString (16);
  680. }
  681. function
  682. deletePost (id)
  683. {
  684. delId.value = id;
  685. }
  686. function
  687. deletePostReally ()
  688. {
  689. if (delId.value == '')
  690. window.alert ('削除したいレスの番号を入れろ!!!!!');
  691. else if (delPass.value == '')
  692. window.alert ('設定した削除用パスワードを入れろ!!!!!!!!!!!!!!!!!');
  693. else
  694. {
  695. window.location.href = `#${delId.value}`;
  696. setTimeout (
  697. function ()
  698. {
  699. if (window.confirm (`レス番号 ${delId.value} の投稿を削除します.\nよろしいですかい?????`))
  700. {
  701. const searchParams = new URLSearchParams (window.location.search);
  702. window.location.href = `./modules/delete.php?thread=${searchParams.get (
  703. 'thread')}&id=${delId.value}&pass=${delPass.value}`;
  704. }
  705. else
  706. window.location.href = '#del';
  707. }, 100);
  708. }
  709. }
  710. function
  711. closeModal ()
  712. {
  713. modal.style.display = 'none';
  714. }
  715. function
  716. applyResolution ()
  717. {
  718. if (!(((changeWidth.value < canvasRoot.width)
  719. || changeHeight.value < canvasRoot.height)
  720. && !(window.confirm ('指定されている幅もしくは高さの値がサイズ変更前よりも小さいです.\nこのままだと右端もしくは下端が変更後のサイズに合わせてカットされてしまいます.\nよろしいかな?????'))))
  721. {
  722. canvasWidth = Math.max (32, Math.min (changeWidth.value, 640));
  723. canvasHeight = Math.max (24, Math.min (changeHeight.value, 480));
  724. canvasArea.style.width = canvasWidth + 'px';
  725. canvasArea.style.height = canvasHeight + 'px';
  726. for (let i = 0; i < layerMax; ++i)
  727. {
  728. const canvas4replace = document.createElement ('canvas');
  729. canvas4replace.width = canvasWidth;
  730. canvas4replace.height = canvasHeight;
  731. const canvas4replaceCtx = canvas4replace.getContext ('2d');
  732. if (i == 0)
  733. {
  734. canvas4replaceCtx.fillStyle = 'white';
  735. canvas4replaceCtx.fillRect (0, 0, canvasWidth, canvasHeight);
  736. }
  737. canvas4replaceCtx.drawImage (canvas[i], 0, 0);
  738. canvas[i].width = canvasWidth;
  739. canvas[i].height = canvasHeight;
  740. canvasCtx[i].drawImage (canvas4replace, 0, 0);
  741. }
  742. canvasPerfect.width = canvasRoot.width = canvasWidth;
  743. canvasPerfect.height = canvasRoot.height = canvasHeight;
  744. reDraw ();
  745. closeModal ();
  746. }
  747. }
  748. function
  749. reDraw ()
  750. {
  751. canvasRootCtx.fillStyle = 'white';
  752. canvasRootCtx.fillRect (0, 0, canvasWidth, canvasHeight);
  753. for (let i = 0; i < layerMax; ++i)
  754. {
  755. if (!(layerSep.checked && (i != layer.value)))
  756. canvasRootCtx.drawImage (canvas[i], 0, 0);
  757. }
  758. }
  759. function
  760. changeLayer (from, to, fromHistory)
  761. {
  762. const _canvas = canvas[from];
  763. const _canvasCtx = canvasCtx[from];
  764. canvas[from] = canvas[to];
  765. canvas[to] = _canvas;
  766. canvasCtx[from] = canvasCtx[to];
  767. canvasCtx[to] = _canvasCtx;
  768. if (!(fromHistory))
  769. {
  770. layer.value = to;
  771. // 履歴を更新
  772. historySt[countSt] = count;
  773. ++countSt;
  774. countMax = countSt;
  775. history[count] = {type: 'changeLayer',
  776. from: from,
  777. to: to};
  778. ++count;
  779. }
  780. reDraw ();
  781. }
  782. function
  783. addLayer (lyr, fromHistory)
  784. {
  785. const newLayer = document.createElement ('label');
  786. canvas.splice (lyr, 0, document.createElement ('canvas'));
  787. canvasCtx.splice (lyr, 0, canvas[lyr].getContext ('2d'));
  788. canvas[lyr].width = canvasWidth;
  789. canvas[lyr].height = canvasHeight;
  790. newLayer.innerHTML
  791. = ` <input onclick="reDraw ()"
  792. type="radio"
  793. name="layer"
  794. value="${layerMax}" />${layerMax}`;
  795. layer[layerMax - 1].parentNode.after (newLayer);
  796. ++layerMax;
  797. reDraw ();
  798. if (!(fromHistory))
  799. {
  800. layer.value = lyr;
  801. // 履歴を更新
  802. historySt[countSt] = count;
  803. ++countSt;
  804. countMax = countSt;
  805. history[count] = {type: 'addLayer',
  806. layer: lyr};
  807. ++count;
  808. }
  809. }
  810. function
  811. delLayer (lyr, fromHistory)
  812. {
  813. --layerMax;
  814. canvas[lyr].remove ();
  815. canvas.splice (lyr, 1);
  816. canvasCtx.splice (lyr, 1);
  817. layer[layerMax].parentNode.remove ();
  818. if (!(fromHistory))
  819. {
  820. layer.value = Math.max (0, lyr - 1);
  821. // 履歴を更新
  822. historySt[countSt] = count;
  823. ++countSt;
  824. countMax = countSt;
  825. history[count] = {type: 'delLayer',
  826. layer: lyr};
  827. ++count;
  828. }
  829. }