rays 0.1.16 → 0.1.17

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 257d69d9a622000399ced2465f798ffa54db2e1943cc9eeea9c1a0d3f40503c2
4
- data.tar.gz: 302a565d951db2bdbb8d7fcf114fc26064ed55e9b1936f7683f24b77975d70d9
3
+ metadata.gz: 9b9970ac7078fc1ff19c2039b5d9c3714eeaaba98d5825e374639707231fe212
4
+ data.tar.gz: 12d43f68907b92bc4fcc293273dfaf1353e17006654e245973d3866f31cac2de
5
5
  SHA512:
6
- metadata.gz: 69618fc55b5fe15c1f13c49f4e52e468f734ceb874a5cffc0186747595ad0df06f096b63a2b6e6e8271327385373e1c9efe6b23954c94064e961d3e547658577
7
- data.tar.gz: d0c8e174262e60747382005cefcc0c7ebf7a03d8300ea607976f861dcc0dbec61bff530aad0d890df4cfd05b116cc3b6f5d119a97be6c4e63e061dd89731b0df
6
+ metadata.gz: 4f732d776a9cf8940d22dde937d72d25a38fda8c8842484d525ae105c0ce591dd20b6e10742418e708c60d22b29e44ab5129649538f7593ad834a38ee9cd54a4
7
+ data.tar.gz: 42d6400febd65098c2ccc37bafde776ad0b42129996e7edbfc74f9d8862622e3439389fcbbbe3788f5f62c4298dad30d0981df45726874dfb41f47047d9d7117
@@ -19,11 +19,14 @@ VALUE alloc(VALUE klass)
19
19
  }
20
20
 
21
21
  static
22
- VALUE initialize(VALUE self)
22
+ VALUE setup(VALUE self, VALUE device_name, VALUE min_width, VALUE min_height, VALUE resize, VALUE crop)
23
23
  {
24
24
  RUCY_CHECK_OBJ(Rays::Camera, self);
25
25
 
26
- *THIS = Rays::Camera();
26
+ *THIS = Rays::Camera(
27
+ device_name ? to<const char*>(device_name) : NULL,
28
+ to<int>(min_width), to<int>(min_height),
29
+ to<bool>(resize), to<bool>(crop));
27
30
  return self;
28
31
  }
29
32
 
@@ -48,6 +51,66 @@ VALUE is_active(VALUE self)
48
51
  return value(THIS->is_active());
49
52
  }
50
53
 
54
+ static
55
+ VALUE set_min_width(VALUE self, VALUE width)
56
+ {
57
+ CHECK;
58
+ THIS->set_min_width(to<int>(width));
59
+ return value(THIS->min_width());
60
+ }
61
+
62
+ static
63
+ VALUE min_width(VALUE self)
64
+ {
65
+ CHECK;
66
+ return value(THIS->min_width());
67
+ }
68
+
69
+ static
70
+ VALUE set_min_height(VALUE self, VALUE height)
71
+ {
72
+ CHECK;
73
+ THIS->set_min_height(to<int>(height));
74
+ return value(THIS->min_height());
75
+ }
76
+
77
+ static
78
+ VALUE min_height(VALUE self)
79
+ {
80
+ CHECK;
81
+ return value(THIS->min_height());
82
+ }
83
+
84
+ static
85
+ VALUE set_resize(VALUE self, VALUE resize)
86
+ {
87
+ CHECK;
88
+ THIS->set_resize(to<bool>(resize));
89
+ return value(THIS->is_resize());
90
+ }
91
+
92
+ static
93
+ VALUE is_resize(VALUE self)
94
+ {
95
+ CHECK;
96
+ return value(THIS->is_resize());
97
+ }
98
+
99
+ static
100
+ VALUE set_crop(VALUE self, VALUE crop)
101
+ {
102
+ CHECK;
103
+ THIS->set_crop(to<bool>(crop));
104
+ return value(THIS->is_crop());
105
+ }
106
+
107
+ static
108
+ VALUE is_crop(VALUE self)
109
+ {
110
+ CHECK;
111
+ return value(THIS->is_crop());
112
+ }
113
+
51
114
  static
52
115
  VALUE image(VALUE self)
53
116
  {
@@ -56,6 +119,17 @@ VALUE image(VALUE self)
56
119
  return img ? value(*img) : nil();
57
120
  }
