1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-05-14 14:35:38 +02:00

Merge branch 'develop' into room

This commit is contained in:
syuilo
2026-03-15 16:38:33 +09:00
111 changed files with 3292 additions and 2748 deletions

View File

@@ -10,6 +10,10 @@ const float PI = 3.141592653589793;
const float TWO_PI = 6.283185307179586;
const float HALF_PI = 1.5707963267948966;
const float goldenAngle = 2.39996323;
const int sampleCount = 256;
const float sampleCountF = float(sampleCount);
in vec2 in_uv;
uniform sampler2D in_texture;
uniform vec2 in_resolution;
@@ -18,7 +22,6 @@ uniform vec2 u_scale;
uniform bool u_ellipse;
uniform float u_angle;
uniform float u_radius;
uniform int u_samples;
out vec4 out_color;
float rand(vec2 value) {
@@ -51,17 +54,7 @@ void main() {
vec4 result = vec4(0.0);
float totalSamples = 0.0;
// Make blur radius resolution-independent by using a percentage of image size
float referenceSize = min(in_resolution.x, in_resolution.y);
float normalizedRadius = u_radius / 100.0;
float radiusPx = normalizedRadius * referenceSize;
vec2 texelSize = 1.0 / in_resolution;
int sampleCount = max(u_samples, 1);
float sampleCountF = float(sampleCount);
float jitter = rand(in_uv * in_resolution);
float goldenAngle = 2.39996323;
float jitter = rand(in_uv);
// Sample in a circular pattern to avoid axis-aligned artifacts
for (int i = 0; i < sampleCount; i++) {
@@ -69,15 +62,11 @@ void main() {
float radius = sqrt((fi + 0.5) / sampleCountF);
float theta = (fi + jitter) * goldenAngle;
vec2 direction = vec2(cos(theta), sin(theta));
vec2 offset = direction * (radiusPx * radius) * texelSize;
vec2 sampleUV = in_uv + offset;
if (sampleUV.x >= 0.0 && sampleUV.x <= 1.0 && sampleUV.y >= 0.0 && sampleUV.y <= 1.0) {
float weight = exp(-radius * radius * 4.0);
result += texture(in_texture, sampleUV) * weight;
totalSamples += weight;
}
vec2 offset = direction * (u_radius * radius);
float weight = exp(-radius * radius * 4.0);
result += texture(in_texture, in_uv + offset) * weight;
totalSamples += weight;
}
out_color = totalSamples > 0.0 ? result / totalSamples : texture(in_texture, in_uv);
out_color = result / totalSamples;
}

View File

@@ -24,7 +24,6 @@ export const fn = defineImageCompositorFunction<{
gl.uniform1i(u.ellipse, params.ellipse ? 1 : 0);
gl.uniform1f(u.angle, params.angle / 2);
gl.uniform1f(u.radius, params.radius);
gl.uniform1i(u.samples, 256);
},
});
@@ -84,10 +83,10 @@ export const uiDefinition = {
radius: {
label: i18n.ts._imageEffector._fxProps.strength,
type: 'number',
default: 10.0,
default: 0.15,
min: 0.0,
max: 20.0,
step: 0.5,
max: 0.3,
step: 0.01,
},
},
} satisfies ImageEffectorUiDefinition<typeof fn>;

View File

