From 8249e2929b64657b727c92d3a5854d802e77608c Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Tue, 18 Feb 2025 22:53:18 -0600 Subject: [PATCH] Create initial fujifilm recipe type --- app/film-demo/animate/page.tsx | 7 +-- app/film-demo/page.tsx | 7 +-- src/app/CommandK.tsx | 2 +- .../FilmSimulationImageResponse.tsx | 2 +- src/photo/form/index.ts | 4 +- src/photo/server.ts | 4 +- src/platforms/fujifilm/index.ts | 48 +++++++++++++++++ src/platforms/fujifilm/recipe.ts | 20 +++++++ .../{fujifilm.ts => fujifilm/simulation.ts} | 53 ++----------------- src/simulation/PhotoFilmSimulation.tsx | 2 +- src/simulation/PhotoFilmSimulationIcon.tsx | 2 +- src/simulation/index.ts | 2 +- 12 files changed, 90 insertions(+), 63 deletions(-) create mode 100644 src/platforms/fujifilm/index.ts create mode 100644 src/platforms/fujifilm/recipe.ts rename src/platforms/{fujifilm.ts => fujifilm/simulation.ts} (80%) diff --git a/app/film-demo/animate/page.tsx b/app/film-demo/animate/page.tsx index 11612df7..ba877ed0 100644 --- a/app/film-demo/animate/page.tsx +++ b/app/film-demo/animate/page.tsx @@ -2,9 +2,10 @@ import SiteGrid from '@/components/SiteGrid'; import { clsx } from 'clsx/lite'; -import { FILM_SIMULATION_FORM_INPUT_OPTIONS } from '@/platforms/fujifilm'; -import PhotoFilmSimulation from - '@/simulation/PhotoFilmSimulation'; +import { + FILM_SIMULATION_FORM_INPUT_OPTIONS, +} from '@/platforms/fujifilm/simulation'; +import PhotoFilmSimulation from '@/simulation/PhotoFilmSimulation'; import { useEffect, useState } from 'react'; export default function FilmPage() { diff --git a/app/film-demo/page.tsx b/app/film-demo/page.tsx index a5e7467c..cb5724c1 100644 --- a/app/film-demo/page.tsx +++ b/app/film-demo/page.tsx @@ -1,6 +1,7 @@ -import { FILM_SIMULATION_FORM_INPUT_OPTIONS } from '@/platforms/fujifilm'; -import PhotoFilmSimulation from - '@/simulation/PhotoFilmSimulation'; +import { + FILM_SIMULATION_FORM_INPUT_OPTIONS, +} from '@/platforms/fujifilm/simulation'; +import PhotoFilmSimulation from '@/simulation/PhotoFilmSimulation'; export default function FilmPage() { return ( diff --git a/src/app/CommandK.tsx b/src/app/CommandK.tsx index 0df5f312..7d239adc 100644 --- a/src/app/CommandK.tsx +++ b/src/app/CommandK.tsx @@ -18,7 +18,7 @@ import { formatCount, formatCountDescriptive } from '@/utility/string'; import PhotoFilmSimulationIcon from '@/simulation/PhotoFilmSimulationIcon'; import { IoMdCamera } from 'react-icons/io'; import { ADMIN_DEBUG_TOOLS_ENABLED, SHOW_FILM_SIMULATIONS } from './config'; -import { labelForFilmSimulation } from '@/platforms/fujifilm'; +import { labelForFilmSimulation } from '@/platforms/fujifilm/simulation'; import { getUniqueFocalLengths } from '@/photo/db/query'; import { formatFocalLength } from '@/focal'; import { TbCone } from 'react-icons/tb'; diff --git a/src/image-response/FilmSimulationImageResponse.tsx b/src/image-response/FilmSimulationImageResponse.tsx index 3b458146..f83af711 100644 --- a/src/image-response/FilmSimulationImageResponse.tsx +++ b/src/image-response/FilmSimulationImageResponse.tsx @@ -4,7 +4,7 @@ import ImagePhotoGrid from './components/ImagePhotoGrid'; import ImageContainer from './components/ImageContainer'; import { labelForFilmSimulation, -} from '@/platforms/fujifilm'; +} from '@/platforms/fujifilm/simulation'; import PhotoFilmSimulationIcon from '@/simulation/PhotoFilmSimulationIcon'; import { FilmSimulation } from '@/simulation'; diff --git a/src/photo/form/index.ts b/src/photo/form/index.ts index 83991340..c07997ee 100644 --- a/src/photo/form/index.ts +++ b/src/photo/form/index.ts @@ -18,11 +18,11 @@ import { convertStringToArray } from '@/utility/string'; import { generateNanoid } from '@/utility/nanoid'; import { FILM_SIMULATION_FORM_INPUT_OPTIONS, - MAKE_FUJIFILM, -} from '@/platforms/fujifilm'; +} from '@/platforms/fujifilm/simulation'; import { FilmSimulation } from '@/simulation'; import { GEO_PRIVACY_ENABLED } from '@/app/config'; import { TAG_FAVS, getValidationMessageForTags } from '@/tag'; +import { MAKE_FUJIFILM } from '@/platforms/fujifilm'; type VirtualFields = 'favorite'; diff --git a/src/photo/server.ts b/src/photo/server.ts index 5a368db1..57a8fbef 100644 --- a/src/photo/server.ts +++ b/src/photo/server.ts @@ -5,8 +5,7 @@ import { import { convertExifToFormData } from '@/photo/form'; import { getFujifilmSimulationFromMakerNote, - isExifForFujifilm, -} from '@/platforms/fujifilm'; +} from '@/platforms/fujifilm/simulation'; import { ExifData, ExifParserFactory } from 'ts-exif-parser'; import { PhotoFormData } from './form'; import { FilmSimulation } from '@/simulation'; @@ -15,6 +14,7 @@ import { GEO_PRIVACY_ENABLED, PRESERVE_ORIGINAL_UPLOADS, } from '@/app/config'; +import { isExifForFujifilm } from '@/platforms/fujifilm'; const IMAGE_WIDTH_RESIZE = 200; const IMAGE_WIDTH_BLUR = 200; diff --git a/src/platforms/fujifilm/index.ts b/src/platforms/fujifilm/index.ts new file mode 100644 index 00000000..250b54e0 --- /dev/null +++ b/src/platforms/fujifilm/index.ts @@ -0,0 +1,48 @@ +// MakerNote tag IDs and values referenced from: +// github.com/exiftool/exiftool/blob/master/lib/Image/ExifTool/FujiFilm.pm + +import type { ExifData } from 'ts-exif-parser'; + +export const MAKE_FUJIFILM = 'FUJIFILM'; + +const BYTE_INDEX_TAG_COUNT = 12; +const BYTE_INDEX_FIRST_TAG = 14; +const BYTES_PER_TAG = 12; +const BYTE_OFFSET_TAG_TYPE = 2; +const BYTE_OFFSET_TAG_VALUE = 8; + +export const TAG_ID_SATURATION = 0x1003; +export const TAG_ID_FILM_MODE = 0x1401; + +export const isExifForFujifilm = (data: ExifData) => + data.tags?.Make === MAKE_FUJIFILM; + +export const parseFujifilmMakerNote = ( + bytes: Buffer, + valueForTagUInt: (tagId: number, value: number) => void, +) => { + const tagCount = bytes.readUint16LE(BYTE_INDEX_TAG_COUNT); + for (let i = 0; i < tagCount; i++) { + const index = BYTE_INDEX_FIRST_TAG + i * BYTES_PER_TAG; + if (index + BYTES_PER_TAG < bytes.length) { + const tagId = bytes.readUInt16LE(index); + const tagType = bytes.readUInt16LE(index + BYTE_OFFSET_TAG_TYPE); + switch (tagType) { + // UInt16 + case 3: + valueForTagUInt( + tagId, + bytes.readUInt16LE(index + BYTE_OFFSET_TAG_VALUE), + ); + break; + // UInt32 + case 4: + valueForTagUInt( + tagId, + bytes.readUInt32LE(index + BYTE_OFFSET_TAG_VALUE), + ); + break; + } + } + } +}; \ No newline at end of file diff --git a/src/platforms/fujifilm/recipe.ts b/src/platforms/fujifilm/recipe.ts new file mode 100644 index 00000000..9a2962c0 --- /dev/null +++ b/src/platforms/fujifilm/recipe.ts @@ -0,0 +1,20 @@ +export interface FujifilmRecipe { + dynamicRange: number + highlight: number + shadow: number + color: number + noiseReduction: number + sharpening: number + clarity: number + grainEffect: { + type: 'strong' | 'medium' | 'weak' + size: 'small' | 'large' + } + colorChromeEffect: 'strong' | 'medium' | 'weak' + colorChromeEffectBlue: 'off' | 'weak' | 'strong' + whiteBalance: { + type: string + red: number + blue: number + } +} diff --git a/src/platforms/fujifilm.ts b/src/platforms/fujifilm/simulation.ts similarity index 80% rename from src/platforms/fujifilm.ts rename to src/platforms/fujifilm/simulation.ts index 6411765c..12ac0438 100644 --- a/src/platforms/fujifilm.ts +++ b/src/platforms/fujifilm/simulation.ts @@ -1,18 +1,8 @@ -// MakerNote tag IDs and values referenced from: -// github.com/exiftool/exiftool/blob/master/lib/Image/ExifTool/FujiFilm.pm - -import type { ExifData } from 'ts-exif-parser'; - -export const MAKE_FUJIFILM = 'FUJIFILM'; - -const BYTE_INDEX_TAG_COUNT = 12; -const BYTE_INDEX_FIRST_TAG = 14; -const BYTES_PER_TAG = 12; -const BYTE_OFFSET_TAG_TYPE = 2; -const BYTE_OFFSET_TAG_VALUE = 8; - -const TAG_ID_SATURATION = 0x1003; -const TAG_ID_FILM_MODE = 0x1401; +import { + TAG_ID_FILM_MODE, + parseFujifilmMakerNote, + TAG_ID_SATURATION, +} from '.'; type FujifilmSimulationFromSaturation = 'monochrome' | @@ -46,9 +36,6 @@ export type FujifilmSimulation = FujifilmSimulationFromSaturation | FujifilmMode; -export const isExifForFujifilm = (data: ExifData) => - data.tags?.Make === MAKE_FUJIFILM; - const getFujifilmSimulationFromSaturation = ( value?: number, ): FujifilmSimulationFromSaturation | undefined => { @@ -231,36 +218,6 @@ export const FILM_SIMULATION_FORM_INPUT_OPTIONS = Object export const labelForFilmSimulation = (simulation: FujifilmSimulation) => FILM_SIMULATION_LABELS[simulation]; -const parseFujifilmMakerNote = ( - bytes: Buffer, - valueForTagUInt: (tagId: number, value: number) => void, -) => { - const tagCount = bytes.readUint16LE(BYTE_INDEX_TAG_COUNT); - for (let i = 0; i < tagCount; i++) { - const index = BYTE_INDEX_FIRST_TAG + i * BYTES_PER_TAG; - if (index + BYTES_PER_TAG < bytes.length) { - const tagId = bytes.readUInt16LE(index); - const tagType = bytes.readUInt16LE(index + BYTE_OFFSET_TAG_TYPE); - switch (tagType) { - // UInt16 - case 3: - valueForTagUInt( - tagId, - bytes.readUInt16LE(index + BYTE_OFFSET_TAG_VALUE), - ); - break; - // UInt32 - case 4: - valueForTagUInt( - tagId, - bytes.readUInt32LE(index + BYTE_OFFSET_TAG_VALUE), - ); - break; - } - } - } -}; - export const getFujifilmSimulationFromMakerNote = ( bytes: Buffer, ): FujifilmSimulation | undefined => { diff --git a/src/simulation/PhotoFilmSimulation.tsx b/src/simulation/PhotoFilmSimulation.tsx index e6c03f72..e1e9139a 100644 --- a/src/simulation/PhotoFilmSimulation.tsx +++ b/src/simulation/PhotoFilmSimulation.tsx @@ -1,4 +1,4 @@ -import { labelForFilmSimulation } from '@/platforms/fujifilm'; +import { labelForFilmSimulation } from '@/platforms/fujifilm/simulation'; import PhotoFilmSimulationIcon from './PhotoFilmSimulationIcon'; import { pathForFilmSimulation } from '@/app/paths'; import { FilmSimulation } from '.'; diff --git a/src/simulation/PhotoFilmSimulationIcon.tsx b/src/simulation/PhotoFilmSimulationIcon.tsx index 230d8c96..9c05d18d 100644 --- a/src/simulation/PhotoFilmSimulationIcon.tsx +++ b/src/simulation/PhotoFilmSimulationIcon.tsx @@ -1,5 +1,5 @@ /* eslint-disable max-len */ -import { labelForFilmSimulation } from '@/platforms/fujifilm'; +import { labelForFilmSimulation } from '@/platforms/fujifilm/simulation'; import { CSSProperties } from 'react'; import { FilmSimulation } from '.'; diff --git a/src/simulation/index.ts b/src/simulation/index.ts index 499660c0..5b283887 100644 --- a/src/simulation/index.ts +++ b/src/simulation/index.ts @@ -11,7 +11,7 @@ import { import { FujifilmSimulation, labelForFilmSimulation, -} from '@/platforms/fujifilm'; +} from '@/platforms/fujifilm/simulation'; export type FilmSimulation = FujifilmSimulation;