58
121
 
122
+ static
123
+ VALUE device_names(VALUE self)
124
+ {
125
+ auto names = Rays::get_camera_device_names();
126
+
127
+ std::vector<Value> v;
128
+ for (auto it = names.begin(), end = names.end(); it != end; ++it)
129
+ v.emplace_back(value(it->c_str()));
130
+ return value(v.size(), &v[0]);
131
+ }
132
+
59
133
 
60
134
  static Class cCamera;
61
135
 
@@ -66,11 +140,20 @@ Init_camera ()
66
140
 
67
141
  cCamera = rb_define_class_under(mRays, "Camera", rb_cObject);
68
142
  rb_define_alloc_func(cCamera, alloc);
69
- rb_define_private_method(cCamera, "initialize", RUBY_METHOD_FUNC(initialize), -1);
143
+ rb_define_private_method(cCamera, "setup", RUBY_METHOD_FUNC(setup), 5);
70
144
  rb_define_method(cCamera, "start", RUBY_METHOD_FUNC(start), 0);
71
145
  rb_define_method(cCamera, "stop", RUBY_METHOD_FUNC(stop), 0);
72
146
  cCamera.define_method("active?", is_active);
147
+ rb_define_method(cCamera, "min_width=", RUBY_METHOD_FUNC(set_min_width), 1);
148
+ rb_define_method(cCamera, "min_width", RUBY_METHOD_FUNC(min_width), 0);
149
+ rb_define_method(cCamera, "min_height=", RUBY_METHOD_FUNC(set_min_height), 1);
150
+ rb_define_method(cCamera, "min_height", RUBY_METHOD_FUNC(min_height), 0);
151
+ rb_define_method(cCamera, "resize=", RUBY_METHOD_FUNC(set_resize), 1);
152
+ cCamera.define_method("resize?", is_resize);
153
+ rb_define_method(cCamera, "crop=", RUBY_METHOD_FUNC(set_crop), 1);
154
+ cCamera.define_method("crop?", is_crop);
73
155
  rb_define_method(cCamera, "image", RUBY_METHOD_FUNC(image), 0);
156
+ rb_define_module_function(cCamera, "device_names", RUBY_METHOD_FUNC(device_names), 0);
74
157
  }
75
158
 
76
159
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.16
1
+ 0.1.17
@@ -110,7 +110,7 @@ Init_bitmap ()
110
110
  cBitmap.define_alloc_func(alloc);
111
111
  cBitmap.define_private_method("initialize", initialize);
112
112
  cBitmap.define_private_method("initialize_copy", initialize_copy);
113
- cBitmap.define_method("width", width);
113
+ cBitmap.define_method("width", width);
114
114
  cBitmap.define_method("height", height);
115
115
  cBitmap.define_method("color_space", color_space);
116
116
  cBitmap.define_method("[]=", set_at);
@@ -20,11 +20,14 @@ RUCY_DEF_ALLOC(alloc, klass)
20
20
  RUCY_END
21
21
 
22
22
  static
23
- RUCY_DEFN(initialize)
23
+ RUCY_DEF5(setup, device_name, min_width, min_height, resize, crop)
24
24
  {
25
25
  RUCY_CHECK_OBJ(Rays::Camera, self);
26
26
 
27
- *THIS = Rays::Camera();
27
+ *THIS = Rays::Camera(
28
+ device_name ? to<const char*>(device_name) : NULL,
29
+ to<int>(min_width), to<int>(min_height),
30
+ to<bool>(resize), to<bool>(crop));
28
31
  return self;
29
32
  }
30
33
  RUCY_END
@@ -53,6 +56,74 @@ RUCY_DEF0(is_active)
53
56
  }
54
57
  RUCY_END
55
58
 
