Other versions of this page
Watermarks
The Camera SDK provides extensive watermark APIs that you can use to add an image overlay to media files taken within your mobile app. Watermarking works:
- on all devices
- on pictures, snapshots and videos
- with any image asset
Watermarks are not added to the camera preview because that is trivial to do using the system's UI.
Configuration
To interact with the watermark APIs, you'll use the WatermarkManager
class:
swiftlet watermarks: WatermarkManager = cameraManager.watermarks
kotlinval watermarks: WatermarkManager = cameraManager.watermarks
Add and Remove
The WatermarkManager
stores watermarks by their String
key. You can define this key on your own and use it for both
addition and removal of new watermarks:
swiftlet watermark: Watermark = watermarks.add(id: "app_logo")
// Later, to remove it, use one of the following:
watermarks.remove(id: "app_logo")
watermarks.remove(id: watermark.id)
kotlinval watermark: Watermark = watermarks.add(id = "app_logo")
// Later, to remove it, use one of the following:
watermarks.remove(id = "app_logo")
watermarks.remove(id = watermark.id)
Targets
As soon as a watermark is added, it will appear on any picture, snapshot
and video recording that will follow. You can however filter the watermark targets by passing a list of Watermark.Target
to WatermarkManager.add()
.
For example, add(id = "logo", targets = [Watermark.Target.Video])
add(id: "logo", targets: [.video])
will configure the watermark so that it only appears on videos.
Target | Description |
---|---|
Target.Video Target.video | Shows this watermark on video recordings. |
Target.Picture Target.picture | Shows this watermark on high quality pictures. |
Target.Snapshot Target.snapshot | Shows this watermark on snapshots. |
Content
When adding new watermarks, a Watermark
object is returned. It can be used to configure, among other things, the contents
of the watermark.
swiftlet watermark: Watermark = watermarks.add(id: "app_logo")
try watermark.content(UIImage(named: "AppLogo"))
kotlinval watermark: Watermark = watermarks.add(id = "app_logo")
watermark.content(R.raw.app_logo)
There are several possible sources for your watermark contents:
- Flat colors: use
content(red: Float, green: Float, blue: Float, alpha: Float)
- Byte buffers: use
content(Data, width: Int, height: Int, flags: [Flags])
. The buffer should hold raw RGBA values as unsigned bytes (8 bits per channel) - UIKit images: use
content(UIImage)
. For example, the image may be loaded from application assets usingUIImage(named: "AssetName")
- Flat colors: use
content(red: Float, green: Float, blue: Float, alpha: Float)
- Byte buffers: use
content(ByteBuffer, width: Int, height: Int, flags: List<Flags>)
. The buffer should hold raw RGBA values as unsigned bytes (8 bits per channel) - Bitmaps: use
content(Bitmap, recycle: Boolean)
. Passing true to the recycle flag ensures thatBitmap.recycle()
will be called - Files and file descriptors: use
content(File)
orcontent(FileDescriptor)
- Raw assets: use
content(rawResourceId: Int)
. The asset should be placed in theres/raw
folder and referenced asR.raw.asset_name
.
Some of the APIs above accept a list of Watermark.Flags
:
Target | Description |
---|---|
Flags.Premultiplied Flags.premultiplied | Notifies the rendering engine that the RGB channels were multiplied by the opacity. |
Flags.FlipVertically Flags.flipVertically | Notifies the rendering engine that the underlying data was loaded from the bottom up, so the image should be flipped vertically before display. |
Size
To configure the size of a watermark, use the size(width: Float?, height: Float?)
API.
It accept optional values for width and height in the [0,1] range, where 1
the represents the full width
(or height) of the destination surface.
swift// Asset with 10% of the target's height, free width
watermark.content(UIImage(named: "MyAsset"))
watermark.size(width: nil, height: 0.1)
// Full-screen red box, will completely cover the image
watermark.content(red: 1, green: 0, blue: 0)
watermark.size(width: 1, height: 1)
kotlin// Asset with 10% of the target's height, free width
watermark.content(R.raw.my_asset)
watermark.size(width = null, height = 0.1)
// Full-screen red box, will completely cover the image
watermark.content(red = 1F, green = 0F, blue = 0F)
watermark.size(width = 1F, height = 1F)
Dimensions must be relative because watermarks are applied onto targets of different size. For example, a picture will likely have a higher resolution than snapshots. Here are all the possible combinations:
Width | Height | Distorted | Meaning |
---|---|---|---|
Specified | Null / Nil | No | Watermark will fit the specified width. The height will be automatically computed based on the content's intrinsic dimensions to avoid distortion. |
Null / Nil | Specified | No | Watermark will fit the specified height. The width will be automatically computed based on the content's intrinsic dimensions to avoid distortion. |
Null / Nil | Null / Nil | No | Watermark will be sized according to its intrinsic size in pixels. This usage is discouraged since the watermark may look different on different targets. |
Specified | Specified | Yes | Enforces both width and height. The resulting image, if the content has any intrinsic dimension (e.g. not a color), may appear distorted. This usage is required for color-based watermarks. |
Position
To configure the position of a watermark, use the position(anchor: Point, offset: Point?)
API.
- anchor:
x
andy
coordinates of a point in the [0,1] range, where(0,0)
represents the top-left corner and(1,1)
represents the bottom-left corner. These coordinates on the watermark image will be anchored to the same coordinates on the target surface. See the example below. - offset (optional): extra offset to be applied after anchoring, for example to give the watermark some padding from the target borders.
swiftwatermark.position(anchor: .init(x: 0, y: 0)) // Top-left
watermark.position(anchor: .init(x: 1, y: 1)) // Bottom-right
watermark.position(anchor: .init(x: 0, y: 1)) // Bottom
watermark.position(anchor: .init(x: 0.5, y: 0.5)) // Centered
kotlinwatermark.position(anchor = Point(0F, 0F)) // Top-left
watermark.position(anchor = Point(1F, 1F)) // Bottom-right
watermark.position(anchor = Point(0F, 1F)) // Bottom
watermark.position(anchor = Point(0.5F, 0.5F)) // Centered
Dimensions for both anchors and offsets must be relative because watermarks are applied onto targets of different size. For example, a picture will likely have a higher resolution than snapshots.
Advanced Usage
Dynamic Watermarks
In most cases, you will add a watermark, configure it, and then let the user take watermarked pictures and videos. However, you can call the configuration APIs at any moment during the app lifecycle.
swift// Big, centered overlay
watermark.size(width: 0.4, height: nil)
watermark.position(anchor: .init(x: 0.5, y: 0.5))
// Later... make it smaller and move to the side:
watermark.size(width: 0.2, height: nil)
watermark.position(anchor: .init(x: 1, y: 1))
kotlin// Big, centered overlay
watermark.size(width = 0.4F, height = null)
watermark.position(anchor = Point(0.5F, 0.5F))
// Later... make it smaller and move to the side:
watermark.size(width = 0.2F, height = null)
watermark.position(anchor = Point(1F, 1F))
This means that by sending frequent updates, watermarks can be animated to appear as smoothly moving objects in video recordings.
More powerful animation APIs are on the project roadmap. Stay tuned for updates.
Composition
You can present more than one more watermark on screen at the same time. Thanks to color support, this can be used to create creative effects in video recordings. For instance, the example below would show a full-screen watermark on black background, that can be removed after recording has started (with a slight delay):
swiftlet background = watermarks.add(id: "background", targets: [.video])
let foreground = watermarks.add(id: "foreground", targets: [.video])
background.content(red: 0, green: 0, blue: 0)
foreground.content(UIImage(named: "AppLogo"))
foreground.position(anchor: .init(x: 0.5, y: 0.5)
foreground.size(width: 0.4, height: nil)
// A few seconds after recording has started...
watermarks.remove(id: background.id)
watermarks.remove(id: foreground.id)
kotlinval background = watermarks.add("background", targets: listOf(Target.Video))
val foreground = watermarks.add("foreground", targets: listOf(Target.Video))
background.content(red = 0F, green = 0F, blue = 0F)
foreground.content(UIImage(named: "AppLogo"))
foreground.position(anchor = Point(0.5F, 0.5F)
foreground.size(width = 0.4F, height = null)
// A few seconds after recording has started...
watermarks.remove(id = background.id)
watermarks.remove(id = foreground.id)