rays 0.1.16 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  {