> ## Documentation Index
> Fetch the complete documentation index at: https://twigz.arqqin.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Image Processing

> Advanced image processing, optimization, and validation functions

## Overview

The Image Processing API provides comprehensive image handling capabilities including compression, resizing, validation, and variant generation for different use cases across the platform.

**Location:** `convex/shared/imageProcessing.ts`

<CardGroup cols={2}>
  <Card title="Smart Compression" icon="compress">
    Automatic image compression with quality optimization
  </Card>

  <Card title="Size Validation" icon="ruler">
    Validates images against type-specific size limits
  </Card>

  <Card title="Variant Generation" icon="layers">
    Creates multiple image sizes for responsive display
  </Card>

  <Card title="Format Optimization" icon="image">
    Converts images to optimized JPEG format
  </Card>
</CardGroup>

## Image Size Limits

The system enforces different size limits based on image type:

<AccordionGroup>
  <Accordion title="Avatar Images">
    * **Max Size**: 2MB
    * **Max Dimensions**: 512x512px
    * **Use Case**: User profile pictures
  </Accordion>

  <Accordion title="Store Logo">
    * **Max Size**: 2MB
    * **Max Dimensions**: 800x800px
    * **Use Case**: Store branding and identification
  </Accordion>

  <Accordion title="Store Cover">
    * **Max Size**: 5MB
    * **Max Dimensions**: 1920x1080px
    * **Use Case**: Store banner images
  </Accordion>

  <Accordion title="Trade License">
    * **Max Size**: 10MB
    * **Max Dimensions**: 2048x2048px
    * **Use Case**: Document verification
  </Accordion>

  <Accordion title="Product Images">
    * **Max Size**: 5MB
    * **Max Dimensions**: 1920x1920px
    * **Use Case**: Product photos (primary and additional)
  </Accordion>

  <Accordion title="Review Photos">
    * **Max Size**: 5MB
    * **Max Dimensions**: 1920x1920px
    * **Use Case**: Customer review images
  </Accordion>

  <Accordion title="Chat Images">
    * **Max Size**: 5MB
    * **Max Dimensions**: 1920x1920px
    * **Use Case**: Images shared in conversations
  </Accordion>
</AccordionGroup>

## Process Image

Process and optimize an uploaded image with automatic compression and resizing.

<ParamField path="storageId" type="Id<'_storage'>" required>
  Storage ID of the uploaded image
</ParamField>

<ParamField path="imageType" type="'avatar' | 'storeLogo' | 'storeCover' | 'tradeLicense' | 'productPrimary' | 'productAdditional' | 'reviewPhoto' | 'chatImage'" required>
  Type of image being processed
</ParamField>

