Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ai-catalog/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<string name="open_sample_button">Open sample</string>
<string name="imagen_sample_title">Image generation with Imagen</string>
<string name="imagen_sample_description">Generate images with Imagen, Google image generation model</string>
<string name="imagen_editing_sample_title">Imagen Editing using Inpainting</string>
<string name="imagen_editing_sample_title">Inpainting &amp; Outpainting with Imagen</string>
<string name="imagen_editing_sample_description">Generate images and edit only specific areas of a generated image with Inpainting</string>
<string name="magic_selfie_sample_title">Magic Selfie with Imagen and ML Kit</string>
<string name="magic_selfie_sample_description">Change the background of your selfies with Imagen and the ML Kit Segmentation API</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,17 @@ package com.android.ai.samples.imagenediting.data
import android.graphics.Bitmap
import com.google.firebase.Firebase
import com.google.firebase.ai.ai
import com.google.firebase.ai.type.Dimensions
import com.google.firebase.ai.type.GenerativeBackend
import com.google.firebase.ai.type.ImagenAspectRatio
import com.google.firebase.ai.type.ImagenEditMode
import com.google.firebase.ai.type.ImagenEditingConfig
import com.google.firebase.ai.type.ImagenGenerationConfig
import com.google.firebase.ai.type.ImagenImageFormat
import com.google.firebase.ai.type.ImagenMaskReference
import com.google.firebase.ai.type.ImagenRawImage
import com.google.firebase.ai.type.ImagenRawMask
import com.google.firebase.ai.type.ImagenStyleReference
import com.google.firebase.ai.type.PublicPreviewAPI
import com.google.firebase.ai.type.toImagenInlineImage
import javax.inject.Inject
Expand Down Expand Up @@ -120,4 +123,29 @@ class ImagenEditingDataSource @Inject constructor() {
)
return imageResponse.images.first().asBitmap()
}

/**
* Outpaints an image to the target dimensions using the Firebase Imagen API.
* This function extends the original image by generating content around it
* based on the provided prompt and target dimensions.
*
* @param sourceImage The original bitmap image to be outpainted.
* @param targetDimensions The desired dimensions of the outpainted image.
* @param prompt An optional text prompt to guide the outpainting process.
* @return The outpainted bitmap image.
*/
@OptIn(PublicPreviewAPI::class)
suspend fun outpaintImage(
sourceImage: Bitmap,
targetDimensions: Dimensions,
prompt: String = "",
): Bitmap {
val imageResponse = editingModel.outpaintImage(
image = sourceImage.toImagenInlineImage(),
newDimensions = targetDimensions,
prompt = prompt,
)

return imageResponse.images.first().asBitmap()
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@

package com.android.ai.samples.imagenediting.ui

import android.graphics.Bitmap
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
Expand Down Expand Up @@ -47,6 +49,7 @@ import com.android.ai.samples.imagenediting.R
fun GenerationInput(
uiState: ImagenEditingUIState,
onGenerateClick: (String) -> Unit,
onOutpaintClick: (prompt: String) -> Unit,
onInpaintClick: (prompt: String) -> Unit,
enabled: Boolean,
modifier: Modifier = Modifier,
Expand Down Expand Up @@ -81,21 +84,39 @@ fun GenerationInput(
)

if (uiState !is ImagenEditingUIState.ImageMasked) {
Button(
onClick = {
onGenerateClick(promptTextField)
},
enabled = canGenerate,
contentPadding = ButtonDefaults.ButtonWithIconContentPadding,
modifier = Modifier.fillMaxWidth(),
) {
Icon(
Icons.Default.SmartToy,
contentDescription = null,
modifier = Modifier.size(ButtonDefaults.IconSize),
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(text = stringResource(R.string.editing_generate_button))
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Button(
onClick = {
onGenerateClick(promptTextField)
},
enabled = canGenerate,
contentPadding = ButtonDefaults.ButtonWithIconContentPadding,
modifier = Modifier.weight(1f),
) {
Icon(
Icons.Default.SmartToy,
contentDescription = null,
modifier = Modifier.size(ButtonDefaults.IconSize),
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(text = stringResource(R.string.editing_generate_button))
}

val bitmap: Bitmap? = (uiState as? ImagenEditingUIState.ImageGenerated)?.bitmap
if (bitmap != null) {
Button(
onClick = {
onOutpaintClick(promptTextField)
},
enabled = enabled && bitmap.width < 4096 && bitmap.height < 4096,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This enabled check is not robust enough. It allows the user to click 'Expand' on an image that will become too large after the operation (which doubles the dimensions), leading to a failed request and a poor user experience.

The validation should be predictive, ensuring the resulting dimensions are valid. For example, it should check if bitmap.width * 2 < 4096.

Additionally, the 4096 limit is a magic number and should be a shared constant.

Suggested change
enabled = enabled && bitmap.width < 4096 && bitmap.height < 4096,
enabled = enabled && (bitmap.width * 2) < 4096 && (bitmap.height * 2) < 4096,

contentPadding = ButtonDefaults.ButtonWithIconContentPadding,
modifier = Modifier.weight(1f),
) {
Icon(Icons.Default.AutoFixHigh, contentDescription = null)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(text = stringResource(R.string.editing_expand_button))
}
}
}
}

Expand All @@ -109,7 +130,7 @@ fun GenerationInput(
modifier = Modifier.fillMaxWidth(),
) {
Icon(
Icons.Default.AutoFixHigh, // Using a different icon for inpainting
Icons.Default.AutoFixHigh,
contentDescription = null,
modifier = Modifier.size(ButtonDefaults.IconSize),
)
Expand Down
Loading
Loading