59
+ static
60
+ RUCY_DEF1(set_min_width, width)
61
+ {
62
+ CHECK;
63
+ THIS->set_min_width(to<int>(width));
64
+ return value(THIS->min_width());
65
+ }
66
+ RUCY_END
67
+
68
+ static
69
+ RUCY_DEF0(min_width)
70
+ {
71
+ CHECK;
72
+ return value(THIS->min_width());
73
+ }
74
+ RUCY_END
75
+
76
+ static
77
+ RUCY_DEF1(set_min_height, height)
78
+ {
79
+ CHECK;
80
+ THIS->set_min_height(to<int>(height));
81
+ return value(THIS->min_height());
82
+ }
83
+ RUCY_END
84
+
85
+ static
86
+ RUCY_DEF0(min_height)
87
+ {
88
+ CHECK;
89
+ return value(THIS->min_height());
90
+ }
91
+ RUCY_END
92
+
93
+ static
94
+ RUCY_DEF1(set_resize, resize)
95
+ {
96
+ CHECK;
97
+ THIS->set_resize(to<bool>(resize));
98
+ return value(THIS->is_resize());
99
+ }
100
+ RUCY_END
101
+
102
+ static
103
+ RUCY_DEF0(is_resize)
104
+ {
105
+ CHECK;
106
+ return value(THIS->is_resize());
107
+ }
108
+ RUCY_END
109
+
110
+ static
111
+ RUCY_DEF1(set_crop, crop)
112
+ {
113
+ CHECK;
114
+ THIS->set_crop(to<bool>(crop));
115
+ return value(THIS->is_crop());
116
+ }
117
+ RUCY_END
118
+
119
+ static
120
+ RUCY_DEF0(is_crop)
121
+ {
122
+ CHECK;
123
+ return value(THIS->is_crop());
124
+ }
125
+ RUCY_END
126
+
56
127
  static
57
128
  RUCY_DEF0(image)
58
129
  {
@@ -62,6 +133,18 @@ RUCY_DEF0(image)
62
133
  }
63
134
  RUCY_END
64
135
 
136
+ static
137
+ RUCY_DEF0(device_names)
138
+ {
139
+ auto names = Rays::get_camera_device_names();
140
+
141
+ std::vector<Value> v;
142
+ for (auto it = names.begin(), end = names.end(); it != end; ++it)
143
+ v.emplace_back(value(it->c_str()));
144
+ return value(v.size(), &v[0]);
145
+ }
146
+ RUCY_END
147
+
65
148
 
66
149
  static Class cCamera;
67
150
 
@@ -72,11 +155,20 @@ Init_camera ()
72
155
 
73
156
  cCamera = mRays.define_class("Camera");
74
157
  cCamera.define_alloc_func(alloc);
75
- cCamera.define_private_method("initialize", initialize);
76
- cCamera.define_method("start", start);
77
- cCamera.define_method("stop", stop);
158
+ cCamera.define_private_method("setup", setup);
159
+ cCamera.define_method("start", start);
160
+ cCamera.define_method("stop", stop);
78
161
  cCamera.define_method("active?", is_active);
79
- cCamera.define_method("image", image);
162
+ cCamera.define_method("min_width=", set_min_width);
163
+ cCamera.define_method("min_width", min_width);
164
+ cCamera.define_method("min_height=", set_min_height);
165
+ cCamera.define_method("min_height", min_height);
166
+ cCamera.define_method("resize=", set_resize);
167
+ cCamera.define_method("resize?", is_resize);
168
+ cCamera.define_method("crop=", set_crop);
169
+ cCamera.define_method("crop?", is_crop);
170
+ cCamera.define_method("image", image);
171
+ cCamera.define_module_function("device_names", device_names);
80
172
  }
81
173
 
82
174
 
@@ -4,6 +4,7 @@
4
4
  #define __RAYS_CAMERA_H__
5
5
 
6
6
 
7
+ #include <vector>
7
8
  #include <xot/pimpl.h>
8
9
  #include <rays/defs.h>
9
10
  #include <rays/image.h>
@@ -20,7 +21,12 @@ namespace Rays
20
21
 
21
22
  public:
22
23
 
23
- Camera ();
24
+ Camera (
25
+ const char* device_name = NULL,
26
+ int min_width = -1,
27
+ int min_height = -1,
28
+ bool resize = true,
29
+ bool crop = true);
24
30
 
25
31
  ~Camera ();
26
32
 
@@ -30,6 +36,22 @@ namespace Rays
30
36
 
31
37
  bool is_active () const;
32
38
 
39
+ void set_min_width (int width);
40
+
41
+ int min_width () const;
42
+
43
+ void set_min_height (int height);
44
+
45
+ int min_height () const;
46
+
47
+ void set_resize (bool resize = true);
48
+
49
+ bool is_resize () const;
50
+
51
+ void set_crop (bool crop = true);
52
+
53
+ bool is_crop () const;
54
+
33
55
  const Image* image () const;
