Fraser commited on
Commit
bf70839
·
1 Parent(s): b63d7b9
src/lib/components/MonsterGenerator/MonsterGenerator.svelte CHANGED
@@ -28,7 +28,10 @@ Create a Pokémon-style monster that transforms the object into an imaginative c
28
 
29
  Guidelines:
30
  - Take the object's key visual elements (colors, shapes, materials) and incorporate them into a creature design
31
- - Add creature features like eyes, limbs, mouth, or other body parts that make sense
 
 
 
32
  - The object should be recognizable but creatively interpreted
33
 
34
  Rarity assessment: Common objects = weaker monsters. Unique/rare objects = stronger monsters.
@@ -42,7 +45,7 @@ Include:
42
  const IMAGE_GENERATION_PROMPT = (concept: string) => `Convert this monster concept into a clear and succinct description of its appearance:
43
  "${concept}"
44
 
45
- Include all of its visual details, format the description as a single long sentence.`;
46
 
47
  const MONSTER_STATS_PROMPT = (concept: string) => `Convert the following monster concept into a JSON object with stats:
48
 
 
28
 
29
  Guidelines:
30
  - Take the object's key visual elements (colors, shapes, materials) and incorporate them into a creature design
31
+ - Add eyes (can be glowing, mechanical, multiple, etc.) positioned where they make sense
32
+ - Include limbs (legs, arms, wings, tentacles) that grow from or replace parts of the object
33
+ - Add a mouth, beak, or feeding apparatus if appropriate
34
+ - Add creature elements like tail, fins, claws, or horns where fitting
35
  - The object should be recognizable but creatively interpreted
36
 
37
  Rarity assessment: Common objects = weaker monsters. Unique/rare objects = stronger monsters.
 
45
  const IMAGE_GENERATION_PROMPT = (concept: string) => `Convert this monster concept into a clear and succinct description of its appearance:
46
  "${concept}"
47
 
48
+ Include all of its visual details, most importantly include its creature features, format the description as a single long sentence.`;
49
 
50
  const MONSTER_STATS_PROMPT = (concept: string) => `Convert the following monster concept into a JSON object with stats:
51
 
src/lib/utils/imageProcessing.ts CHANGED
@@ -1,5 +1,6 @@
1
  /**
2
  * Converts an image URL to a base64 data URL with white background made transparent
 
3
  */
4
  export async function makeWhiteTransparent(imageUrl: string): Promise<string> {
5
  return new Promise((resolve, reject) => {
@@ -24,23 +25,99 @@ export async function makeWhiteTransparent(imageUrl: string): Promise<string> {
24
  // Get image data
25
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
26
  const data = imageData.data;
 
 
27
 
28
- // Define white threshold (adjust if needed)
29
- const whiteThreshold = 240; // RGB values above this are considered "white"
30
 
31
- // Make white pixels transparent
32
- for (let i = 0; i < data.length; i += 4) {
 
 
 
 
 
33
  const r = data[i];
34
  const g = data[i + 1];
35
  const b = data[i + 2];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
- // Check if pixel is white-ish
38
- if (r > whiteThreshold && g > whiteThreshold && b > whiteThreshold) {
39
- // Calculate how "white" this pixel is (0-1)
40
- const whiteness = Math.min(r, g, b) / 255;
 
 
 
 
 
 
 
41
 
42
- // Make it transparent based on whiteness
43
- data[i + 3] = Math.floor((1 - whiteness) * 255);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  }
45
  }
46
 
 
1
  /**
2
  * Converts an image URL to a base64 data URL with white background made transparent
3
+ * Uses flood-fill from edges to only remove background white, preserving internal white
4
  */
5
  export async function makeWhiteTransparent(imageUrl: string): Promise<string> {
6
  return new Promise((resolve, reject) => {
 
25
  // Get image data
26
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
27
  const data = imageData.data;
28
+ const width = canvas.width;
29
+ const height = canvas.height;
30
 
31
+ // Create a mask to track which pixels to make transparent
32
+ const mask = new Uint8Array(width * height);
33
 
34
+ // Define white threshold with some tolerance
35
+ const whiteThreshold = 240;
36
+ const tolerance = 20;
37
+
38
+ // Helper function to check if a pixel is white-ish
39
+ const isWhite = (index: number): boolean => {
40
+ const i = index * 4;
41
  const r = data[i];
42
  const g = data[i + 1];
43
  const b = data[i + 2];
44
+ return r > whiteThreshold && g > whiteThreshold && b > whiteThreshold;
45
+ };
46
+
47
+ // Helper function to check if colors are similar
48
+ const colorSimilar = (i1: number, i2: number): boolean => {
49
+ const idx1 = i1 * 4;
50
+ const idx2 = i2 * 4;
51
+ return Math.abs(data[idx1] - data[idx2]) < tolerance &&
52
+ Math.abs(data[idx1 + 1] - data[idx2 + 1]) < tolerance &&
53
+ Math.abs(data[idx1 + 2] - data[idx2 + 2]) < tolerance;
54
+ };
55
+
56
+ // Flood fill from edges
57
+ const queue: number[] = [];
58
+
59
+ // Add all edge pixels that are white to the queue
60
+ // Top and bottom edges
61
+ for (let x = 0; x < width; x++) {
62
+ if (isWhite(x)) {
63
+ queue.push(x);
64
+ mask[x] = 1;
65
+ }
66
+ const bottomIdx = (height - 1) * width + x;
67
+ if (isWhite(bottomIdx)) {
68
+ queue.push(bottomIdx);
69
+ mask[bottomIdx] = 1;
70
+ }
71
+ }
72
+
73
+ // Left and right edges
74
+ for (let y = 1; y < height - 1; y++) {
75
+ const leftIdx = y * width;
76
+ if (isWhite(leftIdx)) {
77
+ queue.push(leftIdx);
78
+ mask[leftIdx] = 1;
79
+ }
80
+ const rightIdx = y * width + width - 1;
81
+ if (isWhite(rightIdx)) {
82
+ queue.push(rightIdx);
83
+ mask[rightIdx] = 1;
84
+ }
85
+ }
86
+
87
+ // Flood fill
88
+ while (queue.length > 0) {
89
+ const idx = queue.pop()!;
90
+ const x = idx % width;
91
+ const y = Math.floor(idx / width);
92
 
93
+ // Check 4 neighbors
94
+ const neighbors = [
95
+ { dx: -1, dy: 0 }, // left
96
+ { dx: 1, dy: 0 }, // right
97
+ { dx: 0, dy: -1 }, // up
98
+ { dx: 0, dy: 1 } // down
99
+ ];
100
+
101
+ for (const { dx, dy } of neighbors) {
102
+ const nx = x + dx;
103
+ const ny = y + dy;
104
 
105
+ if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
106
+ const nIdx = ny * width + nx;
107
+
108
+ // If not already marked and color is similar to current pixel
109
+ if (!mask[nIdx] && isWhite(nIdx) && colorSimilar(idx, nIdx)) {
110
+ mask[nIdx] = 1;
111
+ queue.push(nIdx);
112
+ }
113
+ }
114
+ }
115
+ }
116
+
117
+ // Apply transparency based on mask
118
+ for (let i = 0; i < mask.length; i++) {
119
+ if (mask[i]) {
120
+ data[i * 4 + 3] = 0; // Set alpha to 0
121
  }
122
  }
123