Help
RSS
API
Feed
Maltego
Contact
Domain > artpcb.mistune.top
×
More information on this domain is in
AlienVault OTX
Is this malicious?
Yes
No
DNS Resolutions
Date
IP Address
2025-09-30
104.21.70.60
(
ClassC
)
2026-02-09
172.67.220.117
(
ClassC
)
Port 443
HTTP/1.1 200 OKDate: Mon, 09 Feb 2026 16:07:06 GMTContent-Type: text/html; charsetutf-8Transfer-Encoding: chunkedConnection: keep-aliveAccess-Control-Allow-Origin: *Cache-Control: public, max-age0, must-revalidatereferrer-policy: strict-origin-when-cross-originx-content-type-options: nosniffReport-To: {group:cf-nel,max_age:604800,endpoints:{url:https://a.nel.cloudflare.com/report/v4?sJ6Fo0TB%2FAosOrnN60Z7BlUf%2FHKrLhsPqqDlmHpRuGrkJsAG7JoIAoWNoZ1mUBAe%2FZczjaCL3FiHOLn8SbeUYVotWCJbPowaIKG7sZ8wQsc1Zpw%3D%3D}}Nel: {report_to:cf-nel,success_fraction:0.0,max_age:604800}Server: cloudflarecf-cache-status: DYNAMICCF-RAY: 9cb49e288e5ae6e0-PDXalt-svc: h3:443; ma86400 !DOCTYPE html>html langzh>head> meta charsetUTF-8 /> title>智能PCB图像处理工作台/title> !-- FAVICON START --> link relicon hrefdata:image/svg+xml,%3Csvg xmlnshttp://www.w3.org/2000/svg viewBox0 0 100 100%3E%3Crect width100 height100 rx15 fill%23004D40/%3E%3Cg fill%23FFD700%3E%3Ccircle cx20 cy20 r10/%3E%3Ccircle cx80 cy20 r10/%3E%3Ccircle cx20 cy80 r10/%3E%3Ccircle cx80 cy80 r10/%3E%3C/g%3E%3Cpath dM 20 50 Q 50 20, 80 50 stroke%23FFF9C4 stroke-width8 stroke-linecapround fillnone/%3E%3C/svg%3E> !-- FAVICON END --> meta nameviewport contentwidthdevice-width, initial-scale1.0 /> script srchttps://cdn.tailwindcss.com>/script> script srchttps://cdn.jsdelivr.net/npm/browser-image-compression@2.0.2/dist/browser-image-compression.min.js>/script> script srchttps://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js>/script> script srchttps://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js>/script> style> .fade { transition: opacity 0.5s ease; } .color-picker { display: inline-block; width: 24px; height: 24px; border: 2px solid #fff; border-radius: 50%; cursor: pointer; box-shadow: 0 0 2px rgba(0,0,0,0.5); } .color-picker.selected { border-color: #000; transform: scale(1.1); } .slider-container { display: flex; align-items: center; gap: 8px; } .slider-value { min-width: 40px; text-align: center; } .brush-size-selector { margin-top: 8px; } .brush-size-label { font-size: 14px; margin-right: 8px; } .brush-size-input { width: 60px; padding: 2px; } .region-mode { background-color: #e2f0fd !important; } .tool-button { cursor: pointer; border: 1px solid #ccc; padding: 4px 8px; border-radius: 4px; font-size: 12px; margin-right: 4px; } .tool-button.active { background-color: #2563eb; color: white; border-color: #2563eb; } .tool-button:disabled, button:disabled { opacity: 0.5; cursor: not-allowed; } .path-preview { position: absolute; top: 0; left: 0; pointer-events: none; } @media (max-width: 768px) { .container { padding: 20px; } .canvas-container { width: 100%; } } /style>/head>body classbg-gray-100 text-gray-900 font-sans p-6> div classmax-w-6xl mx-auto space-y-6 container> h1 classtext-3xl font-bold text-center>智能PCB图像处理工作台/h1> !-- 上传区域 --> div iddrop-area classborder-2 border-dashed border-blue-400 rounded-lg p-6 bg-white text-center cursor-pointer> p classtext-lg>点击或拖拽图片到此/p> input typefile iduploader acceptimage/* classhidden> div idimg-info classtext-sm text-gray-600 mt-2>/div> /div> !-- 原图编辑工具 --> div idoriginalEditTool classhidden bg-white p-4 rounded shadow> h2 classfont-semibold mb-2>原图编辑工具/h2> div classmb-4> p classtext-sm text-gray-600 mb-2>选择工具和颜色,然后在原图上操作/p> div classflex mb-2> button idoriginalBrushBtn classtool-button active>画笔工具/button> button idoriginalLassoBtn classtool-button>套索工具/button> button idoriginalFillBtn classtool-button>填充工具/button> /div> div classslider-container mb-2> span classtext-sm>填充容差:/span> input typerange idtoleranceSlider min0 max100 value30 classflex-1> span idtoleranceValue classslider-value>30/span> /div> div classbrush-size-selector> span classbrush-size-label>画笔大小:/span> input typenumber idbrushSizeOriginal min1 max100 value10 classbrush-size-input> span>像素/span> /div> div idavailableColorsOriginal classflex flex-wrap gap-2>/div> /div> div classflex gap-4> button idclearOriginalSelectionBtn classbg-gray-200 hover:bg-gray-300 px-3 py-1 rounded text-sm hidden>清除选区/button> button idundoOriginalBtn classbg-gray-200 hover:bg-gray-300 px-3 py-1 rounded text-sm disabled>撤销/button> button idresetOriginalBtn classbg-gray-200 hover:bg-gray-300 px-3 py-1 rounded text-sm>重置/button> button idnextStepBtn classbg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded text-sm>下一步:选择色系/button> /div> /div> !-- 色系与工艺选择 --> div idconfigSelection classhidden grid grid-cols-1 md:grid-cols-2 gap-4 bg-white p-4 rounded shadow> !-- 色系选择 --> div idcolorSelection classspace-y-2> h2 classfont-semibold>选择主颜色系:/h2> div classflex flex-wrap gap-x-4 gap-y-2> label classflex items-center space-x-2> input typeradio namemainColor valueblue classaccent-blue-600> span>蓝色系/span> /label> label classflex items-center space-x-2> input typeradio namemainColor valuered classaccent-red-600> span>红色系/span> /label> label classflex items-center space-x-2> input typeradio namemainColor valuepurple classaccent-purple-600> span>紫色系/span> /label> label classflex items-center space-x-2> input typeradio namemainColor valuewhite classaccent-gray-400> span>白色系 (黑色丝印)/span> /label> label classflex items-center space-x-2> input typeradio namemainColor valueblack classaccent-black> span>黑色系/span> /label> label classflex items-center space-x-2> input typeradio namemainColor valueyellow classaccent-yellow-500> span>黄色系/span> /label> label classflex items-center space-x-2> input typeradio namemainColor valuegreen classaccent-green-600> span>绿色系/span> /label> /div> /div> !-- 表面处理工艺 --> div idsurfaceFinishSelection classspace-y-2> h2 classfont-semibold>选择表面处理工艺:/h2> div classflex flex-wrap gap-x-4 gap-y-2> label classflex items-center space-x-2> input typeradio namesurfaceFinish valuehasl checked classaccent-gray-500> span>喷锡 (银色)/span> /label> label classflex items-center space-x-2> input typeradio namesurfaceFinish valueenig classaccent-yellow-500> span>沉金 (金色)/span> /label> /div> div classslider-container mt-2> span classtext-sm>金属色检测容差:/span> input typerange iddetectionToleranceSlider min0 max100 value25 classflex-1> span iddetectionToleranceValue classslider-value>25/span> /div> /div> /div> !-- 简化图编辑工具 --> div idsimplifiedEditTool classhidden bg-white p-4 rounded shadow> h2 classfont-semibold mb-2>简化图编辑工具/h2> div classmb-4> p classtext-sm text-gray-600 mb-2>选择工具和颜色,然后在简化图上操作/p> div classflex mb-2> button idsimplifiedBrushBtn classtool-button active>画笔工具/button> button idsimplifiedLassoBtn classtool-button>套索工具/button> button idsimplifiedFillBtn classtool-button>填充工具/button> /div> div classbrush-size-selector> span classbrush-size-label>画笔大小:/span> input typenumber idbrushSizeSimplified min1 max100 value10 classbrush-size-input> span>像素/span> /div> div idavailableColorsSimplified classflex flex-wrap gap-2>/div> /div> div classflex gap-4> button idclearSimplifiedSelectionBtn classbg-gray-200 hover:bg-gray-300 px-3 py-1 rounded text-sm hidden>清除选区/button> button idundoSimplifiedBtn classbg-gray-200 hover:bg-gray-300 px-3 py-1 rounded text-sm disabled>撤销/button> button idresetSimplifiedBtn classbg-gray-200 hover:bg-gray-300 px-3 py-1 rounded text-sm>重置/button> button idgenerateBtn classbg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded text-sm>一键生成/button> /div> /div> !-- 提示 --> div idtoast classfixed bottom-6 left-1/2 transform -translate-x-1/2 bg-black text-white px-4 py-2 rounded shadow opacity-0 fade text-sm z-50>/div> !-- 原图与简化图 --> div classgrid grid-cols-1 md:grid-cols-2 gap-6> div classbg-white rounded shadow p-4 canvas-container> h2 classfont-semibold mb-2>原图 span classtext-sm font-normal>(点击图片可修改颜色)/span>/h2> div classrelative w-full> canvas idoriginalCanvas classw-full border rounded cursor-crosshair>/canvas> canvas idoriginalPathPreview classpath-preview w-full border border-transparent rounded>/canvas> /div> /div> div classbg-white rounded shadow p-4 canvas-container> h2 classfont-semibold mb-2>简化图 span classtext-sm font-normal>(点击图片可修改颜色)/span>/h2> div classrelative w-full> canvas idsimplifiedCanvas classw-full border rounded cursor-crosshair>/canvas> canvas idsimplifiedPathPreview classpath-preview w-full border border-transparent rounded>/canvas> /div> /div> /div> !-- 遮罩图层 --> div idmaskContainer classhidden bg-white rounded shadow p-4> h2 classtext-lg font-semibold mb-4>遮罩图层/h2> div idmasks classflex flex-wrap gap-4>/div> /div> !-- 合成图层 --> div idlayerContainer classhidden bg-white rounded shadow p-4> h2 classtext-lg font-semibold mb-4>合成图层/h2> div idlayers classflex flex-wrap gap-4>/div> /div> !-- 实物预览图 --> div idpreviewContainer classhidden bg-white rounded shadow p-4> h2 classtext-lg font-semibold mb-4>实物预览图/h2> div idpreview classflex flex-wrap gap-4> canvas idpreviewCanvas classw-full border rounded max-w-md>/canvas> /div> /div> !-- 返回修改按钮 --> div idbackToEditContainer classhidden bg-white p-4 rounded shadow text-center> button idbackToOriginalBtn classbg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded mr-4>返回修改原图/button> button idbackToSimplifiedBtn classbg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded>返回修改简化图/button> /div> !-- 自定义文件名输入框 --> div classbg-white p-4 rounded shadow> h2 classfont-semibold>自定义下载文件名:/h2> input typetext idcustomFilename classw-full p-2 border rounded placeholder请输入自定义文件名> /div> !-- 保留底部下载按钮 --> div classtext-center mt-4> button iddownloadAllBtn classbg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded>一键下载所有图片/button> /div> /div> footer classtext-center text-sm text-gray-500 py-4 mt-8 border-t border-gray-200> p> 由 B 站 UP 主 a hrefhttps://space.bilibili.com/1031031979 target_blank relnoopener noreferrer classtext-blue-600 hover:underline>不是孙小华呀/a> 制作 /p> p classmt-1> a hrefhttps://space.bilibili.com/1775976544 target_blank relnoopener noreferrer classtext-blue-600 hover:underline>星辉棉花/a> 通过 Gemini 修改并上传至 Cloudflare Pages /p> /footer> script> // 常量定义 const TOOLS { BRUSH: brush, LASSO: lasso, FILL: fill }; const MAX_COLOR_DISTANCE Math.sqrt(255 ** 2 * 3); // ~441.67 const COLOR_SCHEMES { blue: { 深蓝: 22, 31, 125, 浅蓝: 93, 167, 227, 深绿: 25, 53, 34, 浅绿: 249, 225, 149, 黑色: 6, 16, 8, 白色: 230, 234, 235 }, red: { 深红: 125, 22, 22, 浅红: 227, 93, 93, 深绿: 25, 53, 34, 浅绿: 249, 225, 149, 黑色: 6, 16, 8, 白色: 230, 234, 235 }, purple: { 深紫: 125, 22, 125, 浅紫: 227, 93, 227, 深绿: 25, 53, 34, 浅绿: 249, 225, 149, 黑色: 6, 16, 8, 白色: 230, 234, 235 }, white: { 深灰: 100, 100, 100, 浅灰: 200, 200, 200, 深绿: 25, 53, 34, 浅绿: 249, 225, 149, 黑色: 10, 10, 10, 白色: 0, 0, 0 }, black: { 深灰: 30, 30, 30, 浅灰: 80, 80, 80, 深绿: 25, 53, 34, 浅绿: 249, 225, 149, 银色: 160, 160, 160, 白色: 230, 234, 235 }, yellow: { 暗黄: 218, 165, 32, 亮黄: 255, 215, 0, 深绿: 25, 53, 34, 浅绿: 249, 225, 149, 黑色: 6, 16, 8, 白色: 230, 234, 235 }, green: { 暗绿: 0, 100, 0, 亮绿: 34, 139, 34, 深绿: 25, 53, 34, 浅绿: 249, 225, 149, 黑色: 6, 16, 8, 白色: 230, 234, 235 } }; const DETECTION_COLORS { silver: 192, 192, 192, gold: 255, 215, 0 }; const REPLACE_COLORS { 深蓝: 22, 31, 125, 浅蓝: 93, 167, 227, 深红: 125, 22, 22, 浅红: 227, 93, 93, 深紫: 125, 22, 125, 浅紫: 227, 93, 227, 深绿: 25, 53, 34, 浅绿: 249, 225, 149, 黑色: 6, 16, 8, 白色: 230, 234, 235, 深灰: 100, 100, 100, 浅灰: 200, 200, 200, 白色(黑丝印): 0, 0, 0, 黑色(白板): 10, 10, 10, 深灰(黑板): 30, 30, 30, 浅灰(黑板): 80, 80, 80, 银色: 160, 160, 160, 暗黄: 218, 165, 32, 亮黄: 255, 215, 0, 暗绿: 0, 100, 0, 亮绿: 34, 139, 34, }; const RENAME_MAP { 正面铜皮层: 放在顶层, 白色: 放在顶层丝印层, 正面阻焊层: 放在顶层阻焊层, 深蓝: 放在底层注意镜像, 深红: 放在底层注意镜像, 深紫: 放在底层注意镜像, 深灰: 放在底层注意镜像, 暗黄: 放在底层注意镜像, 暗绿: 放在底层注意镜像, 背面阻焊层: 放在底层阻焊层注意镜像 }; class PCBImageProcessor { constructor() { this.image null; this.currentColorScheme {}; this.simplifiedImageData null; this.originalEditHistory ; this.simplifiedEditHistory ; this.maskList ; this.selectedOriginalColor null; this.selectedSimplifiedColor null; this.currentTolerance 30; this.detectionTolerance 25; this.brushSizeOriginal 10; this.brushSizeSimplified 10; this.isPaintingOriginalfalse; this.lastPointOriginal{x:0,y:0}; this.isPaintingSimplifiedfalse; this.lastPointSimplified{x:0,y:0}; this.isDrawingLassoOriginalfalse; this.isDrawingLassoSimplifiedfalse; this.lassoPointsOriginal; this.lassoPointsSimplified; this.toolOriginal brush; this.toolSimplified brush; this.surfaceFinish hasl; this.initElements(); this.setupEventListeners(); } initElements(){this.uploaderdocument.getElementById(uploader);this.dropAreadocument.getElementById(drop-area);this.originalCanvasdocument.getElementById(originalCanvas);this.simplifiedCanvasdocument.getElementById(simplifiedCanvas);this.originalPathPreviewdocument.getElementById(originalPathPreview);this.simplifiedPathPreviewdocument.getElementById(simplifiedPathPreview);this.masksContainerdocument.getElementById(masks);this.layersContainerdocument.getElementById(layers);this.toastdocument.getElementById(toast);this.imgInfodocument.getElementById(img-info);this.originalEditTooldocument.getElementById(originalEditTool);this.simplifiedEditTooldocument.getElementById(simplifiedEditTool);this.availableColorsOriginaldocument.getElementById(availableColorsOriginal);this.availableColorsSimplifieddocument.getElementById(availableColorsSimplified);this.undoOriginalBtndocument.getElementById(undoOriginalBtn);this.resetOriginalBtndocument.getElementById(resetOriginalBtn);this.nextStepBtndocument.getElementById(nextStepBtn);this.undoSimplifiedBtndocument.getElementById(undoSimplifiedBtn);this.resetSimplifiedBtndocument.getElementById(resetSimplifiedBtn);this.generateBtndocument.getElementById(generateBtn);this.maskContainerdocument.getElementById(maskContainer);this.layerContainerdocument.getElementById(layerContainer);this.previewContainerdocument.getElementById(previewContainer);this.toleranceSliderdocument.getElementById(toleranceSlider);this.toleranceValuedocument.getElementById(toleranceValue);this.detectionToleranceSliderdocument.getElementById(detectionToleranceSlider);this.detectionToleranceValuedocument.getElementById(detectionToleranceValue);this.brushSizeOriginalInputdocument.getElementById(brushSizeOriginal);this.brushSizeSimplifiedInputdocument.getElementById(brushSizeSimplified);this.downloadAllBtndocument.getElementById(downloadAllBtn);this.backToEditContainerdocument.getElementById(backToEditContainer);this.backToOriginalBtndocument.getElementById(backToOriginalBtn);this.backToSimplifiedBtndocument.getElementById(backToSimplifiedBtn);this.originalBrushBtndocument.getElementById(originalBrushBtn);this.originalLassoBtndocument.getElementById(originalLassoBtn);this.originalFillBtndocument.getElementById(originalFillBtn);this.simplifiedBrushBtndocument.getElementById(simplifiedBrushBtn);this.simplifiedLassoBtndocument.getElementById(simplifiedLassoBtn);this.simplifiedFillBtndocument.getElementById(simplifiedFillBtn);this.clearOriginalSelectionBtndocument.getElementById(clearOriginalSelectionBtn);this.clearSimplifiedSelectionBtndocument.getElementById(clearSimplifiedSelectionBtn);this.configSelectiondocument.getElementById(configSelection);} setupEventListeners(){this.dropArea.addEventListener(click,()>this.uploader.click());this.dropArea.addEventListener(dragover,e>{e.preventDefault();this.dropArea.classList.add(bg-blue-100);});this.dropArea.addEventListener(dragleave,()>this.dropArea.classList.remove(bg-blue-100));this.dropArea.addEventListener(drop,e>{e.preventDefault();this.dropArea.classList.remove(bg-blue-100);this.handleImageUpload(e.dataTransfer.files0);});this.uploader.addEventListener(change,e>this.handleImageUpload(e.target.files0));this.toleranceSlider.addEventListener(input,()>{this.currentToleranceparseInt(this.toleranceSlider.value);this.toleranceValue.textContentthis.currentTolerance;});this.detectionToleranceSlider.addEventListener(input,()>{this.detectionToleranceparseInt(this.detectionToleranceSlider.value);this.detectionToleranceValue.textContentthis.detectionTolerance;});this.detectionToleranceSlider.addEventListener(change,()>{if(this.simplifiedImageData){this.initialProcessPCB();}});this.brushSizeOriginalInput.addEventListener(input,()>{this.brushSizeOriginalparseInt(this.brushSizeOriginalInput.value)||10;});this.brushSizeSimplifiedInput.addEventListener(input,()>{this.brushSizeSimplifiedparseInt(this.brushSizeSimplifiedInput.value)||10;});this.undoOriginalBtn.addEventListener(click,()>this.undoOriginal());this.resetOriginalBtn.addEventListener(click,()>this.resetOriginal());this.nextStepBtn.addEventListener(click,()>this.nextStep());this.undoSimplifiedBtn.addEventListener(click,()>this.undoSimplified());this.resetSimplifiedBtn.addEventListener(click,()>this.resetSimplified());this.generateBtn.addEventListener(click,()>this.generate());this.downloadAllBtn.addEventListener(click,()>this.downloadAllAsZip());this.backToOriginalBtn.addEventListener(click,()>this.backToOriginal());this.backToSimplifiedBtn.addEventListener(click,()>this.backToSimplified());this.clearOriginalSelectionBtn.addEventListener(click,()>this.clearOriginalSelection());this.clearSimplifiedSelectionBtn.addEventListener(click,()>this.clearSimplifiedSelection());this.originalBrushBtn.addEventListener(click,()>this.setOriginalTool(TOOLS.BRUSH));this.originalLassoBtn.addEventListener(click,()>this.setOriginalTool(TOOLS.LASSO));this.originalFillBtn.addEventListener(click,()>this.setOriginalTool(TOOLS.FILL));this.simplifiedBrushBtn.addEventListener(click,()>this.setSimplifiedTool(TOOLS.BRUSH));this.simplifiedLassoBtn.addEventListener(click,()>this.setSimplifiedTool(TOOLS.LASSO));this.simplifiedFillBtn.addEventListener(click,()>this.setSimplifiedTool(TOOLS.FILL));document.querySelectorAll(inputnamemainColor).forEach(input>{input.addEventListener(change,()>{if(input.checked){this.currentColorSchemeCOLOR_SCHEMESinput.value;this.initialProcessPCB();}});});document.querySelectorAll(inputnamesurfaceFinish).forEach(input>{input.addEventListener(change,()>{if(input.checked){this.surfaceFinishinput.value;if(this.simplifiedImageData){this.initialProcessPCB();}}});});} getCanvasCoordinates(canvas,clientX,clientY){const rectcanvas.getBoundingClientRect();return{x:Math.floor((clientX-rect.left)*(canvas.width/rect.width)),y:Math.floor((clientY-rect.top)*(canvas.height/rect.height))};} setOriginalTool(tool){this.toolOriginaltool;this.originalBrushBtn.classList.toggle(active,toolTOOLS.BRUSH);this.originalLassoBtn.classList.toggle(active,toolTOOLS.LASSO);this.originalFillBtn.classList.toggle(active,toolTOOLS.FILL);this.originalCanvas.style.cursortoolTOOLS.FILL?cell:crosshair;this.clearOriginalSelectionBtn.classList.toggle(hidden,tool!TOOLS.LASSO);} setSimplifiedTool(tool){this.toolSimplifiedtool;this.simplifiedBrushBtn.classList.toggle(active,toolTOOLS.BRUSH);this.simplifiedLassoBtn.classList.toggle(active,toolTOOLS.LASSO);this.simplifiedFillBtn.classList.toggle(active,toolTOOLS.FILL);this.simplifiedCanvas.style.cursortoolTOOLS.FILL?cell:crosshair;this.clearSimplifiedSelectionBtn.classList.toggle(hidden,tool!TOOLS.LASSO);} clearOriginalSelection(){this.lassoPointsOriginal;this.originalPathPreview.getContext(2d).clearRect(0,0,this.originalPathPreview.width,this.originalPathPreview.height);} clearSimplifiedSelection(){this.lassoPointsSimplified;this.simplifiedPathPreview.getContext(2d).clearRect(0,0,this.simplifiedPathPreview.width,this.simplifiedPathPreview.height);} showToast(msg){this.toast.innerTextmsg;this.toast.style.opacity1;setTimeout(()>{this.toast.style.opacity0;},2000);} async handleImageUpload(file){this.showToast(正在压缩图片...);if(!file.type.startsWith(image/)){this.showToast(请上传有效的图像文件);return;}try{const compressedawait imageCompression(file,{maxSizeMB:2,maxWidthOrHeight:1500,useWebWorker:true});const readernew FileReader();reader.onloade>{this.imagenew Image();this.image.onload()>{this.originalCanvas.widththis.simplifiedCanvas.widththis.image.width;this.originalCanvas.heightthis.simplifiedCanvas.heightthis.image.height;this.originalPathPreview.widththis.simplifiedPathPreview.widththis.image.width;this.originalPathPreview.heightthis.simplifiedPathPreview.heightthis.image.height;this.originalCanvas.getContext(2d).drawImage(this.image,0,0);this.imgInfo.innerText`尺寸:${this.image.width}x${this.image.height},大小:${(compressed.size/1024/1024).toFixed(2)} MB`;this.originalEditTool.classList.remove(hidden);this.setupOriginalColorPicker();this.setupCanvasHandlers(original);this.originalEditHistory;this.simplifiedEditHistory;this.maskList;this.maskContainer.classList.add(hidden);this.layerContainer.classList.add(hidden);this.previewContainer.classList.add(hidden);this.backToEditContainer.classList.add(hidden);this.configSelection.classList.add(hidden);this.simplifiedEditTool.classList.add(hidden);this.simplifiedImageDatanull;this.setOriginalTool(TOOLS.BRUSH);this.setSimplifiedTool(TOOLS.BRUSH);};this.image.srce.target.result;};reader.readAsDataURL(compressed);}catch(error){this.showToast(图片处理出错: +error.message);}} setupOriginalColorPicker(){this.availableColorsOriginal.innerHTML;Object.entries(REPLACE_COLORS).forEach((name,color)>{const colorDivdocument.createElement(div);colorDiv.classNamecolor-picker;colorDiv.style.backgroundColor`rgb(${color.join(,)})`;colorDiv.titlename;colorDiv.addEventListener(click,()>{document.querySelectorAll(#availableColorsOriginal .color-picker).forEach(el>el.classList.remove(selected));colorDiv.classList.add(selected);this.selectedOriginalColorcolor;});this.availableColorsOriginal.appendChild(colorDiv);});const firstColorPickerthis.availableColorsOriginal.querySelector(.color-picker);if(firstColorPicker)firstColorPicker.click();} setupCanvasHandlers(type){const isOriginaltypeoriginal;const canvasisOriginal?this.originalCanvas:this.simplifiedCanvas;const previewCanvasisOriginal?this.originalPathPreview:this.simplifiedPathPreview;const getStateprop>thisprop+(isOriginal?Original:Simplified);const setState(prop,value)>{thisprop+(isOriginal?Original:Simplified)value;};canvas.onmousedowne>{const selectedColorisOriginal?this.selectedOriginalColor:this.selectedSimplifiedColor;if(!selectedColor){this.showToast(请先选择一种颜色);return;}isOriginal?this.saveOriginalCanvasState():this.saveSimplifiedCanvasState();const{x,y}this.getCanvasCoordinates(canvas,e.clientX,e.clientY);const currentToolgetState(tool);if(currentToolTOOLS.BRUSH){setState(isPainting,true);setState(lastPoint,{x,y});isOriginal?this.paintOriginalBrush(x,y,x,y):this.paintSimplifiedBrush(x,y,x,y);}else if(currentToolTOOLS.LASSO){setState(isDrawingLasso,true);setState(lassoPoints,x,y);const ctxpreviewCanvas.getContext(2d);ctx.clearRect(0,0,previewCanvas.width,previewCanvas.height);ctx.strokeStylergba(0,0,255,0.5);ctx.lineWidth2;ctx.beginPath();ctx.moveTo(x,y);ctx.stroke();}else if(currentToolTOOLS.FILL){isOriginal?this.fillOriginalArea(x,y):this.fillSimplifiedArea(x,y);}};canvas.onmousemovee>{const{x,y}this.getCanvasCoordinates(canvas,e.clientX,e.clientY);if(getState(isPainting)){const lastPointgetState(lastPoint);isOriginal?this.paintOriginalBrush(lastPoint.x,lastPoint.y,x,y):this.paintSimplifiedBrush(lastPoint.x,lastPoint.y,x,y);setState(lastPoint,{x,y});}else if(getState(isDrawingLasso)){const lassoPointsgetState(lassoPoints);lassoPoints.push(x,y);const ctxpreviewCanvas.getContext(2d);ctx.clearRect(0,0,previewCanvas.width,previewCanvas.height);ctx.beginPath();ctx.moveTo(lassoPoints00,lassoPoints01);for(let i1;ilassoPoints.length;i++){ctx.lineTo(lassoPointsi0,lassoPointsi1);}ctx.stroke();}};const stopDrawing()>{setState(isPainting,false);if(getState(isDrawingLasso)){setState(isDrawingLasso,false);if(getState(lassoPoints).length>2){isOriginal?this.modifyOriginalLassoArea():this.modifySimplifiedLassoArea();}isOriginal?this.clearOriginalSelection():this.clearSimplifiedSelection();}if(!isOriginal){this.simplifiedImageDatathis.simplifiedCanvas.getContext(2d).getImageData(0,0,this.simplifiedCanvas.width,this.simplifiedCanvas.height);}};canvas.onmouseupstopDrawing;canvas.onmouseleavestopDrawing;} paintOriginalBrush(x1,y1,x2,y2){const ctxthis.originalCanvas.getContext(2d);ctx.strokeStyle`rgb(${this.selectedOriginalColor.join(,)})`;ctx.lineWidththis.brushSizeOriginal;ctx.lineCapround;ctx.lineJoinround;ctx.beginPath();ctx.moveTo(x1,y1);ctx.lineTo(x2,y2);ctx.stroke();} modifyOriginalLassoArea(){const ctxthis.originalCanvas.getContext(2d);ctx.fillStyle`rgb(${this.selectedOriginalColor.join(,)})`;ctx.beginPath();ctx.moveTo(this.lassoPointsOriginal00,this.lassoPointsOriginal01);for(let i1;ithis.lassoPointsOriginal.length;i++){ctx.lineTo(this.lassoPointsOriginali0,this.lassoPointsOriginali1);}ctx.closePath();ctx.fill();} fillOriginalArea(x,y){const ctxthis.originalCanvas.getContext(2d);const targetColorctx.getImageData(x,y,1,1).data;const fillColorthis.selectedOriginalColor;if(fillColor0targetColor0&&fillColor1targetColor1&&fillColor2targetColor2)return;const imageDatactx.getImageData(0,0,this.originalCanvas.width,this.originalCanvas.height);const visitednew Uint8Array(imageData.width,imageData.height);const queuex,y;while(queue.length>0){constcx,cyqueue.shift();if(cx0||cx>imageData.width||cy0||cy>imageData.height||visitedcy*imageData.width+cx)continue;const i(cy*imageData.width+cx)*4;constr,g,bimageData.datai,imageData.datai+1,imageData.datai+2;if(Math.hypot(r-targetColor0,g-targetColor1,b-targetColor2)this.currentTolerance){imageData.data.set(fillColor,i);visitedcy*imageData.width+cx1;queue.push(cx+1,cy,cx-1,cy,cx,cy+1,cx,cy-1);}}ctx.putImageData(imageData,0,0);} saveOriginalCanvasState(){this.originalEditHistory.push(this.originalCanvas.getContext(2d).getImageData(0,0,this.originalCanvas.width,this.originalCanvas.height));this.undoOriginalBtn.disabledfalse;} undoOriginal(){if(this.originalEditHistory.length>0){this.originalCanvas.getContext(2d).putImageData(this.originalEditHistory.pop(),0,0);this.undoOriginalBtn.disabledthis.originalEditHistory.length0;}} resetOriginal(){if(confirm(确定要重置所有修改吗?)){this.originalCanvas.getContext(2d).drawImage(this.image,0,0);this.originalEditHistory;this.undoOriginalBtn.disabledtrue;this.clearOriginalSelection();}} nextStep(){this.configSelection.classList.remove(hidden);this.originalEditTool.classList.add(hidden);if(!document.querySelector(inputnamemainColor:checked)){document.querySelector(inputnamemainColorvalueblue).click();}} setupSimplifiedColorPicker(){this.availableColorsSimplified.innerHTML;Object.entries(this.currentColorScheme).forEach((name,color)>{const colorDivdocument.createElement(div);colorDiv.classNamecolor-picker;colorDiv.style.backgroundColor`rgb(${color.join(,)})`;colorDiv.titlename;colorDiv.addEventListener(click,()>{document.querySelectorAll(#availableColorsSimplified .color-picker).forEach(el>el.classList.remove(selected));colorDiv.classList.add(selected);this.selectedSimplifiedColorname;});this.availableColorsSimplified.appendChild(colorDiv);});const firstColorPickerthis.availableColorsSimplified.querySelector(.color-picker);if(firstColorPicker)firstColorPicker.click();} paintSimplifiedBrush(x1,y1,x2,y2){const ctxthis.simplifiedCanvas.getContext(2d);const colorthis.currentColorSchemethis.selectedSimplifiedColor;ctx.strokeStyle`rgb(${color.join(,)})`;ctx.lineWidththis.brushSizeSimplified;ctx.lineCapround;ctx.lineJoinround;ctx.beginPath();ctx.moveTo(x1,y1);ctx.lineTo(x2,y2);ctx.stroke();} modifySimplifiedLassoArea(){const ctxthis.simplifiedCanvas.getContext(2d);const colorthis.currentColorSchemethis.selectedSimplifiedColor;ctx.fillStyle`rgb(${color.join(,)})`;ctx.beginPath();ctx.moveTo(this.lassoPointsSimplified00,this.lassoPointsSimplified01);for(let i1;ithis.lassoPointsSimplified.length;i++){ctx.lineTo(this.lassoPointsSimplifiedi0,this.lassoPointsSimplifiedi1);}ctx.closePath();ctx.fill();} fillSimplifiedArea(x,y){const ctxthis.simplifiedCanvas.getContext(2d);const targetColorctx.getImageData(x,y,1,1).data;const newColorthis.currentColorSchemethis.selectedSimplifiedColor;if(targetColor0newColor0&&targetColor1newColor1&&targetColor2newColor2)return;const imageDatactx.getImageData(0,0,this.simplifiedCanvas.width,this.simplifiedCanvas.height);const queuex,y;const visitednew Set(`${x},${y}`);while(queue.length>0){constcx,cyqueue.shift();const i(cy*imageData.width+cx)*4;if(imageData.dataitargetColor0&&imageData.datai+1targetColor1&&imageData.datai+2targetColor2){imageData.data.set(newColor,i);const neighborscx+1,cy,cx-1,cy,cx,cy+1,cx,cy-1;for(constnx,nyof neighbors){if(nx>0&&nximageData.width&&ny>0&&nyimageData.height&&!visited.has(`${nx},${ny}`)){visited.add(`${nx},${ny}`);queue.push(nx,ny);}}}}ctx.putImageData(imageData,0,0);} saveSimplifiedCanvasState(){this.simplifiedEditHistory.push(this.simplifiedCanvas.getContext(2d).getImageData(0,0,this.simplifiedCanvas.width,this.simplifiedCanvas.height));this.undoSimplifiedBtn.disabledfalse;} undoSimplified(){if(this.simplifiedEditHistory.length>0){const prevStatethis.simplifiedEditHistory.pop();this.simplifiedCanvas.getContext(2d).putImageData(prevState,0,0);this.simplifiedImageDataprevState;this.undoSimplifiedBtn.disabledthis.simplifiedEditHistory.length0;}} resetSimplified(){if(confirm(确定要重置所有修改吗?)){this.initialProcessPCB();this.simplifiedEditHistory;this.undoSimplifiedBtn.disabledtrue;this.clearSimplifiedSelection();}} getExposedCopperColorName(){return document.querySelector(inputnamemainColor:checked).valueblack?银色:黑色;} initialProcessPCB(){ this.showToast(正在生成简化图像...); document.body.style.cursor wait; this.generateBtn.disabled true; setTimeout(() > { const w this.originalCanvas.width, h this.originalCanvas.height; const data this.originalCanvas.getContext(2d).getImageData(0, 0, w, h); const simplifiedCtx this.simplifiedCanvas.getContext(2d); this.simplifiedImageData simplifiedCtx.createImageData(w, h); const exposedCopperName this.getExposedCopperColorName(); const exposedCopperColor this.currentColorSchemeexposedCopperName; const detectionColor this.surfaceFinish enig ? DETECTION_COLORS.gold : DETECTION_COLORS.silver; const normalizedTolerance this.detectionTolerance / 100; const toleranceThreshold (normalizedTolerance ** 2) * MAX_COLOR_DISTANCE; for (let i 0; i data.data.length; i + 4) { const pixel data.datai, data.datai + 1, data.datai + 2; let override false; const detectionDist Math.hypot(detectionColor0 - pixel0, detectionColor1 - pixel1, detectionColor2 - pixel2); if (detectionDist toleranceThreshold) { this.simplifiedImageData.data.set(...exposedCopperColor, 255, i); override true; } if (!override) { let closest 白色, minDist Infinity; for (const name, color of Object.entries(this.currentColorScheme)) { const d Math.hypot(color0 - pixel0, color1 - pixel1, color2 - pixel2); if (d minDist) { minDist d; closest name; } } this.simplifiedImageData.data.set(...this.currentColorSchemeclosest, 255, i); } } simplifiedCtx.putImageData(this.simplifiedImageData, 0, 0); this.setupSimplifiedColorPicker(); this.simplifiedEditTool.classList.remove(hidden); this.setupCanvasHandlers(simplified); document.body.style.cursor default; this.generateBtn.disabled false; this.showToast(简化图像已生成); }, 10);} generate(){this.showToast(正在生成遮罩和图层...);document.body.style.cursorwait;this.generateBtn.disabledtrue;this.backToOriginalBtn.disabledtrue;this.backToSimplifiedBtn.disabledtrue;setTimeout(()>{this.simplifiedEditTool.classList.add(hidden);this.maskContainer.classList.remove(hidden);this.layerContainer.classList.remove(hidden);this.previewContainer.classList.remove(hidden);this.backToEditContainer.classList.remove(hidden);this.processPCB();document.body.style.cursordefault;this.generateBtn.disabledfalse;this.backToOriginalBtn.disabledfalse;this.backToSimplifiedBtn.disabledfalse;this.showToast(处理完成);},10);} backToOriginal(){this.maskContainer.classList.add(hidden);this.layerContainer.classList.add(hidden);this.previewContainer.classList.add(hidden);this.backToEditContainer.classList.add(hidden);this.originalEditTool.classList.remove(hidden);this.showToast(已返回原图编辑模式);} backToSimplified(){this.maskContainer.classList.add(hidden);this.layerContainer.classList.add(hidden);this.previewContainer.classList.add(hidden);this.backToEditContainer.classList.add(hidden);this.simplifiedEditTool.classList.remove(hidden);this.showToast(已返回简化图编辑模式);} processPCB(){const wthis.simplifiedCanvas.width,hthis.simplifiedCanvas.height;const datathis.simplifiedImageData;this.maskList;const buffers{};for(const name in this.currentColorScheme){buffersnamenew Uint8ClampedArray(w*h*4);}for(let i0;idata.data.length;i+4){const pixeldata.datai,data.datai+1,data.datai+2;for(constname,colorof Object.entries(this.currentColorScheme)){if(pixel0color0&&pixel1color1&&pixel2color2){buffersname.set(...color,255,i);break;}}}this.masksContainer.innerHTML;for(constname,bufferof Object.entries(buffers)){const canvasdocument.createElement(canvas);canvas.widthw;canvas.heighth;const ctxcanvas.getContext(2d);ctx.putImageData(new ImageData(buffer,w,h),0,0);this.addCornerMarkers(ctx,w,h);const imgdocument.createElement(img);img.srccanvas.toDataURL();img.classNamew-24 h-24 rounded border hover:shadow cursor-pointer;img.titlename;img.onclick()>this.downloadImage(img.src,`${name}_mask.png`);this.masksContainer.appendChild(img);this.maskList.push({name,canvas,dataURL:img.src});}this.generateLayers();this.generatePreview();} addCornerMarkers(ctx,width,height){ctx.fillStylered;ctx.fillRect(0,0,1,1);ctx.fillRect(width-1,0,1,1);ctx.fillRect(0,height-1,1,1);ctx.fillRect(width-1,height-1,1,1);} generateLayers(){const wthis.simplifiedCanvas.width,hthis.simplifiedCanvas.height;this.layersContainer.innerHTML;const findMaskCanvasname>this.maskList.find(m>m.namename)?.canvas;const combinenames>{const canvasdocument.createElement(canvas);canvas.widthw;canvas.heighth;const ctxcanvas.getContext(2d);names.forEach(n>{const mfindMaskCanvas(n);if(m)ctx.drawImage(m,0,0);});return canvas;};const currentSchemeValuedocument.querySelector(inputnamemainColor:checked).value;const exposedCopperNamethis.getExposedCopperColorName();let mainColorName;switch(currentSchemeValue){caseblack:mainColorName浅灰;break;casewhite:mainColorName浅灰;break;caseyellow:mainColorName亮黄;break;casegreen:mainColorName亮绿;break;casered:mainColorName浅红;break;casepurple:mainColorName浅紫;break;default:mainColorName浅蓝;}const layers{正面阻焊层:combine(深绿,浅绿,exposedCopperName),正面铜皮层:combine(mainColorName,exposedCopperName),背面阻焊层:combine(浅绿)};for(constlayerName,canvasof Object.entries(layers)){const imgdocument.createElement(img);img.srccanvas.toDataURL();img.classNamew-24 h-24 rounded border hover:shadow cursor-pointer;img.titlelayerName;img.onclick ()>this.downloadImage(img.src,`${layerName}.png`);this.layersContainer.appendChild(img);this.maskList.push({name:layerName,canvas,dataURL:img.src});}} generatePreview(){const wthis.simplifiedCanvas.width,hthis.simplifiedCanvas.height;const datathis.simplifiedImageData;const previewCanvasdocument.getElementById(previewCanvas);previewCanvas.widthw;previewCanvas.heighth;const previewCtxpreviewCanvas.getContext(2d);const previewDatapreviewCtx.createImageData(w,h);const exposedCopperNamethis.getExposedCopperColorName();const exposedCopperColorthis.currentColorSchemeexposedCopperName;const previewMetalColorthis.surfaceFinishenig?218,175,56:160,160,160;for(let i0;idata.data.length;i+4){constr,g,bdata.datai,data.datai+1,data.datai+2;if(rexposedCopperColor0&&gexposedCopperColor1&&bexposedCopperColor2){previewData.data.set(...previewMetalColor,255,i);}else{previewData.data.set(data.data.slice(i,i+4),i);}}previewCtx.putImageData(previewData,0,0);const previewImgContainerdocument.getElementById(preview);previewImgContainer.innerHTML;const previewImgdocument.createElement(img);previewImg.srcpreviewCanvas.toDataURL();previewImg.classNamew-24 h-24 rounded border hover:shadow cursor-pointer;previewImg.title实物预览图;previewImg.onclick ()>this.downloadImage(previewImg.src,实物预览图.png);previewImgContainer.appendChild(previewImg);this.maskList.push({name:实物预览图,canvas:previewCanvas,dataURL:previewImg.src});} downloadImage(url,filename){const adocument.createElement(a);a.hrefurl;a.downloadfilename;a.click();} downloadAllAsZip(){const customFilenamedocument.getElementById(customFilename).value.trim()||pcb灯光画;this.showToast(正在打包下载...);const zipnew JSZip();this.maskList.forEach(({name,dataURL})>{const renameRENAME_MAPname||name;zip.file(`${customFilename}-${rename}.png`,dataURL.split(,)1,{base64:true});});zip.generateAsync({type:blob}).then(blob>{saveAs(blob,`${customFilename}.zip`);setTimeout(()>this.showToast(下载完成!),1000);});} } document.addEventListener(DOMContentLoaded, () > new PCBImageProcessor()); /script>/body>/html>
View on OTX
|
View on ThreatMiner
Please enable JavaScript to view the
comments powered by Disqus.
Data with thanks to
AlienVault OTX
,
VirusTotal
,
Malwr
and
others
. [
Sitemap
]