34
56
 
35
57
  operator bool () const;
@@ -43,6 +65,9 @@ namespace Rays
43
65
  };// Camera
44
66
 
45
67
 
68
+ std::vector<String> get_camera_device_names ();
69
+
70
+
46
71
  }// Rays
47
72
 
48
73
 
@@ -10,8 +10,11 @@ module Rays
10
10
 
11
11
  class Camera
12
12
 
13
- def initialize (*args, &block)
14
- super *args
13
+ def initialize (
14
+ min_width = -1, min_height = -1,
15
+ device_name: nil, resize: true, crop: true, &block)
16
+
17
+ setup device_name, min_width, min_height, resize, crop
15
18
  Xot::BlockUtil.instance_eval_or_block_call self, &block if block
16
19
  end
17
20
 
@@ -28,8 +28,8 @@ Gem::Specification.new do |s|
28
28
  s.platform = Gem::Platform::RUBY
29
29
  s.required_ruby_version = '~> 2'
30
30
 
31
- s.add_runtime_dependency 'xot', '~> 0.1'
32
- s.add_runtime_dependency 'rucy', '~> 0.1'
31
+ s.add_runtime_dependency 'xot', '~> 0.1.16'
32
+ s.add_runtime_dependency 'rucy', '~> 0.1.16'
33
33
 
34
34
  s.files = `git ls-files`.split $/
35
35
  s.executables = s.files.grep(%r{^bin/}) {|f| File.basename f}
@@ -1,7 +1,7 @@
1
1
  // -*- mode: c++ -*-
2
2
  #pragma once
3
- #ifndef __RAYS_SRC_OSX_BITMAP_H__
4
- #define __RAYS_SRC_OSX_BITMAP_H__
3
+ #ifndef __RAYS_SRC_IOS_BITMAP_H__
4
+ #define __RAYS_SRC_IOS_BITMAP_H__
5
5
 
6
6
 
7
7
  #import <CoreGraphics/CGImage.h>
@@ -12,7 +12,9 @@ namespace Rays
12
12
  {
13
13
 
14
14
 
15
- void Bitmap_copy_pixels (Bitmap* bitmap, CGImageRef image);
15
+ void Bitmap_draw_image (
16
+ Bitmap* bitmap, CGImageRef image,
17
+ coord x = 0, coord y = 0, coord width = -1, coord height = -1);
16
18
 
17
19
 
18
20
  }// Rays
@@ -176,8 +176,12 @@ namespace Rays
176
176
  }
177
177
 
178
178
  void
179
- Bitmap_copy_pixels (Bitmap* bitmap, CGImageRef image)
179
+ Bitmap_draw_image (
180
+ Bitmap* bitmap, CGImageRef image,
181
+ coord x, coord y, coord width, coord height)
180
182
  {
183
+ if (width == 0 || height == 0) return;
184
+
181
185
  if (!bitmap || !image)
182
186
  argument_error(__FILE__, __LINE__);
183
187
 
@@ -185,9 +189,9 @@ namespace Rays
185
189
  if (!context)
186
190
  rays_error(__FILE__, __LINE__, "getting CGContext failed.");
187
191
 
188
- size_t width = CGImageGetWidth(image);
189
- size_t height = CGImageGetHeight(image);
190
- CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
192
+ if (width < 0) width = (coord) CGImageGetWidth(image);
193
+ if (height < 0) height = (coord) CGImageGetHeight(image);
194
+ CGContextDrawImage(context, CGRectMake(x, y, width, height), image);
191
195
 
192
196
  Bitmap_set_modified(bitmap);
193
197
  }
@@ -265,7 +269,7 @@ namespace Rays
265
269
  if (!bmp)
266
270
  rays_error(__FILE__, __LINE__, "invalid bitmap.");
267
271
 
268
- Bitmap_copy_pixels(&bmp, image);
272
+ Bitmap_draw_image(&bmp, image);
269
273
  return bmp;
270
274
  }
271
275
 
@@ -3,6 +3,7 @@
3
3
 
4
4
 
5
5
  #import <AVFoundation/AVFoundation.h>
6
+ #include "rays/exception.h"
6
7
  #include "bitmap.h"
