How to Build AI Outpainting for iOS and Android: From Canvas to Generation
We received a request: an iOS photo editing app — users want to expand images beyond the frame. An original 9:16 photo needs to become 16:9 without losing content. Outpainting draws side areas while preserving style, lighting, and details. Another case — e-commerce: a product on a white background needs to be shown in an interior. AI creates the environment without a reshoot. A third example: a user takes a screenshot and wants to expand it into a full scene. We are a team with 5+ years of experience in mobile development and AI integration, having delivered over 10 projects with outpainting — from photo editors to visual search engines. Below are technical details, typical pitfalls, and working solutions.
What problems does outpainting solve?
Non-standard aspect ratios — Instagram requires 1:1, but the source is 4:3. Cropping loses context. Outpainting extends the background while keeping the composition. Pulling the camera back — the photographer shot a close-up, but a wide shot is needed. One operation and AI draws the surroundings. Creative layouts — a designer needs to combine multiple expanded images into a single canvas. Outpainting makes seams invisible.
How to prepare a mask for outpainting?
The essence of outpainting: place the original image on a larger canvas (with neutral margins), pass it to the API as image + mask (mask = new empty areas), and receive the filled result.
func prepareOutpaintingCanvas(
original: UIImage,
targetSize: CGSize,
placement: CGPoint // where to place the original on the canvas
) -> (image: UIImage, mask: UIImage) {
// Create canvas of required size
UIGraphicsBeginImageContextWithOptions(targetSize, false, 1.0)
let context = UIGraphicsGetCurrentContext()!
// Fill with gray (neutral color for expansion areas)
context.setFillColor(UIColor.gray.cgColor)
context.fill(CGRect(origin: .zero, size: targetSize))
// Insert the original
original.draw(at: placement)
let compositeImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
// Create mask: black pixels = keep, white = redraw
// For DALL-E: alpha channel instead of black/white
UIGraphicsBeginImageContextWithOptions(targetSize, false, 1.0)
let maskContext = UIGraphicsGetCurrentContext()!
// White (transparent for DALL-E) — new areas
maskContext.setFillColor(UIColor.white.cgColor)
maskContext.fill(CGRect(origin: .zero, size: targetSize))
// Black (opaque) — original image
maskContext.setFillColor(UIColor.black.cgColor)
maskContext.fill(CGRect(origin: placement, size: original.size))
let maskImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return (compositeImage, maskImage)
}
The placement of the original on the canvas determines the expansion direction: center — all sides, left edge — only rightward, arbitrary — the user controls via drag.
UI for controlling expansion
Interaction pattern:
- The user sees the original image inside a canvas frame.
- They can drag the image inside the frame (or move the frame).
- They select the final aspect ratio: 16:9, 4:3, 1:1, or custom.
- They tap Expand.
// Android: draggable image inside canvas with GestureDetector
class OutpaintingView(context: Context) : View(context) {
var imageOffsetX = 0f
var imageOffsetY = 0f
private val gestureDetector = GestureDetector(context, object : SimpleOnGestureListener() {
override fun onScroll(e1: MotionEvent?, e2: MotionEvent, dx: Float, dy: Float): Boolean {
imageOffsetX -= dx
imageOffsetY -= dy
// Limit offset to canvas bounds
imageOffsetX = imageOffsetX.coerceIn(-maxOffsetX, 0f)
imageOffsetY = imageOffsetY.coerceIn(-maxOffsetY, 0f)
invalidate()
return true
}
})
override fun onDraw(canvas: Canvas) {
canvas.drawColor(Color.DKGRAY) // background of expandable area
canvas.drawBitmap(originalBitmap, imageOffsetX, imageOffsetY, null)
}
}
Sending to DALL-E 2
// DALL-E 2: image and mask are passed as PNG with alpha channel
// In the mask: transparency = edit, opacity = keep
func outpaint(composite: UIImage, mask: UIImage, prompt: String, targetSize: String = "1024x1024") async throws -> UIImage {
// Convert mask: white pixels → transparent (alpha = 0)
let alphaMask = convertToAlphaMask(mask)
guard let compositeData = composite.pngData(),
let maskData = alphaMask.pngData() else { throw OutpaintError.conversionFailed }
// Request identical to inpainting — same endpoint /v1/images/edits
return try await sendInpaintRequest(imageData: compositeData, maskData: maskData, prompt: prompt, size: targetSize)
}
private func convertToAlphaMask(_ mask: UIImage) -> UIImage {
UIGraphicsBeginImageContextWithOptions(mask.size, false, 1.0)
guard let context = UIGraphicsGetCurrentContext() else { return mask }
// Invert: white → transparent
context.setBlendMode(.normal)
mask.draw(in: CGRect(origin: .zero, size: mask.size))
// Invert alpha channel via CIImage
UIGraphicsEndImageContext()
// Use Core Image for mask inversion
let ciImage = CIImage(image: mask)!
let inverted = ciImage.applyingFilter("CIColorInvert")
return UIImage(ciImage: inverted)
}
Which AI engine to choose for outpainting?
If you need a simple API integration without customization, DALL-E 2 delivers great quality out of the box. If you need customization (style, sample control, arbitrary sizes), Stable Diffusion is preferable. Stable Diffusion can be 2x cheaper per image for high volumes compared to DALL-E 2. We pick the engine based on the client's task and help with model deployment on their own servers. Official API specs: OpenAI DALL-E API. Also see Wikipedia on Outpainting.
Comparison: DALL-E 2 vs Stable Diffusion for outpainting
| Parameter | DALL-E 2 | Stable Diffusion (via Replicate/FAL) |
|---|---|---|
| Request format | image + mask (PNG with alpha) | image + mask (PNG/JPEG) |
| Sizes | 256x256, 512x512, 1024x1024 | Any multiple of 64 |
| Outpainting support | Yes (as inpainting) | Yes (via canvas expansion) |
| Quality | High, little control | Depends on model and LoRA |
| Speed | ~10 sec | ~15-30 sec (depending on size) |
| API cost | from $0.02 per 1024x1024 | from $0.01 to $0.05 per image |
Work process: from idea to release
- Analytics — study current UX, define target ratios and use cases.
- Design — wireframe of interactive canvas, API contracts.
- Implementation — client logic in Swift/Kotlin, backend integration.
- Testing — mask correctness, edge cases (very small/large images).
- Deployment — publish to App Store/Google Play, monitoring.
Deliverables: what's included in the work
| Stage | Result |
|---|---|
| Consultation | Project assessment, AI engine selection, timeline |
| Development | Ready outpainting module with documentation, source code, and API keys setup |
| Integration | Embedding into existing application with step-by-step guide |
| Testing | QA against checklist (seams, style, performance) with access to test reports |
| Training | Documentation and workshop for your developers |
| Support | 6 months of free support after release, including bug fixes and minor updates |
We guarantee a 6-month support period and offer a 30-day money-back guarantee on our work.
Typical problems and their solutions
Visible seam at the border of original and generated part — occurs when the original is placed with a sharp edge on a neutral background. Solution: a small gradient transition (feather) on the mask edges — blur the mask by 10–20 pixels.
Style mismatch — the model generated something of a completely different style. The prompt should describe the entire scene, not just the new part: "sunny urban landscape, photorealism, warm tones" not "continue".
Timeline
Basic outpainting with DALL-E 2 (fixed expansion directions) — from 5 to 7 days. Interactive draggable canvas with arbitrary positioning, multiple iterations, history — from 3 to 4 weeks.
Pricing
Our outpainting feature development starts at $5,000 for a basic implementation and can go up to $15,000 for a fully interactive module. Contact us for a detailed quote.
If your project requires outpainting, order a consultation — we will find the optimal solution. Get a consultation on implementing outpainting in your mobile app — contact us, we will prepare a detailed commercial proposal.







