Skip to content
Merged
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
6 changes: 3 additions & 3 deletions Ports/iOSPort/nativeSources/CN1Camera.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
#import "CodenameOne_GLViewController.h"
#import <Foundation/Foundation.h>

#ifdef INCLUDE_CAMERA_USAGE
#ifdef INCLUDE_CN1_CAMERA
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>

Expand All @@ -33,7 +33,7 @@
/// `IOSNative` create / mutate / destroy instances and read frames out
/// through the AVCaptureVideoDataOutputSampleBufferDelegate.
///
/// Compiled in only when `INCLUDE_CAMERA_USAGE` is defined (i.e. the app
/// Compiled in only when `INCLUDE_CN1_CAMERA` is defined (i.e. the app
/// has `NSCameraUsageDescription` set; `AiDependencyTable` does this
/// automatically when any class in `com.codename1.camera.*` is referenced).
/// Works on iOS, iPadOS and Mac Catalyst -- AVFoundation has the same
Expand Down Expand Up @@ -81,4 +81,4 @@

@end

#endif // INCLUDE_CAMERA_USAGE
#endif // INCLUDE_CN1_CAMERA
48 changes: 26 additions & 22 deletions Ports/iOSPort/nativeSources/CN1Camera.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@
#import "CN1Camera.h"
#import "xmlvm.h"

#ifdef INCLUDE_CAMERA_USAGE
#ifdef INCLUDE_CN1_CAMERA
#import "java_lang_String.h"
#import "com_codename1_impl_ios_IOSCameraImpl.h"

// Defined in IOSNative.m; converts an NSData into a Java byte[]. It pulls the
// thread state via getThreadLocalData() internally, so it takes no thread arg.
extern JAVA_OBJECT nsDataToByteArr(NSData *data);

