Compare commits
No commits in common. "8bc66a4ee962b302136727f69b45b67ec6fb0407" and "08280f38e38413b78bf25f91e3629f62614c7cec" have entirely different histories.
8bc66a4ee9
...
08280f38e3
44
.github/workflows/publish.yml
vendored
@ -1,44 +0,0 @@
|
|||||||
name: Publish to npm
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
concurrency:
|
|
||||||
group: npm-publish
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 18
|
|
||||||
registry-url: https://registry.npmjs.org/
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: npm ci --ignore-scripts
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: npm run build
|
|
||||||
|
|
||||||
- name: Check version is not already published
|
|
||||||
run: |
|
|
||||||
PKG_VERSION=$(node -p "require('./package.json').version")
|
|
||||||
NPM_VERSION=$(npm view react-native-mask-segment-canvas version 2>/dev/null || echo "0.0.0")
|
|
||||||
if [ "$PKG_VERSION" = "$NPM_VERSION" ]; then
|
|
||||||
echo "Error: version $PKG_VERSION already exists on npm. Please bump the version in package.json."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Publishing version $PKG_VERSION (npm latest: $NPM_VERSION)"
|
|
||||||
|
|
||||||
- name: Publish to npm
|
|
||||||
run: npm publish --access public
|
|
||||||
env:
|
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
||||||
22
.gitignore
vendored
@ -73,25 +73,3 @@ yarn-error.log
|
|||||||
!.yarn/releases
|
!.yarn/releases
|
||||||
!.yarn/sdks
|
!.yarn/sdks
|
||||||
!.yarn/versions
|
!.yarn/versions
|
||||||
|
|
||||||
# Example app (monorepo integration demo)
|
|
||||||
example/node_modules/
|
|
||||||
example/android/.gradle/
|
|
||||||
example/android/app/.cxx/
|
|
||||||
example/android/app/build/
|
|
||||||
example/android/build/
|
|
||||||
example/ios/Pods/
|
|
||||||
example/ios/build/
|
|
||||||
example/ios/DerivedData/
|
|
||||||
|
|
||||||
# IDE / editor
|
|
||||||
.cursor/
|
|
||||||
.idea/
|
|
||||||
*.swp
|
|
||||||
*~
|
|
||||||
|
|
||||||
# Logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
|
|||||||
35
.npmignore
@ -1,35 +0,0 @@
|
|||||||
# Demo app
|
|
||||||
example/
|
|
||||||
App.tsx
|
|
||||||
index.js
|
|
||||||
app.json
|
|
||||||
metro.config.js
|
|
||||||
babel.config.js
|
|
||||||
|
|
||||||
# Tests
|
|
||||||
__tests__/
|
|
||||||
coverage/
|
|
||||||
|
|
||||||
# Native demo projects
|
|
||||||
ios/
|
|
||||||
android/
|
|
||||||
|
|
||||||
# Config files (not needed in published package)
|
|
||||||
tsconfig.json
|
|
||||||
tsconfig.build.json
|
|
||||||
.eslintrc.js
|
|
||||||
.prettierrc.js
|
|
||||||
|
|
||||||
# Development
|
|
||||||
.cursor/
|
|
||||||
agent-transcripts/
|
|
||||||
assets/
|
|
||||||
scripts/
|
|
||||||
|
|
||||||
# Dependencies
|
|
||||||
node_modules/
|
|
||||||
package-lock.json
|
|
||||||
|
|
||||||
# OS
|
|
||||||
.DS_Store
|
|
||||||
*.log
|
|
||||||
290
App.tsx
@ -1,220 +1,130 @@
|
|||||||
/**
|
/**
|
||||||
* Mask Segment Demo App
|
* Sample React Native App
|
||||||
* Mask segmentation canvas based on OpenCV + Skia
|
* https://github.com/facebook/react-native
|
||||||
*
|
*
|
||||||
* Note: This is a demo for library development testing, directly referencing ./src.
|
* @format
|
||||||
* 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 from 'react';
|
||||||
|
import type {PropsWithChildren} from 'react';
|
||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
ScrollView,
|
||||||
StatusBar,
|
StatusBar,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
Text,
|
Text,
|
||||||
|
useColorScheme,
|
||||||
View,
|
View,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
|
|
||||||
import MaskSegmentCanvas, {
|
|
||||||
type MaskSegmentCanvasRef,
|
|
||||||
type MaskSegmentSession,
|
|
||||||
type MaskSegmentWatchState,
|
|
||||||
MASK_SEMANTIC_COLORS,
|
|
||||||
} from './src';
|
|
||||||
import { resolveAssetPath } from './src/utils/resolveAssetPath';
|
|
||||||
import { prewarmPngBgrCacheAsync } from './src/utils/pngImage';
|
|
||||||
|
|
||||||
const TEST_ORIGIN = require('./assets/test/origin.png');
|
import {
|
||||||
const TEST_MASK = require('./assets/test/mask.png');
|
Colors,
|
||||||
|
DebugInstructions,
|
||||||
|
Header,
|
||||||
|
LearnMoreLinks,
|
||||||
|
ReloadInstructions,
|
||||||
|
} from 'react-native/Libraries/NewAppScreen';
|
||||||
|
|
||||||
const INTERACTIVE_WATCH_STATES: MaskSegmentWatchState[] = [
|
type SectionProps = PropsWithChildren<{
|
||||||
'interactive',
|
title: string;
|
||||||
'mask_paths_ready',
|
}>;
|
||||||
];
|
|
||||||
|
|
||||||
function formatWatchStatus(state: MaskSegmentWatchState | ''): string {
|
function Section({children, title}: SectionProps): React.JSX.Element {
|
||||||
if (!state) {
|
const isDarkMode = useColorScheme() === 'dark';
|
||||||
return '';
|
return (
|
||||||
}
|
<View style={styles.sectionContainer}>
|
||||||
if (state === 'interactive') {
|
<Text
|
||||||
return 'Colorable (outline loading...)';
|
style={[
|
||||||
}
|
styles.sectionTitle,
|
||||||
if (state === 'mask_paths_ready') {
|
{
|
||||||
return 'Ready';
|
color: isDarkMode ? Colors.white : Colors.black,
|
||||||
}
|
},
|
||||||
if (state === 'error') {
|
]}>
|
||||||
return 'Failed';
|
{title}
|
||||||
}
|
</Text>
|
||||||
return `Loading: ${state}`;
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.sectionDescription,
|
||||||
|
{
|
||||||
|
color: isDarkMode ? Colors.light : Colors.dark,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
{children}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function App(): React.JSX.Element {
|
function App(): React.JSX.Element {
|
||||||
const canvasRef = useRef<MaskSegmentCanvasRef>(null);
|
const isDarkMode = useColorScheme() === 'dark';
|
||||||
const [testPaths, setTestPaths] = useState<{
|
|
||||||
origin: string;
|
|
||||||
mask: string;
|
|
||||||
} | null>(null);
|
|
||||||
const [loadError, setLoadError] = useState('');
|
|
||||||
const [watchState, setWatchState] = useState<MaskSegmentWatchState | ''>('');
|
|
||||||
const [sessionDraft, setSessionDraft] = useState<MaskSegmentSession | null>(
|
|
||||||
null,
|
|
||||||
);
|
|
||||||
const isFullyReady = watchState === 'mask_paths_ready';
|
|
||||||
const isInitLoading =
|
|
||||||
testPaths != null &&
|
|
||||||
watchState !== '' &&
|
|
||||||
!INTERACTIVE_WATCH_STATES.includes(watchState as MaskSegmentWatchState) &&
|
|
||||||
watchState !== 'error';
|
|
||||||
|
|
||||||
useEffect(() => {
|
const backgroundStyle = {
|
||||||
let cancelled = false;
|
backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
|
||||||
|
};
|
||||||
|
|
||||||
(async () => {
|
/*
|
||||||
try {
|
* To keep the template simple and small we're adding padding to prevent view
|
||||||
const [origin, mask] = await Promise.all([
|
* from rendering under the System UI.
|
||||||
resolveAssetPath(TEST_ORIGIN, 'gym_test_origin.png'),
|
* For bigger apps the recommendation is to use `react-native-safe-area-context`:
|
||||||
resolveAssetPath(TEST_MASK, 'gym_test_mask.png'),
|
* https://github.com/AppAndFlow/react-native-safe-area-context
|
||||||
]);
|
*
|
||||||
await prewarmPngBgrCacheAsync([origin, mask]);
|
* You can read more about it here:
|
||||||
if (!cancelled) {
|
* https://github.com/react-native-community/discussions-and-proposals/discussions/827
|
||||||
setTestPaths({ origin, mask });
|
*/
|
||||||
}
|
const safePadding = '5%';
|
||||||
} catch (e) {
|
|
||||||
if (!cancelled) {
|
|
||||||
setLoadError(e instanceof Error ? e.message : String(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
cancelled = true;
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaProvider>
|
<View style={backgroundStyle}>
|
||||||
<SafeAreaView style={styles.root}>
|
<StatusBar
|
||||||
<StatusBar barStyle="dark-content" backgroundColor="#fff" />
|
barStyle={isDarkMode ? 'light-content' : 'dark-content'}
|
||||||
{loadError ? (
|
backgroundColor={backgroundStyle.backgroundColor}
|
||||||
<View style={styles.center}>
|
/>
|
||||||
<Text style={styles.errorText}>{loadError}</Text>
|
<ScrollView
|
||||||
</View>
|
style={backgroundStyle}>
|
||||||
) : testPaths ? (
|
<View style={{paddingRight: safePadding}}>
|
||||||
<>
|
<Header/>
|
||||||
{watchState ? (
|
</View>
|
||||||
<Text style={styles.watchText}>
|
<View
|
||||||
Status: {formatWatchStatus(watchState)}
|
style={{
|
||||||
{isFullyReady ? ' · Carousel dashed line ready' : null}
|
backgroundColor: isDarkMode ? Colors.black : Colors.white,
|
||||||
</Text>
|
paddingHorizontal: safePadding,
|
||||||
) : null}
|
paddingBottom: safePadding,
|
||||||
<View style={styles.canvasHost}>
|
}}>
|
||||||
<MaskSegmentCanvas
|
<Section title="Step One">
|
||||||
ref={canvasRef}
|
Edit <Text style={styles.highlight}>App.tsx</Text> to change this
|
||||||
originUrl={testPaths.origin}
|
screen and then come back to see your edits.
|
||||||
maskUrl={testPaths.mask}
|
</Section>
|
||||||
semanticColors={MASK_SEMANTIC_COLORS}
|
<Section title="See Your Changes">
|
||||||
regionOutlineColor="rgba(20, 120, 235, 0.58)"
|
<ReloadInstructions />
|
||||||
showDebugPickers
|
</Section>
|
||||||
initialSession={sessionDraft ?? undefined}
|
<Section title="Debug">
|
||||||
onWatch={(state, durationMs, detail) => {
|
<DebugInstructions />
|
||||||
setWatchState(state);
|
</Section>
|
||||||
if (__DEV__) {
|
<Section title="Learn More">
|
||||||
const extra =
|
Read the docs to discover what to do next:
|
||||||
detail && Object.keys(detail).length > 0
|
</Section>
|
||||||
? ` ${JSON.stringify(detail)}`
|
<LearnMoreLinks />
|
||||||
: '';
|
</View>
|
||||||
console.log(
|
</ScrollView>
|
||||||
`[Demo onWatch] ${state} ${durationMs.toFixed(0)}ms${extra}`,
|
</View>
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onPaintCallback={payload => {
|
|
||||||
if (__DEV__) {
|
|
||||||
if (payload.kind === 'brush_required') {
|
|
||||||
console.log('[Demo onPaint]', payload.hint, payload.regionName);
|
|
||||||
} else {
|
|
||||||
console.log('[Demo onPaint]', payload.regionName, payload.color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onError={message => {
|
|
||||||
setLoadError(message);
|
|
||||||
setWatchState('error');
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{isInitLoading ? (
|
|
||||||
<View style={styles.initOverlay} pointerEvents="none">
|
|
||||||
<ActivityIndicator size="small" color="#333" />
|
|
||||||
<Text style={styles.initOverlayText}>
|
|
||||||
{formatWatchStatus(watchState)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
) : null}
|
|
||||||
</View>
|
|
||||||
{sessionDraft ? (
|
|
||||||
<Text style={styles.sessionText}>
|
|
||||||
Restored MMKV draft ({sessionDraft.painted.length} regions)
|
|
||||||
</Text>
|
|
||||||
) : null}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<View style={styles.center}>
|
|
||||||
<ActivityIndicator size="large" color="#333" />
|
|
||||||
<Text style={styles.loadingText}>Loading gym test image...</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</SafeAreaView>
|
|
||||||
</SafeAreaProvider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
root: {
|
sectionContainer: {
|
||||||
flex: 1,
|
marginTop: 32,
|
||||||
backgroundColor: '#fff',
|
paddingHorizontal: 24,
|
||||||
},
|
},
|
||||||
center: {
|
sectionTitle: {
|
||||||
flex: 1,
|
fontSize: 24,
|
||||||
alignItems: 'center',
|
fontWeight: '600',
|
||||||
justifyContent: 'center',
|
|
||||||
padding: 24,
|
|
||||||
},
|
},
|
||||||
loadingText: {
|
sectionDescription: {
|
||||||
marginTop: 12,
|
marginTop: 8,
|
||||||
color: '#666',
|
fontSize: 18,
|
||||||
fontSize: 14,
|
fontWeight: '400',
|
||||||
},
|
},
|
||||||
errorText: {
|
highlight: {
|
||||||
color: '#c33',
|
fontWeight: '700',
|
||||||
fontSize: 14,
|
|
||||||
textAlign: 'center',
|
|
||||||
},
|
|
||||||
watchText: {
|
|
||||||
paddingHorizontal: 12,
|
|
||||||
paddingTop: 8,
|
|
||||||
color: '#555',
|
|
||||||
fontSize: 12,
|
|
||||||
},
|
|
||||||
canvasHost: {
|
|
||||||
flex: 1,
|
|
||||||
position: 'relative',
|
|
||||||
},
|
|
||||||
initOverlay: {
|
|
||||||
...StyleSheet.absoluteFillObject,
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
backgroundColor: 'rgba(255, 255, 255, 0.72)',
|
|
||||||
gap: 8,
|
|
||||||
},
|
|
||||||
initOverlayText: {
|
|
||||||
color: '#666',
|
|
||||||
fontSize: 13,
|
|
||||||
},
|
|
||||||
sessionText: {
|
|
||||||
paddingHorizontal: 12,
|
|
||||||
paddingBottom: 8,
|
|
||||||
color: '#2a7',
|
|
||||||
fontSize: 12,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -2,80 +2,12 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { MaskSegmentSession } from '../src/components/MaskSegmentCanvas.types';
|
import React from 'react';
|
||||||
import { createRuntimeConfig } from '../src/utils/maskSegmentRuntime';
|
import ReactTestRenderer from 'react-test-renderer';
|
||||||
|
import App from '../App';
|
||||||
|
|
||||||
jest.mock('react-native', () => ({
|
test('renders correctly', async () => {
|
||||||
View: 'View',
|
await ReactTestRenderer.act(() => {
|
||||||
Text: 'Text',
|
ReactTestRenderer.create(<App />);
|
||||||
StyleSheet: { create: (s: object) => s, hairlineWidth: 1 },
|
|
||||||
ActivityIndicator: 'ActivityIndicator',
|
|
||||||
StatusBar: 'StatusIndicator',
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('react-native-safe-area-context', () => ({
|
|
||||||
SafeAreaProvider: ({ children }: { children: unknown }) => children,
|
|
||||||
SafeAreaView: ({ children }: { children: unknown }) => children,
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('../src/components/MaskSegmentCanvas', () => {
|
|
||||||
const React = require('react');
|
|
||||||
const { forwardRef } = React;
|
|
||||||
return {
|
|
||||||
__esModule: true,
|
|
||||||
default: forwardRef(() => null),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
jest.mock('../src/utils/pngImage', () => ({
|
|
||||||
prewarmPngBgrCacheAsync: jest.fn(() => Promise.resolve()),
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('../src/utils/resolveAssetPath', () => ({
|
|
||||||
resolveAssetPath: jest.fn((_asset: number, name: string) =>
|
|
||||||
Promise.resolve(`file:///mock/${name}`),
|
|
||||||
),
|
|
||||||
}));
|
|
||||||
|
|
||||||
test('session round-trip shape', () => {
|
|
||||||
const session: MaskSegmentSession = {
|
|
||||||
version: 1,
|
|
||||||
originUrl: 'file:///mock/origin.png',
|
|
||||||
maskUrl: 'file:///mock/mask.png',
|
|
||||||
painted: [
|
|
||||||
{
|
|
||||||
regionId: 0,
|
|
||||||
regionName: 'wall',
|
|
||||||
color: { b: 100, g: 120, r: 140 },
|
|
||||||
configJson: { sku: 'paint-001' },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
paintHistory: [0],
|
|
||||||
currentColor: { b: 100, g: 120, r: 140 },
|
|
||||||
currentColorConfigJson: { sku: 'paint-001' },
|
|
||||||
savedAt: Date.now(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const restored = JSON.parse(JSON.stringify(session)) as MaskSegmentSession;
|
|
||||||
expect(restored.version).toBe(1);
|
|
||||||
expect(restored.painted).toHaveLength(1);
|
|
||||||
expect(restored.painted[0].configJson).toEqual({ sku: 'paint-001' });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('runtime config defaults', () => {
|
|
||||||
const runtime = createRuntimeConfig();
|
|
||||||
expect(runtime.pipeline.maxImageLongSide).toBe(720);
|
|
||||||
expect(runtime.paint.palette).toHaveLength(6);
|
|
||||||
expect(runtime.mask.semanticColors.length).toBeGreaterThan(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('renders app shell', async () => {
|
|
||||||
const React = require('react');
|
|
||||||
const ReactTestRenderer = require('react-test-renderer');
|
|
||||||
const App = require('../App').default;
|
|
||||||
|
|
||||||
await ReactTestRenderer.act(async () => {
|
|
||||||
ReactTestRenderer.create(React.createElement(App));
|
|
||||||
await Promise.resolve();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,79 +0,0 @@
|
|||||||
jest.mock('../src/utils/opencvAdapter', () => ({
|
|
||||||
__esModule: true,
|
|
||||||
default: {},
|
|
||||||
}));
|
|
||||||
|
|
||||||
import {
|
|
||||||
buildPaintColorMapImage,
|
|
||||||
} from '../src/utils/paintColorMapTexture';
|
|
||||||
import {
|
|
||||||
bgrBufferToRgbaBuffer,
|
|
||||||
bgrToLabL,
|
|
||||||
prepareFreqLayersFromBgrBuffer,
|
|
||||||
} from '../src/utils/freqLayerPrep';
|
|
||||||
import { getRegionPaintEffect } from '../src/utils/paintShaderRuntime';
|
|
||||||
|
|
||||||
jest.mock('@shopify/react-native-skia', () => {
|
|
||||||
const makeImage = jest.fn(() => ({
|
|
||||||
dispose: jest.fn(),
|
|
||||||
width: () => 2,
|
|
||||||
height: () => 2,
|
|
||||||
}));
|
|
||||||
return {
|
|
||||||
Skia: {
|
|
||||||
Data: { fromBytes: jest.fn(() => ({})) },
|
|
||||||
Image: { MakeImage: makeImage },
|
|
||||||
RuntimeEffect: {
|
|
||||||
Make: jest.fn(() => ({
|
|
||||||
getUniformCount: () => 4,
|
|
||||||
getUniformName: (i: number) =>
|
|
||||||
['colorBaseOpacity', 'lLightOpacity', 'textureOpacity', 'showOrigin'][
|
|
||||||
i
|
|
||||||
],
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
AlphaType: { Opaque: 1, Unpremul: 3 },
|
|
||||||
ColorType: { RGBA_8888: 4 },
|
|
||||||
TileMode: { Clamp: 0 },
|
|
||||||
ImageFormat: { PNG: 4 },
|
|
||||||
Canvas: 'Canvas',
|
|
||||||
Fill: 'Fill',
|
|
||||||
Shader: 'Shader',
|
|
||||||
ImageShader: 'ImageShader',
|
|
||||||
Group: 'Group',
|
|
||||||
drawAsImage: jest.fn(),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
test('bgrToLabL returns clamped 0-255', () => {
|
|
||||||
expect(bgrToLabL(0, 0, 0)).toBeGreaterThanOrEqual(0);
|
|
||||||
expect(bgrToLabL(255, 255, 255)).toBeLessThanOrEqual(255);
|
|
||||||
expect(bgrToLabL(128, 128, 128)).toBeGreaterThan(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('bgrBufferToRgbaBuffer swaps BGR to RGBA', () => {
|
|
||||||
const bgr = new Uint8Array([10, 20, 30]);
|
|
||||||
const rgba = bgrBufferToRgbaBuffer(bgr, 1, 1);
|
|
||||||
expect(Array.from(rgba)).toEqual([30, 20, 10, 255]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('buildPaintColorMapImage marks painted pixels', () => {
|
|
||||||
const pick = new Uint8Array([0, 1, 2]);
|
|
||||||
const painted = new Map([
|
|
||||||
[0, { b: 1, g: 2, r: 3 }],
|
|
||||||
[1, { b: 4, g: 5, r: 6 }],
|
|
||||||
]);
|
|
||||||
const image = buildPaintColorMapImage(pick, 3, 1, painted);
|
|
||||||
expect(image).toBeTruthy();
|
|
||||||
expect(require('@shopify/react-native-skia').Skia.Image.MakeImage).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('prepareFreqLayersFromBgrBuffer rejects invalid buffer size', async () => {
|
|
||||||
await expect(prepareFreqLayersFromBgrBuffer(new Uint8Array(2), 1, 1)).resolves.toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('regionPaint SkSL compiles via RuntimeEffect', () => {
|
|
||||||
const effect = getRegionPaintEffect();
|
|
||||||
expect(effect.getUniformCount()).toBe(4);
|
|
||||||
});
|
|
||||||
@ -1,198 +0,0 @@
|
|||||||
jest.mock('../src/utils/opencvAdapter', () => ({
|
|
||||||
__esModule: true,
|
|
||||||
default: {},
|
|
||||||
}));
|
|
||||||
|
|
||||||
import {
|
|
||||||
resetMaskSegmentRuntimeConfig,
|
|
||||||
setMaskSegmentRuntimeConfig,
|
|
||||||
} from '../src/utils/maskSegmentRuntime';
|
|
||||||
import type { SegmentMaskResult } from '../src/utils/maskSegmentation';
|
|
||||||
import { splitWallRegionsByTexture } from '../src/utils/wallTextureSplit';
|
|
||||||
|
|
||||||
const WALL_IDX = 3;
|
|
||||||
const IGNORE = 255;
|
|
||||||
|
|
||||||
function buildSyntheticWallResult(
|
|
||||||
cols: number,
|
|
||||||
rows: number,
|
|
||||||
): SegmentMaskResult {
|
|
||||||
const pixelCount = cols * rows;
|
|
||||||
const labels = new Uint8Array(pixelCount);
|
|
||||||
labels.fill(WALL_IDX);
|
|
||||||
const baseboardBinary = new Uint8Array(pixelCount);
|
|
||||||
const pick = new Uint8Array(pixelCount);
|
|
||||||
pick.fill(1);
|
|
||||||
|
|
||||||
return {
|
|
||||||
regions: [
|
|
||||||
{
|
|
||||||
id: 0,
|
|
||||||
name: 'wall',
|
|
||||||
hex: '#4363D8',
|
|
||||||
color: { b: 216, g: 99, r: 67 },
|
|
||||||
polygons: [
|
|
||||||
[
|
|
||||||
{ x: 0, y: 0 },
|
|
||||||
{ x: cols, y: 0 },
|
|
||||||
{ x: cols, y: rows },
|
|
||||||
{ x: 0, y: rows },
|
|
||||||
],
|
|
||||||
],
|
|
||||||
bbox: { x: 0, y: 0, w: cols, h: rows },
|
|
||||||
area: pixelCount,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
pickMap: { buffer: pick, cols, rows },
|
|
||||||
labels,
|
|
||||||
baseboardBinary,
|
|
||||||
segCols: cols,
|
|
||||||
segRows: rows,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildSplitOrigin(
|
|
||||||
cols: number,
|
|
||||||
rows: number,
|
|
||||||
leftBgr: [number, number, number],
|
|
||||||
rightBgr: [number, number, number],
|
|
||||||
): Uint8Array {
|
|
||||||
const buffer = new Uint8Array(cols * rows * 3);
|
|
||||||
const mid = Math.floor(cols / 2);
|
|
||||||
for (let y = 0; y < rows; y++) {
|
|
||||||
for (let x = 0; x < cols; x++) {
|
|
||||||
const [b, g, r] = x < mid ? leftBgr : rightBgr;
|
|
||||||
const i = (y * cols + x) * 3;
|
|
||||||
buffer[i] = b;
|
|
||||||
buffer[i + 1] = g;
|
|
||||||
buffer[i + 2] = r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
function pickRegionIdAt(
|
|
||||||
pick: Uint8Array,
|
|
||||||
cols: number,
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
): number | null {
|
|
||||||
const code = pick[y * cols + x];
|
|
||||||
return code > 0 ? code - 1 : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
resetMaskSegmentRuntimeConfig();
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
resetMaskSegmentRuntimeConfig();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('splitWalls:false leaves regions unchanged', () => {
|
|
||||||
const cols = 24;
|
|
||||||
const rows = 12;
|
|
||||||
const base = buildSyntheticWallResult(cols, rows);
|
|
||||||
const origin = buildSplitOrigin(cols, rows, [8, 8, 8], [240, 240, 240]);
|
|
||||||
|
|
||||||
setMaskSegmentRuntimeConfig({ maskConfig: { splitWalls: false } });
|
|
||||||
const result = splitWallRegionsByTexture(base, origin, cols, rows, 10);
|
|
||||||
|
|
||||||
expect(result.regions.find(r => r.name === 'wall')).toBeTruthy();
|
|
||||||
expect(result.regions.some(r => /^wall-\d+$/.test(r.name))).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('white wall and blue wall split into separate regions', () => {
|
|
||||||
const cols = 32;
|
|
||||||
const rows = 16;
|
|
||||||
const base = buildSyntheticWallResult(cols, rows);
|
|
||||||
const origin = buildSplitOrigin(cols, rows, [245, 245, 245], [180, 120, 60]);
|
|
||||||
|
|
||||||
setMaskSegmentRuntimeConfig({
|
|
||||||
maskConfig: { splitWalls: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = splitWallRegionsByTexture(base, origin, cols, rows, 10);
|
|
||||||
const wallSubs = result.regions.filter(r => /^wall-\d+$/.test(r.name));
|
|
||||||
expect(wallSubs.length).toBeGreaterThanOrEqual(2);
|
|
||||||
|
|
||||||
const pick = result.pickMap.buffer;
|
|
||||||
const leftId = pickRegionIdAt(pick, cols, 4, rows >> 1);
|
|
||||||
const rightId = pickRegionIdAt(pick, cols, cols - 4, rows >> 1);
|
|
||||||
expect(leftId).not.toBeNull();
|
|
||||||
expect(rightId).not.toBeNull();
|
|
||||||
expect(leftId).not.toBe(rightId);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('splitWalls:true splits wall into wall-1 and wall-2 by chroma', () => {
|
|
||||||
const cols = 24;
|
|
||||||
const rows = 12;
|
|
||||||
const base = buildSyntheticWallResult(cols, rows);
|
|
||||||
// Left blue, right warm (obvious chroma difference)
|
|
||||||
const origin = buildSplitOrigin(cols, rows, [220, 50, 30], [50, 200, 240]);
|
|
||||||
|
|
||||||
setMaskSegmentRuntimeConfig({
|
|
||||||
maskConfig: {
|
|
||||||
splitWalls: true,
|
|
||||||
splitWallsMinAreaRatio: 0.001,
|
|
||||||
splitWallsColorDistSq: 400,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = splitWallRegionsByTexture(base, origin, cols, rows, 10);
|
|
||||||
|
|
||||||
const wallSubs = result.regions.filter(r => /^wall-\d+$/.test(r.name));
|
|
||||||
expect(wallSubs.length).toBeGreaterThanOrEqual(2);
|
|
||||||
expect(result.regions.some(r => r.name === 'wall')).toBe(false);
|
|
||||||
expect(result.wallSubLabels).toBeDefined();
|
|
||||||
|
|
||||||
const pick = result.pickMap.buffer;
|
|
||||||
const leftId = pickRegionIdAt(pick, cols, 4, rows >> 1);
|
|
||||||
const rightId = pickRegionIdAt(pick, cols, cols - 4, rows >> 1);
|
|
||||||
expect(leftId).not.toBeNull();
|
|
||||||
expect(rightId).not.toBeNull();
|
|
||||||
expect(leftId).not.toBe(rightId);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('same-color wall with lighting gradient stays one region', () => {
|
|
||||||
const cols = 32;
|
|
||||||
const rows = 16;
|
|
||||||
const base = buildSyntheticWallResult(cols, rows);
|
|
||||||
const origin = new Uint8Array(cols * rows * 3);
|
|
||||||
for (let y = 0; y < rows; y++) {
|
|
||||||
for (let x = 0; x < cols; x++) {
|
|
||||||
const t = x / Math.max(1, cols - 1);
|
|
||||||
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 + 1] = Math.round(70 + t * 50);
|
|
||||||
origin[i + 2] = Math.round(40 + t * 30);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setMaskSegmentRuntimeConfig({
|
|
||||||
maskConfig: { splitWalls: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = splitWallRegionsByTexture(base, origin, cols, rows, 10);
|
|
||||||
const wallSubs = result.regions.filter(r => /^wall-\d+$/.test(r.name));
|
|
||||||
expect(wallSubs).toHaveLength(1);
|
|
||||||
expect(wallSubs[0].name).toBe('wall-1');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('single-texture wall becomes wall-1 when splitWalls enabled', () => {
|
|
||||||
const cols = 16;
|
|
||||||
const rows = 8;
|
|
||||||
const base = buildSyntheticWallResult(cols, rows);
|
|
||||||
const origin = buildSplitOrigin(cols, rows, [120, 120, 120], [120, 120, 120]);
|
|
||||||
|
|
||||||
setMaskSegmentRuntimeConfig({
|
|
||||||
maskConfig: { splitWalls: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = splitWallRegionsByTexture(base, origin, cols, rows, 10);
|
|
||||||
|
|
||||||
const wallSubs = result.regions.filter(r => /^wall-\d+$/.test(r.name));
|
|
||||||
expect(wallSubs).toHaveLength(1);
|
|
||||||
expect(wallSubs[0].name).toBe('wall-1');
|
|
||||||
});
|
|
||||||
@ -1,8 +1,6 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".MainApplication"
|
android:name=".MainApplication"
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 695 KiB |
|
Before Width: | Height: | Size: 1.5 MiB |
@ -1,4 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: ['module:@react-native/babel-preset'],
|
presets: ['module:@react-native/babel-preset'],
|
||||||
plugins: ['react-native-reanimated/plugin'],
|
|
||||||
};
|
};
|
||||||
|
|||||||
5
dist/components/MaskSegmentCanvas.d.ts
vendored
@ -1,5 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import type { MaskSegmentCanvasProps, MaskSegmentCanvasRef } from './MaskSegmentCanvas.types';
|
|
||||||
export type { MaskSegmentCanvasProps, MaskSegmentCanvasRef, MaskSegmentSession, MaskSegmentWatchState, PaintedRegionRecord, BgrColor, MaskSemanticColor, } from './MaskSegmentCanvas.types';
|
|
||||||
declare const MaskSegmentCanvas: React.ForwardRefExoticComponent<MaskSegmentCanvasProps & React.RefAttributes<MaskSegmentCanvasRef>>;
|
|
||||||
export default MaskSegmentCanvas;
|
|
||||||
1
dist/components/MaskSegmentCanvas.js
vendored
178
dist/components/MaskSegmentCanvas.types.d.ts
vendored
@ -1,178 +0,0 @@
|
|||||||
import type { StyleProp, ViewStyle } from 'react-native';
|
|
||||||
import type { SegmentRegion } from '../utils/maskSegmentation';
|
|
||||||
import type { MaskSemanticColor } from '../utils/maskSemanticPalette';
|
|
||||||
export type BgrColor = {
|
|
||||||
b: number;
|
|
||||||
g: number;
|
|
||||||
r: number;
|
|
||||||
};
|
|
||||||
export type MaskSegmentWatchState = 'init' | 'images_loaded' | 'mask_aligned' | 'mask_sampled' | 'regions_ready' | 'layers_ready' | 'interactive' | 'mask_paths_ready' | 'error';
|
|
||||||
export type MaskSegmentWatchDetail = {
|
|
||||||
regionCount?: number;
|
|
||||||
maskPathsReady?: boolean;
|
|
||||||
freqLayersReady?: boolean;
|
|
||||||
errorMessage?: string;
|
|
||||||
};
|
|
||||||
export type PipelinePreset = 'high' | 'medium' | 'low';
|
|
||||||
export type PipelineConfig = {
|
|
||||||
maxImageLongSide?: number;
|
|
||||||
/** low/high frequency LAB processing longest side (can be lower than maxImageLongSide, Shader stretch sampling) */
|
|
||||||
paintFreqMaxLongSide?: number;
|
|
||||||
originPreviewMaxLongSide?: number;
|
|
||||||
maskPathMaxLongSide?: number;
|
|
||||||
minContourArea?: number;
|
|
||||||
contourApproxEpsilon?: number;
|
|
||||||
maxRegions?: number;
|
|
||||||
};
|
|
||||||
export type MaskSegmentConfig = {
|
|
||||||
semanticColors?: MaskSemanticColor[];
|
|
||||||
baseboardMaxColorDist?: number;
|
|
||||||
blackThreshold?: number;
|
|
||||||
quantStep?: number;
|
|
||||||
baseboardStripQuantKeys?: string[];
|
|
||||||
wallQuantKeys?: string[];
|
|
||||||
cabinetQuantKeys?: string[];
|
|
||||||
maxRegionColors?: number;
|
|
||||||
secondarySemanticNames?: string[];
|
|
||||||
secondaryMinPixelRatio?: number;
|
|
||||||
junctionHRadiusPx?: number;
|
|
||||||
junctionVRadiusPx?: number;
|
|
||||||
kickBridgeHalfWPx?: number;
|
|
||||||
baseboardJunctionRowMarginPx?: number;
|
|
||||||
baseboardJunctionVReachPx?: number;
|
|
||||||
baseboardMinRunPx?: number;
|
|
||||||
/** only used in wall mask, split wall into wall-1, wall-2... by texture boundary */
|
|
||||||
splitWalls?: boolean;
|
|
||||||
/** wall mask only, max number of wall sub-regions */
|
|
||||||
splitWallsMaxCount?: number;
|
|
||||||
/** wall mask only, min area ratio (relative to seg total pixels) */
|
|
||||||
splitWallsMinAreaRatio?: number;
|
|
||||||
/** wall mask only, Lab chroma + high-frequency texture feature distance squared threshold (excluding brightness, reducing shadow impact) */
|
|
||||||
splitWallsColorDistSq?: number;
|
|
||||||
/** wall mask only, chroma smoothing radius (pixels), only used for texture energy calculation */
|
|
||||||
splitWallsChromaBlurRadius?: number;
|
|
||||||
/** wall mask only, low saturation (white/gray wall) junction radius, used to force separate colored walls */
|
|
||||||
splitWallsNeutralChromaMax?: number;
|
|
||||||
};
|
|
||||||
export type PaintConfig = {
|
|
||||||
palette?: BgrColor[];
|
|
||||||
colorBaseOpacity?: number;
|
|
||||||
lLightOpacity?: number;
|
|
||||||
textureOpacity?: number;
|
|
||||||
lLowBlurKernel?: number;
|
|
||||||
lLowContrast?: number;
|
|
||||||
lLowBrightness?: number;
|
|
||||||
lHighGain?: number;
|
|
||||||
maskFeatherColor?: number;
|
|
||||||
maskFeatherTexture?: number;
|
|
||||||
regionOverlayFill?: string;
|
|
||||||
regionOutlineStrokeWidth?: number;
|
|
||||||
};
|
|
||||||
export type InteractionConfig = {
|
|
||||||
kickMaskPickRadiusPx?: number;
|
|
||||||
pickMapSearchRadiusPx?: number;
|
|
||||||
thinStripPadding?: number;
|
|
||||||
regionPadding?: number;
|
|
||||||
initRegionFlashMs?: number;
|
|
||||||
enableInitRegionFlash?: boolean;
|
|
||||||
};
|
|
||||||
export type PaintedRegionRecord = {
|
|
||||||
regionId: number;
|
|
||||||
regionName: string;
|
|
||||||
color: BgrColor;
|
|
||||||
configJson?: Record<string, unknown>;
|
|
||||||
};
|
|
||||||
export type MaskSegmentSession = {
|
|
||||||
version: 1;
|
|
||||||
originUrl: string;
|
|
||||||
maskUrl: string;
|
|
||||||
painted: PaintedRegionRecord[];
|
|
||||||
paintHistory: number[];
|
|
||||||
currentColor?: BgrColor;
|
|
||||||
currentColorConfigJson?: Record<string, unknown>;
|
|
||||||
savedAt: number;
|
|
||||||
};
|
|
||||||
export type SavePaintResult = {
|
|
||||||
filePath: string;
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
paintedCount: number;
|
|
||||||
previewPath?: string;
|
|
||||||
};
|
|
||||||
export type SavePaintOptions = {
|
|
||||||
destDir?: string;
|
|
||||||
};
|
|
||||||
export type PaintSuccessPayload = {
|
|
||||||
kind: 'painted';
|
|
||||||
regionId: number;
|
|
||||||
regionName: string;
|
|
||||||
color: BgrColor;
|
|
||||||
configJson?: Record<string, unknown>;
|
|
||||||
};
|
|
||||||
export type PaintBrushRequiredPayload = {
|
|
||||||
kind: 'brush_required';
|
|
||||||
/** brush not selected, hint text */
|
|
||||||
hint: string;
|
|
||||||
regionId: number;
|
|
||||||
regionName: string;
|
|
||||||
};
|
|
||||||
export type PaintCallbackPayload = PaintSuccessPayload | PaintBrushRequiredPayload;
|
|
||||||
export type MaskSegmentCanvasRef = {
|
|
||||||
reset: () => void;
|
|
||||||
swap: (showOrigin?: boolean) => void;
|
|
||||||
save: (options?: SavePaintOptions) => Promise<SavePaintResult>;
|
|
||||||
session: () => MaskSegmentSession;
|
|
||||||
loadSession: (session: MaskSegmentSession) => void;
|
|
||||||
setPaintColor: (color: BgrColor, configJson?: Record<string, unknown>) => void;
|
|
||||||
setMaskConfig: (config: MaskSegmentConfig) => void;
|
|
||||||
clearAllPaint: () => void;
|
|
||||||
/** Undo the most recent single coloring (paint) step. Distinct from clearAllPaint (full reset). */
|
|
||||||
undoSelection?: () => void;
|
|
||||||
resegment: () => Promise<void>;
|
|
||||||
getRegions: () => SegmentRegion[];
|
|
||||||
getPaintedRegions: () => PaintedRegionRecord[];
|
|
||||||
/** Returns the most recent auto-export or save() result, if any. */
|
|
||||||
getLastExport?: () => SavePaintResult | null;
|
|
||||||
};
|
|
||||||
export type MaskSegmentCanvasProps = {
|
|
||||||
originUrl?: string;
|
|
||||||
maskUrl?: string;
|
|
||||||
/** @deprecated use originUrl */
|
|
||||||
originImgPath?: string;
|
|
||||||
/** @deprecated use maskUrl */
|
|
||||||
maskImgPath?: string;
|
|
||||||
/** mask semantic recognition colors, initialization configuration; equivalent to maskConfig.semanticColors */
|
|
||||||
semanticColors?: MaskSemanticColor[];
|
|
||||||
/** partition dashed outline highlight color, initialization configuration; equivalent to paintConfig.regionOverlayFill */
|
|
||||||
regionOutlineColor?: string;
|
|
||||||
maskConfig?: MaskSegmentConfig;
|
|
||||||
/** Performance preset (high / medium / low). Merged with pipelineConfig overrides. */
|
|
||||||
pipelinePreset?: PipelinePreset;
|
|
||||||
pipelineConfig?: PipelineConfig;
|
|
||||||
paintConfig?: PaintConfig;
|
|
||||||
interactionConfig?: InteractionConfig;
|
|
||||||
initialSession?: MaskSegmentSession;
|
|
||||||
initialPaintColor?: BgrColor;
|
|
||||||
initialPaintConfigJson?: Record<string, unknown>;
|
|
||||||
disabled?: boolean;
|
|
||||||
style?: StyleProp<ViewStyle>;
|
|
||||||
/**
|
|
||||||
* Max container height available for this component (px). When set, the SDK
|
|
||||||
* computes canvas dimensions as a fit-contain within (screenWidth - 20, maxHeight)
|
|
||||||
* instead of using the full image aspect at full screen width.
|
|
||||||
*/
|
|
||||||
maxHeight?: number;
|
|
||||||
onWatch?: (state: MaskSegmentWatchState, durationMs: number, detail?: MaskSegmentWatchDetail) => void;
|
|
||||||
onPaintCallback?: (payload: PaintCallbackPayload) => void;
|
|
||||||
onError?: (message: string, error?: unknown) => void;
|
|
||||||
/**
|
|
||||||
* When true, once the canvas reaches a ready interactive state (segmentation complete
|
|
||||||
* + any initialSession / painted colors applied), the SDK will automatically call its
|
|
||||||
* internal save pipeline to produce the recolored result image and fire onExported.
|
|
||||||
* This moves "auto-generate After preview" capability inside the SDK.
|
|
||||||
*/
|
|
||||||
autoExportOnReady?: boolean;
|
|
||||||
/** Fired by SDK when autoExportOnReady produced a result (the recolored file). */
|
|
||||||
onExported?: (result: SavePaintResult) => void;
|
|
||||||
};
|
|
||||||
export type { SegmentRegion, MaskSemanticColor };
|
|
||||||
1
dist/components/MaskSegmentCanvas.types.js
vendored
@ -1 +0,0 @@
|
|||||||
"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);
|
|
||||||
6
dist/index.d.ts
vendored
@ -1,6 +0,0 @@
|
|||||||
export { default } from './components/MaskSegmentCanvas';
|
|
||||||
export type { BgrColor, InteractionConfig, MaskSegmentCanvasProps, MaskSegmentCanvasRef, MaskSegmentConfig, MaskSegmentSession, MaskSegmentWatchDetail, MaskSegmentWatchState, MaskSemanticColor, PaintBrushRequiredPayload, PaintCallbackPayload, PaintSuccessPayload, PaintConfig, PaintedRegionRecord, PipelineConfig, PipelinePreset, SavePaintOptions, SavePaintResult, SegmentRegion, } from './components/MaskSegmentCanvas.types';
|
|
||||||
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';
|
|
||||||
1
dist/index.js
vendored
@ -1 +0,0 @@
|
|||||||
"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");
|
|
||||||
2
dist/shaders/regionPaint.sksl.d.ts
vendored
@ -1,2 +0,0 @@
|
|||||||
/** 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";
|
|
||||||
70
dist/shaders/regionPaint.sksl.js
vendored
@ -1,70 +0,0 @@
|
|||||||
"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=`
|
|
||||||
uniform shader originTex;
|
|
||||||
uniform shader paintColorTex;
|
|
||||||
uniform shader lowFreqTex;
|
|
||||||
uniform shader highFreqTex;
|
|
||||||
|
|
||||||
uniform float colorBaseOpacity;
|
|
||||||
uniform float lLightOpacity;
|
|
||||||
uniform float textureOpacity;
|
|
||||||
uniform float showOrigin;
|
|
||||||
|
|
||||||
float luminance(half3 c) {
|
|
||||||
return dot(c, half3(0.2126, 0.7152, 0.0722));
|
|
||||||
}
|
|
||||||
|
|
||||||
half3 setLuminance(float lum, half3 base) {
|
|
||||||
float diff = lum - luminance(base);
|
|
||||||
return base + diff;
|
|
||||||
}
|
|
||||||
|
|
||||||
half3 luminosityBlend(half3 base, half3 blend) {
|
|
||||||
return setLuminance(luminance(blend), base);
|
|
||||||
}
|
|
||||||
|
|
||||||
half3 overlayBlend(half3 base, half3 blend) {
|
|
||||||
half3 low = 2.0 * base * blend;
|
|
||||||
half3 high = 1.0 - 2.0 * (1.0 - base) * (1.0 - blend);
|
|
||||||
return mix(low, high, step(half3(0.5), base));
|
|
||||||
}
|
|
||||||
|
|
||||||
half4 main(float2 coord) {
|
|
||||||
half4 origin = originTex.eval(coord);
|
|
||||||
if (showOrigin > 0.5) {
|
|
||||||
return origin;
|
|
||||||
}
|
|
||||||
|
|
||||||
half4 paintEntry = paintColorTex.eval(coord);
|
|
||||||
// The paintColorMap uses Unpremul alpha: painted pixels \u2192 (R,G,B,255),
|
|
||||||
// transparent pixels \u2192 (0,0,0,0). GPU bilinear sampling interpolates
|
|
||||||
// straight-alpha values, so at boundaries rgb = trueColor * sampled.a
|
|
||||||
// (contaminated with black from the transparent neighbour).
|
|
||||||
//
|
|
||||||
// Unpremultiply to recover the true paint color:
|
|
||||||
// trueColor = sampled.rgb / sampled.a
|
|
||||||
// This eliminates dark fringing at region boundaries.
|
|
||||||
float pa = paintEntry.a + 0.0001;
|
|
||||||
paintEntry.rgb /= pa;
|
|
||||||
|
|
||||||
// Gate sub-pixel alpha to kill residual sampling noise.
|
|
||||||
// Thresholds are deliberately low (\u22481.3\u20133.8 in byte space) because
|
|
||||||
// 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.
|
|
||||||
// Using *= preserves the smooth edge; higher-alpha samples pass through.
|
|
||||||
paintEntry.a *= smoothstep(0.005, 0.015, paintEntry.a);
|
|
||||||
|
|
||||||
half3 paintRgb = paintEntry.rgb;
|
|
||||||
half lowL = lowFreqTex.eval(coord).r;
|
|
||||||
half highL = highFreqTex.eval(coord).r;
|
|
||||||
|
|
||||||
half3 base = paintRgb * colorBaseOpacity;
|
|
||||||
half3 lit = luminosityBlend(base, half3(lowL));
|
|
||||||
half3 withLight = mix(base, lit, lLightOpacity);
|
|
||||||
half3 tex = overlayBlend(withLight, half3(highL));
|
|
||||||
half3 finalRgb = mix(withLight, tex, textureOpacity);
|
|
||||||
|
|
||||||
// Soft edge blend using the (feathered) alpha from the paint color map as coverage.
|
|
||||||
half3 blended = mix(origin.rgb, finalRgb, paintEntry.a);
|
|
||||||
return half4(blended, 1.0);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
80
dist/utils/canvasGeometry.d.ts
vendored
@ -1,80 +0,0 @@
|
|||||||
import { type SkImage } from '@shopify/react-native-skia';
|
|
||||||
import type { SegmentRegion } from './maskSegmentation';
|
|
||||||
import type { BgrColor } from '../components/MaskSegmentCanvas.types';
|
|
||||||
export type PaintResourceLayers = {
|
|
||||||
lowFreqImage: SkImage;
|
|
||||||
highFreqImage: SkImage;
|
|
||||||
};
|
|
||||||
export type ContainRect = {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
w: number;
|
|
||||||
h: number;
|
|
||||||
};
|
|
||||||
export type WorkScaledBgr = {
|
|
||||||
buffer: Uint8Array;
|
|
||||||
cols: number;
|
|
||||||
rows: number;
|
|
||||||
};
|
|
||||||
export declare function bgrColorEquals(a: BgrColor, b: BgrColor): boolean;
|
|
||||||
export declare function rectsEqual(a: ContainRect, b: ContainRect): boolean;
|
|
||||||
export declare function getContainRect(canvasW: number, canvasH: number, imgW: number, imgH: number): ContainRect;
|
|
||||||
export declare function canvasToNormalized(cx: number, cy: number, canvasW: number, canvasH: number, imgW: number, imgH: number): {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
} | null;
|
|
||||||
/** Skia matrix: pan → scale around viewport center. Matches screenToCanvasCoords. */
|
|
||||||
export declare function buildZoomPanMatrix(panX: number, panY: number, scale: number, canvasW: number, canvasH: number): import("@shopify/react-native-skia").SkMatrix;
|
|
||||||
/** Clamp pan so scaled containRect does not expose empty margins beyond the viewport. */
|
|
||||||
export declare function clampPanOffset(pan: {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
}, scale: number, canvasW: number, canvasH: number, containRect: ContainRect | null): {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* 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 declare function screenToCanvasCoords(screenX: number, screenY: number, canvasW: number, canvasH: number, zoomScale: number, panOffset: {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
}): {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
};
|
|
||||||
export declare function pointInPolygon(x: number, y: number, points: {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
}[]): boolean;
|
|
||||||
export declare function pointInPolygonWithPadding(x: number, y: number, points: {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
}[], padding: number): boolean;
|
|
||||||
export declare function getRegionHitPolygons(reg: SegmentRegion): {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
}[][];
|
|
||||||
export declare function pointHitsRegion(x: number, y: number, reg: SegmentRegion, options?: {
|
|
||||||
thinPadding?: number;
|
|
||||||
}): boolean;
|
|
||||||
export declare function pointStrictlyHitsRegion(x: number, y: number, reg: SegmentRegion): boolean;
|
|
||||||
export declare function resolveRegionHit(regions: SegmentRegion[], x: number, y: number): number | null;
|
|
||||||
export declare function pickKickRegionFromMask(normX: number, normY: number, pick: {
|
|
||||||
buffer: Uint8Array;
|
|
||||||
cols: number;
|
|
||||||
rows: number;
|
|
||||||
}, kickRegionId: number, baseboardPickMask?: Uint8Array | null, strict?: boolean): number | null;
|
|
||||||
export declare function pickKickNearStrip(normX: number, normY: number, kickReg: SegmentRegion): boolean;
|
|
||||||
export declare function lookupRegionFromPickMap(normX: number, normY: number, pick: {
|
|
||||||
buffer: Uint8Array;
|
|
||||||
cols: number;
|
|
||||||
rows: number;
|
|
||||||
}, radiusPx?: number): number | null;
|
|
||||||
export declare function releasePaintResourceLayers(layers: PaintResourceLayers | 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 timeLog(tag: string): void;
|
|
||||||
1
dist/utils/canvasGeometry.js
vendored
@ -1 +0,0 @@
|
|||||||
"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}
|
|
||||||
43
dist/utils/compositePaintedImage.d.ts
vendored
@ -1,43 +0,0 @@
|
|||||||
import type { BgrColor, SavePaintResult } from '../components/MaskSegmentCanvas.types';
|
|
||||||
import type { SkImage } from '@shopify/react-native-skia';
|
|
||||||
export type CompositePaintInput = {
|
|
||||||
originBuffer: Uint8Array;
|
|
||||||
cols: number;
|
|
||||||
rows: number;
|
|
||||||
pickBuffer: Uint8Array;
|
|
||||||
paintedRegions: Map<number, BgrColor>;
|
|
||||||
destDir?: string;
|
|
||||||
/**
|
|
||||||
* Preferred path for rich export: PNG base64 from makeImageSnapshot() — written
|
|
||||||
* directly to disk without an extra decode/re-encode round trip.
|
|
||||||
*/
|
|
||||||
exportPngBase64?: string;
|
|
||||||
/**
|
|
||||||
* Preferred path for rich export: if the caller (MaskSegmentCanvas) provides bytes
|
|
||||||
* that were produced by makeImageSnapshot() on a high-resolution Canvas rendering the
|
|
||||||
* exact same PaintShaderLayer + regionPaint SkSL at work resolution, we write them
|
|
||||||
* directly. This captures the live editor appearance (lighting + high/low-freq texture)
|
|
||||||
* without CPU pixel math and without a second declarative drawAsImage.
|
|
||||||
*/
|
|
||||||
exportPngBytes?: Uint8Array;
|
|
||||||
/**
|
|
||||||
* Fallback rich path (when no pre-captured snapshot bytes): pass the live textures
|
|
||||||
* so we can try renderPaintedImageOffscreen (drawAsImage with the shader tree).
|
|
||||||
*/
|
|
||||||
shaderTextures?: {
|
|
||||||
originImage: SkImage;
|
|
||||||
paintColorMap: SkImage;
|
|
||||||
lowFreqImage: SkImage;
|
|
||||||
highFreqImage: SkImage;
|
|
||||||
};
|
|
||||||
/** The logical size at which to render the shader tree for export (typically the work image res). */
|
|
||||||
renderWidth?: number;
|
|
||||||
renderHeight?: number;
|
|
||||||
};
|
|
||||||
/** Export painted regions as a recolored PNG.
|
|
||||||
* Priority (best to fallback):
|
|
||||||
* 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* (rebuild same PaintShaderLayer + SkSL via renderPaintedImageOffscreen / drawAsImage).
|
|
||||||
* 3. CPU per-pixel recolor (flat, no lighting/texture, last-resort fallback ensuring save never breaks).
|
|
||||||
*/
|
|
||||||
export declare function compositePaintedImage(input: CompositePaintInput): Promise<SavePaintResult>;
|
|
||||||
1
dist/utils/compositePaintedImage.js
vendored
@ -1 +0,0 @@
|
|||||||
"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}}
|
|
||||||
19
dist/utils/exportUtils.d.ts
vendored
@ -1,19 +0,0 @@
|
|||||||
import type { BgrColor } from '../components/MaskSegmentCanvas.types';
|
|
||||||
/** Stable fingerprint for painted region colors — used to reuse cached exports. */
|
|
||||||
export declare function paintedRegionsFingerprint(painted: Map<number, BgrColor>): string;
|
|
||||||
export declare function writePngBase64ToFile(filePath: string, base64: string): Promise<void>;
|
|
||||||
export declare function writePngBytesToFile(filePath: string, bytes: Uint8Array): Promise<void>;
|
|
||||||
export declare function stripFilePrefix(uri: string): string;
|
|
||||||
export declare function resolveExportResultForDestDir(cached: {
|
|
||||||
filePath: string;
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
paintedCount: number;
|
|
||||||
previewPath?: string;
|
|
||||||
}, destDir?: string): Promise<{
|
|
||||||
filePath: string;
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
paintedCount: number;
|
|
||||||
previewPath?: string;
|
|
||||||
}>;
|
|
||||||
1
dist/utils/exportUtils.js
vendored
@ -1 +0,0 @@
|
|||||||
"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}}
|
|
||||||
28
dist/utils/freqLayerPrep.d.ts
vendored
@ -1,28 +0,0 @@
|
|||||||
import { type WrappedMat } from './opencvAdapter';
|
|
||||||
import type { SkImage } from '@shopify/react-native-skia';
|
|
||||||
export type FreqLayerImages = {
|
|
||||||
lowFreqImage: SkImage;
|
|
||||||
highFreqImage: SkImage;
|
|
||||||
};
|
|
||||||
export type PaintResourceBatch = {
|
|
||||||
originImage: SkImage;
|
|
||||||
layers: FreqLayerImages;
|
|
||||||
};
|
|
||||||
/** 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;
|
|
||||||
/** BGR → 8-bit Lab (L/a/b mapped to 0–255) */
|
|
||||||
export declare function bgrToLab(b: number, g: number, r: number): {
|
|
||||||
l: number;
|
|
||||||
a: number;
|
|
||||||
b: number;
|
|
||||||
};
|
|
||||||
export declare function bgrBufferToRgbaBuffer(bgr: Uint8Array, cols: number, rows: number): Uint8Array;
|
|
||||||
export declare function releaseFreqLayerImages(layers: FreqLayerImages | null): void;
|
|
||||||
/** reuse the uploaded BGR Mat, avoid duplicate bufferToMat + JS↔native roundtrip */
|
|
||||||
export declare function prepareFreqLayersFromWorkMat(workMat: WrappedMat, cols: number, rows: number): Promise<FreqLayerImages | null>;
|
|
||||||
/** 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>;
|
|
||||||
/** @deprecated test compatibility; production path please use preparePaintResourcesFromWorkBuffer */
|
|
||||||
export declare function prepareFreqLayersFromBgrBuffer(bgrBuffer: Uint8Array, cols: number, rows: number): Promise<FreqLayerImages | null>;
|
|
||||||
/** original BGR → Skia RGBA (OpenCV cvtColor, parallel with freq) */
|
|
||||||
export declare function originBgrBufferToSkiaImage(bgrBuffer: Uint8Array, cols: number, rows: number): Promise<SkImage | null>;
|
|
||||||
1
dist/utils/freqLayerPrep.js
vendored
@ -1 +0,0 @@
|
|||||||
"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)}
|
|
||||||
17
dist/utils/maskOutlinePaths.d.ts
vendored
@ -1,17 +0,0 @@
|
|||||||
import { type SkPath } from '@shopify/react-native-skia';
|
|
||||||
import type { SegmentRegion, RegionMaskData } from './maskSegmentation';
|
|
||||||
export declare function buildRegionOutlinePathForRegion(regionId: number, regions: SegmentRegion[], maskData: RegionMaskData, rect: {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
w: number;
|
|
||||||
h: number;
|
|
||||||
}, normSeed?: {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
}): SkPath;
|
|
||||||
export declare function buildAllRegionOutlinePaths(regions: SegmentRegion[], maskData: RegionMaskData, rect: {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
w: number;
|
|
||||||
h: number;
|
|
||||||
}): Map<number, SkPath>;
|
|
||||||
1
dist/utils/maskOutlinePaths.js
vendored
42
dist/utils/maskSegmentRuntime.d.ts
vendored
@ -1,42 +0,0 @@
|
|||||||
import { type MaskSemanticColor } from './maskSemanticPalette';
|
|
||||||
import type { InteractionConfig, MaskSegmentConfig, PaintConfig, PipelineConfig, PipelinePreset } from '../components/MaskSegmentCanvas.types';
|
|
||||||
/** high */
|
|
||||||
export declare const PIPELINE_HIGH: Required<PipelineConfig>;
|
|
||||||
/** middle */
|
|
||||||
export declare const PIPELINE_MEDIUM: Required<PipelineConfig>;
|
|
||||||
/** low */
|
|
||||||
export declare const PIPELINE_LOW: Required<PipelineConfig>;
|
|
||||||
export declare const PIPELINE_PRESETS: Record<PipelinePreset, Required<PipelineConfig>>;
|
|
||||||
export declare const DEFAULT_PIPELINE_CONFIG: Required<PipelineConfig>;
|
|
||||||
export declare function resolvePipelineConfig(preset?: PipelinePreset, overrides?: PipelineConfig): Required<PipelineConfig>;
|
|
||||||
export declare const DEFAULT_PAINT_CONFIG: Required<PaintConfig>;
|
|
||||||
export declare const DEFAULT_INTERACTION_CONFIG: Required<InteractionConfig>;
|
|
||||||
export declare const DEFAULT_MASK_CONFIG: Required<Omit<MaskSegmentConfig, 'baseboardStripQuantKeys' | 'wallQuantKeys' | 'cabinetQuantKeys' | 'secondarySemanticNames'>> & {
|
|
||||||
baseboardStripQuantKeys: Set<string>;
|
|
||||||
wallQuantKeys: Set<string>;
|
|
||||||
cabinetQuantKeys: Set<string>;
|
|
||||||
secondarySemanticNames: Set<string>;
|
|
||||||
semanticColors: MaskSemanticColor[];
|
|
||||||
};
|
|
||||||
export type ResolvedMaskSegmentRuntime = {
|
|
||||||
pipeline: Required<PipelineConfig>;
|
|
||||||
mask: typeof DEFAULT_MASK_CONFIG;
|
|
||||||
paint: Required<PaintConfig>;
|
|
||||||
interaction: Required<InteractionConfig>;
|
|
||||||
};
|
|
||||||
export declare function mergeMaskConfig(partial?: MaskSegmentConfig): typeof DEFAULT_MASK_CONFIG;
|
|
||||||
export declare function createRuntimeConfig(input?: {
|
|
||||||
pipelineConfig?: PipelineConfig;
|
|
||||||
maskConfig?: MaskSegmentConfig;
|
|
||||||
paintConfig?: PaintConfig;
|
|
||||||
interactionConfig?: InteractionConfig;
|
|
||||||
}): ResolvedMaskSegmentRuntime;
|
|
||||||
export declare function getMaskRuntimeRevision(): number;
|
|
||||||
export declare function setMaskSegmentRuntimeConfig(input?: {
|
|
||||||
pipelineConfig?: PipelineConfig;
|
|
||||||
maskConfig?: MaskSegmentConfig;
|
|
||||||
paintConfig?: PaintConfig;
|
|
||||||
interactionConfig?: InteractionConfig;
|
|
||||||
}): ResolvedMaskSegmentRuntime;
|
|
||||||
export declare function getMaskSegmentRuntimeConfig(): ResolvedMaskSegmentRuntime;
|
|
||||||
export declare function resetMaskSegmentRuntimeConfig(): ResolvedMaskSegmentRuntime;
|
|
||||||
1
dist/utils/maskSegmentRuntime.js
vendored
@ -1 +0,0 @@
|
|||||||
"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}
|
|
||||||
122
dist/utils/maskSegmentation.d.ts
vendored
@ -1,122 +0,0 @@
|
|||||||
import { type WrappedMat } from './opencvAdapter';
|
|
||||||
import { type SkPath } from '@shopify/react-native-skia';
|
|
||||||
export type RegionPickMap = {
|
|
||||||
buffer: Uint8Array;
|
|
||||||
cols: number;
|
|
||||||
rows: number;
|
|
||||||
};
|
|
||||||
export type SegmentMaskResult = {
|
|
||||||
regions: SegmentRegion[];
|
|
||||||
pickMap: RegionPickMap;
|
|
||||||
labels: Uint8Array;
|
|
||||||
baseboardBinary: Uint8Array;
|
|
||||||
segCols: number;
|
|
||||||
segRows: number;
|
|
||||||
/** splitWalls only: wall pixels → sub-region index 0..N-1, non-wall is WALL_SUB_LABEL_NONE */
|
|
||||||
wallSubLabels?: Uint8Array;
|
|
||||||
};
|
|
||||||
export type SegmentRegion = {
|
|
||||||
id: number;
|
|
||||||
/** semantic partition name (door / cabinet / baseboard ...) */
|
|
||||||
name: string;
|
|
||||||
/** reference color hex */
|
|
||||||
hex: string;
|
|
||||||
/** reference color (BGR) */
|
|
||||||
color: {
|
|
||||||
b: number;
|
|
||||||
g: number;
|
|
||||||
r: number;
|
|
||||||
};
|
|
||||||
polygons: {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
}[][];
|
|
||||||
/** paint/highlight mask: strict pixel strip, no black hole filling */
|
|
||||||
maskPolygons?: {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
}[][];
|
|
||||||
hitPolygons?: {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
}[][];
|
|
||||||
outlinePolygons?: {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
}[][];
|
|
||||||
bbox: {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
w: number;
|
|
||||||
h: number;
|
|
||||||
};
|
|
||||||
area: number;
|
|
||||||
/** baseboard etc. thin strip areas, click detection needs tolerance */
|
|
||||||
thinStrip?: boolean;
|
|
||||||
};
|
|
||||||
export declare function buildRegionOutlinePolygons(reg: SegmentRegion): NormPoint[][];
|
|
||||||
import { buildAllRegionOutlinePaths, buildRegionOutlinePathForRegion } from './maskOutlinePaths';
|
|
||||||
export { buildAllRegionOutlinePaths, buildRegionOutlinePathForRegion };
|
|
||||||
/** build mask from binary mask row by row (for Skia PathBuilder) */
|
|
||||||
export declare function appendMaskBinaryToPathBuilder(binary: Uint8Array, cols: number, rows: number, rect: {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
w: number;
|
|
||||||
h: number;
|
|
||||||
}, builder: {
|
|
||||||
moveTo: (x: number, y: number) => unknown;
|
|
||||||
lineTo: (x: number, y: number) => unknown;
|
|
||||||
close: () => unknown;
|
|
||||||
}, 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: {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
w: number;
|
|
||||||
h: number;
|
|
||||||
}, builder: {
|
|
||||||
moveTo: (x: number, y: number) => unknown;
|
|
||||||
lineTo: (x: number, y: number) => unknown;
|
|
||||||
close: () => unknown;
|
|
||||||
}, minRunPx?: number): void;
|
|
||||||
export type RegionMaskData = {
|
|
||||||
labels: Uint8Array;
|
|
||||||
baseboardBinary: Uint8Array;
|
|
||||||
cols: number;
|
|
||||||
rows: number;
|
|
||||||
wallSubLabels?: Uint8Array;
|
|
||||||
};
|
|
||||||
/** 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;
|
|
||||||
/** 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: {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
w: number;
|
|
||||||
h: number;
|
|
||||||
}): Map<number, SkPath>;
|
|
||||||
type NormPoint = {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
};
|
|
||||||
export declare function buildBaseboardBinaryFromMask(buffer: Uint8Array, cols: number, rows: number): Uint8Array;
|
|
||||||
/** 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 isBaseboardMaskPixel(buffer: Uint8Array, cols: number, rows: number, x: number, y: number, baseboardBinary?: Uint8Array | null): boolean;
|
|
||||||
export { isStrictBaseboardPixel as isBaseboardPixel } from './maskSemanticPalette';
|
|
||||||
export declare function getMaskQuantKey(b: number, g: number, r: number): string;
|
|
||||||
/** @deprecated Use isBaseboardMaskPixel */
|
|
||||||
export declare function isKickPlatePixel(b: number, g: number, r: number): boolean;
|
|
||||||
export declare function extractRegionsFromMaskBuffer(buffer: Uint8Array, cols: number, rows: number, _options: {
|
|
||||||
minArea: number;
|
|
||||||
approxEpsilon: number;
|
|
||||||
}): Promise<SegmentMaskResult>;
|
|
||||||
export declare function extractRegionsFromMaskBufferSync(buffer: Uint8Array, cols: number, rows: number, _options: {
|
|
||||||
minArea: number;
|
|
||||||
approxEpsilon: number;
|
|
||||||
}): SegmentMaskResult;
|
|
||||||
/** @deprecated Use extractRegionsFromMaskBuffer */
|
|
||||||
export declare function extractRegionsFromMask(maskMat: WrappedMat, options: {
|
|
||||||
minArea: number;
|
|
||||||
approxEpsilon: number;
|
|
||||||
}): Promise<SegmentRegion[]>;
|
|
||||||
1
dist/utils/maskSegmentation.js
vendored
30
dist/utils/maskSemanticPalette.d.ts
vendored
@ -1,30 +0,0 @@
|
|||||||
export type MaskSemanticColor = {
|
|
||||||
name: string;
|
|
||||||
hex: string;
|
|
||||||
/** reference color (BGR, consistent with mask buffer channel) */
|
|
||||||
bgr: {
|
|
||||||
b: number;
|
|
||||||
g: number;
|
|
||||||
r: number;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
/** mask semantic color table (consistent with backend partition color reference) */
|
|
||||||
export declare const MASK_SEMANTIC_COLORS: MaskSemanticColor[];
|
|
||||||
export declare const BASEBOARD_SEMANTIC_NAME = "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 getSemanticColorByName(name: string): MaskSemanticColor | undefined;
|
|
||||||
/**
|
|
||||||
* 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 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>;
|
|
||||||
/** quantized color of the wall on the mask */
|
|
||||||
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 function getBaseboardStripQuantKeys(): Set<string>;
|
|
||||||
export declare function getWallQuantKeys(): Set<string>;
|
|
||||||
export declare function getCabinetQuantKeys(): Set<string>;
|
|
||||||
1
dist/utils/maskSemanticPalette.js
vendored
@ -1 +0,0 @@
|
|||||||
"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}
|
|
||||||
115
dist/utils/opencvAdapter.d.ts
vendored
@ -1,115 +0,0 @@
|
|||||||
import { DataTypes, ColorConversionCodes, ThresholdTypes, MorphShapes, MorphTypes, RetrievalModes, ContourApproximationModes, InterpolationFlags, type Mat, type PointVector } from 'react-native-fast-opencv';
|
|
||||||
import type { SkImage } from '@shopify/react-native-skia';
|
|
||||||
type Point = {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
};
|
|
||||||
type BBox = {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
};
|
|
||||||
type BgrColor = {
|
|
||||||
b: number;
|
|
||||||
g: number;
|
|
||||||
r: number;
|
|
||||||
};
|
|
||||||
export declare class WrappedMat {
|
|
||||||
readonly mat: Mat;
|
|
||||||
cols: number;
|
|
||||||
rows: number;
|
|
||||||
readonly channels: number;
|
|
||||||
constructor(mat: Mat, cols: number, rows: number, channels?: number);
|
|
||||||
release(): void;
|
|
||||||
clone(): Promise<WrappedMat>;
|
|
||||||
}
|
|
||||||
export declare class ContourWrapper {
|
|
||||||
readonly pointVector: PointVector;
|
|
||||||
constructor(pointVector: PointVector);
|
|
||||||
release(): void;
|
|
||||||
}
|
|
||||||
declare const cv: {
|
|
||||||
IMREAD_GRAYSCALE: number;
|
|
||||||
THRESH_BINARY: ThresholdTypes;
|
|
||||||
MORPH_RECT: MorphShapes;
|
|
||||||
MORPH_ELLIPSE: MorphShapes;
|
|
||||||
MORPH_OPEN: MorphTypes;
|
|
||||||
MORPH_CLOSE: MorphTypes;
|
|
||||||
RETR_EXTERNAL: RetrievalModes;
|
|
||||||
CHAIN_APPROX_SIMPLE: ContourApproximationModes;
|
|
||||||
CHAIN_APPROX_NONE: ContourApproximationModes;
|
|
||||||
COLOR_BGR2GRAY: ColorConversionCodes;
|
|
||||||
COLOR_BGR2Lab: ColorConversionCodes;
|
|
||||||
COLOR_Lab2BGR: ColorConversionCodes;
|
|
||||||
COLOR_GRAY2BGR: ColorConversionCodes;
|
|
||||||
CV_8UC1: DataTypes;
|
|
||||||
CV_16SC1: DataTypes;
|
|
||||||
INTER_LINEAR: InterpolationFlags;
|
|
||||||
INTER_NEAREST: InterpolationFlags;
|
|
||||||
ensurePngPath(path: string, cacheFileName?: string): Promise<string>;
|
|
||||||
imread(path: string, flags?: number): Promise<WrappedMat>;
|
|
||||||
createMat(cols: number, rows: number, channels?: 1 | 3 | 4): WrappedMat;
|
|
||||||
cvtColor(src: WrappedMat, code: ColorConversionCodes): Promise<WrappedMat>;
|
|
||||||
/** Three-channel color space conversion (BGR/Lab etc.) */
|
|
||||||
cvtColorBgr(src: WrappedMat, code: ColorConversionCodes): WrappedMat;
|
|
||||||
/** Grayscale Mat → 3-channel BGR (for Skia display) */
|
|
||||||
grayToBgr(src: WrappedMat): WrappedMat;
|
|
||||||
/** 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>;
|
|
||||||
/** JS binary buffer (0/255) → single-channel Mat */
|
|
||||||
binaryBufferToMat(buffer: Uint8Array, cols: number, rows: number): WrappedMat;
|
|
||||||
/** Continuous BGR buffer → 3-channel Mat */
|
|
||||||
bgrBufferToMat(buffer: Uint8Array, cols: number, rows: number): WrappedMat;
|
|
||||||
/** Write JS-side grayscale binary image to temp PGM and read back as Mat */
|
|
||||||
grayBufferToMat(gray: Uint8Array, cols: number, rows: number): Promise<WrappedMat>;
|
|
||||||
/**
|
|
||||||
* Export Mat pixels. Clone first to ensure contiguous memory, avoiding row misalignment from native matToBuffer ignoring step.
|
|
||||||
*/
|
|
||||||
matToBuffer(src: WrappedMat): {
|
|
||||||
buffer: Uint8Array;
|
|
||||||
cols: number;
|
|
||||||
rows: number;
|
|
||||||
channels: number;
|
|
||||||
};
|
|
||||||
inRangeBgr(src: WrappedMat, color: BgrColor, tolerance: number, dst: WrappedMat): Promise<void>;
|
|
||||||
resize(src: WrappedMat, dst: WrappedMat, size: {
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
}, interpolation?: InterpolationFlags): Promise<void>;
|
|
||||||
/** 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>;
|
|
||||||
/** BGR Mat → RGBA continuous buffer (for Skia direct transfer) */
|
|
||||||
matToRgbaBuffer(src: WrappedMat): Promise<{
|
|
||||||
buffer: Uint8Array;
|
|
||||||
cols: number;
|
|
||||||
rows: number;
|
|
||||||
}>;
|
|
||||||
/** BGR Mat → Skia image (bypasses low/high freq PNG encoding) */
|
|
||||||
matToSkiaImage(src: WrappedMat): Promise<SkImage | null>;
|
|
||||||
/** Single-channel grayscale Mat → Skia RGBA (bypasses BGR pseudocolor + 4-channel matToBuffer) */
|
|
||||||
grayMatToSkiaImage(src: WrappedMat): SkImage | null;
|
|
||||||
/** Continuous BGR buffer → Skia image (work-resolution origin / freq layers, reusing OpenCV decode result) */
|
|
||||||
bgrBufferToSkiaImage(buffer: Uint8Array, cols: number, rows: number): Promise<SkImage | null>;
|
|
||||||
threshold(src: WrappedMat, dst: WrappedMat, thresh: number, maxval: number, type: number): Promise<void>;
|
|
||||||
getStructuringElement(shape: MorphShapes, ksize: {
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
}): Promise<WrappedMat>;
|
|
||||||
morphologyEx(src: WrappedMat, dst: WrappedMat, op: MorphTypes, kernel: WrappedMat): Promise<void>;
|
|
||||||
findContours(image: WrappedMat, mode: RetrievalModes, method: ContourApproximationModes): Promise<ContourWrapper[]>;
|
|
||||||
contourArea(contour: ContourWrapper): Promise<number>;
|
|
||||||
boundingRect(contour: ContourWrapper): Promise<BBox>;
|
|
||||||
arcLength(contour: ContourWrapper, closed: boolean): Promise<number>;
|
|
||||||
approxPolyDP(contour: ContourWrapper, epsilon: number, closed: boolean): Promise<Point[]>;
|
|
||||||
GaussianBlur(src: WrappedMat, dst: WrappedMat, ksize: {
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
}, sigma: number): Promise<void>;
|
|
||||||
extractChannel(src: WrappedMat, dst: WrappedMat, channel: number): void;
|
|
||||||
convertTo(src: WrappedMat, dst: WrappedMat, rtype: number, alpha?: number, beta?: number): void;
|
|
||||||
subtract(src1: WrappedMat, src2: WrappedMat, dst: WrappedMat): Promise<void>;
|
|
||||||
addWeighted(src1: WrappedMat, alpha: number, src2: WrappedMat | null, beta: number, gamma: number, dst: WrappedMat): Promise<void>;
|
|
||||||
imwrite(path: string, mat: WrappedMat): Promise<void>;
|
|
||||||
};
|
|
||||||
export default cv;
|
|
||||||
4
dist/utils/opencvAdapter.js
vendored
@ -1,4 +0,0 @@
|
|||||||
"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
|
|
||||||
${e} ${r}
|
|
||||||
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;
|
|
||||||
4
dist/utils/paintColorMapTexture.d.ts
vendored
@ -1,4 +0,0 @@
|
|||||||
import { type SkImage } from '@shopify/react-native-skia';
|
|
||||||
import type { BgrColor } from '../components/MaskSegmentCanvas.types';
|
|
||||||
/** 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;
|
|
||||||
1
dist/utils/paintColorMapTexture.js
vendored
@ -1 +0,0 @@
|
|||||||
"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)}
|
|
||||||
39
dist/utils/paintShaderRuntime.d.ts
vendored
@ -1,39 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { type SkImage, type SkRuntimeEffect } from '@shopify/react-native-skia';
|
|
||||||
import type { BgrColor } from '../components/MaskSegmentCanvas.types';
|
|
||||||
export declare function getRegionPaintEffect(): SkRuntimeEffect;
|
|
||||||
export type PaintShaderTextures = {
|
|
||||||
originImage: SkImage;
|
|
||||||
paintColorMap: SkImage;
|
|
||||||
lowFreqImage: SkImage;
|
|
||||||
highFreqImage: SkImage;
|
|
||||||
};
|
|
||||||
export declare function buildPaintShaderUniforms(showOrigin: boolean): {
|
|
||||||
colorBaseOpacity: number;
|
|
||||||
lLightOpacity: number;
|
|
||||||
textureOpacity: number;
|
|
||||||
showOrigin: number;
|
|
||||||
};
|
|
||||||
export type PaintShaderLayerProps = PaintShaderTextures & {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
showOrigin?: boolean;
|
|
||||||
};
|
|
||||||
/** Full-screen paint Shader layer inside Canvas */
|
|
||||||
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 type OffscreenPaintInput = PaintShaderTextures & {
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
showOrigin?: boolean;
|
|
||||||
};
|
|
||||||
/** Offscreen-rendered shader composite matching the preview */
|
|
||||||
export declare function renderPaintedImageOffscreen(input: OffscreenPaintInput): Promise<SkImage | null>;
|
|
||||||
export declare function releasePaintShaderTextures(textures: {
|
|
||||||
originImage?: SkImage | null;
|
|
||||||
paintColorMap?: SkImage | null;
|
|
||||||
lowFreqImage?: SkImage | null;
|
|
||||||
highFreqImage?: SkImage | null;
|
|
||||||
}): void;
|
|
||||||
1
dist/utils/paintShaderRuntime.js
vendored
@ -1 +0,0 @@
|
|||||||
"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()}
|
|
||||||
3
dist/utils/pickMapTexture.d.ts
vendored
@ -1,3 +0,0 @@
|
|||||||
import { type SkImage } from '@shopify/react-native-skia';
|
|
||||||
/** pickMap pixel value regionId+1 → RGBA texture (R channel stores lookup code) */
|
|
||||||
export declare function pickBufferToSkImage(pickBuffer: Uint8Array, cols: number, rows: number): SkImage | null;
|
|
||||||
1
dist/utils/pickMapTexture.js
vendored
@ -1 +0,0 @@
|
|||||||
"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)}
|
|
||||||
48
dist/utils/pngImage.d.ts
vendored
@ -1,48 +0,0 @@
|
|||||||
import { type Mat } from 'react-native-fast-opencv';
|
|
||||||
export declare const PNG_EXT = ".png";
|
|
||||||
/** PNG compression level 0 = lossless */
|
|
||||||
export declare const PNG_COMPRESSION = 0;
|
|
||||||
export declare function normalizePath(path: string): string;
|
|
||||||
/** Skia useImage requires a URI; OpenCV / RNFS use bare paths */
|
|
||||||
export declare function toSkiaUri(path: string | null | undefined): string | null;
|
|
||||||
export declare function toPngFileName(name: string): string;
|
|
||||||
export declare function isPngPath(path: string): boolean;
|
|
||||||
/** 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>;
|
|
||||||
/** 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>;
|
|
||||||
/** Generate a stable PNG cache name from the source path */
|
|
||||||
export declare function pngCacheName(sourcePath: string, prefix: string): string;
|
|
||||||
/** Clean up segmentation/OpenCV derived cache, keep original image and mask source files */
|
|
||||||
export declare function clearDerivedImageCache(): Promise<number>;
|
|
||||||
type PngHeader = {
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
bitDepth: number;
|
|
||||||
colorType: number;
|
|
||||||
};
|
|
||||||
/** Parse PNG IHDR from base64 (without OpenCV), used as fallback when 16-bit Mat toJSValue crashes */
|
|
||||||
export declare function readPngHeaderFromBase64(base64: string): PngHeader;
|
|
||||||
/** 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;
|
|
||||||
* 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).
|
|
||||||
*
|
|
||||||
* pngHeader (optional): when toJSValue crashes due to 16-bit Mat, use PNG file header info
|
|
||||||
* for fallback conversion, bypassing native toJSValue call.
|
|
||||||
*/
|
|
||||||
export declare function ensureMat8U(srcMat: Mat, pngHeader?: PngHeader): {
|
|
||||||
mat: Mat;
|
|
||||||
extraReleaseIds: string[];
|
|
||||||
};
|
|
||||||
export type PngBgrBuffer = {
|
|
||||||
buffer: Uint8Array;
|
|
||||||
cols: number;
|
|
||||||
rows: number;
|
|
||||||
};
|
|
||||||
export declare function prewarmPngBgrCache(paths: string[]): void;
|
|
||||||
export declare function prewarmPngBgrCacheAsync(paths: string[]): Promise<void>;
|
|
||||||
export declare function pngContentCacheKey(path: string): Promise<string>;
|
|
||||||
export declare function readPngBgrBuffer(path: string): Promise<PngBgrBuffer>;
|
|
||||||
export declare function resizeBgrBuffer(buffer: Uint8Array, srcCols: number, srcRows: number, dstCols: number, dstRows: number): Uint8Array;
|
|
||||||
export {};
|
|
||||||
1
dist/utils/pngImage.js
vendored
2
dist/utils/resolveAssetPath.d.ts
vendored
@ -1,2 +0,0 @@
|
|||||||
/** Resolve a require() asset to a PNG local path (readable by OpenCV / RNFS) */
|
|
||||||
export declare function resolveAssetPath(assetModule: number, cacheFileName: string): Promise<string>;
|
|
||||||
1
dist/utils/resolveAssetPath.js
vendored
@ -1 +0,0 @@
|
|||||||
"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)}
|
|
||||||
3
dist/utils/resolveImageUrl.d.ts
vendored
@ -1,3 +0,0 @@
|
|||||||
export declare function hashUrl(url: string): string;
|
|
||||||
/** 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>;
|
|
||||||
1
dist/utils/resolveImageUrl.js
vendored
@ -1 +0,0 @@
|
|||||||
"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)}
|
|
||||||
3
dist/utils/skiaImage.d.ts
vendored
@ -1,3 +0,0 @@
|
|||||||
import { type SkImage } from '@shopify/react-native-skia';
|
|
||||||
/** 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;
|
|
||||||
1
dist/utils/skiaImage.js
vendored
@ -1 +0,0 @@
|
|||||||
"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)}
|
|
||||||
8
dist/utils/wallTextureSplit.d.ts
vendored
@ -1,8 +0,0 @@
|
|||||||
import type { SegmentMaskResult } from './maskSegmentation';
|
|
||||||
/** Placeholder value for non-wall pixels in wallSubLabels */
|
|
||||||
export declare const WALL_SUB_LABEL_NONE = 255;
|
|
||||||
/**
|
|
||||||
* 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 isWallSubRegionName(name: string): boolean;
|
|
||||||
1
dist/utils/wallTextureSplit.js
vendored
731
example/App.tsx
@ -1,731 +0,0 @@
|
|||||||
/**
|
|
||||||
* MaskSegmentCanvas business integration demo
|
|
||||||
*
|
|
||||||
* This file fully simulates the integration method of a real business project:
|
|
||||||
* - Only use public API through `import ... from 'react-native-mask-segment-canvas'`
|
|
||||||
* - Do not depend on the internal implementation of the library (do not import ../src)
|
|
||||||
* - Overlay: PNG preheating, status management, callback processing, Ref operations, draft recovery, error handling
|
|
||||||
*
|
|
||||||
* You can directly copy this file to your own React Native project as a reference.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
||||||
import {
|
|
||||||
ActivityIndicator,
|
|
||||||
Alert,
|
|
||||||
ScrollView,
|
|
||||||
StatusBar,
|
|
||||||
StyleSheet,
|
|
||||||
Text,
|
|
||||||
TouchableOpacity,
|
|
||||||
View,
|
|
||||||
} from 'react-native';
|
|
||||||
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
|
|
||||||
|
|
||||||
import MaskSegmentCanvas, {
|
|
||||||
type BgrColor,
|
|
||||||
type MaskSegmentCanvasRef,
|
|
||||||
type MaskSegmentSession,
|
|
||||||
type MaskSegmentWatchState,
|
|
||||||
type MaskSemanticColor,
|
|
||||||
type PaintCallbackPayload,
|
|
||||||
type PipelinePreset,
|
|
||||||
type SavePaintResult,
|
|
||||||
MASK_SEMANTIC_COLORS,
|
|
||||||
BASEBOARD_SEMANTIC_NAME,
|
|
||||||
DEFAULT_PIPELINE_CONFIG,
|
|
||||||
DEFAULT_MASK_CONFIG,
|
|
||||||
DEFAULT_PAINT_CONFIG,
|
|
||||||
DEFAULT_INTERACTION_CONFIG,
|
|
||||||
prewarmPngBgrCacheAsync,
|
|
||||||
resolveAssetPath,
|
|
||||||
} from 'react-native-mask-segment-canvas';
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// test images — two sets of example images, support switching
|
|
||||||
// replace your image path (file:// or http(s)://) when integrating into your business project
|
|
||||||
// ============================================================================
|
|
||||||
const TEST_IMAGE_GROUPS: Array<{
|
|
||||||
label: string;
|
|
||||||
origin: number;
|
|
||||||
mask: number;
|
|
||||||
originCacheName: string;
|
|
||||||
maskCacheName: string;
|
|
||||||
}> = [
|
|
||||||
{
|
|
||||||
label: 'picture group 1',
|
|
||||||
origin: require('./assets/origin.png'),
|
|
||||||
mask: require('./assets/mask.png'),
|
|
||||||
originCacheName: 'example_origin_g1.png',
|
|
||||||
maskCacheName: 'example_mask_g1.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'picture group 2',
|
|
||||||
origin: require('./assets/origin-1.png'),
|
|
||||||
mask: require('./assets/mask-1.png'),
|
|
||||||
originCacheName: 'example_origin_g2.png',
|
|
||||||
maskCacheName: 'example_mask_g2.png',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// custom semantic colors example (gym scene)
|
|
||||||
// ============================================================================
|
|
||||||
const GYM_CUSTOM_COLORS: MaskSemanticColor[] = [
|
|
||||||
{ name: 'wall', hex: '#4363D8', bgr: { b: 216, g: 99, r: 67 } },
|
|
||||||
{ name: 'ceiling', hex: '#3CB44B', bgr: { b: 75, g: 180, r: 60 } },
|
|
||||||
{ name: 'floor', hex: '#E6194B', bgr: { b: 75, g: 25, r: 230 } },
|
|
||||||
{ name: 'window', hex: '#F58231', bgr: { b: 49, g: 130, r: 245 } },
|
|
||||||
{ name: 'door', hex: '#911EB4', bgr: { b: 180, g: 30, r: 145 } },
|
|
||||||
{ name: 'pillar', hex: '#46F0F0', bgr: { b: 240, g: 240, r: 70 } },
|
|
||||||
];
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// preset brush colors (outside the bottom color bar, business can set through ref.setPaintColor)
|
|
||||||
// ============================================================================
|
|
||||||
const PAINT_PRESETS: Array<{ label: string; color: BgrColor }> = [
|
|
||||||
{ label: 'Ivory white', color: { b: 200, g: 230, r: 245 } },
|
|
||||||
{ label: 'Yellow', color: { b: 150, g: 220, r: 245 } },
|
|
||||||
{ label: 'Light gray', color: { b: 180, g: 180, r: 180 } },
|
|
||||||
{ label: 'Light blue', color: { b: 220, g: 200, r: 170 } },
|
|
||||||
];
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// watchState tools
|
|
||||||
// ============================================================================
|
|
||||||
const INTERACTIVE_STATES: MaskSegmentWatchState[] = [
|
|
||||||
'interactive',
|
|
||||||
'mask_paths_ready',
|
|
||||||
];
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// main page
|
|
||||||
// ============================================================================
|
|
||||||
function App(): React.JSX.Element {
|
|
||||||
const canvasRef = useRef<MaskSegmentCanvasRef>(null);
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// State
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
const [imagePaths, setImagePaths] = useState<{
|
|
||||||
origin: string;
|
|
||||||
mask: string;
|
|
||||||
} | null>(null);
|
|
||||||
const [pathsError, setPathsError] = useState('');
|
|
||||||
const [watchState, setWatchState] = useState<MaskSegmentWatchState | ''>('');
|
|
||||||
const [watchDetail, setWatchDetail] = useState<Record<string, unknown>>({});
|
|
||||||
const [errorMessage, setErrorMessage] = useState('');
|
|
||||||
const [toastMessage, setToastMessage] = useState('');
|
|
||||||
const [saveResult, setSaveResult] = useState<SavePaintResult | null>(null);
|
|
||||||
const [sessionDraft] = useState<MaskSegmentSession | null>(null);
|
|
||||||
|
|
||||||
// Demo mode
|
|
||||||
const [useCustomColors, setUseCustomColors] = useState(false);
|
|
||||||
const [splitWalls, setSplitWalls] = useState(false);
|
|
||||||
const [pipelinePreset, setPipelinePreset] = useState<PipelinePreset>('medium');
|
|
||||||
const [groupIndex, setGroupIndex] = useState(0);
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// derived state
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
const isInteractive = INTERACTIVE_STATES.includes(
|
|
||||||
watchState as MaskSegmentWatchState,
|
|
||||||
);
|
|
||||||
const isOutlineReady = watchState === 'mask_paths_ready';
|
|
||||||
const isInitLoading =
|
|
||||||
imagePaths != null &&
|
|
||||||
watchState !== '' &&
|
|
||||||
!INTERACTIVE_STATES.includes(watchState as MaskSegmentWatchState) &&
|
|
||||||
watchState !== 'error';
|
|
||||||
|
|
||||||
const semanticColors = useCustomColors ? GYM_CUSTOM_COLORS : MASK_SEMANTIC_COLORS;
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// Init: resolve test image paths (require → local PNG cache path)
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
useEffect(() => {
|
|
||||||
let cancelled = false;
|
|
||||||
|
|
||||||
void (async () => {
|
|
||||||
try {
|
|
||||||
setWatchState('');
|
|
||||||
setWatchDetail({});
|
|
||||||
setErrorMessage('');
|
|
||||||
setSaveResult(null);
|
|
||||||
setPathsError('');
|
|
||||||
setImagePaths(null);
|
|
||||||
|
|
||||||
const group = TEST_IMAGE_GROUPS[groupIndex];
|
|
||||||
const [origin, mask] = await Promise.all([
|
|
||||||
resolveAssetPath(group.origin, group.originCacheName),
|
|
||||||
resolveAssetPath(group.mask, group.maskCacheName),
|
|
||||||
]);
|
|
||||||
await prewarmPngBgrCacheAsync([origin, mask]);
|
|
||||||
if (!cancelled) {
|
|
||||||
setImagePaths({ origin, mask });
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
if (!cancelled) {
|
|
||||||
setPathsError(e instanceof Error ? e.message : String(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
cancelled = true;
|
|
||||||
};
|
|
||||||
}, [groupIndex]);
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// Toast message
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
const showToast = useCallback((msg: string) => {
|
|
||||||
setToastMessage(msg);
|
|
||||||
setTimeout(() => setToastMessage(''), 2500);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// onWatch callback
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
const handleWatch = useCallback(
|
|
||||||
(
|
|
||||||
state: MaskSegmentWatchState,
|
|
||||||
durationMs: number,
|
|
||||||
detail?: Record<string, unknown>,
|
|
||||||
) => {
|
|
||||||
setWatchState(state);
|
|
||||||
if (detail) setWatchDetail(detail);
|
|
||||||
console.log(
|
|
||||||
`[Example onWatch] ${state} ${durationMs.toFixed(0)}ms`,
|
|
||||||
detail ?? '',
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// onPaintCallback — handle paint success / brush not selected two scenarios
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
const handlePaintCallback = useCallback((payload: PaintCallbackPayload) => {
|
|
||||||
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);
|
|
||||||
console.log('[Example] Need to select a brush:', payload.regionName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// paint success
|
|
||||||
console.log(
|
|
||||||
'[Example] Paint success:',
|
|
||||||
payload.regionName,
|
|
||||||
`(${payload.regionId})`,
|
|
||||||
payload.color,
|
|
||||||
);
|
|
||||||
}, [showToast]);
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// onError callback
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
const handleError = useCallback((message: string) => {
|
|
||||||
setErrorMessage(message);
|
|
||||||
setWatchState('error');
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// Ref operations encapsulation
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
const handleSave = useCallback(async () => {
|
|
||||||
if (!isInteractive) return;
|
|
||||||
try {
|
|
||||||
const result = await canvasRef.current?.save();
|
|
||||||
if (result) {
|
|
||||||
setSaveResult(result);
|
|
||||||
Alert.alert('Save success', `Path: ${result.filePath}\nPainted ${result.paintedCount} regions`);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
Alert.alert('Save failed', e instanceof Error ? e.message : String(e));
|
|
||||||
}
|
|
||||||
}, [isInteractive]);
|
|
||||||
|
|
||||||
const handleReset = useCallback(() => canvasRef.current?.reset(), []);
|
|
||||||
const handleSwap = useCallback(() => canvasRef.current?.swap(), []);
|
|
||||||
const handleClearAll = useCallback(() => {
|
|
||||||
canvasRef.current?.clearAllPaint();
|
|
||||||
showToast('All paint cleared');
|
|
||||||
}, [showToast]);
|
|
||||||
|
|
||||||
const handleExportSession = useCallback(() => {
|
|
||||||
const session = canvasRef.current?.session();
|
|
||||||
if (session) {
|
|
||||||
console.log('[Example] Session snapshot:', JSON.stringify(session, null, 2));
|
|
||||||
Alert.alert(
|
|
||||||
'Session snapshot',
|
|
||||||
`Painted ${session.painted.length} regions\nCan be stored in MMKV / AsyncStorage to implement draft recovery`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleSetPaintColor = useCallback(
|
|
||||||
(color: BgrColor, label: string) => {
|
|
||||||
canvasRef.current?.setPaintColor(color, { preset: label });
|
|
||||||
showToast(`Selected brush: ${label}`);
|
|
||||||
},
|
|
||||||
[showToast],
|
|
||||||
);
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// render: error / loading / ready
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
if (pathsError) {
|
|
||||||
return (
|
|
||||||
<SafeAreaView style={styles.root}>
|
|
||||||
<View style={styles.center}>
|
|
||||||
<Text style={styles.errorText}>Image loading failed</Text>
|
|
||||||
<Text style={styles.errorDetail}>{pathsError}</Text>
|
|
||||||
</View>
|
|
||||||
</SafeAreaView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!imagePaths) {
|
|
||||||
return (
|
|
||||||
<SafeAreaView style={styles.root}>
|
|
||||||
<View style={styles.center}>
|
|
||||||
<ActivityIndicator size="large" color="#4363D8" />
|
|
||||||
<Text style={styles.loadingText}>Preheating PNG cache…</Text>
|
|
||||||
</View>
|
|
||||||
</SafeAreaView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SafeAreaView style={styles.root}>
|
|
||||||
<StatusBar barStyle="dark-content" backgroundColor="#fff" />
|
|
||||||
|
|
||||||
{/* top: status + mode switch */}
|
|
||||||
<View style={styles.topBar}>
|
|
||||||
<View style={styles.topBarRow}>
|
|
||||||
<Text style={styles.statusLabel}>
|
|
||||||
Status:{' '}
|
|
||||||
<Text
|
|
||||||
style={[
|
|
||||||
styles.statusValue,
|
|
||||||
isInteractive && styles.statusReady,
|
|
||||||
watchState === 'error' && styles.statusError,
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{watchState || 'Initializing…'}
|
|
||||||
</Text>
|
|
||||||
{isOutlineReady ? ' · Carousel ready' : ''}
|
|
||||||
{isInteractive && !isOutlineReady ? ' · Outline loading' : ''}
|
|
||||||
</Text>
|
|
||||||
<Text style={styles.regionCount}>
|
|
||||||
{watchDetail.regionCount != null
|
|
||||||
? `${watchDetail.regionCount} partitions`
|
|
||||||
: ''}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* mode switch */}
|
|
||||||
<ScrollView
|
|
||||||
horizontal
|
|
||||||
showsHorizontalScrollIndicator={false}
|
|
||||||
style={styles.modeRow}
|
|
||||||
contentContainerStyle={styles.modeRowContent}
|
|
||||||
>
|
|
||||||
<TouchableOpacity
|
|
||||||
style={[styles.modeChip, !useCustomColors && styles.modeChipActive]}
|
|
||||||
onPress={() => setUseCustomColors(false)}
|
|
||||||
>
|
|
||||||
<Text style={[styles.modeChipText, !useCustomColors && styles.modeChipTextActive]}>
|
|
||||||
Default color palette
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity
|
|
||||||
style={[styles.modeChip, useCustomColors && styles.modeChipActive]}
|
|
||||||
onPress={() => setUseCustomColors(true)}
|
|
||||||
>
|
|
||||||
<Text style={[styles.modeChipText, useCustomColors && styles.modeChipTextActive]}>
|
|
||||||
Custom color palette
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<Text style={styles.modeDivider}>|</Text>
|
|
||||||
{TEST_IMAGE_GROUPS.map((group, idx) => (
|
|
||||||
<TouchableOpacity
|
|
||||||
key={group.label}
|
|
||||||
style={[styles.modeChip, groupIndex === idx && styles.modeChipActive]}
|
|
||||||
onPress={() => setGroupIndex(idx)}
|
|
||||||
>
|
|
||||||
<Text style={[styles.modeChipText, groupIndex === idx && styles.modeChipTextActive]}>
|
|
||||||
{group.label}
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
))}
|
|
||||||
<Text style={styles.modeDivider}>|</Text>
|
|
||||||
{(['low', 'medium', 'high'] as PipelinePreset[]).map(p => (
|
|
||||||
<TouchableOpacity
|
|
||||||
key={p}
|
|
||||||
style={[styles.modeChip, pipelinePreset === p && styles.modeChipActive]}
|
|
||||||
onPress={() => setPipelinePreset(p)}
|
|
||||||
>
|
|
||||||
<Text style={[styles.modeChipText, pipelinePreset === p && styles.modeChipTextActive]}>
|
|
||||||
{p === 'low' ? 'Low precision' : p === 'medium' ? 'Medium precision' : 'High precision'}
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
))}
|
|
||||||
<Text style={styles.modeDivider}>|</Text>
|
|
||||||
<TouchableOpacity
|
|
||||||
style={[styles.modeChip, splitWalls && styles.modeChipActive]}
|
|
||||||
onPress={() => setSplitWalls(v => !v)}
|
|
||||||
>
|
|
||||||
<Text style={[styles.modeChipText, splitWalls && styles.modeChipTextActive]}>
|
|
||||||
(split walls)
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</ScrollView>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* canvas */}
|
|
||||||
<View style={styles.canvasHost}>
|
|
||||||
<MaskSegmentCanvas
|
|
||||||
key={`image-group-${groupIndex}-split-${splitWalls ? 1 : 0}`}
|
|
||||||
ref={canvasRef}
|
|
||||||
style={styles.canvas}
|
|
||||||
originUrl={imagePaths.origin}
|
|
||||||
maskUrl={imagePaths.mask}
|
|
||||||
semanticColors={semanticColors}
|
|
||||||
regionOutlineColor="rgba(20, 120, 235, 0.58)"
|
|
||||||
pipelinePreset={pipelinePreset}
|
|
||||||
maskConfig={{
|
|
||||||
...DEFAULT_MASK_CONFIG,
|
|
||||||
maxRegionColors: 6,
|
|
||||||
splitWalls,
|
|
||||||
}}
|
|
||||||
paintConfig={{
|
|
||||||
...DEFAULT_PAINT_CONFIG,
|
|
||||||
colorBaseOpacity: 0.88,
|
|
||||||
}}
|
|
||||||
interactionConfig={{
|
|
||||||
...DEFAULT_INTERACTION_CONFIG,
|
|
||||||
initRegionFlashMs: 1000,
|
|
||||||
enableInitRegionFlash: true,
|
|
||||||
}}
|
|
||||||
disabled={!isInteractive}
|
|
||||||
initialSession={sessionDraft ?? undefined}
|
|
||||||
onWatch={handleWatch}
|
|
||||||
onPaintCallback={handlePaintCallback}
|
|
||||||
onError={handleError}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* initialization loading mask */}
|
|
||||||
{isInitLoading && (
|
|
||||||
<View style={styles.initOverlay} pointerEvents="none">
|
|
||||||
<ActivityIndicator size="small" color="#4363D8" />
|
|
||||||
<Text style={styles.initOverlayText}>
|
|
||||||
Initializing: {watchState}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Toast */}
|
|
||||||
{toastMessage ? (
|
|
||||||
<View style={styles.toast} pointerEvents="none">
|
|
||||||
<Text style={styles.toastText}>{toastMessage}</Text>
|
|
||||||
</View>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{/* bottom: business operation bar / Ref method demonstration */}
|
|
||||||
<View style={styles.bottomBar}>
|
|
||||||
<ScrollView
|
|
||||||
horizontal
|
|
||||||
showsHorizontalScrollIndicator={false}
|
|
||||||
contentContainerStyle={styles.bottomBarContent}
|
|
||||||
>
|
|
||||||
{/* preset brush (replace bottom color bar) */}
|
|
||||||
<Text style={styles.sectionLabel}>Preset brush:</Text>
|
|
||||||
{PAINT_PRESETS.map(p => (
|
|
||||||
<TouchableOpacity
|
|
||||||
key={p.label}
|
|
||||||
style={[styles.paintBtn, { backgroundColor: `rgb(${p.color.r},${p.color.g},${p.color.b})` }]}
|
|
||||||
onPress={() => handleSetPaintColor(p.color, p.label)}
|
|
||||||
disabled={!isInteractive}
|
|
||||||
>
|
|
||||||
<Text style={styles.paintBtnText}>{p.label}</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<View style={styles.divider} />
|
|
||||||
|
|
||||||
{/* Ref operations */}
|
|
||||||
<Text style={styles.sectionLabel}>Operations:</Text>
|
|
||||||
<TouchableOpacity
|
|
||||||
style={[styles.actionBtn, styles.actionBtnDanger]}
|
|
||||||
onPress={handleReset}
|
|
||||||
disabled={!isInteractive}
|
|
||||||
>
|
|
||||||
<Text style={styles.actionBtnText}>Undo</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity
|
|
||||||
style={styles.actionBtn}
|
|
||||||
onPress={handleSwap}
|
|
||||||
disabled={!isInteractive}
|
|
||||||
>
|
|
||||||
<Text style={styles.actionBtnText}>Compare</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity
|
|
||||||
style={styles.actionBtn}
|
|
||||||
onPress={handleClearAll}
|
|
||||||
disabled={!isInteractive}
|
|
||||||
>
|
|
||||||
<Text style={styles.actionBtnText}>Clear</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity
|
|
||||||
style={[styles.actionBtn, styles.actionBtnPrimary]}
|
|
||||||
onPress={handleSave}
|
|
||||||
disabled={!isInteractive}
|
|
||||||
>
|
|
||||||
<Text style={styles.actionBtnTextPrimary}>Save</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity
|
|
||||||
style={styles.actionBtn}
|
|
||||||
onPress={handleExportSession}
|
|
||||||
disabled={!isInteractive}
|
|
||||||
>
|
|
||||||
<Text style={styles.actionBtnText}>Export session</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</ScrollView>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* error display */}
|
|
||||||
{errorMessage ? (
|
|
||||||
<View style={styles.errorBar}>
|
|
||||||
<Text style={styles.errorBarText}>Error: {errorMessage}</Text>
|
|
||||||
</View>
|
|
||||||
) : null}
|
|
||||||
</SafeAreaView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// styles
|
|
||||||
// ============================================================================
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
root: {
|
|
||||||
flex: 1,
|
|
||||||
backgroundColor: '#fff',
|
|
||||||
},
|
|
||||||
center: {
|
|
||||||
flex: 1,
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
padding: 24,
|
|
||||||
},
|
|
||||||
loadingText: {
|
|
||||||
marginTop: 12,
|
|
||||||
color: '#888',
|
|
||||||
fontSize: 14,
|
|
||||||
},
|
|
||||||
errorText: {
|
|
||||||
color: '#c33',
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: '600',
|
|
||||||
},
|
|
||||||
errorDetail: {
|
|
||||||
marginTop: 8,
|
|
||||||
color: '#999',
|
|
||||||
fontSize: 13,
|
|
||||||
textAlign: 'center',
|
|
||||||
},
|
|
||||||
|
|
||||||
// top status bar
|
|
||||||
topBar: {
|
|
||||||
paddingHorizontal: 12,
|
|
||||||
paddingTop: 8,
|
|
||||||
paddingBottom: 4,
|
|
||||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
||||||
borderBottomColor: '#e8e8e8',
|
|
||||||
backgroundColor: '#fafafa',
|
|
||||||
},
|
|
||||||
topBarRow: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
alignItems: 'center',
|
|
||||||
},
|
|
||||||
statusLabel: {
|
|
||||||
fontSize: 12,
|
|
||||||
color: '#888',
|
|
||||||
},
|
|
||||||
statusValue: {
|
|
||||||
fontWeight: '600',
|
|
||||||
color: '#555',
|
|
||||||
},
|
|
||||||
statusReady: {
|
|
||||||
color: '#2a7',
|
|
||||||
},
|
|
||||||
statusError: {
|
|
||||||
color: '#c33',
|
|
||||||
},
|
|
||||||
regionCount: {
|
|
||||||
fontSize: 11,
|
|
||||||
color: '#aaa',
|
|
||||||
},
|
|
||||||
modeRow: {
|
|
||||||
marginTop: 6,
|
|
||||||
},
|
|
||||||
modeRowContent: {
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: 6,
|
|
||||||
},
|
|
||||||
modeChip: {
|
|
||||||
paddingHorizontal: 10,
|
|
||||||
paddingVertical: 4,
|
|
||||||
borderRadius: 12,
|
|
||||||
backgroundColor: '#eee',
|
|
||||||
},
|
|
||||||
modeChipActive: {
|
|
||||||
backgroundColor: '#4363D8',
|
|
||||||
},
|
|
||||||
modeChipText: {
|
|
||||||
fontSize: 11,
|
|
||||||
color: '#666',
|
|
||||||
},
|
|
||||||
modeChipTextActive: {
|
|
||||||
color: '#fff',
|
|
||||||
fontWeight: '600',
|
|
||||||
},
|
|
||||||
modeDivider: {
|
|
||||||
color: '#ddd',
|
|
||||||
fontSize: 11,
|
|
||||||
marginHorizontal: 2,
|
|
||||||
},
|
|
||||||
|
|
||||||
// canvas
|
|
||||||
canvasHost: {
|
|
||||||
flex: 1,
|
|
||||||
position: 'relative',
|
|
||||||
height: 280,
|
|
||||||
},
|
|
||||||
canvas: {
|
|
||||||
flex: 1,
|
|
||||||
},
|
|
||||||
initOverlay: {
|
|
||||||
...StyleSheet.absoluteFillObject,
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
backgroundColor: 'rgba(255, 255, 255, 0.8)',
|
|
||||||
gap: 8,
|
|
||||||
},
|
|
||||||
initOverlayText: {
|
|
||||||
color: '#888',
|
|
||||||
fontSize: 13,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Toast
|
|
||||||
toast: {
|
|
||||||
position: 'absolute',
|
|
||||||
top: 120,
|
|
||||||
left: 20,
|
|
||||||
right: 20,
|
|
||||||
alignItems: 'center',
|
|
||||||
zIndex: 999,
|
|
||||||
},
|
|
||||||
toastText: {
|
|
||||||
backgroundColor: 'rgba(0,0,0,0.78)',
|
|
||||||
color: '#fff',
|
|
||||||
fontSize: 13,
|
|
||||||
paddingHorizontal: 16,
|
|
||||||
paddingVertical: 8,
|
|
||||||
borderRadius: 8,
|
|
||||||
overflow: 'hidden',
|
|
||||||
},
|
|
||||||
|
|
||||||
// bottom operation bar
|
|
||||||
bottomBar: {
|
|
||||||
borderTopWidth: StyleSheet.hairlineWidth,
|
|
||||||
borderTopColor: '#e8e8e8',
|
|
||||||
backgroundColor: '#fafafa',
|
|
||||||
paddingVertical: 8,
|
|
||||||
},
|
|
||||||
bottomBarContent: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
paddingHorizontal: 12,
|
|
||||||
gap: 8,
|
|
||||||
},
|
|
||||||
sectionLabel: {
|
|
||||||
fontSize: 12,
|
|
||||||
color: '#999',
|
|
||||||
fontWeight: '600',
|
|
||||||
},
|
|
||||||
divider: {
|
|
||||||
width: 1,
|
|
||||||
height: 20,
|
|
||||||
backgroundColor: '#e0e0e0',
|
|
||||||
marginHorizontal: 4,
|
|
||||||
},
|
|
||||||
|
|
||||||
// brush button
|
|
||||||
paintBtn: {
|
|
||||||
paddingHorizontal: 10,
|
|
||||||
paddingVertical: 6,
|
|
||||||
borderRadius: 14,
|
|
||||||
borderWidth: 1.5,
|
|
||||||
borderColor: '#ddd',
|
|
||||||
},
|
|
||||||
paintBtnText: {
|
|
||||||
fontSize: 11,
|
|
||||||
color: '#333',
|
|
||||||
fontWeight: '600',
|
|
||||||
textShadowColor: 'rgba(255,255,255,0.6)',
|
|
||||||
textShadowOffset: { width: 0, height: 1 },
|
|
||||||
textShadowRadius: 2,
|
|
||||||
},
|
|
||||||
|
|
||||||
// operation button
|
|
||||||
actionBtn: {
|
|
||||||
paddingHorizontal: 12,
|
|
||||||
paddingVertical: 6,
|
|
||||||
borderRadius: 6,
|
|
||||||
backgroundColor: '#f0f0f0',
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: '#ddd',
|
|
||||||
},
|
|
||||||
actionBtnPrimary: {
|
|
||||||
backgroundColor: '#4363D8',
|
|
||||||
borderColor: '#4363D8',
|
|
||||||
},
|
|
||||||
actionBtnDanger: {
|
|
||||||
borderColor: '#e88',
|
|
||||||
},
|
|
||||||
actionBtnText: {
|
|
||||||
fontSize: 12,
|
|
||||||
color: '#555',
|
|
||||||
fontWeight: '500',
|
|
||||||
},
|
|
||||||
actionBtnTextPrimary: {
|
|
||||||
fontSize: 12,
|
|
||||||
color: '#fff',
|
|
||||||
fontWeight: '600',
|
|
||||||
},
|
|
||||||
|
|
||||||
// error bar
|
|
||||||
errorBar: {
|
|
||||||
backgroundColor: '#fff0f0',
|
|
||||||
paddingHorizontal: 12,
|
|
||||||
paddingVertical: 6,
|
|
||||||
borderTopWidth: StyleSheet.hairlineWidth,
|
|
||||||
borderTopColor: '#fcc',
|
|
||||||
},
|
|
||||||
errorBarText: {
|
|
||||||
fontSize: 12,
|
|
||||||
color: '#c33',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default function Root(): React.JSX.Element {
|
|
||||||
return (
|
|
||||||
<SafeAreaProvider>
|
|
||||||
<App />
|
|
||||||
</SafeAreaProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,211 +0,0 @@
|
|||||||
# MaskSegmentCanvas Example
|
|
||||||
|
|
||||||
A Demo project that **fully simulates real-world business integration**, showing how to integrate `react-native-mask-segment-canvas` into your React Native project.
|
|
||||||
|
|
||||||
## Difference from the Library's Own Demo
|
|
||||||
|
|
||||||
| Project | Import Method | Purpose |
|
|
||||||
| ---- | -------- | ---- |
|
|
||||||
| Root `App.tsx` | `import ... from './src'` (internal source) | Library author self-testing |
|
|
||||||
| **This example/** | `import ... from 'react-native-mask-segment-canvas'` (public API) | **Business integration reference** |
|
|
||||||
|
|
||||||
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
|
|
||||||
# 1. Enter the example directory
|
|
||||||
cd example
|
|
||||||
|
|
||||||
# 2. Install dependencies (auto-links the parent library)
|
|
||||||
npm install
|
|
||||||
|
|
||||||
# 3. Apply postinstall patches (patch-package patches react-native-fast-opencv)
|
|
||||||
# Automatically runs after npm install. If it didn't, run manually:
|
|
||||||
npx patch-package
|
|
||||||
|
|
||||||
# 4. iOS: Install native dependencies
|
|
||||||
cd ios && pod install && cd ..
|
|
||||||
|
|
||||||
# 5. Start Metro
|
|
||||||
npm start
|
|
||||||
|
|
||||||
# 6. In another terminal, run
|
|
||||||
npm run ios
|
|
||||||
# or
|
|
||||||
npm run android
|
|
||||||
```
|
|
||||||
|
|
||||||
## File Overview
|
|
||||||
|
|
||||||
```
|
|
||||||
example/
|
|
||||||
├── App.tsx # ★ Core: Complete integration example page
|
|
||||||
├── index.js # RN entry (registers gesture-handler + Buffer polyfill)
|
|
||||||
├── app.json # App name config
|
|
||||||
├── package.json # Standalone dependency config, "react-native-mask-segment-canvas": "file:.."
|
|
||||||
├── metro.config.js # Metro config (watchFolders pointing to parent directory)
|
|
||||||
├── babel.config.js # Babel config (includes reanimated plugin)
|
|
||||||
├── tsconfig.json # TypeScript config
|
|
||||||
└── README.md # This file
|
|
||||||
```
|
|
||||||
|
|
||||||
## Features Covered in App.tsx
|
|
||||||
|
|
||||||
`App.tsx` is a complete page you can reference directly, covering:
|
|
||||||
|
|
||||||
| Feature | Relevant Code Location |
|
|
||||||
| ---- | ------------ |
|
|
||||||
| **PNG pre-warming** | `useEffect` → `prewarmPngBgrCacheAsync` |
|
|
||||||
| **State management** | `watchState` / `isInteractive` / `isOutlineReady` derived states |
|
|
||||||
| **onWatch callback** | `handleWatch` — tracks initialization stages |
|
|
||||||
| **onPaintCallback** | `handlePaintCallback` — handles both successful paint and missing-brush scenarios |
|
|
||||||
| **onError callback** | `handleError` — captures segmentation/load failures |
|
|
||||||
| **Ref operations** | `save` / `reset` / `swap` / `clearAllPaint` / `session` |
|
|
||||||
| **setPaintColor** | Preset brush colors via `ref.setPaintColor` |
|
|
||||||
| **Custom semantic color table** | `GYM_CUSTOM_COLORS` example + mode toggle UI |
|
|
||||||
| **Pipeline resolution toggle** | `pipelinePreset` low/medium/high resolution switching |
|
|
||||||
| **Toast notifications** | `brush_required` callback when no brush is selected + custom Toast |
|
|
||||||
| **Loading/error UI** | PNG pre-warm loading, initialization Loading, error display |
|
|
||||||
| **Draft recovery** | `sessionDraft` state + `initialSession` prop |
|
|
||||||
|
|
||||||
## Integrating into Your Own Project
|
|
||||||
|
|
||||||
### Option 1: npm install (recommended for production)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install react-native-mask-segment-canvas
|
|
||||||
```
|
|
||||||
|
|
||||||
### Option 2: Local development
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# In the library directory
|
|
||||||
npm link
|
|
||||||
|
|
||||||
# In your project
|
|
||||||
npm link react-native-mask-segment-canvas
|
|
||||||
```
|
|
||||||
|
|
||||||
Your `metro.config.js` needs to add:
|
|
||||||
|
|
||||||
```js
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
module.exports = mergeConfig(getDefaultConfig(__dirname), {
|
|
||||||
watchFolders: [path.resolve(__dirname, '../MaskSegmentApp')],
|
|
||||||
resolver: {
|
|
||||||
nodeModulesPaths: [path.resolve(__dirname, 'node_modules')],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Option 3: file: dependency (used by this example)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"dependencies": {
|
|
||||||
"react-native-mask-segment-canvas": "file:../MaskSegmentApp"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Required peerDependencies
|
|
||||||
|
|
||||||
```bash
|
|
||||||
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
|
|
||||||
# Safe area insets
|
|
||||||
npm install react-native-safe-area-context
|
|
||||||
```
|
|
||||||
|
|
||||||
### postinstall Configuration
|
|
||||||
|
|
||||||
Your `package.json` needs:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"scripts": {
|
|
||||||
"postinstall": "patch-package"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"patch-package": "^8.0.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Common Issues
|
|
||||||
|
|
||||||
**Getting "module not found" after `npm install`?**
|
|
||||||
- Make sure `postinstall` ran (`npx patch-package`)
|
|
||||||
- Check that Metro config's `watchFolders` includes the library directory
|
|
||||||
|
|
||||||
**`pod install` fails?**
|
|
||||||
```bash
|
|
||||||
cd ios
|
|
||||||
bundle install
|
|
||||||
bundle exec pod install --repo-update
|
|
||||||
```
|
|
||||||
|
|
||||||
**Android build errors?**
|
|
||||||
```bash
|
|
||||||
cd android && ./gradlew clean && cd ..
|
|
||||||
```
|
|
||||||
|
|
||||||
**Duplicate module errors at runtime (most common)**
|
|
||||||
|
|
||||||
In monorepo, npm link, or `file:..` setups, you'll frequently encounter these "similar-looking" errors:
|
|
||||||
|
|
||||||
- `SkiaPictureView must be a function (received 'undefined')`
|
|
||||||
- `createAnimatedNode: Animated node[...] already exists` (including UIFrameGuarded variants)
|
|
||||||
- Other Fabric ViewManager / native module singleton conflicts
|
|
||||||
|
|
||||||
**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. **At the very top of index.js** (must come first):
|
|
||||||
|
|
||||||
```js
|
|
||||||
import 'react-native-gesture-handler';
|
|
||||||
import 'react-native-reanimated';
|
|
||||||
import '@shopify/react-native-skia';
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **metro.config.js** (use extraNodeModules + blockList for double safety):
|
|
||||||
|
|
||||||
```js
|
|
||||||
const path = require('path');
|
|
||||||
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
|
|
||||||
|
|
||||||
const yourNodeModules = path.resolve(__dirname, 'node_modules');
|
|
||||||
|
|
||||||
const singletons = [
|
|
||||||
'react', 'react-native',
|
|
||||||
'react-native-reanimated',
|
|
||||||
'@shopify/react-native-skia',
|
|
||||||
'react-native-gesture-handler',
|
|
||||||
'react-native-fast-opencv',
|
|
||||||
'react-native-safe-area-context',
|
|
||||||
'react-native-fs',
|
|
||||||
'react-native-image-picker',
|
|
||||||
];
|
|
||||||
|
|
||||||
module.exports = mergeConfig(getDefaultConfig(__dirname), {
|
|
||||||
watchFolders: [path.resolve(__dirname, '../MaskSegmentApp')],
|
|
||||||
resolver: {
|
|
||||||
nodeModulesPaths: [yourNodeModules],
|
|
||||||
extraNodeModules: singletons.reduce((acc, p) => (acc[p] = path.resolve(yourNodeModules, p), acc), {}),
|
|
||||||
blockList: singletons.map(p => new RegExp(`/MaskSegmentApp/node_modules/${p.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}/`)),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
> `example/metro.config.js` is already written following this standard template. You can reference it directly.
|
|
||||||
|
|
||||||
After completing the two steps above, you **must**:
|
|
||||||
- Restart Metro (`npx react-native start --reset-cache`)
|
|
||||||
- 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.
|
|
||||||
@ -1,119 +0,0 @@
|
|||||||
apply plugin: "com.android.application"
|
|
||||||
apply plugin: "org.jetbrains.kotlin.android"
|
|
||||||
apply plugin: "com.facebook.react"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the configuration block to customize your React Native Android app.
|
|
||||||
* By default you don't need to apply any configuration, just uncomment the lines you need.
|
|
||||||
*/
|
|
||||||
react {
|
|
||||||
/* Folders */
|
|
||||||
// The root of your project, i.e. where "package.json" lives. Default is '../..'
|
|
||||||
// root = file("../../")
|
|
||||||
// The folder where the react-native NPM package is. Default is ../../node_modules/react-native
|
|
||||||
// reactNativeDir = file("../../node_modules/react-native")
|
|
||||||
// The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen
|
|
||||||
// codegenDir = file("../../node_modules/@react-native/codegen")
|
|
||||||
// The cli.js file which is the React Native CLI entrypoint. Default is ../../node_modules/react-native/cli.js
|
|
||||||
// cliFile = file("../../node_modules/react-native/cli.js")
|
|
||||||
|
|
||||||
/* Variants */
|
|
||||||
// The list of variants to that are debuggable. For those we're going to
|
|
||||||
// skip the bundling of the JS bundle and the assets. By default is just 'debug'.
|
|
||||||
// If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
|
|
||||||
// debuggableVariants = ["liteDebug", "prodDebug"]
|
|
||||||
|
|
||||||
/* Bundling */
|
|
||||||
// A list containing the node command and its flags. Default is just 'node'.
|
|
||||||
// nodeExecutableAndArgs = ["node"]
|
|
||||||
//
|
|
||||||
// The command to run when bundling. By default is 'bundle'
|
|
||||||
// bundleCommand = "ram-bundle"
|
|
||||||
//
|
|
||||||
// The path to the CLI configuration file. Default is empty.
|
|
||||||
// bundleConfig = file(../rn-cli.config.js)
|
|
||||||
//
|
|
||||||
// The name of the generated asset file containing your JS bundle
|
|
||||||
// bundleAssetName = "MyApplication.android.bundle"
|
|
||||||
//
|
|
||||||
// The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
|
|
||||||
// entryFile = file("../js/MyApplication.android.js")
|
|
||||||
//
|
|
||||||
// A list of extra flags to pass to the 'bundle' commands.
|
|
||||||
// See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
|
|
||||||
// extraPackagerArgs = []
|
|
||||||
|
|
||||||
/* Hermes Commands */
|
|
||||||
// The hermes compiler command to run. By default it is 'hermesc'
|
|
||||||
// hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
|
|
||||||
//
|
|
||||||
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
|
|
||||||
// hermesFlags = ["-O", "-output-source-map"]
|
|
||||||
|
|
||||||
/* Autolinking */
|
|
||||||
autolinkLibrariesWithApp()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
|
|
||||||
*/
|
|
||||||
def enableProguardInReleaseBuilds = false
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The preferred build flavor of JavaScriptCore (JSC)
|
|
||||||
*
|
|
||||||
* For example, to use the international variant, you can use:
|
|
||||||
* `def jscFlavor = io.github.react-native-community:jsc-android-intl:2026004.+`
|
|
||||||
*
|
|
||||||
* The international variant includes ICU i18n library and necessary data
|
|
||||||
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
|
|
||||||
* give correct results when using with locales other than en-US. Note that
|
|
||||||
* this variant is about 6MiB larger per architecture than default.
|
|
||||||
*/
|
|
||||||
def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+'
|
|
||||||
|
|
||||||
android {
|
|
||||||
ndkVersion rootProject.ext.ndkVersion
|
|
||||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
|
||||||
compileSdk rootProject.ext.compileSdkVersion
|
|
||||||
|
|
||||||
namespace "com.masksegmentcanvasexample"
|
|
||||||
defaultConfig {
|
|
||||||
applicationId "com.masksegmentcanvasexample"
|
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
|
||||||
versionCode 1
|
|
||||||
versionName "1.0"
|
|
||||||
}
|
|
||||||
signingConfigs {
|
|
||||||
debug {
|
|
||||||
storeFile file('debug.keystore')
|
|
||||||
storePassword 'android'
|
|
||||||
keyAlias 'androiddebugkey'
|
|
||||||
keyPassword 'android'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buildTypes {
|
|
||||||
debug {
|
|
||||||
signingConfig signingConfigs.debug
|
|
||||||
}
|
|
||||||
release {
|
|
||||||
// Caution! In production, you need to generate your own keystore file.
|
|
||||||
// see https://reactnative.dev/docs/signed-apk-android.
|
|
||||||
signingConfig signingConfigs.debug
|
|
||||||
minifyEnabled enableProguardInReleaseBuilds
|
|
||||||
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
// The version of react-native is set by the React Native Gradle Plugin
|
|
||||||
implementation("com.facebook.react:react-android")
|
|
||||||
|
|
||||||
if (hermesEnabled.toBoolean()) {
|
|
||||||
implementation("com.facebook.react:hermes-android")
|
|
||||||
} else {
|
|
||||||
implementation jscFlavor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
10
example/android/app/proguard-rules.pro
vendored
@ -1,10 +0,0 @@
|
|||||||
# Add project specific ProGuard rules here.
|
|
||||||
# By default, the flags in this file are appended to flags specified
|
|
||||||
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
|
|
||||||
# You can edit the include path and order by changing the proguardFiles
|
|
||||||
# directive in build.gradle.
|
|
||||||
#
|
|
||||||
# For more details, see
|
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
||||||
|
|
||||||
# Add any project specific keep options here:
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
|
|
||||||
<application
|
|
||||||
android:usesCleartextTraffic="true"
|
|
||||||
tools:targetApi="28"
|
|
||||||
tools:ignore="GoogleAppIndexingWarning"/>
|
|
||||||
</manifest>
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
|
||||||
|
|
||||||
<application
|
|
||||||
android:name=".MainApplication"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:icon="@mipmap/ic_launcher"
|
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
|
||||||
android:allowBackup="false"
|
|
||||||
android:theme="@style/AppTheme"
|
|
||||||
android:supportsRtl="true">
|
|
||||||
<activity
|
|
||||||
android:name=".MainActivity"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
|
|
||||||
android:launchMode="singleTask"
|
|
||||||
android:windowSoftInputMode="adjustResize"
|
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MAIN" />
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
</application>
|
|
||||||
</manifest>
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
package com.masksegmentcanvasexample
|
|
||||||
|
|
||||||
import com.facebook.react.ReactActivity
|
|
||||||
import com.facebook.react.ReactActivityDelegate
|
|
||||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
|
|
||||||
import com.facebook.react.defaults.DefaultReactActivityDelegate
|
|
||||||
|
|
||||||
class MainActivity : ReactActivity() {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the name of the main component registered from JavaScript. This is used to schedule
|
|
||||||
* rendering of the component.
|
|
||||||
*/
|
|
||||||
override fun getMainComponentName(): String = "MaskSegmentCanvasExample"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
|
|
||||||
* which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
|
|
||||||
*/
|
|
||||||
override fun createReactActivityDelegate(): ReactActivityDelegate =
|
|
||||||
DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)
|
|
||||||
}
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
package com.masksegmentcanvasexample
|
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import com.facebook.react.PackageList
|
|
||||||
import com.facebook.react.ReactApplication
|
|
||||||
import com.facebook.react.ReactHost
|
|
||||||
import com.facebook.react.ReactNativeHost
|
|
||||||
import com.facebook.react.ReactPackage
|
|
||||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
|
|
||||||
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
|
|
||||||
import com.facebook.react.defaults.DefaultReactNativeHost
|
|
||||||
import com.facebook.react.soloader.OpenSourceMergedSoMapping
|
|
||||||
import com.facebook.soloader.SoLoader
|
|
||||||
|
|
||||||
class MainApplication : Application(), ReactApplication {
|
|
||||||
|
|
||||||
override val reactNativeHost: ReactNativeHost =
|
|
||||||
object : DefaultReactNativeHost(this) {
|
|
||||||
override fun getPackages(): List<ReactPackage> =
|
|
||||||
PackageList(this).packages.apply {
|
|
||||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
|
||||||
// add(MyReactNativePackage())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getJSMainModuleName(): String = "index"
|
|
||||||
|
|
||||||
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
|
|
||||||
|
|
||||||
override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
|
|
||||||
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
|
|
||||||
}
|
|
||||||
|
|
||||||
override val reactHost: ReactHost
|
|
||||||
get() = getDefaultReactHost(applicationContext, reactNativeHost)
|
|
||||||
|
|
||||||
override fun onCreate() {
|
|
||||||
super.onCreate()
|
|
||||||
SoLoader.init(this, OpenSourceMergedSoMapping)
|
|
||||||
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
|
||||||
// If you opted-in for the New Architecture, we load the native entry point for this app.
|
|
||||||
load()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Copyright (C) 2014 The Android Open Source Project
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
-->
|
|
||||||
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:insetLeft="@dimen/abc_edit_text_inset_horizontal_material"
|
|
||||||
android:insetRight="@dimen/abc_edit_text_inset_horizontal_material"
|
|
||||||
android:insetTop="@dimen/abc_edit_text_inset_top_material"
|
|
||||||
android:insetBottom="@dimen/abc_edit_text_inset_bottom_material"
|
|
||||||
>
|
|
||||||
|
|
||||||
<selector>
|
|
||||||
<!--
|
|
||||||
This file is a copy of abc_edit_text_material (https://bit.ly/3k8fX7I).
|
|
||||||
The item below with state_pressed="false" and state_focused="false" causes a NullPointerException.
|
|
||||||
NullPointerException:tempt to invoke virtual method 'android.graphics.drawable.Drawable android.graphics.drawable.Drawable$ConstantState.newDrawable(android.content.res.Resources)'
|
|
||||||
|
|
||||||
<item android:state_pressed="false" android:state_focused="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
|
|
||||||
|
|
||||||
For more info, see https://bit.ly/3CdLStv (react-native/pull/29452) and https://bit.ly/3nxOMoR.
|
|
||||||
-->
|
|
||||||
<item android:state_enabled="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
|
|
||||||
<item android:drawable="@drawable/abc_textfield_activated_mtrl_alpha"/>
|
|
||||||
</selector>
|
|
||||||
|
|
||||||
</inset>
|
|
||||||
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 15 KiB |
@ -1,3 +0,0 @@
|
|||||||
<resources>
|
|
||||||
<string name="app_name">MaskSegmentCanvasExample</string>
|
|
||||||
</resources>
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
<resources>
|
|
||||||
|
|
||||||
<!-- Base application theme. -->
|
|
||||||
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
|
|
||||||
<!-- Customize your theme here. -->
|
|
||||||
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
</resources>
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
buildscript {
|
|
||||||
ext {
|
|
||||||
buildToolsVersion = "35.0.0"
|
|
||||||
minSdkVersion = 24
|
|
||||||
compileSdkVersion = 35
|
|
||||||
targetSdkVersion = 35
|
|
||||||
ndkVersion = "27.1.12297006"
|
|
||||||
kotlinVersion = "2.0.21"
|
|
||||||
}
|
|
||||||
repositories {
|
|
||||||
google()
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
dependencies {
|
|
||||||
classpath("com.android.tools.build:gradle")
|
|
||||||
classpath("com.facebook.react:react-native-gradle-plugin")
|
|
||||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
apply plugin: "com.facebook.react.rootproject"
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
# Project-wide Gradle settings.
|
|
||||||
|
|
||||||
# IDE (e.g. Android Studio) users:
|
|
||||||
# Gradle settings configured through the IDE *will override*
|
|
||||||
# any settings specified in this file.
|
|
||||||
|
|
||||||
# For more details on how to configure your build environment visit
|
|
||||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
|
||||||
|
|
||||||
# Specifies the JVM arguments used for the daemon process.
|
|
||||||
# The setting is particularly useful for tweaking memory settings.
|
|
||||||
# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
|
|
||||||
org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
|
|
||||||
|
|
||||||
# When configured, Gradle will run in incubating parallel mode.
|
|
||||||
# This option should only be used with decoupled projects. More details, visit
|
|
||||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
|
||||||
# org.gradle.parallel=true
|
|
||||||
|
|
||||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
|
||||||
# Android operating system, and which are packaged with your app's APK
|
|
||||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
|
||||||
android.useAndroidX=true
|
|
||||||
|
|
||||||
# Use this property to specify which architecture you want to build.
|
|
||||||
# You can also override it from the CLI using
|
|
||||||
# ./gradlew <task> -PreactNativeArchitectures=x86_64
|
|
||||||
reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
|
|
||||||
|
|
||||||
# Use this property to enable support to the new architecture.
|
|
||||||
# This will allow you to use TurboModules and the Fabric render in
|
|
||||||
# your application. You should enable this flag either if you want
|
|
||||||
# to write custom TurboModules/Fabric components OR use libraries that
|
|
||||||
# are providing them.
|
|
||||||
newArchEnabled=true
|
|
||||||
|
|
||||||
# Use this property to enable or disable the Hermes JS engine.
|
|
||||||
# If set to false, you will be using JSC instead.
|
|
||||||
hermesEnabled=true
|
|
||||||
BIN
example/android/gradle/wrapper/gradle-wrapper.jar
vendored
@ -1,7 +0,0 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
|
||||||
distributionPath=wrapper/dists
|
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
|
||||||
networkTimeout=10000
|
|
||||||
validateDistributionUrl=true
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
|
||||||
zipStorePath=wrapper/dists
|
|
||||||
251
example/android/gradlew
vendored
@ -1,251 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
#
|
|
||||||
# Copyright © 2015-2021 the original authors.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
|
||||||
#
|
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
#
|
|
||||||
# Gradle start up script for POSIX generated by Gradle.
|
|
||||||
#
|
|
||||||
# Important for running:
|
|
||||||
#
|
|
||||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
|
||||||
# noncompliant, but you have some other compliant shell such as ksh or
|
|
||||||
# bash, then to run this script, type that shell name before the whole
|
|
||||||
# command line, like:
|
|
||||||
#
|
|
||||||
# ksh Gradle
|
|
||||||
#
|
|
||||||
# Busybox and similar reduced shells will NOT work, because this script
|
|
||||||
# requires all of these POSIX shell features:
|
|
||||||
# * functions;
|
|
||||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
|
||||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
|
||||||
# * compound commands having a testable exit status, especially «case»;
|
|
||||||
# * various built-in commands including «command», «set», and «ulimit».
|
|
||||||
#
|
|
||||||
# Important for patching:
|
|
||||||
#
|
|
||||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
|
||||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
|
||||||
#
|
|
||||||
# The "traditional" practice of packing multiple parameters into a
|
|
||||||
# space-separated string is a well documented source of bugs and security
|
|
||||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
|
||||||
# options in "$@", and eventually passing that to Java.
|
|
||||||
#
|
|
||||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
|
||||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
|
||||||
# see the in-line comments for details.
|
|
||||||
#
|
|
||||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
|
||||||
# Darwin, MinGW, and NonStop.
|
|
||||||
#
|
|
||||||
# (3) This script is generated from the Groovy template
|
|
||||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
|
||||||
# within the Gradle project.
|
|
||||||
#
|
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
|
||||||
#
|
|
||||||
##############################################################################
|
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
|
||||||
|
|
||||||
# Resolve links: $0 may be a link
|
|
||||||
app_path=$0
|
|
||||||
|
|
||||||
# Need this for daisy-chained symlinks.
|
|
||||||
while
|
|
||||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
|
||||||
[ -h "$app_path" ]
|
|
||||||
do
|
|
||||||
ls=$( ls -ld "$app_path" )
|
|
||||||
link=${ls#*' -> '}
|
|
||||||
case $link in #(
|
|
||||||
/*) app_path=$link ;; #(
|
|
||||||
*) app_path=$APP_HOME$link ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
# This is normally unused
|
|
||||||
# shellcheck disable=SC2034
|
|
||||||
APP_BASE_NAME=${0##*/}
|
|
||||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
|
||||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
|
||||||
MAX_FD=maximum
|
|
||||||
|
|
||||||
warn () {
|
|
||||||
echo "$*"
|
|
||||||
} >&2
|
|
||||||
|
|
||||||
die () {
|
|
||||||
echo
|
|
||||||
echo "$*"
|
|
||||||
echo
|
|
||||||
exit 1
|
|
||||||
} >&2
|
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
|
||||||
cygwin=false
|
|
||||||
msys=false
|
|
||||||
darwin=false
|
|
||||||
nonstop=false
|
|
||||||
case "$( uname )" in #(
|
|
||||||
CYGWIN* ) cygwin=true ;; #(
|
|
||||||
Darwin* ) darwin=true ;; #(
|
|
||||||
MSYS* | MINGW* ) msys=true ;; #(
|
|
||||||
NONSTOP* ) nonstop=true ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
|
||||||
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
|
||||||
if [ -n "$JAVA_HOME" ] ; then
|
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
|
||||||
# IBM's JDK on AIX uses strange locations for the executables
|
|
||||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
|
||||||
else
|
|
||||||
JAVACMD=$JAVA_HOME/bin/java
|
|
||||||
fi
|
|
||||||
if [ ! -x "$JAVACMD" ] ; then
|
|
||||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
|
||||||
location of your Java installation."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
JAVACMD=java
|
|
||||||
if ! command -v java >/dev/null 2>&1
|
|
||||||
then
|
|
||||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
|
||||||
location of your Java installation."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
|
||||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
|
||||||
case $MAX_FD in #(
|
|
||||||
max*)
|
|
||||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
|
||||||
# shellcheck disable=SC2039,SC3045
|
|
||||||
MAX_FD=$( ulimit -H -n ) ||
|
|
||||||
warn "Could not query maximum file descriptor limit"
|
|
||||||
esac
|
|
||||||
case $MAX_FD in #(
|
|
||||||
'' | soft) :;; #(
|
|
||||||
*)
|
|
||||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
|
||||||
# shellcheck disable=SC2039,SC3045
|
|
||||||
ulimit -n "$MAX_FD" ||
|
|
||||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Collect all arguments for the java command, stacking in reverse order:
|
|
||||||
# * args from the command line
|
|
||||||
# * the main class name
|
|
||||||
# * -classpath
|
|
||||||
# * -D...appname settings
|
|
||||||
# * --module-path (only if needed)
|
|
||||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
|
||||||
|
|
||||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
|
||||||
if "$cygwin" || "$msys" ; then
|
|
||||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
|
||||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
|
||||||
|
|
||||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
|
||||||
|
|
||||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
|
||||||
for arg do
|
|
||||||
if
|
|
||||||
case $arg in #(
|
|
||||||
-*) false ;; # don't mess with options #(
|
|
||||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
|
||||||
[ -e "$t" ] ;; #(
|
|
||||||
*) false ;;
|
|
||||||
esac
|
|
||||||
then
|
|
||||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
|
||||||
fi
|
|
||||||
# Roll the args list around exactly as many times as the number of
|
|
||||||
# args, so each arg winds up back in the position where it started, but
|
|
||||||
# possibly modified.
|
|
||||||
#
|
|
||||||
# NB: a `for` loop captures its iteration list before it begins, so
|
|
||||||
# changing the positional parameters here affects neither the number of
|
|
||||||
# iterations, nor the values presented in `arg`.
|
|
||||||
shift # remove old arg
|
|
||||||
set -- "$@" "$arg" # push replacement arg
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
|
||||||
|
|
||||||
# Collect all arguments for the java command:
|
|
||||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
|
||||||
# and any embedded shellness will be escaped.
|
|
||||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
|
||||||
# treated as '${Hostname}' itself on the command line.
|
|
||||||
|
|
||||||
set -- \
|
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
|
||||||
-classpath "$CLASSPATH" \
|
|
||||||
org.gradle.wrapper.GradleWrapperMain \
|
|
||||||
"$@"
|
|
||||||
|
|
||||||
# Stop when "xargs" is not available.
|
|
||||||
if ! command -v xargs >/dev/null 2>&1
|
|
||||||
then
|
|
||||||
die "xargs is not available"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Use "xargs" to parse quoted args.
|
|
||||||
#
|
|
||||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
|
||||||
#
|
|
||||||
# In Bash we could simply go:
|
|
||||||
#
|
|
||||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
|
||||||
# set -- "${ARGS[@]}" "$@"
|
|
||||||
#
|
|
||||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
|
||||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
|
||||||
# character that might be a shell metacharacter, then use eval to reverse
|
|
||||||
# that process (while maintaining the separation between arguments), and wrap
|
|
||||||
# the whole thing up as a single "set" statement.
|
|
||||||
#
|
|
||||||
# This will of course break if any of these variables contains a newline or
|
|
||||||
# an unmatched quote.
|
|
||||||
#
|
|
||||||
|
|
||||||
eval "set -- $(
|
|
||||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
|
||||||
xargs -n1 |
|
|
||||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
|
||||||
tr '\n' ' '
|
|
||||||
)" '"$@"'
|
|
||||||
|
|
||||||
exec "$JAVACMD" "$@"
|
|
||||||
94
example/android/gradlew.bat
vendored
@ -1,94 +0,0 @@
|
|||||||
@rem
|
|
||||||
@rem Copyright 2015 the original author or authors.
|
|
||||||
@rem
|
|
||||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
@rem you may not use this file except in compliance with the License.
|
|
||||||
@rem You may obtain a copy of the License at
|
|
||||||
@rem
|
|
||||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
@rem
|
|
||||||
@rem Unless required by applicable law or agreed to in writing, software
|
|
||||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
@rem See the License for the specific language governing permissions and
|
|
||||||
@rem limitations under the License.
|
|
||||||
@rem
|
|
||||||
@rem SPDX-License-Identifier: Apache-2.0
|
|
||||||
@rem
|
|
||||||
|
|
||||||
@if "%DEBUG%"=="" @echo off
|
|
||||||
@rem ##########################################################################
|
|
||||||
@rem
|
|
||||||
@rem Gradle startup script for Windows
|
|
||||||
@rem
|
|
||||||
@rem ##########################################################################
|
|
||||||
|
|
||||||
@rem Set local scope for the variables with windows NT shell
|
|
||||||
if "%OS%"=="Windows_NT" setlocal
|
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
|
||||||
if "%DIRNAME%"=="" set DIRNAME=.
|
|
||||||
@rem This is normally unused
|
|
||||||
set APP_BASE_NAME=%~n0
|
|
||||||
set APP_HOME=%DIRNAME%
|
|
||||||
|
|
||||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
|
||||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
|
||||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
|
||||||
|
|
||||||
@rem Find java.exe
|
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
|
||||||
|
|
||||||
set JAVA_EXE=java.exe
|
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
|
||||||
if %ERRORLEVEL% equ 0 goto execute
|
|
||||||
|
|
||||||
echo. 1>&2
|
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
|
||||||
echo. 1>&2
|
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
|
||||||
echo location of your Java installation. 1>&2
|
|
||||||
|
|
||||||
goto fail
|
|
||||||
|
|
||||||
:findJavaFromJavaHome
|
|
||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
|
||||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|
||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
|
||||||
|
|
||||||
echo. 1>&2
|
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
|
||||||
echo. 1>&2
|
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
|
||||||
echo location of your Java installation. 1>&2
|
|
||||||
|
|
||||||
goto fail
|
|
||||||
|
|
||||||
:execute
|
|
||||||
@rem Setup the command line
|
|
||||||
|
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|
||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
|
||||||
|
|
||||||
:end
|
|
||||||
@rem End local scope for the variables with windows NT shell
|
|
||||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
|
||||||
|
|
||||||
:fail
|
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
|
||||||
rem the _cmd.exe /c_ return code!
|
|
||||||
set EXIT_CODE=%ERRORLEVEL%
|
|
||||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
|
||||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
|
||||||
exit /b %EXIT_CODE%
|
|
||||||
|
|
||||||
:mainEnd
|
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
|
||||||
|
|
||||||
:omega
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") }
|
|
||||||
plugins { id("com.facebook.react.settings") }
|
|
||||||
extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
|
|
||||||
rootProject.name = 'MaskSegmentCanvasExample'
|
|
||||||
include ':app'
|
|
||||||
includeBuild('../node_modules/@react-native/gradle-plugin')
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "MaskSegmentCanvasExample",
|
|
||||||
"displayName": "MaskSegmentCanvas Example"
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 926 KiB |
|
Before Width: | Height: | Size: 695 KiB |
|
Before Width: | Height: | Size: 462 KiB |
|
Before Width: | Height: | Size: 1.5 MiB |
@ -1,4 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
presets: ['module:@react-native/babel-preset'],
|
|
||||||
plugins: ['react-native-reanimated/plugin'],
|
|
||||||
};
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
/**
|
|
||||||
* @format
|
|
||||||
*/
|
|
||||||
|
|
||||||
// [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
|
|
||||||
// 2. react-native-reanimated
|
|
||||||
// 3. @shopify/react-native-skia
|
|
||||||
//
|
|
||||||
// Used together with example/metro.config.js extraNodeModules + blockList,
|
|
||||||
// this completely avoids duplicate module issues in monorepo/file: setups:
|
|
||||||
// - SkiaPictureView config getter undefined
|
|
||||||
// - createAnimatedNode: Animated node already exists
|
|
||||||
// - Other Fabric / ViewManager conflicts
|
|
||||||
import 'react-native-gesture-handler';
|
|
||||||
import 'react-native-reanimated';
|
|
||||||
import '@shopify/react-native-skia';
|
|
||||||
|
|
||||||
import { Buffer } from 'buffer';
|
|
||||||
global.Buffer = global.Buffer || Buffer;
|
|
||||||
|
|
||||||
import { AppRegistry } from 'react-native';
|
|
||||||
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
||||||
import App from './App';
|
|
||||||
import { name as appName } from './app.json';
|
|
||||||
|
|
||||||
AppRegistry.registerComponent(appName, () => RootComponent);
|
|
||||||
|
|
||||||
function RootComponent() {
|
|
||||||
return (
|
|
||||||
<GestureHandlerRootView style={{ flex: 1 }}>
|
|
||||||
<App />
|
|
||||||
</GestureHandlerRootView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
# This `.xcode.env` file is versioned and is used to source the environment
|
|
||||||
# used when running script phases inside Xcode.
|
|
||||||
# To customize your local environment, you can create an `.xcode.env.local`
|
|
||||||
# file that is not versioned.
|
|
||||||
|
|
||||||
# NODE_BINARY variable contains the PATH to the node executable.
|
|
||||||
#
|
|
||||||
# Customize the NODE_BINARY variable here.
|
|
||||||
# For example, to use nvm with brew, add the following line
|
|
||||||
# . "$(brew --prefix nvm)/nvm.sh" --no-use
|
|
||||||
export NODE_BINARY=$(command -v node)
|
|
||||||
@ -1,505 +0,0 @@
|
|||||||
// !$*UTF8*$!
|
|
||||||
{
|
|
||||||
archiveVersion = 1;
|
|
||||||
classes = {
|
|
||||||
};
|
|
||||||
objectVersion = 54;
|
|
||||||
objects = {
|
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
|
||||||
0C80B921A6F3F58F76C31292 /* libPods-MaskSegmentCanvasExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-MaskSegmentCanvasExample.a */; };
|
|
||||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
|
||||||
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; };
|
|
||||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
|
|
||||||
/* End PBXBuildFile section */
|
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
|
||||||
00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = {
|
|
||||||
isa = PBXContainerItemProxy;
|
|
||||||
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
|
||||||
proxyType = 1;
|
|
||||||
remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
|
|
||||||
remoteInfo = MaskSegmentCanvasExample;
|
|
||||||
};
|
|
||||||
/* End PBXContainerItemProxy section */
|
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
|
||||||
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
|
||||||
13B07F961A680F5B00A75B9A /* MaskSegmentCanvasExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MaskSegmentCanvasExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = MaskSegmentCanvasExample/Images.xcassets; sourceTree = "<group>"; };
|
|
||||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = MaskSegmentCanvasExample/Info.plist; sourceTree = "<group>"; };
|
|
||||||
13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = MaskSegmentCanvasExample/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
|
||||||
3B4392A12AC88292D35C810B /* Pods-MaskSegmentCanvasExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MaskSegmentCanvasExample.debug.xcconfig"; path = "Target Support Files/Pods-MaskSegmentCanvasExample/Pods-MaskSegmentCanvasExample.debug.xcconfig"; sourceTree = "<group>"; };
|
|
||||||
5709B34CF0A7D63546082F79 /* Pods-MaskSegmentCanvasExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MaskSegmentCanvasExample.release.xcconfig"; path = "Target Support Files/Pods-MaskSegmentCanvasExample/Pods-MaskSegmentCanvasExample.release.xcconfig"; sourceTree = "<group>"; };
|
|
||||||
5DCACB8F33CDC322A6C60F78 /* libPods-MaskSegmentCanvasExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MaskSegmentCanvasExample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = MaskSegmentCanvasExample/AppDelegate.swift; sourceTree = "<group>"; };
|
|
||||||
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = MaskSegmentCanvasExample/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
|
||||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
|
||||||
/* End PBXFileReference section */
|
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
|
||||||
13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
|
|
||||||
isa = PBXFrameworksBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
0C80B921A6F3F58F76C31292 /* libPods-MaskSegmentCanvasExample.a in Frameworks */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXFrameworksBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
|
||||||
00E356F01AD99517003FC87E /* Supporting Files */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
00E356F11AD99517003FC87E /* Info.plist */,
|
|
||||||
);
|
|
||||||
name = "Supporting Files";
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
13B07FAE1A68108700A75B9A /* MaskSegmentCanvasExample */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
13B07FB51A68108700A75B9A /* Images.xcassets */,
|
|
||||||
761780EC2CA45674006654EE /* AppDelegate.swift */,
|
|
||||||
13B07FB61A68108700A75B9A /* Info.plist */,
|
|
||||||
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
|
|
||||||
13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */,
|
|
||||||
);
|
|
||||||
name = MaskSegmentCanvasExample;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
|
|
||||||
5DCACB8F33CDC322A6C60F78 /* libPods-MaskSegmentCanvasExample.a */,
|
|
||||||
);
|
|
||||||
name = Frameworks;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
);
|
|
||||||
name = Libraries;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
83CBB9F61A601CBA00E9B192 = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
13B07FAE1A68108700A75B9A /* MaskSegmentCanvasExample */,
|
|
||||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
|
||||||
83CBBA001A601CBA00E9B192 /* Products */,
|
|
||||||
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
|
||||||
BBD78D7AC51CEA395F1C20DB /* Pods */,
|
|
||||||
);
|
|
||||||
indentWidth = 2;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
tabWidth = 2;
|
|
||||||
usesTabs = 0;
|
|
||||||
};
|
|
||||||
83CBBA001A601CBA00E9B192 /* Products */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
13B07F961A680F5B00A75B9A /* MaskSegmentCanvasExample.app */,
|
|
||||||
);
|
|
||||||
name = Products;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
BBD78D7AC51CEA395F1C20DB /* Pods */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
3B4392A12AC88292D35C810B /* Pods-MaskSegmentCanvasExample.debug.xcconfig */,
|
|
||||||
5709B34CF0A7D63546082F79 /* Pods-MaskSegmentCanvasExample.release.xcconfig */,
|
|
||||||
);
|
|
||||||
path = Pods;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
/* End PBXGroup section */
|
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
|
||||||
13B07F861A680F5B00A75B9A /* MaskSegmentCanvasExample */ = {
|
|
||||||
isa = PBXNativeTarget;
|
|
||||||
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "MaskSegmentCanvasExample" */;
|
|
||||||
buildPhases = (
|
|
||||||
C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */,
|
|
||||||
13B07F871A680F5B00A75B9A /* Sources */,
|
|
||||||
13B07F8C1A680F5B00A75B9A /* Frameworks */,
|
|
||||||
13B07F8E1A680F5B00A75B9A /* Resources */,
|
|
||||||
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
|
||||||
00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */,
|
|
||||||
E235C05ADACE081382539298 /* [CP] Copy Pods Resources */,
|
|
||||||
);
|
|
||||||
buildRules = (
|
|
||||||
);
|
|
||||||
dependencies = (
|
|
||||||
);
|
|
||||||
name = MaskSegmentCanvasExample;
|
|
||||||
productName = MaskSegmentCanvasExample;
|
|
||||||
productReference = 13B07F961A680F5B00A75B9A /* MaskSegmentCanvasExample.app */;
|
|
||||||
productType = "com.apple.product-type.application";
|
|
||||||
};
|
|
||||||
/* End PBXNativeTarget section */
|
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
|
||||||
83CBB9F71A601CBA00E9B192 /* Project object */ = {
|
|
||||||
isa = PBXProject;
|
|
||||||
attributes = {
|
|
||||||
LastUpgradeCheck = 1210;
|
|
||||||
TargetAttributes = {
|
|
||||||
13B07F861A680F5B00A75B9A = {
|
|
||||||
LastSwiftMigration = 1120;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "MaskSegmentCanvasExample" */;
|
|
||||||
compatibilityVersion = "Xcode 12.0";
|
|
||||||
developmentRegion = en;
|
|
||||||
hasScannedForEncodings = 0;
|
|
||||||
knownRegions = (
|
|
||||||
en,
|
|
||||||
Base,
|
|
||||||
);
|
|
||||||
mainGroup = 83CBB9F61A601CBA00E9B192;
|
|
||||||
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
|
|
||||||
projectDirPath = "";
|
|
||||||
projectRoot = "";
|
|
||||||
targets = (
|
|
||||||
13B07F861A680F5B00A75B9A /* MaskSegmentCanvasExample */,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
/* End PBXProject section */
|
|
||||||
|
|
||||||
/* Begin PBXResourcesBuildPhase section */
|
|
||||||
00E356EC1AD99517003FC87E /* Resources */ = {
|
|
||||||
isa = PBXResourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
13B07F8E1A680F5B00A75B9A /* Resources */ = {
|
|
||||||
isa = PBXResourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
|
|
||||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXResourcesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
|
||||||
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputPaths = (
|
|
||||||
"$(SRCROOT)/.xcode.env.local",
|
|
||||||
"$(SRCROOT)/.xcode.env",
|
|
||||||
);
|
|
||||||
name = "Bundle React Native code and images";
|
|
||||||
outputPaths = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n";
|
|
||||||
};
|
|
||||||
00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputFileListPaths = (
|
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-MaskSegmentCanvasExample/Pods-MaskSegmentCanvasExample-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
|
||||||
);
|
|
||||||
name = "[CP] Embed Pods Frameworks";
|
|
||||||
outputFileListPaths = (
|
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-MaskSegmentCanvasExample/Pods-MaskSegmentCanvasExample-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MaskSegmentCanvasExample/Pods-MaskSegmentCanvasExample-frameworks.sh\"\n";
|
|
||||||
showEnvVarsInLog = 0;
|
|
||||||
};
|
|
||||||
C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputFileListPaths = (
|
|
||||||
);
|
|
||||||
inputPaths = (
|
|
||||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
|
||||||
"${PODS_ROOT}/Manifest.lock",
|
|
||||||
);
|
|
||||||
name = "[CP] Check Pods Manifest.lock";
|
|
||||||
outputFileListPaths = (
|
|
||||||
);
|
|
||||||
outputPaths = (
|
|
||||||
"$(DERIVED_FILE_DIR)/Pods-MaskSegmentCanvasExample-checkManifestLockResult.txt",
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
|
||||||
showEnvVarsInLog = 0;
|
|
||||||
};
|
|
||||||
E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputFileListPaths = (
|
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-MaskSegmentCanvasExample/Pods-MaskSegmentCanvasExample-resources-${CONFIGURATION}-input-files.xcfilelist",
|
|
||||||
);
|
|
||||||
name = "[CP] Copy Pods Resources";
|
|
||||||
outputFileListPaths = (
|
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-MaskSegmentCanvasExample/Pods-MaskSegmentCanvasExample-resources-${CONFIGURATION}-output-files.xcfilelist",
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MaskSegmentCanvasExample/Pods-MaskSegmentCanvasExample-resources.sh\"\n";
|
|
||||||
showEnvVarsInLog = 0;
|
|
||||||
};
|
|
||||||
/* End PBXShellScriptBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
|
||||||
13B07F871A680F5B00A75B9A /* Sources */ = {
|
|
||||||
isa = PBXSourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXSourcesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXTargetDependency section */
|
|
||||||
00E356F51AD99517003FC87E /* PBXTargetDependency */ = {
|
|
||||||
isa = PBXTargetDependency;
|
|
||||||
target = 13B07F861A680F5B00A75B9A /* MaskSegmentCanvasExample */;
|
|
||||||
targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
|
|
||||||
};
|
|
||||||
/* End PBXTargetDependency section */
|
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
|
||||||
13B07F941A680F5B00A75B9A /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-MaskSegmentCanvasExample.debug.xcconfig */;
|
|
||||||
buildSettings = {
|
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
|
||||||
CLANG_ENABLE_MODULES = YES;
|
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
|
||||||
ENABLE_BITCODE = NO;
|
|
||||||
INFOPLIST_FILE = MaskSegmentCanvasExample/Info.plist;
|
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
|
||||||
"$(inherited)",
|
|
||||||
"@executable_path/Frameworks",
|
|
||||||
);
|
|
||||||
MARKETING_VERSION = 1.0;
|
|
||||||
OTHER_LDFLAGS = (
|
|
||||||
"$(inherited)",
|
|
||||||
"-ObjC",
|
|
||||||
"-lc++",
|
|
||||||
);
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
|
||||||
PRODUCT_NAME = MaskSegmentCanvasExample;
|
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
|
||||||
SWIFT_VERSION = 5.0;
|
|
||||||
VERSIONING_SYSTEM = "apple-generic";
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
13B07F951A680F5B00A75B9A /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-MaskSegmentCanvasExample.release.xcconfig */;
|
|
||||||
buildSettings = {
|
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
|
||||||
CLANG_ENABLE_MODULES = YES;
|
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
|
||||||
INFOPLIST_FILE = MaskSegmentCanvasExample/Info.plist;
|
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
|
||||||
"$(inherited)",
|
|
||||||
"@executable_path/Frameworks",
|
|
||||||
);
|
|
||||||
MARKETING_VERSION = 1.0;
|
|
||||||
OTHER_LDFLAGS = (
|
|
||||||
"$(inherited)",
|
|
||||||
"-ObjC",
|
|
||||||
"-lc++",
|
|
||||||
);
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
|
||||||
PRODUCT_NAME = MaskSegmentCanvasExample;
|
|
||||||
SWIFT_VERSION = 5.0;
|
|
||||||
VERSIONING_SYSTEM = "apple-generic";
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
83CBBA201A601CBA00E9B192 /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
||||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
|
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
|
||||||
CLANG_ENABLE_MODULES = YES;
|
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
|
||||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
|
||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_COMMA = YES;
|
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
|
||||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
|
||||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
|
||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
|
||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
|
||||||
COPY_PHASE_STRIP = NO;
|
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
|
||||||
ENABLE_TESTABILITY = YES;
|
|
||||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
|
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
|
||||||
GCC_DYNAMIC_NO_PIC = NO;
|
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
|
||||||
"DEBUG=1",
|
|
||||||
"$(inherited)",
|
|
||||||
);
|
|
||||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
|
||||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
|
||||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
|
||||||
/usr/lib/swift,
|
|
||||||
"$(inherited)",
|
|
||||||
);
|
|
||||||
LIBRARY_SEARCH_PATHS = (
|
|
||||||
"\"$(SDKROOT)/usr/lib/swift\"",
|
|
||||||
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
|
|
||||||
"\"$(inherited)\"",
|
|
||||||
);
|
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
|
||||||
OTHER_CPLUSPLUSFLAGS = (
|
|
||||||
"$(OTHER_CFLAGS)",
|
|
||||||
"-DFOLLY_NO_CONFIG",
|
|
||||||
"-DFOLLY_MOBILE=1",
|
|
||||||
"-DFOLLY_USE_LIBCPP=1",
|
|
||||||
"-DFOLLY_CFG_NO_COROUTINES=1",
|
|
||||||
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
|
|
||||||
);
|
|
||||||
SDKROOT = iphoneos;
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
83CBBA211A601CBA00E9B192 /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
||||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
|
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
|
||||||
CLANG_ENABLE_MODULES = YES;
|
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
|
||||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
|
||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_COMMA = YES;
|
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
|
||||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
|
||||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
|
||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
|
||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
|
||||||
COPY_PHASE_STRIP = YES;
|
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
|
||||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
|
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
|
||||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
|
||||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
|
||||||
/usr/lib/swift,
|
|
||||||
"$(inherited)",
|
|
||||||
);
|
|
||||||
LIBRARY_SEARCH_PATHS = (
|
|
||||||
"\"$(SDKROOT)/usr/lib/swift\"",
|
|
||||||
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
|
|
||||||
"\"$(inherited)\"",
|
|
||||||
);
|
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
|
||||||
OTHER_CPLUSPLUSFLAGS = (
|
|
||||||
"$(OTHER_CFLAGS)",
|
|
||||||
"-DFOLLY_NO_CONFIG",
|
|
||||||
"-DFOLLY_MOBILE=1",
|
|
||||||
"-DFOLLY_USE_LIBCPP=1",
|
|
||||||
"-DFOLLY_CFG_NO_COROUTINES=1",
|
|
||||||
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
|
|
||||||
);
|
|
||||||
SDKROOT = iphoneos;
|
|
||||||
VALIDATE_PRODUCT = YES;
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
/* End XCBuildConfiguration section */
|
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
|
||||||
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "MaskSegmentCanvasExample" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
13B07F941A680F5B00A75B9A /* Debug */,
|
|
||||||
13B07F951A680F5B00A75B9A /* Release */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Release;
|
|
||||||
};
|
|
||||||
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "MaskSegmentCanvasExample" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
83CBBA201A601CBA00E9B192 /* Debug */,
|
|
||||||
83CBBA211A601CBA00E9B192 /* Release */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Release;
|
|
||||||
};
|
|
||||||
/* End XCConfigurationList section */
|
|
||||||
};
|
|
||||||
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
|
||||||
}
|
|
||||||
@ -1,88 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1210"
|
|
||||||
version = "1.3">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
|
||||||
BuildableName = "MaskSegmentCanvasExample.app"
|
|
||||||
BlueprintName = "MaskSegmentCanvasExample"
|
|
||||||
ReferencedContainer = "container:MaskSegmentCanvasExample.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
||||||
<Testables>
|
|
||||||
<TestableReference
|
|
||||||
skipped = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
|
|
||||||
BuildableName = "MaskSegmentCanvasExampleTests.xctest"
|
|
||||||
BlueprintName = "MaskSegmentCanvasExampleTests"
|
|
||||||
ReferencedContainer = "container:MaskSegmentCanvasExample.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</TestableReference>
|
|
||||||
</Testables>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
|
||||||
BuildableName = "MaskSegmentCanvasExample.app"
|
|
||||||
BlueprintName = "MaskSegmentCanvasExample"
|
|
||||||
ReferencedContainer = "container:MaskSegmentCanvasExample.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
|
||||||
BuildableName = "MaskSegmentCanvasExample.app"
|
|
||||||
BlueprintName = "MaskSegmentCanvasExample"
|
|
||||||
ReferencedContainer = "container:MaskSegmentCanvasExample.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
import UIKit
|
|
||||||
import React
|
|
||||||
import React_RCTAppDelegate
|
|
||||||
import ReactAppDependencyProvider
|
|
||||||
|
|
||||||
@main
|
|
||||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
|
||||||
var window: UIWindow?
|
|
||||||
|
|
||||||
var reactNativeDelegate: ReactNativeDelegate?
|
|
||||||
var reactNativeFactory: RCTReactNativeFactory?
|
|
||||||
|
|
||||||
func application(
|
|
||||||
_ application: UIApplication,
|
|
||||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
|
|
||||||
) -> Bool {
|
|
||||||
let delegate = ReactNativeDelegate()
|
|
||||||
let factory = RCTReactNativeFactory(delegate: delegate)
|
|
||||||
delegate.dependencyProvider = RCTAppDependencyProvider()
|
|
||||||
|
|
||||||
reactNativeDelegate = delegate
|
|
||||||
reactNativeFactory = factory
|
|
||||||
|
|
||||||
window = UIWindow(frame: UIScreen.main.bounds)
|
|
||||||
|
|
||||||
factory.startReactNative(
|
|
||||||
withModuleName: "MaskSegmentCanvasExample",
|
|
||||||
in: window,
|
|
||||||
launchOptions: launchOptions
|
|
||||||
)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate {
|
|
||||||
override func sourceURL(for bridge: RCTBridge) -> URL? {
|
|
||||||
self.bundleURL()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func bundleURL() -> URL? {
|
|
||||||
#if DEBUG
|
|
||||||
RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
|
|
||||||
#else
|
|
||||||
Bundle.main.url(forResource: "main", withExtension: "jsbundle")
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "20x20"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "3x",
|
|
||||||
"size" : "20x20"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "29x29"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "3x",
|
|
||||||
"size" : "29x29"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "40x40"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "3x",
|
|
||||||
"size" : "40x40"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "60x60"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "3x",
|
|
||||||
"size" : "60x60"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "ios-marketing",
|
|
||||||
"scale" : "1x",
|
|
||||||
"size" : "1024x1024"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"info" : {
|
|
||||||
"version" : 1,
|
|
||||||
"author" : "xcode"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>en</string>
|
|
||||||
<key>CFBundleDisplayName</key>
|
|
||||||
<string>MaskSegmentCanvas Example</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>$(PRODUCT_NAME)</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>APPL</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>$(MARKETING_VERSION)</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
|
||||||
<key>LSRequiresIPhoneOS</key>
|
|
||||||
<true/>
|
|
||||||
<key>NSAppTransportSecurity</key>
|
|
||||||
<dict>
|
|
||||||
<!-- Do not change NSAllowsArbitraryLoads to true, or you will risk app rejection! -->
|
|
||||||
<key>NSAllowsArbitraryLoads</key>
|
|
||||||
<false/>
|
|
||||||
<key>NSAllowsLocalNetworking</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
<key>NSLocationWhenInUseUsageDescription</key>
|
|
||||||
<string></string>
|
|
||||||
<key>UILaunchStoryboardName</key>
|
|
||||||
<string>LaunchScreen</string>
|
|
||||||
<key>UIRequiredDeviceCapabilities</key>
|
|
||||||
<array>
|
|
||||||
<string>arm64</string>
|
|
||||||
</array>
|
|
||||||
<key>UISupportedInterfaceOrientations</key>
|
|
||||||
<array>
|
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
|
||||||
</array>
|
|
||||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
|
||||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
|
||||||
<dependencies>
|
|
||||||
<deployment identifier="iOS"/>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
|
||||||
</dependencies>
|
|
||||||
<scenes>
|
|
||||||
<!--View Controller-->
|
|
||||||
<scene sceneID="EHf-IW-A2E">
|
|
||||||
<objects>
|
|
||||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
|
||||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="MaskSegmentCanvasExample" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="GJd-Yh-RWb">
|
|
||||||
<rect key="frame" x="0.0" y="202" width="375" height="43"/>
|
|
||||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Powered by React Native" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="MN2-I3-ftu">
|
|
||||||
<rect key="frame" x="0.0" y="626" width="375" height="21"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
</subviews>
|
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="Bcu-3y-fUS" firstAttribute="bottom" secondItem="MN2-I3-ftu" secondAttribute="bottom" constant="20" id="OZV-Vh-mqD"/>
|
|
||||||
<constraint firstItem="Bcu-3y-fUS" firstAttribute="centerX" secondItem="GJd-Yh-RWb" secondAttribute="centerX" id="Q3B-4B-g5h"/>
|
|
||||||
<constraint firstItem="MN2-I3-ftu" firstAttribute="centerX" secondItem="Bcu-3y-fUS" secondAttribute="centerX" id="akx-eg-2ui"/>
|
|
||||||
<constraint firstItem="MN2-I3-ftu" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" id="i1E-0Y-4RG"/>
|
|
||||||
<constraint firstItem="GJd-Yh-RWb" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="bottom" multiplier="1/3" constant="1" id="moa-c2-u7t"/>
|
|
||||||
<constraint firstItem="GJd-Yh-RWb" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" symbolic="YES" id="x7j-FC-K8j"/>
|
|
||||||
</constraints>
|
|
||||||
<viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>
|
|
||||||
</view>
|
|
||||||
</viewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="52.173913043478265" y="375"/>
|
|
||||||
</scene>
|
|
||||||
</scenes>
|
|
||||||
</document>
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>NSPrivacyAccessedAPITypes</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>NSPrivacyAccessedAPIType</key>
|
|
||||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
|
||||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
||||||
<array>
|
|
||||||
<string>C617.1</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>NSPrivacyAccessedAPIType</key>
|
|
||||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
|
||||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
||||||
<array>
|
|
||||||
<string>CA92.1</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>NSPrivacyAccessedAPIType</key>
|
|
||||||
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
|
|
||||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
||||||
<array>
|
|
||||||
<string>35F9.1</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>NSPrivacyCollectedDataTypes</key>
|
|
||||||
<array/>
|
|
||||||
<key>NSPrivacyTracking</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
# Resolve react_native_pods.rb with node to allow for hoisting
|
|
||||||
require Pod::Executable.execute_command('node', ['-p',
|
|
||||||
'require.resolve(
|
|
||||||
"react-native/scripts/react_native_pods.rb",
|
|
||||||
{paths: [process.argv[1]]},
|
|
||||||
)', __dir__]).strip
|
|
||||||
|
|
||||||
platform :ios, min_ios_version_supported
|
|
||||||
prepare_react_native_project!
|
|
||||||
|
|
||||||
linkage = ENV['USE_FRAMEWORKS']
|
|
||||||
if linkage != nil
|
|
||||||
Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
|
|
||||||
use_frameworks! :linkage => linkage.to_sym
|
|
||||||
end
|
|
||||||
|
|
||||||
target 'MaskSegmentCanvasExample' do
|
|
||||||
config = use_native_modules!
|
|
||||||
|
|
||||||
use_react_native!(
|
|
||||||
:path => config[:reactNativePath],
|
|
||||||
# An absolute path to your application root.
|
|
||||||
:app_path => "#{Pod::Config.instance.installation_root}/.."
|
|
||||||
)
|
|
||||||
|
|
||||||
post_install do |installer|
|
|
||||||
# https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
|
|
||||||
react_native_post_install(
|
|
||||||
installer,
|
|
||||||
config[:reactNativePath],
|
|
||||||
:mac_catalyst_enabled => false,
|
|
||||||
# :ccache_enabled => true
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||