7
8
 
8
9
 
@@ -16,9 +17,10 @@ static int video_input_queue_index = 0;
16
17
  @implementation VideoInput
17
18
 
18
19
  {
19
- AVCaptureSession* captureSession;
20
- dispatch_queue_t captureQueue;
21
- CGImageRef captureImage;
20
+ AVCaptureSession* session;
21
+ dispatch_queue_t queue;
22
+ CGImageRef image;
23
+ AVCaptureVideoOrientation orientation;
22
24
  }
23
25
 
24
26
  - (id) init
@@ -26,9 +28,10 @@ static int video_input_queue_index = 0;
26
28
  self = [super init];
27
29
  if (self)
28
30
  {
29
- captureSession = nil;
30
- captureQueue = nil;
31
- captureImage = nil;
31
+ session = nil;
32
+ queue = nil;
33
+ image = nil;
34
+ orientation = AVCaptureVideoOrientationPortrait;
32
35
  }
33
36
  return self;
34
37
  }
@@ -38,37 +41,38 @@ static int video_input_queue_index = 0;
38
41
  [self stop];
39
42
  [self clearImage];
40
43
 
41
- if (captureQueue)
44
+ if (queue)
42
45
  {
43
- dispatch_release(captureQueue);
44
- captureQueue = nil;
46
+ dispatch_release(queue);
47
+ queue = nil;
45
48
  }
46
49
 
47
50
  [super dealloc];
48
51
  }
49
52
 
50
- - (dispatch_queue_t) queue
53
+ - (dispatch_queue_t) getQueue
51
54
  {
52
- if (!captureQueue)
55
+ if (!queue)
53
56
  {
54
57
  auto name = Xot::stringf(
55
58
  "org.xord.RaysVideoInputQueue_%d",
56
59
  video_input_queue_index++);
57
- captureQueue = dispatch_queue_create(name, DISPATCH_QUEUE_SERIAL);
60
+ queue = dispatch_queue_create(name, DISPATCH_QUEUE_SERIAL);
58
61
  }
59
- return captureQueue;
62
+ return queue;
60
63
  }
61
64
 
62
- - (BOOL) start
65
+ - (BOOL) start: (AVCaptureDevice*) device
66
+ preset: (AVCaptureSessionPreset) preset
63
67
  {
64
- [self stop];
68
+ if (!device) return NO;
65
69
 
66
- AVCaptureSession* session = [[[AVCaptureSession alloc] init] autorelease];
67
- session.sessionPreset = AVCaptureSessionPresetHigh;
70
+ [self stop];
71
+ [self updateOrientation];
68
72
 
69
- AVCaptureDevice* device =
70
- [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo];
71
- if (!device) return NO;
73
+ AVCaptureSession* sess = [[[AVCaptureSession alloc] init] autorelease];
74
+ if (preset != nil)
75
+ sess.sessionPreset = preset;
72
76
 
73
77
  //device.activeVideoMinFrameDuration = CMTimeMake(1, 30);
74
78
 
@@ -76,7 +80,7 @@ static int video_input_queue_index = 0;
76
80
  AVCaptureDeviceInput* input = [[[AVCaptureDeviceInput alloc]
77
81
  initWithDevice: device error: &error]
78
82
  autorelease];
79
- if (!input || error || ![session canAddInput: input])
83
+ if (!input || error || ![sess canAddInput: input])
80
84
  return NO;
81
85
 
82
86
  AVCaptureVideoDataOutput* output =
@@ -85,22 +89,63 @@ static int video_input_queue_index = 0;
85
89
  (NSString*) kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)
86
90
  };
87
91
  output.alwaysDiscardsLateVideoFrames = YES;
88
- [output setSampleBufferDelegate: self queue: self.queue];
89
- if (![session canAddOutput: output])
92
+ [output setSampleBufferDelegate: self queue: [self getQueue]];
93
+ if (![sess canAddOutput: output])
90
94
  return NO;
91
95
 
