Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 4 additions & 0 deletions apps/example/src/Examples/API/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ export const examples = [
screen: "Atlas",
title: "🎯 Atlas",
},
{
screen: "HDR",
title: "🌈 16-bit / HDR (iOS)",
},
] as const;

const styles = StyleSheet.create({
Expand Down
1 change: 1 addition & 0 deletions apps/example/src/Examples/API/Routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@ export type Routes = {
FirstFrameEmpty: undefined;
PictureBug: undefined;
Atlas: undefined;
HDR: undefined;
};
8 changes: 8 additions & 0 deletions apps/example/src/Examples/API/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from "react";
import { createNativeStackNavigator } from "@react-navigation/native-stack";

import { ImageLoading } from "../ImageLoading/ImageLoading";
import { HDR } from "../HDR";

import type { Routes } from "./Routes";
import { List } from "./List";
Expand Down Expand Up @@ -313,6 +314,13 @@ export const API = () => {
title: "🎯 Atlas",
}}
/>
<Stack.Screen
name="HDR"
component={HDR}
options={{
title: "🌈 16-bit / HDR (iOS)",
}}
/>
</Stack.Navigator>
);
};
158 changes: 158 additions & 0 deletions apps/example/src/Examples/HDR/HDR.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import React, { useMemo } from "react";
import {
Platform,
ScrollView,
StyleSheet,
Text,
View,
useWindowDimensions,
} from "react-native";
import { Canvas, Fill, Shader, Skia, vec } from "@shopify/react-native-skia";

// A deliberately stressful scene:
// 1. A near-black vertical gradient (0 -> 15/255). 8-bit quantization
// produces visible banding. 10-bit / F16 stays smooth.
// 2. A bright "sun" emitting linear values up to 3.0. With rgba16f +
// EDR enabled, this glows brighter than SDR white on HDR displays.
// 3. A saturated red disc using pure P3 primaries that fall outside
// the sRGB gamut.
const source = Skia.RuntimeEffect.Make(`
uniform float2 resolution;

half4 main(float2 fragCoord) {
float2 uv = fragCoord / resolution;

// Dark gradient, top -> bottom, only 16 distinct 8-bit levels.
float v = uv.y * (15.0 / 255.0);
half3 color = half3(v);

// HDR highlight: white at 3.0 in extended linear P3.
float2 sunCenter = float2(resolution.x * 0.5, resolution.y * 0.30);
float sunDist = length(fragCoord - sunCenter);
float sun = smoothstep(60.0, 20.0, sunDist);
color += half3(3.0) * half(sun);

// Wide-gamut red splash. RGB(1, 0, 0) in a P3 surface is more
// saturated than the same in sRGB.
float2 redCenter = float2(resolution.x * 0.5, resolution.y * 0.75);
float redDist = length(fragCoord - redCenter);
float red = smoothstep(80.0, 50.0, redDist);
color = mix(color, half3(1.0, 0.0, 0.0), half(red));

return half4(color, 1.0);
}
`)!;

type Format = "bgra8" | "bgra10" | "rgba16f";
type CS = "srgb" | "p3";

interface PanelProps {
format: Format;
colorSpace: CS;
label: string;
description: string;
height: number;
}

const Panel = ({
format,
colorSpace,
label,
description,
height,
}: PanelProps) => {
const { width } = useWindowDimensions();
const uniforms = useMemo(
() => ({ resolution: vec(width, height) }),
[width, height]
);
return (
<View style={styles.panel}>
<View style={styles.labelRow}>
<Text style={styles.label}>{label}</Text>
<Text style={styles.description}>{description}</Text>
</View>
<Canvas
style={{ width, height }}
pixelFormat={format}
colorSpace={colorSpace}
>
<Fill>
<Shader source={source} uniforms={uniforms} />
</Fill>
</Canvas>
</View>
);
};

const PANEL_HEIGHT = 320;

