image_generation/
image_generation.rs1use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
2use gemini_rust::{Gemini, GenerationConfig};
3use std::env;
4use std::fs;
5
6#[tokio::main]
9async fn main() -> Result<(), Box<dyn std::error::Error>> {
10 let api_key = env::var("GEMINI_API_KEY").expect("GEMINI_API_KEY environment variable not set");
12
13 let client = Gemini::with_model(api_key, "models/gemini-2.5-flash-image-preview".to_string());
15
16 println!("--- Text-to-Image Generation ---");
17
18 let response = client
20 .generate_content()
21 .with_user_message(
22 "Create a picture of a nano banana dish in a fancy restaurant with a Gemini theme. \
23 The scene should be photorealistic with elegant lighting and sophisticated presentation."
24 )
25 .with_generation_config(GenerationConfig {
26 temperature: Some(0.7),
27 max_output_tokens: Some(8192), ..Default::default()
29 })
30 .execute()
31 .await?;
32
33 for (i, candidate) in response.candidates.iter().enumerate() {
35 if let Some(parts) = &candidate.content.parts {
36 println!("Processing candidate {}", i + 1);
37
38 for (j, part) in parts.iter().enumerate() {
39 match part {
40 gemini_rust::Part::Text { text, .. } => {
41 println!("Text response {}: {}", j + 1, text);
42 }
43 gemini_rust::Part::InlineData { inline_data } => {
44 println!("Image response {} found!", j + 1);
45 println!("MIME type: {}", inline_data.mime_type);
46
47 match BASE64.decode(&inline_data.data) {
49 Ok(image_bytes) => {
50 let filename = format!("generated_image_{}.png", j + 1);
51 fs::write(&filename, image_bytes)?;
52 println!("Image saved as: {}", filename);
53 }
54 Err(e) => {
55 println!("Failed to decode image data: {}", e);
56 }
57 }
58 }
59 _ => {
60 println!("Other part type encountered");
61 }
62 }
63 }
64 }
65 }
66
67 println!("\n--- Advanced Image Generation Examples ---");
68
69 let product_response = client
71 .generate_content()
72 .with_user_message(
73 "A high-resolution, studio-lit product photograph of a minimalist ceramic coffee mug \
74 with a matte black finish on a clean white marble surface. The lighting is a \
75 three-point softbox setup to eliminate harsh shadows. The camera angle is a \
76 slightly elevated 45-degree angle to showcase the mug's elegant handle design. \
77 Ultra-realistic, with sharp focus on the mug's texture. 16:9 aspect ratio.",
78 )
79 .execute()
80 .await?;
81
82 save_generated_images(&product_response, "product_mockup")?;
83
84 let logo_response = client
86 .generate_content()
87 .with_user_message(
88 "Create a modern, minimalist logo for a coffee shop called 'The Daily Grind' \
89 with clean sans-serif typography. The design should feature a stylized coffee bean \
90 icon integrated with the text. Use a warm color palette of deep brown and cream. \
91 The background must be transparent.",
92 )
93 .execute()
94 .await?;
95
96 save_generated_images(&logo_response, "coffee_logo")?;
97
98 let art_response = client
100 .generate_content()
101 .with_user_message(
102 "A kawaii-style sticker of a happy red panda programmer, featuring round eyes and \
103 a cheerful expression, sitting in front of a laptop with tiny hearts floating around. \
104 Use a bright and vibrant color palette with soft pastels. The design should have \
105 clean line art and cell-shaded style. The background must be transparent.",
106 )
107 .execute()
108 .await?;
109
110 save_generated_images(&art_response, "red_panda_sticker")?;
111
112 let minimal_response = client
114 .generate_content()
115 .with_user_message(
116 "A minimalist composition featuring a single, delicate red maple leaf positioned in the \
117 bottom-right of the frame. The background is a vast, empty cream canvas, creating \
118 significant negative space. Soft, subtle lighting. 16:9 aspect ratio."
119 )
120 .execute()
121 .await?;
122
123 save_generated_images(&minimal_response, "minimalist_leaf")?;
124
125 println!("\nAll image generation examples completed!");
126 println!("Check the current directory for the generated image files.");
127
128 Ok(())
129}
130
131fn save_generated_images(
133 response: &gemini_rust::GenerationResponse,
134 prefix: &str,
135) -> Result<(), Box<dyn std::error::Error>> {
136 for candidate in response.candidates.iter() {
137 if let Some(parts) = &candidate.content.parts {
138 let mut image_count = 0;
139 let mut text_parts = Vec::new();
140
141 for part in parts.iter() {
142 match part {
143 gemini_rust::Part::Text { text, .. } => {
144 text_parts.push(text.clone());
145 }
146 gemini_rust::Part::InlineData { inline_data } => {
147 image_count += 1;
148 match BASE64.decode(&inline_data.data) {
149 Ok(image_bytes) => {
150 let filename = format!("{}_{}.png", prefix, image_count);
151 fs::write(&filename, image_bytes)?;
152 println!("Generated image saved as: {}", filename);
153 }
154 Err(e) => {
155 println!("Failed to decode image data: {}", e);
156 }
157 }
158 }
159 _ => {}
160 }
161 }
162
163 if !text_parts.is_empty() {
165 println!("Text response for {}: {}", prefix, text_parts.join("\n"));
166 }
167 }
168 }
169 Ok(())
170}