实现透明度混合算法
This commit is contained in:
parent
b21502526e
commit
b142e5d906
@ -24,7 +24,6 @@
|
||||
return [ netJson, skinBinary ];
|
||||
}
|
||||
window.moduleName = 'IF_ID';
|
||||
// window.avoidWasm = 'avoid.wasm';
|
||||
</script>
|
||||
</head>
|
||||
|
||||
|
@ -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('</script>')
|
||||
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)
|
||||
fp.write('\n'.join(new_html))
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user