92
- [session addInput: input];
93
- [session addOutput: output];
94
- [session startRunning];
96
+ [sess addInput: input];
97
+ [sess addOutput: output];
98
+
99
+ AVCaptureConnection* connection =
100
+ [output connectionWithMediaType: AVMediaTypeVideo];
101
+ if (connection)
102
+ {
103
+ if (connection.isVideoOrientationSupported)
104
+ [connection setVideoOrientation: orientation];
95
105
 
96
- captureSession = [session retain];
106
+ if (connection.isVideoMirroringSupported)
107
+ {
108
+ [connection setVideoMirrored:
109
+ device.position == AVCaptureDevicePositionFront];
110
+ }
111
+ }
112
+
113
+ [sess startRunning];
114
+
115
+ session = [sess retain];
97
116
  return YES;
98
117
  }
99
118
 
119
+ - (void) updateCaptureOrientation
120
+ {
121
+ assert(Thread.isMainThread);
122
+
123
+ switch (UIApplication.sharedApplication.statusBarOrientation)
124
+ {
125
+ case UIInterfaceOrientationPortraitUpsideDown:
126
+ orientation = AVCaptureVideoOrientationPortraitUpsideDown;
127
+ break;
128
+
129
+ case UIInterfaceOrientationPortraitLandscapeLeft:
130
+ orientation = AVCaptureVideoOrientationLandscapeLeft;
131
+ break;
132
+
133
+ case UIInterfaceOrientationPortraitLandscapeRight:
134
+ orientation = AVCaptureVideoOrientationLandscapeRight;
135
+ break;
136
+
137
+ default:
138
+ orientation = AVCaptureVideoOrientationPortrait;
139
+ break;
140
+ }
141
+ }
142
+
100
143
  - (void) captureOutput: (AVCaptureOutput*) output
101
144
  didOutputSampleBuffer: (CMSampleBufferRef) sampleBuffer
102
145
  fromConnection: (AVCaptureConnection*) connection
103
146
  {
147
+ [connection setVideoOrientation: orientation];
148
+
104
149
  CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
105
150
  if (!pixelBuffer) return;
106
151
 
@@ -115,35 +160,37 @@ static int video_input_queue_index = 0;
115
160
 
116
161
  dispatch_async(dispatch_get_main_queue(), ^{
117
162
  [self clearImage];
118
- captureImage = cgImage;
163
+ image = cgImage;
164
+
165
+ [self updateOrientation];
119
166
  });
120
167
  }
121
168
 
122
169
  - (void) stop
123
170
  {
124
- if (!captureSession) return;
171
+ if (!session) return;
125
172
 
126
- [captureSession stopRunning];
127
- [captureSession release];
128
- captureSession = nil;
173
+ [session stopRunning];
174
+ [session release];
175
+ session = nil;
129
176
  }
130
177
 
131
178
  - (BOOL) isActive
132
179
  {
133
- return captureSession != nil;
180
+ return session != nil;
134
181
  }
135
182
 
136
183
  - (void) clearImage
137
184
  {
138
- if (!captureImage) return;
185
+ if (!image) return;
139
186
 
140
- CGImageRelease(captureImage);
141
- captureImage = nil;
187
+ CGImageRelease(image);
188
+ image = nil;
142
189
  }
143
190
 
144
191
  - (CGImageRef) getImage
145
192
  {
146
- return captureImage;
193
+ return image;
147
194
  }
148
195
 
149
196
  @end// VideoInput
@@ -156,10 +203,51 @@ namespace Rays
156
203
  struct Camera::Data
