Refactor image metadata handling for user color embedding
- Updated useUserColor hook to support multiple image formats (PNG, WebP, JPEG, GIF) for color embedding and extraction. - Introduced unified image metadata utilities in imageMetadata.ts for detecting formats and handling color operations. - Added GIF metadata utilities in gifMetadata.ts for reading and writing user color. - Implemented JPEG metadata utilities in jpegMetadata.ts for embedding and extracting user color. - Created WebP metadata utilities in webpMetadata.ts for handling user color in WebP images. - Enhanced error handling and logging for unsupported formats and operations.
This commit is contained in:
@@ -3,10 +3,18 @@ import { MatrixClient } from 'matrix-js-sdk';
|
||||
import { useMatrixClient } from './useMatrixClient';
|
||||
import { useMediaAuthentication } from './useMediaAuthentication';
|
||||
import { mxcUrlToHttp } from '../utils/matrix';
|
||||
import { embedColorInPNG, extractColorFromPNG, fetchAndExtractColor, removeColorFromPNG } from '../utils/pngMetadata';
|
||||
import {
|
||||
embedColorInImage,
|
||||
extractColorFromImage,
|
||||
removeColorFromImage,
|
||||
fetchAndExtractColor,
|
||||
detectImageFormat,
|
||||
getMimeType,
|
||||
getExtension,
|
||||
} from '../utils/imageMetadata';
|
||||
|
||||
/**
|
||||
* Fetches the user's current avatar as raw PNG data
|
||||
* Fetches the user's current avatar as raw image data
|
||||
*/
|
||||
async function fetchAvatarData(
|
||||
mx: MatrixClient,
|
||||
@@ -26,8 +34,8 @@ async function fetchAvatarData(
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to manage the user's chosen profile color, stored in avatar PNG metadata
|
||||
* The color is embedded in the PNG tEXt chunk and syncs via the avatar
|
||||
* Hook to manage the user's chosen profile color, stored in avatar image metadata
|
||||
* The color is embedded in PNG tEXt chunk or WebP XMP chunk and syncs via the avatar
|
||||
* @returns The current color, a setter function, and loading state
|
||||
*/
|
||||
export function useUserColor(): [
|
||||
@@ -102,23 +110,32 @@ export function useUserColor(): [
|
||||
|
||||
console.log('Original avatar size:', avatarData.byteLength, 'bytes');
|
||||
|
||||
// Modify PNG metadata
|
||||
// Detect image format
|
||||
const format = detectImageFormat(avatarData);
|
||||
console.log('Detected image format:', format);
|
||||
|
||||
if (format === 'unknown') {
|
||||
console.error('Unsupported avatar image format - must be PNG, WebP, JPEG, or GIF');
|
||||
throw new Error('Unsupported image format');
|
||||
}
|
||||
|
||||
// Modify image metadata
|
||||
let newAvatarData: Uint8Array | null;
|
||||
if (newColor) {
|
||||
newAvatarData = embedColorInPNG(avatarData, newColor);
|
||||
newAvatarData = embedColorInImage(avatarData, newColor);
|
||||
} else {
|
||||
newAvatarData = removeColorFromPNG(avatarData);
|
||||
newAvatarData = removeColorFromImage(avatarData);
|
||||
}
|
||||
|
||||
if (!newAvatarData) {
|
||||
console.error('Failed to modify avatar PNG metadata - not a valid PNG?');
|
||||
throw new Error('Failed to modify PNG metadata');
|
||||
console.error('Failed to modify avatar image metadata');
|
||||
throw new Error('Failed to modify image metadata');
|
||||
}
|
||||
|
||||
console.log('Modified avatar size:', newAvatarData.byteLength, 'bytes');
|
||||
|
||||
// Verify the color was embedded correctly before uploading
|
||||
const verifyColor = extractColorFromPNG(newAvatarData);
|
||||
const verifyColor = extractColorFromImage(newAvatarData);
|
||||
console.log('Verification - embedded color:', verifyColor);
|
||||
|
||||
if (newColor && verifyColor !== newColor) {
|
||||
@@ -126,11 +143,13 @@ export function useUserColor(): [
|
||||
throw new Error('Color embedding verification failed');
|
||||
}
|
||||
|
||||
// Upload modified avatar
|
||||
const blob = new Blob([newAvatarData], { type: 'image/png' });
|
||||
// Upload modified avatar with correct MIME type
|
||||
const mimeType = getMimeType(format);
|
||||
const extension = getExtension(format);
|
||||
const blob = new Blob([newAvatarData], { type: mimeType });
|
||||
const uploadResponse = await mx.uploadContent(blob, {
|
||||
name: 'avatar.png',
|
||||
type: 'image/png',
|
||||
name: `avatar.${extension}`,
|
||||
type: mimeType,
|
||||
});
|
||||
|
||||
console.log('Uploaded new avatar:', uploadResponse.content_uri);
|
||||
|
||||
Reference in New Issue
Block a user