<ParamField path="quality" type="number">
  JPEG quality (1-100, default: 80)
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const result = await convex.action(api.shared.imageProcessing.processImage, {
    storageId: "file123456789",
    imageType: "productPrimary",
    quality: 85
  });
  ```

  ```javascript JavaScript theme={null}
  const result = await convex.action("shared.imageProcessing.processImage", {
    storageId: "file123456789",
    imageType: "productPrimary",
    quality: 85
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "success": true,
    "originalSize": 2048000,
    "compressedSize": 512000,
    "compressionRatio": 75,
    "width": 1920,
    "height": 1920,
    "format": "jpeg",
    "newStorageId": "file987654321"
  }
  ```
</ResponseExample>

<Note>
  This function processes images in a Node.js environment using Jimp for image manipulation. The processed image is stored as a new file and the original is preserved.
</Note>

## Validate Image Upload

Validate an uploaded image before processing to ensure it meets size and dimension requirements.

<ParamField path="storageId" type="Id<'_storage'>" required>
  Storage ID of the uploaded image
</ParamField>

<ParamField path="imageType" type="'avatar' | 'storeLogo' | 'storeCover' | 'tradeLicense' | 'productPrimary' | 'productAdditional' | 'reviewPhoto' | 'chatImage'" required>
  Type of image being validated
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const validation = await convex.action(api.shared.imageProcessing.validateImageUpload, {
    storageId: "file123456789",
    imageType: "avatar"
  });
  ```

  ```javascript JavaScript theme={null}
  const validation = await convex.action("shared.imageProcessing.validateImageUpload", {
    storageId: "file123456789",
    imageType: "avatar"
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "valid": true,
    "sizeInMB": 1.5,
    "width": 512,
    "height": 512
  }
  ```
</ResponseExample>

<ResponseExample title="Validation Failed">
  ```json Response theme={null}
  {
    "valid": false,
    "error": "Image size (6.2MB) exceeds maximum allowed size (5MB)",
    "sizeInMB": 6.2
  }
  ```
</ResponseExample>

## Create Image Variants

Create multiple image variants (thumbnails, medium, large) for responsive display.

<ParamField path="storageId" type="Id<'_storage'>" required>
  Storage ID of the source image
</ParamField>

<ParamField path="variants" type="Array<object>" required>
  Array of variant configurations
</ParamField>

<ParamField path="variants[].name" type="string" required>
  Name for the variant (e.g., "thumbnail", "medium", "large")
</ParamField>

<ParamField path="variants[].width" type="number" required>
  Target width in pixels
</ParamField>

<ParamField path="variants[].height" type="number">
  Target height in pixels (optional - maintains aspect ratio if not provided)
</ParamField>

<ParamField path="variants[].quality" type="number">
  JPEG quality for this variant (default: 80)
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const result = await convex.action(api.shared.imageProcessing.createImageVariants, {
    storageId: "file123456789",
    variants: [
      {
        name: "thumbnail",
        width: 150,
        height: 150,
        quality: 70
      },
      {
        name: "medium",
        width: 400,
        quality: 80
      },
      {
        name: "large",
        width: 800,
        quality: 85
      }
    ]
  });
  ```

  ```javascript JavaScript theme={null}
  const result = await convex.action("shared.imageProcessing.createImageVariants", {
    storageId: "file123456789",
    variants: [
      {
        name: "thumbnail",
        width: 150,
        height: 150,
        quality: 70
      },
      {
        name: "medium",
        width: 400,
        quality: 80
      },
      {
        name: "large",
        width: 800,
        quality: 85
      }
    ]
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "success": true,
    "variants": [
      {
        "name": "thumbnail",
        "storageId": "file111222333",
        "width": 150,
        "height": 150,
        "size": 12500
      },
      {
        "name": "medium",
        "storageId": "file444555666",
        "width": 400,
        "height": 300,
        "size": 45600
      },
      {
        "name": "large",
        "storageId": "file777888999",
        "width": 800,
        "height": 600,
        "size": 128000
      }
    ]
  }
  ```
</ResponseExample>

## Complete Image Upload Flow

Here's a complete example of uploading and processing an image:

<CodeGroup>
  ```typescript React Native theme={null}
  import { useAction } from 'convex/react';
  import { api } from './convex/_generated/api';
  import * as ImagePicker from 'expo-image-picker';

  function useImageUpload() {
    const validateImage = useAction(api.shared.imageProcessing.validateImageUpload);
    const processImage = useAction(api.shared.imageProcessing.processImage);
    const createVariants = useAction(api.shared.imageProcessing.createImageVariants);

    const uploadProductImage = async (imageType: 'productPrimary' | 'productAdditional') => {
      try {
        // 1. Pick image from gallery
        const result = await ImagePicker.launchImageLibraryAsync({
          mediaTypes: ImagePicker.MediaTypeOptions.Images,
          allowsEditing: true,
          aspect: [1, 1],
          quality: 1,
        });

        if (!result.assets?.[0]) {
          throw new Error('No image selected');
        }

        // 2. Upload to Convex storage
        const response = await fetch(result.assets[0].uri);
        const blob = await response.blob();
        const storageId = await convex.storage.store(blob);

        // 3. Validate image
        const validation = await validateImage({
          storageId,
          imageType
        });

        if (!validation.valid) {
          throw new Error(validation.error);
        }

        // 4. Process and optimize image
        const processed = await processImage({
          storageId,
          imageType,
          quality: 85
        });

        // 5. Create variants for responsive display
        const variants = await createVariants({
          storageId: processed.newStorageId,
          variants: [
            { name: "thumbnail", width: 150, height: 150, quality: 70 },
            { name: "medium", width: 400, quality: 80 },
            { name: "large", width: 800, quality: 85 }
          ]
        });

        return {
          originalStorageId: storageId,
          processedStorageId: processed.newStorageId,
          variants: variants.variants,
          compressionStats: {
            originalSize: processed.originalSize,
            compressedSize: processed.compressedSize,
            compressionRatio: processed.compressionRatio
          }
        };

      } catch (error) {
        console.error('Image upload failed:', error);
        throw error;
      }
    };

    return { uploadProductImage };
  }
  ```

  ```typescript Vue Composition API theme={null}
  import { useConvexAction } from '@convex-vue/core';

  export function useImageUpload() {
    const validateImage = useConvexAction('shared.imageProcessing.validateImageUpload');
    const processImage = useConvexAction('shared.imageProcessing.processImage');
    const createVariants = useConvexAction('shared.imageProcessing.createImageVariants');

    const uploadAvatar = async (file: File) => {
      try {
        // 1. Upload file to storage
        const storageId = await convex.storage.store(file);

        // 2. Validate image
        const validation = await validateImage({
          storageId,
          imageType: 'avatar'
        });

        if (!validation.valid) {
          throw new Error(validation.error);
        }

        // 3. Process image
        const processed = await processImage({
          storageId,
          imageType: 'avatar',
          quality: 90
        });

        return {
          originalStorageId: storageId,
          processedStorageId: processed.newStorageId,
          dimensions: {
            width: processed.width,
            height: processed.height
          }
        };

      } catch (error) {
        console.error('Avatar upload failed:', error);
        throw error;
      }
    };

    return { uploadAvatar };
  }
  ```

  ```javascript JavaScript theme={null}
  // Node.js backend integration
  const uploadStoreLogo = async (imageBuffer, storeId) => {
    try {
      // 1. Upload to storage
      const storageId = await convex.storage.store(imageBuffer);

      // 2. Validate
      const validation = await convex.action('shared.imageProcessing.validateImageUpload', {
        storageId,
        imageType: 'storeLogo'
      });

      if (!validation.valid) {
        throw new Error(validation.error);
      }

      // 3. Process
      const processed = await convex.action('shared.imageProcessing.processImage', {
        storageId,
        imageType: 'storeLogo',
        quality: 85
      });

      // 4. Update store record
      await convex.mutation('stores.operations.updateStore', {
        storeId,
        logoStorageId: processed.newStorageId
      });

      return processed.newStorageId;

    } catch (error) {
      console.error('Store logo upload failed:', error);
      throw error;
    }
  };
  ```
</CodeGroup>

## Image Type Guidelines

<AccordionGroup>
  <Accordion title="Avatar Images">
    **Best Practices:**

    * Use square aspect ratio (1:1)
    * High quality (90+) for user-facing images
    * Consider adding border radius in UI

    **Use Cases:**

    * Customer profile pictures
    * Store owner avatars
  </Accordion>

  <Accordion title="Product Images">
    **Best Practices:**

    * Use square or 4:3 aspect ratio
    * Good lighting and clear product visibility
    * Multiple angles for better representation

    **Use Cases:**

    * Product catalog images
    * Featured product displays
  </Accordion>

  <Accordion title="Store Branding">
    **Best Practices:**

    * Logo: Simple, recognizable at small sizes
    * Cover: Wide format, represents store theme
    * Consistent with brand colors

    **Use Cases:**

    * Store identification
    * Marketing materials
  </Accordion>

  <Accordion title="Document Images">
    **Best Practices:**

    * High resolution for text readability
    * Good contrast and lighting
    * Full document visible in frame

    **Use Cases:**

    * Trade license verification
    * Identity documents
  </Accordion>
</AccordionGroup>

## Error Handling

<AccordionGroup>
  <Accordion title="Image too large">
    **Status Code**: `400`

    ```json theme={null}
    {
      "error": "Image size (6.2MB) exceeds maximum allowed size (5MB)"
    }
    ```
  </Accordion>

  <Accordion title="Invalid image format">
    **Status Code**: `400`

    ```json theme={null}
    {
      "error": "Unsupported image format"
    }
    ```
  </Accordion>

  <Accordion title="Processing failed">
    **Status Code**: `500`

    ```json theme={null}
    {
      "error": "Failed to process image: Invalid image data"
    }
    ```
  </Accordion>

  <Accordion title="Storage not found">
    **Status Code**: `404`

    ```json theme={null}
    {
      "error": "Image not found in storage"
    }
    ```
  </Accordion>
</AccordionGroup>

## Performance Tips

<CardGroup cols={2}>
  <Card title="Compression Quality" icon="sliders">
    Use quality 70-80 for thumbnails, 85-90 for main images
  </Card>

  <Card title="Variant Sizes" icon="layers">
    Create variants only for sizes you actually use
  </Card>

  <Card title="Batch Processing" icon="zap">
    Process multiple images in parallel when possible
  </Card>

  <Card title="Storage Cleanup" icon="trash">
    Clean up original files after processing if not needed
  </Card>
</CardGroup>

<Note>
  All image processing functions run in a Node.js environment and may take several seconds to complete for large images. Consider showing loading indicators to users.
</Note>

<Tip>
  Always validate images before processing to provide immediate feedback to users about size and format issues.
</Tip>
