Images
Loading, drawing, sampling and deriving images.
Image takes a require()d asset, URL, file path, SVG
markup or base64:
import { Canvas, Image } from 'react-native-tgfx'
<Canvas style={{ width: 300, height: 400 }}>
<Image source={require('./cat.png')} x={0} y={0} width={300} height={200} fit="cover" />
<Image source="https://picsum.photos/300/200" x={0} y={200} width={300} height={200} />
</Canvas>For hundreds of animated nodes, TgfxImage is
faster. It takes a loaded handle:
import { Canvas, TgfxImage, useImage } from 'react-native-tgfx'
const photo = useImage(require('./cat.png'))
photo != null && (
<TgfxImage image={photo} x={0} y={0} width={300} height={200} fit="cover" />
)useImage returns TgfxImage or null.
useImageState returns ImageState with loading and error.
loadImage is the promise form.
ImageProps · Fit · Sampling
In Android release builds, asset names can't differ only by extension
(logo.png, logo.webp).
SVG documents
A whole SVG document draws with ImageSVG:
import { Canvas, ImageSVG, useImage } from 'react-native-tgfx'
const icon = useImage(require('./logo.svg'))
const dot = useImage('<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" fill="#0a84ff"/></svg>')
<ImageSVG image={icon} x={0} y={0} width={96} height={96} />
<ImageSVG image={dot} x={104} y={0} width={96} height={96} /><ImageSVG> is <TgfxImage> under a Skia-compatible name. It is redrawn at
any size. Use <Path> for a single d string.
Colour profiles
Embedded ICC profiles are honoured: images are converted into the canvas's
colour space. An sRGB canvas clips out-of-gamut colour. A
colorSpace="display-p3" canvas keeps it.
Images with no readable profile are read as sRGB.
On iOS, PNG and JPEG decode to sRGB, so a P3 canvas cannot recover wide-gamut colour.
Sub-rects
<TgfxImage
x={0} y={0} width={60} height={60}
image={sheet}
rect={{ x: 32, y: 0, width: 32, height: 32 }}
sampling="nearest"
/>As a shader
ImageShader paints any shape with an image.
<Circle cx={60} cy={60} r={50}>
<ImageShader image={photo} rect={{ x: 10, y: 10, width: 100, height: 100 }} fit="cover" />
</Circle>Derived images
These reuse the decoded source.
const thumb = photo.scaled(200, 200)
const sprite = sheet.subset(0, 0, 64, 64)
const upright = photo.oriented('rightTop')
const frosted = photo.blurred(20, 20)oriented takes an EXIF Orientation.
useDerivedImage is the hook form (Derivation).
const thumb = useDerivedImage(photo, { type: 'scaled', width: 200, height: 200 })Encode and save
Any image handle encodes to bytes or saves to disk:
const bytes = await image.encode('webp', 90) // zero-copy ArrayBuffer, for upload
await image.save(`${dir}/thumb.jpg`, 'jpeg', 85) // written natively; bytes never enter JSformat is 'png' (default), 'jpeg', 'webp' or 'raw' (straight-alpha
RGBA). Disposed handles and live camera or video frames reject.
Native buffers
imageFromNativeBuffer wraps a camera or
decoder buffer without a copy (PixelFormat).
Release the producer's buffer after wrapping, or use
useCameraFrame.
let frameImage: TgfxImage
const buffer = frame.getNativeBuffer()
try {
frameImage = imageFromNativeBuffer(buffer.pointer, 'bt601-limited')
} finally {
buffer.release()
frame.dispose()
}
// Draw with frameImage, then dispose it when the stream ends.Decoded pixels
imageFromPixels wraps premultiplied RGBA,
such as an ML model's output. rowBytes (a multiple of 4) defaults to tightly
packed.
const image = imageFromPixels(pixels, width, height)A buffer from another native module is borrowed, so don't write to it while the
handle uses it. A JS ArrayBuffer is copied.
subset, scaled, oriented, blurred, encode and save throw on these
handles.
For a stream, reuse one handle with
setPixels:
if (image != null) {
image.setPixels(pixels, width, height)
} else {
image = imageFromPixels(pixels, width, height)
}Cache
setImageCacheLimit(32 * 1024 * 1024)The default is 64 MB, applied to both the decoded-pixel and GPU caches.
getCacheInfo reports usage.
purgeCaches frees it all, e.g. on memory pressure:
const { imageBytes, imageLimit, gpuBytes, offscreenGpuBytes } = getCacheInfo()
purgeCaches()Handles stay valid. Read gpuBytes a frame after purging.
Video
useVideo returns a Video
whose currentFrame goes straight to <TgfxImage>. It needs Reanimated and
Worklets. loadVideo returns the raw native player
instead, so call updateFrame() and release() yourself.
Its options are a VideoOptions:
useVideo(require('./clip.mp4'), {
seek: 1500, // jump to 1.5s; seeks again whenever this changes
paused: false, // plays as soon as it's open (the default)
looping: true,
volume: 0.5, // 0 to 1, defaults to 0 - a canvas video is usually decoration
})