refactor: remove Chinese, add build obfuscation, polish README
Some checks failed
Publish to npm / publish (push) Has been cancelled
Some checks failed
Publish to npm / publish (push) Has been cancelled
- Remove all Chinese characters from src/, example/, ios/, patches/, tests/ - Add esbuild-based build obfuscation (minify + identifier mangle + no sourcemaps) - Drop src/ from npm publish, only ship minified dist/ - Remove source maps and declaration maps from build output - Add README icons and visual polish throughout - Fix broken Table of Contents anchor links in README Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
843544b9e8
commit
8bc66a4ee9
@ -24,6 +24,7 @@ tsconfig.build.json
|
|||||||
.cursor/
|
.cursor/
|
||||||
agent-transcripts/
|
agent-transcripts/
|
||||||
assets/
|
assets/
|
||||||
|
scripts/
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|||||||
22
App.tsx
22
App.tsx
@ -1,9 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* Mask Segment Demo App
|
* Mask Segment Demo App
|
||||||
* 基于 OpenCV + Skia 的掩码分区交互画布
|
* Mask segmentation canvas based on OpenCV + Skia
|
||||||
*
|
*
|
||||||
* 注意:这是库开发自测用的 Demo,直接引用 ./src。
|
* Note: This is a demo for library development testing, directly referencing ./src.
|
||||||
* 业务项目集成演示请参考 example/ 目录(使用公开包名导入)。
|
* For business project integration demonstration, please refer to the example/ directory (import using the public package name).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
@ -37,15 +37,15 @@ function formatWatchStatus(state: MaskSegmentWatchState | ''): string {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
if (state === 'interactive') {
|
if (state === 'interactive') {
|
||||||
return '可上色(轮廓加载中…)';
|
return 'Colorable (outline loading...)';
|
||||||
}
|
}
|
||||||
if (state === 'mask_paths_ready') {
|
if (state === 'mask_paths_ready') {
|
||||||
return '就绪';
|
return 'Ready';
|
||||||
}
|
}
|
||||||
if (state === 'error') {
|
if (state === 'error') {
|
||||||
return '失败';
|
return 'Failed';
|
||||||
}
|
}
|
||||||
return `加载中:${state}`;
|
return `Loading: ${state}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function App(): React.JSX.Element {
|
function App(): React.JSX.Element {
|
||||||
@ -103,8 +103,8 @@ function App(): React.JSX.Element {
|
|||||||
<>
|
<>
|
||||||
{watchState ? (
|
{watchState ? (
|
||||||
<Text style={styles.watchText}>
|
<Text style={styles.watchText}>
|
||||||
状态: {formatWatchStatus(watchState)}
|
Status: {formatWatchStatus(watchState)}
|
||||||
{isFullyReady ? ' · 轮播虚线已就绪' : null}
|
{isFullyReady ? ' · Carousel dashed line ready' : null}
|
||||||
</Text>
|
</Text>
|
||||||
) : null}
|
) : null}
|
||||||
<View style={styles.canvasHost}>
|
<View style={styles.canvasHost}>
|
||||||
@ -153,14 +153,14 @@ function App(): React.JSX.Element {
|
|||||||
</View>
|
</View>
|
||||||
{sessionDraft ? (
|
{sessionDraft ? (
|
||||||
<Text style={styles.sessionText}>
|
<Text style={styles.sessionText}>
|
||||||
已恢复 MMKV 草稿({sessionDraft.painted.length} 区域)
|
Restored MMKV draft ({sessionDraft.painted.length} regions)
|
||||||
</Text>
|
</Text>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<View style={styles.center}>
|
<View style={styles.center}>
|
||||||
<ActivityIndicator size="large" color="#333" />
|
<ActivityIndicator size="large" color="#333" />
|
||||||
<Text style={styles.loadingText}>加载健身房测试图…</Text>
|
<Text style={styles.loadingText}>Loading gym test image...</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
|
|||||||
@ -128,7 +128,7 @@ test('splitWalls:true splits wall into wall-1 and wall-2 by chroma', () => {
|
|||||||
const cols = 24;
|
const cols = 24;
|
||||||
const rows = 12;
|
const rows = 12;
|
||||||
const base = buildSyntheticWallResult(cols, rows);
|
const base = buildSyntheticWallResult(cols, rows);
|
||||||
// 左蓝右暖色(色度差异明显)
|
// Left blue, right warm (obvious chroma difference)
|
||||||
const origin = buildSplitOrigin(cols, rows, [220, 50, 30], [50, 200, 240]);
|
const origin = buildSplitOrigin(cols, rows, [220, 50, 30], [50, 200, 240]);
|
||||||
|
|
||||||
setMaskSegmentRuntimeConfig({
|
setMaskSegmentRuntimeConfig({
|
||||||
@ -163,7 +163,7 @@ test('same-color wall with lighting gradient stays one region', () => {
|
|||||||
for (let x = 0; x < cols; x++) {
|
for (let x = 0; x < cols; x++) {
|
||||||
const t = x / Math.max(1, cols - 1);
|
const t = x / Math.max(1, cols - 1);
|
||||||
const i = (y * cols + x) * 3;
|
const i = (y * cols + x) * 3;
|
||||||
// 同一蓝色墙面,从左暗阴影到右亮部
|
// Same blue wall, from dark shadow on left to bright area on right
|
||||||
origin[i] = Math.round(160 + t * 70);
|
origin[i] = Math.round(160 + t * 70);
|
||||||
origin[i + 1] = Math.round(70 + t * 50);
|
origin[i + 1] = Math.round(70 + t * 50);
|
||||||
origin[i + 2] = Math.round(40 + t * 30);
|
origin[i + 2] = Math.round(40 + t * 30);
|
||||||
|
|||||||
1
dist/components/MaskSegmentCanvas.d.ts
vendored
1
dist/components/MaskSegmentCanvas.d.ts
vendored
@ -3,4 +3,3 @@ import type { MaskSegmentCanvasProps, MaskSegmentCanvasRef } from './MaskSegment
|
|||||||
export type { MaskSegmentCanvasProps, MaskSegmentCanvasRef, MaskSegmentSession, MaskSegmentWatchState, PaintedRegionRecord, BgrColor, MaskSemanticColor, } from './MaskSegmentCanvas.types';
|
export type { MaskSegmentCanvasProps, MaskSegmentCanvasRef, MaskSegmentSession, MaskSegmentWatchState, PaintedRegionRecord, BgrColor, MaskSemanticColor, } from './MaskSegmentCanvas.types';
|
||||||
declare const MaskSegmentCanvas: React.ForwardRefExoticComponent<MaskSegmentCanvasProps & React.RefAttributes<MaskSegmentCanvasRef>>;
|
declare const MaskSegmentCanvas: React.ForwardRefExoticComponent<MaskSegmentCanvasProps & React.RefAttributes<MaskSegmentCanvasRef>>;
|
||||||
export default MaskSegmentCanvas;
|
export default MaskSegmentCanvas;
|
||||||
//# sourceMappingURL=MaskSegmentCanvas.d.ts.map
|
|
||||||
1
dist/components/MaskSegmentCanvas.d.ts.map
vendored
1
dist/components/MaskSegmentCanvas.d.ts.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"MaskSegmentCanvas.d.ts","sourceRoot":"","sources":["../../src/components/MaskSegmentCanvas.tsx"],"names":[],"mappings":"AAAA,OAAO,KAQN,MAAM,OAAO,CAAC;AA6Cf,OAAO,KAAK,EAEV,sBAAsB,EACtB,oBAAoB,EAMrB,MAAM,2BAA2B,CAAC;AAcnC,YAAY,EACV,sBAAsB,EACtB,oBAAoB,EACpB,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,EACnB,QAAQ,EACR,iBAAiB,GAClB,MAAM,2BAA2B,CAAC;AA+BnC,QAAA,MAAM,iBAAiB,qGAo/DrB,CAAC;AAmBH,eAAe,iBAAiB,CAAC"}
|
|
||||||
1589
dist/components/MaskSegmentCanvas.js
vendored
1589
dist/components/MaskSegmentCanvas.js
vendored
File diff suppressed because one or more lines are too long
1
dist/components/MaskSegmentCanvas.js.map
vendored
1
dist/components/MaskSegmentCanvas.js.map
vendored
File diff suppressed because one or more lines are too long
25
dist/components/MaskSegmentCanvas.types.d.ts
vendored
25
dist/components/MaskSegmentCanvas.types.d.ts
vendored
@ -16,7 +16,7 @@ export type MaskSegmentWatchDetail = {
|
|||||||
export type PipelinePreset = 'high' | 'medium' | 'low';
|
export type PipelinePreset = 'high' | 'medium' | 'low';
|
||||||
export type PipelineConfig = {
|
export type PipelineConfig = {
|
||||||
maxImageLongSide?: number;
|
maxImageLongSide?: number;
|
||||||
/** 高低频 LAB 处理最长边(可低于 maxImageLongSide,Shader 拉伸采样) */
|
/** low/high frequency LAB processing longest side (can be lower than maxImageLongSide, Shader stretch sampling) */
|
||||||
paintFreqMaxLongSide?: number;
|
paintFreqMaxLongSide?: number;
|
||||||
originPreviewMaxLongSide?: number;
|
originPreviewMaxLongSide?: number;
|
||||||
maskPathMaxLongSide?: number;
|
maskPathMaxLongSide?: number;
|
||||||
@ -41,17 +41,17 @@ export type MaskSegmentConfig = {
|
|||||||
baseboardJunctionRowMarginPx?: number;
|
baseboardJunctionRowMarginPx?: number;
|
||||||
baseboardJunctionVReachPx?: number;
|
baseboardJunctionVReachPx?: number;
|
||||||
baseboardMinRunPx?: number;
|
baseboardMinRunPx?: number;
|
||||||
/** 在 wall 掩码内按纹理边界细分为 wall-1、wall-2… */
|
/** only used in wall mask, split wall into wall-1, wall-2... by texture boundary */
|
||||||
splitWalls?: boolean;
|
splitWalls?: boolean;
|
||||||
/** 墙壁子区最大数量 */
|
/** wall mask only, max number of wall sub-regions */
|
||||||
splitWallsMaxCount?: number;
|
splitWallsMaxCount?: number;
|
||||||
/** 碎块最小面积比(相对 seg 总像素) */
|
/** wall mask only, min area ratio (relative to seg total pixels) */
|
||||||
splitWallsMinAreaRatio?: number;
|
splitWallsMinAreaRatio?: number;
|
||||||
/** Lab 色度 + 高频纹理特征距离平方阈值(不含亮度,削弱光影影响) */
|
/** wall mask only, Lab chroma + high-frequency texture feature distance squared threshold (excluding brightness, reducing shadow impact) */
|
||||||
splitWallsColorDistSq?: number;
|
splitWallsColorDistSq?: number;
|
||||||
/** 色度平滑半径(像素),仅用于纹理能量计算 */
|
/** wall mask only, chroma smoothing radius (pixels), only used for texture energy calculation */
|
||||||
splitWallsChromaBlurRadius?: number;
|
splitWallsChromaBlurRadius?: number;
|
||||||
/** 低饱和(白/灰墙)判定半径,用于与有色墙强制分界 */
|
/** wall mask only, low saturation (white/gray wall) junction radius, used to force separate colored walls */
|
||||||
splitWallsNeutralChromaMax?: number;
|
splitWallsNeutralChromaMax?: number;
|
||||||
};
|
};
|
||||||
export type PaintConfig = {
|
export type PaintConfig = {
|
||||||
@ -111,7 +111,7 @@ export type PaintSuccessPayload = {
|
|||||||
};
|
};
|
||||||
export type PaintBrushRequiredPayload = {
|
export type PaintBrushRequiredPayload = {
|
||||||
kind: 'brush_required';
|
kind: 'brush_required';
|
||||||
/** 未选笔刷时的提示文案 */
|
/** brush not selected, hint text */
|
||||||
hint: string;
|
hint: string;
|
||||||
regionId: number;
|
regionId: number;
|
||||||
regionName: string;
|
regionName: string;
|
||||||
@ -137,13 +137,13 @@ export type MaskSegmentCanvasRef = {
|
|||||||
export type MaskSegmentCanvasProps = {
|
export type MaskSegmentCanvasProps = {
|
||||||
originUrl?: string;
|
originUrl?: string;
|
||||||
maskUrl?: string;
|
maskUrl?: string;
|
||||||
/** @deprecated 使用 originUrl */
|
/** @deprecated use originUrl */
|
||||||
originImgPath?: string;
|
originImgPath?: string;
|
||||||
/** @deprecated 使用 maskUrl */
|
/** @deprecated use maskUrl */
|
||||||
maskImgPath?: string;
|
maskImgPath?: string;
|
||||||
/** 掩码语义识别色,初始化配置;等同 maskConfig.semanticColors */
|
/** mask semantic recognition colors, initialization configuration; equivalent to maskConfig.semanticColors */
|
||||||
semanticColors?: MaskSemanticColor[];
|
semanticColors?: MaskSemanticColor[];
|
||||||
/** 分区虚线高亮色,初始化配置;等同 paintConfig.regionOverlayFill */
|
/** partition dashed outline highlight color, initialization configuration; equivalent to paintConfig.regionOverlayFill */
|
||||||
regionOutlineColor?: string;
|
regionOutlineColor?: string;
|
||||||
maskConfig?: MaskSegmentConfig;
|
maskConfig?: MaskSegmentConfig;
|
||||||
/** Performance preset (high / medium / low). Merged with pipelineConfig overrides. */
|
/** Performance preset (high / medium / low). Merged with pipelineConfig overrides. */
|
||||||
@ -176,4 +176,3 @@ export type MaskSegmentCanvasProps = {
|
|||||||
onExported?: (result: SavePaintResult) => void;
|
onExported?: (result: SavePaintResult) => void;
|
||||||
};
|
};
|
||||||
export type { SegmentRegion, MaskSemanticColor };
|
export type { SegmentRegion, MaskSemanticColor };
|
||||||
//# sourceMappingURL=MaskSegmentCanvas.types.d.ts.map
|
|
||||||
File diff suppressed because one or more lines are too long
3
dist/components/MaskSegmentCanvas.types.js
vendored
3
dist/components/MaskSegmentCanvas.types.js
vendored
@ -1,2 +1 @@
|
|||||||
export {};
|
"use strict";var r=Object.defineProperty;var a=Object.getOwnPropertyDescriptor;var s=Object.getOwnPropertyNames;var l=Object.prototype.hasOwnProperty;var g=(n,e,t,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of s(e))!l.call(n,i)&&i!==t&&r(n,i,{get:()=>e[i],enumerable:!(o=a(e,i))||o.enumerable});return n};var m=n=>g(r({},"__esModule",{value:!0}),n);var u={};module.exports=m(u);
|
||||||
//# sourceMappingURL=MaskSegmentCanvas.types.js.map
|
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"MaskSegmentCanvas.types.js","sourceRoot":"","sources":["../../src/components/MaskSegmentCanvas.types.ts"],"names":[],"mappings":""}
|
|
||||||
1
dist/index.d.ts
vendored
1
dist/index.d.ts
vendored
@ -4,4 +4,3 @@ export { BASEBOARD_SEMANTIC_NAME, MASK_SEMANTIC_COLORS, } from './utils/maskSema
|
|||||||
export { createRuntimeConfig, DEFAULT_INTERACTION_CONFIG, DEFAULT_MASK_CONFIG, DEFAULT_PAINT_CONFIG, DEFAULT_PIPELINE_CONFIG, PIPELINE_HIGH, PIPELINE_LOW, PIPELINE_MEDIUM, PIPELINE_PRESETS, getMaskSegmentRuntimeConfig, resolvePipelineConfig, setMaskSegmentRuntimeConfig, } from './utils/maskSegmentRuntime';
|
export { createRuntimeConfig, DEFAULT_INTERACTION_CONFIG, DEFAULT_MASK_CONFIG, DEFAULT_PAINT_CONFIG, DEFAULT_PIPELINE_CONFIG, PIPELINE_HIGH, PIPELINE_LOW, PIPELINE_MEDIUM, PIPELINE_PRESETS, getMaskSegmentRuntimeConfig, resolvePipelineConfig, setMaskSegmentRuntimeConfig, } from './utils/maskSegmentRuntime';
|
||||||
export { prewarmPngBgrCache, prewarmPngBgrCacheAsync, } from './utils/pngImage';
|
export { prewarmPngBgrCache, prewarmPngBgrCacheAsync, } from './utils/pngImage';
|
||||||
export { resolveAssetPath } from './utils/resolveAssetPath';
|
export { resolveAssetPath } from './utils/resolveAssetPath';
|
||||||
//# sourceMappingURL=index.d.ts.map
|
|
||||||
1
dist/index.d.ts.map
vendored
1
dist/index.d.ts.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AACzD,YAAY,EACV,QAAQ,EACR,iBAAiB,EACjB,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,EACjB,yBAAyB,EACzB,oBAAoB,EACpB,mBAAmB,EACnB,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,aAAa,GACd,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EACL,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,aAAa,EACb,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,2BAA2B,EAC3B,qBAAqB,EACrB,2BAA2B,GAC5B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC"}
|
|
||||||
7
dist/index.js
vendored
7
dist/index.js
vendored
@ -1,6 +1 @@
|
|||||||
export { default } from './components/MaskSegmentCanvas';
|
"use strict";var m=Object.create;var i=Object.defineProperty;var E=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var M=Object.getPrototypeOf,f=Object.prototype.hasOwnProperty;var p=(a,n)=>{for(var t in n)i(a,t,{get:n[t],enumerable:!0})},g=(a,n,t,P)=>{if(n&&typeof n=="object"||typeof n=="function")for(let o of I(n))!f.call(a,o)&&o!==t&&i(a,o,{get:()=>n[o],enumerable:!(P=E(n,o))||P.enumerable});return a};var _=(a,n,t)=>(t=a!=null?m(M(a)):{},g(n||!a||!a.__esModule?i(t,"default",{value:a,enumerable:!0}):t,a)),l=a=>g(i({},"__esModule",{value:!0}),a);var A={};p(A,{BASEBOARD_SEMANTIC_NAME:()=>r.BASEBOARD_SEMANTIC_NAME,DEFAULT_INTERACTION_CONFIG:()=>e.DEFAULT_INTERACTION_CONFIG,DEFAULT_MASK_CONFIG:()=>e.DEFAULT_MASK_CONFIG,DEFAULT_PAINT_CONFIG:()=>e.DEFAULT_PAINT_CONFIG,DEFAULT_PIPELINE_CONFIG:()=>e.DEFAULT_PIPELINE_CONFIG,MASK_SEMANTIC_COLORS:()=>r.MASK_SEMANTIC_COLORS,PIPELINE_HIGH:()=>e.PIPELINE_HIGH,PIPELINE_LOW:()=>e.PIPELINE_LOW,PIPELINE_MEDIUM:()=>e.PIPELINE_MEDIUM,PIPELINE_PRESETS:()=>e.PIPELINE_PRESETS,createRuntimeConfig:()=>e.createRuntimeConfig,default:()=>C.default,getMaskSegmentRuntimeConfig:()=>e.getMaskSegmentRuntimeConfig,prewarmPngBgrCache:()=>s.prewarmPngBgrCache,prewarmPngBgrCacheAsync:()=>s.prewarmPngBgrCacheAsync,resolveAssetPath:()=>S.resolveAssetPath,resolvePipelineConfig:()=>e.resolvePipelineConfig,setMaskSegmentRuntimeConfig:()=>e.setMaskSegmentRuntimeConfig});module.exports=l(A);var C=_(require("./components/MaskSegmentCanvas")),r=require("./utils/maskSemanticPalette"),e=require("./utils/maskSegmentRuntime"),s=require("./utils/pngImage"),S=require("./utils/resolveAssetPath");
|
||||||
export { BASEBOARD_SEMANTIC_NAME, MASK_SEMANTIC_COLORS, } from './utils/maskSemanticPalette';
|
|
||||||
export { createRuntimeConfig, DEFAULT_INTERACTION_CONFIG, DEFAULT_MASK_CONFIG, DEFAULT_PAINT_CONFIG, DEFAULT_PIPELINE_CONFIG, PIPELINE_HIGH, PIPELINE_LOW, PIPELINE_MEDIUM, PIPELINE_PRESETS, getMaskSegmentRuntimeConfig, resolvePipelineConfig, setMaskSegmentRuntimeConfig, } from './utils/maskSegmentRuntime';
|
|
||||||
export { prewarmPngBgrCache, prewarmPngBgrCacheAsync, } from './utils/pngImage';
|
|
||||||
export { resolveAssetPath } from './utils/resolveAssetPath';
|
|
||||||
//# sourceMappingURL=index.js.map
|
|
||||||
|
|||||||
1
dist/index.js.map
vendored
1
dist/index.js.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAsBzD,OAAO,EACL,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,aAAa,EACb,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,2BAA2B,EAC3B,qBAAqB,EACrB,2BAA2B,GAC5B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC"}
|
|
||||||
3
dist/shaders/regionPaint.sksl.d.ts
vendored
3
dist/shaders/regionPaint.sksl.d.ts
vendored
@ -1,3 +1,2 @@
|
|||||||
/** SkSL:分区上色(保留原图明暗与纹理,按 paintColorMap 叠色) */
|
/** SkSL: split wall regions by texture boundary, then paint the regions (preserve original image luminance and texture, overlay paintColorMap) */
|
||||||
export declare const REGION_PAINT_SKSL = "\nuniform shader originTex;\nuniform shader paintColorTex;\nuniform shader lowFreqTex;\nuniform shader highFreqTex;\n\nuniform float colorBaseOpacity;\nuniform float lLightOpacity;\nuniform float textureOpacity;\nuniform float showOrigin;\n\nfloat luminance(half3 c) {\n return dot(c, half3(0.2126, 0.7152, 0.0722));\n}\n\nhalf3 setLuminance(float lum, half3 base) {\n float diff = lum - luminance(base);\n return base + diff;\n}\n\nhalf3 luminosityBlend(half3 base, half3 blend) {\n return setLuminance(luminance(blend), base);\n}\n\nhalf3 overlayBlend(half3 base, half3 blend) {\n half3 low = 2.0 * base * blend;\n half3 high = 1.0 - 2.0 * (1.0 - base) * (1.0 - blend);\n return mix(low, high, step(half3(0.5), base));\n}\n\nhalf4 main(float2 coord) {\n half4 origin = originTex.eval(coord);\n if (showOrigin > 0.5) {\n return origin;\n }\n\n half4 paintEntry = paintColorTex.eval(coord);\n // The paintColorMap uses Unpremul alpha: painted pixels \u2192 (R,G,B,255),\n // transparent pixels \u2192 (0,0,0,0). GPU bilinear sampling interpolates\n // straight-alpha values, so at boundaries rgb = trueColor * sampled.a\n // (contaminated with black from the transparent neighbour).\n //\n // Unpremultiply to recover the true paint color:\n // trueColor = sampled.rgb / sampled.a\n // This eliminates dark fringing at region boundaries.\n float pa = paintEntry.a + 0.0001;\n paintEntry.rgb /= pa;\n\n // Gate sub-pixel alpha to kill residual sampling noise.\n // Thresholds are deliberately low (\u22481.3\u20133.8 in byte space) because\n // post-unpremul the RGB is correct at any alpha \u2265 0 \u2014 we only need\n // to suppress samples that contribute negligibly to the final blend.\n // Using *= preserves the smooth edge; higher-alpha samples pass through.\n paintEntry.a *= smoothstep(0.005, 0.015, paintEntry.a);\n\n half3 paintRgb = paintEntry.rgb;\n half lowL = lowFreqTex.eval(coord).r;\n half highL = highFreqTex.eval(coord).r;\n\n half3 base = paintRgb * colorBaseOpacity;\n half3 lit = luminosityBlend(base, half3(lowL));\n half3 withLight = mix(base, lit, lLightOpacity);\n half3 tex = overlayBlend(withLight, half3(highL));\n half3 finalRgb = mix(withLight, tex, textureOpacity);\n\n // Soft edge blend using the (feathered) alpha from the paint color map as coverage.\n half3 blended = mix(origin.rgb, finalRgb, paintEntry.a);\n return half4(blended, 1.0);\n}\n";
|
export declare const REGION_PAINT_SKSL = "\nuniform shader originTex;\nuniform shader paintColorTex;\nuniform shader lowFreqTex;\nuniform shader highFreqTex;\n\nuniform float colorBaseOpacity;\nuniform float lLightOpacity;\nuniform float textureOpacity;\nuniform float showOrigin;\n\nfloat luminance(half3 c) {\n return dot(c, half3(0.2126, 0.7152, 0.0722));\n}\n\nhalf3 setLuminance(float lum, half3 base) {\n float diff = lum - luminance(base);\n return base + diff;\n}\n\nhalf3 luminosityBlend(half3 base, half3 blend) {\n return setLuminance(luminance(blend), base);\n}\n\nhalf3 overlayBlend(half3 base, half3 blend) {\n half3 low = 2.0 * base * blend;\n half3 high = 1.0 - 2.0 * (1.0 - base) * (1.0 - blend);\n return mix(low, high, step(half3(0.5), base));\n}\n\nhalf4 main(float2 coord) {\n half4 origin = originTex.eval(coord);\n if (showOrigin > 0.5) {\n return origin;\n }\n\n half4 paintEntry = paintColorTex.eval(coord);\n // The paintColorMap uses Unpremul alpha: painted pixels \u2192 (R,G,B,255),\n // transparent pixels \u2192 (0,0,0,0). GPU bilinear sampling interpolates\n // straight-alpha values, so at boundaries rgb = trueColor * sampled.a\n // (contaminated with black from the transparent neighbour).\n //\n // Unpremultiply to recover the true paint color:\n // trueColor = sampled.rgb / sampled.a\n // This eliminates dark fringing at region boundaries.\n float pa = paintEntry.a + 0.0001;\n paintEntry.rgb /= pa;\n\n // Gate sub-pixel alpha to kill residual sampling noise.\n // Thresholds are deliberately low (\u22481.3\u20133.8 in byte space) because\n // post-unpremul the RGB is correct at any alpha \u2265 0 \u2014 we only need\n // to suppress samples that contribute negligibly to the final blend.\n // Using *= preserves the smooth edge; higher-alpha samples pass through.\n paintEntry.a *= smoothstep(0.005, 0.015, paintEntry.a);\n\n half3 paintRgb = paintEntry.rgb;\n half lowL = lowFreqTex.eval(coord).r;\n half highL = highFreqTex.eval(coord).r;\n\n half3 base = paintRgb * colorBaseOpacity;\n half3 lit = luminosityBlend(base, half3(lowL));\n half3 withLight = mix(base, lit, lLightOpacity);\n half3 tex = overlayBlend(withLight, half3(highL));\n half3 finalRgb = mix(withLight, tex, textureOpacity);\n\n // Soft edge blend using the (feathered) alpha from the paint color map as coverage.\n half3 blended = mix(origin.rgb, finalRgb, paintEntry.a);\n return half4(blended, 1.0);\n}\n";
|
||||||
//# sourceMappingURL=regionPaint.sksl.d.ts.map
|
|
||||||
1
dist/shaders/regionPaint.sksl.d.ts.map
vendored
1
dist/shaders/regionPaint.sksl.d.ts.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"regionPaint.sksl.d.ts","sourceRoot":"","sources":["../../src/shaders/regionPaint.sksl.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,eAAO,MAAM,iBAAiB,83EAqE7B,CAAC"}
|
|
||||||
12
dist/shaders/regionPaint.sksl.js
vendored
12
dist/shaders/regionPaint.sksl.js
vendored
@ -1,5 +1,4 @@
|
|||||||
/** SkSL:分区上色(保留原图明暗与纹理,按 paintColorMap 叠色) */
|
"use strict";var t=Object.defineProperty;var n=Object.getOwnPropertyDescriptor;var o=Object.getOwnPropertyNames;var h=Object.prototype.hasOwnProperty;var s=(e,a)=>{for(var i in a)t(e,i,{get:a[i],enumerable:!0})},f=(e,a,i,r)=>{if(a&&typeof a=="object"||typeof a=="function")for(let l of o(a))!h.call(e,l)&&l!==i&&t(e,l,{get:()=>a[l],enumerable:!(r=n(a,l))||r.enumerable});return e};var p=e=>f(t({},"__esModule",{value:!0}),e);var g={};s(g,{REGION_PAINT_SKSL:()=>u});module.exports=p(g);const u=`
|
||||||
export const REGION_PAINT_SKSL = `
|
|
||||||
uniform shader originTex;
|
uniform shader originTex;
|
||||||
uniform shader paintColorTex;
|
uniform shader paintColorTex;
|
||||||
uniform shader lowFreqTex;
|
uniform shader lowFreqTex;
|
||||||
@ -36,8 +35,8 @@ half4 main(float2 coord) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
half4 paintEntry = paintColorTex.eval(coord);
|
half4 paintEntry = paintColorTex.eval(coord);
|
||||||
// The paintColorMap uses Unpremul alpha: painted pixels → (R,G,B,255),
|
// The paintColorMap uses Unpremul alpha: painted pixels \u2192 (R,G,B,255),
|
||||||
// transparent pixels → (0,0,0,0). GPU bilinear sampling interpolates
|
// transparent pixels \u2192 (0,0,0,0). GPU bilinear sampling interpolates
|
||||||
// straight-alpha values, so at boundaries rgb = trueColor * sampled.a
|
// straight-alpha values, so at boundaries rgb = trueColor * sampled.a
|
||||||
// (contaminated with black from the transparent neighbour).
|
// (contaminated with black from the transparent neighbour).
|
||||||
//
|
//
|
||||||
@ -48,8 +47,8 @@ half4 main(float2 coord) {
|
|||||||
paintEntry.rgb /= pa;
|
paintEntry.rgb /= pa;
|
||||||
|
|
||||||
// Gate sub-pixel alpha to kill residual sampling noise.
|
// Gate sub-pixel alpha to kill residual sampling noise.
|
||||||
// Thresholds are deliberately low (≈1.3–3.8 in byte space) because
|
// Thresholds are deliberately low (\u22481.3\u20133.8 in byte space) because
|
||||||
// post-unpremul the RGB is correct at any alpha ≥ 0 — we only need
|
// post-unpremul the RGB is correct at any alpha \u2265 0 \u2014 we only need
|
||||||
// to suppress samples that contribute negligibly to the final blend.
|
// to suppress samples that contribute negligibly to the final blend.
|
||||||
// Using *= preserves the smooth edge; higher-alpha samples pass through.
|
// Using *= preserves the smooth edge; higher-alpha samples pass through.
|
||||||
paintEntry.a *= smoothstep(0.005, 0.015, paintEntry.a);
|
paintEntry.a *= smoothstep(0.005, 0.015, paintEntry.a);
|
||||||
@ -69,4 +68,3 @@ half4 main(float2 coord) {
|
|||||||
return half4(blended, 1.0);
|
return half4(blended, 1.0);
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
//# sourceMappingURL=regionPaint.sksl.js.map
|
|
||||||
1
dist/shaders/regionPaint.sksl.js.map
vendored
1
dist/shaders/regionPaint.sksl.js.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"regionPaint.sksl.js","sourceRoot":"","sources":["../../src/shaders/regionPaint.sksl.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqEhC,CAAC"}
|
|
||||||
1
dist/utils/canvasGeometry.d.ts
vendored
1
dist/utils/canvasGeometry.d.ts
vendored
@ -78,4 +78,3 @@ export declare function releasePaintResourceLayers(layers: PaintResourceLayers |
|
|||||||
export declare function releaseOriginSkImage(image: SkImage | null): void;
|
export declare function releaseOriginSkImage(image: SkImage | null): void;
|
||||||
export declare function prepareWorkScaledBgrBuffer(bgrBuffer: Uint8Array, cols: number, rows: number, workScale: number): Promise<WorkScaledBgr>;
|
export declare function prepareWorkScaledBgrBuffer(bgrBuffer: Uint8Array, cols: number, rows: number, workScale: number): Promise<WorkScaledBgr>;
|
||||||
export declare function timeLog(tag: string): void;
|
export declare function timeLog(tag: string): void;
|
||||||
//# sourceMappingURL=canvasGeometry.d.ts.map
|
|
||||||
1
dist/utils/canvasGeometry.d.ts.map
vendored
1
dist/utils/canvasGeometry.d.ts.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"canvasGeometry.d.ts","sourceRoot":"","sources":["../../src/utils/canvasGeometry.ts"],"names":[],"mappings":"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"}
|
|
||||||
307
dist/utils/canvasGeometry.js
vendored
307
dist/utils/canvasGeometry.js
vendored
@ -1,306 +1 @@
|
|||||||
import { Skia } from '@shopify/react-native-skia';
|
"use strict";var h=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var A=Object.prototype.hasOwnProperty;var F=(e,r)=>{for(var n in r)h(e,n,{get:r[n],enumerable:!0})},T=(e,r,n,o)=>{if(r&&typeof r=="object"||typeof r=="function")for(let u of I(r))!A.call(e,u)&&u!==n&&h(e,u,{get:()=>r[u],enumerable:!(o=q(r,u))||o.enumerable});return e};var L=e=>T(h({},"__esModule",{value:!0}),e);var G={};F(G,{bgrColorEquals:()=>U,buildZoomPanMatrix:()=>E,canvasToNormalized:()=>j,clampPanOffset:()=>N,getContainRect:()=>C,getRegionHitPolygons:()=>S,lookupRegionFromPickMap:()=>D,pickKickNearStrip:()=>$,pickKickRegionFromMask:()=>K,pointHitsRegion:()=>B,pointInPolygon:()=>M,pointInPolygonWithPadding:()=>p,pointStrictlyHitsRegion:()=>w,prepareWorkScaledBgrBuffer:()=>Z,rectsEqual:()=>_,releaseOriginSkImage:()=>V,releasePaintResourceLayers:()=>O,resolveRegionHit:()=>Y,screenToCanvasCoords:()=>X,timeLog:()=>z});module.exports=L(G);var P=require("@shopify/react-native-skia"),d=require("./maskSegmentation"),g=require("./maskSegmentRuntime"),R=require("./pngImage");function U(e,r){return e.b===r.b&&e.g===r.g&&e.r===r.r}function _(e,r){return e.x===r.x&&e.y===r.y&&e.w===r.w&&e.h===r.h}function C(e,r,n,o){const u=n/o,m=e/r;if(u>m){const s=e,l=e/u;return{x:0,y:(r-l)/2,w:s,h:l}}const i=r,t=r*u;return{x:(e-t)/2,y:0,w:t,h:i}}function j(e,r,n,o,u,m){const i=C(n,o,u,m);return e<i.x||e>i.x+i.w||r<i.y||r>i.y+i.h?null:{x:(e-i.x)/i.w,y:(r-i.y)/i.h}}function E(e,r,n,o,u){const m=o/2,i=u/2,t=P.Skia.Matrix();return t.translate(e,r),t.translate(m,i),t.scale(n,n),t.translate(-m,-i),t}function N(e,r,n,o,u){if(!u||r<=1||n<=0||o<=0)return{x:0,y:0};const m=n/2,i=o/2,t=u,s=m+r*(t.x-m),l=m+r*(t.x+t.w-m),a=i+r*(t.y-i),f=i+r*(t.y+t.h-i);let c=e.x,b=e.y;return l-s>n?c=Math.max(n-l,Math.min(-s,c)):c=0,f-a>o?b=Math.max(o-f,Math.min(-a,b)):b=0,{x:c,y:b}}function X(e,r,n,o,u,m){return u<=1?{x:e,y:r}:{x:(e-m.x-n/2)/u+n/2,y:(r-m.y-o/2)/u+o/2}}function M(e,r,n){let o=!1;for(let u=0,m=n.length-1;u<n.length;m=u++){const i=n[u].x,t=n[u].y,s=n[m].x,l=n[m].y;t>r!=l>r&&e<(s-i)*(r-t)/(l-t)+i&&(o=!o)}return o}function p(e,r,n,o){if(n.length<3)return!1;let u=n[0].x,m=n[0].x,i=n[0].y,t=n[0].y;for(const s of n)u=Math.min(u,s.x),m=Math.max(m,s.x),i=Math.min(i,s.y),t=Math.max(t,s.y);return e>=u-o&&e<=m+o&&r>=i-o&&r<=t+o&&(t-i<o*2.5||m-u<o*2.5)?!0:M(e,r,n)}function S(e){return e.hitPolygons&&e.hitPolygons.length>0?e.hitPolygons:e.polygons}function B(e,r,n,o){const u=(0,g.getMaskSegmentRuntimeConfig)().interaction,m=o?.thinPadding??u.thinStripPadding,i=n.thinStrip?m:u.regionPadding;return S(n).some(t=>t.length>=3&&p(e,r,t,i))}function w(e,r,n){return S(n).some(o=>o.length>=3&&M(e,r,o))}function Y(e,r,n){const o=[];for(const t of e){const s=t.thinStrip?.005:0,l=t.bbox;r<l.x-s||r>l.x+l.w+s||n<l.y-s||n>l.y+l.h+s||B(r,n,t)&&o.push(t)}if(o.length===0)return null;if(o.length===1)return o[0].id;const u=o.filter(t=>!t.thinStrip&&w(r,n,t));if(u.length>0)return u.sort((t,s)=>t.area-s.area),u[0].id;const m=o.filter(t=>t.thinStrip&&w(r,n,t));if(m.length>0)return m.sort((t,s)=>t.area-s.area),m[0].id;const i=o.filter(t=>!t.thinStrip);return i.length>0?(i.sort((t,s)=>t.area-s.area),i[0].id):(o.sort((t,s)=>t.area-s.area),o[0].id)}function K(e,r,n,o,u,m=!1){const i=Math.floor(e*n.cols),t=Math.floor(r*n.rows);if(i<0||t<0||i>=n.cols||t>=n.rows)return null;if(m)return u?u[t*n.cols+i]?o:null:(0,d.isBaseboardMaskPixel)(n.buffer,n.cols,n.rows,i,t)?o:null;const s=(0,g.getMaskSegmentRuntimeConfig)().interaction,l=Math.max(s.kickMaskPickRadiusPx,Math.floor(n.cols*.022)),a=l*l;for(let f=-l;f<=l;f++)for(let c=-l;c<=l;c++){if(c*c+f*f>a)continue;const b=i+c,x=t+f;if(!(b<0||x<0||b>=n.cols||x>=n.rows)){if(u){if(u[x*n.cols+b])return o;continue}if((0,d.isBaseboardMaskPixel)(n.buffer,n.cols,n.rows,b,x))return o}}return null}function $(e,r,n){const o=n.hitPolygons??n.polygons,u=(0,g.getMaskSegmentRuntimeConfig)().interaction.thinStripPadding+.004;return o.some(m=>m.length>=3&&p(e,r,m,u))}function D(e,r,n,o=(0,g.getMaskSegmentRuntimeConfig)().interaction.pickMapSearchRadiusPx){const u=Math.min(n.cols-1,Math.max(0,Math.floor(e*n.cols))),m=Math.min(n.rows-1,Math.max(0,Math.floor(r*n.rows))),i=(a,f)=>n.buffer[f*n.cols+a],t=i(u,m);if(t>0)return t-1;if(o<=0)return null;const s=Math.max(4,o),l=s*s;for(let a=-s;a<=s;a++)for(let f=-s;f<=s;f++){if(f*f+a*a>l)continue;const c=u+f,b=m+a;if(c<0||b<0||c>=n.cols||b>=n.rows)continue;const x=i(c,b);if(x>0)return x-1}return null}function O(e){e&&(e.lowFreqImage.dispose(),e.highFreqImage.dispose())}function V(e){e&&e.dispose()}async function Z(e,r,n,o){if(o>=1)return{buffer:e,cols:r,rows:n};const u=Math.floor(r*o),m=Math.floor(n*o);return{buffer:(0,R.resizeBgrBuffer)(e,r,n,u,m),cols:u,rows:m}}let y=0;function z(e){if(!__DEV__)return;const r=performance.now(),n=y?r-y:0;console.log(`[\u23F1 ${e}] ${n.toFixed(2)} ms`),y=r}
|
||||||
import { isBaseboardMaskPixel } from './maskSegmentation';
|
|
||||||
import { getMaskSegmentRuntimeConfig } from './maskSegmentRuntime';
|
|
||||||
import { resizeBgrBuffer } from './pngImage';
|
|
||||||
/* ==========================================================================
|
|
||||||
* 颜色工具
|
|
||||||
* ========================================================================== */
|
|
||||||
export function bgrColorEquals(a, b) {
|
|
||||||
return a.b === b.b && a.g === b.g && a.r === b.r;
|
|
||||||
}
|
|
||||||
/* ==========================================================================
|
|
||||||
* 几何工具
|
|
||||||
* ========================================================================== */
|
|
||||||
export function rectsEqual(a, b) {
|
|
||||||
return a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h;
|
|
||||||
}
|
|
||||||
export function getContainRect(canvasW, canvasH, imgW, imgH) {
|
|
||||||
const imgAspect = imgW / imgH;
|
|
||||||
const canvasAspect = canvasW / canvasH;
|
|
||||||
if (imgAspect > canvasAspect) {
|
|
||||||
const w = canvasW;
|
|
||||||
const h = canvasW / imgAspect;
|
|
||||||
return { x: 0, y: (canvasH - h) / 2, w, h };
|
|
||||||
}
|
|
||||||
const h = canvasH;
|
|
||||||
const w = canvasH * imgAspect;
|
|
||||||
return { x: (canvasW - w) / 2, y: 0, w, h };
|
|
||||||
}
|
|
||||||
export function canvasToNormalized(cx, cy, canvasW, canvasH, imgW, imgH) {
|
|
||||||
const rect = getContainRect(canvasW, canvasH, imgW, imgH);
|
|
||||||
if (cx < rect.x ||
|
|
||||||
cx > rect.x + rect.w ||
|
|
||||||
cy < rect.y ||
|
|
||||||
cy > rect.y + rect.h) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
x: (cx - rect.x) / rect.w,
|
|
||||||
y: (cy - rect.y) / rect.h,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/** Skia matrix: pan → scale around viewport center. Matches screenToCanvasCoords. */
|
|
||||||
export function buildZoomPanMatrix(panX, panY, scale, canvasW, canvasH) {
|
|
||||||
const cx = canvasW / 2;
|
|
||||||
const cy = canvasH / 2;
|
|
||||||
const m = Skia.Matrix();
|
|
||||||
m.translate(panX, panY);
|
|
||||||
m.translate(cx, cy);
|
|
||||||
m.scale(scale, scale);
|
|
||||||
m.translate(-cx, -cy);
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
/** Clamp pan so scaled containRect does not expose empty margins beyond the viewport. */
|
|
||||||
export function clampPanOffset(pan, scale, canvasW, canvasH, containRect) {
|
|
||||||
if (!containRect || scale <= 1 || canvasW <= 0 || canvasH <= 0) {
|
|
||||||
return { x: 0, y: 0 };
|
|
||||||
}
|
|
||||||
const cx = canvasW / 2;
|
|
||||||
const cy = canvasH / 2;
|
|
||||||
const r = containRect;
|
|
||||||
const scaledMinX = cx + scale * (r.x - cx);
|
|
||||||
const scaledMaxX = cx + scale * (r.x + r.w - cx);
|
|
||||||
const scaledMinY = cy + scale * (r.y - cy);
|
|
||||||
const scaledMaxY = cy + scale * (r.y + r.h - cy);
|
|
||||||
let x = pan.x;
|
|
||||||
let y = pan.y;
|
|
||||||
if (scaledMaxX - scaledMinX > canvasW) {
|
|
||||||
x = Math.max(canvasW - scaledMaxX, Math.min(-scaledMinX, x));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
x = 0;
|
|
||||||
}
|
|
||||||
if (scaledMaxY - scaledMinY > canvasH) {
|
|
||||||
y = Math.max(canvasH - scaledMaxY, Math.min(-scaledMinY, y));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
y = 0;
|
|
||||||
}
|
|
||||||
return { x, y };
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Inverse of the Skia Group transform applied during pinch-zoom.
|
|
||||||
* Converts a raw touch point (screen pixels) back to the canvas coordinate
|
|
||||||
* space where the image and regions are positioned before any scale/pan.
|
|
||||||
* When zoomScale ≤ 1 (no zoom), returns the input unchanged.
|
|
||||||
*/
|
|
||||||
export function screenToCanvasCoords(screenX, screenY, canvasW, canvasH, zoomScale, panOffset) {
|
|
||||||
if (zoomScale <= 1)
|
|
||||||
return { x: screenX, y: screenY };
|
|
||||||
return {
|
|
||||||
x: (screenX - panOffset.x - canvasW / 2) / zoomScale + canvasW / 2,
|
|
||||||
y: (screenY - panOffset.y - canvasH / 2) / zoomScale + canvasH / 2,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/* ==========================================================================
|
|
||||||
* 点击检测
|
|
||||||
* ========================================================================== */
|
|
||||||
export function pointInPolygon(x, y, points) {
|
|
||||||
let inside = false;
|
|
||||||
for (let i = 0, j = points.length - 1; i < points.length; j = i++) {
|
|
||||||
const xi = points[i].x;
|
|
||||||
const yi = points[i].y;
|
|
||||||
const xj = points[j].x;
|
|
||||||
const yj = points[j].y;
|
|
||||||
const intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;
|
|
||||||
if (intersect)
|
|
||||||
inside = !inside;
|
|
||||||
}
|
|
||||||
return inside;
|
|
||||||
}
|
|
||||||
export function pointInPolygonWithPadding(x, y, points, padding) {
|
|
||||||
if (points.length < 3) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let minX = points[0].x;
|
|
||||||
let maxX = points[0].x;
|
|
||||||
let minY = points[0].y;
|
|
||||||
let maxY = points[0].y;
|
|
||||||
for (const point of points) {
|
|
||||||
minX = Math.min(minX, point.x);
|
|
||||||
maxX = Math.max(maxX, point.x);
|
|
||||||
minY = Math.min(minY, point.y);
|
|
||||||
maxY = Math.max(maxY, point.y);
|
|
||||||
}
|
|
||||||
if (x >= minX - padding &&
|
|
||||||
x <= maxX + padding &&
|
|
||||||
y >= minY - padding &&
|
|
||||||
y <= maxY + padding) {
|
|
||||||
if (maxY - minY < padding * 2.5 || maxX - minX < padding * 2.5) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pointInPolygon(x, y, points);
|
|
||||||
}
|
|
||||||
export function getRegionHitPolygons(reg) {
|
|
||||||
return reg.hitPolygons && reg.hitPolygons.length > 0
|
|
||||||
? reg.hitPolygons
|
|
||||||
: reg.polygons;
|
|
||||||
}
|
|
||||||
export function pointHitsRegion(x, y, reg, options) {
|
|
||||||
const interaction = getMaskSegmentRuntimeConfig().interaction;
|
|
||||||
const thinPadding = options?.thinPadding ?? interaction.thinStripPadding;
|
|
||||||
const padding = reg.thinStrip ? thinPadding : interaction.regionPadding;
|
|
||||||
return getRegionHitPolygons(reg).some(poly => poly.length >= 3 && pointInPolygonWithPadding(x, y, poly, padding));
|
|
||||||
}
|
|
||||||
export function pointStrictlyHitsRegion(x, y, reg) {
|
|
||||||
return getRegionHitPolygons(reg).some(poly => poly.length >= 3 && pointInPolygon(x, y, poly));
|
|
||||||
}
|
|
||||||
export function resolveRegionHit(regions, x, y) {
|
|
||||||
const hits = [];
|
|
||||||
for (const reg of regions) {
|
|
||||||
const bboxPad = reg.thinStrip ? 0.005 : 0;
|
|
||||||
const b = reg.bbox;
|
|
||||||
if (x < b.x - bboxPad ||
|
|
||||||
x > b.x + b.w + bboxPad ||
|
|
||||||
y < b.y - bboxPad ||
|
|
||||||
y > b.y + b.h + bboxPad) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (pointHitsRegion(x, y, reg)) {
|
|
||||||
hits.push(reg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hits.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (hits.length === 1) {
|
|
||||||
return hits[0].id;
|
|
||||||
}
|
|
||||||
const strictNonThin = hits.filter(reg => !reg.thinStrip && pointStrictlyHitsRegion(x, y, reg));
|
|
||||||
if (strictNonThin.length > 0) {
|
|
||||||
strictNonThin.sort((a, b) => a.area - b.area);
|
|
||||||
return strictNonThin[0].id;
|
|
||||||
}
|
|
||||||
const strictThin = hits.filter(reg => reg.thinStrip && pointStrictlyHitsRegion(x, y, reg));
|
|
||||||
if (strictThin.length > 0) {
|
|
||||||
strictThin.sort((a, b) => a.area - b.area);
|
|
||||||
return strictThin[0].id;
|
|
||||||
}
|
|
||||||
const nonThin = hits.filter(reg => !reg.thinStrip);
|
|
||||||
if (nonThin.length > 0) {
|
|
||||||
nonThin.sort((a, b) => a.area - b.area);
|
|
||||||
return nonThin[0].id;
|
|
||||||
}
|
|
||||||
hits.sort((a, b) => a.area - b.area);
|
|
||||||
return hits[0].id;
|
|
||||||
}
|
|
||||||
/* ==========================================================================
|
|
||||||
* 踢脚线/掩码拾取
|
|
||||||
* ========================================================================== */
|
|
||||||
export function pickKickRegionFromMask(normX, normY, pick, kickRegionId, baseboardPickMask, strict = false) {
|
|
||||||
const cx = Math.floor(normX * pick.cols);
|
|
||||||
const cy = Math.floor(normY * pick.rows);
|
|
||||||
if (cx < 0 || cy < 0 || cx >= pick.cols || cy >= pick.rows) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (strict) {
|
|
||||||
if (baseboardPickMask) {
|
|
||||||
return baseboardPickMask[cy * pick.cols + cx] ? kickRegionId : null;
|
|
||||||
}
|
|
||||||
return isBaseboardMaskPixel(pick.buffer, pick.cols, pick.rows, cx, cy)
|
|
||||||
? kickRegionId
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
const interaction = getMaskSegmentRuntimeConfig().interaction;
|
|
||||||
const radius = Math.max(interaction.kickMaskPickRadiusPx, Math.floor(pick.cols * 0.022));
|
|
||||||
const radiusSq = radius * radius;
|
|
||||||
for (let dy = -radius; dy <= radius; dy++) {
|
|
||||||
for (let dx = -radius; dx <= radius; dx++) {
|
|
||||||
if (dx * dx + dy * dy > radiusSq) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const x = cx + dx;
|
|
||||||
const y = cy + dy;
|
|
||||||
if (x < 0 || y < 0 || x >= pick.cols || y >= pick.rows) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (baseboardPickMask) {
|
|
||||||
if (baseboardPickMask[y * pick.cols + x]) {
|
|
||||||
return kickRegionId;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (isBaseboardMaskPixel(pick.buffer, pick.cols, pick.rows, x, y)) {
|
|
||||||
return kickRegionId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
export function pickKickNearStrip(normX, normY, kickReg) {
|
|
||||||
const polys = kickReg.hitPolygons ?? kickReg.polygons;
|
|
||||||
const pad = getMaskSegmentRuntimeConfig().interaction.thinStripPadding + 0.004;
|
|
||||||
return polys.some(poly => poly.length >= 3 && pointInPolygonWithPadding(normX, normY, poly, pad));
|
|
||||||
}
|
|
||||||
export function lookupRegionFromPickMap(normX, normY, pick, radiusPx = getMaskSegmentRuntimeConfig().interaction.pickMapSearchRadiusPx) {
|
|
||||||
const cx = Math.min(pick.cols - 1, Math.max(0, Math.floor(normX * pick.cols)));
|
|
||||||
const cy = Math.min(pick.rows - 1, Math.max(0, Math.floor(normY * pick.rows)));
|
|
||||||
const readCode = (x, y) => pick.buffer[y * pick.cols + x];
|
|
||||||
const center = readCode(cx, cy);
|
|
||||||
if (center > 0) {
|
|
||||||
return center - 1;
|
|
||||||
}
|
|
||||||
if (radiusPx <= 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const r = Math.max(4, radiusPx);
|
|
||||||
const rSq = r * r;
|
|
||||||
for (let dy = -r; dy <= r; dy++) {
|
|
||||||
for (let dx = -r; dx <= r; dx++) {
|
|
||||||
if (dx * dx + dy * dy > rSq) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const x = cx + dx;
|
|
||||||
const y = cy + dy;
|
|
||||||
if (x < 0 || y < 0 || x >= pick.cols || y >= pick.rows) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const code = readCode(x, y);
|
|
||||||
if (code > 0) {
|
|
||||||
return code - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
/* ==========================================================================
|
|
||||||
* 资源释放
|
|
||||||
* ========================================================================== */
|
|
||||||
export function releasePaintResourceLayers(layers) {
|
|
||||||
if (!layers) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
layers.lowFreqImage.dispose();
|
|
||||||
layers.highFreqImage.dispose();
|
|
||||||
}
|
|
||||||
export function releaseOriginSkImage(image) {
|
|
||||||
if (image) {
|
|
||||||
image.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* ==========================================================================
|
|
||||||
* 工作缓冲区缩放
|
|
||||||
* ========================================================================== */
|
|
||||||
export async function prepareWorkScaledBgrBuffer(bgrBuffer, cols, rows, workScale) {
|
|
||||||
if (workScale >= 1) {
|
|
||||||
return { buffer: bgrBuffer, cols, rows };
|
|
||||||
}
|
|
||||||
const workCols = Math.floor(cols * workScale);
|
|
||||||
const workRows = Math.floor(rows * workScale);
|
|
||||||
const buffer = resizeBgrBuffer(bgrBuffer, cols, rows, workCols, workRows);
|
|
||||||
return { buffer, cols: workCols, rows: workRows };
|
|
||||||
}
|
|
||||||
/* ==========================================================================
|
|
||||||
* 分段计时工具(仅开发环境生效)
|
|
||||||
* ========================================================================== */
|
|
||||||
let _timeLogTs = 0;
|
|
||||||
export function timeLog(tag) {
|
|
||||||
if (!__DEV__)
|
|
||||||
return;
|
|
||||||
const now = performance.now();
|
|
||||||
const dt = _timeLogTs ? now - _timeLogTs : 0;
|
|
||||||
console.log(`[⏱ ${tag}] ${dt.toFixed(2)} ms`);
|
|
||||||
_timeLogTs = now;
|
|
||||||
}
|
|
||||||
//# sourceMappingURL=canvasGeometry.js.map
|
|
||||||
|
|||||||
1
dist/utils/canvasGeometry.js.map
vendored
1
dist/utils/canvasGeometry.js.map
vendored
File diff suppressed because one or more lines are too long
13
dist/utils/compositePaintedImage.d.ts
vendored
13
dist/utils/compositePaintedImage.d.ts
vendored
@ -16,7 +16,7 @@ export type CompositePaintInput = {
|
|||||||
* Preferred path for rich export: if the caller (MaskSegmentCanvas) provides bytes
|
* Preferred path for rich export: if the caller (MaskSegmentCanvas) provides bytes
|
||||||
* that were produced by makeImageSnapshot() on a high-resolution Canvas rendering the
|
* that were produced by makeImageSnapshot() on a high-resolution Canvas rendering the
|
||||||
* exact same PaintShaderLayer + regionPaint SkSL at work resolution, we write them
|
* exact same PaintShaderLayer + regionPaint SkSL at work resolution, we write them
|
||||||
* directly. This captures the live editor 质感 (lighting + high/low-freq texture)
|
* directly. This captures the live editor appearance (lighting + high/low-freq texture)
|
||||||
* without CPU pixel math and without a second declarative drawAsImage.
|
* without CPU pixel math and without a second declarative drawAsImage.
|
||||||
*/
|
*/
|
||||||
exportPngBytes?: Uint8Array;
|
exportPngBytes?: Uint8Array;
|
||||||
@ -34,11 +34,10 @@ export type CompositePaintInput = {
|
|||||||
renderWidth?: number;
|
renderWidth?: number;
|
||||||
renderHeight?: number;
|
renderHeight?: number;
|
||||||
};
|
};
|
||||||
/** 将上色区域导出为 recolored PNG。
|
/** Export painted regions as a recolored PNG.
|
||||||
* 优先级(从好到保底):
|
* Priority (best to fallback):
|
||||||
* 1. exportPngBytes(调用方用 makeImageSnapshot 在高分辨率 Canvas 上捕获的完整 shader 结果)—— 推荐的“保存快照”路径,无 CPU 逐像素,无二次 drawAsImage。
|
* 1. exportPngBytes (full shader result captured by caller via makeImageSnapshot on high-res Canvas) — recommended "snapshot" path, no CPU per-pixel, no secondary drawAsImage.
|
||||||
* 2. shaderTextures + render*(通过 renderPaintedImageOffscreen / drawAsImage 重建同一套 PaintShaderLayer + SkSL)。
|
* 2. shaderTextures + render* (rebuild same PaintShaderLayer + SkSL via renderPaintedImageOffscreen / drawAsImage).
|
||||||
* 3. CPU 逐像素 recolor(flat,无光照/纹理,仅作最后兜底,保证保存不中断)。
|
* 3. CPU per-pixel recolor (flat, no lighting/texture, last-resort fallback ensuring save never breaks).
|
||||||
*/
|
*/
|
||||||
export declare function compositePaintedImage(input: CompositePaintInput): Promise<SavePaintResult>;
|
export declare function compositePaintedImage(input: CompositePaintInput): Promise<SavePaintResult>;
|
||||||
//# sourceMappingURL=compositePaintedImage.d.ts.map
|
|
||||||
1
dist/utils/compositePaintedImage.d.ts.map
vendored
1
dist/utils/compositePaintedImage.d.ts.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"compositePaintedImage.d.ts","sourceRoot":"","sources":["../../src/utils/compositePaintedImage.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AACvF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAM1D,MAAM,MAAM,mBAAmB,GAAG;IAChC,YAAY,EAAE,UAAU,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,UAAU,CAAC;IACvB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,UAAU,CAAC;IAC5B;;;OAGG;IACH,cAAc,CAAC,EAAE;QACf,WAAW,EAAE,OAAO,CAAC;QACrB,aAAa,EAAE,OAAO,CAAC;QACvB,YAAY,EAAE,OAAO,CAAC;QACtB,aAAa,EAAE,OAAO,CAAC;KACxB,CAAC;IACF,qGAAqG;IACrG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAyCF;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,eAAe,CAAC,CA6G1B"}
|
|
||||||
147
dist/utils/compositePaintedImage.js
vendored
147
dist/utils/compositePaintedImage.js
vendored
@ -1,146 +1 @@
|
|||||||
import { Buffer } from 'buffer';
|
"use strict";var U=Object.create;var y=Object.defineProperty;var E=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames;var M=Object.getPrototypeOf,v=Object.prototype.hasOwnProperty;var R=(e,t)=>{for(var r in t)y(e,r,{get:t[r],enumerable:!0})},A=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of V(t))!v.call(e,s)&&s!==r&&y(e,s,{get:()=>t[s],enumerable:!(n=E(t,s))||n.enumerable});return e};var S=(e,t,r)=>(r=e!=null?U(M(e)):{},A(t||!e||!e.__esModule?y(r,"default",{value:e,enumerable:!0}):r,e)),T=e=>A(y({},"__esModule",{value:!0}),e);var z={};R(z,{compositePaintedImage:()=>Z});module.exports=T(z);var C=require("buffer"),k=S(require("react-native-fs")),x=S(require("upng-js")),F=require("./paintShaderRuntime"),I=require("./exportUtils");function q(e,t,r,n,s){const m=n*s,c=new Uint8Array(m*4),d=new Map;for(const[a,g]of r)d.set(a+1,g);for(let a=0;a<m;a++){const g=t[a],l=g>0?d.get(g):void 0,i=a*4;if(l)c[i]=l.r,c[i+1]=l.g,c[i+2]=l.b,c[i+3]=255;else{const f=a*3;c[i]=e[f+2],c[i+1]=e[f+1],c[i+2]=e[f],c[i+3]=255}}const h=x.default.encode([c.buffer],n,s,0);return new Uint8Array(h)}async function Z(e){const{originBuffer:t,cols:r,rows:n,pickBuffer:s,paintedRegions:m,destDir:c,exportPngBase64:d,exportPngBytes:h,shaderTextures:a,renderWidth:g,renderHeight:l}=e;if(m.size===0)throw new Error("No painted regions, cannot save");if(s.length!==r*n){const o="pickMap size does not match image";throw console.error("[VIZ-SAVE] composite will throw:",o,{pickLen:s.length,expected:r*n,cols:r,rows:n}),new Error(o)}if(t.length!==r*n*3){const o="Original buffer size does not match image";throw console.error("[VIZ-SAVE] composite will throw:",o,{originLen:t.length,expected:r*n*3,cols:r,rows:n}),new Error(o)}let i,f,B=!1,P=!1;if(d&&d.length>0)f=d,B=!0;else if(h&&h.length>0)i=h,B=!0;else if(a&&g&&l)try{const o=await(0,F.renderPaintedImageOffscreen)({originImage:a.originImage,paintColorMap:a.paintColorMap,lowFreqImage:a.lowFreqImage,highFreqImage:a.highFreqImage,width:g,height:l,showOrigin:!1});if(o){let w="";try{const p=o.encodeToBase64;typeof p=="function"&&(w=p.call(o)||"")}catch(p){console.warn("[VIZ-SAVE] offscreen encodeToBase64 failed:",p)}w&&w.length>0&&(i=new Uint8Array(C.Buffer.from(w,"base64")),P=!0);try{const p=o.dispose;typeof p=="function"&&p.call(o)}catch{}}}catch(o){console.warn("[VIZ-SAVE] rich shader offscreen for export failed (will fallback):",o)}if(!f&&!i)try{i=q(t,s,m,r,n)}catch{throw new Error("CPU recolor PNG decoding failed")}const b=c??k.default.CachesDirectoryPath,u=`${b}/painted_${Date.now()}.png`;try{f?await(0,I.writePngBase64ToFile)(u,f):await(0,I.writePngBytesToFile)(u,i)}catch(o){throw console.error("[VIZ-SAVE] composite writeFile threw:",o,{filePath:u,dir:b}),o}return{filePath:u,width:r,height:n,paintedCount:m.size}}
|
||||||
import RNFS from 'react-native-fs';
|
|
||||||
// upng-js: used for PNG encode of CPU recolor.
|
|
||||||
import UPNG from 'upng-js';
|
|
||||||
import { renderPaintedImageOffscreen } from './paintShaderRuntime';
|
|
||||||
import { writePngBase64ToFile, writePngBytesToFile } from './exportUtils';
|
|
||||||
/**
|
|
||||||
* CPU recolor: directly map pick codes to painted BGR colors (or copy origin).
|
|
||||||
* Produces RGBA PNG bytes via upng-js. This is the *fallback* path when rich shader offscreen
|
|
||||||
* is not available or fails. It produces flat colors without the editor's lighting + freq texture.
|
|
||||||
*/
|
|
||||||
function cpuRecolorToPngBytes(originBgr, pickBuffer, paintedRegions, cols, rows) {
|
|
||||||
const pixelCount = cols * rows;
|
|
||||||
const rgba = new Uint8Array(pixelCount * 4);
|
|
||||||
const colorByPickCode = new Map();
|
|
||||||
for (const [regionId, color] of paintedRegions) {
|
|
||||||
colorByPickCode.set(regionId + 1, color);
|
|
||||||
}
|
|
||||||
for (let i = 0; i < pixelCount; i++) {
|
|
||||||
const code = pickBuffer[i];
|
|
||||||
const color = code > 0 ? colorByPickCode.get(code) : undefined;
|
|
||||||
const d = i * 4;
|
|
||||||
if (color) {
|
|
||||||
rgba[d] = color.r;
|
|
||||||
rgba[d + 1] = color.g;
|
|
||||||
rgba[d + 2] = color.b;
|
|
||||||
rgba[d + 3] = 255;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const s = i * 3;
|
|
||||||
rgba[d] = originBgr[s + 2]; // RGB <- BGR
|
|
||||||
rgba[d + 1] = originBgr[s + 1];
|
|
||||||
rgba[d + 2] = originBgr[s];
|
|
||||||
rgba[d + 3] = 255;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const png = UPNG.encode([rgba.buffer], cols, rows, 0);
|
|
||||||
return new Uint8Array(png);
|
|
||||||
}
|
|
||||||
/** 将上色区域导出为 recolored PNG。
|
|
||||||
* 优先级(从好到保底):
|
|
||||||
* 1. exportPngBytes(调用方用 makeImageSnapshot 在高分辨率 Canvas 上捕获的完整 shader 结果)—— 推荐的“保存快照”路径,无 CPU 逐像素,无二次 drawAsImage。
|
|
||||||
* 2. shaderTextures + render*(通过 renderPaintedImageOffscreen / drawAsImage 重建同一套 PaintShaderLayer + SkSL)。
|
|
||||||
* 3. CPU 逐像素 recolor(flat,无光照/纹理,仅作最后兜底,保证保存不中断)。
|
|
||||||
*/
|
|
||||||
export async function compositePaintedImage(input) {
|
|
||||||
const { originBuffer, cols, rows, pickBuffer, paintedRegions, destDir, exportPngBase64, exportPngBytes, shaderTextures, renderWidth, renderHeight, } = input;
|
|
||||||
if (paintedRegions.size === 0) {
|
|
||||||
throw new Error('No painted regions, cannot save');
|
|
||||||
}
|
|
||||||
if (pickBuffer.length !== cols * rows) {
|
|
||||||
const msg = 'pickMap size does not match image';
|
|
||||||
console.error('[VIZ-SAVE] composite will throw:', msg, { pickLen: pickBuffer.length, expected: cols * rows, cols, rows });
|
|
||||||
throw new Error(msg);
|
|
||||||
}
|
|
||||||
if (originBuffer.length !== cols * rows * 3) {
|
|
||||||
const msg = 'Original buffer size does not match image';
|
|
||||||
console.error('[VIZ-SAVE] composite will throw:', msg, { originLen: originBuffer.length, expected: cols * rows * 3, cols, rows });
|
|
||||||
throw new Error(msg);
|
|
||||||
}
|
|
||||||
let pngBytesForWrite;
|
|
||||||
let pngBase64ForWrite;
|
|
||||||
let usedSnapshot = false;
|
|
||||||
let usedRichShader = false;
|
|
||||||
// 1) Highest priority: pre-encoded PNG base64 from makeImageSnapshot (no extra conversion).
|
|
||||||
if (exportPngBase64 && exportPngBase64.length > 0) {
|
|
||||||
pngBase64ForWrite = exportPngBase64;
|
|
||||||
usedSnapshot = true;
|
|
||||||
}
|
|
||||||
// 1b) Snapshot bytes fallback (legacy callers).
|
|
||||||
else if (exportPngBytes && exportPngBytes.length > 0) {
|
|
||||||
pngBytesForWrite = exportPngBytes;
|
|
||||||
usedSnapshot = true;
|
|
||||||
}
|
|
||||||
// 2) 回退 rich:用 live 纹理 + drawAsImage 重建与编辑器一致的 shader 结果(带光照 + 频率纹理)。
|
|
||||||
else if (shaderTextures && renderWidth && renderHeight) {
|
|
||||||
try {
|
|
||||||
const offImg = await renderPaintedImageOffscreen({
|
|
||||||
originImage: shaderTextures.originImage,
|
|
||||||
paintColorMap: shaderTextures.paintColorMap,
|
|
||||||
lowFreqImage: shaderTextures.lowFreqImage,
|
|
||||||
highFreqImage: shaderTextures.highFreqImage,
|
|
||||||
width: renderWidth,
|
|
||||||
height: renderHeight,
|
|
||||||
showOrigin: false,
|
|
||||||
});
|
|
||||||
if (offImg) {
|
|
||||||
let b64 = '';
|
|
||||||
try {
|
|
||||||
const enc = offImg.encodeToBase64;
|
|
||||||
if (typeof enc === 'function') {
|
|
||||||
b64 = enc.call(offImg) || '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (encErr) {
|
|
||||||
console.warn('[VIZ-SAVE] offscreen encodeToBase64 failed:', encErr);
|
|
||||||
}
|
|
||||||
if (b64 && b64.length > 0) {
|
|
||||||
pngBytesForWrite = new Uint8Array(Buffer.from(b64, 'base64'));
|
|
||||||
usedRichShader = true;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const disp = offImg.dispose;
|
|
||||||
if (typeof disp === 'function')
|
|
||||||
disp.call(offImg);
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
console.warn('[VIZ-SAVE] rich shader offscreen for export failed (will fallback):', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 3) 最后兜底:CPU 逐像素(flat 颜色,无 editor 质感)。
|
|
||||||
if (!pngBase64ForWrite && !pngBytesForWrite) {
|
|
||||||
try {
|
|
||||||
pngBytesForWrite = cpuRecolorToPngBytes(originBuffer, pickBuffer, paintedRegions, cols, rows);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
throw new Error('CPU recolor PNG decoding failed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const dir = destDir ?? RNFS.CachesDirectoryPath;
|
|
||||||
const filePath = `${dir}/painted_${Date.now()}.png`;
|
|
||||||
try {
|
|
||||||
if (pngBase64ForWrite) {
|
|
||||||
await writePngBase64ToFile(filePath, pngBase64ForWrite);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
await writePngBytesToFile(filePath, pngBytesForWrite);
|
|
||||||
}
|
|
||||||
void usedSnapshot;
|
|
||||||
void usedRichShader;
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
console.error('[VIZ-SAVE] composite writeFile threw:', e, { filePath, dir });
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
filePath,
|
|
||||||
width: cols,
|
|
||||||
height: rows,
|
|
||||||
paintedCount: paintedRegions.size,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
//# sourceMappingURL=compositePaintedImage.js.map
|
|
||||||
|
|||||||
1
dist/utils/compositePaintedImage.js.map
vendored
1
dist/utils/compositePaintedImage.js.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"compositePaintedImage.js","sourceRoot":"","sources":["../../src/utils/compositePaintedImage.ts"],"names":[],"mappings":"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"}
|
|
||||||
1
dist/utils/exportUtils.d.ts
vendored
1
dist/utils/exportUtils.d.ts
vendored
@ -17,4 +17,3 @@ export declare function resolveExportResultForDestDir(cached: {
|
|||||||
paintedCount: number;
|
paintedCount: number;
|
||||||
previewPath?: string;
|
previewPath?: string;
|
||||||
}>;
|
}>;
|
||||||
//# sourceMappingURL=exportUtils.d.ts.map
|
|
||||||
1
dist/utils/exportUtils.d.ts.map
vendored
1
dist/utils/exportUtils.d.ts.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"exportUtils.d.ts","sourceRoot":"","sources":["../../src/utils/exportUtils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uCAAuC,CAAC;AAEtE,mFAAmF;AACnF,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,MAAM,CAMhF;AAED,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1F;AAED,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5F;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAsB,6BAA6B,CACjD,MAAM,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EACvG,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAW1G"}
|
|
||||||
33
dist/utils/exportUtils.js
vendored
33
dist/utils/exportUtils.js
vendored
@ -1,32 +1 @@
|
|||||||
import { Buffer } from 'buffer';
|
"use strict";var f=Object.create;var o=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var u=Object.getOwnPropertyNames;var l=Object.getPrototypeOf,h=Object.prototype.hasOwnProperty;var w=(t,r)=>{for(var e in r)o(t,e,{get:r[e],enumerable:!0})},a=(t,r,e,i)=>{if(r&&typeof r=="object"||typeof r=="function")for(let n of u(r))!h.call(t,n)&&n!==e&&o(t,n,{get:()=>r[n],enumerable:!(i=m(r,n))||i.enumerable});return t};var P=(t,r,e)=>(e=t!=null?f(l(t)):{},a(r||!t||!t.__esModule?o(e,"default",{value:t,enumerable:!0}):e,t)),b=t=>a(o({},"__esModule",{value:!0}),t);var v={};w(v,{paintedRegionsFingerprint:()=>F,resolveExportResultForDestDir:()=>x,stripFilePrefix:()=>p,writePngBase64ToFile:()=>y,writePngBytesToFile:()=>c});module.exports=b(v);var g=require("buffer"),s=P(require("react-native-fs"));function F(t){return t.size===0?"":[...t.entries()].sort((e,i)=>e[0]-i[0]).map(([e,i])=>`${e}:${i.r},${i.g},${i.b}`).join("|")}async function y(t,r){await s.default.writeFile(t,r,"base64")}async function c(t,r){await s.default.writeFile(t,g.Buffer.from(r).toString("base64"),"base64")}function p(t){return t.startsWith("file://")?t.slice(7):t}async function x(t,r){if(!r)return t;const e=p(t.filePath);if(e.startsWith(r))return t;const i=`${r}/painted_${Date.now()}.png`;return await s.default.copyFile(e,i),{...t,filePath:i}}
|
||||||
import RNFS from 'react-native-fs';
|
|
||||||
/** Stable fingerprint for painted region colors — used to reuse cached exports. */
|
|
||||||
export function paintedRegionsFingerprint(painted) {
|
|
||||||
if (painted.size === 0) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
const entries = [...painted.entries()].sort((a, b) => a[0] - b[0]);
|
|
||||||
return entries.map(([id, c]) => `${id}:${c.r},${c.g},${c.b}`).join('|');
|
|
||||||
}
|
|
||||||
export async function writePngBase64ToFile(filePath, base64) {
|
|
||||||
await RNFS.writeFile(filePath, base64, 'base64');
|
|
||||||
}
|
|
||||||
export async function writePngBytesToFile(filePath, bytes) {
|
|
||||||
await RNFS.writeFile(filePath, Buffer.from(bytes).toString('base64'), 'base64');
|
|
||||||
}
|
|
||||||
export function stripFilePrefix(uri) {
|
|
||||||
return uri.startsWith('file://') ? uri.slice('file://'.length) : uri;
|
|
||||||
}
|
|
||||||
export async function resolveExportResultForDestDir(cached, destDir) {
|
|
||||||
if (!destDir) {
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
const src = stripFilePrefix(cached.filePath);
|
|
||||||
if (src.startsWith(destDir)) {
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
const filePath = `${destDir}/painted_${Date.now()}.png`;
|
|
||||||
await RNFS.copyFile(src, filePath);
|
|
||||||
return { ...cached, filePath };
|
|
||||||
}
|
|
||||||
//# sourceMappingURL=exportUtils.js.map
|
|
||||||
|
|||||||
1
dist/utils/exportUtils.js.map
vendored
1
dist/utils/exportUtils.js.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"exportUtils.js","sourceRoot":"","sources":["../../src/utils/exportUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,IAAI,MAAM,iBAAiB,CAAC;AAGnC,mFAAmF;AACnF,MAAM,UAAU,yBAAyB,CAAC,OAA8B;IACtE,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC,EAAE;QACtB,OAAO,EAAE,CAAC;KACX;IACD,MAAM,OAAO,GAAG,CAAC,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC1E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,QAAgB,EAAE,MAAc;IACzE,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,QAAgB,EAAE,KAAiB;IAC3E,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC;AAClF,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,OAAO,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AACvE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,MAAuG,EACvG,OAAgB;IAEhB,IAAI,CAAC,OAAO,EAAE;QACZ,OAAO,MAAM,CAAC;KACf;IACD,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC7C,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE;QAC3B,OAAO,MAAM,CAAC;KACf;IACD,MAAM,QAAQ,GAAG,GAAG,OAAO,YAAY,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC;IACxD,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACnC,OAAO,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,CAAC;AACjC,CAAC"}
|
|
||||||
13
dist/utils/freqLayerPrep.d.ts
vendored
13
dist/utils/freqLayerPrep.d.ts
vendored
@ -8,9 +8,9 @@ export type PaintResourceBatch = {
|
|||||||
originImage: SkImage;
|
originImage: SkImage;
|
||||||
layers: FreqLayerImages;
|
layers: FreqLayerImages;
|
||||||
};
|
};
|
||||||
/** OpenCV 8-bit Lab L 通道(BGR 输入,供单测与近似对照) */
|
/** OpenCV 8-bit Lab L channel (BGR input, for single test and approximate comparison) */
|
||||||
export declare function bgrToLabL(b: number, g: number, r: number): number;
|
export declare function bgrToLabL(b: number, g: number, r: number): number;
|
||||||
/** BGR → 8-bit Lab(L/a/b 均映射到 0–255) */
|
/** BGR → 8-bit Lab (L/a/b mapped to 0–255) */
|
||||||
export declare function bgrToLab(b: number, g: number, r: number): {
|
export declare function bgrToLab(b: number, g: number, r: number): {
|
||||||
l: number;
|
l: number;
|
||||||
a: number;
|
a: number;
|
||||||
@ -18,12 +18,11 @@ export declare function bgrToLab(b: number, g: number, r: number): {
|
|||||||
};
|
};
|
||||||
export declare function bgrBufferToRgbaBuffer(bgr: Uint8Array, cols: number, rows: number): Uint8Array;
|
export declare function bgrBufferToRgbaBuffer(bgr: Uint8Array, cols: number, rows: number): Uint8Array;
|
||||||
export declare function releaseFreqLayerImages(layers: FreqLayerImages | null): void;
|
export declare function releaseFreqLayerImages(layers: FreqLayerImages | null): void;
|
||||||
/** 复用已上传的 BGR Mat,避免重复 bufferToMat + JS↔原生往返 */
|
/** reuse the uploaded BGR Mat, avoid duplicate bufferToMat + JS↔native roundtrip */
|
||||||
export declare function prepareFreqLayersFromWorkMat(workMat: WrappedMat, cols: number, rows: number): Promise<FreqLayerImages | null>;
|
export declare function prepareFreqLayersFromWorkMat(workMat: WrappedMat, cols: number, rows: number): Promise<FreqLayerImages | null>;
|
||||||
/** 单次 Mat 上传 → 高低频 + 原图 Skia(并行,高低频先就绪时可回调) */
|
/** single time Mat upload → high/low frequency + original Skia (parallel, callback when high/low frequency is ready) */
|
||||||
export declare function preparePaintResourcesFromWorkBuffer(bgrBuffer: Uint8Array, cols: number, rows: number, onFreqLayersReady?: (layers: FreqLayerImages) => void): Promise<PaintResourceBatch | null>;
|
export declare function preparePaintResourcesFromWorkBuffer(bgrBuffer: Uint8Array, cols: number, rows: number, onFreqLayersReady?: (layers: FreqLayerImages) => void): Promise<PaintResourceBatch | null>;
|
||||||
/** @deprecated 测试兼容;生产路径请用 preparePaintResourcesFromWorkBuffer */
|
/** @deprecated test compatibility; production path please use preparePaintResourcesFromWorkBuffer */
|
||||||
export declare function prepareFreqLayersFromBgrBuffer(bgrBuffer: Uint8Array, cols: number, rows: number): Promise<FreqLayerImages | null>;
|
export declare function prepareFreqLayersFromBgrBuffer(bgrBuffer: Uint8Array, cols: number, rows: number): Promise<FreqLayerImages | null>;
|
||||||
/** 原图 BGR → Skia RGBA(OpenCV cvtColor,与 freq 并行) */
|
/** original BGR → Skia RGBA (OpenCV cvtColor, parallel with freq) */
|
||||||
export declare function originBgrBufferToSkiaImage(bgrBuffer: Uint8Array, cols: number, rows: number): Promise<SkImage | null>;
|
export declare function originBgrBufferToSkiaImage(bgrBuffer: Uint8Array, cols: number, rows: number): Promise<SkImage | null>;
|
||||||
//# sourceMappingURL=freqLayerPrep.d.ts.map
|
|
||||||
1
dist/utils/freqLayerPrep.d.ts.map
vendored
1
dist/utils/freqLayerPrep.d.ts.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"freqLayerPrep.d.ts","sourceRoot":"","sources":["../../src/utils/freqLayerPrep.ts"],"names":[],"mappings":"AAAA,OAAW,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAG1D,MAAM,MAAM,eAAe,GAAG;IAC5B,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,eAAe,CAAC;CACzB,CAAC;AAEF,6CAA6C;AAC7C,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,wCAAwC;AACxC,wBAAgB,QAAQ,CACtB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,GACR;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAkCrC;AAED,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,UAAU,EACf,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,UAAU,CAYZ;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,QAGpE;AAyDD,gDAAgD;AAChD,wBAAsB,4BAA4B,CAChD,OAAO,EAAE,UAAU,EACnB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAyEjC;AAED,+CAA+C;AAC/C,wBAAsB,mCAAmC,CACvD,SAAS,EAAE,UAAU,EACrB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,GACpD,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CA8BpC;AAED,kEAAkE;AAClE,wBAAsB,8BAA8B,CAClD,SAAS,EAAE,UAAU,EACrB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAWjC;AAED,oDAAoD;AACpD,wBAAsB,0BAA0B,CAC9C,SAAS,EAAE,UAAU,EACrB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAEzB"}
|
|
||||||
179
dist/utils/freqLayerPrep.js
vendored
179
dist/utils/freqLayerPrep.js
vendored
@ -1,178 +1 @@
|
|||||||
import cv from './opencvAdapter';
|
"use strict";var C=Object.create;var h=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var B=Object.getOwnPropertyNames;var W=Object.getPrototypeOf,T=Object.prototype.hasOwnProperty;var _=(e,r)=>{for(var t in r)h(e,t,{get:r[t],enumerable:!0})},I=(e,r,t,a)=>{if(r&&typeof r=="object"||typeof r=="function")for(let n of B(r))!T.call(e,n)&&n!==t&&h(e,n,{get:()=>r[n],enumerable:!(a=k(r,n))||a.enumerable});return e};var P=(e,r,t)=>(t=e!=null?C(W(e)):{},I(r||!e||!e.__esModule?h(t,"default",{value:e,enumerable:!0}):t,e)),R=e=>I(h({},"__esModule",{value:!0}),e);var H={};_(H,{bgrBufferToRgbaBuffer:()=>A,bgrToLab:()=>L,bgrToLabL:()=>v,originBgrBufferToSkiaImage:()=>G,prepareFreqLayersFromBgrBuffer:()=>E,prepareFreqLayersFromWorkMat:()=>w,preparePaintResourcesFromWorkBuffer:()=>V,releaseFreqLayerImages:()=>q});module.exports=R(H);var o=P(require("./opencvAdapter")),y=require("./maskSegmentRuntime");function v(e,r,t){return L(e,r,t).l}function L(e,r,t){let a=t/255,n=r/255,i=e/255;a=a>.04045?Math.pow((a+.055)/1.055,2.4):a/12.92,n=n>.04045?Math.pow((n+.055)/1.055,2.4):n/12.92,i=i>.04045?Math.pow((i+.055)/1.055,2.4):i/12.92;const s=a*.412453+n*.35758+i*.180423,l=a*.212671+n*.71516+i*.072169,m=a*.019334+n*.119193+i*.950227,c=.950456,f=1,g=1.088754,u=6/29,b=u*u*u;let M=s/c,p=l/f,d=m/g;M=M>b?Math.cbrt(M):M/(3*u*u)+4/29,p=p>b?Math.cbrt(p):p/(3*u*u)+4/29,d=d>b?Math.cbrt(d):d/(3*u*u)+4/29;const x=p*116-16,F=500*(M-p),S=200*(p-d);return{l:Math.max(0,Math.min(255,Math.round(x*255/100))),a:Math.max(0,Math.min(255,Math.round(F+128))),b:Math.max(0,Math.min(255,Math.round(S+128)))}}function A(e,r,t){const a=r*t,n=new Uint8Array(a*4);for(let i=0;i<a;i++){const s=i*3,l=i*4;n[l]=e[s+2],n[l+1]=e[s+1],n[l+2]=e[s],n[l+3]=255}return n}function q(e){e?.lowFreqImage.dispose(),e?.highFreqImage.dispose()}async function U(e,r,t,a,n){const i=o.default.createMat(t,a,1),s=o.default.createMat(t,a,1),l=o.default.createMat(t,a,1),m=o.default.createMat(t,a,1);try{return o.default.convertTo(e,i,o.default.CV_16SC1),o.default.convertTo(r,s,o.default.CV_16SC1),await o.default.subtract(i,s,l),await o.default.addWeighted(l,n,null,0,128,l),o.default.convertTo(l,m,o.default.CV_8UC1),m}finally{i.release(),s.release(),l.release()}}async function z(e,r,t){const a=(0,y.getMaskSegmentRuntimeConfig)().pipeline.paintFreqMaxLongSide,n=Math.max(r,t);if(n<=a)return{mat:e,cols:r,rows:t,owned:!1};const i=a/n,s=Math.floor(r*i),l=Math.floor(t*i),m=o.default.createMat(s,l,3);return await o.default.resize(e,m,{width:s,height:l},o.default.INTER_LINEAR),{mat:m,cols:s,rows:l,owned:!0}}async function w(e,r,t){const a=(0,y.getMaskSegmentRuntimeConfig)().paint,n=__DEV__?performance.now():0,i=await z(e,r,t),s=i.mat,l=i.cols,m=i.rows;let c=null,f=null,g=null,u=null;try{c=o.default.cvtColorBgr(s,o.default.COLOR_BGR2Lab),f=o.default.createMat(l,m,1),o.default.extractChannel(c,f,0),c.release(),c=null,g=o.default.createMat(l,m,1);const b=a.lLowBlurKernel;await o.default.GaussianBlur(f,g,{width:b,height:b},0),u=await U(f,g,l,m,a.lHighGain),await o.default.addWeighted(g,a.lLowContrast,null,0,128*(1-a.lLowContrast),g),await o.default.addWeighted(g,a.lLowBrightness,null,0,128*(1-a.lLowBrightness),g);const M=o.default.grayMatToSkiaImage(g),p=o.default.grayMatToSkiaImage(u);return!M||!p?(M?.dispose(),p?.dispose(),null):{lowFreqImage:M,highFreqImage:p}}finally{i.owned&&s.release(),c?.release(),f?.release(),g?.release(),u?.release()}}async function V(e,r,t,a){const n=r*t;if(e.length!==n*3)return null;const i=__DEV__?performance.now():0,s=o.default.bgrBufferToMat(e,r,t);try{const l=o.default.matToSkiaImage(s),c=await w(s,r,t);if(!c)return(await l)?.dispose(),null;a?.(c);const f=await l;return f?{originImage:f,layers:c}:(q(c),null)}finally{s.release()}}async function E(e,r,t){const a=r*t;if(e.length!==a*3)return null;const n=o.default.bgrBufferToMat(e,r,t);try{return await w(n,r,t)}finally{n.release()}}async function G(e,r,t){return o.default.bgrBufferToSkiaImage(e,r,t)}
|
||||||
import { getMaskSegmentRuntimeConfig } from './maskSegmentRuntime';
|
|
||||||
/** OpenCV 8-bit Lab L 通道(BGR 输入,供单测与近似对照) */
|
|
||||||
export function bgrToLabL(b, g, r) {
|
|
||||||
return bgrToLab(b, g, r).l;
|
|
||||||
}
|
|
||||||
/** BGR → 8-bit Lab(L/a/b 均映射到 0–255) */
|
|
||||||
export function bgrToLab(b, g, r) {
|
|
||||||
let rf = r / 255;
|
|
||||||
let gf = g / 255;
|
|
||||||
let bf = b / 255;
|
|
||||||
rf = rf > 0.04045 ? Math.pow((rf + 0.055) / 1.055, 2.4) : rf / 12.92;
|
|
||||||
gf = gf > 0.04045 ? Math.pow((gf + 0.055) / 1.055, 2.4) : gf / 12.92;
|
|
||||||
bf = bf > 0.04045 ? Math.pow((bf + 0.055) / 1.055, 2.4) : bf / 12.92;
|
|
||||||
const x = rf * 0.412453 + gf * 0.35758 + bf * 0.180423;
|
|
||||||
const y = rf * 0.212671 + gf * 0.71516 + bf * 0.072169;
|
|
||||||
const z = rf * 0.019334 + gf * 0.119193 + bf * 0.950227;
|
|
||||||
const xn = 0.950456;
|
|
||||||
const yn = 1;
|
|
||||||
const zn = 1.088754;
|
|
||||||
const delta = 6 / 29;
|
|
||||||
const delta3 = delta * delta * delta;
|
|
||||||
let fx = x / xn;
|
|
||||||
let fy = y / yn;
|
|
||||||
let fz = z / zn;
|
|
||||||
fx = fx > delta3 ? Math.cbrt(fx) : fx / (3 * delta * delta) + 4 / 29;
|
|
||||||
fy = fy > delta3 ? Math.cbrt(fy) : fy / (3 * delta * delta) + 4 / 29;
|
|
||||||
fz = fz > delta3 ? Math.cbrt(fz) : fz / (3 * delta * delta) + 4 / 29;
|
|
||||||
const L = fy * 116 - 16;
|
|
||||||
const a = 500 * (fx - fy);
|
|
||||||
const bLab = 200 * (fy - fz);
|
|
||||||
return {
|
|
||||||
l: Math.max(0, Math.min(255, Math.round((L * 255) / 100))),
|
|
||||||
a: Math.max(0, Math.min(255, Math.round(a + 128))),
|
|
||||||
b: Math.max(0, Math.min(255, Math.round(bLab + 128))),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
export function bgrBufferToRgbaBuffer(bgr, cols, rows) {
|
|
||||||
const pixelCount = cols * rows;
|
|
||||||
const rgba = new Uint8Array(pixelCount * 4);
|
|
||||||
for (let i = 0; i < pixelCount; i++) {
|
|
||||||
const s = i * 3;
|
|
||||||
const d = i * 4;
|
|
||||||
rgba[d] = bgr[s + 2];
|
|
||||||
rgba[d + 1] = bgr[s + 1];
|
|
||||||
rgba[d + 2] = bgr[s];
|
|
||||||
rgba[d + 3] = 255;
|
|
||||||
}
|
|
||||||
return rgba;
|
|
||||||
}
|
|
||||||
export function releaseFreqLayerImages(layers) {
|
|
||||||
layers?.lowFreqImage.dispose();
|
|
||||||
layers?.highFreqImage.dispose();
|
|
||||||
}
|
|
||||||
/** 16-bit 有符号差分 → 8-bit 高频层(detail * gain + 128) */
|
|
||||||
async function buildHighFreqMatNative(lMat, lLowMat, cols, rows, gain) {
|
|
||||||
const l16 = cv.createMat(cols, rows, 1);
|
|
||||||
const lLow16 = cv.createMat(cols, rows, 1);
|
|
||||||
const diff16 = cv.createMat(cols, rows, 1);
|
|
||||||
const high8 = cv.createMat(cols, rows, 1);
|
|
||||||
try {
|
|
||||||
cv.convertTo(lMat, l16, cv.CV_16SC1);
|
|
||||||
cv.convertTo(lLowMat, lLow16, cv.CV_16SC1);
|
|
||||||
await cv.subtract(l16, lLow16, diff16);
|
|
||||||
await cv.addWeighted(diff16, gain, null, 0, 128, diff16);
|
|
||||||
cv.convertTo(diff16, high8, cv.CV_8UC1);
|
|
||||||
return high8;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
l16.release();
|
|
||||||
lLow16.release();
|
|
||||||
diff16.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async function downscaleMatForFreq(workMat, cols, rows) {
|
|
||||||
const maxLongSide = getMaskSegmentRuntimeConfig().pipeline.paintFreqMaxLongSide;
|
|
||||||
const longSide = Math.max(cols, rows);
|
|
||||||
if (longSide <= maxLongSide) {
|
|
||||||
return { mat: workMat, cols, rows, owned: false };
|
|
||||||
}
|
|
||||||
const scale = maxLongSide / longSide;
|
|
||||||
const freqCols = Math.floor(cols * scale);
|
|
||||||
const freqRows = Math.floor(rows * scale);
|
|
||||||
const resized = cv.createMat(freqCols, freqRows, 3);
|
|
||||||
await cv.resize(workMat, resized, { width: freqCols, height: freqRows }, cv.INTER_LINEAR);
|
|
||||||
return { mat: resized, cols: freqCols, rows: freqRows, owned: true };
|
|
||||||
}
|
|
||||||
/** 复用已上传的 BGR Mat,避免重复 bufferToMat + JS↔原生往返 */
|
|
||||||
export async function prepareFreqLayersFromWorkMat(workMat, cols, rows) {
|
|
||||||
const paintCfg = getMaskSegmentRuntimeConfig().paint;
|
|
||||||
const blurStart = __DEV__ ? performance.now() : 0;
|
|
||||||
const scaled = await downscaleMatForFreq(workMat, cols, rows);
|
|
||||||
const freqMat = scaled.mat;
|
|
||||||
const freqCols = scaled.cols;
|
|
||||||
const freqRows = scaled.rows;
|
|
||||||
let labMat = null;
|
|
||||||
let lMat = null;
|
|
||||||
let lLowMat = null;
|
|
||||||
let lHighMat = null;
|
|
||||||
try {
|
|
||||||
labMat = cv.cvtColorBgr(freqMat, cv.COLOR_BGR2Lab);
|
|
||||||
lMat = cv.createMat(freqCols, freqRows, 1);
|
|
||||||
cv.extractChannel(labMat, lMat, 0);
|
|
||||||
labMat.release();
|
|
||||||
labMat = null;
|
|
||||||
lLowMat = cv.createMat(freqCols, freqRows, 1);
|
|
||||||
const kernel = paintCfg.lLowBlurKernel;
|
|
||||||
await cv.GaussianBlur(lMat, lLowMat, { width: kernel, height: kernel }, 0);
|
|
||||||
lHighMat = await buildHighFreqMatNative(lMat, lLowMat, freqCols, freqRows, paintCfg.lHighGain);
|
|
||||||
await cv.addWeighted(lLowMat, paintCfg.lLowContrast, null, 0, 128 * (1 - paintCfg.lLowContrast), lLowMat);
|
|
||||||
await cv.addWeighted(lLowMat, paintCfg.lLowBrightness, null, 0, 128 * (1 - paintCfg.lLowBrightness), lLowMat);
|
|
||||||
const lowFreqImage = cv.grayMatToSkiaImage(lLowMat);
|
|
||||||
const highFreqImage = cv.grayMatToSkiaImage(lHighMat);
|
|
||||||
if (!lowFreqImage || !highFreqImage) {
|
|
||||||
lowFreqImage?.dispose();
|
|
||||||
highFreqImage?.dispose();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return { lowFreqImage, highFreqImage };
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
if (scaled.owned) {
|
|
||||||
freqMat.release();
|
|
||||||
}
|
|
||||||
labMat?.release();
|
|
||||||
lMat?.release();
|
|
||||||
lLowMat?.release();
|
|
||||||
lHighMat?.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/** 单次 Mat 上传 → 高低频 + 原图 Skia(并行,高低频先就绪时可回调) */
|
|
||||||
export async function preparePaintResourcesFromWorkBuffer(bgrBuffer, cols, rows, onFreqLayersReady) {
|
|
||||||
const pixelCount = cols * rows;
|
|
||||||
if (bgrBuffer.length !== pixelCount * 3) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const prepStart = __DEV__ ? performance.now() : 0;
|
|
||||||
const workMat = cv.bgrBufferToMat(bgrBuffer, cols, rows);
|
|
||||||
try {
|
|
||||||
const originPromise = cv.matToSkiaImage(workMat);
|
|
||||||
const freqPromise = prepareFreqLayersFromWorkMat(workMat, cols, rows);
|
|
||||||
const layers = await freqPromise;
|
|
||||||
if (!layers) {
|
|
||||||
const originImage = await originPromise;
|
|
||||||
originImage?.dispose();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
onFreqLayersReady?.(layers);
|
|
||||||
const originImage = await originPromise;
|
|
||||||
if (!originImage) {
|
|
||||||
releaseFreqLayerImages(layers);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return { originImage, layers };
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
workMat.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/** @deprecated 测试兼容;生产路径请用 preparePaintResourcesFromWorkBuffer */
|
|
||||||
export async function prepareFreqLayersFromBgrBuffer(bgrBuffer, cols, rows) {
|
|
||||||
const pixelCount = cols * rows;
|
|
||||||
if (bgrBuffer.length !== pixelCount * 3) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const workMat = cv.bgrBufferToMat(bgrBuffer, cols, rows);
|
|
||||||
try {
|
|
||||||
return await prepareFreqLayersFromWorkMat(workMat, cols, rows);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
workMat.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/** 原图 BGR → Skia RGBA(OpenCV cvtColor,与 freq 并行) */
|
|
||||||
export async function originBgrBufferToSkiaImage(bgrBuffer, cols, rows) {
|
|
||||||
return cv.bgrBufferToSkiaImage(bgrBuffer, cols, rows);
|
|
||||||
}
|
|
||||||
//# sourceMappingURL=freqLayerPrep.js.map
|
|
||||||
|
|||||||
1
dist/utils/freqLayerPrep.js.map
vendored
1
dist/utils/freqLayerPrep.js.map
vendored
File diff suppressed because one or more lines are too long
1
dist/utils/maskOutlinePaths.d.ts
vendored
1
dist/utils/maskOutlinePaths.d.ts
vendored
@ -15,4 +15,3 @@ export declare function buildAllRegionOutlinePaths(regions: SegmentRegion[], mas
|
|||||||
w: number;
|
w: number;
|
||||||
h: number;
|
h: number;
|
||||||
}): Map<number, SkPath>;
|
}): Map<number, SkPath>;
|
||||||
//# sourceMappingURL=maskOutlinePaths.d.ts.map
|
|
||||||
1
dist/utils/maskOutlinePaths.d.ts.map
vendored
1
dist/utils/maskOutlinePaths.d.ts.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"maskOutlinePaths.d.ts","sourceRoot":"","sources":["../../src/utils/maskOutlinePaths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAE/D,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAqcxE,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,aAAa,EAAE,EACxB,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EACpD,QAAQ,CAAC,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAClC,MAAM,CAUR;AA4DD,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,aAAa,EAAE,EACxB,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GACnD,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAoBrB"}
|
|
||||||
420
dist/utils/maskOutlinePaths.js
vendored
420
dist/utils/maskOutlinePaths.js
vendored
File diff suppressed because one or more lines are too long
1
dist/utils/maskOutlinePaths.js.map
vendored
1
dist/utils/maskOutlinePaths.js.map
vendored
File diff suppressed because one or more lines are too long
1
dist/utils/maskSegmentRuntime.d.ts
vendored
1
dist/utils/maskSegmentRuntime.d.ts
vendored
@ -40,4 +40,3 @@ export declare function setMaskSegmentRuntimeConfig(input?: {
|
|||||||
}): ResolvedMaskSegmentRuntime;
|
}): ResolvedMaskSegmentRuntime;
|
||||||
export declare function getMaskSegmentRuntimeConfig(): ResolvedMaskSegmentRuntime;
|
export declare function getMaskSegmentRuntimeConfig(): ResolvedMaskSegmentRuntime;
|
||||||
export declare function resetMaskSegmentRuntimeConfig(): ResolvedMaskSegmentRuntime;
|
export declare function resetMaskSegmentRuntimeConfig(): ResolvedMaskSegmentRuntime;
|
||||||
//# sourceMappingURL=maskSegmentRuntime.d.ts.map
|
|
||||||
1
dist/utils/maskSegmentRuntime.d.ts.map
vendored
1
dist/utils/maskSegmentRuntime.d.ts.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"maskSegmentRuntime.d.ts","sourceRoot":"","sources":["../../src/utils/maskSegmentRuntime.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,iBAAiB,EACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,cAAc,EAEf,MAAM,uCAAuC,CAAC;AAC/C,WAAW;AACX,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,cAAc,CAQlD,CAAC;AAEF,aAAa;AACb,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,cAAc,CAQpD,CAAC;AAEF,UAAU;AACV,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,cAAc,CAQjD,CAAC;AACF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,cAAc,EAAE,QAAQ,CAAC,cAAc,CAAC,CAI7E,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,cAAc,CAAmB,CAAC;AAEjF,wBAAgB,qBAAqB,CACnC,MAAM,CAAC,EAAE,cAAc,EACvB,SAAS,CAAC,EAAE,cAAc,GACzB,QAAQ,CAAC,cAAc,CAAC,CAI1B;AAWD,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,WAAW,CAkBtD,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,QAAQ,CAAC,iBAAiB,CAOlE,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CACxC,IAAI,CACF,iBAAiB,EACf,yBAAyB,GACzB,eAAe,GACf,kBAAkB,GAClB,wBAAwB,CAC3B,CACF,GAAG;IACF,uBAAuB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACrC,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,sBAAsB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACpC,cAAc,EAAE,iBAAiB,EAAE,CAAC;CAwBrC,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC;IACnC,IAAI,EAAE,OAAO,mBAAmB,CAAC;IACjC,KAAK,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC7B,WAAW,EAAE,QAAQ,CAAC,iBAAiB,CAAC,CAAC;CAC1C,CAAC;AAMF,wBAAgB,eAAe,CAC7B,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,mBAAmB,CA0D5B;AAED,wBAAgB,mBAAmB,CAAC,KAAK,CAAC,EAAE;IAC1C,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACvC,GAAG,0BAA0B,CAiB7B;AAKD,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED,wBAAgB,2BAA2B,CAAC,KAAK,CAAC,EAAE;IAClD,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACvC,GAAG,0BAA0B,CAiB7B;AAED,wBAAgB,2BAA2B,IAAI,0BAA0B,CAExE;AAED,wBAAgB,6BAA6B,IAAI,0BAA0B,CAI1E"}
|
|
||||||
198
dist/utils/maskSegmentRuntime.js
vendored
198
dist/utils/maskSegmentRuntime.js
vendored
@ -1,197 +1 @@
|
|||||||
import { BASEBOARD_STRIP_QUANT_KEYS, CABINET_QUANT_KEYS, MASK_SEMANTIC_COLORS, WALL_QUANT_KEYS, } from './maskSemanticPalette';
|
"use strict";var l=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var b=Object.getOwnPropertyNames;var M=Object.prototype.hasOwnProperty;var k=(e,o)=>{for(var a in o)l(e,a,{get:o[a],enumerable:!0})},y=(e,o,a,p)=>{if(o&&typeof o=="object"||typeof o=="function")for(let s of b(o))!M.call(e,s)&&s!==a&&l(e,s,{get:()=>o[s],enumerable:!(p=f(o,s))||p.enumerable});return e};var h=e=>y(l({},"__esModule",{value:!0}),e);var K={};k(K,{DEFAULT_INTERACTION_CONFIG:()=>P,DEFAULT_MASK_CONFIG:()=>n,DEFAULT_PAINT_CONFIG:()=>C,DEFAULT_PIPELINE_CONFIG:()=>c,PIPELINE_HIGH:()=>x,PIPELINE_LOW:()=>S,PIPELINE_MEDIUM:()=>u,PIPELINE_PRESETS:()=>R,createRuntimeConfig:()=>d,getMaskRuntimeRevision:()=>L,getMaskSegmentRuntimeConfig:()=>N,mergeMaskConfig:()=>m,resetMaskSegmentRuntimeConfig:()=>W,resolvePipelineConfig:()=>I,setMaskSegmentRuntimeConfig:()=>E});module.exports=h(K);var t=require("./maskSemanticPalette");const x={maxImageLongSide:1440,paintFreqMaxLongSide:960,originPreviewMaxLongSide:720,maskPathMaxLongSide:960,minContourArea:50,contourApproxEpsilon:.002,maxRegions:800},u={maxImageLongSide:720,paintFreqMaxLongSide:480,originPreviewMaxLongSide:360,maskPathMaxLongSide:480,minContourArea:100,contourApproxEpsilon:.003,maxRegions:500},S={maxImageLongSide:360,paintFreqMaxLongSide:240,originPreviewMaxLongSide:180,maskPathMaxLongSide:240,minContourArea:200,contourApproxEpsilon:.005,maxRegions:300},R={high:x,medium:u,low:S},c=u;function I(e,o){return{...e!=null?R[e]:c,...o}}const A=[{b:138,g:126,r:110},{b:92,g:124,r:86},{b:70,g:80,r:158},{b:54,g:134,r:182},{b:128,g:98,r:142},{b:76,g:120,r:138}],C={palette:A,colorBaseOpacity:.88,lLightOpacity:.5,textureOpacity:.85,lLowBlurKernel:7,lLowContrast:1.1,lLowBrightness:.92,lHighGain:1.22,maskFeatherColor:1.6,maskFeatherTexture:.9,regionOverlayFill:"#FFC14D",regionOutlineStrokeWidth:4},P={kickMaskPickRadiusPx:36,pickMapSearchRadiusPx:14,thinStripPadding:.008,regionPadding:.003,initRegionFlashMs:1e3,enableInitRegionFlash:!0},n={semanticColors:t.MASK_SEMANTIC_COLORS,baseboardMaxColorDist:42,blackThreshold:30,quantStep:64,baseboardStripQuantKeys:new Set(t.BASEBOARD_STRIP_QUANT_KEYS),wallQuantKeys:new Set(t.WALL_QUANT_KEYS),cabinetQuantKeys:new Set(t.CABINET_QUANT_KEYS),maxRegionColors:6,secondarySemanticNames:new Set(["garageDoor","roof","eave"]),secondaryMinPixelRatio:.002,junctionHRadiusPx:24,junctionVRadiusPx:2,kickBridgeHalfWPx:6,baseboardJunctionRowMarginPx:1,baseboardJunctionVReachPx:2,baseboardMinRunPx:2,splitWalls:!1,splitWallsMaxCount:8,splitWallsMinAreaRatio:.002,splitWallsColorDistSq:1400,splitWallsChromaBlurRadius:5,splitWallsNeutralChromaMax:14};function r(e){return new Set(e??[])}function m(e){return e?{semanticColors:e.semanticColors??n.semanticColors,baseboardMaxColorDist:e.baseboardMaxColorDist??n.baseboardMaxColorDist,blackThreshold:e.blackThreshold??n.blackThreshold,quantStep:e.quantStep??n.quantStep,baseboardStripQuantKeys:e.baseboardStripQuantKeys?r(e.baseboardStripQuantKeys):new Set(n.baseboardStripQuantKeys),wallQuantKeys:e.wallQuantKeys?r(e.wallQuantKeys):new Set(n.wallQuantKeys),cabinetQuantKeys:e.cabinetQuantKeys?r(e.cabinetQuantKeys):new Set(n.cabinetQuantKeys),maxRegionColors:e.maxRegionColors??n.maxRegionColors,secondarySemanticNames:e.secondarySemanticNames?r(e.secondarySemanticNames):new Set(n.secondarySemanticNames),secondaryMinPixelRatio:e.secondaryMinPixelRatio??n.secondaryMinPixelRatio,junctionHRadiusPx:e.junctionHRadiusPx??n.junctionHRadiusPx,junctionVRadiusPx:e.junctionVRadiusPx??n.junctionVRadiusPx,kickBridgeHalfWPx:e.kickBridgeHalfWPx??n.kickBridgeHalfWPx,baseboardJunctionRowMarginPx:e.baseboardJunctionRowMarginPx??n.baseboardJunctionRowMarginPx,baseboardJunctionVReachPx:e.baseboardJunctionVReachPx??n.baseboardJunctionVReachPx,baseboardMinRunPx:e.baseboardMinRunPx??n.baseboardMinRunPx,splitWalls:e.splitWalls??n.splitWalls,splitWallsMaxCount:e.splitWallsMaxCount??n.splitWallsMaxCount,splitWallsMinAreaRatio:e.splitWallsMinAreaRatio??n.splitWallsMinAreaRatio,splitWallsColorDistSq:e.splitWallsColorDistSq??n.splitWallsColorDistSq,splitWallsChromaBlurRadius:e.splitWallsChromaBlurRadius??n.splitWallsChromaBlurRadius,splitWallsNeutralChromaMax:e.splitWallsNeutralChromaMax??n.splitWallsNeutralChromaMax}:{...n}}function d(e){return{pipeline:{...c,...e?.pipelineConfig},mask:m(e?.maskConfig),paint:{...C,...e?.paintConfig,palette:e?.paintConfig?.palette??C.palette},interaction:{...P,...e?.interactionConfig}}}let i=d(),g=0;function L(){return g}function E(e){return i={pipeline:e?.pipelineConfig?{...i.pipeline,...e.pipelineConfig}:i.pipeline,mask:e?.maskConfig?m(e.maskConfig):i.mask,paint:e?.paintConfig?{...i.paint,...e.paintConfig}:i.paint,interaction:e?.interactionConfig?{...i.interaction,...e.interactionConfig}:i.interaction},g+=1,i}function N(){return i}function W(){return i=d(),g+=1,i}
|
||||||
/** high */
|
|
||||||
export const PIPELINE_HIGH = {
|
|
||||||
maxImageLongSide: 1440,
|
|
||||||
paintFreqMaxLongSide: 960,
|
|
||||||
originPreviewMaxLongSide: 720,
|
|
||||||
maskPathMaxLongSide: 960,
|
|
||||||
minContourArea: 50,
|
|
||||||
contourApproxEpsilon: 0.002,
|
|
||||||
maxRegions: 800,
|
|
||||||
};
|
|
||||||
/** middle */
|
|
||||||
export const PIPELINE_MEDIUM = {
|
|
||||||
maxImageLongSide: 720,
|
|
||||||
paintFreqMaxLongSide: 480,
|
|
||||||
originPreviewMaxLongSide: 360,
|
|
||||||
maskPathMaxLongSide: 480,
|
|
||||||
minContourArea: 100,
|
|
||||||
contourApproxEpsilon: 0.003,
|
|
||||||
maxRegions: 500,
|
|
||||||
};
|
|
||||||
/** low */
|
|
||||||
export const PIPELINE_LOW = {
|
|
||||||
maxImageLongSide: 360,
|
|
||||||
paintFreqMaxLongSide: 240,
|
|
||||||
originPreviewMaxLongSide: 180,
|
|
||||||
maskPathMaxLongSide: 240,
|
|
||||||
minContourArea: 200,
|
|
||||||
contourApproxEpsilon: 0.005,
|
|
||||||
maxRegions: 300,
|
|
||||||
};
|
|
||||||
export const PIPELINE_PRESETS = {
|
|
||||||
high: PIPELINE_HIGH,
|
|
||||||
medium: PIPELINE_MEDIUM,
|
|
||||||
low: PIPELINE_LOW,
|
|
||||||
};
|
|
||||||
export const DEFAULT_PIPELINE_CONFIG = PIPELINE_MEDIUM;
|
|
||||||
export function resolvePipelineConfig(preset, overrides) {
|
|
||||||
const base = preset != null ? PIPELINE_PRESETS[preset] : DEFAULT_PIPELINE_CONFIG;
|
|
||||||
return { ...base, ...overrides };
|
|
||||||
}
|
|
||||||
const DEFAULT_PAINT_PALETTE = [
|
|
||||||
{ b: 138, g: 126, r: 110 },
|
|
||||||
{ b: 92, g: 124, r: 86 },
|
|
||||||
{ b: 70, g: 80, r: 158 },
|
|
||||||
{ b: 54, g: 134, r: 182 },
|
|
||||||
{ b: 128, g: 98, r: 142 },
|
|
||||||
{ b: 76, g: 120, r: 138 },
|
|
||||||
];
|
|
||||||
export const DEFAULT_PAINT_CONFIG = {
|
|
||||||
palette: DEFAULT_PAINT_PALETTE,
|
|
||||||
// Optimized coloring: slightly stronger base color fidelity while preserving natural lighting.
|
|
||||||
colorBaseOpacity: 0.88,
|
|
||||||
lLightOpacity: 0.50,
|
|
||||||
// Strengthened texture retention (high-freq detail overlay) for richer surface appearance after recolor.
|
|
||||||
textureOpacity: 0.85,
|
|
||||||
// Slightly tighter low-freq lighting kernel for cleaner wall/ceiling shading without over-smoothing.
|
|
||||||
lLowBlurKernel: 7,
|
|
||||||
lLowContrast: 1.10,
|
|
||||||
lLowBrightness: 0.92,
|
|
||||||
lHighGain: 1.22,
|
|
||||||
// Edge handling: small positive feather produces soft transitions at painted region boundaries.
|
|
||||||
// color feather drives the paintColorMap alpha softness (used by shader for blend-to-origin).
|
|
||||||
maskFeatherColor: 1.6,
|
|
||||||
maskFeatherTexture: 0.9,
|
|
||||||
regionOverlayFill: '#FFC14D',
|
|
||||||
regionOutlineStrokeWidth: 4,
|
|
||||||
};
|
|
||||||
export const DEFAULT_INTERACTION_CONFIG = {
|
|
||||||
kickMaskPickRadiusPx: 36,
|
|
||||||
pickMapSearchRadiusPx: 14,
|
|
||||||
thinStripPadding: 0.008,
|
|
||||||
regionPadding: 0.003,
|
|
||||||
initRegionFlashMs: 1000,
|
|
||||||
enableInitRegionFlash: true,
|
|
||||||
};
|
|
||||||
export const DEFAULT_MASK_CONFIG = {
|
|
||||||
semanticColors: MASK_SEMANTIC_COLORS,
|
|
||||||
baseboardMaxColorDist: 42,
|
|
||||||
blackThreshold: 30,
|
|
||||||
quantStep: 64,
|
|
||||||
baseboardStripQuantKeys: new Set(BASEBOARD_STRIP_QUANT_KEYS),
|
|
||||||
wallQuantKeys: new Set(WALL_QUANT_KEYS),
|
|
||||||
cabinetQuantKeys: new Set(CABINET_QUANT_KEYS),
|
|
||||||
maxRegionColors: 6,
|
|
||||||
secondarySemanticNames: new Set(['garageDoor', 'roof', 'eave']),
|
|
||||||
secondaryMinPixelRatio: 0.002,
|
|
||||||
junctionHRadiusPx: 24,
|
|
||||||
junctionVRadiusPx: 2,
|
|
||||||
kickBridgeHalfWPx: 6,
|
|
||||||
baseboardJunctionRowMarginPx: 1,
|
|
||||||
baseboardJunctionVReachPx: 2,
|
|
||||||
baseboardMinRunPx: 2,
|
|
||||||
splitWalls: false,
|
|
||||||
splitWallsMaxCount: 8,
|
|
||||||
splitWallsMinAreaRatio: 0.002,
|
|
||||||
splitWallsColorDistSq: 1400,
|
|
||||||
splitWallsChromaBlurRadius: 5,
|
|
||||||
splitWallsNeutralChromaMax: 14,
|
|
||||||
};
|
|
||||||
function toStringSet(values) {
|
|
||||||
return new Set(values ?? []);
|
|
||||||
}
|
|
||||||
export function mergeMaskConfig(partial) {
|
|
||||||
if (!partial) {
|
|
||||||
return { ...DEFAULT_MASK_CONFIG };
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
semanticColors: partial.semanticColors ?? DEFAULT_MASK_CONFIG.semanticColors,
|
|
||||||
baseboardMaxColorDist: partial.baseboardMaxColorDist ?? DEFAULT_MASK_CONFIG.baseboardMaxColorDist,
|
|
||||||
blackThreshold: partial.blackThreshold ?? DEFAULT_MASK_CONFIG.blackThreshold,
|
|
||||||
quantStep: partial.quantStep ?? DEFAULT_MASK_CONFIG.quantStep,
|
|
||||||
baseboardStripQuantKeys: partial.baseboardStripQuantKeys
|
|
||||||
? toStringSet(partial.baseboardStripQuantKeys)
|
|
||||||
: new Set(DEFAULT_MASK_CONFIG.baseboardStripQuantKeys),
|
|
||||||
wallQuantKeys: partial.wallQuantKeys
|
|
||||||
? toStringSet(partial.wallQuantKeys)
|
|
||||||
: new Set(DEFAULT_MASK_CONFIG.wallQuantKeys),
|
|
||||||
cabinetQuantKeys: partial.cabinetQuantKeys
|
|
||||||
? toStringSet(partial.cabinetQuantKeys)
|
|
||||||
: new Set(DEFAULT_MASK_CONFIG.cabinetQuantKeys),
|
|
||||||
maxRegionColors: partial.maxRegionColors ?? DEFAULT_MASK_CONFIG.maxRegionColors,
|
|
||||||
secondarySemanticNames: partial.secondarySemanticNames
|
|
||||||
? toStringSet(partial.secondarySemanticNames)
|
|
||||||
: new Set(DEFAULT_MASK_CONFIG.secondarySemanticNames),
|
|
||||||
secondaryMinPixelRatio: partial.secondaryMinPixelRatio ??
|
|
||||||
DEFAULT_MASK_CONFIG.secondaryMinPixelRatio,
|
|
||||||
junctionHRadiusPx: partial.junctionHRadiusPx ?? DEFAULT_MASK_CONFIG.junctionHRadiusPx,
|
|
||||||
junctionVRadiusPx: partial.junctionVRadiusPx ?? DEFAULT_MASK_CONFIG.junctionVRadiusPx,
|
|
||||||
kickBridgeHalfWPx: partial.kickBridgeHalfWPx ?? DEFAULT_MASK_CONFIG.kickBridgeHalfWPx,
|
|
||||||
baseboardJunctionRowMarginPx: partial.baseboardJunctionRowMarginPx ??
|
|
||||||
DEFAULT_MASK_CONFIG.baseboardJunctionRowMarginPx,
|
|
||||||
baseboardJunctionVReachPx: partial.baseboardJunctionVReachPx ??
|
|
||||||
DEFAULT_MASK_CONFIG.baseboardJunctionVReachPx,
|
|
||||||
baseboardMinRunPx: partial.baseboardMinRunPx ?? DEFAULT_MASK_CONFIG.baseboardMinRunPx,
|
|
||||||
splitWalls: partial.splitWalls ?? DEFAULT_MASK_CONFIG.splitWalls,
|
|
||||||
splitWallsMaxCount: partial.splitWallsMaxCount ?? DEFAULT_MASK_CONFIG.splitWallsMaxCount,
|
|
||||||
splitWallsMinAreaRatio: partial.splitWallsMinAreaRatio ??
|
|
||||||
DEFAULT_MASK_CONFIG.splitWallsMinAreaRatio,
|
|
||||||
splitWallsColorDistSq: partial.splitWallsColorDistSq ??
|
|
||||||
DEFAULT_MASK_CONFIG.splitWallsColorDistSq,
|
|
||||||
splitWallsChromaBlurRadius: partial.splitWallsChromaBlurRadius ??
|
|
||||||
DEFAULT_MASK_CONFIG.splitWallsChromaBlurRadius,
|
|
||||||
splitWallsNeutralChromaMax: partial.splitWallsNeutralChromaMax ??
|
|
||||||
DEFAULT_MASK_CONFIG.splitWallsNeutralChromaMax,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
export function createRuntimeConfig(input) {
|
|
||||||
return {
|
|
||||||
pipeline: {
|
|
||||||
...DEFAULT_PIPELINE_CONFIG,
|
|
||||||
...input?.pipelineConfig,
|
|
||||||
},
|
|
||||||
mask: mergeMaskConfig(input?.maskConfig),
|
|
||||||
paint: {
|
|
||||||
...DEFAULT_PAINT_CONFIG,
|
|
||||||
...input?.paintConfig,
|
|
||||||
palette: input?.paintConfig?.palette ?? DEFAULT_PAINT_CONFIG.palette,
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
...DEFAULT_INTERACTION_CONFIG,
|
|
||||||
...input?.interactionConfig,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
let activeRuntime = createRuntimeConfig();
|
|
||||||
let runtimeRevision = 0;
|
|
||||||
export function getMaskRuntimeRevision() {
|
|
||||||
return runtimeRevision;
|
|
||||||
}
|
|
||||||
export function setMaskSegmentRuntimeConfig(input) {
|
|
||||||
activeRuntime = {
|
|
||||||
pipeline: input?.pipelineConfig
|
|
||||||
? { ...activeRuntime.pipeline, ...input.pipelineConfig }
|
|
||||||
: activeRuntime.pipeline,
|
|
||||||
mask: input?.maskConfig
|
|
||||||
? mergeMaskConfig(input.maskConfig)
|
|
||||||
: activeRuntime.mask,
|
|
||||||
paint: input?.paintConfig
|
|
||||||
? { ...activeRuntime.paint, ...input.paintConfig }
|
|
||||||
: activeRuntime.paint,
|
|
||||||
interaction: input?.interactionConfig
|
|
||||||
? { ...activeRuntime.interaction, ...input.interactionConfig }
|
|
||||||
: activeRuntime.interaction,
|
|
||||||
};
|
|
||||||
runtimeRevision += 1;
|
|
||||||
return activeRuntime;
|
|
||||||
}
|
|
||||||
export function getMaskSegmentRuntimeConfig() {
|
|
||||||
return activeRuntime;
|
|
||||||
}
|
|
||||||
export function resetMaskSegmentRuntimeConfig() {
|
|
||||||
activeRuntime = createRuntimeConfig();
|
|
||||||
runtimeRevision += 1;
|
|
||||||
return activeRuntime;
|
|
||||||
}
|
|
||||||
//# sourceMappingURL=maskSegmentRuntime.js.map
|
|
||||||
|
|||||||
1
dist/utils/maskSegmentRuntime.js.map
vendored
1
dist/utils/maskSegmentRuntime.js.map
vendored
File diff suppressed because one or more lines are too long
27
dist/utils/maskSegmentation.d.ts
vendored
27
dist/utils/maskSegmentation.d.ts
vendored
@ -12,16 +12,16 @@ export type SegmentMaskResult = {
|
|||||||
baseboardBinary: Uint8Array;
|
baseboardBinary: Uint8Array;
|
||||||
segCols: number;
|
segCols: number;
|
||||||
segRows: number;
|
segRows: number;
|
||||||
/** splitWalls 时:墙像素 → 子区索引 0..N-1,非墙为 WALL_SUB_LABEL_NONE */
|
/** splitWalls only: wall pixels → sub-region index 0..N-1, non-wall is WALL_SUB_LABEL_NONE */
|
||||||
wallSubLabels?: Uint8Array;
|
wallSubLabels?: Uint8Array;
|
||||||
};
|
};
|
||||||
export type SegmentRegion = {
|
export type SegmentRegion = {
|
||||||
id: number;
|
id: number;
|
||||||
/** 语义分区名(door / cabinet / baseboard …) */
|
/** semantic partition name (door / cabinet / baseboard ...) */
|
||||||
name: string;
|
name: string;
|
||||||
/** 参考色 hex */
|
/** reference color hex */
|
||||||
hex: string;
|
hex: string;
|
||||||
/** 参考色(BGR) */
|
/** reference color (BGR) */
|
||||||
color: {
|
color: {
|
||||||
b: number;
|
b: number;
|
||||||
g: number;
|
g: number;
|
||||||
@ -31,7 +31,7 @@ export type SegmentRegion = {
|
|||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
}[][];
|
}[][];
|
||||||
/** 上色/高亮蒙版:严格像素条带,不填充黑色空洞 */
|
/** paint/highlight mask: strict pixel strip, no black hole filling */
|
||||||
maskPolygons?: {
|
maskPolygons?: {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
@ -51,13 +51,13 @@ export type SegmentRegion = {
|
|||||||
h: number;
|
h: number;
|
||||||
};
|
};
|
||||||
area: number;
|
area: number;
|
||||||
/** 踢脚线等细条区域,点击检测需加宽容差 */
|
/** baseboard etc. thin strip areas, click detection needs tolerance */
|
||||||
thinStrip?: boolean;
|
thinStrip?: boolean;
|
||||||
};
|
};
|
||||||
export declare function buildRegionOutlinePolygons(reg: SegmentRegion): NormPoint[][];
|
export declare function buildRegionOutlinePolygons(reg: SegmentRegion): NormPoint[][];
|
||||||
import { buildAllRegionOutlinePaths, buildRegionOutlinePathForRegion } from './maskOutlinePaths';
|
import { buildAllRegionOutlinePaths, buildRegionOutlinePathForRegion } from './maskOutlinePaths';
|
||||||
export { buildAllRegionOutlinePaths, buildRegionOutlinePathForRegion };
|
export { buildAllRegionOutlinePaths, buildRegionOutlinePathForRegion };
|
||||||
/** 从二值图逐行条带构建蒙版(供 Skia PathBuilder 使用) */
|
/** build mask from binary mask row by row (for Skia PathBuilder) */
|
||||||
export declare function appendMaskBinaryToPathBuilder(binary: Uint8Array, cols: number, rows: number, rect: {
|
export declare function appendMaskBinaryToPathBuilder(binary: Uint8Array, cols: number, rows: number, rect: {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
@ -68,7 +68,7 @@ export declare function appendMaskBinaryToPathBuilder(binary: Uint8Array, cols:
|
|||||||
lineTo: (x: number, y: number) => unknown;
|
lineTo: (x: number, y: number) => unknown;
|
||||||
close: () => unknown;
|
close: () => unknown;
|
||||||
}, minRunPx?: number): void;
|
}, minRunPx?: number): void;
|
||||||
/** 从语义标签逐行条带构建蒙版(避免维护多张二值图) */
|
/** build mask from semantic labels row by row (avoid maintaining multiple binary masks) */
|
||||||
export declare function appendLabelMaskToPathBuilder(labels: Uint8Array, semanticIndex: number, cols: number, rows: number, rect: {
|
export declare function appendLabelMaskToPathBuilder(labels: Uint8Array, semanticIndex: number, cols: number, rows: number, rect: {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
@ -86,9 +86,9 @@ export type RegionMaskData = {
|
|||||||
rows: number;
|
rows: number;
|
||||||
wallSubLabels?: Uint8Array;
|
wallSubLabels?: Uint8Array;
|
||||||
};
|
};
|
||||||
/** 蒙版路径构建降采样(屏幕显示不需要分割分辨率,点击仍用全分辨率 pickMap) */
|
/** downsample mask path building (screen display does not need segmentation resolution, click still uses full resolution pickMap) */
|
||||||
export declare function downsampleMaskDataForPaths(maskData: RegionMaskData, maxLongSide: number): RegionMaskData;
|
export declare function downsampleMaskDataForPaths(maskData: RegionMaskData, maxLongSide: number): RegionMaskData;
|
||||||
/** 单次扫描构建所有分区 Skia 蒙版路径(单 label pass,避免每像素 × 语义数循环) */
|
/** single pass build all partition Skia mask paths (single label pass, avoid per pixel × semantic count loop) */
|
||||||
export declare function buildAllRegionMaskPaths(regions: SegmentRegion[], maskData: RegionMaskData, rect: {
|
export declare function buildAllRegionMaskPaths(regions: SegmentRegion[], maskData: RegionMaskData, rect: {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
@ -100,12 +100,12 @@ type NormPoint = {
|
|||||||
y: number;
|
y: number;
|
||||||
};
|
};
|
||||||
export declare function buildBaseboardBinaryFromMask(buffer: Uint8Array, cols: number, rows: number): Uint8Array;
|
export declare function buildBaseboardBinaryFromMask(buffer: Uint8Array, cols: number, rows: number): Uint8Array;
|
||||||
/** 分割分辨率踢脚线二值图最近邻放大到点击查表分辨率(避免全图 junction 重算) */
|
/** Upscale segmentation-resolution baseboard binary to tap-lookup resolution via nearest-neighbor (avoids full-image junction recomputation) */
|
||||||
export declare function upscaleBinaryMask(src: Uint8Array, srcCols: number, srcRows: number, dstCols: number, dstRows: number): Uint8Array;
|
export declare function upscaleBinaryMask(src: Uint8Array, srcCols: number, srcRows: number, dstCols: number, dstRows: number): Uint8Array;
|
||||||
export declare function isBaseboardMaskPixel(buffer: Uint8Array, cols: number, rows: number, x: number, y: number, baseboardBinary?: Uint8Array | null): boolean;
|
export declare function isBaseboardMaskPixel(buffer: Uint8Array, cols: number, rows: number, x: number, y: number, baseboardBinary?: Uint8Array | null): boolean;
|
||||||
export { isStrictBaseboardPixel as isBaseboardPixel } from './maskSemanticPalette';
|
export { isStrictBaseboardPixel as isBaseboardPixel } from './maskSemanticPalette';
|
||||||
export declare function getMaskQuantKey(b: number, g: number, r: number): string;
|
export declare function getMaskQuantKey(b: number, g: number, r: number): string;
|
||||||
/** @deprecated 请使用 isBaseboardMaskPixel */
|
/** @deprecated Use isBaseboardMaskPixel */
|
||||||
export declare function isKickPlatePixel(b: number, g: number, r: number): boolean;
|
export declare function isKickPlatePixel(b: number, g: number, r: number): boolean;
|
||||||
export declare function extractRegionsFromMaskBuffer(buffer: Uint8Array, cols: number, rows: number, _options: {
|
export declare function extractRegionsFromMaskBuffer(buffer: Uint8Array, cols: number, rows: number, _options: {
|
||||||
minArea: number;
|
minArea: number;
|
||||||
@ -115,9 +115,8 @@ export declare function extractRegionsFromMaskBufferSync(buffer: Uint8Array, col
|
|||||||
minArea: number;
|
minArea: number;
|
||||||
approxEpsilon: number;
|
approxEpsilon: number;
|
||||||
}): SegmentMaskResult;
|
}): SegmentMaskResult;
|
||||||
/** @deprecated 请使用 extractRegionsFromMaskBuffer */
|
/** @deprecated Use extractRegionsFromMaskBuffer */
|
||||||
export declare function extractRegionsFromMask(maskMat: WrappedMat, options: {
|
export declare function extractRegionsFromMask(maskMat: WrappedMat, options: {
|
||||||
minArea: number;
|
minArea: number;
|
||||||
approxEpsilon: number;
|
approxEpsilon: number;
|
||||||
}): Promise<SegmentRegion[]>;
|
}): Promise<SegmentRegion[]>;
|
||||||
//# sourceMappingURL=maskSegmentation.d.ts.map
|
|
||||||
1
dist/utils/maskSegmentation.d.ts.map
vendored
1
dist/utils/maskSegmentation.d.ts.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"maskSegmentation.d.ts","sourceRoot":"","sources":["../../src/utils/maskSegmentation.ts"],"names":[],"mappings":"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"}
|
|
||||||
1213
dist/utils/maskSegmentation.js
vendored
1213
dist/utils/maskSegmentation.js
vendored
File diff suppressed because one or more lines are too long
1
dist/utils/maskSegmentation.js.map
vendored
1
dist/utils/maskSegmentation.js.map
vendored
File diff suppressed because one or more lines are too long
15
dist/utils/maskSemanticPalette.d.ts
vendored
15
dist/utils/maskSemanticPalette.d.ts
vendored
@ -1,31 +1,30 @@
|
|||||||
export type MaskSemanticColor = {
|
export type MaskSemanticColor = {
|
||||||
name: string;
|
name: string;
|
||||||
hex: string;
|
hex: string;
|
||||||
/** 参考色(BGR,与掩码 buffer 通道一致) */
|
/** reference color (BGR, consistent with mask buffer channel) */
|
||||||
bgr: {
|
bgr: {
|
||||||
b: number;
|
b: number;
|
||||||
g: number;
|
g: number;
|
||||||
r: number;
|
r: number;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
/** 掩码语义色表(与后端分区颜色参考一致) */
|
/** mask semantic color table (consistent with backend partition color reference) */
|
||||||
export declare const MASK_SEMANTIC_COLORS: MaskSemanticColor[];
|
export declare const MASK_SEMANTIC_COLORS: MaskSemanticColor[];
|
||||||
export declare const BASEBOARD_SEMANTIC_NAME = "baseboard";
|
export declare const BASEBOARD_SEMANTIC_NAME = "baseboard";
|
||||||
/** 将掩码像素归类到最近的语义色(baseboard 仅严格橙色命中) */
|
/** classify mask pixel to the nearest semantic color (baseboard only strictly hit orange) */
|
||||||
export declare function classifyBgrPixelToSemantic(b: number, g: number, r: number): string;
|
export declare function classifyBgrPixelToSemantic(b: number, g: number, r: number): string;
|
||||||
export declare function getSemanticColorByName(name: string): MaskSemanticColor | undefined;
|
export declare function getSemanticColorByName(name: string): MaskSemanticColor | undefined;
|
||||||
/**
|
/**
|
||||||
* 踢脚线须更接近 #F58231 且明显优于黄柜 / 蓝墙,避免整块黄区被误判。
|
* The baseboard must be closer to #F58231 and significantly better than the yellow cabinet / blue wall, to avoid being mistakenly judged as a whole yellow area.
|
||||||
*/
|
*/
|
||||||
export declare function isStrictBaseboardPixel(b: number, g: number, r: number): boolean;
|
export declare function isStrictBaseboardPixel(b: number, g: number, r: number): boolean;
|
||||||
export declare function isBaseboardPixel(b: number, g: number, r: number): boolean;
|
export declare function isBaseboardPixel(b: number, g: number, r: number): boolean;
|
||||||
/** 掩码上墙/柜交界细条的量化色 */
|
/** quantized color of the wall/cabinet junction strip on the mask */
|
||||||
export declare const BASEBOARD_STRIP_QUANT_KEYS: Set<string>;
|
export declare const BASEBOARD_STRIP_QUANT_KEYS: Set<string>;
|
||||||
/** 掩码上墙面量化色 */
|
/** quantized color of the wall on the mask */
|
||||||
export declare const WALL_QUANT_KEYS: Set<string>;
|
export declare const WALL_QUANT_KEYS: Set<string>;
|
||||||
/** 掩码上柜/地面量化色 */
|
/** quantized color of the cabinet/floor on the mask */
|
||||||
export declare const CABINET_QUANT_KEYS: Set<string>;
|
export declare const CABINET_QUANT_KEYS: Set<string>;
|
||||||
export declare function getBaseboardStripQuantKeys(): Set<string>;
|
export declare function getBaseboardStripQuantKeys(): Set<string>;
|
||||||
export declare function getWallQuantKeys(): Set<string>;
|
export declare function getWallQuantKeys(): Set<string>;
|
||||||
export declare function getCabinetQuantKeys(): Set<string>;
|
export declare function getCabinetQuantKeys(): Set<string>;
|
||||||
//# sourceMappingURL=maskSemanticPalette.d.ts.map
|
|
||||||
1
dist/utils/maskSemanticPalette.d.ts.map
vendored
1
dist/utils/maskSemanticPalette.d.ts.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"maskSemanticPalette.d.ts","sourceRoot":"","sources":["../../src/utils/maskSemanticPalette.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,+BAA+B;IAC/B,GAAG,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1C,CAAC;AAEF,0BAA0B;AAC1B,eAAO,MAAM,oBAAoB,EAAE,iBAAiB,EAUnD,CAAC;AAEF,eAAO,MAAM,uBAAuB,cAAc,CAAC;AA8EnD,wCAAwC;AACxC,wBAAgB,0BAA0B,CACxC,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,GACR,MAAM,CA8BR;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAGlF;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,GACR,OAAO,CAYT;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAEzE;AAED,qBAAqB;AACrB,eAAO,MAAM,0BAA0B,aAAuC,CAAC;AAE/E,eAAe;AACf,eAAO,MAAM,eAAe,aAM1B,CAAC;AAEH,iBAAiB;AACjB,eAAO,MAAM,kBAAkB,aAI7B,CAAC;AAEH,wBAAgB,0BAA0B,IAAI,GAAG,CAAC,MAAM,CAAC,CAExD;AAED,wBAAgB,gBAAgB,IAAI,GAAG,CAAC,MAAM,CAAC,CAE9C;AAED,wBAAgB,mBAAmB,IAAI,GAAG,CAAC,MAAM,CAAC,CAEjD"}
|
|
||||||
135
dist/utils/maskSemanticPalette.js
vendored
135
dist/utils/maskSemanticPalette.js
vendored
@ -1,134 +1 @@
|
|||||||
import { getMaskRuntimeRevision, getMaskSegmentRuntimeConfig, } from './maskSegmentRuntime';
|
"use strict";var l=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var B=Object.getOwnPropertyNames;var T=Object.prototype.hasOwnProperty;var M=(e,n)=>{for(var t in n)l(e,t,{get:n[t],enumerable:!0})},w=(e,n,t,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let a of B(n))!T.call(e,a)&&a!==t&&l(e,a,{get:()=>n[a],enumerable:!(r=y(n,a))||r.enumerable});return e};var A=e=>w(l({},"__esModule",{value:!0}),e);var O={};M(O,{BASEBOARD_SEMANTIC_NAME:()=>c,BASEBOARD_STRIP_QUANT_KEYS:()=>N,CABINET_QUANT_KEYS:()=>F,MASK_SEMANTIC_COLORS:()=>p,WALL_QUANT_KEYS:()=>K,classifyBgrPixelToSemantic:()=>k,getBaseboardStripQuantKeys:()=>I,getCabinetQuantKeys:()=>q,getSemanticColorByName:()=>_,getWallQuantKeys:()=>Q,isBaseboardPixel:()=>h,isStrictBaseboardPixel:()=>E});module.exports=A(O);var i=require("./maskSegmentRuntime");const p=[{name:"door",hex:"#E6194B",bgr:{b:75,g:25,r:230}},{name:"ceiling",hex:"#3CB44B",bgr:{b:75,g:180,r:60}},{name:"cabinet",hex:"#FFE119",bgr:{b:25,g:225,r:255}},{name:"wall",hex:"#4363D8",bgr:{b:216,g:99,r:67}},{name:"baseboard",hex:"#F58231",bgr:{b:49,g:130,r:245}},{name:"windowFrame",hex:"#911EB4",bgr:{b:180,g:30,r:145}},{name:"garageDoor",hex:"#46F0F0",bgr:{b:240,g:240,r:70}},{name:"roof",hex:"#F032E6",bgr:{b:230,g:50,r:240}},{name:"eave",hex:"#BCF60C",bgr:{b:12,g:246,r:188}}],c="baseboard";let f=-1,m=null;function D(e){return e.map(n=>({name:n.name,rgb:{r:n.bgr.r,g:n.bgr.g,b:n.bgr.b}}))}function S(e){const n=p.find(t=>t.name===e);return{name:n.name,rgb:{r:n.bgr.r,g:n.bgr.g,b:n.bgr.b}}}function C(){const e=(0,i.getMaskRuntimeRevision)();if(f===e&&m)return m;const n=(0,i.getMaskSegmentRuntimeConfig)().mask,t=D(n.semanticColors),r=t.find(o=>o.name===c)??S(c),a=t.find(o=>o.name==="cabinet")??S("cabinet"),b=t.find(o=>o.name==="wall")??S("wall"),g=n.baseboardMaxColorDist;return m={baseboardMaxColorDistSq:g*g,semanticRgb:t,baseboardRgb:r,cabinetRgb:a,wallRgb:b},f=e,m}function s(e,n){const t=e.r-n.r,r=e.g-n.g,a=e.b-n.b;return t*t+r*r+a*a}function k(e,n,t){const r=C(),a={r:t,g:n,b:e},b=s(a,r.baseboardRgb.rgb),g=s(a,r.cabinetRgb.rgb),o=s(a,r.wallRgb.rgb);if(b<=r.baseboardMaxColorDistSq&&b<g&&b<o)return c;let x=r.semanticRgb[0],d=Number.POSITIVE_INFINITY;for(const u of r.semanticRgb){if(u.name===c)continue;const R=s(a,u.rgb);R<d&&(d=R,x=u)}return x?.name??"wall"}function _(e){return(0,i.getMaskSegmentRuntimeConfig)().mask.semanticColors.find(t=>t.name===e)}function E(e,n,t){const r=C(),a={r:t,g:n,b:e},b=s(a,r.baseboardRgb.rgb),g=s(a,r.cabinetRgb.rgb),o=s(a,r.wallRgb.rgb);return b<=r.baseboardMaxColorDistSq&&b<g&&b<o}function h(e,n,t){return E(e,n,t)}const N=new Set(["0,255,255","64,255,255"]),K=new Set(["192,128,64","192,64,64","128,64,64","192,192,128","128,128,64"]),F=new Set(["0,192,255","64,192,255","128,192,255"]);function I(){return(0,i.getMaskSegmentRuntimeConfig)().mask.baseboardStripQuantKeys}function Q(){return(0,i.getMaskSegmentRuntimeConfig)().mask.wallQuantKeys}function q(){return(0,i.getMaskSegmentRuntimeConfig)().mask.cabinetQuantKeys}
|
||||||
/** 掩码语义色表(与后端分区颜色参考一致) */
|
|
||||||
export const MASK_SEMANTIC_COLORS = [
|
|
||||||
{ name: 'door', hex: '#E6194B', bgr: { b: 75, g: 25, r: 230 } },
|
|
||||||
{ name: 'ceiling', hex: '#3CB44B', bgr: { b: 75, g: 180, r: 60 } },
|
|
||||||
{ name: 'cabinet', hex: '#FFE119', bgr: { b: 25, g: 225, r: 255 } },
|
|
||||||
{ name: 'wall', hex: '#4363D8', bgr: { b: 216, g: 99, r: 67 } },
|
|
||||||
{ name: 'baseboard', hex: '#F58231', bgr: { b: 49, g: 130, r: 245 } },
|
|
||||||
{ name: 'windowFrame', hex: '#911EB4', bgr: { b: 180, g: 30, r: 145 } },
|
|
||||||
{ name: 'garageDoor', hex: '#46F0F0', bgr: { b: 240, g: 240, r: 70 } },
|
|
||||||
{ name: 'roof', hex: '#F032E6', bgr: { b: 230, g: 50, r: 240 } },
|
|
||||||
{ name: 'eave', hex: '#BCF60C', bgr: { b: 12, g: 246, r: 188 } },
|
|
||||||
];
|
|
||||||
export const BASEBOARD_SEMANTIC_NAME = 'baseboard';
|
|
||||||
let contextRevision = -1;
|
|
||||||
let cachedContext = null;
|
|
||||||
function buildSemanticRgb(colors) {
|
|
||||||
return colors.map(entry => ({
|
|
||||||
name: entry.name,
|
|
||||||
rgb: {
|
|
||||||
r: entry.bgr.r,
|
|
||||||
g: entry.bgr.g,
|
|
||||||
b: entry.bgr.b,
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
function getDefaultSemanticEntry(name) {
|
|
||||||
const color = MASK_SEMANTIC_COLORS.find(c => c.name === name);
|
|
||||||
return {
|
|
||||||
name: color.name,
|
|
||||||
rgb: { r: color.bgr.r, g: color.bgr.g, b: color.bgr.b },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
function getSemanticContext() {
|
|
||||||
const revision = getMaskRuntimeRevision();
|
|
||||||
if (contextRevision === revision && cachedContext) {
|
|
||||||
return cachedContext;
|
|
||||||
}
|
|
||||||
const mask = getMaskSegmentRuntimeConfig().mask;
|
|
||||||
const semanticRgb = buildSemanticRgb(mask.semanticColors);
|
|
||||||
const baseboardRgb = semanticRgb.find(entry => entry.name === BASEBOARD_SEMANTIC_NAME) ?? getDefaultSemanticEntry(BASEBOARD_SEMANTIC_NAME);
|
|
||||||
const cabinetRgb = semanticRgb.find(entry => entry.name === 'cabinet') ??
|
|
||||||
getDefaultSemanticEntry('cabinet');
|
|
||||||
const wallRgb = semanticRgb.find(entry => entry.name === 'wall') ??
|
|
||||||
getDefaultSemanticEntry('wall');
|
|
||||||
const maxDist = mask.baseboardMaxColorDist;
|
|
||||||
cachedContext = {
|
|
||||||
baseboardMaxColorDistSq: maxDist * maxDist,
|
|
||||||
semanticRgb,
|
|
||||||
baseboardRgb,
|
|
||||||
cabinetRgb,
|
|
||||||
wallRgb,
|
|
||||||
};
|
|
||||||
contextRevision = revision;
|
|
||||||
return cachedContext;
|
|
||||||
}
|
|
||||||
function colorDistanceSq(a, b) {
|
|
||||||
const dr = a.r - b.r;
|
|
||||||
const dg = a.g - b.g;
|
|
||||||
const db = a.b - b.b;
|
|
||||||
return dr * dr + dg * dg + db * db;
|
|
||||||
}
|
|
||||||
/** 将掩码像素归类到最近的语义色(baseboard 仅严格橙色命中) */
|
|
||||||
export function classifyBgrPixelToSemantic(b, g, r) {
|
|
||||||
const ctx = getSemanticContext();
|
|
||||||
const pixel = { r, g, b };
|
|
||||||
const distToBaseboard = colorDistanceSq(pixel, ctx.baseboardRgb.rgb);
|
|
||||||
const distToCabinet = colorDistanceSq(pixel, ctx.cabinetRgb.rgb);
|
|
||||||
const distToWall = colorDistanceSq(pixel, ctx.wallRgb.rgb);
|
|
||||||
if (distToBaseboard <= ctx.baseboardMaxColorDistSq &&
|
|
||||||
distToBaseboard < distToCabinet &&
|
|
||||||
distToBaseboard < distToWall) {
|
|
||||||
return BASEBOARD_SEMANTIC_NAME;
|
|
||||||
}
|
|
||||||
let best = ctx.semanticRgb[0];
|
|
||||||
let bestDist = Number.POSITIVE_INFINITY;
|
|
||||||
for (const entry of ctx.semanticRgb) {
|
|
||||||
if (entry.name === BASEBOARD_SEMANTIC_NAME) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const dist = colorDistanceSq(pixel, entry.rgb);
|
|
||||||
if (dist < bestDist) {
|
|
||||||
bestDist = dist;
|
|
||||||
best = entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return best?.name ?? 'wall';
|
|
||||||
}
|
|
||||||
export function getSemanticColorByName(name) {
|
|
||||||
const colors = getMaskSegmentRuntimeConfig().mask.semanticColors;
|
|
||||||
return colors.find(entry => entry.name === name);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 踢脚线须更接近 #F58231 且明显优于黄柜 / 蓝墙,避免整块黄区被误判。
|
|
||||||
*/
|
|
||||||
export function isStrictBaseboardPixel(b, g, r) {
|
|
||||||
const ctx = getSemanticContext();
|
|
||||||
const pixel = { r, g, b };
|
|
||||||
const distToBaseboard = colorDistanceSq(pixel, ctx.baseboardRgb.rgb);
|
|
||||||
const distToCabinet = colorDistanceSq(pixel, ctx.cabinetRgb.rgb);
|
|
||||||
const distToWall = colorDistanceSq(pixel, ctx.wallRgb.rgb);
|
|
||||||
return (distToBaseboard <= ctx.baseboardMaxColorDistSq &&
|
|
||||||
distToBaseboard < distToCabinet &&
|
|
||||||
distToBaseboard < distToWall);
|
|
||||||
}
|
|
||||||
export function isBaseboardPixel(b, g, r) {
|
|
||||||
return isStrictBaseboardPixel(b, g, r);
|
|
||||||
}
|
|
||||||
/** 掩码上墙/柜交界细条的量化色 */
|
|
||||||
export const BASEBOARD_STRIP_QUANT_KEYS = new Set(['0,255,255', '64,255,255']);
|
|
||||||
/** 掩码上墙面量化色 */
|
|
||||||
export const WALL_QUANT_KEYS = new Set([
|
|
||||||
'192,128,64',
|
|
||||||
'192,64,64',
|
|
||||||
'128,64,64',
|
|
||||||
'192,192,128',
|
|
||||||
'128,128,64',
|
|
||||||
]);
|
|
||||||
/** 掩码上柜/地面量化色 */
|
|
||||||
export const CABINET_QUANT_KEYS = new Set([
|
|
||||||
'0,192,255',
|
|
||||||
'64,192,255',
|
|
||||||
'128,192,255',
|
|
||||||
]);
|
|
||||||
export function getBaseboardStripQuantKeys() {
|
|
||||||
return getMaskSegmentRuntimeConfig().mask.baseboardStripQuantKeys;
|
|
||||||
}
|
|
||||||
export function getWallQuantKeys() {
|
|
||||||
return getMaskSegmentRuntimeConfig().mask.wallQuantKeys;
|
|
||||||
}
|
|
||||||
export function getCabinetQuantKeys() {
|
|
||||||
return getMaskSegmentRuntimeConfig().mask.cabinetQuantKeys;
|
|
||||||
}
|
|
||||||
//# sourceMappingURL=maskSemanticPalette.js.map
|
|
||||||
|
|||||||
1
dist/utils/maskSemanticPalette.js.map
vendored
1
dist/utils/maskSemanticPalette.js.map
vendored
File diff suppressed because one or more lines are too long
25
dist/utils/opencvAdapter.d.ts
vendored
25
dist/utils/opencvAdapter.d.ts
vendored
@ -51,20 +51,20 @@ declare const cv: {
|
|||||||
imread(path: string, flags?: number): Promise<WrappedMat>;
|
imread(path: string, flags?: number): Promise<WrappedMat>;
|
||||||
createMat(cols: number, rows: number, channels?: 1 | 3 | 4): WrappedMat;
|
createMat(cols: number, rows: number, channels?: 1 | 3 | 4): WrappedMat;
|
||||||
cvtColor(src: WrappedMat, code: ColorConversionCodes): Promise<WrappedMat>;
|
cvtColor(src: WrappedMat, code: ColorConversionCodes): Promise<WrappedMat>;
|
||||||
/** 三通道色彩空间转换(BGR/Lab 等) */
|
/** Three-channel color space conversion (BGR/Lab etc.) */
|
||||||
cvtColorBgr(src: WrappedMat, code: ColorConversionCodes): WrappedMat;
|
cvtColorBgr(src: WrappedMat, code: ColorConversionCodes): WrappedMat;
|
||||||
/** 灰度 Mat → 三通道 BGR(供 Skia 显示) */
|
/** Grayscale Mat → 3-channel BGR (for Skia display) */
|
||||||
grayToBgr(src: WrappedMat): WrappedMat;
|
grayToBgr(src: WrappedMat): WrappedMat;
|
||||||
/** 掩码统一为三通道 BGR;已是 3 通道则原样返回,色序由分割侧 swapBr 检测 */
|
/** Normalize mask to 3-channel BGR; return as-is if already 3-channel, color order checked by segmentation side swapBr */
|
||||||
ensureBgr3(src: WrappedMat): Promise<WrappedMat>;
|
ensureBgr3(src: WrappedMat): Promise<WrappedMat>;
|
||||||
/** JS 二值缓冲(0/255)→ 单通道 Mat */
|
/** JS binary buffer (0/255) → single-channel Mat */
|
||||||
binaryBufferToMat(buffer: Uint8Array, cols: number, rows: number): WrappedMat;
|
binaryBufferToMat(buffer: Uint8Array, cols: number, rows: number): WrappedMat;
|
||||||
/** 连续 BGR 缓冲 → 三通道 Mat */
|
/** Continuous BGR buffer → 3-channel Mat */
|
||||||
bgrBufferToMat(buffer: Uint8Array, cols: number, rows: number): WrappedMat;
|
bgrBufferToMat(buffer: Uint8Array, cols: number, rows: number): WrappedMat;
|
||||||
/** 将 JS 侧生成的灰度二值图写入临时 PGM 并读回 Mat */
|
/** Write JS-side grayscale binary image to temp PGM and read back as Mat */
|
||||||
grayBufferToMat(gray: Uint8Array, cols: number, rows: number): Promise<WrappedMat>;
|
grayBufferToMat(gray: Uint8Array, cols: number, rows: number): Promise<WrappedMat>;
|
||||||
/**
|
/**
|
||||||
* 导出 Mat 像素。先 clone 保证内存连续,避免原生 matToBuffer 忽略 step 导致行错位。
|
* Export Mat pixels. Clone first to ensure contiguous memory, avoiding row misalignment from native matToBuffer ignoring step.
|
||||||
*/
|
*/
|
||||||
matToBuffer(src: WrappedMat): {
|
matToBuffer(src: WrappedMat): {
|
||||||
buffer: Uint8Array;
|
buffer: Uint8Array;
|
||||||
@ -77,19 +77,19 @@ declare const cv: {
|
|||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
}, interpolation?: InterpolationFlags): Promise<void>;
|
}, interpolation?: InterpolationFlags): Promise<void>;
|
||||||
/** BGR 缓冲原生缩放(掩码用语义色,默认最近邻) */
|
/** Native BGR buffer resize (mask uses semantic colors, default nearest-neighbor) */
|
||||||
resizeBgrBuffer(buffer: Uint8Array, srcCols: number, srcRows: number, dstCols: number, dstRows: number, interpolation?: InterpolationFlags): Promise<Uint8Array>;
|
resizeBgrBuffer(buffer: Uint8Array, srcCols: number, srcRows: number, dstCols: number, dstRows: number, interpolation?: InterpolationFlags): Promise<Uint8Array>;
|
||||||
/** BGR Mat → RGBA 连续缓冲(供 Skia 直传) */
|
/** BGR Mat → RGBA continuous buffer (for Skia direct transfer) */
|
||||||
matToRgbaBuffer(src: WrappedMat): Promise<{
|
matToRgbaBuffer(src: WrappedMat): Promise<{
|
||||||
buffer: Uint8Array;
|
buffer: Uint8Array;
|
||||||
cols: number;
|
cols: number;
|
||||||
rows: number;
|
rows: number;
|
||||||
}>;
|
}>;
|
||||||
/** BGR Mat → Skia 图像(跳过低频/高频 PNG 编码) */
|
/** BGR Mat → Skia image (bypasses low/high freq PNG encoding) */
|
||||||
matToSkiaImage(src: WrappedMat): Promise<SkImage | null>;
|
matToSkiaImage(src: WrappedMat): Promise<SkImage | null>;
|
||||||
/** 单通道灰度 Mat → Skia RGBA(跳过 BGR 伪彩 + 四通道 matToBuffer) */
|
/** Single-channel grayscale Mat → Skia RGBA (bypasses BGR pseudocolor + 4-channel matToBuffer) */
|
||||||
grayMatToSkiaImage(src: WrappedMat): SkImage | null;
|
grayMatToSkiaImage(src: WrappedMat): SkImage | null;
|
||||||
/** 连续 BGR 缓冲 → Skia 图像(工作分辨率原图 / 高低频,复用 OpenCV 解码结果) */
|
/** Continuous BGR buffer → Skia image (work-resolution origin / freq layers, reusing OpenCV decode result) */
|
||||||
bgrBufferToSkiaImage(buffer: Uint8Array, cols: number, rows: number): Promise<SkImage | null>;
|
bgrBufferToSkiaImage(buffer: Uint8Array, cols: number, rows: number): Promise<SkImage | null>;
|
||||||
threshold(src: WrappedMat, dst: WrappedMat, thresh: number, maxval: number, type: number): Promise<void>;
|
threshold(src: WrappedMat, dst: WrappedMat, thresh: number, maxval: number, type: number): Promise<void>;
|
||||||
getStructuringElement(shape: MorphShapes, ksize: {
|
getStructuringElement(shape: MorphShapes, ksize: {
|
||||||
@ -113,4 +113,3 @@ declare const cv: {
|
|||||||
imwrite(path: string, mat: WrappedMat): Promise<void>;
|
imwrite(path: string, mat: WrappedMat): Promise<void>;
|
||||||
};
|
};
|
||||||
export default cv;
|
export default cv;
|
||||||
//# sourceMappingURL=opencvAdapter.d.ts.map
|
|
||||||
1
dist/utils/opencvAdapter.d.ts.map
vendored
1
dist/utils/opencvAdapter.d.ts.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"opencvAdapter.d.ts","sourceRoot":"","sources":["../../src/utils/opencvAdapter.ts"],"names":[],"mappings":"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"}
|
|
||||||
357
dist/utils/opencvAdapter.js
vendored
357
dist/utils/opencvAdapter.js
vendored
@ -1,353 +1,4 @@
|
|||||||
/**
|
"use strict";var w=Object.create;var h=Object.defineProperty;var _=Object.getOwnPropertyDescriptor;var W=Object.getOwnPropertyNames;var T=Object.getPrototypeOf,E=Object.prototype.hasOwnProperty;var A=(t,e,r)=>e in t?h(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r;var S=(t,e)=>{for(var r in e)h(t,r,{get:e[r],enumerable:!0})},P=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of W(e))!E.call(t,o)&&o!==r&&h(t,o,{get:()=>e[o],enumerable:!(n=_(e,o))||n.enumerable});return t};var I=(t,e,r)=>(r=t!=null?w(T(t)):{},P(e||!t||!t.__esModule?h(r,"default",{value:t,enumerable:!0}):r,t)),k=t=>P(h({},"__esModule",{value:!0}),t);var b=(t,e,r)=>A(t,typeof e!="symbol"?e+"":e,r);var N={};S(N,{ContourWrapper:()=>v,WrappedMat:()=>l,default:()=>L});module.exports=k(N);var f=I(require("react-native-fs")),a=require("react-native-fast-opencv"),u=require("./pngImage"),C=require("./skiaImage");const g=0;class l{constructor(e,r,n,o=3){b(this,"mat");b(this,"cols");b(this,"rows");b(this,"channels");this.mat=e,this.cols=r,this.rows=n,this.channels=o}release(){a.OpenCV.releaseBuffers([this.mat.id])}async clone(){const e=a.OpenCV.invoke("clone",this.mat);return new l(e,this.cols,this.rows,this.channels)}}class v{constructor(e){b(this,"pointVector");this.pointVector=e}release(){a.OpenCV.releaseBuffers([this.pointVector.id])}}function B(t,e,r){return e===void 0?a.OpenCV.createObject(a.ObjectType.Scalar,t):r===void 0?a.OpenCV.createObject(a.ObjectType.Scalar,t,e,0):a.OpenCV.createObject(a.ObjectType.Scalar,t,e,r)}async function y(t,e=!1){const r=(0,u.normalizePath)(t),n=await f.default.readFile(r,"base64"),o=a.OpenCV.base64ToMat(n);let i;try{i=(0,u.readPngHeaderFromBase64)(n)}catch{}const{mat:s,extraReleaseIds:p}=(0,u.ensureMat8U)(o,i),m=a.OpenCV.toJSValue(s);if(e){const M=a.OpenCV.createObject(a.ObjectType.Mat,m.rows,m.cols,a.DataTypes.CV_8UC1);return a.OpenCV.invoke("cvtColor",s,M,a.ColorConversionCodes.COLOR_BGR2GRAY),a.OpenCV.releaseBuffers([...new Set([o.id,s.id,...p])]),new l(M,m.cols,m.rows,1)}const d=m.type===a.DataTypes.CV_8UC1?1:m.type===a.DataTypes.CV_8UC4?4:3;return s.id!==o.id&&a.OpenCV.releaseBuffers([o.id]),new l(s,m.cols,m.rows,d)}function R(t,e){return a.OpenCV.createObject(a.ObjectType.Size,t,e)}function O(t){const e=Math.max(1,Math.round(t));return e%2===0?e+1:e}const c={IMREAD_GRAYSCALE:g,THRESH_BINARY:a.ThresholdTypes.THRESH_BINARY,MORPH_RECT:a.MorphShapes.MORPH_RECT,MORPH_ELLIPSE:a.MorphShapes.MORPH_ELLIPSE,MORPH_OPEN:a.MorphTypes.MORPH_OPEN,MORPH_CLOSE:a.MorphTypes.MORPH_CLOSE,RETR_EXTERNAL:a.RetrievalModes.RETR_EXTERNAL,CHAIN_APPROX_SIMPLE:a.ContourApproximationModes.CHAIN_APPROX_SIMPLE,CHAIN_APPROX_NONE:a.ContourApproximationModes.CHAIN_APPROX_NONE,COLOR_BGR2GRAY:a.ColorConversionCodes.COLOR_BGR2GRAY,COLOR_BGR2Lab:a.ColorConversionCodes.COLOR_BGR2Lab,COLOR_Lab2BGR:a.ColorConversionCodes.COLOR_Lab2BGR,COLOR_GRAY2BGR:a.ColorConversionCodes.COLOR_GRAY2BGR,CV_8UC1:a.DataTypes.CV_8UC1,CV_16SC1:a.DataTypes.CV_16SC1,INTER_LINEAR:a.InterpolationFlags.INTER_LINEAR,INTER_NEAREST:a.InterpolationFlags.INTER_NEAREST,async ensurePngPath(t,e){const r=e??(0,u.pngCacheName)(t,"img");return(0,u.ensurePngFile)(t,r)},async imread(t,e){const r=(0,u.normalizePath)(t);if((0,u.isPngPath)(r)&&await f.default.exists(r)){if(e===g)return y(r,!0);const{buffer:p,cols:m,rows:d}=await(0,u.readPngBgrBuffer)(r);return c.bgrBufferToMat(p,m,d)}const n=await(0,u.ensurePngFile)(t,(0,u.pngCacheName)(t,"imread"));if(e===g)return y(n,!0);const{buffer:o,cols:i,rows:s}=await(0,u.readPngBgrBuffer)(n);return c.bgrBufferToMat(o,i,s)},createMat(t,e,r=1){const n=r===1?a.DataTypes.CV_8UC1:r===3?a.DataTypes.CV_8UC3:a.DataTypes.CV_8UC4,o=a.OpenCV.createObject(a.ObjectType.Mat,e,t,n);return new l(o,t,e,r)},async cvtColor(t,e){const r=c.createMat(t.cols,t.rows,1);return a.OpenCV.invoke("cvtColor",t.mat,r.mat,e),r},cvtColorBgr(t,e){const r=c.createMat(t.cols,t.rows,3);return a.OpenCV.invoke("cvtColor",t.mat,r.mat,e),r},grayToBgr(t){const e=c.createMat(t.cols,t.rows,3);return a.OpenCV.invoke("cvtColor",t.mat,e.mat,a.ColorConversionCodes.COLOR_GRAY2BGR),e},async ensureBgr3(t){if(t.channels===3)return t;const e=c.createMat(t.cols,t.rows,3),r=t.channels===4?a.ColorConversionCodes.COLOR_BGRA2BGR:a.ColorConversionCodes.COLOR_GRAY2BGR;return a.OpenCV.invoke("cvtColor",t.mat,e.mat,r),e},binaryBufferToMat(t,e,r){const n=a.OpenCV.bufferToMat("uint8",r,e,1,t);return new l(n,e,r,1)},bgrBufferToMat(t,e,r){const n=a.OpenCV.bufferToMat("uint8",r,e,3,t);return new l(n,e,r,3)},async grayBufferToMat(t,e,r){const n=`${f.default.CachesDirectoryPath}/seg_bin_${Date.now()}.pgm`,o=`P5
|
||||||
* OpenCV 便捷适配层
|
${e} ${r}
|
||||||
* 将 react-native-fast-opencv 的 invoke API 封装为 MaskSegmentCanvas 所需的 async 风格接口
|
255
|
||||||
*/
|
`,i=new TextEncoder().encode(o),s=new Uint8Array(i.length+t.length);s.set(i,0),s.set(t,i.length);let p="";const m=8192;for(let d=0;d<s.length;d+=m){const M=s.subarray(d,d+m);p+=String.fromCharCode(...M)}await f.default.writeFile(n,btoa(p),"base64");try{return y(n,!0)}finally{await f.default.exists(n)&&await f.default.unlink(n)}},matToBuffer(t){const e=a.OpenCV.invoke("clone",t.mat);try{const{buffer:r,cols:n,rows:o,channels:i}=a.OpenCV.matToBuffer(e,"uint8");return{buffer:r,cols:n,rows:o,channels:i}}finally{a.OpenCV.releaseBuffers([e.id])}},async inRangeBgr(t,e,r,n){const o=B(Math.max(0,e.b-r),Math.max(0,e.g-r),Math.max(0,e.r-r)),i=B(Math.min(255,e.b+r),Math.min(255,e.g+r),Math.min(255,e.r+r));a.OpenCV.invoke("inRange",t.mat,o,i,n.mat)},async resize(t,e,r,n=a.InterpolationFlags.INTER_LINEAR){const o=R(r.width,r.height);a.OpenCV.invoke("resize",t.mat,e.mat,o,0,0,n),e.cols=r.width,e.rows=r.height},async resizeBgrBuffer(t,e,r,n,o,i=a.InterpolationFlags.INTER_NEAREST){if(e===n&&r===o)return t;const s=c.bgrBufferToMat(t,e,r),p=c.createMat(n,o,3);try{return await c.resize(s,p,{width:n,height:o},i),c.matToBuffer(p).buffer}finally{s.release(),p.release()}},async matToRgbaBuffer(t){const e=c.createMat(t.cols,t.rows,4);try{a.OpenCV.invoke("cvtColor",t.mat,e.mat,a.ColorConversionCodes.COLOR_BGR2RGBA);const{buffer:r,cols:n,rows:o}=c.matToBuffer(e);return{buffer:r,cols:n,rows:o}}finally{e.release()}},async matToSkiaImage(t){const{buffer:e,cols:r,rows:n}=await c.matToRgbaBuffer(t);return(0,C.rgbaBufferToSkiaImage)(e,r,n)},grayMatToSkiaImage(t){const{buffer:e,cols:r,rows:n}=c.matToBuffer(t),o=r*n,i=new Uint8Array(o*4);for(let s=0;s<o;s++){const p=e[s],m=s*4;i[m]=p,i[m+1]=p,i[m+2]=p,i[m+3]=255}return(0,C.rgbaBufferToSkiaImage)(i,r,n)},async bgrBufferToSkiaImage(t,e,r){const n=c.bgrBufferToMat(t,e,r);try{return await c.matToSkiaImage(n)}finally{n.release()}},async threshold(t,e,r,n,o){a.OpenCV.invoke("threshold",t.mat,e.mat,r,n,o)},async getStructuringElement(t,e){const r=R(e.width,e.height),n=a.OpenCV.invoke("getStructuringElement",t,r);return new l(n,e.width,e.height,1)},async morphologyEx(t,e,r,n){a.OpenCV.invoke("morphologyEx",t.mat,e.mat,r,n.mat)},async findContours(t,e,r){const n=a.OpenCV.createObject(a.ObjectType.PointVectorOfVectors);a.OpenCV.invoke("findContours",t.mat,n,e,r);const i=a.OpenCV.toJSValue(n).array.map((s,p)=>{const m=a.OpenCV.copyObjectFromVector(n,p);return new v(m)});return a.OpenCV.releaseBuffers([n.id]),i},async contourArea(t){return a.OpenCV.invoke("contourArea",t.pointVector,!1).value},async boundingRect(t){const e=a.OpenCV.invoke("boundingRect",t.pointVector),r=a.OpenCV.toJSValue(e);return{x:r.x,y:r.y,width:r.width,height:r.height}},async arcLength(t,e){return a.OpenCV.invoke("arcLength",t.pointVector,e).value},async approxPolyDP(t,e,r){const n=a.OpenCV.createObject(a.ObjectType.PointVector);try{return a.OpenCV.invoke("approxPolyDP",t.pointVector,n,e,r),a.OpenCV.toJSValue(n).array}finally{a.OpenCV.releaseBuffers([n.id])}},async GaussianBlur(t,e,r,n){const o=O(r.width),i=O(r.height),s=R(o,i);a.OpenCV.invoke("GaussianBlur",t.mat,e.mat,s,n)},extractChannel(t,e,r){a.OpenCV.invoke("extractChannel",t.mat,e.mat,r)},convertTo(t,e,r,n=1,o=0){a.OpenCV.invoke("convertTo",t.mat,e.mat,r,n,o)},async subtract(t,e,r){a.OpenCV.invoke("subtract",t.mat,e.mat,r.mat)},async addWeighted(t,e,r,n,o,i){r?a.OpenCV.invoke("addWeighted",t.mat,e,r.mat,n,o,i.mat):a.OpenCV.invoke("addWeighted",t.mat,e,t.mat,0,o,i.mat)},async imwrite(t,e){const r=(0,u.normalizePath)(t);a.OpenCV.saveMatToFile(e.mat,r,"png",u.PNG_COMPRESSION)}};var L=c;
|
||||||
import RNFS from 'react-native-fs';
|
|
||||||
import { OpenCV, ObjectType, DataTypes, ColorConversionCodes, ThresholdTypes, MorphShapes, MorphTypes, RetrievalModes, ContourApproximationModes, InterpolationFlags, } from 'react-native-fast-opencv';
|
|
||||||
import { PNG_COMPRESSION, ensureMat8U, ensurePngFile, isPngPath, normalizePath, pngCacheName, readPngBgrBuffer, readPngHeaderFromBase64, } from './pngImage';
|
|
||||||
import { rgbaBufferToSkiaImage } from './skiaImage';
|
|
||||||
const IMREAD_GRAYSCALE = 0;
|
|
||||||
export class WrappedMat {
|
|
||||||
mat;
|
|
||||||
cols;
|
|
||||||
rows;
|
|
||||||
channels;
|
|
||||||
constructor(mat, cols, rows, channels = 3) {
|
|
||||||
this.mat = mat;
|
|
||||||
this.cols = cols;
|
|
||||||
this.rows = rows;
|
|
||||||
this.channels = channels;
|
|
||||||
}
|
|
||||||
release() {
|
|
||||||
OpenCV.releaseBuffers([this.mat.id]);
|
|
||||||
}
|
|
||||||
async clone() {
|
|
||||||
const cloned = OpenCV.invoke('clone', this.mat);
|
|
||||||
return new WrappedMat(cloned, this.cols, this.rows, this.channels);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export class ContourWrapper {
|
|
||||||
pointVector;
|
|
||||||
constructor(pointVector) {
|
|
||||||
this.pointVector = pointVector;
|
|
||||||
}
|
|
||||||
release() {
|
|
||||||
OpenCV.releaseBuffers([this.pointVector.id]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function createScalar(a, b, c) {
|
|
||||||
if (b === undefined) {
|
|
||||||
return OpenCV.createObject(ObjectType.Scalar, a);
|
|
||||||
}
|
|
||||||
if (c === undefined) {
|
|
||||||
return OpenCV.createObject(ObjectType.Scalar, a, b, 0);
|
|
||||||
}
|
|
||||||
return OpenCV.createObject(ObjectType.Scalar, a, b, c);
|
|
||||||
}
|
|
||||||
async function readImageFromPath(path, grayscale = false) {
|
|
||||||
const filePath = normalizePath(path);
|
|
||||||
const base64 = await RNFS.readFile(filePath, 'base64');
|
|
||||||
const srcMat = OpenCV.base64ToMat(base64);
|
|
||||||
// 16-bit PNG 降级: toJSValue 可能崩溃,先解析 PNG 头
|
|
||||||
let pngHeader;
|
|
||||||
try {
|
|
||||||
pngHeader = readPngHeaderFromBase64(base64);
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
// 非 PNG 不传
|
|
||||||
}
|
|
||||||
const { mat, extraReleaseIds } = ensureMat8U(srcMat, pngHeader);
|
|
||||||
const info = OpenCV.toJSValue(mat);
|
|
||||||
if (grayscale) {
|
|
||||||
const gray = OpenCV.createObject(ObjectType.Mat, info.rows, info.cols, DataTypes.CV_8UC1);
|
|
||||||
OpenCV.invoke('cvtColor', mat, gray, ColorConversionCodes.COLOR_BGR2GRAY);
|
|
||||||
OpenCV.releaseBuffers([
|
|
||||||
...new Set([srcMat.id, mat.id, ...extraReleaseIds]),
|
|
||||||
]);
|
|
||||||
return new WrappedMat(gray, info.cols, info.rows, 1);
|
|
||||||
}
|
|
||||||
const channels = info.type === DataTypes.CV_8UC1
|
|
||||||
? 1
|
|
||||||
: info.type === DataTypes.CV_8UC4
|
|
||||||
? 4
|
|
||||||
: 3;
|
|
||||||
if (mat.id !== srcMat.id) {
|
|
||||||
OpenCV.releaseBuffers([srcMat.id]);
|
|
||||||
}
|
|
||||||
return new WrappedMat(mat, info.cols, info.rows, channels);
|
|
||||||
}
|
|
||||||
function createSize(width, height) {
|
|
||||||
return OpenCV.createObject(ObjectType.Size, width, height);
|
|
||||||
}
|
|
||||||
/** OpenCV GaussianBlur 要求核宽高为正奇数 */
|
|
||||||
function normalizeGaussianKernel(size) {
|
|
||||||
const n = Math.max(1, Math.round(size));
|
|
||||||
return n % 2 === 0 ? n + 1 : n;
|
|
||||||
}
|
|
||||||
const cv = {
|
|
||||||
IMREAD_GRAYSCALE,
|
|
||||||
THRESH_BINARY: ThresholdTypes.THRESH_BINARY,
|
|
||||||
MORPH_RECT: MorphShapes.MORPH_RECT,
|
|
||||||
MORPH_ELLIPSE: MorphShapes.MORPH_ELLIPSE,
|
|
||||||
MORPH_OPEN: MorphTypes.MORPH_OPEN,
|
|
||||||
MORPH_CLOSE: MorphTypes.MORPH_CLOSE,
|
|
||||||
RETR_EXTERNAL: RetrievalModes.RETR_EXTERNAL,
|
|
||||||
CHAIN_APPROX_SIMPLE: ContourApproximationModes.CHAIN_APPROX_SIMPLE,
|
|
||||||
CHAIN_APPROX_NONE: ContourApproximationModes.CHAIN_APPROX_NONE,
|
|
||||||
COLOR_BGR2GRAY: ColorConversionCodes.COLOR_BGR2GRAY,
|
|
||||||
COLOR_BGR2Lab: ColorConversionCodes.COLOR_BGR2Lab,
|
|
||||||
COLOR_Lab2BGR: ColorConversionCodes.COLOR_Lab2BGR,
|
|
||||||
COLOR_GRAY2BGR: ColorConversionCodes.COLOR_GRAY2BGR,
|
|
||||||
CV_8UC1: DataTypes.CV_8UC1,
|
|
||||||
CV_16SC1: DataTypes.CV_16SC1,
|
|
||||||
INTER_LINEAR: InterpolationFlags.INTER_LINEAR,
|
|
||||||
INTER_NEAREST: InterpolationFlags.INTER_NEAREST,
|
|
||||||
async ensurePngPath(path, cacheFileName) {
|
|
||||||
const name = cacheFileName ?? pngCacheName(path, 'img');
|
|
||||||
return ensurePngFile(path, name);
|
|
||||||
},
|
|
||||||
async imread(path, flags) {
|
|
||||||
const filePath = normalizePath(path);
|
|
||||||
if (isPngPath(filePath) && (await RNFS.exists(filePath))) {
|
|
||||||
if (flags === IMREAD_GRAYSCALE) {
|
|
||||||
return readImageFromPath(filePath, true);
|
|
||||||
}
|
|
||||||
const { buffer, cols, rows } = await readPngBgrBuffer(filePath);
|
|
||||||
return cv.bgrBufferToMat(buffer, cols, rows);
|
|
||||||
}
|
|
||||||
const pngPath = await ensurePngFile(path, pngCacheName(path, 'imread'));
|
|
||||||
if (flags === IMREAD_GRAYSCALE) {
|
|
||||||
return readImageFromPath(pngPath, true);
|
|
||||||
}
|
|
||||||
const { buffer, cols, rows } = await readPngBgrBuffer(pngPath);
|
|
||||||
return cv.bgrBufferToMat(buffer, cols, rows);
|
|
||||||
},
|
|
||||||
createMat(cols, rows, channels = 1) {
|
|
||||||
const type = channels === 1
|
|
||||||
? DataTypes.CV_8UC1
|
|
||||||
: channels === 3
|
|
||||||
? DataTypes.CV_8UC3
|
|
||||||
: DataTypes.CV_8UC4;
|
|
||||||
const mat = OpenCV.createObject(ObjectType.Mat, rows, cols, type);
|
|
||||||
return new WrappedMat(mat, cols, rows, channels);
|
|
||||||
},
|
|
||||||
async cvtColor(src, code) {
|
|
||||||
const dst = cv.createMat(src.cols, src.rows, 1);
|
|
||||||
OpenCV.invoke('cvtColor', src.mat, dst.mat, code);
|
|
||||||
return dst;
|
|
||||||
},
|
|
||||||
/** 三通道色彩空间转换(BGR/Lab 等) */
|
|
||||||
cvtColorBgr(src, code) {
|
|
||||||
const dst = cv.createMat(src.cols, src.rows, 3);
|
|
||||||
OpenCV.invoke('cvtColor', src.mat, dst.mat, code);
|
|
||||||
return dst;
|
|
||||||
},
|
|
||||||
/** 灰度 Mat → 三通道 BGR(供 Skia 显示) */
|
|
||||||
grayToBgr(src) {
|
|
||||||
const dst = cv.createMat(src.cols, src.rows, 3);
|
|
||||||
OpenCV.invoke('cvtColor', src.mat, dst.mat, ColorConversionCodes.COLOR_GRAY2BGR);
|
|
||||||
return dst;
|
|
||||||
},
|
|
||||||
/** 掩码统一为三通道 BGR;已是 3 通道则原样返回,色序由分割侧 swapBr 检测 */
|
|
||||||
async ensureBgr3(src) {
|
|
||||||
if (src.channels === 3) {
|
|
||||||
return src;
|
|
||||||
}
|
|
||||||
const dst = cv.createMat(src.cols, src.rows, 3);
|
|
||||||
const code = src.channels === 4
|
|
||||||
? ColorConversionCodes.COLOR_BGRA2BGR
|
|
||||||
: ColorConversionCodes.COLOR_GRAY2BGR;
|
|
||||||
OpenCV.invoke('cvtColor', src.mat, dst.mat, code);
|
|
||||||
return dst;
|
|
||||||
},
|
|
||||||
/** JS 二值缓冲(0/255)→ 单通道 Mat */
|
|
||||||
binaryBufferToMat(buffer, cols, rows) {
|
|
||||||
const mat = OpenCV.bufferToMat('uint8', rows, cols, 1, buffer);
|
|
||||||
return new WrappedMat(mat, cols, rows, 1);
|
|
||||||
},
|
|
||||||
/** 连续 BGR 缓冲 → 三通道 Mat */
|
|
||||||
bgrBufferToMat(buffer, cols, rows) {
|
|
||||||
const mat = OpenCV.bufferToMat('uint8', rows, cols, 3, buffer);
|
|
||||||
return new WrappedMat(mat, cols, rows, 3);
|
|
||||||
},
|
|
||||||
/** 将 JS 侧生成的灰度二值图写入临时 PGM 并读回 Mat */
|
|
||||||
async grayBufferToMat(gray, cols, rows) {
|
|
||||||
const path = `${RNFS.CachesDirectoryPath}/seg_bin_${Date.now()}.pgm`;
|
|
||||||
const header = `P5\n${cols} ${rows}\n255\n`;
|
|
||||||
const headerBytes = new TextEncoder().encode(header);
|
|
||||||
const fileBytes = new Uint8Array(headerBytes.length + gray.length);
|
|
||||||
fileBytes.set(headerBytes, 0);
|
|
||||||
fileBytes.set(gray, headerBytes.length);
|
|
||||||
let binary = '';
|
|
||||||
const chunkSize = 8192;
|
|
||||||
for (let i = 0; i < fileBytes.length; i += chunkSize) {
|
|
||||||
const slice = fileBytes.subarray(i, i + chunkSize);
|
|
||||||
binary += String.fromCharCode(...slice);
|
|
||||||
}
|
|
||||||
await RNFS.writeFile(path, btoa(binary), 'base64');
|
|
||||||
try {
|
|
||||||
return readImageFromPath(path, true);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
if (await RNFS.exists(path)) {
|
|
||||||
await RNFS.unlink(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 导出 Mat 像素。先 clone 保证内存连续,避免原生 matToBuffer 忽略 step 导致行错位。
|
|
||||||
*/
|
|
||||||
matToBuffer(src) {
|
|
||||||
const continuous = OpenCV.invoke('clone', src.mat);
|
|
||||||
try {
|
|
||||||
const { buffer, cols, rows, channels } = OpenCV.matToBuffer(continuous, 'uint8');
|
|
||||||
return { buffer, cols, rows, channels };
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
OpenCV.releaseBuffers([continuous.id]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async inRangeBgr(src, color, tolerance, dst) {
|
|
||||||
const lower = createScalar(Math.max(0, color.b - tolerance), Math.max(0, color.g - tolerance), Math.max(0, color.r - tolerance));
|
|
||||||
const upper = createScalar(Math.min(255, color.b + tolerance), Math.min(255, color.g + tolerance), Math.min(255, color.r + tolerance));
|
|
||||||
OpenCV.invoke('inRange', src.mat, lower, upper, dst.mat);
|
|
||||||
},
|
|
||||||
async resize(src, dst, size, interpolation = InterpolationFlags.INTER_LINEAR) {
|
|
||||||
const dsize = createSize(size.width, size.height);
|
|
||||||
OpenCV.invoke('resize', src.mat, dst.mat, dsize, 0, 0, interpolation);
|
|
||||||
dst.cols = size.width;
|
|
||||||
dst.rows = size.height;
|
|
||||||
},
|
|
||||||
/** BGR 缓冲原生缩放(掩码用语义色,默认最近邻) */
|
|
||||||
async resizeBgrBuffer(buffer, srcCols, srcRows, dstCols, dstRows, interpolation = InterpolationFlags.INTER_NEAREST) {
|
|
||||||
if (srcCols === dstCols && srcRows === dstRows) {
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
const srcMat = cv.bgrBufferToMat(buffer, srcCols, srcRows);
|
|
||||||
const dstMat = cv.createMat(dstCols, dstRows, 3);
|
|
||||||
try {
|
|
||||||
await cv.resize(srcMat, dstMat, { width: dstCols, height: dstRows }, interpolation);
|
|
||||||
return cv.matToBuffer(dstMat).buffer;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
srcMat.release();
|
|
||||||
dstMat.release();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/** BGR Mat → RGBA 连续缓冲(供 Skia 直传) */
|
|
||||||
async matToRgbaBuffer(src) {
|
|
||||||
const dst = cv.createMat(src.cols, src.rows, 4);
|
|
||||||
try {
|
|
||||||
OpenCV.invoke('cvtColor', src.mat, dst.mat, ColorConversionCodes.COLOR_BGR2RGBA);
|
|
||||||
const { buffer, cols, rows } = cv.matToBuffer(dst);
|
|
||||||
return { buffer, cols, rows };
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
dst.release();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/** BGR Mat → Skia 图像(跳过低频/高频 PNG 编码) */
|
|
||||||
async matToSkiaImage(src) {
|
|
||||||
const { buffer, cols, rows } = await cv.matToRgbaBuffer(src);
|
|
||||||
return rgbaBufferToSkiaImage(buffer, cols, rows);
|
|
||||||
},
|
|
||||||
/** 单通道灰度 Mat → Skia RGBA(跳过 BGR 伪彩 + 四通道 matToBuffer) */
|
|
||||||
grayMatToSkiaImage(src) {
|
|
||||||
const { buffer, cols, rows } = cv.matToBuffer(src);
|
|
||||||
const pixelCount = cols * rows;
|
|
||||||
const rgba = new Uint8Array(pixelCount * 4);
|
|
||||||
for (let i = 0; i < pixelCount; i++) {
|
|
||||||
const value = buffer[i];
|
|
||||||
const offset = i * 4;
|
|
||||||
rgba[offset] = value;
|
|
||||||
rgba[offset + 1] = value;
|
|
||||||
rgba[offset + 2] = value;
|
|
||||||
rgba[offset + 3] = 255;
|
|
||||||
}
|
|
||||||
return rgbaBufferToSkiaImage(rgba, cols, rows);
|
|
||||||
},
|
|
||||||
/** 连续 BGR 缓冲 → Skia 图像(工作分辨率原图 / 高低频,复用 OpenCV 解码结果) */
|
|
||||||
async bgrBufferToSkiaImage(buffer, cols, rows) {
|
|
||||||
const mat = cv.bgrBufferToMat(buffer, cols, rows);
|
|
||||||
try {
|
|
||||||
return await cv.matToSkiaImage(mat);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
mat.release();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async threshold(src, dst, thresh, maxval, type) {
|
|
||||||
OpenCV.invoke('threshold', src.mat, dst.mat, thresh, maxval, type);
|
|
||||||
},
|
|
||||||
async getStructuringElement(shape, ksize) {
|
|
||||||
const size = createSize(ksize.width, ksize.height);
|
|
||||||
const kernel = OpenCV.invoke('getStructuringElement', shape, size);
|
|
||||||
return new WrappedMat(kernel, ksize.width, ksize.height, 1);
|
|
||||||
},
|
|
||||||
async morphologyEx(src, dst, op, kernel) {
|
|
||||||
OpenCV.invoke('morphologyEx', src.mat, dst.mat, op, kernel.mat);
|
|
||||||
},
|
|
||||||
async findContours(image, mode, method) {
|
|
||||||
const contours = OpenCV.createObject(ObjectType.PointVectorOfVectors);
|
|
||||||
OpenCV.invoke('findContours', image.mat, contours, mode, method);
|
|
||||||
const data = OpenCV.toJSValue(contours);
|
|
||||||
const wrappers = data.array.map((_, index) => {
|
|
||||||
const pv = OpenCV.copyObjectFromVector(contours, index);
|
|
||||||
return new ContourWrapper(pv);
|
|
||||||
});
|
|
||||||
OpenCV.releaseBuffers([contours.id]);
|
|
||||||
return wrappers;
|
|
||||||
},
|
|
||||||
async contourArea(contour) {
|
|
||||||
const result = OpenCV.invoke('contourArea', contour.pointVector, false);
|
|
||||||
return result.value;
|
|
||||||
},
|
|
||||||
async boundingRect(contour) {
|
|
||||||
const rect = OpenCV.invoke('boundingRect', contour.pointVector);
|
|
||||||
const js = OpenCV.toJSValue(rect);
|
|
||||||
return { x: js.x, y: js.y, width: js.width, height: js.height };
|
|
||||||
},
|
|
||||||
async arcLength(contour, closed) {
|
|
||||||
const result = OpenCV.invoke('arcLength', contour.pointVector, closed);
|
|
||||||
return result.value;
|
|
||||||
},
|
|
||||||
async approxPolyDP(contour, epsilon, closed) {
|
|
||||||
const approx = OpenCV.createObject(ObjectType.PointVector);
|
|
||||||
try {
|
|
||||||
OpenCV.invoke('approxPolyDP', contour.pointVector, approx, epsilon, closed);
|
|
||||||
return OpenCV.toJSValue(approx).array;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
OpenCV.releaseBuffers([approx.id]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async GaussianBlur(src, dst, ksize, sigma) {
|
|
||||||
const width = normalizeGaussianKernel(ksize.width);
|
|
||||||
const height = normalizeGaussianKernel(ksize.height);
|
|
||||||
const size = createSize(width, height);
|
|
||||||
OpenCV.invoke('GaussianBlur', src.mat, dst.mat, size, sigma);
|
|
||||||
},
|
|
||||||
extractChannel(src, dst, channel) {
|
|
||||||
OpenCV.invoke('extractChannel', src.mat, dst.mat, channel);
|
|
||||||
},
|
|
||||||
convertTo(src, dst, rtype, alpha = 1, beta = 0) {
|
|
||||||
OpenCV.invoke('convertTo', src.mat, dst.mat, rtype, alpha, beta);
|
|
||||||
},
|
|
||||||
async subtract(src1, src2, dst) {
|
|
||||||
OpenCV.invoke('subtract', src1.mat, src2.mat, dst.mat);
|
|
||||||
},
|
|
||||||
async addWeighted(src1, alpha, src2, beta, gamma, dst) {
|
|
||||||
if (src2) {
|
|
||||||
OpenCV.invoke('addWeighted', src1.mat, alpha, src2.mat, beta, gamma, dst.mat);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
OpenCV.invoke('addWeighted', src1.mat, alpha, src1.mat, 0, gamma, dst.mat);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async imwrite(path, mat) {
|
|
||||||
const filePath = normalizePath(path);
|
|
||||||
OpenCV.saveMatToFile(mat.mat, filePath, 'png', PNG_COMPRESSION);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
export default cv;
|
|
||||||
//# sourceMappingURL=opencvAdapter.js.map
|
|
||||||
|
|||||||
1
dist/utils/opencvAdapter.js.map
vendored
1
dist/utils/opencvAdapter.js.map
vendored
File diff suppressed because one or more lines are too long
3
dist/utils/paintColorMapTexture.d.ts
vendored
3
dist/utils/paintColorMapTexture.d.ts
vendored
@ -1,5 +1,4 @@
|
|||||||
import { type SkImage } from '@shopify/react-native-skia';
|
import { type SkImage } from '@shopify/react-native-skia';
|
||||||
import type { BgrColor } from '../components/MaskSegmentCanvas.types';
|
import type { BgrColor } from '../components/MaskSegmentCanvas.types';
|
||||||
/** 按 pickMap 展开的上色颜色图(与 pick 同尺寸,未上色像素 a=0)。支持 maskFeather 产生软边缘 alpha。 */
|
/** Paint color map expanded by pickMap (same size as pick, unpainted pixels have a=0). Supports maskFeather for soft-edge alpha. */
|
||||||
export declare function buildPaintColorMapImage(pickBuffer: Uint8Array, cols: number, rows: number, paintedRegions: Map<number, BgrColor>, featherRadius?: number): SkImage;
|
export declare function buildPaintColorMapImage(pickBuffer: Uint8Array, cols: number, rows: number, paintedRegions: Map<number, BgrColor>, featherRadius?: number): SkImage;
|
||||||
//# sourceMappingURL=paintColorMapTexture.d.ts.map
|
|
||||||
1
dist/utils/paintColorMapTexture.d.ts.map
vendored
1
dist/utils/paintColorMapTexture.d.ts.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"paintColorMapTexture.d.ts","sourceRoot":"","sources":["../../src/utils/paintColorMapTexture.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,OAAO,EACb,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uCAAuC,CAAC;AA4ItE,2EAA2E;AAC3E,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,EACrC,aAAa,SAAI,GAChB,OAAO,CAqFT"}
|
|
||||||
204
dist/utils/paintColorMapTexture.js
vendored
204
dist/utils/paintColorMapTexture.js
vendored
@ -1,203 +1 @@
|
|||||||
import { Skia, AlphaType, ColorType, } from '@shopify/react-native-skia';
|
"use strict";var U=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var g=Object.getOwnPropertyNames;var b=Object.prototype.hasOwnProperty;var C=(m,t)=>{for(var f in t)U(m,f,{get:t[f],enumerable:!0})},I=(m,t,f,p)=>{if(t&&typeof t=="object"||typeof t=="function")for(let x of g(t))!b.call(m,x)&&x!==f&&U(m,x,{get:()=>t[x],enumerable:!(p=B(t,x))||p.enumerable});return m};var P=m=>I(U({},"__esModule",{value:!0}),m);var R={};C(R,{buildPaintColorMapImage:()=>T});module.exports=P(R);var s=require("@shopify/react-native-skia");function k(m,t,f,p){const x=Math.max(1,Math.min(16,Math.round(p))),r=new Uint8Array(t*f*4),l=new Uint8Array(t*f*4);for(let u=0;u<f;u++)for(let y=0;y<t;y++){let c=0,h=0,M=0,e=0,n=0;const o=u*t*4;for(let a=-x;a<=x;a++){const d=y+a;if(d>=0&&d<t){const A=o+d*4;c+=m[A],h+=m[A+1],M+=m[A+2],e+=m[A+3],n++}}const i=o+y*4;r[i]=Math.round(c/n),r[i+1]=Math.round(h/n),r[i+2]=Math.round(M/n),r[i+3]=Math.round(e/n)}for(let u=0;u<t;u++)for(let y=0;y<f;y++){let c=0,h=0,M=0,e=0,n=0;for(let i=-x;i<=x;i++){const a=y+i;if(a>=0&&a<f){const d=a*t*4+u*4;c+=r[d],h+=r[d+1],M+=r[d+2],e+=r[d+3],n++}}const o=y*t*4+u*4;l[o]=Math.round(c/n),l[o+1]=Math.round(h/n),l[o+2]=Math.round(M/n),l[o+3]=Math.round(e/n)}return l}function G(m,t,f,p){const x=Math.max(1,Math.min(8,Math.round(p))),r=new Uint8Array(t*f*4),l=new Uint8Array(t*f*4);for(let u=0;u<f;u++)for(let y=0;y<t;y++){let c=0,h=0,M=0,e=0;const n=u*t*4;for(let i=-x;i<=x;i++){const a=y+i;if(a>=0&&a<t){const d=n+a*4;m[d+3]>e?(c=m[d],h=m[d+1],M=m[d+2],e=m[d+3]):m[d+3]===e&&e>0&&(c=Math.max(c,m[d]),h=Math.max(h,m[d+1]),M=Math.max(M,m[d+2]))}}const o=n+y*4;r[o]=c,r[o+1]=h,r[o+2]=M,r[o+3]=e}for(let u=0;u<t;u++)for(let y=0;y<f;y++){let c=0,h=0,M=0,e=0;for(let o=-x;o<=x;o++){const i=y+o;if(i>=0&&i<f){const a=i*t*4+u*4;r[a+3]>e?(c=r[a],h=r[a+1],M=r[a+2],e=r[a+3]):r[a+3]===e&&e>0&&(c=Math.max(c,r[a]),h=Math.max(h,r[a+1]),M=Math.max(M,r[a+2]))}}const n=y*t*4+u*4;l[n]=c,l[n+1]=h,l[n+2]=M,l[n+3]=e}return l}function T(m,t,f,p,x=0){const r=t*f,l=new Map;for(const[e,n]of p)l.set(e+1,n);if(x<=.1&&x>=-.1){const e=new Uint8Array(r*4);for(let o=0;o<r;o++){const i=m[o],a=i>0?l.get(i):void 0,d=o*4;a&&(e[d]=a.r,e[d+1]=a.g,e[d+2]=a.b,e[d+3]=255)}const n=s.Skia.Data.fromBytes(e);return s.Skia.Image.MakeImage({width:t,height:f,alphaType:s.AlphaType.Unpremul,colorType:s.ColorType.RGBA_8888},n,t*4)}const u=new Uint8Array(r*4);for(let e=0;e<r;e++){const n=m[e],o=n>0?l.get(n):void 0,i=e*4;o&&(u[i]=o.r,u[i+1]=o.g,u[i+2]=o.b,u[i+3]=255)}const y=k(u,t,f,x),c=G(y,t,f,x),h=new Uint8Array(r*4);for(let e=0;e<r;e++){const n=e*4,o=c[n+3];o>0&&(h[n]=Math.min(255,Math.round(c[n]*255/o)),h[n+1]=Math.min(255,Math.round(c[n+1]*255/o)),h[n+2]=Math.min(255,Math.round(c[n+2]*255/o)),h[n+3]=o)}const M=s.Skia.Data.fromBytes(h);return s.Skia.Image.MakeImage({width:t,height:f,alphaType:s.AlphaType.Unpremul,colorType:s.ColorType.RGBA_8888},M,t*4)}
|
||||||
/**
|
|
||||||
* Separable box blur on premultiplied RGBA (4-channel 0-255).
|
|
||||||
* Feather radius in pixels (at the working pick buffer resolution).
|
|
||||||
* Works on premultiplied values so that color naturally diffuses alongside alpha
|
|
||||||
* into the feather region — preventing "scatter dots" at resolution-mismatched boundaries.
|
|
||||||
*/
|
|
||||||
function boxBlurRgbaPremul(rgba, cols, rows, radius) {
|
|
||||||
const r = Math.max(1, Math.min(16, Math.round(radius)));
|
|
||||||
const tmp = new Uint8Array(cols * rows * 4);
|
|
||||||
const dst = new Uint8Array(cols * rows * 4);
|
|
||||||
// Horizontal pass
|
|
||||||
for (let y = 0; y < rows; y++) {
|
|
||||||
for (let x = 0; x < cols; x++) {
|
|
||||||
let sumR = 0, sumG = 0, sumB = 0, sumA = 0;
|
|
||||||
let cnt = 0;
|
|
||||||
const base = y * cols * 4;
|
|
||||||
for (let dx = -r; dx <= r; dx++) {
|
|
||||||
const xx = x + dx;
|
|
||||||
if (xx >= 0 && xx < cols) {
|
|
||||||
const idx = base + xx * 4;
|
|
||||||
sumR += rgba[idx];
|
|
||||||
sumG += rgba[idx + 1];
|
|
||||||
sumB += rgba[idx + 2];
|
|
||||||
sumA += rgba[idx + 3];
|
|
||||||
cnt++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const o = base + x * 4;
|
|
||||||
tmp[o] = Math.round(sumR / cnt);
|
|
||||||
tmp[o + 1] = Math.round(sumG / cnt);
|
|
||||||
tmp[o + 2] = Math.round(sumB / cnt);
|
|
||||||
tmp[o + 3] = Math.round(sumA / cnt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Vertical pass
|
|
||||||
for (let x = 0; x < cols; x++) {
|
|
||||||
for (let y = 0; y < rows; y++) {
|
|
||||||
let sumR = 0, sumG = 0, sumB = 0, sumA = 0;
|
|
||||||
let cnt = 0;
|
|
||||||
for (let dy = -r; dy <= r; dy++) {
|
|
||||||
const yy = y + dy;
|
|
||||||
if (yy >= 0 && yy < rows) {
|
|
||||||
const idx = yy * cols * 4 + x * 4;
|
|
||||||
sumR += tmp[idx];
|
|
||||||
sumG += tmp[idx + 1];
|
|
||||||
sumB += tmp[idx + 2];
|
|
||||||
sumA += tmp[idx + 3];
|
|
||||||
cnt++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const o = y * cols * 4 + x * 4;
|
|
||||||
dst[o] = Math.round(sumR / cnt);
|
|
||||||
dst[o + 1] = Math.round(sumG / cnt);
|
|
||||||
dst[o + 2] = Math.round(sumB / cnt);
|
|
||||||
dst[o + 3] = Math.round(sumA / cnt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dst;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Separable dilation (max filter) on premultiplied RGBA.
|
|
||||||
* Fills small holes (alpha=0) inside painted regions by taking the
|
|
||||||
* per-channel maximum in the neighborhood. Applied after boxBlur to
|
|
||||||
* eliminate scatter dots caused by segmentation gaps in the pick buffer.
|
|
||||||
*/
|
|
||||||
function dilateRgbaPremul(rgba, cols, rows, radius) {
|
|
||||||
const r = Math.max(1, Math.min(8, Math.round(radius)));
|
|
||||||
const tmp = new Uint8Array(cols * rows * 4);
|
|
||||||
const dst = new Uint8Array(cols * rows * 4);
|
|
||||||
// Horizontal pass
|
|
||||||
for (let y = 0; y < rows; y++) {
|
|
||||||
for (let x = 0; x < cols; x++) {
|
|
||||||
let maxR = 0, maxG = 0, maxB = 0, maxA = 0;
|
|
||||||
const base = y * cols * 4;
|
|
||||||
for (let dx = -r; dx <= r; dx++) {
|
|
||||||
const xx = x + dx;
|
|
||||||
if (xx >= 0 && xx < cols) {
|
|
||||||
const idx = base + xx * 4;
|
|
||||||
if (rgba[idx + 3] > maxA) {
|
|
||||||
maxR = rgba[idx];
|
|
||||||
maxG = rgba[idx + 1];
|
|
||||||
maxB = rgba[idx + 2];
|
|
||||||
maxA = rgba[idx + 3];
|
|
||||||
}
|
|
||||||
else if (rgba[idx + 3] === maxA && maxA > 0) {
|
|
||||||
maxR = Math.max(maxR, rgba[idx]);
|
|
||||||
maxG = Math.max(maxG, rgba[idx + 1]);
|
|
||||||
maxB = Math.max(maxB, rgba[idx + 2]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const o = base + x * 4;
|
|
||||||
tmp[o] = maxR;
|
|
||||||
tmp[o + 1] = maxG;
|
|
||||||
tmp[o + 2] = maxB;
|
|
||||||
tmp[o + 3] = maxA;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Vertical pass
|
|
||||||
for (let x = 0; x < cols; x++) {
|
|
||||||
for (let y = 0; y < rows; y++) {
|
|
||||||
let maxR = 0, maxG = 0, maxB = 0, maxA = 0;
|
|
||||||
for (let dy = -r; dy <= r; dy++) {
|
|
||||||
const yy = y + dy;
|
|
||||||
if (yy >= 0 && yy < rows) {
|
|
||||||
const idx = yy * cols * 4 + x * 4;
|
|
||||||
if (tmp[idx + 3] > maxA) {
|
|
||||||
maxR = tmp[idx];
|
|
||||||
maxG = tmp[idx + 1];
|
|
||||||
maxB = tmp[idx + 2];
|
|
||||||
maxA = tmp[idx + 3];
|
|
||||||
}
|
|
||||||
else if (tmp[idx + 3] === maxA && maxA > 0) {
|
|
||||||
maxR = Math.max(maxR, tmp[idx]);
|
|
||||||
maxG = Math.max(maxG, tmp[idx + 1]);
|
|
||||||
maxB = Math.max(maxB, tmp[idx + 2]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const o = y * cols * 4 + x * 4;
|
|
||||||
dst[o] = maxR;
|
|
||||||
dst[o + 1] = maxG;
|
|
||||||
dst[o + 2] = maxB;
|
|
||||||
dst[o + 3] = maxA;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dst;
|
|
||||||
}
|
|
||||||
/** 按 pickMap 展开的上色颜色图(与 pick 同尺寸,未上色像素 a=0)。支持 maskFeather 产生软边缘 alpha。 */
|
|
||||||
export function buildPaintColorMapImage(pickBuffer, cols, rows, paintedRegions, featherRadius = 0) {
|
|
||||||
const pixelCount = cols * rows;
|
|
||||||
const colorByPickCode = new Map();
|
|
||||||
for (const [regionId, color] of paintedRegions) {
|
|
||||||
colorByPickCode.set(regionId + 1, color);
|
|
||||||
}
|
|
||||||
// ── Perf fast-path: no feather → 1-pass direct unpremul RGBA (O(N)) ──
|
|
||||||
// Skips premul buffer, boxBlur H+V, dilate H+V, and unpremul conversion.
|
|
||||||
// The GPU shader handles boundary noise via unpremultiply (rgb /= a) +
|
|
||||||
// smoothstep gate, so no CPU-side dilate/filter is needed.
|
|
||||||
if (featherRadius <= 0.1 && featherRadius >= -0.1) {
|
|
||||||
const rgba = new Uint8Array(pixelCount * 4);
|
|
||||||
for (let i = 0; i < pixelCount; i++) {
|
|
||||||
const code = pickBuffer[i];
|
|
||||||
const color = code > 0 ? colorByPickCode.get(code) : undefined;
|
|
||||||
const o = i * 4;
|
|
||||||
if (color) {
|
|
||||||
rgba[o] = color.r;
|
|
||||||
rgba[o + 1] = color.g;
|
|
||||||
rgba[o + 2] = color.b;
|
|
||||||
rgba[o + 3] = 255;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const data = Skia.Data.fromBytes(rgba);
|
|
||||||
return Skia.Image.MakeImage({ width: cols, height: rows, alphaType: AlphaType.Unpremul, colorType: ColorType.RGBA_8888 }, data, cols * 4);
|
|
||||||
}
|
|
||||||
// ── Feathered path: premul + boxBlur + dilate + unpremul ──
|
|
||||||
// Build hard premultiplied RGBA: where a pick code maps to a painted color,
|
|
||||||
// store (R, G, B, 255) in premul space. Unpainted pixels stay (0,0,0,0).
|
|
||||||
const hardPremul = new Uint8Array(pixelCount * 4);
|
|
||||||
for (let i = 0; i < pixelCount; i++) {
|
|
||||||
const code = pickBuffer[i];
|
|
||||||
const color = code > 0 ? colorByPickCode.get(code) : undefined;
|
|
||||||
const o = i * 4;
|
|
||||||
if (color) {
|
|
||||||
hardPremul[o] = color.r;
|
|
||||||
hardPremul[o + 1] = color.g;
|
|
||||||
hardPremul[o + 2] = color.b;
|
|
||||||
hardPremul[o + 3] = 255;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Box-blur premultiplied RGBA so color naturally diffuses alongside alpha
|
|
||||||
// into the feather region. This prevents scatter dots from bilinear
|
|
||||||
// interpolation at resolution-mismatched boundaries (where unpainted=black
|
|
||||||
// would otherwise leak into boundary samples).
|
|
||||||
const blurredPremul = boxBlurRgbaPremul(hardPremul, cols, rows, featherRadius);
|
|
||||||
// Dilation pass: fill small holes (alpha=0) inside painted regions that
|
|
||||||
// survive the box blur due to segmentation gaps in the pick buffer.
|
|
||||||
const filledPremul = dilateRgbaPremul(blurredPremul, cols, rows, featherRadius);
|
|
||||||
// Unpremultiply and store as RGBA for the final SkImage.
|
|
||||||
const rgba = new Uint8Array(pixelCount * 4);
|
|
||||||
for (let i = 0; i < pixelCount; i++) {
|
|
||||||
const o = i * 4;
|
|
||||||
const a = filledPremul[o + 3];
|
|
||||||
if (a > 0) {
|
|
||||||
rgba[o] = Math.min(255, Math.round((filledPremul[o] * 255) / a));
|
|
||||||
rgba[o + 1] = Math.min(255, Math.round((filledPremul[o + 1] * 255) / a));
|
|
||||||
rgba[o + 2] = Math.min(255, Math.round((filledPremul[o + 2] * 255) / a));
|
|
||||||
rgba[o + 3] = a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const data = Skia.Data.fromBytes(rgba);
|
|
||||||
return Skia.Image.MakeImage({
|
|
||||||
width: cols,
|
|
||||||
height: rows,
|
|
||||||
// Unpremul: rgb stores the target paint color (unmodulated), alpha stores coverage (soft edge feather).
|
|
||||||
// This ensures ImageShader sampling returns the intended paint color + separate coverage alpha,
|
|
||||||
// without premultiplication assumptions that could cause dark fringes on soft edges.
|
|
||||||
alphaType: AlphaType.Unpremul,
|
|
||||||
colorType: ColorType.RGBA_8888,
|
|
||||||
}, data, cols * 4);
|
|
||||||
}
|
|
||||||
//# sourceMappingURL=paintColorMapTexture.js.map
|
|
||||||
|
|||||||
1
dist/utils/paintColorMapTexture.js.map
vendored
1
dist/utils/paintColorMapTexture.js.map
vendored
File diff suppressed because one or more lines are too long
5
dist/utils/paintShaderRuntime.d.ts
vendored
5
dist/utils/paintShaderRuntime.d.ts
vendored
@ -21,7 +21,7 @@ export type PaintShaderLayerProps = PaintShaderTextures & {
|
|||||||
height: number;
|
height: number;
|
||||||
showOrigin?: boolean;
|
showOrigin?: boolean;
|
||||||
};
|
};
|
||||||
/** Canvas 内全屏上色 Shader 层 */
|
/** Full-screen paint Shader layer inside Canvas */
|
||||||
export declare function PaintShaderLayer(props: PaintShaderLayerProps): React.JSX.Element;
|
export declare function PaintShaderLayer(props: PaintShaderLayerProps): React.JSX.Element;
|
||||||
export declare function createPaintColorMapForPaint(pickBuffer: Uint8Array, cols: number, rows: number, paintedRegions: Map<number, BgrColor>): SkImage;
|
export declare function createPaintColorMapForPaint(pickBuffer: Uint8Array, cols: number, rows: number, paintedRegions: Map<number, BgrColor>): SkImage;
|
||||||
export type OffscreenPaintInput = PaintShaderTextures & {
|
export type OffscreenPaintInput = PaintShaderTextures & {
|
||||||
@ -29,7 +29,7 @@ export type OffscreenPaintInput = PaintShaderTextures & {
|
|||||||
height: number;
|
height: number;
|
||||||
showOrigin?: boolean;
|
showOrigin?: boolean;
|
||||||
};
|
};
|
||||||
/** 离屏渲染与预览同源的 shader 合成图 */
|
/** Offscreen-rendered shader composite matching the preview */
|
||||||
export declare function renderPaintedImageOffscreen(input: OffscreenPaintInput): Promise<SkImage | null>;
|
export declare function renderPaintedImageOffscreen(input: OffscreenPaintInput): Promise<SkImage | null>;
|
||||||
export declare function releasePaintShaderTextures(textures: {
|
export declare function releasePaintShaderTextures(textures: {
|
||||||
originImage?: SkImage | null;
|
originImage?: SkImage | null;
|
||||||
@ -37,4 +37,3 @@ export declare function releasePaintShaderTextures(textures: {
|
|||||||
lowFreqImage?: SkImage | null;
|
lowFreqImage?: SkImage | null;
|
||||||
highFreqImage?: SkImage | null;
|
highFreqImage?: SkImage | null;
|
||||||
}): void;
|
}): void;
|
||||||
//# sourceMappingURL=paintShaderRuntime.d.ts.map
|
|
||||||
1
dist/utils/paintShaderRuntime.d.ts.map
vendored
1
dist/utils/paintShaderRuntime.d.ts.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"paintShaderRuntime.d.ts","sourceRoot":"","sources":["../../src/utils/paintShaderRuntime.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAOL,KAAK,OAAO,EACZ,KAAK,eAAe,EACrB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uCAAuC,CAAC;AAMtE,wBAAgB,oBAAoB,IAAI,eAAe,CAUtD;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,OAAO;;;;;EAQ3D;AAED,MAAM,MAAM,qBAAqB,GAAG,mBAAmB,GAAG;IACxD,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAmCF,4BAA4B;AAC5B,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,qBAE5D;AAED,wBAAgB,2BAA2B,CACzC,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,GACpC,OAAO,CAMT;AAED,MAAM,MAAM,mBAAmB,GAAG,mBAAmB,GAAG;IACtD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,4BAA4B;AAC5B,wBAAsB,2BAA2B,CAC/C,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAsBzB;AAED,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE;IACnD,WAAW,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,aAAa,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,YAAY,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,aAAa,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAChC,QAKA"}
|
|
||||||
77
dist/utils/paintShaderRuntime.js
vendored
77
dist/utils/paintShaderRuntime.js
vendored
@ -1,76 +1 @@
|
|||||||
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
"use strict";var p=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var O=Object.getOwnPropertyNames;var b=Object.prototype.hasOwnProperty;var C=(e,r)=>{for(var n in r)p(e,n,{get:r[n],enumerable:!0})},F=(e,r,n,o)=>{if(r&&typeof r=="object"||typeof r=="function")for(let a of O(r))!b.call(e,a)&&a!==n&&p(e,a,{get:()=>r[a],enumerable:!(o=x(r,a))||o.enumerable});return e};var M=e=>F(p({},"__esModule",{value:!0}),e);var T={};C(T,{PaintShaderLayer:()=>q,buildPaintShaderUniforms:()=>P,createPaintColorMapForPaint:()=>R,getRegionPaintEffect:()=>d,releasePaintShaderTextures:()=>L,renderPaintedImageOffscreen:()=>E});module.exports=M(T);var i=require("react/jsx-runtime"),t=require("@shopify/react-native-skia"),I=require("../shaders/regionPaint.sksl"),l=require("./maskSegmentRuntime"),S=require("./paintColorMapTexture");let s=null;function d(){if(s)return s;const e=t.Skia.RuntimeEffect.Make(I.REGION_PAINT_SKSL);if(!e)throw new Error("regionPaint SkSL compile failed");return s=e,e}function P(e){const r=(0,l.getMaskSegmentRuntimeConfig)().paint;return{colorBaseOpacity:r.colorBaseOpacity,lLightOpacity:r.lLightOpacity,textureOpacity:r.textureOpacity,showOrigin:e?1:0}}function u(e){const{originImage:r,paintColorMap:n,lowFreqImage:o,highFreqImage:a,x:m,y:c,width:f,height:h,showOrigin:y=!1}=e,k=d(),w=P(y),g={fit:"fill",tx:"clamp",ty:"clamp",rect:{x:m,y:c,width:f,height:h}};return(0,i.jsx)(t.Rect,{x:m,y:c,width:f,height:h,children:(0,i.jsxs)(t.Shader,{source:k,uniforms:w,children:[(0,i.jsx)(t.ImageShader,{image:r,...g}),(0,i.jsx)(t.ImageShader,{image:n,...g}),(0,i.jsx)(t.ImageShader,{image:o,...g}),(0,i.jsx)(t.ImageShader,{image:a,...g})]})})}function q(e){return u(e)}function R(e,r,n,o){const m=(0,l.getMaskSegmentRuntimeConfig)().paint.maskFeatherColor??0;return(0,S.buildPaintColorMapImage)(e,r,n,o,m)}async function E(e){const{width:r,height:n,showOrigin:o=!1,...a}=e;if(!a.originImage||!a.paintColorMap||!a.lowFreqImage||!a.highFreqImage)return console.warn("[VIZ-SAVE] renderPaintedImageOffscreen: missing one or more shader textures, will fallback"),null;const m=(0,i.jsx)(t.Group,{children:u({...a,x:0,y:0,width:r,height:n,showOrigin:o})});return(0,t.drawAsImage)(m,{width:r,height:n})}function L(e){e.originImage?.dispose(),e.paintColorMap?.dispose(),e.lowFreqImage?.dispose(),e.highFreqImage?.dispose()}
|
||||||
import { Skia, Shader, ImageShader, Group, Rect, drawAsImage, } from '@shopify/react-native-skia';
|
|
||||||
import { REGION_PAINT_SKSL } from '../shaders/regionPaint.sksl';
|
|
||||||
import { getMaskSegmentRuntimeConfig } from './maskSegmentRuntime';
|
|
||||||
import { buildPaintColorMapImage } from './paintColorMapTexture';
|
|
||||||
let cachedEffect = null;
|
|
||||||
export function getRegionPaintEffect() {
|
|
||||||
if (cachedEffect) {
|
|
||||||
return cachedEffect;
|
|
||||||
}
|
|
||||||
const effect = Skia.RuntimeEffect.Make(REGION_PAINT_SKSL);
|
|
||||||
if (!effect) {
|
|
||||||
throw new Error('regionPaint SkSL compile failed');
|
|
||||||
}
|
|
||||||
cachedEffect = effect;
|
|
||||||
return effect;
|
|
||||||
}
|
|
||||||
export function buildPaintShaderUniforms(showOrigin) {
|
|
||||||
const paintCfg = getMaskSegmentRuntimeConfig().paint;
|
|
||||||
return {
|
|
||||||
colorBaseOpacity: paintCfg.colorBaseOpacity,
|
|
||||||
lLightOpacity: paintCfg.lLightOpacity,
|
|
||||||
textureOpacity: paintCfg.textureOpacity,
|
|
||||||
showOrigin: showOrigin ? 1 : 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
function createPaintShaderTree(props) {
|
|
||||||
const { originImage, paintColorMap, lowFreqImage, highFreqImage, x, y, width, height, showOrigin = false, } = props;
|
|
||||||
const effect = getRegionPaintEffect();
|
|
||||||
const uniforms = buildPaintShaderUniforms(showOrigin);
|
|
||||||
const imageShaderProps = {
|
|
||||||
fit: 'fill',
|
|
||||||
tx: 'clamp',
|
|
||||||
ty: 'clamp',
|
|
||||||
rect: { x, y, width, height },
|
|
||||||
};
|
|
||||||
return (_jsx(Rect, { x: x, y: y, width: width, height: height, children: _jsxs(Shader, { source: effect, uniforms: uniforms, children: [_jsx(ImageShader, { image: originImage, ...imageShaderProps }), _jsx(ImageShader, { image: paintColorMap, ...imageShaderProps }), _jsx(ImageShader, { image: lowFreqImage, ...imageShaderProps }), _jsx(ImageShader, { image: highFreqImage, ...imageShaderProps })] }) }));
|
|
||||||
}
|
|
||||||
/** Canvas 内全屏上色 Shader 层 */
|
|
||||||
export function PaintShaderLayer(props) {
|
|
||||||
return createPaintShaderTree(props);
|
|
||||||
}
|
|
||||||
export function createPaintColorMapForPaint(pickBuffer, cols, rows, paintedRegions) {
|
|
||||||
const paintCfg = getMaskSegmentRuntimeConfig().paint;
|
|
||||||
// Prefer color feather for the recolor application alpha (controls soft edge blend for both base color and texture overlay).
|
|
||||||
// texture feather kept in config for future differentiation (e.g. high-freq strength roll-off).
|
|
||||||
const feather = paintCfg.maskFeatherColor ?? 0;
|
|
||||||
return buildPaintColorMapImage(pickBuffer, cols, rows, paintedRegions, feather);
|
|
||||||
}
|
|
||||||
/** 离屏渲染与预览同源的 shader 合成图 */
|
|
||||||
export async function renderPaintedImageOffscreen(input) {
|
|
||||||
const { width, height, showOrigin = false, ...textures } = input;
|
|
||||||
if (!textures.originImage || !textures.paintColorMap || !textures.lowFreqImage || !textures.highFreqImage) {
|
|
||||||
console.warn('[VIZ-SAVE] renderPaintedImageOffscreen: missing one or more shader textures, will fallback');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// drawAsImage expects the *scene content*, not a <Canvas> host component.
|
|
||||||
// Passing a <Canvas> could cause the internal player to construct paints with
|
|
||||||
// undefined values (leading to k.charAt errors in color/enum code).
|
|
||||||
const element = (_jsx(Group, { children: createPaintShaderTree({
|
|
||||||
...textures,
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
showOrigin,
|
|
||||||
}) }));
|
|
||||||
return drawAsImage(element, { width, height });
|
|
||||||
}
|
|
||||||
export function releasePaintShaderTextures(textures) {
|
|
||||||
textures.originImage?.dispose();
|
|
||||||
textures.paintColorMap?.dispose();
|
|
||||||
textures.lowFreqImage?.dispose();
|
|
||||||
textures.highFreqImage?.dispose();
|
|
||||||
}
|
|
||||||
//# sourceMappingURL=paintShaderRuntime.js.map
|
|
||||||
|
|||||||
1
dist/utils/paintShaderRuntime.js.map
vendored
1
dist/utils/paintShaderRuntime.js.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"paintShaderRuntime.js","sourceRoot":"","sources":["../../src/utils/paintShaderRuntime.tsx"],"names":[],"mappings":"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"}
|
|
||||||
3
dist/utils/pickMapTexture.d.ts
vendored
3
dist/utils/pickMapTexture.d.ts
vendored
@ -1,4 +1,3 @@
|
|||||||
import { type SkImage } from '@shopify/react-native-skia';
|
import { type SkImage } from '@shopify/react-native-skia';
|
||||||
/** pickMap 像素值 regionId+1 → RGBA 纹理(R 通道存查表码) */
|
/** pickMap pixel value regionId+1 → RGBA texture (R channel stores lookup code) */
|
||||||
export declare function pickBufferToSkImage(pickBuffer: Uint8Array, cols: number, rows: number): SkImage | null;
|
export declare function pickBufferToSkImage(pickBuffer: Uint8Array, cols: number, rows: number): SkImage | null;
|
||||||
//# sourceMappingURL=pickMapTexture.d.ts.map
|
|
||||||
1
dist/utils/pickMapTexture.d.ts.map
vendored
1
dist/utils/pickMapTexture.d.ts.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"pickMapTexture.d.ts","sourceRoot":"","sources":["../../src/utils/pickMapTexture.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,OAAO,EACb,MAAM,4BAA4B,CAAC;AAEpC,iDAAiD;AACjD,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,OAAO,GAAG,IAAI,CAwBhB"}
|
|
||||||
25
dist/utils/pickMapTexture.js
vendored
25
dist/utils/pickMapTexture.js
vendored
@ -1,24 +1 @@
|
|||||||
import { Skia, AlphaType, ColorType, } from '@shopify/react-native-skia';
|
"use strict";var i=Object.defineProperty;var l=Object.getOwnPropertyDescriptor;var y=Object.getOwnPropertyNames;var h=Object.prototype.hasOwnProperty;var u=(t,e)=>{for(var a in e)i(t,a,{get:e[a],enumerable:!0})},I=(t,e,a,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of y(e))!h.call(t,r)&&r!==a&&i(t,r,{get:()=>e[r],enumerable:!(n=l(e,r))||n.enumerable});return t};var T=t=>I(i({},"__esModule",{value:!0}),t);var A={};u(A,{pickBufferToSkImage:()=>k});module.exports=T(A);var o=require("@shopify/react-native-skia");function k(t,e,a){if(t.length!==e*a)return null;const n=new Uint8Array(e*a*4);for(let m=0;m<t.length;m++){const g=m*4,p=t[m];n[g]=p,n[g+1]=p,n[g+2]=p,n[g+3]=255}const r=o.Skia.Data.fromBytes(n);return o.Skia.Image.MakeImage({width:e,height:a,alphaType:o.AlphaType.Opaque,colorType:o.ColorType.RGBA_8888},r,e*4)}
|
||||||
/** pickMap 像素值 regionId+1 → RGBA 纹理(R 通道存查表码) */
|
|
||||||
export function pickBufferToSkImage(pickBuffer, cols, rows) {
|
|
||||||
if (pickBuffer.length !== cols * rows) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const rgba = new Uint8Array(cols * rows * 4);
|
|
||||||
for (let i = 0; i < pickBuffer.length; i++) {
|
|
||||||
const o = i * 4;
|
|
||||||
const code = pickBuffer[i];
|
|
||||||
rgba[o] = code;
|
|
||||||
rgba[o + 1] = code;
|
|
||||||
rgba[o + 2] = code;
|
|
||||||
rgba[o + 3] = 255;
|
|
||||||
}
|
|
||||||
const data = Skia.Data.fromBytes(rgba);
|
|
||||||
return Skia.Image.MakeImage({
|
|
||||||
width: cols,
|
|
||||||
height: rows,
|
|
||||||
alphaType: AlphaType.Opaque,
|
|
||||||
colorType: ColorType.RGBA_8888,
|
|
||||||
}, data, cols * 4);
|
|
||||||
}
|
|
||||||
//# sourceMappingURL=pickMapTexture.js.map
|
|
||||||
|
|||||||
1
dist/utils/pickMapTexture.js.map
vendored
1
dist/utils/pickMapTexture.js.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"pickMapTexture.js","sourceRoot":"","sources":["../../src/utils/pickMapTexture.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,SAAS,EACT,SAAS,GAEV,MAAM,4BAA4B,CAAC;AAEpC,iDAAiD;AACjD,MAAM,UAAU,mBAAmB,CACjC,UAAsB,EACtB,IAAY,EACZ,IAAY;IAEZ,IAAI,UAAU,CAAC,MAAM,KAAK,IAAI,GAAG,IAAI,EAAE;QACrC,OAAO,IAAI,CAAC;KACb;IACD,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;IAC7C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QAC1C,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAChB,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QAC3B,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;QACf,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;KACnB;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACvC,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,CACzB;QACE,KAAK,EAAE,IAAI;QACX,MAAM,EAAE,IAAI;QACZ,SAAS,EAAE,SAAS,CAAC,MAAM;QAC3B,SAAS,EAAE,SAAS,CAAC,SAAS;KAC/B,EACD,IAAI,EACJ,IAAI,GAAG,CAAC,CACT,CAAC;AACJ,CAAC"}
|
|
||||||
21
dist/utils/pngImage.d.ts
vendored
21
dist/utils/pngImage.d.ts
vendored
@ -1,19 +1,19 @@
|
|||||||
import { type Mat } from 'react-native-fast-opencv';
|
import { type Mat } from 'react-native-fast-opencv';
|
||||||
export declare const PNG_EXT = ".png";
|
export declare const PNG_EXT = ".png";
|
||||||
/** PNG 压缩级别 0 = 无损 */
|
/** PNG compression level 0 = lossless */
|
||||||
export declare const PNG_COMPRESSION = 0;
|
export declare const PNG_COMPRESSION = 0;
|
||||||
export declare function normalizePath(path: string): string;
|
export declare function normalizePath(path: string): string;
|
||||||
/** Skia useImage 需要 URI;OpenCV / RNFS 使用裸路径 */
|
/** Skia useImage requires a URI; OpenCV / RNFS use bare paths */
|
||||||
export declare function toSkiaUri(path: string | null | undefined): string | null;
|
export declare function toSkiaUri(path: string | null | undefined): string | null;
|
||||||
export declare function toPngFileName(name: string): string;
|
export declare function toPngFileName(name: string): string;
|
||||||
export declare function isPngPath(path: string): boolean;
|
export declare function isPngPath(path: string): boolean;
|
||||||
/** 按文件元数据生成指纹,避免整文件读入(原 base64 全量哈希在 1.5MB 图上极慢) */
|
/** Generate fingerprint from file metadata to avoid full-file read (original base64 full hash was extremely slow on 1.5MB images) */
|
||||||
export declare function fileContentFingerprint(path: string): Promise<string>;
|
export declare function fileContentFingerprint(path: string): Promise<string>;
|
||||||
/** 任意图片路径 → 缓存目录下的 PNG 文件路径(已是 PNG 则复制,否则解码转存) */
|
/** Any image path → PNG file path under cache dir (copy if already PNG, otherwise decode and save) */
|
||||||
export declare function ensurePngFile(sourcePath: string, cacheFileName: string): Promise<string>;
|
export declare function ensurePngFile(sourcePath: string, cacheFileName: string): Promise<string>;
|
||||||
/** 根据源路径生成稳定 PNG 缓存名 */
|
/** Generate a stable PNG cache name from the source path */
|
||||||
export declare function pngCacheName(sourcePath: string, prefix: string): string;
|
export declare function pngCacheName(sourcePath: string, prefix: string): string;
|
||||||
/** 清理分割/OpenCV 派生缓存,保留原图与掩码源文件 */
|
/** Clean up segmentation/OpenCV derived cache, keep original image and mask source files */
|
||||||
export declare function clearDerivedImageCache(): Promise<number>;
|
export declare function clearDerivedImageCache(): Promise<number>;
|
||||||
type PngHeader = {
|
type PngHeader = {
|
||||||
width: number;
|
width: number;
|
||||||
@ -21,15 +21,15 @@ type PngHeader = {
|
|||||||
bitDepth: number;
|
bitDepth: number;
|
||||||
colorType: number;
|
colorType: number;
|
||||||
};
|
};
|
||||||
/** 从 base64 解析 PNG IHDR(不依赖 OpenCV),用于 16-bit Mat toJSValue 崩溃时的降级通路 */
|
/** Parse PNG IHDR from base64 (without OpenCV), used as fallback when 16-bit Mat toJSValue crashes */
|
||||||
export declare function readPngHeaderFromBase64(base64: string): PngHeader;
|
export declare function readPngHeaderFromBase64(base64: string): PngHeader;
|
||||||
/** 16-bit / float Mat → 8-bit(fast-opencv 在 patch 前 toJSValue 会截断高位)。
|
/** 16-bit / float Mat → 8-bit (fast-opencv toJSValue truncates high bits before patch).
|
||||||
* Semantic mask PNGs use 16-bit RGB where the label (0-255) is stored as value×257;
|
* Semantic mask PNGs use 16-bit RGB where the label (0-255) is stored as value×257;
|
||||||
* use 1/257 scaling for 3+ channel 16-bit cases so the resulting 8-bit channels contain
|
* use 1/257 scaling for 3+ channel 16-bit cases so the resulting 8-bit channels contain
|
||||||
* the original semantic label values (consistent with the native ensure8U patch).
|
* the original semantic label values (consistent with the native ensure8U patch).
|
||||||
*
|
*
|
||||||
* pngHeader 可选:当 toJSValue 因 16-bit Mat 崩溃时,用 PNG 文件头信息做降级转换,
|
* pngHeader (optional): when toJSValue crashes due to 16-bit Mat, use PNG file header info
|
||||||
* 绕过原生 toJSValue 调用。
|
* for fallback conversion, bypassing native toJSValue call.
|
||||||
*/
|
*/
|
||||||
export declare function ensureMat8U(srcMat: Mat, pngHeader?: PngHeader): {
|
export declare function ensureMat8U(srcMat: Mat, pngHeader?: PngHeader): {
|
||||||
mat: Mat;
|
mat: Mat;
|
||||||
@ -46,4 +46,3 @@ export declare function pngContentCacheKey(path: string): Promise<string>;
|
|||||||
export declare function readPngBgrBuffer(path: string): Promise<PngBgrBuffer>;
|
export declare function readPngBgrBuffer(path: string): Promise<PngBgrBuffer>;
|
||||||
export declare function resizeBgrBuffer(buffer: Uint8Array, srcCols: number, srcRows: number, dstCols: number, dstRows: number): Uint8Array;
|
export declare function resizeBgrBuffer(buffer: Uint8Array, srcCols: number, srcRows: number, dstCols: number, dstRows: number): Uint8Array;
|
||||||
export {};
|
export {};
|
||||||
//# sourceMappingURL=pngImage.d.ts.map
|
|
||||||
1
dist/utils/pngImage.d.ts.map
vendored
1
dist/utils/pngImage.d.ts.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"pngImage.d.ts","sourceRoot":"","sources":["../../src/utils/pngImage.ts"],"names":[],"mappings":"AAEA,OAAO,EAKL,KAAK,GAAG,EACT,MAAM,0BAA0B,CAAC;AAElC,eAAO,MAAM,OAAO,SAAS,CAAC;AAC9B,sBAAsB;AACtB,eAAO,MAAM,eAAe,IAAI,CAAC;AAEjC,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,+CAA+C;AAC/C,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAcxE;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGlD;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE/C;AAcD,oDAAoD;AACpD,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAI1E;AAgCD,kDAAkD;AAClD,wBAAsB,aAAa,CACjC,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC,CA6BjB;AAED,wBAAwB;AACxB,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEvE;AAUD,kCAAkC;AAClC,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAgB9D;AAYD,KAAK,SAAS,GAAG;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAkBF,wEAAwE;AACxE,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAkCjE;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,GAAG,EACX,SAAS,CAAC,EAAE,SAAS,GACpB;IACD,GAAG,EAAE,GAAG,CAAC;IACT,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B,CA2EA;AAsKD,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAKF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAKxD;AAED,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,MAAM,EAAE,GACd,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGtE;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAoD1E;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,UAAU,CAkBZ"}
|
|
||||||
439
dist/utils/pngImage.js
vendored
439
dist/utils/pngImage.js
vendored
File diff suppressed because one or more lines are too long
1
dist/utils/pngImage.js.map
vendored
1
dist/utils/pngImage.js.map
vendored
File diff suppressed because one or more lines are too long
3
dist/utils/resolveAssetPath.d.ts
vendored
3
dist/utils/resolveAssetPath.d.ts
vendored
@ -1,3 +1,2 @@
|
|||||||
/** 将 require() 资源解析为 PNG 本地路径(OpenCV / RNFS 可读) */
|
/** Resolve a require() asset to a PNG local path (readable by OpenCV / RNFS) */
|
||||||
export declare function resolveAssetPath(assetModule: number, cacheFileName: string): Promise<string>;
|
export declare function resolveAssetPath(assetModule: number, cacheFileName: string): Promise<string>;
|
||||||
//# sourceMappingURL=resolveAssetPath.d.ts.map
|
|
||||||
1
dist/utils/resolveAssetPath.d.ts.map
vendored
1
dist/utils/resolveAssetPath.d.ts.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"resolveAssetPath.d.ts","sourceRoot":"","sources":["../../src/utils/resolveAssetPath.ts"],"names":[],"mappings":"AAIA,mDAAmD;AACnD,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC,CAuDjB"}
|
|
||||||
57
dist/utils/resolveAssetPath.js
vendored
57
dist/utils/resolveAssetPath.js
vendored
@ -1,56 +1 @@
|
|||||||
import { Image, Platform } from 'react-native';
|
"use strict";var u=Object.create;var f=Object.defineProperty;var h=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var p=Object.getPrototypeOf,d=Object.prototype.hasOwnProperty;var y=(e,s)=>{for(var t in s)f(e,t,{get:s[t],enumerable:!0})},m=(e,s,t,c)=>{if(s&&typeof s=="object"||typeof s=="function")for(let r of w(s))!d.call(e,r)&&r!==t&&f(e,r,{get:()=>s[r],enumerable:!(c=h(s,r))||c.enumerable});return e};var P=(e,s,t)=>(t=e!=null?u(p(e)):{},m(s||!e||!e.__esModule?f(t,"default",{value:e,enumerable:!0}):t,e)),g=e=>m(f({},"__esModule",{value:!0}),e);var $={};y($,{resolveAssetPath:()=>D});module.exports=g($);var l=require("react-native"),i=P(require("react-native-fs")),a=require("./pngImage");async function D(e,s){const t=(0,a.toPngFileName)(s),c=l.Image.resolveAssetSource(e);if(!c?.uri)throw new Error("Cannot resolve image resource");const{uri:r}=c;if(r.startsWith("file://"))return(0,a.ensurePngFile)(r,t);if(l.Platform.OS==="ios"&&r.startsWith("/"))return(0,a.ensurePngFile)(r,t);if(l.Platform.OS==="ios"&&r.startsWith("/"))return(0,a.ensurePngFile)(r,t);if(r.startsWith("http://")||r.startsWith("https://")){const o=`${i.default.CachesDirectoryPath}/tmp_${Date.now()}_${t}`,{statusCode:n}=await i.default.downloadFile({fromUrl:r,toFile:o}).promise;if(n!==200)throw new Error(`Download resource failed: ${t}`);try{return await(0,a.ensurePngFile)(o,t)}finally{await i.default.exists(o)&&await i.default.unlink(o)}}if(l.Platform.OS==="android"){const o=r.replace("asset:/","").replace("file:///android_asset/",""),n=`${i.default.CachesDirectoryPath}/tmp_${Date.now()}_${t}`;await i.default.copyFileAssets(o,n);try{return await(0,a.ensurePngFile)(n,t)}finally{await i.default.exists(n)&&await i.default.unlink(n)}}return(0,a.ensurePngFile)(r,t)}
|
||||||
import RNFS from 'react-native-fs';
|
|
||||||
import { ensurePngFile, toPngFileName } from './pngImage';
|
|
||||||
/** 将 require() 资源解析为 PNG 本地路径(OpenCV / RNFS 可读) */
|
|
||||||
export async function resolveAssetPath(assetModule, cacheFileName) {
|
|
||||||
const pngCacheName = toPngFileName(cacheFileName);
|
|
||||||
const source = Image.resolveAssetSource(assetModule);
|
|
||||||
if (!source?.uri) {
|
|
||||||
throw new Error('Cannot resolve image resource');
|
|
||||||
}
|
|
||||||
const { uri } = source;
|
|
||||||
if (uri.startsWith('file://')) {
|
|
||||||
return ensurePngFile(uri, pngCacheName);
|
|
||||||
}
|
|
||||||
if (Platform.OS === 'ios' && uri.startsWith('/')) {
|
|
||||||
return ensurePngFile(uri, pngCacheName);
|
|
||||||
}
|
|
||||||
if (Platform.OS === 'ios' && uri.startsWith('/')) {
|
|
||||||
return ensurePngFile(uri, pngCacheName);
|
|
||||||
}
|
|
||||||
if (uri.startsWith('http://') || uri.startsWith('https://')) {
|
|
||||||
const tmpDest = `${RNFS.CachesDirectoryPath}/tmp_${Date.now()}_${pngCacheName}`;
|
|
||||||
const { statusCode } = await RNFS.downloadFile({
|
|
||||||
fromUrl: uri,
|
|
||||||
toFile: tmpDest,
|
|
||||||
}).promise;
|
|
||||||
if (statusCode !== 200) {
|
|
||||||
throw new Error(`Download resource failed: ${pngCacheName}`);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return await ensurePngFile(tmpDest, pngCacheName);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
if (await RNFS.exists(tmpDest)) {
|
|
||||||
await RNFS.unlink(tmpDest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Platform.OS === 'android') {
|
|
||||||
const assetPath = uri
|
|
||||||
.replace('asset:/', '')
|
|
||||||
.replace('file:///android_asset/', '');
|
|
||||||
const tmpDest = `${RNFS.CachesDirectoryPath}/tmp_${Date.now()}_${pngCacheName}`;
|
|
||||||
await RNFS.copyFileAssets(assetPath, tmpDest);
|
|
||||||
try {
|
|
||||||
return await ensurePngFile(tmpDest, pngCacheName);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
if (await RNFS.exists(tmpDest)) {
|
|
||||||
await RNFS.unlink(tmpDest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ensurePngFile(uri, pngCacheName);
|
|
||||||
}
|
|
||||||
//# sourceMappingURL=resolveAssetPath.js.map
|
|
||||||
|
|||||||
1
dist/utils/resolveAssetPath.js.map
vendored
1
dist/utils/resolveAssetPath.js.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"resolveAssetPath.js","sourceRoot":"","sources":["../../src/utils/resolveAssetPath.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,IAAI,MAAM,iBAAiB,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE1D,mDAAmD;AACnD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,WAAmB,EACnB,aAAqB;IAErB,MAAM,YAAY,GAAG,aAAa,CAAC,aAAa,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,KAAK,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC;IACrD,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE;QAChB,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;KAClD;IAED,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC;IAEvB,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE;QAC7B,OAAO,aAAa,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;KACzC;IAED,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE;QAChD,OAAO,aAAa,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;KACzC;IAED,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE;QAChD,OAAO,aAAa,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;KACzC;IAED,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE;QAC3D,MAAM,OAAO,GAAG,GAAG,IAAI,CAAC,mBAAmB,QAAQ,IAAI,CAAC,GAAG,EAAE,IAAI,YAAY,EAAE,CAAC;QAChF,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC;YAC7C,OAAO,EAAE,GAAG;YACZ,MAAM,EAAE,OAAO;SAChB,CAAC,CAAC,OAAO,CAAC;QACX,IAAI,UAAU,KAAK,GAAG,EAAE;YACtB,MAAM,IAAI,KAAK,CAAC,6BAA6B,YAAY,EAAE,CAAC,CAAC;SAC9D;QACD,IAAI;YACF,OAAO,MAAM,aAAa,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;SACnD;gBAAS;YACR,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE;gBAC9B,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;aAC5B;SACF;KACF;IAED,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE;QAC7B,MAAM,SAAS,GAAG,GAAG;aAClB,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;aACtB,OAAO,CAAC,wBAAwB,EAAE,EAAE,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,GAAG,IAAI,CAAC,mBAAmB,QAAQ,IAAI,CAAC,GAAG,EAAE,IAAI,YAAY,EAAE,CAAC;QAChF,MAAM,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAC9C,IAAI;YACF,OAAO,MAAM,aAAa,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;SACnD;gBAAS;YACR,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE;gBAC9B,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;aAC5B;SACF;KACF;IAED,OAAO,aAAa,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;AAC1C,CAAC"}
|
|
||||||
3
dist/utils/resolveImageUrl.d.ts
vendored
3
dist/utils/resolveImageUrl.d.ts
vendored
@ -1,4 +1,3 @@
|
|||||||
export declare function hashUrl(url: string): string;
|
export declare function hashUrl(url: string): string;
|
||||||
/** 将本地路径或远程 URL 解析为 OpenCV / RNFS 可读的 PNG 本地路径 */
|
/** Resolve a local path or remote URL to a PNG local path readable by OpenCV / RNFS */
|
||||||
export declare function resolveImageUrl(source: string, cacheFileName?: string): Promise<string>;
|
export declare function resolveImageUrl(source: string, cacheFileName?: string): Promise<string>;
|
||||||
//# sourceMappingURL=resolveImageUrl.d.ts.map
|
|
||||||
1
dist/utils/resolveImageUrl.d.ts.map
vendored
1
dist/utils/resolveImageUrl.d.ts.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"resolveImageUrl.d.ts","sourceRoot":"","sources":["../../src/utils/resolveImageUrl.ts"],"names":[],"mappings":"AAIA,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAM3C;AAED,kDAAkD;AAClD,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,CAAC,CAiEjB"}
|
|
||||||
71
dist/utils/resolveImageUrl.js
vendored
71
dist/utils/resolveImageUrl.js
vendored
@ -1,70 +1 @@
|
|||||||
import { Platform } from 'react-native';
|
"use strict";var p=Object.create;var m=Object.defineProperty;var w=Object.getOwnPropertyDescriptor;var g=Object.getOwnPropertyNames;var u=Object.getPrototypeOf,d=Object.prototype.hasOwnProperty;var P=(i,r)=>{for(var t in r)m(i,t,{get:r[t],enumerable:!0})},h=(i,r,t,s)=>{if(r&&typeof r=="object"||typeof r=="function")for(let a of g(r))!d.call(i,a)&&a!==t&&m(i,a,{get:()=>r[a],enumerable:!(s=w(r,a))||s.enumerable});return i};var y=(i,r,t)=>(t=i!=null?p(u(i)):{},h(r||!i||!i.__esModule?m(t,"default",{value:i,enumerable:!0}):t,i)),$=i=>h(m({},"__esModule",{value:!0}),i);var x={};P(x,{hashUrl:()=>c,resolveImageUrl:()=>D});module.exports=$(x);var f=require("react-native"),n=y(require("react-native-fs")),e=require("./pngImage");function c(i){let r=0;for(let t=0;t<i.length;t++)r=r*31+i.charCodeAt(t)|0;return Math.abs(r).toString(36)}async function D(i,r){const t=i.trim();if(!t)throw new Error("Image URL is empty");const s=(0,e.toPngFileName)(r??`img_${c(t)}.png`);if(t.startsWith("http://")||t.startsWith("https://")){const o=`${n.default.CachesDirectoryPath}/tmp_${Date.now()}_${s}`,{statusCode:l}=await n.default.downloadFile({fromUrl:t,toFile:o}).promise;if(l!==200)throw new Error(`Download image failed: ${t}`);try{return await(0,e.ensurePngFile)(o,s)}finally{await n.default.exists(o)&&await n.default.unlink(o)}}const a=(0,e.normalizePath)(t);if(await n.default.exists(a)&&(0,e.isPngPath)(a))return a;if(a.startsWith("file://"))return(0,e.ensurePngFile)(a,s);if(f.Platform.OS==="ios"&&a.startsWith("/"))return(0,e.ensurePngFile)(a,s);if(await n.default.exists(a))return(0,e.ensurePngFile)(a,s);if(f.Platform.OS==="android"){const o=t.replace(/^asset:\/?\/?/,"").replace(/^file:\/\/\/android_asset\//,"");if(o!==t){const l=`${n.default.CachesDirectoryPath}/tmp_${Date.now()}_${s}`;await n.default.copyFileAssets(o,l);try{return await(0,e.ensurePngFile)(l,s)}finally{await n.default.exists(l)&&await n.default.unlink(l)}}}return(0,e.ensurePngFile)(t,s)}
|
||||||
import RNFS from 'react-native-fs';
|
|
||||||
import { ensurePngFile, isPngPath, normalizePath, toPngFileName } from './pngImage';
|
|
||||||
export function hashUrl(url) {
|
|
||||||
let hash = 0;
|
|
||||||
for (let i = 0; i < url.length; i++) {
|
|
||||||
hash = (hash * 31 + url.charCodeAt(i)) | 0;
|
|
||||||
}
|
|
||||||
return Math.abs(hash).toString(36);
|
|
||||||
}
|
|
||||||
/** 将本地路径或远程 URL 解析为 OpenCV / RNFS 可读的 PNG 本地路径 */
|
|
||||||
export async function resolveImageUrl(source, cacheFileName) {
|
|
||||||
const trimmed = source.trim();
|
|
||||||
if (!trimmed) {
|
|
||||||
throw new Error('Image URL is empty');
|
|
||||||
}
|
|
||||||
const pngCacheName = toPngFileName(cacheFileName ?? `img_${hashUrl(trimmed)}.png`);
|
|
||||||
if (trimmed.startsWith('http://') || trimmed.startsWith('https://')) {
|
|
||||||
const tmpDest = `${RNFS.CachesDirectoryPath}/tmp_${Date.now()}_${pngCacheName}`;
|
|
||||||
const { statusCode } = await RNFS.downloadFile({
|
|
||||||
fromUrl: trimmed,
|
|
||||||
toFile: tmpDest,
|
|
||||||
}).promise;
|
|
||||||
if (statusCode !== 200) {
|
|
||||||
throw new Error(`Download image failed: ${trimmed}`);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return await ensurePngFile(tmpDest, pngCacheName);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
if (await RNFS.exists(tmpDest)) {
|
|
||||||
await RNFS.unlink(tmpDest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const normalized = normalizePath(trimmed);
|
|
||||||
if (await RNFS.exists(normalized) && isPngPath(normalized)) {
|
|
||||||
return normalized;
|
|
||||||
}
|
|
||||||
if (normalized.startsWith('file://')) {
|
|
||||||
return ensurePngFile(normalized, pngCacheName);
|
|
||||||
}
|
|
||||||
if (Platform.OS === 'ios' && normalized.startsWith('/')) {
|
|
||||||
return ensurePngFile(normalized, pngCacheName);
|
|
||||||
}
|
|
||||||
if (await RNFS.exists(normalized)) {
|
|
||||||
return ensurePngFile(normalized, pngCacheName);
|
|
||||||
}
|
|
||||||
// Android bundled assets: resolveAssetSource may yield asset:/ or
|
|
||||||
// file:///android_asset/ URIs that RNFS cannot read directly.
|
|
||||||
if (Platform.OS === 'android') {
|
|
||||||
const assetPath = trimmed
|
|
||||||
.replace(/^asset:\/?\/?/, '')
|
|
||||||
.replace(/^file:\/\/\/android_asset\//, '');
|
|
||||||
if (assetPath !== trimmed) {
|
|
||||||
const tmpDest = `${RNFS.CachesDirectoryPath}/tmp_${Date.now()}_${pngCacheName}`;
|
|
||||||
await RNFS.copyFileAssets(assetPath, tmpDest);
|
|
||||||
try {
|
|
||||||
return await ensurePngFile(tmpDest, pngCacheName);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
if (await RNFS.exists(tmpDest)) {
|
|
||||||
await RNFS.unlink(tmpDest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ensurePngFile(trimmed, pngCacheName);
|
|
||||||
}
|
|
||||||
//# sourceMappingURL=resolveImageUrl.js.map
|
|
||||||
|
|||||||
1
dist/utils/resolveImageUrl.js.map
vendored
1
dist/utils/resolveImageUrl.js.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"resolveImageUrl.js","sourceRoot":"","sources":["../../src/utils/resolveImageUrl.ts"],"names":[],"mappings":"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"}
|
|
||||||
3
dist/utils/skiaImage.d.ts
vendored
3
dist/utils/skiaImage.d.ts
vendored
@ -1,4 +1,3 @@
|
|||||||
import { type SkImage } from '@shopify/react-native-skia';
|
import { type SkImage } from '@shopify/react-native-skia';
|
||||||
/** 连续 RGBA 缓冲 → Skia 图像(高低频 / 工作分辨率原图内存直传,避免 PNG 落盘) */
|
/** Continuous RGBA buffer → Skia image (direct memory transfer for freq layers / work buffer origin, avoids PNG roundtrip) */
|
||||||
export declare function rgbaBufferToSkiaImage(buffer: Uint8Array, cols: number, rows: number): SkImage | null;
|
export declare function rgbaBufferToSkiaImage(buffer: Uint8Array, cols: number, rows: number): SkImage | null;
|
||||||
//# sourceMappingURL=skiaImage.d.ts.map
|
|
||||||
1
dist/utils/skiaImage.d.ts.map
vendored
1
dist/utils/skiaImage.d.ts.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"skiaImage.d.ts","sourceRoot":"","sources":["../../src/utils/skiaImage.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,OAAO,EACb,MAAM,4BAA4B,CAAC;AAEpC,wDAAwD;AACxD,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,OAAO,GAAG,IAAI,CAYhB"}
|
|
||||||
13
dist/utils/skiaImage.js
vendored
13
dist/utils/skiaImage.js
vendored
@ -1,12 +1 @@
|
|||||||
import { Skia, AlphaType, ColorType, } from '@shopify/react-native-skia';
|
"use strict";var p=Object.defineProperty;var n=Object.getOwnPropertyDescriptor;var g=Object.getOwnPropertyNames;var i=Object.prototype.hasOwnProperty;var u=(a,e)=>{for(var t in e)p(a,t,{get:e[t],enumerable:!0})},y=(a,e,t,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let m of g(e))!i.call(a,m)&&m!==t&&p(a,m,{get:()=>e[m],enumerable:!(o=n(e,m))||o.enumerable});return a};var f=a=>y(p({},"__esModule",{value:!0}),a);var k={};u(k,{rgbaBufferToSkiaImage:()=>h});module.exports=f(k);var r=require("@shopify/react-native-skia");function h(a,e,t){const o=r.Skia.Data.fromBytes(a);return r.Skia.Image.MakeImage({width:e,height:t,alphaType:r.AlphaType.Opaque,colorType:r.ColorType.RGBA_8888},o,e*4)}
|
||||||
/** 连续 RGBA 缓冲 → Skia 图像(高低频 / 工作分辨率原图内存直传,避免 PNG 落盘) */
|
|
||||||
export function rgbaBufferToSkiaImage(buffer, cols, rows) {
|
|
||||||
const data = Skia.Data.fromBytes(buffer);
|
|
||||||
return Skia.Image.MakeImage({
|
|
||||||
width: cols,
|
|
||||||
height: rows,
|
|
||||||
alphaType: AlphaType.Opaque,
|
|
||||||
colorType: ColorType.RGBA_8888,
|
|
||||||
}, data, cols * 4);
|
|
||||||
}
|
|
||||||
//# sourceMappingURL=skiaImage.js.map
|
|
||||||
|
|||||||
1
dist/utils/skiaImage.js.map
vendored
1
dist/utils/skiaImage.js.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"skiaImage.js","sourceRoot":"","sources":["../../src/utils/skiaImage.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,SAAS,EACT,SAAS,GAEV,MAAM,4BAA4B,CAAC;AAEpC,wDAAwD;AACxD,MAAM,UAAU,qBAAqB,CACnC,MAAkB,EAClB,IAAY,EACZ,IAAY;IAEZ,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACzC,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,CACzB;QACE,KAAK,EAAE,IAAI;QACX,MAAM,EAAE,IAAI;QACZ,SAAS,EAAE,SAAS,CAAC,MAAM;QAC3B,SAAS,EAAE,SAAS,CAAC,SAAS;KAC/B,EACD,IAAI,EACJ,IAAI,GAAG,CAAC,CACT,CAAC;AACJ,CAAC"}
|
|
||||||
5
dist/utils/wallTextureSplit.d.ts
vendored
5
dist/utils/wallTextureSplit.d.ts
vendored
@ -1,9 +1,8 @@
|
|||||||
import type { SegmentMaskResult } from './maskSegmentation';
|
import type { SegmentMaskResult } from './maskSegmentation';
|
||||||
/** 非墙像素在 wallSubLabels 中的占位值 */
|
/** Placeholder value for non-wall pixels in wallSubLabels */
|
||||||
export declare const WALL_SUB_LABEL_NONE = 255;
|
export declare const WALL_SUB_LABEL_NONE = 255;
|
||||||
/**
|
/**
|
||||||
* 在语义分割完成后,将 wall 区域按原图纹理特征细分为 wall-1、wall-2…
|
* After semantic segmentation, subdivide the wall region into wall-1, wall-2… by source image texture features
|
||||||
*/
|
*/
|
||||||
export declare function splitWallRegionsByTexture(result: SegmentMaskResult, originBgr: Uint8Array, cols: number, rows: number, minArea: number): SegmentMaskResult;
|
export declare function splitWallRegionsByTexture(result: SegmentMaskResult, originBgr: Uint8Array, cols: number, rows: number, minArea: number): SegmentMaskResult;
|
||||||
export declare function isWallSubRegionName(name: string): boolean;
|
export declare function isWallSubRegionName(name: string): boolean;
|
||||||
//# sourceMappingURL=wallTextureSplit.d.ts.map
|
|
||||||
1
dist/utils/wallTextureSplit.d.ts.map
vendored
1
dist/utils/wallTextureSplit.d.ts.map
vendored
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"wallTextureSplit.d.ts","sourceRoot":"","sources":["../../src/utils/wallTextureSplit.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAiB,MAAM,oBAAoB,CAAC;AAG3E,gCAAgC;AAChC,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAofvC;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,iBAAiB,EACzB,SAAS,EAAE,UAAU,EACrB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GACd,iBAAiB,CAqJnB;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzD"}
|
|
||||||
480
dist/utils/wallTextureSplit.js
vendored
480
dist/utils/wallTextureSplit.js
vendored
File diff suppressed because one or more lines are too long
1
dist/utils/wallTextureSplit.js.map
vendored
1
dist/utils/wallTextureSplit.js.map
vendored
File diff suppressed because one or more lines are too long
148
example/App.tsx
148
example/App.tsx
@ -1,12 +1,12 @@
|
|||||||
/**
|
/**
|
||||||
* MaskSegmentCanvas 业务集成 Demo
|
* MaskSegmentCanvas business integration demo
|
||||||
*
|
*
|
||||||
* 本文件完全模拟真实业务项目的集成方式:
|
* This file fully simulates the integration method of a real business project:
|
||||||
* - 只通过 `import ... from 'react-native-mask-segment-canvas'` 使用公开 API
|
* - Only use public API through `import ... from 'react-native-mask-segment-canvas'`
|
||||||
* - 不依赖库的内部实现(不 import ../src)
|
* - Do not depend on the internal implementation of the library (do not import ../src)
|
||||||
* - 覆盖:PNG 预热、状态管理、回调处理、Ref 操作、草稿恢复、错误处理
|
* - Overlay: PNG preheating, status management, callback processing, Ref operations, draft recovery, error handling
|
||||||
*
|
*
|
||||||
* 可直接复制本文件到你自己的 React Native 项目中作为参考。
|
* You can directly copy this file to your own React Native project as a reference.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
@ -42,8 +42,8 @@ import MaskSegmentCanvas, {
|
|||||||
} from 'react-native-mask-segment-canvas';
|
} from 'react-native-mask-segment-canvas';
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 测试图片 — 两组示例图,支持切换
|
// test images — two sets of example images, support switching
|
||||||
// 业务项目接入时替换为你的图片路径(file:// 或 http(s)://)
|
// replace your image path (file:// or http(s)://) when integrating into your business project
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
const TEST_IMAGE_GROUPS: Array<{
|
const TEST_IMAGE_GROUPS: Array<{
|
||||||
label: string;
|
label: string;
|
||||||
@ -53,14 +53,14 @@ const TEST_IMAGE_GROUPS: Array<{
|
|||||||
maskCacheName: string;
|
maskCacheName: string;
|
||||||
}> = [
|
}> = [
|
||||||
{
|
{
|
||||||
label: '图片组 1',
|
label: 'picture group 1',
|
||||||
origin: require('./assets/origin.png'),
|
origin: require('./assets/origin.png'),
|
||||||
mask: require('./assets/mask.png'),
|
mask: require('./assets/mask.png'),
|
||||||
originCacheName: 'example_origin_g1.png',
|
originCacheName: 'example_origin_g1.png',
|
||||||
maskCacheName: 'example_mask_g1.png',
|
maskCacheName: 'example_mask_g1.png',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '图片组 2',
|
label: 'picture group 2',
|
||||||
origin: require('./assets/origin-1.png'),
|
origin: require('./assets/origin-1.png'),
|
||||||
mask: require('./assets/mask-1.png'),
|
mask: require('./assets/mask-1.png'),
|
||||||
originCacheName: 'example_origin_g2.png',
|
originCacheName: 'example_origin_g2.png',
|
||||||
@ -69,7 +69,7 @@ const TEST_IMAGE_GROUPS: Array<{
|
|||||||
];
|
];
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 自定义语义色表示例(健身房场景)
|
// custom semantic colors example (gym scene)
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
const GYM_CUSTOM_COLORS: MaskSemanticColor[] = [
|
const GYM_CUSTOM_COLORS: MaskSemanticColor[] = [
|
||||||
{ name: 'wall', hex: '#4363D8', bgr: { b: 216, g: 99, r: 67 } },
|
{ name: 'wall', hex: '#4363D8', bgr: { b: 216, g: 99, r: 67 } },
|
||||||
@ -81,17 +81,17 @@ const GYM_CUSTOM_COLORS: MaskSemanticColor[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 预设笔刷色(底部色条之外,业务可通过 ref.setPaintColor 设置)
|
// preset brush colors (outside the bottom color bar, business can set through ref.setPaintColor)
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
const PAINT_PRESETS: Array<{ label: string; color: BgrColor }> = [
|
const PAINT_PRESETS: Array<{ label: string; color: BgrColor }> = [
|
||||||
{ label: '象牙白', color: { b: 200, g: 230, r: 245 } },
|
{ label: 'Ivory white', color: { b: 200, g: 230, r: 245 } },
|
||||||
{ label: '米黄', color: { b: 150, g: 220, r: 245 } },
|
{ label: 'Yellow', color: { b: 150, g: 220, r: 245 } },
|
||||||
{ label: '浅灰', color: { b: 180, g: 180, r: 180 } },
|
{ label: 'Light gray', color: { b: 180, g: 180, r: 180 } },
|
||||||
{ label: '淡蓝', color: { b: 220, g: 200, r: 170 } },
|
{ label: 'Light blue', color: { b: 220, g: 200, r: 170 } },
|
||||||
];
|
];
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// watchState 工具
|
// watchState tools
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
const INTERACTIVE_STATES: MaskSegmentWatchState[] = [
|
const INTERACTIVE_STATES: MaskSegmentWatchState[] = [
|
||||||
'interactive',
|
'interactive',
|
||||||
@ -99,13 +99,13 @@ const INTERACTIVE_STATES: MaskSegmentWatchState[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 主页面
|
// main page
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
function App(): React.JSX.Element {
|
function App(): React.JSX.Element {
|
||||||
const canvasRef = useRef<MaskSegmentCanvasRef>(null);
|
const canvasRef = useRef<MaskSegmentCanvasRef>(null);
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
// 状态
|
// State
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
const [imagePaths, setImagePaths] = useState<{
|
const [imagePaths, setImagePaths] = useState<{
|
||||||
origin: string;
|
origin: string;
|
||||||
@ -119,14 +119,14 @@ function App(): React.JSX.Element {
|
|||||||
const [saveResult, setSaveResult] = useState<SavePaintResult | null>(null);
|
const [saveResult, setSaveResult] = useState<SavePaintResult | null>(null);
|
||||||
const [sessionDraft] = useState<MaskSegmentSession | null>(null);
|
const [sessionDraft] = useState<MaskSegmentSession | null>(null);
|
||||||
|
|
||||||
// Demo 模式
|
// Demo mode
|
||||||
const [useCustomColors, setUseCustomColors] = useState(false);
|
const [useCustomColors, setUseCustomColors] = useState(false);
|
||||||
const [splitWalls, setSplitWalls] = useState(false);
|
const [splitWalls, setSplitWalls] = useState(false);
|
||||||
const [pipelinePreset, setPipelinePreset] = useState<PipelinePreset>('medium');
|
const [pipelinePreset, setPipelinePreset] = useState<PipelinePreset>('medium');
|
||||||
const [groupIndex, setGroupIndex] = useState(0);
|
const [groupIndex, setGroupIndex] = useState(0);
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
// 派生状态
|
// derived state
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
const isInteractive = INTERACTIVE_STATES.includes(
|
const isInteractive = INTERACTIVE_STATES.includes(
|
||||||
watchState as MaskSegmentWatchState,
|
watchState as MaskSegmentWatchState,
|
||||||
@ -141,7 +141,7 @@ function App(): React.JSX.Element {
|
|||||||
const semanticColors = useCustomColors ? GYM_CUSTOM_COLORS : MASK_SEMANTIC_COLORS;
|
const semanticColors = useCustomColors ? GYM_CUSTOM_COLORS : MASK_SEMANTIC_COLORS;
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
// 初始化:解析测试图路径(require → 本地 PNG 缓存路径)
|
// Init: resolve test image paths (require → local PNG cache path)
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
@ -177,7 +177,7 @@ function App(): React.JSX.Element {
|
|||||||
}, [groupIndex]);
|
}, [groupIndex]);
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
// Toast 提示
|
// Toast message
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
const showToast = useCallback((msg: string) => {
|
const showToast = useCallback((msg: string) => {
|
||||||
setToastMessage(msg);
|
setToastMessage(msg);
|
||||||
@ -185,7 +185,7 @@ function App(): React.JSX.Element {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
// onWatch 回调
|
// onWatch callback
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
const handleWatch = useCallback(
|
const handleWatch = useCallback(
|
||||||
(
|
(
|
||||||
@ -204,18 +204,18 @@ function App(): React.JSX.Element {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
// onPaintCallback — 处理上色成功 / 未选笔刷两种场景
|
// onPaintCallback — handle paint success / brush not selected two scenarios
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
const handlePaintCallback = useCallback((payload: PaintCallbackPayload) => {
|
const handlePaintCallback = useCallback((payload: PaintCallbackPayload) => {
|
||||||
if (payload.kind === 'brush_required') {
|
if (payload.kind === 'brush_required') {
|
||||||
// 用户未选笔刷时点击了分区,业务侧弹提示引导选色
|
// user did not select a brush, the business side pops up a prompt to guide selection of color
|
||||||
showToast(payload.hint);
|
showToast(payload.hint);
|
||||||
console.log('[Example] 需选笔刷:', payload.regionName);
|
console.log('[Example] Need to select a brush:', payload.regionName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 上色成功
|
// paint success
|
||||||
console.log(
|
console.log(
|
||||||
'[Example] 上色成功:',
|
'[Example] Paint success:',
|
||||||
payload.regionName,
|
payload.regionName,
|
||||||
`(${payload.regionId})`,
|
`(${payload.regionId})`,
|
||||||
payload.color,
|
payload.color,
|
||||||
@ -223,7 +223,7 @@ function App(): React.JSX.Element {
|
|||||||
}, [showToast]);
|
}, [showToast]);
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
// onError 回调
|
// onError callback
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
const handleError = useCallback((message: string) => {
|
const handleError = useCallback((message: string) => {
|
||||||
setErrorMessage(message);
|
setErrorMessage(message);
|
||||||
@ -231,7 +231,7 @@ function App(): React.JSX.Element {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
// Ref 操作封装
|
// Ref operations encapsulation
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
const handleSave = useCallback(async () => {
|
const handleSave = useCallback(async () => {
|
||||||
if (!isInteractive) return;
|
if (!isInteractive) return;
|
||||||
@ -239,10 +239,10 @@ function App(): React.JSX.Element {
|
|||||||
const result = await canvasRef.current?.save();
|
const result = await canvasRef.current?.save();
|
||||||
if (result) {
|
if (result) {
|
||||||
setSaveResult(result);
|
setSaveResult(result);
|
||||||
Alert.alert('保存成功', `路径: ${result.filePath}\n已上色 ${result.paintedCount} 个区域`);
|
Alert.alert('Save success', `Path: ${result.filePath}\nPainted ${result.paintedCount} regions`);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Alert.alert('保存失败', e instanceof Error ? e.message : String(e));
|
Alert.alert('Save failed', e instanceof Error ? e.message : String(e));
|
||||||
}
|
}
|
||||||
}, [isInteractive]);
|
}, [isInteractive]);
|
||||||
|
|
||||||
@ -250,16 +250,16 @@ function App(): React.JSX.Element {
|
|||||||
const handleSwap = useCallback(() => canvasRef.current?.swap(), []);
|
const handleSwap = useCallback(() => canvasRef.current?.swap(), []);
|
||||||
const handleClearAll = useCallback(() => {
|
const handleClearAll = useCallback(() => {
|
||||||
canvasRef.current?.clearAllPaint();
|
canvasRef.current?.clearAllPaint();
|
||||||
showToast('已清空全部上色');
|
showToast('All paint cleared');
|
||||||
}, [showToast]);
|
}, [showToast]);
|
||||||
|
|
||||||
const handleExportSession = useCallback(() => {
|
const handleExportSession = useCallback(() => {
|
||||||
const session = canvasRef.current?.session();
|
const session = canvasRef.current?.session();
|
||||||
if (session) {
|
if (session) {
|
||||||
console.log('[Example] 会话快照:', JSON.stringify(session, null, 2));
|
console.log('[Example] Session snapshot:', JSON.stringify(session, null, 2));
|
||||||
Alert.alert(
|
Alert.alert(
|
||||||
'会话快照',
|
'Session snapshot',
|
||||||
`已上色 ${session.painted.length} 个区域\n可存入 MMKV / AsyncStorage 实现草稿恢复`,
|
`Painted ${session.painted.length} regions\nCan be stored in MMKV / AsyncStorage to implement draft recovery`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
@ -267,19 +267,19 @@ function App(): React.JSX.Element {
|
|||||||
const handleSetPaintColor = useCallback(
|
const handleSetPaintColor = useCallback(
|
||||||
(color: BgrColor, label: string) => {
|
(color: BgrColor, label: string) => {
|
||||||
canvasRef.current?.setPaintColor(color, { preset: label });
|
canvasRef.current?.setPaintColor(color, { preset: label });
|
||||||
showToast(`已选择笔刷: ${label}`);
|
showToast(`Selected brush: ${label}`);
|
||||||
},
|
},
|
||||||
[showToast],
|
[showToast],
|
||||||
);
|
);
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
// 渲染:错误 / 加载 / 就绪
|
// render: error / loading / ready
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
if (pathsError) {
|
if (pathsError) {
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.root}>
|
<SafeAreaView style={styles.root}>
|
||||||
<View style={styles.center}>
|
<View style={styles.center}>
|
||||||
<Text style={styles.errorText}>图片加载失败</Text>
|
<Text style={styles.errorText}>Image loading failed</Text>
|
||||||
<Text style={styles.errorDetail}>{pathsError}</Text>
|
<Text style={styles.errorDetail}>{pathsError}</Text>
|
||||||
</View>
|
</View>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
@ -291,7 +291,7 @@ function App(): React.JSX.Element {
|
|||||||
<SafeAreaView style={styles.root}>
|
<SafeAreaView style={styles.root}>
|
||||||
<View style={styles.center}>
|
<View style={styles.center}>
|
||||||
<ActivityIndicator size="large" color="#4363D8" />
|
<ActivityIndicator size="large" color="#4363D8" />
|
||||||
<Text style={styles.loadingText}>正在预热 PNG 缓存…</Text>
|
<Text style={styles.loadingText}>Preheating PNG cache…</Text>
|
||||||
</View>
|
</View>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
@ -301,11 +301,11 @@ function App(): React.JSX.Element {
|
|||||||
<SafeAreaView style={styles.root}>
|
<SafeAreaView style={styles.root}>
|
||||||
<StatusBar barStyle="dark-content" backgroundColor="#fff" />
|
<StatusBar barStyle="dark-content" backgroundColor="#fff" />
|
||||||
|
|
||||||
{/* 顶部:状态 + 模式切换 */}
|
{/* top: status + mode switch */}
|
||||||
<View style={styles.topBar}>
|
<View style={styles.topBar}>
|
||||||
<View style={styles.topBarRow}>
|
<View style={styles.topBarRow}>
|
||||||
<Text style={styles.statusLabel}>
|
<Text style={styles.statusLabel}>
|
||||||
状态:{' '}
|
Status:{' '}
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
styles.statusValue,
|
styles.statusValue,
|
||||||
@ -313,19 +313,19 @@ function App(): React.JSX.Element {
|
|||||||
watchState === 'error' && styles.statusError,
|
watchState === 'error' && styles.statusError,
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{watchState || '初始化…'}
|
{watchState || 'Initializing…'}
|
||||||
</Text>
|
</Text>
|
||||||
{isOutlineReady ? ' · 轮播就绪' : ''}
|
{isOutlineReady ? ' · Carousel ready' : ''}
|
||||||
{isInteractive && !isOutlineReady ? ' · 轮廓加载中' : ''}
|
{isInteractive && !isOutlineReady ? ' · Outline loading' : ''}
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={styles.regionCount}>
|
<Text style={styles.regionCount}>
|
||||||
{watchDetail.regionCount != null
|
{watchDetail.regionCount != null
|
||||||
? `${watchDetail.regionCount} 个分区`
|
? `${watchDetail.regionCount} partitions`
|
||||||
: ''}
|
: ''}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 模式切换 */}
|
{/* mode switch */}
|
||||||
<ScrollView
|
<ScrollView
|
||||||
horizontal
|
horizontal
|
||||||
showsHorizontalScrollIndicator={false}
|
showsHorizontalScrollIndicator={false}
|
||||||
@ -337,7 +337,7 @@ function App(): React.JSX.Element {
|
|||||||
onPress={() => setUseCustomColors(false)}
|
onPress={() => setUseCustomColors(false)}
|
||||||
>
|
>
|
||||||
<Text style={[styles.modeChipText, !useCustomColors && styles.modeChipTextActive]}>
|
<Text style={[styles.modeChipText, !useCustomColors && styles.modeChipTextActive]}>
|
||||||
默认色表
|
Default color palette
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
@ -345,7 +345,7 @@ function App(): React.JSX.Element {
|
|||||||
onPress={() => setUseCustomColors(true)}
|
onPress={() => setUseCustomColors(true)}
|
||||||
>
|
>
|
||||||
<Text style={[styles.modeChipText, useCustomColors && styles.modeChipTextActive]}>
|
<Text style={[styles.modeChipText, useCustomColors && styles.modeChipTextActive]}>
|
||||||
自定义色表
|
Custom color palette
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<Text style={styles.modeDivider}>|</Text>
|
<Text style={styles.modeDivider}>|</Text>
|
||||||
@ -368,7 +368,7 @@ function App(): React.JSX.Element {
|
|||||||
onPress={() => setPipelinePreset(p)}
|
onPress={() => setPipelinePreset(p)}
|
||||||
>
|
>
|
||||||
<Text style={[styles.modeChipText, pipelinePreset === p && styles.modeChipTextActive]}>
|
<Text style={[styles.modeChipText, pipelinePreset === p && styles.modeChipTextActive]}>
|
||||||
{p === 'low' ? '低精度' : p === 'medium' ? '中精度' : '高精度'}
|
{p === 'low' ? 'Low precision' : p === 'medium' ? 'Medium precision' : 'High precision'}
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
))}
|
))}
|
||||||
@ -378,13 +378,13 @@ function App(): React.JSX.Element {
|
|||||||
onPress={() => setSplitWalls(v => !v)}
|
onPress={() => setSplitWalls(v => !v)}
|
||||||
>
|
>
|
||||||
<Text style={[styles.modeChipText, splitWalls && styles.modeChipTextActive]}>
|
<Text style={[styles.modeChipText, splitWalls && styles.modeChipTextActive]}>
|
||||||
墙壁细分
|
(split walls)
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 画布 */}
|
{/* canvas */}
|
||||||
<View style={styles.canvasHost}>
|
<View style={styles.canvasHost}>
|
||||||
<MaskSegmentCanvas
|
<MaskSegmentCanvas
|
||||||
key={`image-group-${groupIndex}-split-${splitWalls ? 1 : 0}`}
|
key={`image-group-${groupIndex}-split-${splitWalls ? 1 : 0}`}
|
||||||
@ -416,12 +416,12 @@ function App(): React.JSX.Element {
|
|||||||
onError={handleError}
|
onError={handleError}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 初始化加载遮罩 */}
|
{/* initialization loading mask */}
|
||||||
{isInitLoading && (
|
{isInitLoading && (
|
||||||
<View style={styles.initOverlay} pointerEvents="none">
|
<View style={styles.initOverlay} pointerEvents="none">
|
||||||
<ActivityIndicator size="small" color="#4363D8" />
|
<ActivityIndicator size="small" color="#4363D8" />
|
||||||
<Text style={styles.initOverlayText}>
|
<Text style={styles.initOverlayText}>
|
||||||
初始化中:{watchState}
|
Initializing: {watchState}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
@ -434,15 +434,15 @@ function App(): React.JSX.Element {
|
|||||||
</View>
|
</View>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{/* 底部:业务操作栏 / Ref 方法演示 */}
|
{/* bottom: business operation bar / Ref method demonstration */}
|
||||||
<View style={styles.bottomBar}>
|
<View style={styles.bottomBar}>
|
||||||
<ScrollView
|
<ScrollView
|
||||||
horizontal
|
horizontal
|
||||||
showsHorizontalScrollIndicator={false}
|
showsHorizontalScrollIndicator={false}
|
||||||
contentContainerStyle={styles.bottomBarContent}
|
contentContainerStyle={styles.bottomBarContent}
|
||||||
>
|
>
|
||||||
{/* 预设笔刷(替代底部色条) */}
|
{/* preset brush (replace bottom color bar) */}
|
||||||
<Text style={styles.sectionLabel}>预设笔刷:</Text>
|
<Text style={styles.sectionLabel}>Preset brush:</Text>
|
||||||
{PAINT_PRESETS.map(p => (
|
{PAINT_PRESETS.map(p => (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
key={p.label}
|
key={p.label}
|
||||||
@ -456,50 +456,50 @@ function App(): React.JSX.Element {
|
|||||||
|
|
||||||
<View style={styles.divider} />
|
<View style={styles.divider} />
|
||||||
|
|
||||||
{/* Ref 操作 */}
|
{/* Ref operations */}
|
||||||
<Text style={styles.sectionLabel}>操作:</Text>
|
<Text style={styles.sectionLabel}>Operations:</Text>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[styles.actionBtn, styles.actionBtnDanger]}
|
style={[styles.actionBtn, styles.actionBtnDanger]}
|
||||||
onPress={handleReset}
|
onPress={handleReset}
|
||||||
disabled={!isInteractive}
|
disabled={!isInteractive}
|
||||||
>
|
>
|
||||||
<Text style={styles.actionBtnText}>撤销</Text>
|
<Text style={styles.actionBtnText}>Undo</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.actionBtn}
|
style={styles.actionBtn}
|
||||||
onPress={handleSwap}
|
onPress={handleSwap}
|
||||||
disabled={!isInteractive}
|
disabled={!isInteractive}
|
||||||
>
|
>
|
||||||
<Text style={styles.actionBtnText}>对比</Text>
|
<Text style={styles.actionBtnText}>Compare</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.actionBtn}
|
style={styles.actionBtn}
|
||||||
onPress={handleClearAll}
|
onPress={handleClearAll}
|
||||||
disabled={!isInteractive}
|
disabled={!isInteractive}
|
||||||
>
|
>
|
||||||
<Text style={styles.actionBtnText}>清空</Text>
|
<Text style={styles.actionBtnText}>Clear</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[styles.actionBtn, styles.actionBtnPrimary]}
|
style={[styles.actionBtn, styles.actionBtnPrimary]}
|
||||||
onPress={handleSave}
|
onPress={handleSave}
|
||||||
disabled={!isInteractive}
|
disabled={!isInteractive}
|
||||||
>
|
>
|
||||||
<Text style={styles.actionBtnTextPrimary}>保存</Text>
|
<Text style={styles.actionBtnTextPrimary}>Save</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.actionBtn}
|
style={styles.actionBtn}
|
||||||
onPress={handleExportSession}
|
onPress={handleExportSession}
|
||||||
disabled={!isInteractive}
|
disabled={!isInteractive}
|
||||||
>
|
>
|
||||||
<Text style={styles.actionBtnText}>导出会话</Text>
|
<Text style={styles.actionBtnText}>Export session</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 错误展示 */}
|
{/* error display */}
|
||||||
{errorMessage ? (
|
{errorMessage ? (
|
||||||
<View style={styles.errorBar}>
|
<View style={styles.errorBar}>
|
||||||
<Text style={styles.errorBarText}>错误: {errorMessage}</Text>
|
<Text style={styles.errorBarText}>Error: {errorMessage}</Text>
|
||||||
</View>
|
</View>
|
||||||
) : null}
|
) : null}
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
@ -507,7 +507,7 @@ function App(): React.JSX.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 样式
|
// styles
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
root: {
|
root: {
|
||||||
@ -537,7 +537,7 @@ const styles = StyleSheet.create({
|
|||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
},
|
},
|
||||||
|
|
||||||
// 顶部状态栏
|
// top status bar
|
||||||
topBar: {
|
topBar: {
|
||||||
paddingHorizontal: 12,
|
paddingHorizontal: 12,
|
||||||
paddingTop: 8,
|
paddingTop: 8,
|
||||||
@ -599,7 +599,7 @@ const styles = StyleSheet.create({
|
|||||||
marginHorizontal: 2,
|
marginHorizontal: 2,
|
||||||
},
|
},
|
||||||
|
|
||||||
// 画布
|
// canvas
|
||||||
canvasHost: {
|
canvasHost: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
@ -639,7 +639,7 @@ const styles = StyleSheet.create({
|
|||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
},
|
},
|
||||||
|
|
||||||
// 底部操作栏
|
// bottom operation bar
|
||||||
bottomBar: {
|
bottomBar: {
|
||||||
borderTopWidth: StyleSheet.hairlineWidth,
|
borderTopWidth: StyleSheet.hairlineWidth,
|
||||||
borderTopColor: '#e8e8e8',
|
borderTopColor: '#e8e8e8',
|
||||||
@ -664,7 +664,7 @@ const styles = StyleSheet.create({
|
|||||||
marginHorizontal: 4,
|
marginHorizontal: 4,
|
||||||
},
|
},
|
||||||
|
|
||||||
// 笔刷按钮
|
// brush button
|
||||||
paintBtn: {
|
paintBtn: {
|
||||||
paddingHorizontal: 10,
|
paddingHorizontal: 10,
|
||||||
paddingVertical: 6,
|
paddingVertical: 6,
|
||||||
@ -681,7 +681,7 @@ const styles = StyleSheet.create({
|
|||||||
textShadowRadius: 2,
|
textShadowRadius: 2,
|
||||||
},
|
},
|
||||||
|
|
||||||
// 操作按钮
|
// operation button
|
||||||
actionBtn: {
|
actionBtn: {
|
||||||
paddingHorizontal: 12,
|
paddingHorizontal: 12,
|
||||||
paddingVertical: 6,
|
paddingVertical: 6,
|
||||||
@ -708,7 +708,7 @@ const styles = StyleSheet.create({
|
|||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
},
|
},
|
||||||
|
|
||||||
// 错误条
|
// error bar
|
||||||
errorBar: {
|
errorBar: {
|
||||||
backgroundColor: '#fff0f0',
|
backgroundColor: '#fff0f0',
|
||||||
paddingHorizontal: 12,
|
paddingHorizontal: 12,
|
||||||
|
|||||||
@ -1,93 +1,93 @@
|
|||||||
# MaskSegmentCanvas Example
|
# MaskSegmentCanvas Example
|
||||||
|
|
||||||
这是一个**完全模拟真实业务项目集成**的 Demo,展示如何在你的 React Native 工程中接入 `react-native-mask-segment-canvas`。
|
A Demo project that **fully simulates real-world business integration**, showing how to integrate `react-native-mask-segment-canvas` into your React Native project.
|
||||||
|
|
||||||
## 与库本身 Demo 的区别
|
## Difference from the Library's Own Demo
|
||||||
|
|
||||||
| 项目 | 引入方式 | 用途 |
|
| Project | Import Method | Purpose |
|
||||||
| ---- | -------- | ---- |
|
| ---- | -------- | ---- |
|
||||||
| 根目录 `App.tsx` | `import ... from './src'`(内部源码) | 库作者自测 |
|
| Root `App.tsx` | `import ... from './src'` (internal source) | Library author self-testing |
|
||||||
| **本 example/** | `import ... from 'react-native-mask-segment-canvas'`(公开 API) | **业务集成参考** |
|
| **This example/** | `import ... from 'react-native-mask-segment-canvas'` (public API) | **Business integration reference** |
|
||||||
|
|
||||||
本 example 只依赖库的公开 API,不触碰 `src/` 内部实现,是你接入时可以直接复制的模板。
|
This example only depends on the library's public API and does not touch any `src/` internals. It serves as a template you can directly copy into your project.
|
||||||
|
|
||||||
## 快速开始
|
## Quick Start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. 进入 example 目录
|
# 1. Enter the example directory
|
||||||
cd example
|
cd example
|
||||||
|
|
||||||
# 2. 安装依赖(自动 link 父目录的库)
|
# 2. Install dependencies (auto-links the parent library)
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
# 3. 应用 postinstall 补丁(patch-package 修补 react-native-fast-opencv)
|
# 3. Apply postinstall patches (patch-package patches react-native-fast-opencv)
|
||||||
# npm install 后自动执行,若未执行请手动:
|
# Automatically runs after npm install. If it didn't, run manually:
|
||||||
npx patch-package
|
npx patch-package
|
||||||
|
|
||||||
# 4. iOS:安装原生依赖
|
# 4. iOS: Install native dependencies
|
||||||
cd ios && pod install && cd ..
|
cd ios && pod install && cd ..
|
||||||
|
|
||||||
# 5. 启动 Metro
|
# 5. Start Metro
|
||||||
npm start
|
npm start
|
||||||
|
|
||||||
# 6. 另开终端运行
|
# 6. In another terminal, run
|
||||||
npm run ios
|
npm run ios
|
||||||
# 或
|
# or
|
||||||
npm run android
|
npm run android
|
||||||
```
|
```
|
||||||
|
|
||||||
## 文件说明
|
## File Overview
|
||||||
|
|
||||||
```
|
```
|
||||||
example/
|
example/
|
||||||
├── App.tsx # ★ 核心:完整的集成示例页面
|
├── App.tsx # ★ Core: Complete integration example page
|
||||||
├── index.js # RN 入口(注册 gesture-handler + Buffer polyfill)
|
├── index.js # RN entry (registers gesture-handler + Buffer polyfill)
|
||||||
├── app.json # 应用名配置
|
├── app.json # App name config
|
||||||
├── package.json # 独立依赖配置,"react-native-mask-segment-canvas": "file:.."
|
├── package.json # Standalone dependency config, "react-native-mask-segment-canvas": "file:.."
|
||||||
├── metro.config.js # Metro 配置(watchFolders 指向父目录)
|
├── metro.config.js # Metro config (watchFolders pointing to parent directory)
|
||||||
├── babel.config.js # Babel 配置(含 reanimated 插件)
|
├── babel.config.js # Babel config (includes reanimated plugin)
|
||||||
├── tsconfig.json # TypeScript 配置
|
├── tsconfig.json # TypeScript config
|
||||||
└── README.md # 本文件
|
└── README.md # This file
|
||||||
```
|
```
|
||||||
|
|
||||||
## App.tsx 覆盖的功能点
|
## Features Covered in App.tsx
|
||||||
|
|
||||||
`App.tsx` 是一个可直接参考的完整页面,涵盖:
|
`App.tsx` is a complete page you can reference directly, covering:
|
||||||
|
|
||||||
| 功能 | 对应代码位置 |
|
| Feature | Relevant Code Location |
|
||||||
| ---- | ------------ |
|
| ---- | ------------ |
|
||||||
| **PNG 预热** | `useEffect` → `prewarmPngBgrCacheAsync` |
|
| **PNG pre-warming** | `useEffect` → `prewarmPngBgrCacheAsync` |
|
||||||
| **状态管理** | `watchState` / `isInteractive` / `isOutlineReady` 等派生状态 |
|
| **State management** | `watchState` / `isInteractive` / `isOutlineReady` derived states |
|
||||||
| **onWatch 回调** | `handleWatch` — 跟踪初始化阶段 |
|
| **onWatch callback** | `handleWatch` — tracks initialization stages |
|
||||||
| **onPaintCallback** | `handlePaintCallback` — 处理上色成功 / 未选笔刷两种场景 |
|
| **onPaintCallback** | `handlePaintCallback` — handles both successful paint and missing-brush scenarios |
|
||||||
| **onError 回调** | `handleError` — 捕获分割/加载失败 |
|
| **onError callback** | `handleError` — captures segmentation/load failures |
|
||||||
| **Ref 操作** | `save` / `reset` / `swap` / `clearAllPaint` / `session` |
|
| **Ref operations** | `save` / `reset` / `swap` / `clearAllPaint` / `session` |
|
||||||
| **setPaintColor** | 预设笔刷色,通过 `ref.setPaintColor` 设置 |
|
| **setPaintColor** | Preset brush colors via `ref.setPaintColor` |
|
||||||
| **自定义语义色表** | `GYM_CUSTOM_COLORS` 示例 + 模式切换 UI |
|
| **Custom semantic color table** | `GYM_CUSTOM_COLORS` example + mode toggle UI |
|
||||||
| **Pipeline 精度切换** | `pipelinePreset` 低/中/高精度切换 |
|
| **Pipeline resolution toggle** | `pipelinePreset` low/medium/high resolution switching |
|
||||||
| **Toast 提示** | 未选笔刷时 `brush_required` 回调 + 自定义 Toast |
|
| **Toast notifications** | `brush_required` callback when no brush is selected + custom Toast |
|
||||||
| **加载态/错误态 UI** | PNG 预热加载、初始化 Loading、错误展示 |
|
| **Loading/error UI** | PNG pre-warm loading, initialization Loading, error display |
|
||||||
| **草稿恢复** | `sessionDraft` 状态 + `initialSession` prop |
|
| **Draft recovery** | `sessionDraft` state + `initialSession` prop |
|
||||||
|
|
||||||
## 集成到自己项目
|
## Integrating into Your Own Project
|
||||||
|
|
||||||
### 方式一:npm install(推荐生产环境)
|
### Option 1: npm install (recommended for production)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install react-native-mask-segment-canvas
|
npm install react-native-mask-segment-canvas
|
||||||
```
|
```
|
||||||
|
|
||||||
### 方式二:本地联调(开发阶段)
|
### Option 2: Local development
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 在库目录
|
# In the library directory
|
||||||
npm link
|
npm link
|
||||||
|
|
||||||
# 在你的项目
|
# In your project
|
||||||
npm link react-native-mask-segment-canvas
|
npm link react-native-mask-segment-canvas
|
||||||
```
|
```
|
||||||
|
|
||||||
你的 `metro.config.js` 需要添加:
|
Your `metro.config.js` needs to add:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
@ -100,7 +100,7 @@ module.exports = mergeConfig(getDefaultConfig(__dirname), {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
### 方式三:file: 依赖(本 example 使用的方式)
|
### Option 3: file: dependency (used by this example)
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@ -110,19 +110,19 @@ module.exports = mergeConfig(getDefaultConfig(__dirname), {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 必装 peerDependencies
|
### Required peerDependencies
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install @shopify/react-native-skia react-native-reanimated react-native-fast-opencv react-native-fs buffer
|
npm install @shopify/react-native-skia react-native-reanimated react-native-fast-opencv react-native-fs buffer
|
||||||
# 若使用相册选图
|
# If using photo library picker
|
||||||
npm install react-native-image-picker
|
npm install react-native-image-picker
|
||||||
# 安全区适配
|
# Safe area insets
|
||||||
npm install react-native-safe-area-context
|
npm install react-native-safe-area-context
|
||||||
```
|
```
|
||||||
|
|
||||||
### postinstall 配置
|
### postinstall Configuration
|
||||||
|
|
||||||
你的 `package.json` 需要:
|
Your `package.json` needs:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@ -135,37 +135,37 @@ npm install react-native-safe-area-context
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 常见问题
|
## Common Issues
|
||||||
|
|
||||||
**`npm install` 后报模块找不到?**
|
**Getting "module not found" after `npm install`?**
|
||||||
- 确认已执行 `postinstall`(`npx patch-package`)
|
- Make sure `postinstall` ran (`npx patch-package`)
|
||||||
- 检查 Metro 配置中 `watchFolders` 是否包含库目录
|
- Check that Metro config's `watchFolders` includes the library directory
|
||||||
|
|
||||||
**`pod install` 失败?**
|
**`pod install` fails?**
|
||||||
```bash
|
```bash
|
||||||
cd ios
|
cd ios
|
||||||
bundle install
|
bundle install
|
||||||
bundle exec pod install --repo-update
|
bundle exec pod install --repo-update
|
||||||
```
|
```
|
||||||
|
|
||||||
**Android 编译错误?**
|
**Android build errors?**
|
||||||
```bash
|
```bash
|
||||||
cd android && ./gradlew clean && cd ..
|
cd android && ./gradlew clean && cd ..
|
||||||
```
|
```
|
||||||
|
|
||||||
**运行时出现「重复模块」类错误(最常见)**
|
**Duplicate module errors at runtime (most common)**
|
||||||
|
|
||||||
在 monorepo、npm link、`file:..` 场景下,经常会遇到下面这些「类似问题」:
|
In monorepo, npm link, or `file:..` setups, you'll frequently encounter these "similar-looking" errors:
|
||||||
|
|
||||||
- `SkiaPictureView must be a function (received 'undefined')`
|
- `SkiaPictureView must be a function (received 'undefined')`
|
||||||
- `createAnimatedNode: Animated node[...] already exists`(含 UIFrameGuarded 变体)
|
- `createAnimatedNode: Animated node[...] already exists` (including UIFrameGuarded variants)
|
||||||
- 其他 Fabric ViewManager / native module 单例冲突
|
- Other Fabric ViewManager / native module singleton conflicts
|
||||||
|
|
||||||
**原因**:Metro 同时加载了多份 `@shopify/react-native-skia`、`react-native-reanimated`、`react-native-gesture-handler`、`react-native-fast-opencv`、`react-native-safe-area-context` 等 peer 依赖。
|
**Root cause**: Metro is loading multiple copies of `@shopify/react-native-skia`, `react-native-reanimated`, `react-native-gesture-handler`, `react-native-fast-opencv`, `react-native-safe-area-context`, and other peer dependencies.
|
||||||
|
|
||||||
**推荐完整解决方案**(直接复制到你的项目):
|
**Recommended complete solution** (copy directly into your project):
|
||||||
|
|
||||||
1. **index.js 最顶部**(必须在最前面):
|
1. **At the very top of index.js** (must come first):
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import 'react-native-gesture-handler';
|
import 'react-native-gesture-handler';
|
||||||
@ -173,7 +173,7 @@ cd android && ./gradlew clean && cd ..
|
|||||||
import '@shopify/react-native-skia';
|
import '@shopify/react-native-skia';
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **metro.config.js**(使用 extraNodeModules + blockList 双保险):
|
2. **metro.config.js** (use extraNodeModules + blockList for double safety):
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
@ -202,10 +202,10 @@ cd android && ./gradlew clean && cd ..
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
> `example/metro.config.js` 已经是按这个标准模板写的,可直接参考。
|
> `example/metro.config.js` is already written following this standard template. You can reference it directly.
|
||||||
|
|
||||||
做完上面两步后,**必须**:
|
After completing the two steps above, you **must**:
|
||||||
- 重启 Metro(`npx react-native start --reset-cache`)
|
- Restart Metro (`npx react-native start --reset-cache`)
|
||||||
- 重新安装 app(建议先 `cd android && ./gradlew clean` 或 iOS pod 后重跑)
|
- Reinstall the app (recommend `cd android && ./gradlew clean` first, or re-run after iOS pod install)
|
||||||
|
|
||||||
这样能一次性解决所有「同类」重复模块导致的运行时错误。
|
This will resolve all "similar" duplicate-module runtime errors in one shot.
|
||||||
|
|||||||
@ -2,17 +2,17 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 【关键 - 必须最顶部】按此顺序提前导入带 JSI/原生注册副作用的库。
|
// [CRITICAL - Must be at the very top] Import libraries with JSI/native registration side effects in this order.
|
||||||
// 推荐顺序:
|
// Recommended order:
|
||||||
// 1. react-native-gesture-handler
|
// 1. react-native-gesture-handler
|
||||||
// 2. react-native-reanimated
|
// 2. react-native-reanimated
|
||||||
// 3. @shopify/react-native-skia
|
// 3. @shopify/react-native-skia
|
||||||
//
|
//
|
||||||
// 配合 example/metro.config.js 的 extraNodeModules + blockList 使用,
|
// Used together with example/metro.config.js extraNodeModules + blockList,
|
||||||
// 能彻底避免 monorepo/file: 场景下的重复模块问题:
|
// this completely avoids duplicate module issues in monorepo/file: setups:
|
||||||
// - SkiaPictureView config getter undefined
|
// - SkiaPictureView config getter undefined
|
||||||
// - createAnimatedNode: Animated node already exists
|
// - createAnimatedNode: Animated node already exists
|
||||||
// - 其他 Fabric / ViewManager 冲突
|
// - Other Fabric / ViewManager conflicts
|
||||||
import 'react-native-gesture-handler';
|
import 'react-native-gesture-handler';
|
||||||
import 'react-native-reanimated';
|
import 'react-native-reanimated';
|
||||||
import '@shopify/react-native-skia';
|
import '@shopify/react-native-skia';
|
||||||
|
|||||||
@ -4,22 +4,22 @@ const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
|
|||||||
/**
|
/**
|
||||||
* Metro configuration for the example app.
|
* Metro configuration for the example app.
|
||||||
*
|
*
|
||||||
* 关键目标:在 monorepo / "file:.." / npm link 场景下,保证所有带原生/JSI/Fabric 代码的
|
* Key goal: in monorepo / "file:.." / npm link setups, ensure all peer dependencies with native/JSI/Fabric code
|
||||||
* peer 依赖都只解析到 **example/node_modules** 下的那一份。
|
* resolve to exactly the one copy under **example/node_modules**.
|
||||||
*
|
*
|
||||||
* 重复解析会导致各种“类似问题”:
|
* Duplicate resolution causes various "similar-looking" errors:
|
||||||
* - SkiaPictureView config getter 为 undefined
|
* - SkiaPictureView config getter is undefined
|
||||||
* - createAnimatedNode: Animated node[...] already exists (Reanimated)
|
* - createAnimatedNode: Animated node[...] already exists (Reanimated)
|
||||||
* - 各种 View 注册冲突、Invalid hook call 等
|
* - Various View registration conflicts, Invalid hook calls, etc.
|
||||||
*
|
*
|
||||||
* 维护原则:凡是 package.json peerDependencies 里声明的、且包含原生代码的,都必须在这里强制单例。
|
* Maintenance rule: any peerDependency listed in package.json that contains native code must be forced to a singleton here.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const parentRoot = path.resolve(__dirname, '..');
|
const parentRoot = path.resolve(__dirname, '..');
|
||||||
const exampleNodeModules = path.resolve(__dirname, 'node_modules');
|
const exampleNodeModules = path.resolve(__dirname, 'node_modules');
|
||||||
|
|
||||||
// 必须单例的依赖列表(来自本库的 peerDependencies + 常见会引发冲突的)。
|
// Singleton dependency list (from this library's peerDependencies + commonly conflicting packages).
|
||||||
// 以后新增 peer 依赖时,记得同步更新这里。
|
// When adding new peer dependencies, remember to update this list.
|
||||||
const singletonPackages = [
|
const singletonPackages = [
|
||||||
'react',
|
'react',
|
||||||
'react-native',
|
'react-native',
|
||||||
@ -29,7 +29,7 @@ const singletonPackages = [
|
|||||||
'react-native-fast-opencv',
|
'react-native-fast-opencv',
|
||||||
'react-native-safe-area-context',
|
'react-native-safe-area-context',
|
||||||
'react-native-fs',
|
'react-native-fs',
|
||||||
// 可选 peer
|
// Optional peer
|
||||||
'react-native-image-picker',
|
'react-native-image-picker',
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -38,14 +38,14 @@ const config = {
|
|||||||
resolver: {
|
resolver: {
|
||||||
nodeModulesPaths: [exampleNodeModules],
|
nodeModulesPaths: [exampleNodeModules],
|
||||||
|
|
||||||
// 方式一(最强):extraNodeModules 强制别名
|
// Method 1 (strongest): extraNodeModules forces aliases
|
||||||
// 让 import 'xxx' 永远拿到 example/node_modules 里的实例
|
// so import 'xxx' always gets the instance in example/node_modules
|
||||||
extraNodeModules: singletonPackages.reduce((acc, pkg) => {
|
extraNodeModules: singletonPackages.reduce((acc, pkg) => {
|
||||||
acc[pkg] = path.resolve(exampleNodeModules, pkg);
|
acc[pkg] = path.resolve(exampleNodeModules, pkg);
|
||||||
return acc;
|
return acc;
|
||||||
}, {}),
|
}, {}),
|
||||||
|
|
||||||
// 方式二(双保险):blockList 完全禁止 Metro 去父目录的 node_modules 里找这些包
|
// Method 2 (double safety): blockList completely prevents Metro from finding these packages in the parent node_modules
|
||||||
blockList: singletonPackages.map(
|
blockList: singletonPackages.map(
|
||||||
(pkg) => new RegExp(`/MaskSegmentApp/node_modules/${pkg.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/`),
|
(pkg) => new RegExp(`/MaskSegmentApp/node_modules/${pkg.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/`),
|
||||||
),
|
),
|
||||||
|
|||||||
6
index.js
6
index.js
@ -2,12 +2,6 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 【关键】在入口最顶部、任何其他代码之前按顺序导入这些包。
|
|
||||||
// 顺序推荐:gesture-handler → reanimated → skia
|
|
||||||
// 目的:确保 JSI / Fabric 组件的 install 和注册只在"正确单例"上执行一次。
|
|
||||||
// 缺失或顺序错误 + 重复模块解析 → 各种类似错误:
|
|
||||||
// SkiaPictureView must be a function (undefined)
|
|
||||||
// createAnimatedNode: Animated node already exists
|
|
||||||
import 'react-native-gesture-handler';
|
import 'react-native-gesture-handler';
|
||||||
import 'react-native-reanimated';
|
import 'react-native-reanimated';
|
||||||
import '@shopify/react-native-skia';
|
import '@shopify/react-native-skia';
|
||||||
|
|||||||
@ -35,9 +35,9 @@
|
|||||||
<key>NSLocationWhenInUseUsageDescription</key>
|
<key>NSLocationWhenInUseUsageDescription</key>
|
||||||
<string></string>
|
<string></string>
|
||||||
<key>NSPhotoLibraryUsageDescription</key>
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
<string>需要访问相册以选择原图和掩码图</string>
|
<string>Need to access the photo album to select the original image and mask image</string>
|
||||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||||
<string>需要保存处理后的图片</string>
|
<string>Need to save the processed image</string>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
<string>LaunchScreen</string>
|
<string>LaunchScreen</string>
|
||||||
<key>UIRequiredDeviceCapabilities</key>
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
|
|||||||
@ -5,10 +5,10 @@ require Pod::Executable.execute_command('node', ['-p',
|
|||||||
{paths: [process.argv[1]]},
|
{paths: [process.argv[1]]},
|
||||||
)', __dir__]).strip
|
)', __dir__]).strip
|
||||||
|
|
||||||
# 使用 GitHub 镜像,避免 pod install clone 失败
|
# Use GitHub mirror to avoid pod install clone failures
|
||||||
require_relative 'offline_pods'
|
require_relative 'offline_pods'
|
||||||
|
|
||||||
# 避免 cdn.cocoapods.org SSL 问题时,可改用 Specs 仓库(体积较大,首次较慢)
|
# When facing cdn.cocoapods.org SSL issues, can switch to Specs repo (larger, slower first time)
|
||||||
# source 'https://github.com/CocoaPods/Specs.git'
|
# source 'https://github.com/CocoaPods/Specs.git'
|
||||||
source 'https://cdn.cocoapods.org/'
|
source 'https://cdn.cocoapods.org/'
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
# CocoaPods 拉取 React Native 第三方库时走 GitHub 镜像,解决 github.com SSL/连接失败
|
# Use GitHub mirror when CocoaPods pulls React Native third-party libraries, to resolve github.com SSL/connection failures
|
||||||
require_relative '../node_modules/react-native/scripts/cocoapods/helpers.rb'
|
require_relative '../node_modules/react-native/scripts/cocoapods/helpers.rb'
|
||||||
|
|
||||||
github_mirror = ENV.fetch('COCOAPODS_GITHUB_MIRROR', 'https://ghproxy.net/https://github.com')
|
github_mirror = ENV.fetch('COCOAPODS_GITHUB_MIRROR', 'https://ghproxy.net/https://github.com')
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# 可选:预检 GitHub 镜像是否可用(默认镜像见 ios/offline_pods.rb)
|
# Optional: pre-check GitHub mirror availability (default mirror see ios/offline_pods.rb)
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
MIRROR="${COCOAPODS_GITHUB_MIRROR:-https://ghproxy.net/https://github.com}"
|
MIRROR="${COCOAPODS_GITHUB_MIRROR:-https://ghproxy.net/https://github.com}"
|
||||||
|
|||||||
@ -1,22 +1,9 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
|
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
|
||||||
|
|
||||||
/**
|
|
||||||
* Metro configuration for library development (root).
|
|
||||||
*
|
|
||||||
* 主要目的:
|
|
||||||
* - 当同时存在 example/ 目录时,防止 Metro 意外从 example/node_modules 里解析到
|
|
||||||
* 另一份 react / reanimated / skia 等带原生/JSI 的包,导致和根目录的实例冲突。
|
|
||||||
*
|
|
||||||
* 如果你只在 example/ 目录下开发测试,请优先关注 example/metro.config.js。
|
|
||||||
*
|
|
||||||
* 这里也使用和 example 一致的 singletonPackages 列表,方便维护。
|
|
||||||
*/
|
|
||||||
|
|
||||||
const rootNodeModules = path.resolve(__dirname, 'node_modules');
|
const rootNodeModules = path.resolve(__dirname, 'node_modules');
|
||||||
const exampleNodeModules = path.resolve(__dirname, 'example/node_modules');
|
const exampleNodeModules = path.resolve(__dirname, 'example/node_modules');
|
||||||
|
|
||||||
// 与 example/ 保持一致的必须单例列表
|
|
||||||
const singletonPackages = [
|
const singletonPackages = [
|
||||||
'react',
|
'react',
|
||||||
'react-native',
|
'react-native',
|
||||||
|
|||||||
490
package-lock.json
generated
490
package-lock.json
generated
@ -1,13 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "react-native-mask-segment-canvas",
|
"name": "react-native-mask-segment-canvas",
|
||||||
"version": "0.1.0",
|
"version": "0.3.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "react-native-mask-segment-canvas",
|
"name": "react-native-mask-segment-canvas",
|
||||||
"version": "0.1.0",
|
"version": "0.3.0",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"upng-js": "^2.1.0"
|
"upng-js": "^2.1.0"
|
||||||
@ -27,6 +28,7 @@
|
|||||||
"@types/jest": "^29.5.13",
|
"@types/jest": "^29.5.13",
|
||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
"@types/react-test-renderer": "^19.0.0",
|
"@types/react-test-renderer": "^19.0.0",
|
||||||
|
"esbuild": "^0.28.1",
|
||||||
"eslint": "^8.19.0",
|
"eslint": "^8.19.0",
|
||||||
"jest": "^29.6.3",
|
"jest": "^29.6.3",
|
||||||
"patch-package": "^8.0.1",
|
"patch-package": "^8.0.1",
|
||||||
@ -2110,6 +2112,448 @@
|
|||||||
"node": ">=0.8.0"
|
"node": ">=0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"aix"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/android-arm": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/android-arm64": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/android-x64": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/darwin-arm64": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/darwin-x64": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/freebsd-arm64": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/freebsd-x64": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-arm": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-arm64": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-ia32": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-loong64": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==",
|
||||||
|
"cpu": [
|
||||||
|
"loong64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-mips64el": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==",
|
||||||
|
"cpu": [
|
||||||
|
"mips64el"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-ppc64": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-riscv64": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==",
|
||||||
|
"cpu": [
|
||||||
|
"riscv64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-s390x": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==",
|
||||||
|
"cpu": [
|
||||||
|
"s390x"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-x64": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/netbsd-arm64": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"netbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/netbsd-x64": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"netbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/openbsd-arm64": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/openbsd-x64": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/openharmony-arm64": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openharmony"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/sunos-x64": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"sunos"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/win32-arm64": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/win32-ia32": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/win32-x64": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@eslint-community/eslint-utils": {
|
"node_modules/@eslint-community/eslint-utils": {
|
||||||
"version": "4.9.1",
|
"version": "4.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
|
||||||
@ -5911,6 +6355,48 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/esbuild": {
|
||||||
|
"version": "0.28.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",
|
||||||
|
"integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"esbuild": "bin/esbuild"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@esbuild/aix-ppc64": "0.28.1",
|
||||||
|
"@esbuild/android-arm": "0.28.1",
|
||||||
|
"@esbuild/android-arm64": "0.28.1",
|
||||||
|
"@esbuild/android-x64": "0.28.1",
|
||||||
|
"@esbuild/darwin-arm64": "0.28.1",
|
||||||
|
"@esbuild/darwin-x64": "0.28.1",
|
||||||
|
"@esbuild/freebsd-arm64": "0.28.1",
|
||||||
|
"@esbuild/freebsd-x64": "0.28.1",
|
||||||
|
"@esbuild/linux-arm": "0.28.1",
|
||||||
|
"@esbuild/linux-arm64": "0.28.1",
|
||||||
|
"@esbuild/linux-ia32": "0.28.1",
|
||||||
|
"@esbuild/linux-loong64": "0.28.1",
|
||||||
|
"@esbuild/linux-mips64el": "0.28.1",
|
||||||
|
"@esbuild/linux-ppc64": "0.28.1",
|
||||||
|
"@esbuild/linux-riscv64": "0.28.1",
|
||||||
|
"@esbuild/linux-s390x": "0.28.1",
|
||||||
|
"@esbuild/linux-x64": "0.28.1",
|
||||||
|
"@esbuild/netbsd-arm64": "0.28.1",
|
||||||
|
"@esbuild/netbsd-x64": "0.28.1",
|
||||||
|
"@esbuild/openbsd-arm64": "0.28.1",
|
||||||
|
"@esbuild/openbsd-x64": "0.28.1",
|
||||||
|
"@esbuild/openharmony-arm64": "0.28.1",
|
||||||
|
"@esbuild/sunos-x64": "0.28.1",
|
||||||
|
"@esbuild/win32-arm64": "0.28.1",
|
||||||
|
"@esbuild/win32-ia32": "0.28.1",
|
||||||
|
"@esbuild/win32-x64": "0.28.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/escalade": {
|
"node_modules/escalade": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
||||||
|
|||||||
10
package.json
10
package.json
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "react-native-mask-segment-canvas",
|
"name": "react-native-mask-segment-canvas",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"description": "React Native 掩码分区交互库:OpenCV 语义分割 + SkSL Shader 上色",
|
"description": "React Native mask segmentation canvas library: OpenCV semantic segmentation + SkSL Shader coloring",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"module": "dist/index.js",
|
"module": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
@ -13,7 +13,6 @@
|
|||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist",
|
"dist",
|
||||||
"src",
|
|
||||||
"patches",
|
"patches",
|
||||||
"README.md"
|
"README.md"
|
||||||
],
|
],
|
||||||
@ -26,7 +25,9 @@
|
|||||||
"postinstall": "patch-package",
|
"postinstall": "patch-package",
|
||||||
"start": "react-native start",
|
"start": "react-native start",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"build": "tsc -p tsconfig.build.json",
|
"build": "npm run build:js && npm run build:types",
|
||||||
|
"build:js": "node scripts/build.mjs",
|
||||||
|
"build:types": "tsc -p tsconfig.build.json",
|
||||||
"prepare": "npm run build"
|
"prepare": "npm run build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -67,6 +68,7 @@
|
|||||||
"@types/jest": "^29.5.13",
|
"@types/jest": "^29.5.13",
|
||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
"@types/react-test-renderer": "^19.0.0",
|
"@types/react-test-renderer": "^19.0.0",
|
||||||
|
"esbuild": "^0.28.1",
|
||||||
"eslint": "^8.19.0",
|
"eslint": "^8.19.0",
|
||||||
"jest": "^29.6.3",
|
"jest": "^29.6.3",
|
||||||
"patch-package": "^8.0.1",
|
"patch-package": "^8.0.1",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user