File size: 4,970 Bytes
d9c705e
 
bf70839
d9c705e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bf70839
 
d9c705e
bf70839
 
d9c705e
bf70839
 
 
 
 
 
 
d9c705e
 
 
bf70839
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d9c705e
bf70839
 
 
 
 
 
 
 
 
 
 
d9c705e
bf70839
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d9c705e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/**
 * Converts an image URL to a base64 data URL with white background made transparent
 * Uses flood-fill from edges to only remove background white, preserving internal white
 */
export async function makeWhiteTransparent(imageUrl: string): Promise<string> {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.crossOrigin = 'anonymous';
    
    img.onload = () => {
      const canvas = document.createElement('canvas');
      const ctx = canvas.getContext('2d');
      
      if (!ctx) {
        reject(new Error('Failed to get canvas context'));
        return;
      }
      
      canvas.width = img.width;
      canvas.height = img.height;
      
      // Draw the image
      ctx.drawImage(img, 0, 0);
      
      // Get image data
      const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
      const data = imageData.data;
      const width = canvas.width;
      const height = canvas.height;
      
      // Create a mask to track which pixels to make transparent
      const mask = new Uint8Array(width * height);
      
      // Define white threshold with some tolerance
      const whiteThreshold = 240;
      const tolerance = 20;
      
      // Helper function to check if a pixel is white-ish
      const isWhite = (index: number): boolean => {
        const i = index * 4;
        const r = data[i];
        const g = data[i + 1];
        const b = data[i + 2];
        return r > whiteThreshold && g > whiteThreshold && b > whiteThreshold;
      };
      
      // Helper function to check if colors are similar
      const colorSimilar = (i1: number, i2: number): boolean => {
        const idx1 = i1 * 4;
        const idx2 = i2 * 4;
        return Math.abs(data[idx1] - data[idx2]) < tolerance &&
               Math.abs(data[idx1 + 1] - data[idx2 + 1]) < tolerance &&
               Math.abs(data[idx1 + 2] - data[idx2 + 2]) < tolerance;
      };
      
      // Flood fill from edges
      const queue: number[] = [];
      
      // Add all edge pixels that are white to the queue
      // Top and bottom edges
      for (let x = 0; x < width; x++) {
        if (isWhite(x)) {
          queue.push(x);
          mask[x] = 1;
        }
        const bottomIdx = (height - 1) * width + x;
        if (isWhite(bottomIdx)) {
          queue.push(bottomIdx);
          mask[bottomIdx] = 1;
        }
      }
      
      // Left and right edges
      for (let y = 1; y < height - 1; y++) {
        const leftIdx = y * width;
        if (isWhite(leftIdx)) {
          queue.push(leftIdx);
          mask[leftIdx] = 1;
        }
        const rightIdx = y * width + width - 1;
        if (isWhite(rightIdx)) {
          queue.push(rightIdx);
          mask[rightIdx] = 1;
        }
      }
      
      // Flood fill
      while (queue.length > 0) {
        const idx = queue.pop()!;
        const x = idx % width;
        const y = Math.floor(idx / width);
        
        // Check 4 neighbors
        const neighbors = [
          { dx: -1, dy: 0 }, // left
          { dx: 1, dy: 0 },  // right
          { dx: 0, dy: -1 }, // up
          { dx: 0, dy: 1 }   // down
        ];
        
        for (const { dx, dy } of neighbors) {
          const nx = x + dx;
          const ny = y + dy;
          
          if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
            const nIdx = ny * width + nx;
            
            // If not already marked and color is similar to current pixel
            if (!mask[nIdx] && isWhite(nIdx) && colorSimilar(idx, nIdx)) {
              mask[nIdx] = 1;
              queue.push(nIdx);
            }
          }
        }
      }
      
      // Apply transparency based on mask
      for (let i = 0; i < mask.length; i++) {
        if (mask[i]) {
          data[i * 4 + 3] = 0; // Set alpha to 0
        }
      }
      
      // Put the modified image data back
      ctx.putImageData(imageData, 0, 0);
      
      // Convert to base64
      const base64 = canvas.toDataURL('image/png');
      resolve(base64);
    };
    
    img.onerror = () => {
      reject(new Error('Failed to load image'));
    };
    
    img.src = imageUrl;
  });
}

/**
 * Fetches an image from URL and converts it to base64
 */
export async function imageUrlToBase64(imageUrl: string): Promise<string> {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.crossOrigin = 'anonymous';
    
    img.onload = () => {
      const canvas = document.createElement('canvas');
      const ctx = canvas.getContext('2d');
      
      if (!ctx) {
        reject(new Error('Failed to get canvas context'));
        return;
      }
      
      canvas.width = img.width;
      canvas.height = img.height;
      ctx.drawImage(img, 0, 0);
      
      const base64 = canvas.toDataURL('image/png');
      resolve(base64);
    };
    
    img.onerror = () => {
      reject(new Error('Failed to load image'));
    };
    
    img.src = imageUrl;
  });
}