157
204
  {
158
205
 
206
+ String device_name;
207
+
208
+ int min_width = -1, min_height = -1;
209
+
210
+ bool resize = false, crop = false;
211
+
159
212
  mutable Image image;
160
213
 
161
214
  VideoInput* video_input = nil;
162
215
 
216
+ AVCaptureSessionPreset get_preset (AVCaptureDevice* device)
217
+ {
218
+ int w = min_width, h = min_height;
219
+ if (w > 0 && h > 0)
220
+ {
221
+ #define SUPPORT(x) \
222
+ [device supportsAVCaptureSessionPreset: AVCaptureSessionPreset##x]
223
+
224
+ //if (w <= 320 && h <= 240 && SUPPORT(320x240))
225
+ // return AVCaptureSessionPreset320x240;
226
+
227
+ if (w <= 352 && h <= 288 && SUPPORT(352x288))
228
+ return AVCaptureSessionPreset352x288;
229
+
230
+ if (w <= 640 && h <= 480 && SUPPORT(640x480))
231
+ return AVCaptureSessionPreset640x480;
232
+
233
+ //if (w <= 960 && h <= 540 && SUPPORT(960x540))
234
+ // return AVCaptureSessionPreset960x540;
235
+
236
+ if (w <= 1280 && h <= 720 && SUPPORT(1280x720))
237
+ return AVCaptureSessionPreset1280x720;
238
+
239
+ if (/*w <= 1920 && h <= 1080 &&*/ SUPPORT(1920x1080))
240
+ return AVCaptureSessionPreset1920x1080;
241
+
242
+ //if (SUPPORT(3840x2160))
243
+ // return AVCaptureSessionPreset3840x2160;
244
+
245
+ #undef SUPPORT
246
+ }
247
+
248
+ return nil;
249
+ }
250
+
163
251
  void update_image_from_video_input () const
164
252
  {
165
253
  if (!video_input) return;
@@ -167,24 +255,160 @@ namespace Rays
167
255
  CGImageRef cgImage = [video_input getImage];
168
256
  if (!cgImage) return;
169
257
 
170
- if (!image)
258
+ coord draw_x, draw_y, draw_width, draw_height;
259
+ int bitmap_width, bitmap_height;
260
+ get_draw_bounds(
261
+ &draw_x, &draw_y, &draw_width, &draw_height,
262
+ &bitmap_width, &bitmap_height,
263
+ cgImage);
264
+
265
+ if (
266
+ !image ||
267
+ image.bitmap().width() != bitmap_width ||
268
+ image.bitmap().height() != bitmap_height)
171
269
  {
172
- Bitmap bmp(
173
- (int) CGImageGetWidth(cgImage),
174
- (int) CGImageGetHeight(cgImage));
175
- image = Image(bmp);
270
+ image = Image(Bitmap(bitmap_width, bitmap_height));
176
271
  }
177
272
 
178
- Bitmap_copy_pixels(&image.bitmap(), cgImage);
273
+ Bitmap_draw_image(
274
+ &image.bitmap(), cgImage,
275
+ draw_x, draw_y, draw_width, draw_height);
179
276
 
180
277
  [video_input clearImage];
181
278
  }
182
279
 
280
+ void get_draw_bounds (
281
+ coord* draw_x, coord* draw_y, coord* draw_width, coord* draw_height,
282
+ int* bitmap_width, int* bitmap_height,
283
+ CGImageRef image) const
284
+ {
285
+ int image_width = (int) CGImageGetWidth(image);
286
+ int image_height = (int) CGImageGetHeight(image);
287
+ float image_ratio = (float) image_width / (float) image_height;
288
+
289
+ if (resize && min_width > 0 && min_height > 0)
290
+ {
291
+ float min_size_ratio = (float) min_width / (float) min_height;
292
+ if (image_ratio > min_size_ratio)
293
+ {
294
+ *draw_width = min_height * image_ratio;
295
+ *draw_height = min_height;
296
+ }
297
+ else
298
+ {
299
+ *draw_width = min_width;
300
+ *draw_height = min_width / image_ratio;
301
+ }
302
+ }
303
+ else if (resize && min_width > 0)
304
+ {
305
+ *draw_width = min_width;
306
+ *draw_height = min_width / image_ratio;
307
+ }
308
+ else if (resize && min_height > 0)
309
+ {
310
+ *draw_width = min_height * image_ratio;
311
+ *draw_height = min_height;
312
+ }
313
+ else
314
+ {
315
+ *draw_width = image_width;
316
+ *draw_height = image_height;
317
+ }
318
+
319
+ *draw_x = 0;
320
+ *draw_y = 0;
321
+ *bitmap_width = *draw_width;
322
+ *bitmap_height = *draw_height;
323
+
324
+ if (crop && min_width > 0)
325
+ {
326
+ *draw_x = min_width / 2 - *draw_width / 2;
327
+ *bitmap_width = min_width;
328
+ }
329
+ else if (crop && min_height > 0)
330
+ {
331
+ *draw_y = min_height / 2 - *draw_height / 2;
332
+ *bitmap_height = min_height;
333
+ }
334
+ }
335
+
183
336
  };// Camera::Data
184
337
 
185
338
 
186
- Camera::Camera ()
339
+ static NSArray<AVCaptureDevice*>*
340
+ get_video_devices ()
187
341
  {
342
+ #if 0
343
+ AVCaptureDeviceDiscoverySession* discoverySession =
344
+ [AVCaptureDeviceDiscoverySession
345
+ discoverySessionWithDeviceTypes: @[
346
+ AVCaptureDeviceTypeBuiltInWideAngleCamera]
347
+ mediaType: AVMediaTypeVideo
348
+ position: AVCaptureDevicePositionUnspecified];
349
+ NSArray<AVCaptureDevice*>* devices = discoverySession.devices;
350
+ for (AVCaptureDevice* d in devices)
351
+ {
352
+ printf("%s\n", d.localizedName.UTF8String);
353
+ }
354
+ #endif
355
+ NSMutableArray<AVCaptureDevice*>* devices = [NSMutableArray array];
356
+ for (AVCaptureDevice* d in AVCaptureDevice.devices)
357
+ {
358
+ if ([d hasMediaType: AVMediaTypeVideo])
359
+ [devices addObject: d];
360
+ }
361
+ return devices;
362
+ }
363
+
364
+ static AVCaptureDevice*
365
+ get_default_video_device ()
366
+ {
367
+ AVCaptureDevice* device =
368
+ [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo];
369
+ if (!device)
370
+ rays_error(__FILE__, __LINE__, "Default camera device is not found.");
371
+
372
+ return device;
373
+ }
374
+
375
+ static AVCaptureDevice*
376
+ get_video_device (const char* name)
377
+ {
378
+ if (!name || *name == 0)
379
+ return get_default_video_device();
380
+
381
+ for (AVCaptureDevice* d in get_video_devices())
382
+ {
383
+ if (strcmp(name, d.localizedName.UTF8String) == 0)
384
+ return d;
385
+ }
386
+
387
+ rays_error(__FILE__, __LINE__, "Camera device '%s' is not found.", name);
388
+ return nil;
389
+ }
390
+
391
+ std::vector<String>
392
+ get_camera_device_names ()
393
+ {
394
+ std::vector<String> names;
395
+ for (AVCaptureDevice* d in get_video_devices())
396
+ names.emplace_back(d.localizedName.UTF8String);
397
+ return names;
398
+ }
399
+
400
+
401
+ Camera::Camera (
402
+ const char* device_name,
403
+ int min_width, int min_height, bool resize, bool crop)
404
+ {
405
+ if (device_name)
406
+ self->device_name = device_name;
407
+
408
+ self->min_width = min_width;
409
+ self->min_height = min_height;
410
+ self->resize = resize;
411
+ self->crop = crop;
188
412
  }
189
413
 
190
414
  Camera::~Camera ()
@@ -197,7 +421,9 @@ namespace Rays
197
421
  Camera::start ()
198
422
  {
199
423
  if (!self->video_input) self->video_input = [[VideoInput alloc] init];
200
- return [self->video_input start];
424
+
425
+ AVCaptureDevice* device = get_video_device(self->device_name.c_str());
426
+ return [self->video_input start: device preset: self->get_preset(device)];
201
427
  }
202
428
 
203
429
  void
@@ -214,6 +440,54 @@ namespace Rays
214
440
  return self->video_input && [self->video_input isActive];
215
441
  }
216
442
 
443
+ void
444
+ Camera::set_min_width (int width)
445
+ {
446
+ self->min_width = width;
447
+ }
448
+
449
+ int
450
+ Camera::min_width () const
451
+ {
452
+ return self->min_width;
453
+ }
454
+
455
+ void
456
+ Camera::set_min_height (int height)
457
+ {
458
+ self->min_height = height;
459
+ }
460
+
461
+ int
462
+ Camera::min_height () const
463
+ {
464
+ return self->min_height;
465
+ }
466
+
467
+ void
468
+ Camera::set_resize (bool resize)
469
+ {
470
+ self->resize = resize;
471
+ }
472
+
473
+ bool
474
+ Camera::is_resize () const
475
+ {
476
+ return self->resize;
477
+ }
478
+
479
+ void
480
+ Camera::set_crop (bool crop)
481
+ {
482
+ self->crop = crop;
483
+ }
484
+
485
+ bool
486
+ Camera::is_crop () const
487
+ {
488
+ return self->crop;
489
+ }
490
+
217
491
  const Image*
218
492
  Camera::image () const
219
493
  {