@@ -14,12 +14,15 @@ uniform sampler2D in_texture;
uniform vec2 in_resolution;
uniform vec2 u_pos;
uniform float u_frequency;
uniform bool u_thresholdEnabled;
uniform float u_threshold;
uniform float u_outlineThickness;
uniform float u_maskSize;
uniform bool u_black;
out vec4 out_color;
float remap(float value, float inputMin, float inputMax, float outputMin, float outputMax) {
return outputMin + (outputMax - outputMin) * ((value - inputMin) / (inputMax - inputMin));
}
void main() {
vec4 in_color = texture(in_texture, in_uv);
vec2 centeredUv = (in_uv - vec2(0.5, 0.5));
@@ -33,16 +36,19 @@ void main() {
float noiseY = (noiseUV.y + seed) * u_frequency;
float noise = (1.0 + snoise(vec3(noiseX, noiseY, time))) / 2.0;
float t = noise;
if (u_thresholdEnabled) t = t < u_threshold ? 1.0 : 0.0;
if (noise < u_threshold) {
out_color = in_color;
} else {
float n = remap(noise, u_threshold, 1.0, 0.0, 1.0);
// TODO: マスクの形自体も揺らぎを与える
float d = distance(uv * vec2(2.0, 2.0), u_pos * vec2(2.0, 2.0));
float mask = d < u_maskSize ? 0.0 : ((d - u_maskSize) * (1.0 + (u_maskSize * 2.0)));
out_color = vec4(
mix(in_color.r, u_black ? 0.0 : 1.0, t * mask),
mix(in_color.g, u_black ? 0.0 : 1.0, t * mask),
mix(in_color.b, u_black ? 0.0 : 1.0, t * mask),
in_color.a
);
// TODO: マスクの形自体も揺らぎを与える
float d = distance(uv * vec2(2.0, 2.0), u_pos * vec2(2.0, 2.0));
float mask = d < u_maskSize ? 0.0 : ((d - u_maskSize) * (1.0 + (u_maskSize * 2.0)));
out_color = vec4(
mix(in_color.r, n < u_outlineThickness ? 0.0 : 1.0, mask),
mix(in_color.g, n < u_outlineThickness ? 0.0 : 1.0, mask),
mix(in_color.b, n < u_outlineThickness ? 0.0 : 1.0, mask),
in_color.a
);
}
}

View File

@@ -12,20 +12,17 @@ export const fn = defineImageCompositorFunction<{
x: number;
y: number;
frequency: number;
smoothing: boolean;
threshold: number;
density: number;
outlineThickness: number;
maskSize: number;
black: boolean;
}>({
shader,
main: ({ gl, u, params }) => {
gl.uniform2f(u.pos, params.x / 2, params.y / 2);
gl.uniform1f(u.frequency, params.frequency * params.frequency);
// thresholdの調整が有効な間はsmoothingが利用できない
gl.uniform1i(u.thresholdEnabled, params.smoothing ? 0 : 1);
gl.uniform1f(u.threshold, params.threshold);
gl.uniform1f(u.threshold, 1.0 - params.density);
gl.uniform1f(u.outlineThickness, params.outlineThickness);
gl.uniform1f(u.maskSize, params.maskSize);
gl.uniform1i(u.black, params.black ? 1 : 0);
},
});
@@ -56,20 +53,22 @@ export const uiDefinition = {
max: 15.0,
step: 0.1,
},
smoothing: {
label: i18n.ts._imageEffector._fxProps.zoomLinesSmoothing,
caption: i18n.ts._imageEffector._fxProps.zoomLinesSmoothingDescription,
type: 'boolean',
default: false,
},
threshold: {
label: i18n.ts._imageEffector._fxProps.zoomLinesThreshold,
density: {
label: i18n.ts._imageEffector._fxProps.density,
type: 'number',
default: 0.5,
min: 0.0,
max: 1.0,
step: 0.01,
},
outlineThickness: {
label: i18n.ts._imageEffector._fxProps.zoomLinesOutlineThickness,
type: 'number',
default: 0.25,
min: 0.0,
max: 1.0,
step: 0.01,
},
maskSize: {
label: i18n.ts._imageEffector._fxProps.zoomLinesMaskSize,
type: 'number',
@@ -78,10 +77,5 @@ export const uiDefinition = {
max: 1.0,
step: 0.01,
},
black: {
label: i18n.ts._imageEffector._fxProps.zoomLinesBlack,
type: 'boolean',
default: false,
},
},
} satisfies ImageEffectorUiDefinition<typeof fn>;

View File

@@ -3,13 +3,21 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { ref } from 'vue';
import { readonly, ref } from 'vue';
import * as os from '@/os.js';
import { store } from '@/store.js';
import { i18n } from '@/i18n.js';
export const storagePersistenceSupported = window.isSecureContext && 'storage' in navigator;
export const storagePersisted = ref(storagePersistenceSupported ? await navigator.storage.persisted() : false);
const storagePersisted = ref(false);
export async function getStoragePersistenceStatusRef() {
if (storagePersistenceSupported) {
storagePersisted.value = await navigator.storage.persisted().catch(() => false);
}
return readonly(storagePersisted);
}
export async function enableStoragePersistence() {
if (!storagePersistenceSupported) return;