export const HDR = () => {
if (Platform.OS !== "ios") {
return (
<View style={[styles.container, styles.notSupported]}>
<Text style={styles.label}>iOS only</Text>
<Text style={styles.description}>
The pixelFormat prop is currently implemented on iOS only.
</Text>
</View>
);
}
return (
<ScrollView style={styles.container} contentContainerStyle={styles.content}>
<Panel
format="bgra8"
colorSpace="srgb"
label="bgra8 + sRGB (baseline)"
description="8-bit sRGB. Banding visible, red disc inside sRGB gamut."
height={PANEL_HEIGHT}
/>
<Panel
format="bgra10"
colorSpace="p3"
label="bgra10 + Display P3"
description="10-bit (BGR10A2Unorm), P3. Smooth gradient, more saturated red."
height={PANEL_HEIGHT}
/>
<Panel
format="rgba16f"
colorSpace="p3"
label="rgba16f + Display P3 (HDR / EDR)"
description="Half-float, EDR on. Sun exceeds SDR white on HDR displays."
height={PANEL_HEIGHT}
/>
</ScrollView>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#000",
},
content: {
paddingBottom: 24,
},
panel: {
marginBottom: 12,
},
notSupported: {
alignItems: "center",
justifyContent: "center",
padding: 24,
},
labelRow: {
paddingHorizontal: 12,
paddingVertical: 6,
},
label: {
color: "#fff",
fontWeight: "600",
fontSize: 14,
},
description: {
color: "#aaa",
fontSize: 11,
marginTop: 2,
},
});
1 change: 1 addition & 0 deletions apps/example/src/Examples/HDR/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { HDR } from "./HDR";
2 changes: 1 addition & 1 deletion externals/depot_tools
Submodule depot_tools updated from a6671c to f63aa5
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ public void setColorSpace(SkiaPictureView view, @Nullable String value) {
// nothing to do here at the moment
}

public void setPixelFormat(SkiaPictureView view, @Nullable String value) {
// iOS-only for now. Codegen regen will turn this into an @Override.
}

@Override
public void setAndroidWarmup(SkiaPictureView view, boolean value) {
view.setAndroidWarmup(value);
Expand Down
10 changes: 6 additions & 4 deletions packages/skia/apple/MetalContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,13 @@ class MetalContext {

std::unique_ptr<RNSkia::WindowContext>
MakeWindow(CALayer *window, int width, int height,
bool useP3ColorSpace = true) {
bool useP3ColorSpace = true,
RNSkia::WindowPixelFormat pixelFormat =
RNSkia::WindowPixelFormat::BGRA8) {
auto device = _device;
return std::make_unique<MetalWindowContext>(_directContext.get(), device,
_commandQueue, window, width,
height, useP3ColorSpace);
return std::make_unique<MetalWindowContext>(
_directContext.get(), device, _commandQueue, window, width, height,
useP3ColorSpace, pixelFormat);
}

GrDirectContext *getDirectContext() { return _directContext.get(); }
Expand Down
14 changes: 13 additions & 1 deletion packages/skia/apple/MetalWindowContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,24 @@
#import <MetalKit/MetalKit.h>

#include "RNWindowContext.h"
#include "WindowPixelFormat.h"

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdocumentation"

#include "include/core/SkColorType.h"

#pragma clang diagnostic pop

class SkiaMetalContext;

class MetalWindowContext : public RNSkia::WindowContext {
public:
MetalWindowContext(GrDirectContext *directContext, id<MTLDevice> device,
id<MTLCommandQueue> commandQueue, CALayer *layer,
int width, int height, bool useP3ColorSpace = true);
int width, int height, bool useP3ColorSpace = true,
RNSkia::WindowPixelFormat pixelFormat =
RNSkia::WindowPixelFormat::BGRA8);
~MetalWindowContext() = default;

sk_sp<SkSurface> getSurface() override;
Expand All @@ -37,4 +47,6 @@ class MetalWindowContext : public RNSkia::WindowContext {
#pragma clang diagnostic pop
id<CAMetalDrawable> _currentDrawable = nil;
bool _useP3ColorSpace = false;
RNSkia::WindowPixelFormat _pixelFormat = RNSkia::WindowPixelFormat::BGRA8;
SkColorType _skColorType = kBGRA_8888_SkColorType;
};
Loading
Loading