@interface CN1Camera ()
@property (nonatomic, copy) NSString *pendingPhotoFilePath;
@property (nonatomic, assign) int pendingPhotoCallbackId;
Expand Down Expand Up @@ -369,51 +373,51 @@ - (void)focusAtX:(float)xNorm y:(float)yNorm {

- (void)fireFrame:(NSData *)jpeg width:(int)w height:(int)h
rotation:(int)rotation timestamp:(int64_t)ts {
JAVA_OBJECT bytes = fromNSData(CN1_THREAD_GET_STATE_PASS_SINGLE_ARG jpeg);
JAVA_OBJECT bytes = nsDataToByteArr(jpeg);
com_codename1_impl_ios_IOSCameraImpl_onFrameDelivered___byte_1ARRAY_int_int_int_long(
CN1_THREAD_GET_STATE_PASS_ARG bytes, w, h, rotation, (JAVA_LONG)ts);
}

- (void)firePhotoCaptured:(int)cbId bytes:(NSData *)jpeg path:(NSString *)path
width:(int)w height:(int)h {
JAVA_OBJECT b = fromNSData(CN1_THREAD_GET_STATE_PASS_SINGLE_ARG jpeg);
JAVA_OBJECT p = fromNSString(CN1_THREAD_GET_STATE_PASS_SINGLE_ARG path);
JAVA_OBJECT b = nsDataToByteArr(jpeg);
JAVA_OBJECT p = fromNSString(CN1_THREAD_GET_STATE_PASS_ARG path);
com_codename1_impl_ios_IOSCameraImpl_onPhotoCaptured___int_byte_1ARRAY_java_lang_String_int_int(
CN1_THREAD_GET_STATE_PASS_ARG cbId, b, p, w, h);
}

- (void)firePhotoFailed:(int)cbId withMessage:(NSString *)msg {
JAVA_OBJECT m = fromNSString(CN1_THREAD_GET_STATE_PASS_SINGLE_ARG msg);
JAVA_OBJECT m = fromNSString(CN1_THREAD_GET_STATE_PASS_ARG msg);
com_codename1_impl_ios_IOSCameraImpl_onPhotoFailed___int_java_lang_String(
CN1_THREAD_GET_STATE_PASS_ARG cbId, m);
}

- (void)fireVideoStopped:(int)cbId path:(NSString *)path {
if (cbId == 0) return;
JAVA_OBJECT p = fromNSString(CN1_THREAD_GET_STATE_PASS_SINGLE_ARG path);
JAVA_OBJECT p = fromNSString(CN1_THREAD_GET_STATE_PASS_ARG path);
com_codename1_impl_ios_IOSCameraImpl_onVideoStopped___int_java_lang_String(
CN1_THREAD_GET_STATE_PASS_ARG cbId, p);
}

@end

#endif // INCLUDE_CAMERA_USAGE
#endif // INCLUDE_CN1_CAMERA

#pragma mark - IOSNative bridge functions
// Each `native void cn1Camera*` declared on IOSNative.java has a matching
// C function below. Naming follows the standard ParparVM mangling:
// com_codename1_impl_ios_IOSNative_<methodName>___<argType>_<argType>
// Sessions are referenced by their CN1Camera Objective-C pointer cast to
// JAVA_LONG; we retain in open* and release in close. When camera support
// is compiled out (INCLUDE_CAMERA_USAGE undefined) the bridge symbols
// is compiled out (INCLUDE_CN1_CAMERA undefined) the bridge symbols
// still exist so ParparVM links cleanly -- they just return null/0 and
// the Java side reports the platform as unsupported.

JAVA_OBJECT com_codename1_impl_ios_IOSNative_cn1CameraEnumerate___R_java_lang_String(
CN1_THREAD_STATE_MULTI_ARG JAVA_OBJECT instanceObject) {
#ifdef INCLUDE_CAMERA_USAGE
#ifdef INCLUDE_CN1_CAMERA
NSString *s = [CN1Camera enumerateCameras];
return fromNSString(CN1_THREAD_GET_STATE_PASS_SINGLE_ARG s);
return fromNSString(CN1_THREAD_GET_STATE_PASS_ARG s);
#else
return JAVA_NULL;
#endif
Expand All @@ -423,7 +427,7 @@ JAVA_LONG com_codename1_impl_ios_IOSNative_cn1CameraOpen___java_lang_String_int_
CN1_THREAD_STATE_MULTI_ARG JAVA_OBJECT instanceObject,
JAVA_OBJECT cameraId, JAVA_INT previewW, JAVA_INT previewH,
JAVA_BOOLEAN captureAudio) {
#ifdef INCLUDE_CAMERA_USAGE
#ifdef INCLUDE_CN1_CAMERA
NSString *idStr = toNSString(CN1_THREAD_GET_STATE_PASS_ARG cameraId);
CN1Camera *cam = [[CN1Camera alloc] init];
BOOL ok = [cam openWithCameraId:idStr previewW:previewW previewH:previewH
Expand All @@ -441,7 +445,7 @@ JAVA_LONG com_codename1_impl_ios_IOSNative_cn1CameraOpen___java_lang_String_int_

JAVA_LONG com_codename1_impl_ios_IOSNative_cn1CameraCreatePreviewView___long_R_long(
CN1_THREAD_STATE_MULTI_ARG JAVA_OBJECT instanceObject, JAVA_LONG sessionPeer) {
#ifdef INCLUDE_CAMERA_USAGE
#ifdef INCLUDE_CN1_CAMERA
#ifndef CN1_USE_ARC
CN1Camera *cam = (CN1Camera *)sessionPeer;
#else
Expand All @@ -462,7 +466,7 @@ void com_codename1_impl_ios_IOSNative_cn1CameraTakePhoto___long_int_int_int_java
CN1_THREAD_STATE_MULTI_ARG JAVA_OBJECT instanceObject, JAVA_LONG sessionPeer,
JAVA_INT width, JAVA_INT height, JAVA_INT quality,
JAVA_OBJECT filePath, JAVA_INT callbackId) {
#ifdef INCLUDE_CAMERA_USAGE
#ifdef INCLUDE_CN1_CAMERA
#ifndef CN1_USE_ARC
CN1Camera *cam = (CN1Camera *)sessionPeer;
#else
Expand All @@ -479,7 +483,7 @@ void com_codename1_impl_ios_IOSNative_cn1CameraTakePhoto___long_int_int_int_java
JAVA_BOOLEAN com_codename1_impl_ios_IOSNative_cn1CameraStartVideo___long_java_lang_String_boolean_R_boolean(
CN1_THREAD_STATE_MULTI_ARG JAVA_OBJECT instanceObject, JAVA_LONG sessionPeer,
JAVA_OBJECT filePath, JAVA_BOOLEAN captureAudio) {
#ifdef INCLUDE_CAMERA_USAGE
#ifdef INCLUDE_CN1_CAMERA
#ifndef CN1_USE_ARC
CN1Camera *cam = (CN1Camera *)sessionPeer;
#else
Expand All @@ -499,7 +503,7 @@ JAVA_BOOLEAN com_codename1_impl_ios_IOSNative_cn1CameraStartVideo___long_java_la
void com_codename1_impl_ios_IOSNative_cn1CameraStopVideo___long_int(
CN1_THREAD_STATE_MULTI_ARG JAVA_OBJECT instanceObject,
JAVA_LONG sessionPeer, JAVA_INT callbackId) {
#ifdef INCLUDE_CAMERA_USAGE
#ifdef INCLUDE_CN1_CAMERA
#ifndef CN1_USE_ARC
CN1Camera *cam = (CN1Camera *)sessionPeer;
#else
Expand All @@ -514,7 +518,7 @@ void com_codename1_impl_ios_IOSNative_cn1CameraStopVideo___long_int(
void com_codename1_impl_ios_IOSNative_cn1CameraSetFrameDelivery___long_boolean_int(
CN1_THREAD_STATE_MULTI_ARG JAVA_OBJECT instanceObject,
JAVA_LONG sessionPeer, JAVA_BOOLEAN enabled, JAVA_INT maxFps) {
#ifdef INCLUDE_CAMERA_USAGE
#ifdef INCLUDE_CN1_CAMERA
#ifndef CN1_USE_ARC
CN1Camera *cam = (CN1Camera *)sessionPeer;
#else
Expand All @@ -527,7 +531,7 @@ void com_codename1_impl_ios_IOSNative_cn1CameraSetFrameDelivery___long_boolean_i
void com_codename1_impl_ios_IOSNative_cn1CameraSetFlash___long_int(
CN1_THREAD_STATE_MULTI_ARG JAVA_OBJECT instanceObject,
JAVA_LONG sessionPeer, JAVA_INT mode) {
#ifdef INCLUDE_CAMERA_USAGE
#ifdef INCLUDE_CN1_CAMERA
#ifndef CN1_USE_ARC
CN1Camera *cam = (CN1Camera *)sessionPeer;
#else
Expand All @@ -540,7 +544,7 @@ void com_codename1_impl_ios_IOSNative_cn1CameraSetFlash___long_int(
void com_codename1_impl_ios_IOSNative_cn1CameraSetZoom___long_float(
CN1_THREAD_STATE_MULTI_ARG JAVA_OBJECT instanceObject,
JAVA_LONG sessionPeer, JAVA_FLOAT ratio) {
#ifdef INCLUDE_CAMERA_USAGE
#ifdef INCLUDE_CN1_CAMERA
#ifndef CN1_USE_ARC
CN1Camera *cam = (CN1Camera *)sessionPeer;
#else
Expand All @@ -553,7 +557,7 @@ void com_codename1_impl_ios_IOSNative_cn1CameraSetZoom___long_float(
void com_codename1_impl_ios_IOSNative_cn1CameraFocus___long_float_float(
CN1_THREAD_STATE_MULTI_ARG JAVA_OBJECT instanceObject,
JAVA_LONG sessionPeer, JAVA_FLOAT xNorm, JAVA_FLOAT yNorm) {
#ifdef INCLUDE_CAMERA_USAGE
#ifdef INCLUDE_CN1_CAMERA
#ifndef CN1_USE_ARC
CN1Camera *cam = (CN1Camera *)sessionPeer;
#else
Expand All @@ -565,7 +569,7 @@ void com_codename1_impl_ios_IOSNative_cn1CameraFocus___long_float_float(

void com_codename1_impl_ios_IOSNative_cn1CameraPause___long(
CN1_THREAD_STATE_MULTI_ARG JAVA_OBJECT instanceObject, JAVA_LONG sessionPeer) {
#ifdef INCLUDE_CAMERA_USAGE
#ifdef INCLUDE_CN1_CAMERA
#ifndef CN1_USE_ARC
CN1Camera *cam = (CN1Camera *)sessionPeer;
#else
Expand All @@ -577,7 +581,7 @@ void com_codename1_impl_ios_IOSNative_cn1CameraPause___long(

void com_codename1_impl_ios_IOSNative_cn1CameraResume___long(
CN1_THREAD_STATE_MULTI_ARG JAVA_OBJECT instanceObject, JAVA_LONG sessionPeer) {
#ifdef INCLUDE_CAMERA_USAGE
#ifdef INCLUDE_CN1_CAMERA
#ifndef CN1_USE_ARC
CN1Camera *cam = (CN1Camera *)sessionPeer;
#else
Expand All @@ -590,7 +594,7 @@ void com_codename1_impl_ios_IOSNative_cn1CameraResume___long(
void com_codename1_impl_ios_IOSNative_cn1CameraClose___long(
CN1_THREAD_STATE_MULTI_ARG JAVA_OBJECT instanceObject, JAVA_LONG sessionPeer) {
if (sessionPeer == 0) return;
#ifdef INCLUDE_CAMERA_USAGE
#ifdef INCLUDE_CN1_CAMERA
#ifndef CN1_USE_ARC
CN1Camera *cam = (CN1Camera *)sessionPeer;
dispatch_async(dispatch_get_main_queue(), ^{
Expand Down
49 changes: 45 additions & 4 deletions Ports/iOSPort/nativeSources/CN1Metalcompat.m
Original file line number Diff line number Diff line change
Expand Up @@ -1612,13 +1612,54 @@ void CN1MetalEndMutableImageDraw(GLUIImage *image) {
}
}

// Wait for a Metal command buffer to finish WITHOUT the risk of blocking the
// calling thread forever. Plain [cb waitUntilCompleted] deadlocks on a buffer
// that was created but never committed (the readback runs between a mutable
// image's Begin and End), and it blocks indefinitely on a genuinely stuck GPU
// command buffer. Both modes intermittently hung the iOS Metal screenshot
// suite inside the readback path (e.g. ChartRotatedScreenshotTest): the app
// stalls with no crash and no Metal validation error, the runner eventually
// SIGTERMs it, and every test after the stall is silently dropped.
//
// Returns YES only when the buffer reached Completed. On NO the caller reads
// back whatever is currently in the texture, so the affected screenshot fails
// visibly against its golden instead of taking down the whole suite.
static BOOL cn1MetalWaitCommandBufferBounded(id<MTLCommandBuffer> cb, double timeoutSeconds) {
if (cb == nil) return YES;
MTLCommandBufferStatus st = cb.status;
if (st == MTLCommandBufferStatusCompleted) return YES;
if (st == MTLCommandBufferStatusError) return NO;
if (st == MTLCommandBufferStatusNotEnqueued || st == MTLCommandBufferStatusEnqueued) {
// Never committed -> it will never be submitted to the GPU, so
// waitUntilCompleted would block forever. Do not wait.
NSLog(@"CN1Metal: readback on an uncommitted command buffer (status=%ld); skipping wait to avoid deadlock", (long)st);
return NO;
}
// Committed/Scheduled: it should complete. Poll the status with a deadline
// as a backstop against a stuck buffer rather than calling the unbounded
// waitUntilCompleted.
NSDate *deadline = [NSDate dateWithTimeIntervalSinceNow:timeoutSeconds];
for (;;) {
st = cb.status;
if (st == MTLCommandBufferStatusCompleted) return YES;
if (st == MTLCommandBufferStatusError) return NO;
if ([deadline timeIntervalSinceNow] <= 0) {
NSLog(@"CN1Metal: command buffer wait timed out after %.1fs (status=%ld); proceeding to avoid suite hang", timeoutSeconds, (long)st);
return NO;
}
[NSThread sleepForTimeInterval:0.004];
}
}

void CN1MetalFlushMutableImageSync(GLUIImage *image) {
if (image == nil) return;
id<MTLCommandBuffer> cb = [image mtlMutableCommandBuffer];
if (cb == nil) return;
[cb waitUntilCompleted];
// Bounded wait: never block the screenshot/readback path forever on an
// uncommitted or stuck command buffer (see cn1MetalWaitCommandBufferBounded).
cn1MetalWaitCommandBufferBounded(cb, 8.0);
// Don't nil the cb -- multiple readbacks of the same already-completed
// buffer should be no-op-fast (waitUntilCompleted is idempotent).
// buffer should be no-op-fast.
}

BOOL CN1MetalReadMutableImagePixels(GLUIImage *image, int *outARGB,
Expand Down Expand Up @@ -1658,7 +1699,7 @@ BOOL CN1MetalReadMutableImagePixels(GLUIImage *image, int *outARGB,
destinationOrigin:MTLOriginMake(0, 0, 0)];
[blit endEncoding];
[blitCb commit];
[blitCb waitUntilCompleted];
cn1MetalWaitCommandBufferBounded(blitCb, 8.0);

// Read shared texture into a temp BGRA buffer, then convert + scale
// into outARGB. Texture dims equal imgWidth/imgHeight when the
Expand Down Expand Up @@ -1741,7 +1782,7 @@ static void cn1MetalReadbackFreeData(void * __unused info, const void *data, siz
destinationOrigin:MTLOriginMake(0, 0, 0)];
[blit endEncoding];
[blitCb commit];
[blitCb waitUntilCompleted];
cn1MetalWaitCommandBufferBounded(blitCb, 8.0);

NSUInteger rowBytes = (NSUInteger)(texW * 4);
NSUInteger byteCount = rowBytes * (NSUInteger)texH;
Expand Down
8 changes: 8 additions & 0 deletions Ports/iOSPort/nativeSources/CodenameOne_GLViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@
// Apple's API-usage scan without declaring an NFC privacy manifest.
//#define CN1_INCLUDE_NFC

// INCLUDE_CN1_CAMERA gates the low-level com.codename1.camera native bridge
// (CN1Camera.{h,m}: AVFoundation AVCaptureSession preview/frames/photo/video).
// IPhoneBuilder uncomments this only when the classpath scanner saw
// com.codename1.camera.*, so apps that use the OLD modal Capture API (which
// only needs INCLUDE_CAMERA_USAGE) do NOT drag in the new AVFoundation-based
// natives. Keep this independent of INCLUDE_CAMERA_USAGE on purpose.
//#define INCLUDE_CN1_CAMERA

// CN1_INCLUDE_OIDC gates the com.codename1.io.oidc native bridge
// (AuthenticationServices.framework import, ASWebAuthenticationSession code
// in CN1OidcBrowser.m). IPhoneBuilder uncomments this only when the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ public class IPhoneBuilder extends Executor {
private boolean usesCryptoGcm;
private boolean usesBiometrics;
private boolean usesNfc;
private boolean usesCn1Camera;
private boolean usesOidc;
private boolean usesAppleSignIn;
private boolean usesWebauthn;
Expand Down Expand Up @@ -728,6 +729,14 @@ public void usesClass(String cls) {
usesNfcHce = true;
}
}
// Low-level camera API (com.codename1.camera.*). Gated on
// actual usage -- NOT on the camera privacy description --
// so the old modal Capture API (which only sets
// INCLUDE_CAMERA_USAGE) does not pull in the new
// AVFoundation-based CN1Camera natives.
if (!usesCn1Camera && cls.indexOf("com/codename1/camera/") == 0) {
usesCn1Camera = true;
}
// OidcClient + SystemBrowser rely on
// ASWebAuthenticationSession (AuthenticationServices.framework,
// iOS 12+).
Expand Down Expand Up @@ -1961,6 +1970,25 @@ public void usesClassMethod(String cls, String method) {
}
}

// Uncomment INCLUDE_CN1_CAMERA in CodenameOne_GLViewController.h
// so the com.codename1.camera native bridge (CN1Camera.{h,m})
// compiles in. This is deliberately independent of
// INCLUDE_CAMERA_USAGE (the old modal Capture API): the new
// AVFoundation natives are only built when the app actually
// references com.codename1.camera.*, matching the AVFoundation
// framework injection driven by the same scan via AiDependencyTable.
if (usesCn1Camera) {
try {
replaceInFile(new File(buildinRes,
"CodenameOne_GLViewController.h"),
"//#define INCLUDE_CN1_CAMERA",
"#define INCLUDE_CN1_CAMERA");
} catch (IOException ex) {
throw new BuildException(
"Failed to enable INCLUDE_CN1_CAMERA", ex);
}
}

// Sign in with Apple requires the
// com.apple.developer.applesignin entitlement; Apple rejects
// builds whose binary references ASAuthorizationAppleIDProvider
Expand Down
Binary file added scripts/android/screenshots/DesktopMode.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ codename1.android.keystoreAlias=
codename1.android.keystorePassword=
codename1.arg.android.useAndroidX=true
codename1.arg.ios.applicationQueriesSchemes=cydia
codename1.arg.ios.NSCameraUsageDescription=Used by the CI smoke test to verify the com.codename1.camera native bridge compiles. The app never opens a camera session.
codename1.arg.ios.newStorageLocation=true
codename1.arg.ios.uiscene=true
codename1.arg.java.version=17
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.codenameone.examples.hellocodenameone

import com.codename1.camera.Camera
import com.codename1.system.Lifecycle
import com.codename1.testing.TestReporting
import com.codename1.ui.CN
Expand All @@ -15,6 +16,20 @@ open class HelloCodenameOne : Lifecycle() {
"Jailbroken device detected by Display.isJailbrokenDevice()."
}
DefaultMethodDemo.validate()
// Reference the low-level camera API (com.codename1.camera.*) so the
// build's bytecode scanner flips IPhoneBuilder.usesCn1Camera /
// AiDependencyTable: this compiles the CN1Camera AVFoundation natives
// on iOS & Mac Catalyst and pulls in CameraX on Android. Without an app
// exercising this API, that native code is gated out of every CI build
// and never gets compiled. isSupported()/getCameras() never open a
// session, so no runtime permission prompt is triggered.
try {
val cameraSupported = Camera.isSupported()
val cameraCount = if (cameraSupported) Camera.getCameras().size else 0
System.out.println("CN1SS:CAMERA_DIAG supported=$cameraSupported cameras=$cameraCount")
} catch (t: Throwable) {
System.out.println("CN1SS:CAMERA_DIAG:EXCEPTION " + t.javaClass.name + ": " + t.message)
}
try {
NativeInterfaceLanguageValidator.validate()
} catch (t: Throwable) {
Expand Down
Binary file modified scripts/ios/screenshots-metal/ChatView_dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified scripts/ios/screenshots-metal/ChatView_light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added scripts/ios/screenshots-metal/DesktopMode.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified scripts/ios/screenshots-metal/ToolbarTheme_dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified scripts/ios/screenshots-metal/ToolbarTheme_light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified scripts/ios/screenshots/ChatView_dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified scripts/ios/screenshots/ChatView_light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added scripts/ios/screenshots/DesktopMode.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed scripts/ios/screenshots/GraphicsPipeline.png
Binary file not shown.
Binary file not shown.
Binary file removed scripts/ios/screenshots/GraphicsStateAndText.png
Binary file not shown.
Binary file removed scripts/ios/screenshots/GraphicsTransformations.png
Binary file not shown.
Binary file modified scripts/ios/screenshots/ToolbarTheme_dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified scripts/ios/screenshots/ToolbarTheme_light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added scripts/javascript/screenshots/DesktopMode.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file modified scripts/mac-native/screenshots/ChatView_dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified scripts/mac-native/screenshots/ChatView_light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added scripts/mac-native/screenshots/DesktopMode.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified scripts/mac-native/screenshots/ToolbarTheme_dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified scripts/mac-native/screenshots/ToolbarTheme_light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading