diff --git a/public/index.html b/public/index.html index 8760ed8..1a0c6dd 100644 --- a/public/index.html +++ b/public/index.html @@ -24,7 +24,6 @@ return [ netJson, skinBinary ]; } window.moduleName = 'IF_ID'; - // window.avoidWasm = 'avoid.wasm'; diff --git a/scripts/vscode-package.py b/scripts/vscode-package.py index fe1da99..0d29051 100644 --- a/scripts/vscode-package.py +++ b/scripts/vscode-package.py @@ -20,6 +20,17 @@ for file in os.listdir('dist'): with open('./dist/index.html', 'r', encoding='utf-8') as fp: html = fp.read() + index = html.index('') + html = html[:index] + '\n' + html[index:] + + new_html = [] + for line in html.split('\n'): + if 'const inputVcdFile =' in line: + new_html.append("const inputVcdFile = 'test.json';") + elif 'window.moduleName' in line: + new_html.append("window.moduleName = 'test.module';") + else: + new_html.append(line.strip()) with open('./dist/index.html', 'w', encoding='utf-8') as fp: - fp.write(html) \ No newline at end of file + fp.write('\n'.join(new_html)) \ No newline at end of file diff --git a/src/api/index.js b/src/api/index.js index 73bfd46..9edbcb6 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -1,5 +1,5 @@ import { colorManager } from "@/components/setting/color"; -import { increaseBrightness, lowerBrightness, parseColor } from "@/hook/color"; +import { increaseBrightness, isLightColorTheme, lowerBrightness, MacroColor, parseColor } from "@/hook/color"; import { globalLookup } from "@/hook/global"; import { pinkLog } from "@/hook/utils"; import axios from 'axios'; @@ -10,38 +10,53 @@ pinkLog('digital-netlist-render mode: ' + mode); let vscode = window.acquireVsCodeApi === undefined ? undefined : acquireVsCodeApi(); -function getColorFromMacro(rootStyles, optionName, theme) { +function getColorFromMacro(rootStyles, optionName, theme, isLight) { if (theme === 'dark') { return rootStyles.getPropertyValue(optionName); } else { + // 亮色模式,pdf 类型的导出一定是这个颜色, + // 此时需要对特殊的几类颜色进行处理 + // 如果此时的颜色主题本来就是偏亮色的,直接返回如下几个的颜色 + switch (optionName) { case '--foreground': case '--wire-color': case '--cross-dot-color': - return '#2D323B'; + if (!isLight) { + return '#2D323B'; + } } + const color = rootStyles.getPropertyValue(optionName); if (!color) { return color; } const rgb = parseColor(color); + const { r, g, b } = increaseBrightness(rgb, 10); return `rgb(${r}, ${g}, ${b})`; } } -function makeStyleString(config = {}) { - const theme = config.theme || 'dark'; +/** + * + * @param {import("@/hook/color").GetColorOption} option + * @returns + */ +function makeStyleString(option = {}) { // 加入全局颜色宏 const colorMacros = []; - const rootStyles = getComputedStyle(document.documentElement); + const macroColor = new MacroColor({ + BaseForegroundColorMacroName: '--foreground', + BaseBackgroundColorMacroName: '--background' + }); for (const item of colorManager.generals) { const colorOption = `--${item.type}-color`; const colorFillOption = `--${item.type}-fill-color`; - const color = getColorFromMacro(rootStyles, colorOption, theme); - const colorFill = getColorFromMacro(rootStyles, colorFillOption, theme); + const color = macroColor.getColor(colorOption, option); + const colorFill = macroColor.getColor(colorFillOption, option); if (color) { colorMacros.push(`${colorOption}: ${color};`); } @@ -54,8 +69,8 @@ function makeStyleString(config = {}) { const colorOption = `--${item.type}-color`; const colorFillOption = `--${item.type}-fill-color`; - const color = getColorFromMacro(rootStyles, colorOption, theme); - const colorFill = getColorFromMacro(rootStyles, colorFillOption, theme); + const color = macroColor.getColor(colorOption, option); + const colorFill = macroColor.getColor(colorFillOption, option); if (color) { colorMacros.push(`${colorOption}: ${color};`); } @@ -71,7 +86,7 @@ function makeStyleString(config = {}) { ]; for (const setting of globalSetting) { - const color = getColorFromMacro(rootStyles, setting, theme); + const color = macroColor.getColor(setting, option); if (color) { colorMacros.push(`${setting}: ${color};`); } @@ -81,11 +96,16 @@ function makeStyleString(config = {}) { return styleString; } -function getCompressedSvgBuffer(config = {}) { +/** + * + * @param {import("@/hook/color").GetColorOption} option + * @returns + */ +function getCompressedSvgBuffer(option = {}) { const selection = globalLookup.netlistRender.selection; const node = selection.node(); - const styleString = makeStyleString(config); + const styleString = makeStyleString(option); const style = document.createElement('style'); style.textContent = styleString; node.insertBefore(style, node.firstChild); @@ -120,7 +140,7 @@ export async function saveAsSvg() { } export async function saveAsPdf() { - const svgBuffer = getCompressedSvgBuffer({ theme: 'light' }); + const svgBuffer = getCompressedSvgBuffer({ mode: 'pdf' }); const moduleName = globalLookup.topModuleName; const node = globalLookup.netlistRender.selection.node(); diff --git a/src/hook/color.js b/src/hook/color.js index 8b5650e..9fa50a0 100644 --- a/src/hook/color.js +++ b/src/hook/color.js @@ -3,6 +3,7 @@ * @property {number} r * @property {number} g * @property {number} b + * @property {number} [a] 透明度,值置于 0 - 1 之间 */ /** @@ -29,7 +30,8 @@ export function parseColor(colorString) { const r = parseInt(matches[1], 10); const g = parseInt(matches[2], 10); const b = parseInt(matches[3], 10); - return { r, g, b }; + const a = parseFloat(matches[4]); + return { r, g, b, a }; } } // 检查是否是 RGB 颜色 @@ -111,4 +113,115 @@ export function isLightColorTheme(r, g, b) { b = gammaCorrected(b); const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b; return luminance > 0.5; +} + +/** + * @description 导出为 rgb css 样式的字符串 + * @param {RgbColor} rgb + */ +export function toRgbCssString(rgb) { + const r = rgb.r; + const g = rgb.g; + const b = rgb.b; + return `rgb(${r}, ${g}, ${b})`; +} + +/** + * @description 导出为 rgba css 样式的字符串 + * @param {RgbColor} rgb + */ +export function toRgbaCssString(rgb) { + const r = rgb.r; + const g = rgb.g; + const b = rgb.b; + const a = rgb.a; + return `rgb(${r}, ${g}, ${b}, ${a})`; +} + +/** + * @typedef ComputedColorOption + * @property {string} BaseForegroundColorMacroName + * @property {string} BaseBackgroundColorMacroName + */ + +/** + * @typedef GetColorOption + * @property {'pdf' | 'svg'} mode + */ + +export class MacroColor { + /** + * + * @param {ComputedColorOption} option + */ + constructor(option = {}) { + this.option = option; + this.rootStyles = getComputedStyle(document.documentElement); + + const foregroundColorString = this.rootStyles.getPropertyValue(option.BaseForegroundColorMacroName || '--foreground'); + const backgroundColorString = this.rootStyles.getPropertyValue(option.BaseBackgroundColorMacroName || '--background'); + const foregroundColor = parseColor(foregroundColorString); + const backgroundColor = parseColor(backgroundColorString); + // 通过背景颜色来判断当前颜色主题的类别 + const isLight = isLightColorTheme(backgroundColor.r, backgroundColor.g, backgroundColor.b); + + /** + * @type {'light' | 'dark'} + */ + this.theme = isLight ? 'light' : 'dark'; + + this.foregroundColor = foregroundColor; + this.backgroundColor = backgroundColor; + } + + /** + * + * @param {string} macroName + * @param {GetColorOption} option + */ + getColor(macroName, option = {}) { + const theme = this.theme; + const rootStyles = this.rootStyles; + const mode = option.mode || 'svg'; + + if (mode === 'svg') { + // svg 模式下,导出的效果和 webview 渲染效果基本一致,直接导出即可 + return rootStyles.getPropertyValue(macroName); + } + + // pdf 模式需要对 黑色主题 的几个特殊颜色进行一定的处理,并对所有透明颜色进行混合处理 + switch (macroName) { + case '--foreground': + case '--wire-color': + case '--cross-dot-color': + if (theme === 'dark') { + return '#2D323B'; + } + } + + const colorString = rootStyles.getPropertyValue(macroName); + if (!colorString) { + // 如果 macroName 不存在,那么此时的 colorString 是空的 + return colorString; + } + const color = parseColor(colorString); + if (!color.a) { + // 不具有透明通道,在 pdf 中渲染效果和 svg 中一致,直接返回即可 + return toRgbCssString(color); + } + + // 透明度插值公式为 c = c_f * \alpha + c_b * (1 - \alpha) + // c_f: 前景颜色 + // c_b: 背景颜色 + // alpha: 透明度 + // 该操作是 channel-wise 的 + const mixedBg = parseColor('#ffffff'); + const mixedColor = { r: 0, g: 0, b: 0 }; + const alpha = color.a; + mixedColor.r = color.r * alpha + mixedBg.r * (1 - alpha); + mixedColor.g = color.g * alpha + mixedBg.g * (1 - alpha); + mixedColor.b = color.b * alpha + mixedBg.b * (1 - alpha); + + return toRgbCssString(mixedColor); + } } \ No newline at end of file