logo.html 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. <!--
  2. Copyright (c) 2006-2023, RT-Thread Development Team
  3. SPDX-License-Identifier: Apache-2.0
  4. Change Logs:
  5. Date Author Notes
  6. 2023-02-25 GuEe-GUI the first version
  7. -->
  8. <!DOCTYPE html>
  9. <html lang="en">
  10. <head>
  11. <meta charset="utf-8">
  12. <meta name="viewport" content="width=device-width, initial-scale=1">
  13. <link rel="shortcut icon" href="https://www.rt-thread.org/favicon.ico" type="image/x-icon">
  14. <title>Logo PPM Previews</title>
  15. <style type="text/css">
  16. body {
  17. min-width: 860px;
  18. margin: 0px;
  19. padding: 0px;
  20. font-family: Consolas, "Cascadia Code", Monaco;
  21. background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjxzdmcgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiB2aWV3Qm94PSIwIDAgMTAgMTAiIGlkPSJjb2RlLWJhY2tncm91bmQiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiAgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPg0KICAgIDx0aXRsZT5jb2RlLWJhY2tncm91bmQ8L3RpdGxlPg0KICAgIDxnPg0KICAgICAgICA8bGluZSB5Mj0iMTAiIHgyPSIxMCIgeTE9IjAiIHgxPSIxMCIgZmlsbD0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2U9IiNlNWU1ZTUiLz4NCiAgICAgICAgPGxpbmUgeTI9IjEwIiB4Mj0iMTAiIHkxPSIxMCIgeDE9IjAiIGZpbGw9Im5vbmUiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSIjZTVlNWU1Ii8+DQogICAgPC9nPg0KPC9zdmc+);
  22. }
  23. .main {
  24. display: flex;
  25. justify-content: center;
  26. }
  27. .controls > div {
  28. padding: 10px;
  29. height: 52px;
  30. line-height: 52px;
  31. }
  32. label {
  33. user-select: none;
  34. font-size: 24px;
  35. font-weight: 400;
  36. color: #494949;
  37. }
  38. .res-input, .ppm-input, #background-color {
  39. cursor: pointer;
  40. border: 2px solid #494949;
  41. border-radius: 8px;
  42. outline: none;
  43. font-size: 18px;
  44. background-color: #fff;
  45. }
  46. .res-input {
  47. height: 32px;
  48. padding: 0 4px;
  49. line-height: 52px;
  50. }
  51. .background-color, .ppm-input {
  52. display: block;
  53. text-align: center;
  54. }
  55. #background-color {
  56. width: 20%;
  57. height: 32px;
  58. }
  59. .ppm-input:hover {
  60. background-color: #eee;
  61. }
  62. #ppm-input {
  63. display: none;
  64. }
  65. .canvas {
  66. justify-content: center;
  67. display: flex;
  68. margin-top: 20px;
  69. }
  70. </style>
  71. </head>
  72. <body>
  73. <div class="main">
  74. <div>
  75. <div class="controls">
  76. <div style="display: flex; justify-content: center;">
  77. <div>
  78. <label class="resolution">Screen Resolution</label>
  79. <input type="number" class="res-input" id="canvas-width" placeholder="width" min="1" step="1">
  80. <label class="resolution">&times;</label>
  81. <input type="number" class="res-input" id="canvas-height" placeholder="height" min="1" step="1">
  82. </div>
  83. </div>
  84. <div>
  85. <label class="background-color" for="background-color">
  86. <span>Background Color</span>
  87. <input type="color" id="background-color">
  88. </label>
  89. </div>
  90. <div>
  91. <label class="ppm-input">
  92. <input type="file" id="ppm-input" accept=".ppm" placeholder="PPM">
  93. <span>Open PPM File</span>
  94. </label>
  95. </div>
  96. </div>
  97. <div class="canvas">
  98. <canvas id="canvas"></canvas>
  99. </div>
  100. </div>
  101. </div>
  102. </body>
  103. <script type="text/javascript">
  104. var Screen = new Object;
  105. window.onload = function() {
  106. Screen.canvas = document.getElementById('canvas');
  107. Screen.ctx = canvas.getContext('2d');
  108. Screen.ppmWidth = 0;
  109. Screen.ppmHeight = 0;
  110. Screen.ppmPixels = [];
  111. Screen.widthInput = document.getElementById('canvas-width');
  112. Screen.heightInput = document.getElementById('canvas-height');
  113. Screen.ColorPicker = document.getElementById('background-color');
  114. Screen.widthInput.value = 1;
  115. Screen.heightInput.value = 1;
  116. Screen.ColorPicker.value = "#000000";
  117. document.getElementById('canvas-width').addEventListener('input', function(e) {
  118. applyCanvasSize();
  119. });
  120. document.getElementById('canvas-height').addEventListener('input', function(e) {
  121. applyCanvasSize();
  122. });
  123. document.getElementById('background-color').addEventListener('input', function(e) {
  124. applyBackgroundColor();
  125. });
  126. document.getElementById('ppm-input').addEventListener('change', function(e) {
  127. const file = e.target.files[0];
  128. if (!file) {
  129. return;
  130. }
  131. const reader = new FileReader();
  132. reader.onload = function(e) {
  133. const ppm_data = e.target.result;
  134. const { width, height, pixels } = parsePPM(ppm_data);
  135. Screen.ppmWidth = width;
  136. Screen.ppmHeight = height;
  137. Screen.ppmPixels = pixels;
  138. Screen.ctx.reset();
  139. applyBackgroundColor();
  140. };
  141. reader.readAsText(file);
  142. });
  143. function parsePPM(data) {
  144. let cursor = 0;
  145. const lines = data.split('\n').filter(line => !line.startsWith('#') && line.trim() !== '');
  146. const magic = lines[cursor++].trim();
  147. if (!['P1', 'P2', 'P3'].includes(magic)) {
  148. alert('Unsupported format');
  149. return;
  150. }
  151. let maxVal = 1;
  152. const [width, height] = lines[cursor++].split(/\s+/).map(Number);
  153. if (magic !== 'P1') {
  154. maxVal = parseInt(lines[cursor++]);
  155. }
  156. const pixels = [];
  157. const values = lines.slice(cursor).join(' ').split(/\s+/).filter(v => v !== '');
  158. if (magic === 'P1' || magic === 'P2') {
  159. for (var i = 0; i < values.length; ++i) {
  160. const val = parseInt(values[i]);
  161. const scaled = magic === 'P1' ? val * 255 : Math.round((val / maxVal) * 255);
  162. pixels.push([scaled, scaled, scaled]);
  163. }
  164. } else if (magic === 'P3') {
  165. for (var i = 0; i < values.length; i += 3) {
  166. const r = Math.round((values[i] / maxVal) * 255);
  167. const g = Math.round((values[i+1] / maxVal) * 255);
  168. const b = Math.round((values[i+2] / maxVal) * 255);
  169. pixels.push([r, g, b]);
  170. }
  171. }
  172. return { width, height, pixels };
  173. }
  174. function renderPPM() {
  175. if (Screen.ppmWidth == 0 || Screen.ppmHeight == 0) {
  176. return;
  177. }
  178. const imageData = Screen.ctx.createImageData(Screen.ppmWidth, Screen.ppmHeight);
  179. for (var i = 0; i < Screen.ppmPixels.length; ++i) {
  180. const [r, g, b] = Screen.ppmPixels[i];
  181. const j = i * 4;
  182. imageData.data[j] = r;
  183. imageData.data[j + 1] = g;
  184. imageData.data[j + 2] = b;
  185. imageData.data[j + 3] = 255;
  186. }
  187. const offsetX = (Screen.canvas.width - Screen.ppmWidth) / 2;
  188. const offsetY = (Screen.canvas.height - Screen.ppmHeight) / 2;
  189. Screen.ctx.putImageData(imageData, offsetX, offsetY);
  190. }
  191. function applyCanvasSize() {
  192. var canvasWidth = parseInt(Screen.widthInput.value);
  193. var canvasHeight = parseInt(Screen.heightInput.value);
  194. if (canvasWidth == 0 || canvasHeight == 0) {
  195. return;
  196. }
  197. Screen.canvas.width = canvasWidth;
  198. Screen.canvas.height = canvasHeight;
  199. Screen.canvas.style.boxShadow = "1px 1px 2px rgba(0, 0, 0, 0.2)";
  200. applyBackgroundColor();
  201. }
  202. function applyBackgroundColor() {
  203. Screen.ctx.fillStyle = Screen.ColorPicker.value;
  204. Screen.ctx.fillRect(0, 0, Screen.canvas.width, Screen.canvas.height);
  205. renderPPM();
  206. }
  207. }
  208. </script>
  209. </html>