From 4ba18690d7abd7eea086bb59e6cbcc8ead9e121a Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 6 Nov 2025 20:25:17 +0900 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20EXIF=E3=83=95=E3=83=AC?= =?UTF-8?q?=E3=83=BC=E3=83=A0=E6=A9=9F=E8=83=BD=20(#16725)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * wip * wip * Update ImageEffector.ts * Update image-label-renderer.ts * Update image-label-renderer.ts * wip * Update image-label-renderer.ts * wip * wip * wip * wip * wip * wip * wip * Update use-uploader.ts * Update watermark.ts * wip * wu * wip * Update image-frame-renderer.ts * wip * wip * Update image-frame-renderer.ts * Create ImageCompositor.ts * Update ImageCompositor.ts * wip * wip * Update ImageEffector.ts * wip * Update use-uploader.ts * wip * wip * wip * wip * Update fxs.ts * wip * wip * wip * Update CHANGELOG.md * wip * wip * Update MkImageEffectorDialog.vue * Update MkImageEffectorDialog.vue * Update MkImageFrameEditorDialog.vue * Update use-uploader.ts * improve error handling * Update use-uploader.ts * 🎨 * wip * wip * lazy load * lazy load * wip * wip * wip --- CHANGELOG.md | 2 + locales/index.d.ts | 176 +++++- locales/ja-JP.yml | 49 +- packages/frontend/package.json | 1 + .../src/components/MkEmbedCodeGenDialog.vue | 23 +- .../MkImageEffectorDialog.Layer.vue | 12 +- .../src/components/MkImageEffectorDialog.vue | 89 +-- .../components/MkImageFrameEditorDialog.vue | 509 ++++++++++++++++++ .../MkWatermarkEditorDialog.Layer.vue | 2 +- .../components/MkWatermarkEditorDialog.vue | 162 ++++-- .../frontend/src/composables/use-uploader.ts | 186 +++++-- packages/frontend/src/lib/ImageCompositor.ts | 313 +++++++++++ packages/frontend/src/lib/pizzax.ts | 2 + .../pages/settings/drive.ImageFrameItem.vue | 113 ++++ .../pages/settings/drive.WatermarkItem.vue | 15 +- .../frontend/src/pages/settings/drive.vue | 86 ++- packages/frontend/src/preferences/def.ts | 23 +- .../blockNoise.glsl | 0 .../blockNoise.ts | 59 +- .../blur.glsl | 0 .../blur.ts | 38 +- .../checker.glsl | 0 .../checker.ts | 31 +- .../chromaticAberration.glsl | 0 .../chromaticAberration.ts | 25 +- .../colorAdjust.glsl | 0 .../colorAdjust.ts | 34 +- .../colorClamp.glsl | 0 .../colorClamp.ts | 33 +- .../colorClampAdvanced.ts | 37 +- .../distort.glsl | 0 .../distort.ts | 31 +- .../fill.glsl | 0 .../fill.ts | 39 +- .../grayscale.glsl | 0 .../image-compositor-functions/grayscale.ts | 21 + .../invert.glsl | 0 .../invert.ts | 28 +- .../mirror.glsl | 0 .../mirror.ts | 29 +- .../pixelate.glsl | 0 .../pixelate.ts | 38 +- .../polkadot.glsl | 0 .../polkadot.ts | 44 +- .../stripe.glsl | 0 .../stripe.ts | 37 +- .../tearing.glsl | 0 .../tearing.ts | 54 +- .../threshold.glsl | 0 .../threshold.ts | 28 +- .../zoomLines.glsl | 0 .../zoomLines.ts | 40 +- .../utility/image-effector/ImageEffector.ts | 484 ++--------------- .../src/utility/image-effector/fxs.ts | 82 +-- .../utility/image-effector/fxs/grayscale.ts | 19 - .../ImageFrameRenderer.ts | 270 ++++++++++ .../utility/image-frame-renderer/frame.glsl | 61 +++ .../src/utility/image-frame-renderer/frame.ts | 57 ++ packages/frontend/src/utility/watermark.ts | 218 -------- .../utility/watermark/WatermarkRenderer.ts | 332 ++++++++++++ .../watermark.glsl} | 0 .../watermark.ts} | 63 +-- packages/frontend/src/utility/webgl.ts | 11 + pnpm-lock.yaml | 18 + 64 files changed, 2838 insertions(+), 1186 deletions(-) create mode 100644 packages/frontend/src/components/MkImageFrameEditorDialog.vue create mode 100644 packages/frontend/src/lib/ImageCompositor.ts create mode 100644 packages/frontend/src/pages/settings/drive.ImageFrameItem.vue rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/blockNoise.glsl (100%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/blockNoise.ts (83%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/blur.glsl (100%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/blur.ts (81%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/checker.glsl (100%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/checker.ts (74%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/chromaticAberration.glsl (100%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/chromaticAberration.ts (66%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/colorAdjust.glsl (100%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/colorAdjust.ts (79%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/colorClamp.glsl (100%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/colorClamp.ts (73%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/colorClampAdvanced.ts (83%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/distort.glsl (100%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/distort.ts (76%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/fill.glsl (100%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/fill.ts (81%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/grayscale.glsl (100%) create mode 100644 packages/frontend/src/utility/image-compositor-functions/grayscale.ts rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/invert.glsl (100%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/invert.ts (68%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/mirror.glsl (100%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/mirror.ts (63%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/pixelate.glsl (100%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/pixelate.ts (81%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/polkadot.glsl (100%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/polkadot.ts (81%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/stripe.glsl (100%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/stripe.ts (77%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/tearing.glsl (100%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/tearing.ts (82%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/threshold.glsl (100%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/threshold.ts (71%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/zoomLines.glsl (100%) rename packages/frontend/src/utility/{image-effector/fxs => image-compositor-functions}/zoomLines.ts (80%) delete mode 100644 packages/frontend/src/utility/image-effector/fxs/grayscale.ts create mode 100644 packages/frontend/src/utility/image-frame-renderer/ImageFrameRenderer.ts create mode 100644 packages/frontend/src/utility/image-frame-renderer/frame.glsl create mode 100644 packages/frontend/src/utility/image-frame-renderer/frame.ts delete mode 100644 packages/frontend/src/utility/watermark.ts create mode 100644 packages/frontend/src/utility/watermark/WatermarkRenderer.ts rename packages/frontend/src/utility/{image-effector/fxs/watermarkPlacement.glsl => watermark/watermark.glsl} (100%) rename packages/frontend/src/utility/{image-effector/fxs/watermarkPlacement.ts => watermark/watermark.ts} (59%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 211296bb4d..057ffd5566 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ - Enhance: DockerのNode.jsが24.10.0に更新されました ### Client +- Feat: 画像にメタデータを含むフレームをつけられる機能 +- Enhance: プリセットを作成しなくても画像にウォーターマークを付与できるように - Enhance: 管理しているチャンネルの見分けがつきやすくなるように - Enhance: プロフィールへのリンクをユーザーポップアップのアバターに追加 - Enhance: ユーザーのノート、フォロー、フォロワーページへのリンクをユーザーポップアップに追加 diff --git a/locales/index.d.ts b/locales/index.d.ts index 43e31c16a1..e4ebfedd3d 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -5625,6 +5625,172 @@ export interface Locale extends ILocale { * あなたは管理者です */ "youAreAdmin": string; + /** + * フレーム + */ + "frame": string; + /** + * プリセット + */ + "presets": string; + /** + * ゼロ埋め + */ + "zeroPadding": string; + "_imageEditing": { + "_vars": { + /** + * ファイルのキャプション + */ + "caption": string; + /** + * ファイル名 + */ + "filename": string; + /** + * 拡張子無しファイル名 + */ + "filename_without_ext": string; + /** + * 撮影年 + */ + "year": string; + /** + * 撮影月 + */ + "month": string; + /** + * 撮影日 + */ + "day": string; + /** + * 撮影した時刻(時) + */ + "hour": string; + /** + * 撮影した時刻(分) + */ + "minute": string; + /** + * 撮影した時刻(秒) + */ + "second": string; + /** + * カメラ名 + */ + "camera_model": string; + /** + * レンズ名 + */ + "camera_lens_model": string; + /** + * 焦点距離 + */ + "camera_mm": string; + /** + * 焦点距離(35mm判換算) + */ + "camera_mm_35": string; + /** + * 絞り + */ + "camera_f": string; + /** + * シャッタースピード + */ + "camera_s": string; + /** + * ISO感度 + */ + "camera_iso": string; + /** + * 緯度 + */ + "gps_lat": string; + /** + * 経度 + */ + "gps_long": string; + }; + }; + "_imageFrameEditor": { + /** + * フレームの編集 + */ + "title": string; + /** + * 画像にフレームやメタデータを含んだラベルを追加して装飾できます。 + */ + "tip": string; + /** + * ヘッダー + */ + "header": string; + /** + * フッター + */ + "footer": string; + /** + * フチの幅 + */ + "borderThickness": string; + /** + * ラベルの幅 + */ + "labelThickness": string; + /** + * ラベルのスケール + */ + "labelScale": string; + /** + * 中央揃え + */ + "centered": string; + /** + * キャプション(大) + */ + "captionMain": string; + /** + * キャプション(小) + */ + "captionSub": string; + /** + * 利用可能な変数 + */ + "availableVariables": string; + /** + * 二次元コード + */ + "withQrCode": string; + /** + * 背景色 + */ + "backgroundColor": string; + /** + * 文字色 + */ + "textColor": string; + /** + * フォント + */ + "font": string; + /** + * セリフ + */ + "fontSerif": string; + /** + * サンセリフ + */ + "fontSansSerif": string; + /** + * 保存せずに終了しますか? + */ + "quitWithoutSaveConfirm": string; + /** + * 画像の読み込みに失敗しました + */ + "failedToLoadImage": string; + }; "_compression": { "_quality": { /** @@ -12354,7 +12520,7 @@ export interface Locale extends ILocale { "defaultPreset": string; "_watermarkEditor": { /** - * 画像にクレジット情報などのウォーターマークを追加することができます。 + * 画像にクレジット情報などのウォーターマークを追加できます。 */ "tip": string; /** @@ -12469,6 +12635,10 @@ export interface Locale extends ILocale { * 空欄にするとアカウントのURLになります */ "leaveBlankToAccountUrl": string; + /** + * 画像の読み込みに失敗しました + */ + "failedToLoadImage": string; }; "_imageEffector": { /** @@ -12487,6 +12657,10 @@ export interface Locale extends ILocale { * 設定項目はありません */ "nothingToConfigure": string; + /** + * 画像の読み込みに失敗しました + */ + "failedToLoadImage": string; "_fxs": { /** * 色収差 diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 0e2ae63804..d0485af208 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1401,6 +1401,51 @@ widgets: "ウィジェット" deviceInfo: "デバイス情報" deviceInfoDescription: "技術的なお問い合わせの際に、以下の情報を併記すると問題の解決に役立つことがあります。" youAreAdmin: "あなたは管理者です" +frame: "フレーム" +presets: "プリセット" +zeroPadding: "ゼロ埋め" + +_imageEditing: + _vars: + caption: "ファイルのキャプション" + filename: "ファイル名" + filename_without_ext: "拡張子無しファイル名" + year: "撮影年" + month: "撮影月" + day: "撮影日" + hour: "撮影した時刻(時)" + minute: "撮影した時刻(分)" + second: "撮影した時刻(秒)" + camera_model: "カメラ名" + camera_lens_model: "レンズ名" + camera_mm: "焦点距離" + camera_mm_35: "焦点距離(35mm判換算)" + camera_f: "絞り" + camera_s: "シャッタースピード" + camera_iso: "ISO感度" + gps_lat: "緯度" + gps_long: "経度" + +_imageFrameEditor: + title: "フレームの編集" + tip: "画像にフレームやメタデータを含んだラベルを追加して装飾できます。" + header: "ヘッダー" + footer: "フッター" + borderThickness: "フチの幅" + labelThickness: "ラベルの幅" + labelScale: "ラベルのスケール" + centered: "中央揃え" + captionMain: "キャプション(大)" + captionSub: "キャプション(小)" + availableVariables: "利用可能な変数" + withQrCode: "二次元コード" + backgroundColor: "背景色" + textColor: "文字色" + font: "フォント" + fontSerif: "セリフ" + fontSansSerif: "サンセリフ" + quitWithoutSaveConfirm: "保存せずに終了しますか?" + failedToLoadImage: "画像の読み込みに失敗しました" _compression: _quality: @@ -3307,7 +3352,7 @@ _userLists: watermark: "ウォーターマーク" defaultPreset: "デフォルトのプリセット" _watermarkEditor: - tip: "画像にクレジット情報などのウォーターマークを追加することができます。" + tip: "画像にクレジット情報などのウォーターマークを追加できます。" quitWithoutSaveConfirm: "保存せずに終了しますか?" driveFileTypeWarn: "このファイルは対応していません" driveFileTypeWarnDescription: "画像ファイルを選択してください" @@ -3336,12 +3381,14 @@ _watermarkEditor: polkadotSubDotRadius: "サブドットの大きさ" polkadotSubDotDivisions: "サブドットの数" leaveBlankToAccountUrl: "空欄にするとアカウントのURLになります" + failedToLoadImage: "画像の読み込みに失敗しました" _imageEffector: title: "エフェクト" addEffect: "エフェクトを追加" discardChangesConfirm: "変更を破棄して終了しますか?" nothingToConfigure: "設定項目はありません" + failedToLoadImage: "画像の読み込みに失敗しました" _fxs: chromaticAberration: "色収差" diff --git a/packages/frontend/package.json b/packages/frontend/package.json index bd81d1d2c6..1ad3437e86 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -48,6 +48,7 @@ "estree-walker": "3.0.3", "eventemitter3": "5.0.1", "execa": "9.6.0", + "exifreader": "4.32.0", "frontend-shared": "workspace:*", "icons-subsetter": "workspace:*", "idb-keyval": "6.2.2", diff --git a/packages/frontend/src/components/MkEmbedCodeGenDialog.vue b/packages/frontend/src/components/MkEmbedCodeGenDialog.vue index 0cb8499699..4f16149caa 100644 --- a/packages/frontend/src/components/MkEmbedCodeGenDialog.vue +++ b/packages/frontend/src/components/MkEmbedCodeGenDialog.vue @@ -24,9 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only :leaveToClass="$style.transition_x_leaveTo" >
{filename} - {{ i18n.ts._imageEditing._vars.filename }}{filename_without_ext} - {{ i18n.ts._imageEditing._vars.filename_without_ext }}{caption} - {{ i18n.ts._imageEditing._vars.caption }}{year} - {{ i18n.ts._imageEditing._vars.year }}{month} - {{ i18n.ts._imageEditing._vars.month }}{day} - {{ i18n.ts._imageEditing._vars.day }}{hour} - {{ i18n.ts._imageEditing._vars.hour }}{minute} - {{ i18n.ts._imageEditing._vars.minute }}{second} - {{ i18n.ts._imageEditing._vars.second }}{0month} - {{ i18n.ts._imageEditing._vars.month }} ({{ i18n.ts.zeroPadding }}){0day} - {{ i18n.ts._imageEditing._vars.day }} ({{ i18n.ts.zeroPadding }}){0hour} - {{ i18n.ts._imageEditing._vars.hour }} ({{ i18n.ts.zeroPadding }}){0minute} - {{ i18n.ts._imageEditing._vars.minute }} ({{ i18n.ts.zeroPadding }}){0second} - {{ i18n.ts._imageEditing._vars.second }} ({{ i18n.ts.zeroPadding }}){camera_model} - {{ i18n.ts._imageEditing._vars.camera_model }}{camera_lens_model} - {{ i18n.ts._imageEditing._vars.camera_lens_model }}{camera_mm} - {{ i18n.ts._imageEditing._vars.camera_mm }}{camera_mm_35} - {{ i18n.ts._imageEditing._vars.camera_mm_35 }}{camera_f} - {{ i18n.ts._imageEditing._vars.camera_f }}{camera_s} - {{ i18n.ts._imageEditing._vars.camera_s }}{camera_iso} - {{ i18n.ts._imageEditing._vars.camera_iso }}{gps_lat} - {{ i18n.ts._imageEditing._vars.gps_lat }}{gps_long} - {{ i18n.ts._imageEditing._vars.gps_long }}