From 84ad104499d9efc0253dae1a60ee070ed375ad95 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 20 Oct 2022 00:44:55 +0300 Subject: Move test applications to src/apps/ The cam and qcam test application share code, currently through a crude hack that references the cam source files directly from the qcam meson.build file. To prepare for the introduction of hosting that code in a static library, move all applications to src/apps/. Signed-off-by: Laurent Pinchart Reviewed-by: Paul Elder Reviewed-by: Kieran Bingham --- src/apps/qcam/assets/feathericons/activity.svg | 1 + src/apps/qcam/assets/feathericons/airplay.svg | 1 + src/apps/qcam/assets/feathericons/alert-circle.svg | 1 + .../qcam/assets/feathericons/alert-octagon.svg | 1 + .../qcam/assets/feathericons/alert-triangle.svg | 1 + src/apps/qcam/assets/feathericons/align-center.svg | 1 + .../qcam/assets/feathericons/align-justify.svg | 1 + src/apps/qcam/assets/feathericons/align-left.svg | 1 + src/apps/qcam/assets/feathericons/align-right.svg | 1 + src/apps/qcam/assets/feathericons/anchor.svg | 1 + src/apps/qcam/assets/feathericons/aperture.svg | 1 + src/apps/qcam/assets/feathericons/archive.svg | 1 + .../qcam/assets/feathericons/arrow-down-circle.svg | 1 + .../qcam/assets/feathericons/arrow-down-left.svg | 1 + .../qcam/assets/feathericons/arrow-down-right.svg | 1 + src/apps/qcam/assets/feathericons/arrow-down.svg | 1 + .../qcam/assets/feathericons/arrow-left-circle.svg | 1 + src/apps/qcam/assets/feathericons/arrow-left.svg | 1 + .../assets/feathericons/arrow-right-circle.svg | 1 + src/apps/qcam/assets/feathericons/arrow-right.svg | 1 + .../qcam/assets/feathericons/arrow-up-circle.svg | 1 + .../qcam/assets/feathericons/arrow-up-left.svg | 1 + .../qcam/assets/feathericons/arrow-up-right.svg | 1 + src/apps/qcam/assets/feathericons/arrow-up.svg | 1 + src/apps/qcam/assets/feathericons/at-sign.svg | 1 + src/apps/qcam/assets/feathericons/award.svg | 1 + src/apps/qcam/assets/feathericons/bar-chart-2.svg | 1 + src/apps/qcam/assets/feathericons/bar-chart.svg | 1 + .../qcam/assets/feathericons/battery-charging.svg | 1 + src/apps/qcam/assets/feathericons/battery.svg | 1 + src/apps/qcam/assets/feathericons/bell-off.svg | 1 + src/apps/qcam/assets/feathericons/bell.svg | 1 + src/apps/qcam/assets/feathericons/bluetooth.svg | 1 + src/apps/qcam/assets/feathericons/bold.svg | 1 + src/apps/qcam/assets/feathericons/book-open.svg | 1 + src/apps/qcam/assets/feathericons/book.svg | 1 + src/apps/qcam/assets/feathericons/bookmark.svg | 1 + src/apps/qcam/assets/feathericons/box.svg | 1 + src/apps/qcam/assets/feathericons/briefcase.svg | 1 + src/apps/qcam/assets/feathericons/calendar.svg | 1 + src/apps/qcam/assets/feathericons/camera-off.svg | 1 + src/apps/qcam/assets/feathericons/camera.svg | 1 + src/apps/qcam/assets/feathericons/cast.svg | 1 + src/apps/qcam/assets/feathericons/check-circle.svg | 1 + src/apps/qcam/assets/feathericons/check-square.svg | 1 + src/apps/qcam/assets/feathericons/check.svg | 1 + src/apps/qcam/assets/feathericons/chevron-down.svg | 1 + src/apps/qcam/assets/feathericons/chevron-left.svg | 1 + .../qcam/assets/feathericons/chevron-right.svg | 1 + src/apps/qcam/assets/feathericons/chevron-up.svg | 1 + .../qcam/assets/feathericons/chevrons-down.svg | 1 + .../qcam/assets/feathericons/chevrons-left.svg | 1 + .../qcam/assets/feathericons/chevrons-right.svg | 1 + src/apps/qcam/assets/feathericons/chevrons-up.svg | 1 + src/apps/qcam/assets/feathericons/chrome.svg | 1 + src/apps/qcam/assets/feathericons/circle.svg | 1 + src/apps/qcam/assets/feathericons/clipboard.svg | 1 + src/apps/qcam/assets/feathericons/clock.svg | 1 + .../qcam/assets/feathericons/cloud-drizzle.svg | 1 + .../qcam/assets/feathericons/cloud-lightning.svg | 1 + src/apps/qcam/assets/feathericons/cloud-off.svg | 1 + src/apps/qcam/assets/feathericons/cloud-rain.svg | 1 + src/apps/qcam/assets/feathericons/cloud-snow.svg | 1 + src/apps/qcam/assets/feathericons/cloud.svg | 1 + src/apps/qcam/assets/feathericons/code.svg | 1 + src/apps/qcam/assets/feathericons/codepen.svg | 1 + src/apps/qcam/assets/feathericons/codesandbox.svg | 1 + src/apps/qcam/assets/feathericons/coffee.svg | 1 + src/apps/qcam/assets/feathericons/columns.svg | 1 + src/apps/qcam/assets/feathericons/command.svg | 1 + src/apps/qcam/assets/feathericons/compass.svg | 1 + src/apps/qcam/assets/feathericons/copy.svg | 1 + .../qcam/assets/feathericons/corner-down-left.svg | 1 + .../qcam/assets/feathericons/corner-down-right.svg | 1 + .../qcam/assets/feathericons/corner-left-down.svg | 1 + .../qcam/assets/feathericons/corner-left-up.svg | 1 + .../qcam/assets/feathericons/corner-right-down.svg | 1 + .../qcam/assets/feathericons/corner-right-up.svg | 1 + .../qcam/assets/feathericons/corner-up-left.svg | 1 + .../qcam/assets/feathericons/corner-up-right.svg | 1 + src/apps/qcam/assets/feathericons/cpu.svg | 1 + src/apps/qcam/assets/feathericons/credit-card.svg | 1 + src/apps/qcam/assets/feathericons/crop.svg | 1 + src/apps/qcam/assets/feathericons/crosshair.svg | 1 + src/apps/qcam/assets/feathericons/database.svg | 1 + src/apps/qcam/assets/feathericons/delete.svg | 1 + src/apps/qcam/assets/feathericons/disc.svg | 1 + src/apps/qcam/assets/feathericons/dollar-sign.svg | 1 + .../qcam/assets/feathericons/download-cloud.svg | 1 + src/apps/qcam/assets/feathericons/download.svg | 1 + src/apps/qcam/assets/feathericons/droplet.svg | 1 + src/apps/qcam/assets/feathericons/edit-2.svg | 1 + src/apps/qcam/assets/feathericons/edit-3.svg | 1 + src/apps/qcam/assets/feathericons/edit.svg | 1 + .../qcam/assets/feathericons/external-link.svg | 1 + src/apps/qcam/assets/feathericons/eye-off.svg | 1 + src/apps/qcam/assets/feathericons/eye.svg | 1 + src/apps/qcam/assets/feathericons/facebook.svg | 1 + src/apps/qcam/assets/feathericons/fast-forward.svg | 1 + src/apps/qcam/assets/feathericons/feather.svg | 1 + src/apps/qcam/assets/feathericons/feathericons.qrc | 11 + src/apps/qcam/assets/feathericons/figma.svg | 1 + src/apps/qcam/assets/feathericons/file-minus.svg | 1 + src/apps/qcam/assets/feathericons/file-plus.svg | 1 + src/apps/qcam/assets/feathericons/file-text.svg | 1 + src/apps/qcam/assets/feathericons/file.svg | 1 + src/apps/qcam/assets/feathericons/film.svg | 1 + src/apps/qcam/assets/feathericons/filter.svg | 1 + src/apps/qcam/assets/feathericons/flag.svg | 1 + src/apps/qcam/assets/feathericons/folder-minus.svg | 1 + src/apps/qcam/assets/feathericons/folder-plus.svg | 1 + src/apps/qcam/assets/feathericons/folder.svg | 1 + src/apps/qcam/assets/feathericons/framer.svg | 1 + src/apps/qcam/assets/feathericons/frown.svg | 1 + src/apps/qcam/assets/feathericons/gift.svg | 1 + src/apps/qcam/assets/feathericons/git-branch.svg | 1 + src/apps/qcam/assets/feathericons/git-commit.svg | 1 + src/apps/qcam/assets/feathericons/git-merge.svg | 1 + .../qcam/assets/feathericons/git-pull-request.svg | 1 + src/apps/qcam/assets/feathericons/github.svg | 1 + src/apps/qcam/assets/feathericons/gitlab.svg | 1 + src/apps/qcam/assets/feathericons/globe.svg | 1 + src/apps/qcam/assets/feathericons/grid.svg | 1 + src/apps/qcam/assets/feathericons/hard-drive.svg | 1 + src/apps/qcam/assets/feathericons/hash.svg | 1 + src/apps/qcam/assets/feathericons/headphones.svg | 1 + src/apps/qcam/assets/feathericons/heart.svg | 1 + src/apps/qcam/assets/feathericons/help-circle.svg | 1 + src/apps/qcam/assets/feathericons/hexagon.svg | 1 + src/apps/qcam/assets/feathericons/home.svg | 1 + src/apps/qcam/assets/feathericons/image.svg | 1 + src/apps/qcam/assets/feathericons/inbox.svg | 1 + src/apps/qcam/assets/feathericons/info.svg | 1 + src/apps/qcam/assets/feathericons/instagram.svg | 1 + src/apps/qcam/assets/feathericons/italic.svg | 1 + src/apps/qcam/assets/feathericons/key.svg | 1 + src/apps/qcam/assets/feathericons/layers.svg | 1 + src/apps/qcam/assets/feathericons/layout.svg | 1 + src/apps/qcam/assets/feathericons/life-buoy.svg | 1 + src/apps/qcam/assets/feathericons/link-2.svg | 1 + src/apps/qcam/assets/feathericons/link.svg | 1 + src/apps/qcam/assets/feathericons/linkedin.svg | 1 + src/apps/qcam/assets/feathericons/list.svg | 1 + src/apps/qcam/assets/feathericons/loader.svg | 1 + src/apps/qcam/assets/feathericons/lock.svg | 1 + src/apps/qcam/assets/feathericons/log-in.svg | 1 + src/apps/qcam/assets/feathericons/log-out.svg | 1 + src/apps/qcam/assets/feathericons/mail.svg | 1 + src/apps/qcam/assets/feathericons/map-pin.svg | 1 + src/apps/qcam/assets/feathericons/map.svg | 1 + src/apps/qcam/assets/feathericons/maximize-2.svg | 1 + src/apps/qcam/assets/feathericons/maximize.svg | 1 + src/apps/qcam/assets/feathericons/meh.svg | 1 + src/apps/qcam/assets/feathericons/menu.svg | 1 + .../qcam/assets/feathericons/message-circle.svg | 1 + .../qcam/assets/feathericons/message-square.svg | 1 + src/apps/qcam/assets/feathericons/mic-off.svg | 1 + src/apps/qcam/assets/feathericons/mic.svg | 1 + src/apps/qcam/assets/feathericons/minimize-2.svg | 1 + src/apps/qcam/assets/feathericons/minimize.svg | 1 + src/apps/qcam/assets/feathericons/minus-circle.svg | 1 + src/apps/qcam/assets/feathericons/minus-square.svg | 1 + src/apps/qcam/assets/feathericons/minus.svg | 1 + src/apps/qcam/assets/feathericons/monitor.svg | 1 + src/apps/qcam/assets/feathericons/moon.svg | 1 + .../qcam/assets/feathericons/more-horizontal.svg | 1 + .../qcam/assets/feathericons/more-vertical.svg | 1 + .../qcam/assets/feathericons/mouse-pointer.svg | 1 + src/apps/qcam/assets/feathericons/move.svg | 1 + src/apps/qcam/assets/feathericons/music.svg | 1 + src/apps/qcam/assets/feathericons/navigation-2.svg | 1 + src/apps/qcam/assets/feathericons/navigation.svg | 1 + src/apps/qcam/assets/feathericons/octagon.svg | 1 + src/apps/qcam/assets/feathericons/package.svg | 1 + src/apps/qcam/assets/feathericons/paperclip.svg | 1 + src/apps/qcam/assets/feathericons/pause-circle.svg | 1 + src/apps/qcam/assets/feathericons/pause.svg | 1 + src/apps/qcam/assets/feathericons/pen-tool.svg | 1 + src/apps/qcam/assets/feathericons/percent.svg | 1 + src/apps/qcam/assets/feathericons/phone-call.svg | 1 + .../qcam/assets/feathericons/phone-forwarded.svg | 1 + .../qcam/assets/feathericons/phone-incoming.svg | 1 + src/apps/qcam/assets/feathericons/phone-missed.svg | 1 + src/apps/qcam/assets/feathericons/phone-off.svg | 1 + .../qcam/assets/feathericons/phone-outgoing.svg | 1 + src/apps/qcam/assets/feathericons/phone.svg | 1 + src/apps/qcam/assets/feathericons/pie-chart.svg | 1 + src/apps/qcam/assets/feathericons/play-circle.svg | 1 + src/apps/qcam/assets/feathericons/play.svg | 1 + src/apps/qcam/assets/feathericons/plus-circle.svg | 1 + src/apps/qcam/assets/feathericons/plus-square.svg | 1 + src/apps/qcam/assets/feathericons/plus.svg | 1 + src/apps/qcam/assets/feathericons/pocket.svg | 1 + src/apps/qcam/assets/feathericons/power.svg | 1 + src/apps/qcam/assets/feathericons/printer.svg | 1 + src/apps/qcam/assets/feathericons/radio.svg | 1 + src/apps/qcam/assets/feathericons/refresh-ccw.svg | 1 + src/apps/qcam/assets/feathericons/refresh-cw.svg | 1 + src/apps/qcam/assets/feathericons/repeat.svg | 1 + src/apps/qcam/assets/feathericons/rewind.svg | 1 + src/apps/qcam/assets/feathericons/rotate-ccw.svg | 1 + src/apps/qcam/assets/feathericons/rotate-cw.svg | 1 + src/apps/qcam/assets/feathericons/rss.svg | 1 + src/apps/qcam/assets/feathericons/save.svg | 1 + src/apps/qcam/assets/feathericons/scissors.svg | 1 + src/apps/qcam/assets/feathericons/search.svg | 1 + src/apps/qcam/assets/feathericons/send.svg | 1 + src/apps/qcam/assets/feathericons/server.svg | 1 + src/apps/qcam/assets/feathericons/settings.svg | 1 + src/apps/qcam/assets/feathericons/share-2.svg | 1 + src/apps/qcam/assets/feathericons/share.svg | 1 + src/apps/qcam/assets/feathericons/shield-off.svg | 1 + src/apps/qcam/assets/feathericons/shield.svg | 1 + src/apps/qcam/assets/feathericons/shopping-bag.svg | 1 + .../qcam/assets/feathericons/shopping-cart.svg | 1 + src/apps/qcam/assets/feathericons/shuffle.svg | 1 + src/apps/qcam/assets/feathericons/sidebar.svg | 1 + src/apps/qcam/assets/feathericons/skip-back.svg | 1 + src/apps/qcam/assets/feathericons/skip-forward.svg | 1 + src/apps/qcam/assets/feathericons/slack.svg | 1 + src/apps/qcam/assets/feathericons/slash.svg | 1 + src/apps/qcam/assets/feathericons/sliders.svg | 1 + src/apps/qcam/assets/feathericons/smartphone.svg | 1 + src/apps/qcam/assets/feathericons/smile.svg | 1 + src/apps/qcam/assets/feathericons/speaker.svg | 1 + src/apps/qcam/assets/feathericons/square.svg | 1 + src/apps/qcam/assets/feathericons/star.svg | 1 + src/apps/qcam/assets/feathericons/stop-circle.svg | 1 + src/apps/qcam/assets/feathericons/sun.svg | 1 + src/apps/qcam/assets/feathericons/sunrise.svg | 1 + src/apps/qcam/assets/feathericons/sunset.svg | 1 + src/apps/qcam/assets/feathericons/tablet.svg | 1 + src/apps/qcam/assets/feathericons/tag.svg | 1 + src/apps/qcam/assets/feathericons/target.svg | 1 + src/apps/qcam/assets/feathericons/terminal.svg | 1 + src/apps/qcam/assets/feathericons/thermometer.svg | 1 + src/apps/qcam/assets/feathericons/thumbs-down.svg | 1 + src/apps/qcam/assets/feathericons/thumbs-up.svg | 1 + src/apps/qcam/assets/feathericons/toggle-left.svg | 1 + src/apps/qcam/assets/feathericons/toggle-right.svg | 1 + src/apps/qcam/assets/feathericons/tool.svg | 1 + src/apps/qcam/assets/feathericons/trash-2.svg | 1 + src/apps/qcam/assets/feathericons/trash.svg | 1 + src/apps/qcam/assets/feathericons/trello.svg | 1 + .../qcam/assets/feathericons/trending-down.svg | 1 + src/apps/qcam/assets/feathericons/trending-up.svg | 1 + src/apps/qcam/assets/feathericons/triangle.svg | 1 + src/apps/qcam/assets/feathericons/truck.svg | 1 + src/apps/qcam/assets/feathericons/tv.svg | 1 + src/apps/qcam/assets/feathericons/twitch.svg | 1 + src/apps/qcam/assets/feathericons/twitter.svg | 1 + src/apps/qcam/assets/feathericons/type.svg | 1 + src/apps/qcam/assets/feathericons/umbrella.svg | 1 + src/apps/qcam/assets/feathericons/underline.svg | 1 + src/apps/qcam/assets/feathericons/unlock.svg | 1 + src/apps/qcam/assets/feathericons/upload-cloud.svg | 1 + src/apps/qcam/assets/feathericons/upload.svg | 1 + src/apps/qcam/assets/feathericons/user-check.svg | 1 + src/apps/qcam/assets/feathericons/user-minus.svg | 1 + src/apps/qcam/assets/feathericons/user-plus.svg | 1 + src/apps/qcam/assets/feathericons/user-x.svg | 1 + src/apps/qcam/assets/feathericons/user.svg | 1 + src/apps/qcam/assets/feathericons/users.svg | 1 + src/apps/qcam/assets/feathericons/video-off.svg | 1 + src/apps/qcam/assets/feathericons/video.svg | 1 + src/apps/qcam/assets/feathericons/voicemail.svg | 1 + src/apps/qcam/assets/feathericons/volume-1.svg | 1 + src/apps/qcam/assets/feathericons/volume-2.svg | 1 + src/apps/qcam/assets/feathericons/volume-x.svg | 1 + src/apps/qcam/assets/feathericons/volume.svg | 1 + src/apps/qcam/assets/feathericons/watch.svg | 1 + src/apps/qcam/assets/feathericons/wifi-off.svg | 1 + src/apps/qcam/assets/feathericons/wifi.svg | 1 + src/apps/qcam/assets/feathericons/wind.svg | 1 + src/apps/qcam/assets/feathericons/x-circle.svg | 1 + src/apps/qcam/assets/feathericons/x-octagon.svg | 1 + src/apps/qcam/assets/feathericons/x-square.svg | 1 + src/apps/qcam/assets/feathericons/x.svg | 1 + src/apps/qcam/assets/feathericons/youtube.svg | 1 + src/apps/qcam/assets/feathericons/zap-off.svg | 1 + src/apps/qcam/assets/feathericons/zap.svg | 1 + src/apps/qcam/assets/feathericons/zoom-in.svg | 1 + src/apps/qcam/assets/feathericons/zoom-out.svg | 1 + src/apps/qcam/assets/shader/RGB.frag | 22 + src/apps/qcam/assets/shader/YUV_2_planes.frag | 42 ++ src/apps/qcam/assets/shader/YUV_3_planes.frag | 36 + src/apps/qcam/assets/shader/YUV_packed.frag | 83 ++ src/apps/qcam/assets/shader/bayer_1x_packed.frag | 216 ++++++ src/apps/qcam/assets/shader/bayer_8.frag | 107 +++ src/apps/qcam/assets/shader/bayer_8.vert | 51 ++ src/apps/qcam/assets/shader/identity.vert | 18 + src/apps/qcam/assets/shader/shaders.qrc | 13 + src/apps/qcam/cam_select_dialog.cpp | 111 +++ src/apps/qcam/cam_select_dialog.h | 47 ++ src/apps/qcam/format_converter.cpp | 359 +++++++++ src/apps/qcam/format_converter.h | 62 ++ src/apps/qcam/main.cpp | 89 +++ src/apps/qcam/main_window.cpp | 790 +++++++++++++++++++ src/apps/qcam/main_window.h | 133 ++++ src/apps/qcam/meson.build | 83 ++ src/apps/qcam/message_handler.cpp | 27 + src/apps/qcam/message_handler.h | 24 + src/apps/qcam/viewfinder.h | 34 + src/apps/qcam/viewfinder_gl.cpp | 835 +++++++++++++++++++++ src/apps/qcam/viewfinder_gl.h | 108 +++ src/apps/qcam/viewfinder_qt.cpp | 181 +++++ src/apps/qcam/viewfinder_qt.h | 64 ++ 307 files changed, 3828 insertions(+) create mode 100644 src/apps/qcam/assets/feathericons/activity.svg create mode 100644 src/apps/qcam/assets/feathericons/airplay.svg create mode 100644 src/apps/qcam/assets/feathericons/alert-circle.svg create mode 100644 src/apps/qcam/assets/feathericons/alert-octagon.svg create mode 100644 src/apps/qcam/assets/feathericons/alert-triangle.svg create mode 100644 src/apps/qcam/assets/feathericons/align-center.svg create mode 100644 src/apps/qcam/assets/feathericons/align-justify.svg create mode 100644 src/apps/qcam/assets/feathericons/align-left.svg create mode 100644 src/apps/qcam/assets/feathericons/align-right.svg create mode 100644 src/apps/qcam/assets/feathericons/anchor.svg create mode 100644 src/apps/qcam/assets/feathericons/aperture.svg create mode 100644 src/apps/qcam/assets/feathericons/archive.svg create mode 100644 src/apps/qcam/assets/feathericons/arrow-down-circle.svg create mode 100644 src/apps/qcam/assets/feathericons/arrow-down-left.svg create mode 100644 src/apps/qcam/assets/feathericons/arrow-down-right.svg create mode 100644 src/apps/qcam/assets/feathericons/arrow-down.svg create mode 100644 src/apps/qcam/assets/feathericons/arrow-left-circle.svg create mode 100644 src/apps/qcam/assets/feathericons/arrow-left.svg create mode 100644 src/apps/qcam/assets/feathericons/arrow-right-circle.svg create mode 100644 src/apps/qcam/assets/feathericons/arrow-right.svg create mode 100644 src/apps/qcam/assets/feathericons/arrow-up-circle.svg create mode 100644 src/apps/qcam/assets/feathericons/arrow-up-left.svg create mode 100644 src/apps/qcam/assets/feathericons/arrow-up-right.svg create mode 100644 src/apps/qcam/assets/feathericons/arrow-up.svg create mode 100644 src/apps/qcam/assets/feathericons/at-sign.svg create mode 100644 src/apps/qcam/assets/feathericons/award.svg create mode 100644 src/apps/qcam/assets/feathericons/bar-chart-2.svg create mode 100644 src/apps/qcam/assets/feathericons/bar-chart.svg create mode 100644 src/apps/qcam/assets/feathericons/battery-charging.svg create mode 100644 src/apps/qcam/assets/feathericons/battery.svg create mode 100644 src/apps/qcam/assets/feathericons/bell-off.svg create mode 100644 src/apps/qcam/assets/feathericons/bell.svg create mode 100644 src/apps/qcam/assets/feathericons/bluetooth.svg create mode 100644 src/apps/qcam/assets/feathericons/bold.svg create mode 100644 src/apps/qcam/assets/feathericons/book-open.svg create mode 100644 src/apps/qcam/assets/feathericons/book.svg create mode 100644 src/apps/qcam/assets/feathericons/bookmark.svg create mode 100644 src/apps/qcam/assets/feathericons/box.svg create mode 100644 src/apps/qcam/assets/feathericons/briefcase.svg create mode 100644 src/apps/qcam/assets/feathericons/calendar.svg create mode 100644 src/apps/qcam/assets/feathericons/camera-off.svg create mode 100644 src/apps/qcam/assets/feathericons/camera.svg create mode 100644 src/apps/qcam/assets/feathericons/cast.svg create mode 100644 src/apps/qcam/assets/feathericons/check-circle.svg create mode 100644 src/apps/qcam/assets/feathericons/check-square.svg create mode 100644 src/apps/qcam/assets/feathericons/check.svg create mode 100644 src/apps/qcam/assets/feathericons/chevron-down.svg create mode 100644 src/apps/qcam/assets/feathericons/chevron-left.svg create mode 100644 src/apps/qcam/assets/feathericons/chevron-right.svg create mode 100644 src/apps/qcam/assets/feathericons/chevron-up.svg create mode 100644 src/apps/qcam/assets/feathericons/chevrons-down.svg create mode 100644 src/apps/qcam/assets/feathericons/chevrons-left.svg create mode 100644 src/apps/qcam/assets/feathericons/chevrons-right.svg create mode 100644 src/apps/qcam/assets/feathericons/chevrons-up.svg create mode 100644 src/apps/qcam/assets/feathericons/chrome.svg create mode 100644 src/apps/qcam/assets/feathericons/circle.svg create mode 100644 src/apps/qcam/assets/feathericons/clipboard.svg create mode 100644 src/apps/qcam/assets/feathericons/clock.svg create mode 100644 src/apps/qcam/assets/feathericons/cloud-drizzle.svg create mode 100644 src/apps/qcam/assets/feathericons/cloud-lightning.svg create mode 100644 src/apps/qcam/assets/feathericons/cloud-off.svg create mode 100644 src/apps/qcam/assets/feathericons/cloud-rain.svg create mode 100644 src/apps/qcam/assets/feathericons/cloud-snow.svg create mode 100644 src/apps/qcam/assets/feathericons/cloud.svg create mode 100644 src/apps/qcam/assets/feathericons/code.svg create mode 100644 src/apps/qcam/assets/feathericons/codepen.svg create mode 100644 src/apps/qcam/assets/feathericons/codesandbox.svg create mode 100644 src/apps/qcam/assets/feathericons/coffee.svg create mode 100644 src/apps/qcam/assets/feathericons/columns.svg create mode 100644 src/apps/qcam/assets/feathericons/command.svg create mode 100644 src/apps/qcam/assets/feathericons/compass.svg create mode 100644 src/apps/qcam/assets/feathericons/copy.svg create mode 100644 src/apps/qcam/assets/feathericons/corner-down-left.svg create mode 100644 src/apps/qcam/assets/feathericons/corner-down-right.svg create mode 100644 src/apps/qcam/assets/feathericons/corner-left-down.svg create mode 100644 src/apps/qcam/assets/feathericons/corner-left-up.svg create mode 100644 src/apps/qcam/assets/feathericons/corner-right-down.svg create mode 100644 src/apps/qcam/assets/feathericons/corner-right-up.svg create mode 100644 src/apps/qcam/assets/feathericons/corner-up-left.svg create mode 100644 src/apps/qcam/assets/feathericons/corner-up-right.svg create mode 100644 src/apps/qcam/assets/feathericons/cpu.svg create mode 100644 src/apps/qcam/assets/feathericons/credit-card.svg create mode 100644 src/apps/qcam/assets/feathericons/crop.svg create mode 100644 src/apps/qcam/assets/feathericons/crosshair.svg create mode 100644 src/apps/qcam/assets/feathericons/database.svg create mode 100644 src/apps/qcam/assets/feathericons/delete.svg create mode 100644 src/apps/qcam/assets/feathericons/disc.svg create mode 100644 src/apps/qcam/assets/feathericons/dollar-sign.svg create mode 100644 src/apps/qcam/assets/feathericons/download-cloud.svg create mode 100644 src/apps/qcam/assets/feathericons/download.svg create mode 100644 src/apps/qcam/assets/feathericons/droplet.svg create mode 100644 src/apps/qcam/assets/feathericons/edit-2.svg create mode 100644 src/apps/qcam/assets/feathericons/edit-3.svg create mode 100644 src/apps/qcam/assets/feathericons/edit.svg create mode 100644 src/apps/qcam/assets/feathericons/external-link.svg create mode 100644 src/apps/qcam/assets/feathericons/eye-off.svg create mode 100644 src/apps/qcam/assets/feathericons/eye.svg create mode 100644 src/apps/qcam/assets/feathericons/facebook.svg create mode 100644 src/apps/qcam/assets/feathericons/fast-forward.svg create mode 100644 src/apps/qcam/assets/feathericons/feather.svg create mode 100644 src/apps/qcam/assets/feathericons/feathericons.qrc create mode 100644 src/apps/qcam/assets/feathericons/figma.svg create mode 100644 src/apps/qcam/assets/feathericons/file-minus.svg create mode 100644 src/apps/qcam/assets/feathericons/file-plus.svg create mode 100644 src/apps/qcam/assets/feathericons/file-text.svg create mode 100644 src/apps/qcam/assets/feathericons/file.svg create mode 100644 src/apps/qcam/assets/feathericons/film.svg create mode 100644 src/apps/qcam/assets/feathericons/filter.svg create mode 100644 src/apps/qcam/assets/feathericons/flag.svg create mode 100644 src/apps/qcam/assets/feathericons/folder-minus.svg create mode 100644 src/apps/qcam/assets/feathericons/folder-plus.svg create mode 100644 src/apps/qcam/assets/feathericons/folder.svg create mode 100644 src/apps/qcam/assets/feathericons/framer.svg create mode 100644 src/apps/qcam/assets/feathericons/frown.svg create mode 100644 src/apps/qcam/assets/feathericons/gift.svg create mode 100644 src/apps/qcam/assets/feathericons/git-branch.svg create mode 100644 src/apps/qcam/assets/feathericons/git-commit.svg create mode 100644 src/apps/qcam/assets/feathericons/git-merge.svg create mode 100644 src/apps/qcam/assets/feathericons/git-pull-request.svg create mode 100644 src/apps/qcam/assets/feathericons/github.svg create mode 100644 src/apps/qcam/assets/feathericons/gitlab.svg create mode 100644 src/apps/qcam/assets/feathericons/globe.svg create mode 100644 src/apps/qcam/assets/feathericons/grid.svg create mode 100644 src/apps/qcam/assets/feathericons/hard-drive.svg create mode 100644 src/apps/qcam/assets/feathericons/hash.svg create mode 100644 src/apps/qcam/assets/feathericons/headphones.svg create mode 100644 src/apps/qcam/assets/feathericons/heart.svg create mode 100644 src/apps/qcam/assets/feathericons/help-circle.svg create mode 100644 src/apps/qcam/assets/feathericons/hexagon.svg create mode 100644 src/apps/qcam/assets/feathericons/home.svg create mode 100644 src/apps/qcam/assets/feathericons/image.svg create mode 100644 src/apps/qcam/assets/feathericons/inbox.svg create mode 100644 src/apps/qcam/assets/feathericons/info.svg create mode 100644 src/apps/qcam/assets/feathericons/instagram.svg create mode 100644 src/apps/qcam/assets/feathericons/italic.svg create mode 100644 src/apps/qcam/assets/feathericons/key.svg create mode 100644 src/apps/qcam/assets/feathericons/layers.svg create mode 100644 src/apps/qcam/assets/feathericons/layout.svg create mode 100644 src/apps/qcam/assets/feathericons/life-buoy.svg create mode 100644 src/apps/qcam/assets/feathericons/link-2.svg create mode 100644 src/apps/qcam/assets/feathericons/link.svg create mode 100644 src/apps/qcam/assets/feathericons/linkedin.svg create mode 100644 src/apps/qcam/assets/feathericons/list.svg create mode 100644 src/apps/qcam/assets/feathericons/loader.svg create mode 100644 src/apps/qcam/assets/feathericons/lock.svg create mode 100644 src/apps/qcam/assets/feathericons/log-in.svg create mode 100644 src/apps/qcam/assets/feathericons/log-out.svg create mode 100644 src/apps/qcam/assets/feathericons/mail.svg create mode 100644 src/apps/qcam/assets/feathericons/map-pin.svg create mode 100644 src/apps/qcam/assets/feathericons/map.svg create mode 100644 src/apps/qcam/assets/feathericons/maximize-2.svg create mode 100644 src/apps/qcam/assets/feathericons/maximize.svg create mode 100644 src/apps/qcam/assets/feathericons/meh.svg create mode 100644 src/apps/qcam/assets/feathericons/menu.svg create mode 100644 src/apps/qcam/assets/feathericons/message-circle.svg create mode 100644 src/apps/qcam/assets/feathericons/message-square.svg create mode 100644 src/apps/qcam/assets/feathericons/mic-off.svg create mode 100644 src/apps/qcam/assets/feathericons/mic.svg create mode 100644 src/apps/qcam/assets/feathericons/minimize-2.svg create mode 100644 src/apps/qcam/assets/feathericons/minimize.svg create mode 100644 src/apps/qcam/assets/feathericons/minus-circle.svg create mode 100644 src/apps/qcam/assets/feathericons/minus-square.svg create mode 100644 src/apps/qcam/assets/feathericons/minus.svg create mode 100644 src/apps/qcam/assets/feathericons/monitor.svg create mode 100644 src/apps/qcam/assets/feathericons/moon.svg create mode 100644 src/apps/qcam/assets/feathericons/more-horizontal.svg create mode 100644 src/apps/qcam/assets/feathericons/more-vertical.svg create mode 100644 src/apps/qcam/assets/feathericons/mouse-pointer.svg create mode 100644 src/apps/qcam/assets/feathericons/move.svg create mode 100644 src/apps/qcam/assets/feathericons/music.svg create mode 100644 src/apps/qcam/assets/feathericons/navigation-2.svg create mode 100644 src/apps/qcam/assets/feathericons/navigation.svg create mode 100644 src/apps/qcam/assets/feathericons/octagon.svg create mode 100644 src/apps/qcam/assets/feathericons/package.svg create mode 100644 src/apps/qcam/assets/feathericons/paperclip.svg create mode 100644 src/apps/qcam/assets/feathericons/pause-circle.svg create mode 100644 src/apps/qcam/assets/feathericons/pause.svg create mode 100644 src/apps/qcam/assets/feathericons/pen-tool.svg create mode 100644 src/apps/qcam/assets/feathericons/percent.svg create mode 100644 src/apps/qcam/assets/feathericons/phone-call.svg create mode 100644 src/apps/qcam/assets/feathericons/phone-forwarded.svg create mode 100644 src/apps/qcam/assets/feathericons/phone-incoming.svg create mode 100644 src/apps/qcam/assets/feathericons/phone-missed.svg create mode 100644 src/apps/qcam/assets/feathericons/phone-off.svg create mode 100644 src/apps/qcam/assets/feathericons/phone-outgoing.svg create mode 100644 src/apps/qcam/assets/feathericons/phone.svg create mode 100644 src/apps/qcam/assets/feathericons/pie-chart.svg create mode 100644 src/apps/qcam/assets/feathericons/play-circle.svg create mode 100644 src/apps/qcam/assets/feathericons/play.svg create mode 100644 src/apps/qcam/assets/feathericons/plus-circle.svg create mode 100644 src/apps/qcam/assets/feathericons/plus-square.svg create mode 100644 src/apps/qcam/assets/feathericons/plus.svg create mode 100644 src/apps/qcam/assets/feathericons/pocket.svg create mode 100644 src/apps/qcam/assets/feathericons/power.svg create mode 100644 src/apps/qcam/assets/feathericons/printer.svg create mode 100644 src/apps/qcam/assets/feathericons/radio.svg create mode 100644 src/apps/qcam/assets/feathericons/refresh-ccw.svg create mode 100644 src/apps/qcam/assets/feathericons/refresh-cw.svg create mode 100644 src/apps/qcam/assets/feathericons/repeat.svg create mode 100644 src/apps/qcam/assets/feathericons/rewind.svg create mode 100644 src/apps/qcam/assets/feathericons/rotate-ccw.svg create mode 100644 src/apps/qcam/assets/feathericons/rotate-cw.svg create mode 100644 src/apps/qcam/assets/feathericons/rss.svg create mode 100644 src/apps/qcam/assets/feathericons/save.svg create mode 100644 src/apps/qcam/assets/feathericons/scissors.svg create mode 100644 src/apps/qcam/assets/feathericons/search.svg create mode 100644 src/apps/qcam/assets/feathericons/send.svg create mode 100644 src/apps/qcam/assets/feathericons/server.svg create mode 100644 src/apps/qcam/assets/feathericons/settings.svg create mode 100644 src/apps/qcam/assets/feathericons/share-2.svg create mode 100644 src/apps/qcam/assets/feathericons/share.svg create mode 100644 src/apps/qcam/assets/feathericons/shield-off.svg create mode 100644 src/apps/qcam/assets/feathericons/shield.svg create mode 100644 src/apps/qcam/assets/feathericons/shopping-bag.svg create mode 100644 src/apps/qcam/assets/feathericons/shopping-cart.svg create mode 100644 src/apps/qcam/assets/feathericons/shuffle.svg create mode 100644 src/apps/qcam/assets/feathericons/sidebar.svg create mode 100644 src/apps/qcam/assets/feathericons/skip-back.svg create mode 100644 src/apps/qcam/assets/feathericons/skip-forward.svg create mode 100644 src/apps/qcam/assets/feathericons/slack.svg create mode 100644 src/apps/qcam/assets/feathericons/slash.svg create mode 100644 src/apps/qcam/assets/feathericons/sliders.svg create mode 100644 src/apps/qcam/assets/feathericons/smartphone.svg create mode 100644 src/apps/qcam/assets/feathericons/smile.svg create mode 100644 src/apps/qcam/assets/feathericons/speaker.svg create mode 100644 src/apps/qcam/assets/feathericons/square.svg create mode 100644 src/apps/qcam/assets/feathericons/star.svg create mode 100644 src/apps/qcam/assets/feathericons/stop-circle.svg create mode 100644 src/apps/qcam/assets/feathericons/sun.svg create mode 100644 src/apps/qcam/assets/feathericons/sunrise.svg create mode 100644 src/apps/qcam/assets/feathericons/sunset.svg create mode 100644 src/apps/qcam/assets/feathericons/tablet.svg create mode 100644 src/apps/qcam/assets/feathericons/tag.svg create mode 100644 src/apps/qcam/assets/feathericons/target.svg create mode 100644 src/apps/qcam/assets/feathericons/terminal.svg create mode 100644 src/apps/qcam/assets/feathericons/thermometer.svg create mode 100644 src/apps/qcam/assets/feathericons/thumbs-down.svg create mode 100644 src/apps/qcam/assets/feathericons/thumbs-up.svg create mode 100644 src/apps/qcam/assets/feathericons/toggle-left.svg create mode 100644 src/apps/qcam/assets/feathericons/toggle-right.svg create mode 100644 src/apps/qcam/assets/feathericons/tool.svg create mode 100644 src/apps/qcam/assets/feathericons/trash-2.svg create mode 100644 src/apps/qcam/assets/feathericons/trash.svg create mode 100644 src/apps/qcam/assets/feathericons/trello.svg create mode 100644 src/apps/qcam/assets/feathericons/trending-down.svg create mode 100644 src/apps/qcam/assets/feathericons/trending-up.svg create mode 100644 src/apps/qcam/assets/feathericons/triangle.svg create mode 100644 src/apps/qcam/assets/feathericons/truck.svg create mode 100644 src/apps/qcam/assets/feathericons/tv.svg create mode 100644 src/apps/qcam/assets/feathericons/twitch.svg create mode 100644 src/apps/qcam/assets/feathericons/twitter.svg create mode 100644 src/apps/qcam/assets/feathericons/type.svg create mode 100644 src/apps/qcam/assets/feathericons/umbrella.svg create mode 100644 src/apps/qcam/assets/feathericons/underline.svg create mode 100644 src/apps/qcam/assets/feathericons/unlock.svg create mode 100644 src/apps/qcam/assets/feathericons/upload-cloud.svg create mode 100644 src/apps/qcam/assets/feathericons/upload.svg create mode 100644 src/apps/qcam/assets/feathericons/user-check.svg create mode 100644 src/apps/qcam/assets/feathericons/user-minus.svg create mode 100644 src/apps/qcam/assets/feathericons/user-plus.svg create mode 100644 src/apps/qcam/assets/feathericons/user-x.svg create mode 100644 src/apps/qcam/assets/feathericons/user.svg create mode 100644 src/apps/qcam/assets/feathericons/users.svg create mode 100644 src/apps/qcam/assets/feathericons/video-off.svg create mode 100644 src/apps/qcam/assets/feathericons/video.svg create mode 100644 src/apps/qcam/assets/feathericons/voicemail.svg create mode 100644 src/apps/qcam/assets/feathericons/volume-1.svg create mode 100644 src/apps/qcam/assets/feathericons/volume-2.svg create mode 100644 src/apps/qcam/assets/feathericons/volume-x.svg create mode 100644 src/apps/qcam/assets/feathericons/volume.svg create mode 100644 src/apps/qcam/assets/feathericons/watch.svg create mode 100644 src/apps/qcam/assets/feathericons/wifi-off.svg create mode 100644 src/apps/qcam/assets/feathericons/wifi.svg create mode 100644 src/apps/qcam/assets/feathericons/wind.svg create mode 100644 src/apps/qcam/assets/feathericons/x-circle.svg create mode 100644 src/apps/qcam/assets/feathericons/x-octagon.svg create mode 100644 src/apps/qcam/assets/feathericons/x-square.svg create mode 100644 src/apps/qcam/assets/feathericons/x.svg create mode 100644 src/apps/qcam/assets/feathericons/youtube.svg create mode 100644 src/apps/qcam/assets/feathericons/zap-off.svg create mode 100644 src/apps/qcam/assets/feathericons/zap.svg create mode 100644 src/apps/qcam/assets/feathericons/zoom-in.svg create mode 100644 src/apps/qcam/assets/feathericons/zoom-out.svg create mode 100644 src/apps/qcam/assets/shader/RGB.frag create mode 100644 src/apps/qcam/assets/shader/YUV_2_planes.frag create mode 100644 src/apps/qcam/assets/shader/YUV_3_planes.frag create mode 100644 src/apps/qcam/assets/shader/YUV_packed.frag create mode 100644 src/apps/qcam/assets/shader/bayer_1x_packed.frag create mode 100644 src/apps/qcam/assets/shader/bayer_8.frag create mode 100644 src/apps/qcam/assets/shader/bayer_8.vert create mode 100644 src/apps/qcam/assets/shader/identity.vert create mode 100644 src/apps/qcam/assets/shader/shaders.qrc create mode 100644 src/apps/qcam/cam_select_dialog.cpp create mode 100644 src/apps/qcam/cam_select_dialog.h create mode 100644 src/apps/qcam/format_converter.cpp create mode 100644 src/apps/qcam/format_converter.h create mode 100644 src/apps/qcam/main.cpp create mode 100644 src/apps/qcam/main_window.cpp create mode 100644 src/apps/qcam/main_window.h create mode 100644 src/apps/qcam/meson.build create mode 100644 src/apps/qcam/message_handler.cpp create mode 100644 src/apps/qcam/message_handler.h create mode 100644 src/apps/qcam/viewfinder.h create mode 100644 src/apps/qcam/viewfinder_gl.cpp create mode 100644 src/apps/qcam/viewfinder_gl.h create mode 100644 src/apps/qcam/viewfinder_qt.cpp create mode 100644 src/apps/qcam/viewfinder_qt.h (limited to 'src/apps/qcam') diff --git a/src/apps/qcam/assets/feathericons/activity.svg b/src/apps/qcam/assets/feathericons/activity.svg new file mode 100644 index 00000000..669a57a7 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/activity.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/airplay.svg b/src/apps/qcam/assets/feathericons/airplay.svg new file mode 100644 index 00000000..7ce73022 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/airplay.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/alert-circle.svg b/src/apps/qcam/assets/feathericons/alert-circle.svg new file mode 100644 index 00000000..8d02b7d1 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/alert-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/alert-octagon.svg b/src/apps/qcam/assets/feathericons/alert-octagon.svg new file mode 100644 index 00000000..de9b03f2 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/alert-octagon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/alert-triangle.svg b/src/apps/qcam/assets/feathericons/alert-triangle.svg new file mode 100644 index 00000000..6dcb0963 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/alert-triangle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/align-center.svg b/src/apps/qcam/assets/feathericons/align-center.svg new file mode 100644 index 00000000..5b8842ea --- /dev/null +++ b/src/apps/qcam/assets/feathericons/align-center.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/align-justify.svg b/src/apps/qcam/assets/feathericons/align-justify.svg new file mode 100644 index 00000000..0539876f --- /dev/null +++ b/src/apps/qcam/assets/feathericons/align-justify.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/align-left.svg b/src/apps/qcam/assets/feathericons/align-left.svg new file mode 100644 index 00000000..9ac852a5 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/align-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/align-right.svg b/src/apps/qcam/assets/feathericons/align-right.svg new file mode 100644 index 00000000..ef139ffa --- /dev/null +++ b/src/apps/qcam/assets/feathericons/align-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/anchor.svg b/src/apps/qcam/assets/feathericons/anchor.svg new file mode 100644 index 00000000..e01627a3 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/anchor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/aperture.svg b/src/apps/qcam/assets/feathericons/aperture.svg new file mode 100644 index 00000000..9936e868 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/aperture.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/archive.svg b/src/apps/qcam/assets/feathericons/archive.svg new file mode 100644 index 00000000..428882c8 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/archive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/arrow-down-circle.svg b/src/apps/qcam/assets/feathericons/arrow-down-circle.svg new file mode 100644 index 00000000..3238091b --- /dev/null +++ b/src/apps/qcam/assets/feathericons/arrow-down-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/arrow-down-left.svg b/src/apps/qcam/assets/feathericons/arrow-down-left.svg new file mode 100644 index 00000000..72483584 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/arrow-down-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/arrow-down-right.svg b/src/apps/qcam/assets/feathericons/arrow-down-right.svg new file mode 100644 index 00000000..81d9822b --- /dev/null +++ b/src/apps/qcam/assets/feathericons/arrow-down-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/arrow-down.svg b/src/apps/qcam/assets/feathericons/arrow-down.svg new file mode 100644 index 00000000..4f84f627 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/arrow-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/arrow-left-circle.svg b/src/apps/qcam/assets/feathericons/arrow-left-circle.svg new file mode 100644 index 00000000..3b19ff8a --- /dev/null +++ b/src/apps/qcam/assets/feathericons/arrow-left-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/arrow-left.svg b/src/apps/qcam/assets/feathericons/arrow-left.svg new file mode 100644 index 00000000..a5058fc7 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/arrow-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/arrow-right-circle.svg b/src/apps/qcam/assets/feathericons/arrow-right-circle.svg new file mode 100644 index 00000000..ff01dd58 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/arrow-right-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/arrow-right.svg b/src/apps/qcam/assets/feathericons/arrow-right.svg new file mode 100644 index 00000000..939b57c5 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/arrow-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/arrow-up-circle.svg b/src/apps/qcam/assets/feathericons/arrow-up-circle.svg new file mode 100644 index 00000000..044a75d3 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/arrow-up-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/arrow-up-left.svg b/src/apps/qcam/assets/feathericons/arrow-up-left.svg new file mode 100644 index 00000000..cea55e87 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/arrow-up-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/arrow-up-right.svg b/src/apps/qcam/assets/feathericons/arrow-up-right.svg new file mode 100644 index 00000000..95678e00 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/arrow-up-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/arrow-up.svg b/src/apps/qcam/assets/feathericons/arrow-up.svg new file mode 100644 index 00000000..16b13aba --- /dev/null +++ b/src/apps/qcam/assets/feathericons/arrow-up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/at-sign.svg b/src/apps/qcam/assets/feathericons/at-sign.svg new file mode 100644 index 00000000..5a5e5d0d --- /dev/null +++ b/src/apps/qcam/assets/feathericons/at-sign.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/award.svg b/src/apps/qcam/assets/feathericons/award.svg new file mode 100644 index 00000000..be70d5a1 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/award.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/bar-chart-2.svg b/src/apps/qcam/assets/feathericons/bar-chart-2.svg new file mode 100644 index 00000000..864167a6 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/bar-chart-2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/bar-chart.svg b/src/apps/qcam/assets/feathericons/bar-chart.svg new file mode 100644 index 00000000..074d7c1a --- /dev/null +++ b/src/apps/qcam/assets/feathericons/bar-chart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/battery-charging.svg b/src/apps/qcam/assets/feathericons/battery-charging.svg new file mode 100644 index 00000000..644cb59c --- /dev/null +++ b/src/apps/qcam/assets/feathericons/battery-charging.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/battery.svg b/src/apps/qcam/assets/feathericons/battery.svg new file mode 100644 index 00000000..7fe87710 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/battery.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/bell-off.svg b/src/apps/qcam/assets/feathericons/bell-off.svg new file mode 100644 index 00000000..4b07c848 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/bell-off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/bell.svg b/src/apps/qcam/assets/feathericons/bell.svg new file mode 100644 index 00000000..bba561c1 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/bell.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/bluetooth.svg b/src/apps/qcam/assets/feathericons/bluetooth.svg new file mode 100644 index 00000000..cebed7b1 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/bluetooth.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/bold.svg b/src/apps/qcam/assets/feathericons/bold.svg new file mode 100644 index 00000000..d1a4efd3 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/bold.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/book-open.svg b/src/apps/qcam/assets/feathericons/book-open.svg new file mode 100644 index 00000000..5e0ca0ab --- /dev/null +++ b/src/apps/qcam/assets/feathericons/book-open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/book.svg b/src/apps/qcam/assets/feathericons/book.svg new file mode 100644 index 00000000..12ffcbc4 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/book.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/bookmark.svg b/src/apps/qcam/assets/feathericons/bookmark.svg new file mode 100644 index 00000000..2239cc58 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/bookmark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/box.svg b/src/apps/qcam/assets/feathericons/box.svg new file mode 100644 index 00000000..d89be30f --- /dev/null +++ b/src/apps/qcam/assets/feathericons/box.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/briefcase.svg b/src/apps/qcam/assets/feathericons/briefcase.svg new file mode 100644 index 00000000..e3af0506 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/briefcase.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/calendar.svg b/src/apps/qcam/assets/feathericons/calendar.svg new file mode 100644 index 00000000..6c7fd870 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/calendar.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/camera-off.svg b/src/apps/qcam/assets/feathericons/camera-off.svg new file mode 100644 index 00000000..daa3e25f --- /dev/null +++ b/src/apps/qcam/assets/feathericons/camera-off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/camera.svg b/src/apps/qcam/assets/feathericons/camera.svg new file mode 100644 index 00000000..0e7f0603 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/camera.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/cast.svg b/src/apps/qcam/assets/feathericons/cast.svg new file mode 100644 index 00000000..63c954d9 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/cast.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/check-circle.svg b/src/apps/qcam/assets/feathericons/check-circle.svg new file mode 100644 index 00000000..f2f4fd1a --- /dev/null +++ b/src/apps/qcam/assets/feathericons/check-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/check-square.svg b/src/apps/qcam/assets/feathericons/check-square.svg new file mode 100644 index 00000000..72ab7a80 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/check-square.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/check.svg b/src/apps/qcam/assets/feathericons/check.svg new file mode 100644 index 00000000..1c209899 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/chevron-down.svg b/src/apps/qcam/assets/feathericons/chevron-down.svg new file mode 100644 index 00000000..278c6a31 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/chevron-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/chevron-left.svg b/src/apps/qcam/assets/feathericons/chevron-left.svg new file mode 100644 index 00000000..747d46d9 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/chevron-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/chevron-right.svg b/src/apps/qcam/assets/feathericons/chevron-right.svg new file mode 100644 index 00000000..258de414 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/chevron-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/chevron-up.svg b/src/apps/qcam/assets/feathericons/chevron-up.svg new file mode 100644 index 00000000..4eb5ecc3 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/chevron-up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/chevrons-down.svg b/src/apps/qcam/assets/feathericons/chevrons-down.svg new file mode 100644 index 00000000..e67ef2fb --- /dev/null +++ b/src/apps/qcam/assets/feathericons/chevrons-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/chevrons-left.svg b/src/apps/qcam/assets/feathericons/chevrons-left.svg new file mode 100644 index 00000000..c32e3983 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/chevrons-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/chevrons-right.svg b/src/apps/qcam/assets/feathericons/chevrons-right.svg new file mode 100644 index 00000000..f5068145 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/chevrons-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/chevrons-up.svg b/src/apps/qcam/assets/feathericons/chevrons-up.svg new file mode 100644 index 00000000..0eaf5183 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/chevrons-up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/chrome.svg b/src/apps/qcam/assets/feathericons/chrome.svg new file mode 100644 index 00000000..9189815e --- /dev/null +++ b/src/apps/qcam/assets/feathericons/chrome.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/circle.svg b/src/apps/qcam/assets/feathericons/circle.svg new file mode 100644 index 00000000..b0090882 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/clipboard.svg b/src/apps/qcam/assets/feathericons/clipboard.svg new file mode 100644 index 00000000..ccee454d --- /dev/null +++ b/src/apps/qcam/assets/feathericons/clipboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/clock.svg b/src/apps/qcam/assets/feathericons/clock.svg new file mode 100644 index 00000000..ea3f5e50 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/clock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/cloud-drizzle.svg b/src/apps/qcam/assets/feathericons/cloud-drizzle.svg new file mode 100644 index 00000000..13af6bb5 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/cloud-drizzle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/cloud-lightning.svg b/src/apps/qcam/assets/feathericons/cloud-lightning.svg new file mode 100644 index 00000000..32d154cc --- /dev/null +++ b/src/apps/qcam/assets/feathericons/cloud-lightning.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/cloud-off.svg b/src/apps/qcam/assets/feathericons/cloud-off.svg new file mode 100644 index 00000000..1e1e7d60 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/cloud-off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/cloud-rain.svg b/src/apps/qcam/assets/feathericons/cloud-rain.svg new file mode 100644 index 00000000..3e0b85b0 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/cloud-rain.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/cloud-snow.svg b/src/apps/qcam/assets/feathericons/cloud-snow.svg new file mode 100644 index 00000000..e4eb8207 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/cloud-snow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/cloud.svg b/src/apps/qcam/assets/feathericons/cloud.svg new file mode 100644 index 00000000..0ee0c632 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/cloud.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/code.svg b/src/apps/qcam/assets/feathericons/code.svg new file mode 100644 index 00000000..c4954b55 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/code.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/codepen.svg b/src/apps/qcam/assets/feathericons/codepen.svg new file mode 100644 index 00000000..ab2a815a --- /dev/null +++ b/src/apps/qcam/assets/feathericons/codepen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/codesandbox.svg b/src/apps/qcam/assets/feathericons/codesandbox.svg new file mode 100644 index 00000000..49848f52 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/codesandbox.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/coffee.svg b/src/apps/qcam/assets/feathericons/coffee.svg new file mode 100644 index 00000000..32905e52 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/coffee.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/columns.svg b/src/apps/qcam/assets/feathericons/columns.svg new file mode 100644 index 00000000..d264b557 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/columns.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/command.svg b/src/apps/qcam/assets/feathericons/command.svg new file mode 100644 index 00000000..93f554c3 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/command.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/compass.svg b/src/apps/qcam/assets/feathericons/compass.svg new file mode 100644 index 00000000..32962608 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/compass.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/copy.svg b/src/apps/qcam/assets/feathericons/copy.svg new file mode 100644 index 00000000..4e0b09f1 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/copy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/corner-down-left.svg b/src/apps/qcam/assets/feathericons/corner-down-left.svg new file mode 100644 index 00000000..9fffb3e9 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/corner-down-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/corner-down-right.svg b/src/apps/qcam/assets/feathericons/corner-down-right.svg new file mode 100644 index 00000000..b27d408d --- /dev/null +++ b/src/apps/qcam/assets/feathericons/corner-down-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/corner-left-down.svg b/src/apps/qcam/assets/feathericons/corner-left-down.svg new file mode 100644 index 00000000..24b8375c --- /dev/null +++ b/src/apps/qcam/assets/feathericons/corner-left-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/corner-left-up.svg b/src/apps/qcam/assets/feathericons/corner-left-up.svg new file mode 100644 index 00000000..e54527cd --- /dev/null +++ b/src/apps/qcam/assets/feathericons/corner-left-up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/corner-right-down.svg b/src/apps/qcam/assets/feathericons/corner-right-down.svg new file mode 100644 index 00000000..a49e6d6c --- /dev/null +++ b/src/apps/qcam/assets/feathericons/corner-right-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/corner-right-up.svg b/src/apps/qcam/assets/feathericons/corner-right-up.svg new file mode 100644 index 00000000..a5c5dce5 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/corner-right-up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/corner-up-left.svg b/src/apps/qcam/assets/feathericons/corner-up-left.svg new file mode 100644 index 00000000..0a1ffd61 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/corner-up-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/corner-up-right.svg b/src/apps/qcam/assets/feathericons/corner-up-right.svg new file mode 100644 index 00000000..0b8f961b --- /dev/null +++ b/src/apps/qcam/assets/feathericons/corner-up-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/cpu.svg b/src/apps/qcam/assets/feathericons/cpu.svg new file mode 100644 index 00000000..2ed16ef7 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/cpu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/credit-card.svg b/src/apps/qcam/assets/feathericons/credit-card.svg new file mode 100644 index 00000000..1b7fd029 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/credit-card.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/crop.svg b/src/apps/qcam/assets/feathericons/crop.svg new file mode 100644 index 00000000..ffbfd045 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/crop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/crosshair.svg b/src/apps/qcam/assets/feathericons/crosshair.svg new file mode 100644 index 00000000..ba394015 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/crosshair.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/database.svg b/src/apps/qcam/assets/feathericons/database.svg new file mode 100644 index 00000000..c296fbcf --- /dev/null +++ b/src/apps/qcam/assets/feathericons/database.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/delete.svg b/src/apps/qcam/assets/feathericons/delete.svg new file mode 100644 index 00000000..8c6074b9 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/disc.svg b/src/apps/qcam/assets/feathericons/disc.svg new file mode 100644 index 00000000..2595b444 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/disc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/dollar-sign.svg b/src/apps/qcam/assets/feathericons/dollar-sign.svg new file mode 100644 index 00000000..1a124d26 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/dollar-sign.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/download-cloud.svg b/src/apps/qcam/assets/feathericons/download-cloud.svg new file mode 100644 index 00000000..f3126fc3 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/download-cloud.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/download.svg b/src/apps/qcam/assets/feathericons/download.svg new file mode 100644 index 00000000..76767a92 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/droplet.svg b/src/apps/qcam/assets/feathericons/droplet.svg new file mode 100644 index 00000000..ca093014 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/droplet.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/edit-2.svg b/src/apps/qcam/assets/feathericons/edit-2.svg new file mode 100644 index 00000000..06830c9d --- /dev/null +++ b/src/apps/qcam/assets/feathericons/edit-2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/edit-3.svg b/src/apps/qcam/assets/feathericons/edit-3.svg new file mode 100644 index 00000000..d728efcc --- /dev/null +++ b/src/apps/qcam/assets/feathericons/edit-3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/edit.svg b/src/apps/qcam/assets/feathericons/edit.svg new file mode 100644 index 00000000..ec7b4ca2 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/external-link.svg b/src/apps/qcam/assets/feathericons/external-link.svg new file mode 100644 index 00000000..6236df3e --- /dev/null +++ b/src/apps/qcam/assets/feathericons/external-link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/eye-off.svg b/src/apps/qcam/assets/feathericons/eye-off.svg new file mode 100644 index 00000000..77c54cb4 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/eye-off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/eye.svg b/src/apps/qcam/assets/feathericons/eye.svg new file mode 100644 index 00000000..9cde2437 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/eye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/facebook.svg b/src/apps/qcam/assets/feathericons/facebook.svg new file mode 100644 index 00000000..2570f56a --- /dev/null +++ b/src/apps/qcam/assets/feathericons/facebook.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/fast-forward.svg b/src/apps/qcam/assets/feathericons/fast-forward.svg new file mode 100644 index 00000000..fa39877a --- /dev/null +++ b/src/apps/qcam/assets/feathericons/fast-forward.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/feather.svg b/src/apps/qcam/assets/feathericons/feather.svg new file mode 100644 index 00000000..ac3b868d --- /dev/null +++ b/src/apps/qcam/assets/feathericons/feather.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/feathericons.qrc b/src/apps/qcam/assets/feathericons/feathericons.qrc new file mode 100644 index 00000000..c5302040 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/feathericons.qrc @@ -0,0 +1,11 @@ + + + + aperture.svg + camera-off.svg + play-circle.svg + save.svg + stop-circle.svg + x-circle.svg + + diff --git a/src/apps/qcam/assets/feathericons/figma.svg b/src/apps/qcam/assets/feathericons/figma.svg new file mode 100644 index 00000000..66fd2178 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/figma.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/file-minus.svg b/src/apps/qcam/assets/feathericons/file-minus.svg new file mode 100644 index 00000000..345756ef --- /dev/null +++ b/src/apps/qcam/assets/feathericons/file-minus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/file-plus.svg b/src/apps/qcam/assets/feathericons/file-plus.svg new file mode 100644 index 00000000..eed12004 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/file-plus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/file-text.svg b/src/apps/qcam/assets/feathericons/file-text.svg new file mode 100644 index 00000000..4197ddd4 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/file-text.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/file.svg b/src/apps/qcam/assets/feathericons/file.svg new file mode 100644 index 00000000..378519ab --- /dev/null +++ b/src/apps/qcam/assets/feathericons/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/film.svg b/src/apps/qcam/assets/feathericons/film.svg new file mode 100644 index 00000000..ac46360d --- /dev/null +++ b/src/apps/qcam/assets/feathericons/film.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/filter.svg b/src/apps/qcam/assets/feathericons/filter.svg new file mode 100644 index 00000000..38a47e04 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/filter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/flag.svg b/src/apps/qcam/assets/feathericons/flag.svg new file mode 100644 index 00000000..037737cb --- /dev/null +++ b/src/apps/qcam/assets/feathericons/flag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/folder-minus.svg b/src/apps/qcam/assets/feathericons/folder-minus.svg new file mode 100644 index 00000000..d5b7af65 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/folder-minus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/folder-plus.svg b/src/apps/qcam/assets/feathericons/folder-plus.svg new file mode 100644 index 00000000..898f2fc9 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/folder-plus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/folder.svg b/src/apps/qcam/assets/feathericons/folder.svg new file mode 100644 index 00000000..134458b9 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/folder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/framer.svg b/src/apps/qcam/assets/feathericons/framer.svg new file mode 100644 index 00000000..3e663478 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/framer.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/frown.svg b/src/apps/qcam/assets/feathericons/frown.svg new file mode 100644 index 00000000..f3122547 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/frown.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/gift.svg b/src/apps/qcam/assets/feathericons/gift.svg new file mode 100644 index 00000000..d2c14bd6 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/gift.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/git-branch.svg b/src/apps/qcam/assets/feathericons/git-branch.svg new file mode 100644 index 00000000..44003726 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/git-branch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/git-commit.svg b/src/apps/qcam/assets/feathericons/git-commit.svg new file mode 100644 index 00000000..e959d725 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/git-commit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/git-merge.svg b/src/apps/qcam/assets/feathericons/git-merge.svg new file mode 100644 index 00000000..c65fffdd --- /dev/null +++ b/src/apps/qcam/assets/feathericons/git-merge.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/git-pull-request.svg b/src/apps/qcam/assets/feathericons/git-pull-request.svg new file mode 100644 index 00000000..fc80bdfd --- /dev/null +++ b/src/apps/qcam/assets/feathericons/git-pull-request.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/github.svg b/src/apps/qcam/assets/feathericons/github.svg new file mode 100644 index 00000000..ff0af481 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/gitlab.svg b/src/apps/qcam/assets/feathericons/gitlab.svg new file mode 100644 index 00000000..85d54a1e --- /dev/null +++ b/src/apps/qcam/assets/feathericons/gitlab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/globe.svg b/src/apps/qcam/assets/feathericons/globe.svg new file mode 100644 index 00000000..0a0586d3 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/grid.svg b/src/apps/qcam/assets/feathericons/grid.svg new file mode 100644 index 00000000..8ef2e9d8 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/grid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/hard-drive.svg b/src/apps/qcam/assets/feathericons/hard-drive.svg new file mode 100644 index 00000000..8e90fa1b --- /dev/null +++ b/src/apps/qcam/assets/feathericons/hard-drive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/hash.svg b/src/apps/qcam/assets/feathericons/hash.svg new file mode 100644 index 00000000..c9c8d41f --- /dev/null +++ b/src/apps/qcam/assets/feathericons/hash.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/headphones.svg b/src/apps/qcam/assets/feathericons/headphones.svg new file mode 100644 index 00000000..fd8915b4 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/headphones.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/heart.svg b/src/apps/qcam/assets/feathericons/heart.svg new file mode 100644 index 00000000..a083b7e2 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/heart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/help-circle.svg b/src/apps/qcam/assets/feathericons/help-circle.svg new file mode 100644 index 00000000..51fddd80 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/help-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/hexagon.svg b/src/apps/qcam/assets/feathericons/hexagon.svg new file mode 100644 index 00000000..eae7f255 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/hexagon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/home.svg b/src/apps/qcam/assets/feathericons/home.svg new file mode 100644 index 00000000..7bb31b23 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/home.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/image.svg b/src/apps/qcam/assets/feathericons/image.svg new file mode 100644 index 00000000..a7d84b98 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/image.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/inbox.svg b/src/apps/qcam/assets/feathericons/inbox.svg new file mode 100644 index 00000000..03a13b4e --- /dev/null +++ b/src/apps/qcam/assets/feathericons/inbox.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/info.svg b/src/apps/qcam/assets/feathericons/info.svg new file mode 100644 index 00000000..a09fa5f1 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/instagram.svg b/src/apps/qcam/assets/feathericons/instagram.svg new file mode 100644 index 00000000..9fdb8e35 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/instagram.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/italic.svg b/src/apps/qcam/assets/feathericons/italic.svg new file mode 100644 index 00000000..a123d371 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/italic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/key.svg b/src/apps/qcam/assets/feathericons/key.svg new file mode 100644 index 00000000..e778e74e --- /dev/null +++ b/src/apps/qcam/assets/feathericons/key.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/layers.svg b/src/apps/qcam/assets/feathericons/layers.svg new file mode 100644 index 00000000..ea788c22 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/layers.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/layout.svg b/src/apps/qcam/assets/feathericons/layout.svg new file mode 100644 index 00000000..28743d92 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/layout.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/life-buoy.svg b/src/apps/qcam/assets/feathericons/life-buoy.svg new file mode 100644 index 00000000..54c2bd7d --- /dev/null +++ b/src/apps/qcam/assets/feathericons/life-buoy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/link-2.svg b/src/apps/qcam/assets/feathericons/link-2.svg new file mode 100644 index 00000000..8cc7f6dd --- /dev/null +++ b/src/apps/qcam/assets/feathericons/link-2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/link.svg b/src/apps/qcam/assets/feathericons/link.svg new file mode 100644 index 00000000..c89dd41c --- /dev/null +++ b/src/apps/qcam/assets/feathericons/link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/linkedin.svg b/src/apps/qcam/assets/feathericons/linkedin.svg new file mode 100644 index 00000000..39531094 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/linkedin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/list.svg b/src/apps/qcam/assets/feathericons/list.svg new file mode 100644 index 00000000..5ce38eaa --- /dev/null +++ b/src/apps/qcam/assets/feathericons/list.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/loader.svg b/src/apps/qcam/assets/feathericons/loader.svg new file mode 100644 index 00000000..e1a70c12 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/loader.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/lock.svg b/src/apps/qcam/assets/feathericons/lock.svg new file mode 100644 index 00000000..de09d9db --- /dev/null +++ b/src/apps/qcam/assets/feathericons/lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/log-in.svg b/src/apps/qcam/assets/feathericons/log-in.svg new file mode 100644 index 00000000..ba0da59a --- /dev/null +++ b/src/apps/qcam/assets/feathericons/log-in.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/log-out.svg b/src/apps/qcam/assets/feathericons/log-out.svg new file mode 100644 index 00000000..c9002c90 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/log-out.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/mail.svg b/src/apps/qcam/assets/feathericons/mail.svg new file mode 100644 index 00000000..2af169e8 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/mail.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/map-pin.svg b/src/apps/qcam/assets/feathericons/map-pin.svg new file mode 100644 index 00000000..d5548e92 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/map-pin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/map.svg b/src/apps/qcam/assets/feathericons/map.svg new file mode 100644 index 00000000..ecebd7bf --- /dev/null +++ b/src/apps/qcam/assets/feathericons/map.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/maximize-2.svg b/src/apps/qcam/assets/feathericons/maximize-2.svg new file mode 100644 index 00000000..e41fc0b7 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/maximize-2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/maximize.svg b/src/apps/qcam/assets/feathericons/maximize.svg new file mode 100644 index 00000000..fc305189 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/maximize.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/meh.svg b/src/apps/qcam/assets/feathericons/meh.svg new file mode 100644 index 00000000..6f57fff2 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/meh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/menu.svg b/src/apps/qcam/assets/feathericons/menu.svg new file mode 100644 index 00000000..e8a84a95 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/menu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/message-circle.svg b/src/apps/qcam/assets/feathericons/message-circle.svg new file mode 100644 index 00000000..4b21b32b --- /dev/null +++ b/src/apps/qcam/assets/feathericons/message-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/message-square.svg b/src/apps/qcam/assets/feathericons/message-square.svg new file mode 100644 index 00000000..6a2e4e59 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/message-square.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/mic-off.svg b/src/apps/qcam/assets/feathericons/mic-off.svg new file mode 100644 index 00000000..0786219c --- /dev/null +++ b/src/apps/qcam/assets/feathericons/mic-off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/mic.svg b/src/apps/qcam/assets/feathericons/mic.svg new file mode 100644 index 00000000..dc5f780c --- /dev/null +++ b/src/apps/qcam/assets/feathericons/mic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/minimize-2.svg b/src/apps/qcam/assets/feathericons/minimize-2.svg new file mode 100644 index 00000000..a720fa6c --- /dev/null +++ b/src/apps/qcam/assets/feathericons/minimize-2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/minimize.svg b/src/apps/qcam/assets/feathericons/minimize.svg new file mode 100644 index 00000000..46d61196 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/minimize.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/minus-circle.svg b/src/apps/qcam/assets/feathericons/minus-circle.svg new file mode 100644 index 00000000..80c0de1e --- /dev/null +++ b/src/apps/qcam/assets/feathericons/minus-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/minus-square.svg b/src/apps/qcam/assets/feathericons/minus-square.svg new file mode 100644 index 00000000..4862832a --- /dev/null +++ b/src/apps/qcam/assets/feathericons/minus-square.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/minus.svg b/src/apps/qcam/assets/feathericons/minus.svg new file mode 100644 index 00000000..93cc7340 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/minus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/monitor.svg b/src/apps/qcam/assets/feathericons/monitor.svg new file mode 100644 index 00000000..6c3556db --- /dev/null +++ b/src/apps/qcam/assets/feathericons/monitor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/moon.svg b/src/apps/qcam/assets/feathericons/moon.svg new file mode 100644 index 00000000..dbf7c6cf --- /dev/null +++ b/src/apps/qcam/assets/feathericons/moon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/more-horizontal.svg b/src/apps/qcam/assets/feathericons/more-horizontal.svg new file mode 100644 index 00000000..dc6a8556 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/more-horizontal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/more-vertical.svg b/src/apps/qcam/assets/feathericons/more-vertical.svg new file mode 100644 index 00000000..cba6958f --- /dev/null +++ b/src/apps/qcam/assets/feathericons/more-vertical.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/mouse-pointer.svg b/src/apps/qcam/assets/feathericons/mouse-pointer.svg new file mode 100644 index 00000000..f5af5591 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/mouse-pointer.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/move.svg b/src/apps/qcam/assets/feathericons/move.svg new file mode 100644 index 00000000..4e251b56 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/move.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/music.svg b/src/apps/qcam/assets/feathericons/music.svg new file mode 100644 index 00000000..7bee2f7e --- /dev/null +++ b/src/apps/qcam/assets/feathericons/music.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/navigation-2.svg b/src/apps/qcam/assets/feathericons/navigation-2.svg new file mode 100644 index 00000000..ae31db96 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/navigation-2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/navigation.svg b/src/apps/qcam/assets/feathericons/navigation.svg new file mode 100644 index 00000000..f600a414 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/navigation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/octagon.svg b/src/apps/qcam/assets/feathericons/octagon.svg new file mode 100644 index 00000000..124c5483 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/octagon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/package.svg b/src/apps/qcam/assets/feathericons/package.svg new file mode 100644 index 00000000..f1e09eec --- /dev/null +++ b/src/apps/qcam/assets/feathericons/package.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/paperclip.svg b/src/apps/qcam/assets/feathericons/paperclip.svg new file mode 100644 index 00000000..b1f69b7a --- /dev/null +++ b/src/apps/qcam/assets/feathericons/paperclip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/pause-circle.svg b/src/apps/qcam/assets/feathericons/pause-circle.svg new file mode 100644 index 00000000..f6b1a8df --- /dev/null +++ b/src/apps/qcam/assets/feathericons/pause-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/pause.svg b/src/apps/qcam/assets/feathericons/pause.svg new file mode 100644 index 00000000..4e78038d --- /dev/null +++ b/src/apps/qcam/assets/feathericons/pause.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/pen-tool.svg b/src/apps/qcam/assets/feathericons/pen-tool.svg new file mode 100644 index 00000000..0d26fa1e --- /dev/null +++ b/src/apps/qcam/assets/feathericons/pen-tool.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/percent.svg b/src/apps/qcam/assets/feathericons/percent.svg new file mode 100644 index 00000000..2cb9719d --- /dev/null +++ b/src/apps/qcam/assets/feathericons/percent.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/phone-call.svg b/src/apps/qcam/assets/feathericons/phone-call.svg new file mode 100644 index 00000000..8b866602 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/phone-call.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/phone-forwarded.svg b/src/apps/qcam/assets/feathericons/phone-forwarded.svg new file mode 100644 index 00000000..aa21befc --- /dev/null +++ b/src/apps/qcam/assets/feathericons/phone-forwarded.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/phone-incoming.svg b/src/apps/qcam/assets/feathericons/phone-incoming.svg new file mode 100644 index 00000000..b2d523a8 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/phone-incoming.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/phone-missed.svg b/src/apps/qcam/assets/feathericons/phone-missed.svg new file mode 100644 index 00000000..4950f09f --- /dev/null +++ b/src/apps/qcam/assets/feathericons/phone-missed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/phone-off.svg b/src/apps/qcam/assets/feathericons/phone-off.svg new file mode 100644 index 00000000..4d00fb3d --- /dev/null +++ b/src/apps/qcam/assets/feathericons/phone-off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/phone-outgoing.svg b/src/apps/qcam/assets/feathericons/phone-outgoing.svg new file mode 100644 index 00000000..fea27a37 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/phone-outgoing.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/phone.svg b/src/apps/qcam/assets/feathericons/phone.svg new file mode 100644 index 00000000..2a35154a --- /dev/null +++ b/src/apps/qcam/assets/feathericons/phone.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/pie-chart.svg b/src/apps/qcam/assets/feathericons/pie-chart.svg new file mode 100644 index 00000000..b5bbe67c --- /dev/null +++ b/src/apps/qcam/assets/feathericons/pie-chart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/play-circle.svg b/src/apps/qcam/assets/feathericons/play-circle.svg new file mode 100644 index 00000000..8766dc7b --- /dev/null +++ b/src/apps/qcam/assets/feathericons/play-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/play.svg b/src/apps/qcam/assets/feathericons/play.svg new file mode 100644 index 00000000..fd76e30d --- /dev/null +++ b/src/apps/qcam/assets/feathericons/play.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/plus-circle.svg b/src/apps/qcam/assets/feathericons/plus-circle.svg new file mode 100644 index 00000000..4291ff05 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/plus-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/plus-square.svg b/src/apps/qcam/assets/feathericons/plus-square.svg new file mode 100644 index 00000000..c380e24b --- /dev/null +++ b/src/apps/qcam/assets/feathericons/plus-square.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/plus.svg b/src/apps/qcam/assets/feathericons/plus.svg new file mode 100644 index 00000000..703c5b7b --- /dev/null +++ b/src/apps/qcam/assets/feathericons/plus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/pocket.svg b/src/apps/qcam/assets/feathericons/pocket.svg new file mode 100644 index 00000000..a3b25619 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/pocket.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/power.svg b/src/apps/qcam/assets/feathericons/power.svg new file mode 100644 index 00000000..598308fc --- /dev/null +++ b/src/apps/qcam/assets/feathericons/power.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/printer.svg b/src/apps/qcam/assets/feathericons/printer.svg new file mode 100644 index 00000000..8a9a7ace --- /dev/null +++ b/src/apps/qcam/assets/feathericons/printer.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/radio.svg b/src/apps/qcam/assets/feathericons/radio.svg new file mode 100644 index 00000000..5abfcd13 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/radio.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/refresh-ccw.svg b/src/apps/qcam/assets/feathericons/refresh-ccw.svg new file mode 100644 index 00000000..10cff0ec --- /dev/null +++ b/src/apps/qcam/assets/feathericons/refresh-ccw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/refresh-cw.svg b/src/apps/qcam/assets/feathericons/refresh-cw.svg new file mode 100644 index 00000000..06c358dd --- /dev/null +++ b/src/apps/qcam/assets/feathericons/refresh-cw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/repeat.svg b/src/apps/qcam/assets/feathericons/repeat.svg new file mode 100644 index 00000000..c7657b08 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/repeat.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/rewind.svg b/src/apps/qcam/assets/feathericons/rewind.svg new file mode 100644 index 00000000..7b0fa3d5 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/rewind.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/rotate-ccw.svg b/src/apps/qcam/assets/feathericons/rotate-ccw.svg new file mode 100644 index 00000000..ade5dc42 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/rotate-ccw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/rotate-cw.svg b/src/apps/qcam/assets/feathericons/rotate-cw.svg new file mode 100644 index 00000000..83dca351 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/rotate-cw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/rss.svg b/src/apps/qcam/assets/feathericons/rss.svg new file mode 100644 index 00000000..c9a13684 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/rss.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/save.svg b/src/apps/qcam/assets/feathericons/save.svg new file mode 100644 index 00000000..46c72990 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/save.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/scissors.svg b/src/apps/qcam/assets/feathericons/scissors.svg new file mode 100644 index 00000000..fd0647ff --- /dev/null +++ b/src/apps/qcam/assets/feathericons/scissors.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/search.svg b/src/apps/qcam/assets/feathericons/search.svg new file mode 100644 index 00000000..8710306d --- /dev/null +++ b/src/apps/qcam/assets/feathericons/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/send.svg b/src/apps/qcam/assets/feathericons/send.svg new file mode 100644 index 00000000..42ef2a24 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/send.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/server.svg b/src/apps/qcam/assets/feathericons/server.svg new file mode 100644 index 00000000..54ce094a --- /dev/null +++ b/src/apps/qcam/assets/feathericons/server.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/settings.svg b/src/apps/qcam/assets/feathericons/settings.svg new file mode 100644 index 00000000..19c27265 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/settings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/share-2.svg b/src/apps/qcam/assets/feathericons/share-2.svg new file mode 100644 index 00000000..09b1c7bc --- /dev/null +++ b/src/apps/qcam/assets/feathericons/share-2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/share.svg b/src/apps/qcam/assets/feathericons/share.svg new file mode 100644 index 00000000..df38c14d --- /dev/null +++ b/src/apps/qcam/assets/feathericons/share.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/shield-off.svg b/src/apps/qcam/assets/feathericons/shield-off.svg new file mode 100644 index 00000000..18692ddd --- /dev/null +++ b/src/apps/qcam/assets/feathericons/shield-off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/shield.svg b/src/apps/qcam/assets/feathericons/shield.svg new file mode 100644 index 00000000..c7c48413 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/shield.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/shopping-bag.svg b/src/apps/qcam/assets/feathericons/shopping-bag.svg new file mode 100644 index 00000000..eaa39e81 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/shopping-bag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/shopping-cart.svg b/src/apps/qcam/assets/feathericons/shopping-cart.svg new file mode 100644 index 00000000..17a40bf4 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/shopping-cart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/shuffle.svg b/src/apps/qcam/assets/feathericons/shuffle.svg new file mode 100644 index 00000000..8cfb5db5 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/shuffle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/sidebar.svg b/src/apps/qcam/assets/feathericons/sidebar.svg new file mode 100644 index 00000000..8ba817e6 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/sidebar.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/skip-back.svg b/src/apps/qcam/assets/feathericons/skip-back.svg new file mode 100644 index 00000000..88d024e2 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/skip-back.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/skip-forward.svg b/src/apps/qcam/assets/feathericons/skip-forward.svg new file mode 100644 index 00000000..f3fdac3a --- /dev/null +++ b/src/apps/qcam/assets/feathericons/skip-forward.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/slack.svg b/src/apps/qcam/assets/feathericons/slack.svg new file mode 100644 index 00000000..5d973466 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/slack.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/slash.svg b/src/apps/qcam/assets/feathericons/slash.svg new file mode 100644 index 00000000..f4131b85 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/slash.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/sliders.svg b/src/apps/qcam/assets/feathericons/sliders.svg new file mode 100644 index 00000000..19c93852 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/sliders.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/smartphone.svg b/src/apps/qcam/assets/feathericons/smartphone.svg new file mode 100644 index 00000000..0171a95a --- /dev/null +++ b/src/apps/qcam/assets/feathericons/smartphone.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/smile.svg b/src/apps/qcam/assets/feathericons/smile.svg new file mode 100644 index 00000000..24dc8a26 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/smile.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/speaker.svg b/src/apps/qcam/assets/feathericons/speaker.svg new file mode 100644 index 00000000..75d5ff9c --- /dev/null +++ b/src/apps/qcam/assets/feathericons/speaker.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/square.svg b/src/apps/qcam/assets/feathericons/square.svg new file mode 100644 index 00000000..6eabc77d --- /dev/null +++ b/src/apps/qcam/assets/feathericons/square.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/star.svg b/src/apps/qcam/assets/feathericons/star.svg new file mode 100644 index 00000000..bcdc31aa --- /dev/null +++ b/src/apps/qcam/assets/feathericons/star.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/stop-circle.svg b/src/apps/qcam/assets/feathericons/stop-circle.svg new file mode 100644 index 00000000..c10d9d47 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/stop-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/sun.svg b/src/apps/qcam/assets/feathericons/sun.svg new file mode 100644 index 00000000..7f51b94d --- /dev/null +++ b/src/apps/qcam/assets/feathericons/sun.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/sunrise.svg b/src/apps/qcam/assets/feathericons/sunrise.svg new file mode 100644 index 00000000..eff4b1e4 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/sunrise.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/sunset.svg b/src/apps/qcam/assets/feathericons/sunset.svg new file mode 100644 index 00000000..a5a22215 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/sunset.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/tablet.svg b/src/apps/qcam/assets/feathericons/tablet.svg new file mode 100644 index 00000000..9c80b40a --- /dev/null +++ b/src/apps/qcam/assets/feathericons/tablet.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/tag.svg b/src/apps/qcam/assets/feathericons/tag.svg new file mode 100644 index 00000000..7219b15f --- /dev/null +++ b/src/apps/qcam/assets/feathericons/tag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/target.svg b/src/apps/qcam/assets/feathericons/target.svg new file mode 100644 index 00000000..be84b17c --- /dev/null +++ b/src/apps/qcam/assets/feathericons/target.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/terminal.svg b/src/apps/qcam/assets/feathericons/terminal.svg new file mode 100644 index 00000000..af459c04 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/terminal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/thermometer.svg b/src/apps/qcam/assets/feathericons/thermometer.svg new file mode 100644 index 00000000..33142ccc --- /dev/null +++ b/src/apps/qcam/assets/feathericons/thermometer.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/thumbs-down.svg b/src/apps/qcam/assets/feathericons/thumbs-down.svg new file mode 100644 index 00000000..3e7bcd6d --- /dev/null +++ b/src/apps/qcam/assets/feathericons/thumbs-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/thumbs-up.svg b/src/apps/qcam/assets/feathericons/thumbs-up.svg new file mode 100644 index 00000000..226c44d8 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/thumbs-up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/toggle-left.svg b/src/apps/qcam/assets/feathericons/toggle-left.svg new file mode 100644 index 00000000..240be290 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/toggle-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/toggle-right.svg b/src/apps/qcam/assets/feathericons/toggle-right.svg new file mode 100644 index 00000000..fc6e81c1 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/toggle-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/tool.svg b/src/apps/qcam/assets/feathericons/tool.svg new file mode 100644 index 00000000..f3cbf3d9 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/tool.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/trash-2.svg b/src/apps/qcam/assets/feathericons/trash-2.svg new file mode 100644 index 00000000..f24d55bf --- /dev/null +++ b/src/apps/qcam/assets/feathericons/trash-2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/trash.svg b/src/apps/qcam/assets/feathericons/trash.svg new file mode 100644 index 00000000..55650bd4 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/trash.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/trello.svg b/src/apps/qcam/assets/feathericons/trello.svg new file mode 100644 index 00000000..b2f599b6 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/trello.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/trending-down.svg b/src/apps/qcam/assets/feathericons/trending-down.svg new file mode 100644 index 00000000..a9d4cfa5 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/trending-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/trending-up.svg b/src/apps/qcam/assets/feathericons/trending-up.svg new file mode 100644 index 00000000..52026a4d --- /dev/null +++ b/src/apps/qcam/assets/feathericons/trending-up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/triangle.svg b/src/apps/qcam/assets/feathericons/triangle.svg new file mode 100644 index 00000000..274b6528 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/triangle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/truck.svg b/src/apps/qcam/assets/feathericons/truck.svg new file mode 100644 index 00000000..33898373 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/truck.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/tv.svg b/src/apps/qcam/assets/feathericons/tv.svg new file mode 100644 index 00000000..955bbfff --- /dev/null +++ b/src/apps/qcam/assets/feathericons/tv.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/twitch.svg b/src/apps/qcam/assets/feathericons/twitch.svg new file mode 100644 index 00000000..17062495 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/twitch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/twitter.svg b/src/apps/qcam/assets/feathericons/twitter.svg new file mode 100644 index 00000000..f8886eca --- /dev/null +++ b/src/apps/qcam/assets/feathericons/twitter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/type.svg b/src/apps/qcam/assets/feathericons/type.svg new file mode 100644 index 00000000..c6b2de33 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/type.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/umbrella.svg b/src/apps/qcam/assets/feathericons/umbrella.svg new file mode 100644 index 00000000..dc77c0cb --- /dev/null +++ b/src/apps/qcam/assets/feathericons/umbrella.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/underline.svg b/src/apps/qcam/assets/feathericons/underline.svg new file mode 100644 index 00000000..044945d4 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/underline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/unlock.svg b/src/apps/qcam/assets/feathericons/unlock.svg new file mode 100644 index 00000000..01dc3597 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/unlock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/upload-cloud.svg b/src/apps/qcam/assets/feathericons/upload-cloud.svg new file mode 100644 index 00000000..a1db297c --- /dev/null +++ b/src/apps/qcam/assets/feathericons/upload-cloud.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/upload.svg b/src/apps/qcam/assets/feathericons/upload.svg new file mode 100644 index 00000000..91eaff75 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/upload.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/user-check.svg b/src/apps/qcam/assets/feathericons/user-check.svg new file mode 100644 index 00000000..42f91b29 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/user-check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/user-minus.svg b/src/apps/qcam/assets/feathericons/user-minus.svg new file mode 100644 index 00000000..44b75f5a --- /dev/null +++ b/src/apps/qcam/assets/feathericons/user-minus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/user-plus.svg b/src/apps/qcam/assets/feathericons/user-plus.svg new file mode 100644 index 00000000..21460f6e --- /dev/null +++ b/src/apps/qcam/assets/feathericons/user-plus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/user-x.svg b/src/apps/qcam/assets/feathericons/user-x.svg new file mode 100644 index 00000000..0c41a481 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/user-x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/user.svg b/src/apps/qcam/assets/feathericons/user.svg new file mode 100644 index 00000000..7bb5f291 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/users.svg b/src/apps/qcam/assets/feathericons/users.svg new file mode 100644 index 00000000..aacf6b08 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/users.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/video-off.svg b/src/apps/qcam/assets/feathericons/video-off.svg new file mode 100644 index 00000000..08ec6973 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/video-off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/video.svg b/src/apps/qcam/assets/feathericons/video.svg new file mode 100644 index 00000000..8ff156aa --- /dev/null +++ b/src/apps/qcam/assets/feathericons/video.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/voicemail.svg b/src/apps/qcam/assets/feathericons/voicemail.svg new file mode 100644 index 00000000..5d78a8e7 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/voicemail.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/volume-1.svg b/src/apps/qcam/assets/feathericons/volume-1.svg new file mode 100644 index 00000000..150e875f --- /dev/null +++ b/src/apps/qcam/assets/feathericons/volume-1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/volume-2.svg b/src/apps/qcam/assets/feathericons/volume-2.svg new file mode 100644 index 00000000..03d521c7 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/volume-2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/volume-x.svg b/src/apps/qcam/assets/feathericons/volume-x.svg new file mode 100644 index 00000000..be442406 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/volume-x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/volume.svg b/src/apps/qcam/assets/feathericons/volume.svg new file mode 100644 index 00000000..53bfe15e --- /dev/null +++ b/src/apps/qcam/assets/feathericons/volume.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/watch.svg b/src/apps/qcam/assets/feathericons/watch.svg new file mode 100644 index 00000000..a1099da3 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/watch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/wifi-off.svg b/src/apps/qcam/assets/feathericons/wifi-off.svg new file mode 100644 index 00000000..35eae43b --- /dev/null +++ b/src/apps/qcam/assets/feathericons/wifi-off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/wifi.svg b/src/apps/qcam/assets/feathericons/wifi.svg new file mode 100644 index 00000000..748c285e --- /dev/null +++ b/src/apps/qcam/assets/feathericons/wifi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/wind.svg b/src/apps/qcam/assets/feathericons/wind.svg new file mode 100644 index 00000000..82b36468 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/wind.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/x-circle.svg b/src/apps/qcam/assets/feathericons/x-circle.svg new file mode 100644 index 00000000..94aad5e5 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/x-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/x-octagon.svg b/src/apps/qcam/assets/feathericons/x-octagon.svg new file mode 100644 index 00000000..85431985 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/x-octagon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/x-square.svg b/src/apps/qcam/assets/feathericons/x-square.svg new file mode 100644 index 00000000..7677c387 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/x-square.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/x.svg b/src/apps/qcam/assets/feathericons/x.svg new file mode 100644 index 00000000..7d5875ca --- /dev/null +++ b/src/apps/qcam/assets/feathericons/x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/youtube.svg b/src/apps/qcam/assets/feathericons/youtube.svg new file mode 100644 index 00000000..c4824385 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/youtube.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/zap-off.svg b/src/apps/qcam/assets/feathericons/zap-off.svg new file mode 100644 index 00000000..c636f8bb --- /dev/null +++ b/src/apps/qcam/assets/feathericons/zap-off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/zap.svg b/src/apps/qcam/assets/feathericons/zap.svg new file mode 100644 index 00000000..8fdafa93 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/zap.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/zoom-in.svg b/src/apps/qcam/assets/feathericons/zoom-in.svg new file mode 100644 index 00000000..da4572d2 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/zoom-in.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/feathericons/zoom-out.svg b/src/apps/qcam/assets/feathericons/zoom-out.svg new file mode 100644 index 00000000..fd678d72 --- /dev/null +++ b/src/apps/qcam/assets/feathericons/zoom-out.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/apps/qcam/assets/shader/RGB.frag b/src/apps/qcam/assets/shader/RGB.frag new file mode 100644 index 00000000..4c374ac9 --- /dev/null +++ b/src/apps/qcam/assets/shader/RGB.frag @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Laurent Pinchart + * + * RGB.frag - Fragment shader code for RGB formats + */ + +#ifdef GL_ES +precision mediump float; +#endif + +varying vec2 textureOut; +uniform sampler2D tex_y; + +void main(void) +{ + vec3 rgb; + + rgb = texture2D(tex_y, textureOut).RGB_PATTERN; + + gl_FragColor = vec4(rgb, 1.0); +} diff --git a/src/apps/qcam/assets/shader/YUV_2_planes.frag b/src/apps/qcam/assets/shader/YUV_2_planes.frag new file mode 100644 index 00000000..1d5d1206 --- /dev/null +++ b/src/apps/qcam/assets/shader/YUV_2_planes.frag @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Linaro + * + * YUV_2_planes.frag - Fragment shader code for NV12, NV16 and NV24 formats + */ + +#ifdef GL_ES +precision mediump float; +#endif + +varying vec2 textureOut; +uniform sampler2D tex_y; +uniform sampler2D tex_u; + +const mat3 yuv2rgb_matrix = mat3( + YUV2RGB_MATRIX +); + +const vec3 yuv2rgb_offset = vec3( + YUV2RGB_Y_OFFSET / 255.0, 128.0 / 255.0, 128.0 / 255.0 +); + +void main(void) +{ + vec3 yuv; + + yuv.x = texture2D(tex_y, textureOut).r; +#if defined(YUV_PATTERN_UV) + yuv.y = texture2D(tex_u, textureOut).r; + yuv.z = texture2D(tex_u, textureOut).a; +#elif defined(YUV_PATTERN_VU) + yuv.y = texture2D(tex_u, textureOut).a; + yuv.z = texture2D(tex_u, textureOut).r; +#else +#error Invalid pattern +#endif + + vec3 rgb = yuv2rgb_matrix * (yuv - yuv2rgb_offset); + + gl_FragColor = vec4(rgb, 1.0); +} diff --git a/src/apps/qcam/assets/shader/YUV_3_planes.frag b/src/apps/qcam/assets/shader/YUV_3_planes.frag new file mode 100644 index 00000000..8f788e90 --- /dev/null +++ b/src/apps/qcam/assets/shader/YUV_3_planes.frag @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Linaro + * + * YUV_3_planes_UV.frag - Fragment shader code for YUV420 format + */ + +#ifdef GL_ES +precision mediump float; +#endif + +varying vec2 textureOut; +uniform sampler2D tex_y; +uniform sampler2D tex_u; +uniform sampler2D tex_v; + +const mat3 yuv2rgb_matrix = mat3( + YUV2RGB_MATRIX +); + +const vec3 yuv2rgb_offset = vec3( + YUV2RGB_Y_OFFSET / 255.0, 128.0 / 255.0, 128.0 / 255.0 +); + +void main(void) +{ + vec3 yuv; + + yuv.x = texture2D(tex_y, textureOut).r; + yuv.y = texture2D(tex_u, textureOut).r; + yuv.z = texture2D(tex_v, textureOut).r; + + vec3 rgb = yuv2rgb_matrix * (yuv - yuv2rgb_offset); + + gl_FragColor = vec4(rgb, 1.0); +} diff --git a/src/apps/qcam/assets/shader/YUV_packed.frag b/src/apps/qcam/assets/shader/YUV_packed.frag new file mode 100644 index 00000000..b9ef9d41 --- /dev/null +++ b/src/apps/qcam/assets/shader/YUV_packed.frag @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Laurent Pinchart + * + * YUV_packed.frag - Fragment shader code for YUYV packed formats + */ + +#ifdef GL_ES +precision mediump float; +#endif + +varying vec2 textureOut; + +uniform sampler2D tex_y; +uniform vec2 tex_step; + +const mat3 yuv2rgb_matrix = mat3( + YUV2RGB_MATRIX +); + +const vec3 yuv2rgb_offset = vec3( + YUV2RGB_Y_OFFSET / 255.0, 128.0 / 255.0, 128.0 / 255.0 +); + +void main(void) +{ + /* + * The sampler won't interpolate the texture correctly along the X axis, + * as each RGBA pixel effectively stores two pixels. We thus need to + * interpolate manually. + * + * In integer texture coordinates, the Y values are layed out in the + * texture memory as follows: + * + * ...| Y U Y V | Y U Y V | Y U Y V |... + * ...| R G B A | R G B A | R G B A |... + * ^ ^ ^ ^ ^ ^ + * | | | | | | + * n-1 n-0.5 n n+0.5 n+1 n+1.5 + * + * For a texture location x in the interval [n, n+1[, sample the left + * and right pixels at n and n+1, and interpolate them with + * + * left.r * (1 - a) + left.b * a if fract(x) < 0.5 + * left.b * (1 - a) + right.r * a if fract(x) >= 0.5 + * + * with a = fract(x * 2) which can also be written + * + * a = fract(x) * 2 if fract(x) < 0.5 + * a = fract(x) * 2 - 1 if fract(x) >= 0.5 + */ + vec2 pos = textureOut; + float f_x = fract(pos.x / tex_step.x); + + vec4 left = texture2D(tex_y, vec2(pos.x - f_x * tex_step.x, pos.y)); + vec4 right = texture2D(tex_y, vec2(pos.x + (1.0 - f_x) * tex_step.x , pos.y)); + +#if defined(YUV_PATTERN_UYVY) + float y_left = mix(left.g, left.a, f_x * 2.0); + float y_right = mix(left.a, right.g, f_x * 2.0 - 1.0); + vec2 uv = mix(left.rb, right.rb, f_x); +#elif defined(YUV_PATTERN_VYUY) + float y_left = mix(left.g, left.a, f_x * 2.0); + float y_right = mix(left.a, right.g, f_x * 2.0 - 1.0); + vec2 uv = mix(left.br, right.br, f_x); +#elif defined(YUV_PATTERN_YUYV) + float y_left = mix(left.r, left.b, f_x * 2.0); + float y_right = mix(left.b, right.r, f_x * 2.0 - 1.0); + vec2 uv = mix(left.ga, right.ga, f_x); +#elif defined(YUV_PATTERN_YVYU) + float y_left = mix(left.r, left.b, f_x * 2.0); + float y_right = mix(left.b, right.r, f_x * 2.0 - 1.0); + vec2 uv = mix(left.ag, right.ag, f_x); +#else +#error Invalid pattern +#endif + + float y = mix(y_left, y_right, step(0.5, f_x)); + + vec3 rgb = yuv2rgb_matrix * (vec3(y, uv) - yuv2rgb_offset); + + gl_FragColor = vec4(rgb, 1.0); +} diff --git a/src/apps/qcam/assets/shader/bayer_1x_packed.frag b/src/apps/qcam/assets/shader/bayer_1x_packed.frag new file mode 100644 index 00000000..f53f5575 --- /dev/null +++ b/src/apps/qcam/assets/shader/bayer_1x_packed.frag @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Based on the code from http://jgt.akpeters.com/papers/McGuire08/ + * + * Efficient, High-Quality Bayer Demosaic Filtering on GPUs + * + * Morgan McGuire + * + * This paper appears in issue Volume 13, Number 4. + * --------------------------------------------------------- + * Copyright (c) 2008, Morgan McGuire. All rights reserved. + * + * + * Modified by Linaro Ltd for 10/12-bit packed vs 8-bit raw Bayer format, + * and for simpler demosaic algorithm. + * Copyright (C) 2020, Linaro + * + * bayer_1x_packed.frag - Fragment shader code for raw Bayer 10-bit and 12-bit + * packed formats + */ + +#ifdef GL_ES +precision mediump float; +#endif + +/* + * These constants are used to select the bytes containing the HS part of + * the pixel value: + * BPP - bytes per pixel, + * THRESHOLD_L = fract(BPP) * 0.5 + 0.02 + * THRESHOLD_H = 1.0 - fract(BPP) * 1.5 + 0.02 + * Let X is the x coordinate in the texture measured in bytes (so that the + * range is from 0 to (stride_-1)) aligned on the nearest pixel. + * E.g. for RAW10P: + * -------------+-------------------+-------------------+-- + * pixel No | 0 1 2 3 | 4 5 6 7 | ... + * -------------+-------------------+-------------------+-- + * byte offset | 0 1 2 3 4 | 5 6 7 8 9 | ... + * -------------+-------------------+-------------------+-- + * X | 0.0 1.25 2.5 3.75 | 5.0 6.25 7.5 8.75 | ... + * -------------+-------------------+-------------------+-- + * If fract(X) < THRESHOLD_L then the previous byte contains the LS + * bits of the pixel values and needs to be skipped. + * If fract(X) > THRESHOLD_H then the next byte contains the LS bits + * of the pixel values and needs to be skipped. + */ +#if defined(RAW10P) +#define BPP 1.25 +#define THRESHOLD_L 0.14 +#define THRESHOLD_H 0.64 +#elif defined(RAW12P) +#define BPP 1.5 +#define THRESHOLD_L 0.27 +#define THRESHOLD_H 0.27 +#else +#error Invalid raw format +#endif + + +varying vec2 textureOut; + +/* the texture size in pixels */ +uniform vec2 tex_size; +uniform vec2 tex_step; +uniform vec2 tex_bayer_first_red; + +uniform sampler2D tex_y; + +void main(void) +{ + vec3 rgb; + + /* + * center_bytes holds the coordinates of the MS byte of the pixel + * being sampled on the [0, stride-1/height-1] range. + * center_pixel holds the coordinates of the pixel being sampled + * on the [0, width/height-1] range. + */ + vec2 center_bytes; + vec2 center_pixel; + + /* + * x- and y-positions of the adjacent pixels on the [0, 1] range. + */ + vec2 xcoords; + vec2 ycoords; + + /* + * The coordinates passed to the shader in textureOut may point + * to a place in between the pixels if the texture format doesn't + * match the image format. In particular, MIPI packed raw Bayer + * formats don't have a matching texture format. + * In this case align the coordinates to the left nearest pixel + * by hand. + */ + center_pixel = floor(textureOut * tex_size); + center_bytes.y = center_pixel.y; + + /* + * Add a small number (a few mantissa's LSBs) to avoid float + * representation issues. Maybe paranoic. + */ + center_bytes.x = BPP * center_pixel.x + 0.02; + + float fract_x = fract(center_bytes.x); + + /* + * The below floor() call ensures that center_bytes.x points + * at one of the bytes representing the 8 higher bits of + * the pixel value, not at the byte containing the LS bits + * of the group of the pixels. + */ + center_bytes.x = floor(center_bytes.x); + center_bytes *= tex_step; + + xcoords = center_bytes.x + vec2(-tex_step.x, tex_step.x); + ycoords = center_bytes.y + vec2(-tex_step.y, tex_step.y); + + /* + * If xcoords[0] points at the byte containing the LS bits + * of the previous group of the pixels, move xcoords[0] one + * byte back. + */ + xcoords[0] += (fract_x < THRESHOLD_L) ? -tex_step.x : 0.0; + + /* + * If xcoords[1] points at the byte containing the LS bits + * of the current group of the pixels, move xcoords[1] one + * byte forward. + */ + xcoords[1] += (fract_x > THRESHOLD_H) ? tex_step.x : 0.0; + + vec2 alternate = mod(center_pixel.xy + tex_bayer_first_red, 2.0); + bool even_col = alternate.x < 1.0; + bool even_row = alternate.y < 1.0; + + /* + * We need to sample the central pixel and the ones with offset + * of -1 to +1 pixel in both X and Y directions. Let's name these + * pixels as below, where C is the central pixel: + * + * +----+----+----+----+ + * | \ x| | | | + * |y \ | -1 | 0 | +1 | + * +----+----+----+----+ + * | +1 | D2 | A1 | D3 | + * +----+----+----+----+ + * | 0 | B0 | C | B1 | + * +----+----+----+----+ + * | -1 | D0 | A0 | D1 | + * +----+----+----+----+ + * + * In the below equations (0,-1).r means "r component of the texel + * shifted by -tex_step.y from the center_bytes one" etc. + * + * In the even row / even column (EE) case the colour values are: + * R = C = (0,0).r, + * G = (A0 + A1 + B0 + B1) / 4.0 = + * ( (0,-1).r + (0,1).r + (-1,0).r + (1,0).r ) / 4.0, + * B = (D0 + D1 + D2 + D3) / 4.0 = + * ( (-1,-1).r + (1,-1).r + (-1,1).r + (1,1).r ) / 4.0 + * + * For even row / odd column (EO): + * R = (B0 + B1) / 2.0 = ( (-1,0).r + (1,0).r ) / 2.0, + * G = C = (0,0).r, + * B = (A0 + A1) / 2.0 = ( (0,-1).r + (0,1).r ) / 2.0 + * + * For odd row / even column (OE): + * R = (A0 + A1) / 2.0 = ( (0,-1).r + (0,1).r ) / 2.0, + * G = C = (0,0).r, + * B = (B0 + B1) / 2.0 = ( (-1,0).r + (1,0).r ) / 2.0 + * + * For odd row / odd column (OO): + * R = (D0 + D1 + D2 + D3) / 4.0 = + * ( (-1,-1).r + (1,-1).r + (-1,1).r + (1,1).r ) / 4.0, + * G = (A0 + A1 + B0 + B1) / 4.0 = + * ( (0,-1).r + (0,1).r + (-1,0).r + (1,0).r ) / 4.0, + * B = C = (0,0).r + */ + + /* + * Fetch the values and precalculate the terms: + * patterns.x = (A0 + A1) / 2.0 + * patterns.y = (B0 + B1) / 2.0 + * patterns.z = (A0 + A1 + B0 + B1) / 4.0 + * patterns.w = (D0 + D1 + D2 + D3) / 4.0 + */ + #define fetch(x, y) texture2D(tex_y, vec2(x, y)).r + + float C = texture2D(tex_y, center_bytes).r; + vec4 patterns = vec4( + fetch(center_bytes.x, ycoords[0]), /* A0: (0,-1) */ + fetch(xcoords[0], center_bytes.y), /* B0: (-1,0) */ + fetch(xcoords[0], ycoords[0]), /* D0: (-1,-1) */ + fetch(xcoords[1], ycoords[0])); /* D1: (1,-1) */ + vec4 temp = vec4( + fetch(center_bytes.x, ycoords[1]), /* A1: (0,1) */ + fetch(xcoords[1], center_bytes.y), /* B1: (1,0) */ + fetch(xcoords[1], ycoords[1]), /* D3: (1,1) */ + fetch(xcoords[0], ycoords[1])); /* D2: (-1,1) */ + patterns = (patterns + temp) * 0.5; + /* .x = (A0 + A1) / 2.0, .y = (B0 + B1) / 2.0 */ + /* .z = (D0 + D3) / 2.0, .w = (D1 + D2) / 2.0 */ + patterns.w = (patterns.z + patterns.w) * 0.5; + patterns.z = (patterns.x + patterns.y) * 0.5; + + rgb = even_col ? + (even_row ? + vec3(C, patterns.zw) : + vec3(patterns.x, C, patterns.y)) : + (even_row ? + vec3(patterns.y, C, patterns.x) : + vec3(patterns.wz, C)); + + gl_FragColor = vec4(rgb, 1.0); +} diff --git a/src/apps/qcam/assets/shader/bayer_8.frag b/src/apps/qcam/assets/shader/bayer_8.frag new file mode 100644 index 00000000..7e35ca88 --- /dev/null +++ b/src/apps/qcam/assets/shader/bayer_8.frag @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* +From http://jgt.akpeters.com/papers/McGuire08/ + +Efficient, High-Quality Bayer Demosaic Filtering on GPUs + +Morgan McGuire + +This paper appears in issue Volume 13, Number 4. +--------------------------------------------------------- +Copyright (c) 2008, Morgan McGuire. All rights reserved. + +Modified by Linaro Ltd to integrate it into libcamera. +Copyright (C) 2021, Linaro +*/ + +//Pixel Shader +#ifdef GL_ES +precision mediump float; +#endif + +/** Monochrome RGBA or GL_LUMINANCE Bayer encoded texture.*/ +uniform sampler2D tex_y; +varying vec4 center; +varying vec4 yCoord; +varying vec4 xCoord; + +void main(void) { + #define fetch(x, y) texture2D(tex_y, vec2(x, y)).r + + float C = texture2D(tex_y, center.xy).r; // ( 0, 0) + const vec4 kC = vec4( 4.0, 6.0, 5.0, 5.0) / 8.0; + + // Determine which of four types of pixels we are on. + vec2 alternate = mod(floor(center.zw), 2.0); + + vec4 Dvec = vec4( + fetch(xCoord[1], yCoord[1]), // (-1,-1) + fetch(xCoord[1], yCoord[2]), // (-1, 1) + fetch(xCoord[2], yCoord[1]), // ( 1,-1) + fetch(xCoord[2], yCoord[2])); // ( 1, 1) + + vec4 PATTERN = (kC.xyz * C).xyzz; + + // Can also be a dot product with (1,1,1,1) on hardware where that is + // specially optimized. + // Equivalent to: D = Dvec[0] + Dvec[1] + Dvec[2] + Dvec[3]; + Dvec.xy += Dvec.zw; + Dvec.x += Dvec.y; + + vec4 value = vec4( + fetch(center.x, yCoord[0]), // ( 0,-2) + fetch(center.x, yCoord[1]), // ( 0,-1) + fetch(xCoord[0], center.y), // (-2, 0) + fetch(xCoord[1], center.y)); // (-1, 0) + + vec4 temp = vec4( + fetch(center.x, yCoord[3]), // ( 0, 2) + fetch(center.x, yCoord[2]), // ( 0, 1) + fetch(xCoord[3], center.y), // ( 2, 0) + fetch(xCoord[2], center.y)); // ( 1, 0) + + // Even the simplest compilers should be able to constant-fold these to + // avoid the division. + // Note that on scalar processors these constants force computation of some + // identical products twice. + const vec4 kA = vec4(-1.0, -1.5, 0.5, -1.0) / 8.0; + const vec4 kB = vec4( 2.0, 0.0, 0.0, 4.0) / 8.0; + const vec4 kD = vec4( 0.0, 2.0, -1.0, -1.0) / 8.0; + + // Conserve constant registers and take advantage of free swizzle on load + #define kE (kA.xywz) + #define kF (kB.xywz) + + value += temp; + + // There are five filter patterns (identity, cross, checker, + // theta, phi). Precompute the terms from all of them and then + // use swizzles to assign to color channels. + // + // Channel Matches + // x cross (e.g., EE G) + // y checker (e.g., EE B) + // z theta (e.g., EO R) + // w phi (e.g., EO R) + #define A (value[0]) + #define B (value[1]) + #define D (Dvec.x) + #define E (value[2]) + #define F (value[3]) + + // Avoid zero elements. On a scalar processor this saves two MADDs + // and it has no effect on a vector processor. + PATTERN.yzw += (kD.yz * D).xyy; + + PATTERN += (kA.xyz * A).xyzx + (kE.xyw * E).xyxz; + PATTERN.xw += kB.xw * B; + PATTERN.xz += kF.xz * F; + + gl_FragColor.rgb = (alternate.y == 0.0) ? + ((alternate.x == 0.0) ? + vec3(C, PATTERN.xy) : + vec3(PATTERN.z, C, PATTERN.w)) : + ((alternate.x == 0.0) ? + vec3(PATTERN.w, C, PATTERN.z) : + vec3(PATTERN.yx, C)); +} diff --git a/src/apps/qcam/assets/shader/bayer_8.vert b/src/apps/qcam/assets/shader/bayer_8.vert new file mode 100644 index 00000000..3695a5e9 --- /dev/null +++ b/src/apps/qcam/assets/shader/bayer_8.vert @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* +From http://jgt.akpeters.com/papers/McGuire08/ + +Efficient, High-Quality Bayer Demosaic Filtering on GPUs + +Morgan McGuire + +This paper appears in issue Volume 13, Number 4. +--------------------------------------------------------- +Copyright (c) 2008, Morgan McGuire. All rights reserved. + +Modified by Linaro Ltd to integrate it into libcamera. +Copyright (C) 2021, Linaro +*/ + +//Vertex Shader + +attribute vec4 vertexIn; +attribute vec2 textureIn; + +uniform vec2 tex_size; /* The texture size in pixels */ +uniform vec2 tex_step; + +/** Pixel position of the first red pixel in the */ +/** Bayer pattern. [{0,1}, {0, 1}]*/ +uniform vec2 tex_bayer_first_red; + +/** .xy = Pixel being sampled in the fragment shader on the range [0, 1] + .zw = ...on the range [0, sourceSize], offset by firstRed */ +varying vec4 center; + +/** center.x + (-2/w, -1/w, 1/w, 2/w); These are the x-positions */ +/** of the adjacent pixels.*/ +varying vec4 xCoord; + +/** center.y + (-2/h, -1/h, 1/h, 2/h); These are the y-positions */ +/** of the adjacent pixels.*/ +varying vec4 yCoord; + +void main(void) { + center.xy = textureIn; + center.zw = textureIn * tex_size + tex_bayer_first_red; + + xCoord = center.x + vec4(-2.0 * tex_step.x, + -tex_step.x, tex_step.x, 2.0 * tex_step.x); + yCoord = center.y + vec4(-2.0 * tex_step.y, + -tex_step.y, tex_step.y, 2.0 * tex_step.y); + + gl_Position = vertexIn; +} diff --git a/src/apps/qcam/assets/shader/identity.vert b/src/apps/qcam/assets/shader/identity.vert new file mode 100644 index 00000000..12c41377 --- /dev/null +++ b/src/apps/qcam/assets/shader/identity.vert @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Linaro + * + * identity.vert - Identity vertex shader for pixel format conversion + */ + +attribute vec4 vertexIn; +attribute vec2 textureIn; +varying vec2 textureOut; + +uniform float stride_factor; + +void main(void) +{ + gl_Position = vertexIn; + textureOut = vec2(textureIn.x * stride_factor, textureIn.y); +} diff --git a/src/apps/qcam/assets/shader/shaders.qrc b/src/apps/qcam/assets/shader/shaders.qrc new file mode 100644 index 00000000..96c709f9 --- /dev/null +++ b/src/apps/qcam/assets/shader/shaders.qrc @@ -0,0 +1,13 @@ + + + + RGB.frag + YUV_2_planes.frag + YUV_3_planes.frag + YUV_packed.frag + bayer_1x_packed.frag + bayer_8.frag + bayer_8.vert + identity.vert + + diff --git a/src/apps/qcam/cam_select_dialog.cpp b/src/apps/qcam/cam_select_dialog.cpp new file mode 100644 index 00000000..3c8b12a9 --- /dev/null +++ b/src/apps/qcam/cam_select_dialog.cpp @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2022, Utkarsh Tiwari + * + * cam_select_dialog.cpp - qcam - Camera Selection dialog + */ + +#include "cam_select_dialog.h" + +#include + +#include +#include + +#include +#include +#include +#include +#include + +CameraSelectorDialog::CameraSelectorDialog(libcamera::CameraManager *cameraManager, + QWidget *parent) + : QDialog(parent), cm_(cameraManager) +{ + /* Use a QFormLayout for the dialog. */ + QFormLayout *layout = new QFormLayout(this); + + /* Setup the camera id combo-box. */ + cameraIdComboBox_ = new QComboBox; + for (const auto &cam : cm_->cameras()) + cameraIdComboBox_->addItem(QString::fromStdString(cam->id())); + + /* Set camera information labels. */ + cameraLocation_ = new QLabel; + cameraModel_ = new QLabel; + + updateCameraInfo(cameraIdComboBox_->currentText()); + connect(cameraIdComboBox_, &QComboBox::currentTextChanged, + this, &CameraSelectorDialog::updateCameraInfo); + + /* Setup the QDialogButton Box */ + QDialogButtonBox *buttonBox = + new QDialogButtonBox(QDialogButtonBox::Ok | + QDialogButtonBox::Cancel); + + connect(buttonBox, &QDialogButtonBox::accepted, + this, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, + this, &QDialog::reject); + + /* Set the layout. */ + layout->addRow("Camera:", cameraIdComboBox_); + layout->addRow("Location:", cameraLocation_); + layout->addRow("Model:", cameraModel_); + layout->addWidget(buttonBox); +} + +CameraSelectorDialog::~CameraSelectorDialog() = default; + +std::string CameraSelectorDialog::getCameraId() +{ + return cameraIdComboBox_->currentText().toStdString(); +} + +/* Hotplug / Unplug Support. */ +void CameraSelectorDialog::addCamera(QString cameraId) +{ + cameraIdComboBox_->addItem(cameraId); +} + +void CameraSelectorDialog::removeCamera(QString cameraId) +{ + int cameraIndex = cameraIdComboBox_->findText(cameraId); + cameraIdComboBox_->removeItem(cameraIndex); +} + +/* Camera Information */ +void CameraSelectorDialog::updateCameraInfo(QString cameraId) +{ + const std::shared_ptr &camera = + cm_->get(cameraId.toStdString()); + + if (!camera) + return; + + const libcamera::ControlList &properties = camera->properties(); + + const auto &location = properties.get(libcamera::properties::Location); + if (location) { + switch (*location) { + case libcamera::properties::CameraLocationFront: + cameraLocation_->setText("Internal front camera"); + break; + case libcamera::properties::CameraLocationBack: + cameraLocation_->setText("Internal back camera"); + break; + case libcamera::properties::CameraLocationExternal: + cameraLocation_->setText("External camera"); + break; + default: + cameraLocation_->setText("Unknown"); + } + } else { + cameraLocation_->setText("Unknown"); + } + + const auto &model = properties.get(libcamera::properties::Model) + .value_or("Unknown"); + + cameraModel_->setText(QString::fromStdString(model)); +} diff --git a/src/apps/qcam/cam_select_dialog.h b/src/apps/qcam/cam_select_dialog.h new file mode 100644 index 00000000..0b7709ed --- /dev/null +++ b/src/apps/qcam/cam_select_dialog.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2022, Utkarsh Tiwari + * + * cam_select_dialog.h - qcam - Camera Selection dialog + */ + +#pragma once + +#include + +#include +#include +#include +#include + +#include +#include + +class QComboBox; +class QLabel; + +class CameraSelectorDialog : public QDialog +{ + Q_OBJECT +public: + CameraSelectorDialog(libcamera::CameraManager *cameraManager, + QWidget *parent); + ~CameraSelectorDialog(); + + std::string getCameraId(); + + /* Hotplug / Unplug Support. */ + void addCamera(QString cameraId); + void removeCamera(QString cameraId); + + /* Camera Information */ + void updateCameraInfo(QString cameraId); + +private: + libcamera::CameraManager *cm_; + + /* UI elements. */ + QComboBox *cameraIdComboBox_; + QLabel *cameraLocation_; + QLabel *cameraModel_; +}; diff --git a/src/apps/qcam/format_converter.cpp b/src/apps/qcam/format_converter.cpp new file mode 100644 index 00000000..9331da0c --- /dev/null +++ b/src/apps/qcam/format_converter.cpp @@ -0,0 +1,359 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * format_convert.cpp - qcam - Convert buffer to RGB + */ + +#include "format_converter.h" + +#include +#include + +#include + +#include + +#include "../cam/image.h" + +#define RGBSHIFT 8 +#ifndef MAX +#define MAX(a,b) ((a)>(b)?(a):(b)) +#endif +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif +#ifndef CLAMP +#define CLAMP(a,low,high) MAX((low),MIN((high),(a))) +#endif +#ifndef CLIP +#define CLIP(x) CLAMP(x,0,255) +#endif + +int FormatConverter::configure(const libcamera::PixelFormat &format, + const QSize &size, unsigned int stride) +{ + switch (format) { + case libcamera::formats::NV12: + formatFamily_ = YUVSemiPlanar; + horzSubSample_ = 2; + vertSubSample_ = 2; + nvSwap_ = false; + break; + case libcamera::formats::NV21: + formatFamily_ = YUVSemiPlanar; + horzSubSample_ = 2; + vertSubSample_ = 2; + nvSwap_ = true; + break; + case libcamera::formats::NV16: + formatFamily_ = YUVSemiPlanar; + horzSubSample_ = 2; + vertSubSample_ = 1; + nvSwap_ = false; + break; + case libcamera::formats::NV61: + formatFamily_ = YUVSemiPlanar; + horzSubSample_ = 2; + vertSubSample_ = 1; + nvSwap_ = true; + break; + case libcamera::formats::NV24: + formatFamily_ = YUVSemiPlanar; + horzSubSample_ = 1; + vertSubSample_ = 1; + nvSwap_ = false; + break; + case libcamera::formats::NV42: + formatFamily_ = YUVSemiPlanar; + horzSubSample_ = 1; + vertSubSample_ = 1; + nvSwap_ = true; + break; + + case libcamera::formats::R8: + formatFamily_ = RGB; + r_pos_ = 0; + g_pos_ = 0; + b_pos_ = 0; + bpp_ = 1; + break; + case libcamera::formats::RGB888: + formatFamily_ = RGB; + r_pos_ = 2; + g_pos_ = 1; + b_pos_ = 0; + bpp_ = 3; + break; + case libcamera::formats::BGR888: + formatFamily_ = RGB; + r_pos_ = 0; + g_pos_ = 1; + b_pos_ = 2; + bpp_ = 3; + break; + case libcamera::formats::ARGB8888: + case libcamera::formats::XRGB8888: + formatFamily_ = RGB; + r_pos_ = 2; + g_pos_ = 1; + b_pos_ = 0; + bpp_ = 4; + break; + case libcamera::formats::RGBA8888: + case libcamera::formats::RGBX8888: + formatFamily_ = RGB; + r_pos_ = 3; + g_pos_ = 2; + b_pos_ = 1; + bpp_ = 4; + break; + case libcamera::formats::ABGR8888: + case libcamera::formats::XBGR8888: + formatFamily_ = RGB; + r_pos_ = 0; + g_pos_ = 1; + b_pos_ = 2; + bpp_ = 4; + break; + case libcamera::formats::BGRA8888: + case libcamera::formats::BGRX8888: + formatFamily_ = RGB; + r_pos_ = 1; + g_pos_ = 2; + b_pos_ = 3; + bpp_ = 4; + break; + + case libcamera::formats::VYUY: + formatFamily_ = YUVPacked; + y_pos_ = 1; + cb_pos_ = 2; + break; + case libcamera::formats::YVYU: + formatFamily_ = YUVPacked; + y_pos_ = 0; + cb_pos_ = 3; + break; + case libcamera::formats::UYVY: + formatFamily_ = YUVPacked; + y_pos_ = 1; + cb_pos_ = 0; + break; + case libcamera::formats::YUYV: + formatFamily_ = YUVPacked; + y_pos_ = 0; + cb_pos_ = 1; + break; + + case libcamera::formats::YUV420: + formatFamily_ = YUVPlanar; + horzSubSample_ = 2; + vertSubSample_ = 2; + nvSwap_ = false; + break; + case libcamera::formats::YVU420: + formatFamily_ = YUVPlanar; + horzSubSample_ = 2; + vertSubSample_ = 2; + nvSwap_ = true; + break; + case libcamera::formats::YUV422: + formatFamily_ = YUVPlanar; + horzSubSample_ = 2; + vertSubSample_ = 1; + nvSwap_ = false; + break; + + case libcamera::formats::MJPEG: + formatFamily_ = MJPEG; + break; + + default: + return -EINVAL; + }; + + format_ = format; + width_ = size.width(); + height_ = size.height(); + stride_ = stride; + + return 0; +} + +void FormatConverter::convert(const Image *src, size_t size, QImage *dst) +{ + switch (formatFamily_) { + case MJPEG: + dst->loadFromData(src->data(0).data(), size, "JPEG"); + break; + case RGB: + convertRGB(src, dst->bits()); + break; + case YUVPacked: + convertYUVPacked(src, dst->bits()); + break; + case YUVSemiPlanar: + convertYUVSemiPlanar(src, dst->bits()); + break; + case YUVPlanar: + convertYUVPlanar(src, dst->bits()); + break; + }; +} + +static void yuv_to_rgb(int y, int u, int v, int *r, int *g, int *b) +{ + int c = y - 16; + int d = u - 128; + int e = v - 128; + *r = CLIP(( 298 * c + 409 * e + 128) >> RGBSHIFT); + *g = CLIP(( 298 * c - 100 * d - 208 * e + 128) >> RGBSHIFT); + *b = CLIP(( 298 * c + 516 * d + 128) >> RGBSHIFT); +} + +void FormatConverter::convertRGB(const Image *srcImage, unsigned char *dst) +{ + const unsigned char *src = srcImage->data(0).data(); + unsigned int x, y; + int r, g, b; + + for (y = 0; y < height_; y++) { + for (x = 0; x < width_; x++) { + r = src[bpp_ * x + r_pos_]; + g = src[bpp_ * x + g_pos_]; + b = src[bpp_ * x + b_pos_]; + + dst[4 * x + 0] = b; + dst[4 * x + 1] = g; + dst[4 * x + 2] = r; + dst[4 * x + 3] = 0xff; + } + + src += stride_; + dst += width_ * 4; + } +} + +void FormatConverter::convertYUVPacked(const Image *srcImage, unsigned char *dst) +{ + const unsigned char *src = srcImage->data(0).data(); + unsigned int src_x, src_y, dst_x, dst_y; + unsigned int src_stride; + unsigned int dst_stride; + unsigned int cr_pos; + int r, g, b, y, cr, cb; + + cr_pos = (cb_pos_ + 2) % 4; + src_stride = stride_; + dst_stride = width_ * 4; + + for (src_y = 0, dst_y = 0; dst_y < height_; src_y++, dst_y++) { + for (src_x = 0, dst_x = 0; dst_x < width_; ) { + cb = src[src_y * src_stride + src_x * 4 + cb_pos_]; + cr = src[src_y * src_stride + src_x * 4 + cr_pos]; + + y = src[src_y * src_stride + src_x * 4 + y_pos_]; + yuv_to_rgb(y, cb, cr, &r, &g, &b); + dst[dst_y * dst_stride + 4 * dst_x + 0] = b; + dst[dst_y * dst_stride + 4 * dst_x + 1] = g; + dst[dst_y * dst_stride + 4 * dst_x + 2] = r; + dst[dst_y * dst_stride + 4 * dst_x + 3] = 0xff; + dst_x++; + + y = src[src_y * src_stride + src_x * 4 + y_pos_ + 2]; + yuv_to_rgb(y, cb, cr, &r, &g, &b); + dst[dst_y * dst_stride + 4 * dst_x + 0] = b; + dst[dst_y * dst_stride + 4 * dst_x + 1] = g; + dst[dst_y * dst_stride + 4 * dst_x + 2] = r; + dst[dst_y * dst_stride + 4 * dst_x + 3] = 0xff; + dst_x++; + + src_x++; + } + } +} + +void FormatConverter::convertYUVPlanar(const Image *srcImage, unsigned char *dst) +{ + unsigned int c_stride = stride_ / horzSubSample_; + unsigned int c_inc = horzSubSample_ == 1 ? 1 : 0; + const unsigned char *src_y = srcImage->data(0).data(); + const unsigned char *src_cb = srcImage->data(1).data(); + const unsigned char *src_cr = srcImage->data(2).data(); + int r, g, b; + + if (nvSwap_) + std::swap(src_cb, src_cr); + + for (unsigned int y = 0; y < height_; y++) { + const unsigned char *line_y = src_y + y * stride_; + const unsigned char *line_cb = src_cb + (y / vertSubSample_) * + c_stride; + const unsigned char *line_cr = src_cr + (y / vertSubSample_) * + c_stride; + + for (unsigned int x = 0; x < width_; x += 2) { + yuv_to_rgb(*line_y, *line_cb, *line_cr, &r, &g, &b); + dst[0] = b; + dst[1] = g; + dst[2] = r; + dst[3] = 0xff; + line_y++; + line_cb += c_inc; + line_cr += c_inc; + dst += 4; + + yuv_to_rgb(*line_y, *line_cb, *line_cr, &r, &g, &b); + dst[0] = b; + dst[1] = g; + dst[2] = r; + dst[3] = 0xff; + line_y++; + line_cb += 1; + line_cr += 1; + dst += 4; + } + } +} + +void FormatConverter::convertYUVSemiPlanar(const Image *srcImage, unsigned char *dst) +{ + unsigned int c_stride = stride_ * (2 / horzSubSample_); + unsigned int c_inc = horzSubSample_ == 1 ? 2 : 0; + unsigned int cb_pos = nvSwap_ ? 1 : 0; + unsigned int cr_pos = nvSwap_ ? 0 : 1; + const unsigned char *src = srcImage->data(0).data(); + const unsigned char *src_c = srcImage->data(1).data(); + int r, g, b; + + for (unsigned int y = 0; y < height_; y++) { + const unsigned char *src_y = src + y * stride_; + const unsigned char *src_cb = src_c + (y / vertSubSample_) * + c_stride + cb_pos; + const unsigned char *src_cr = src_c + (y / vertSubSample_) * + c_stride + cr_pos; + + for (unsigned int x = 0; x < width_; x += 2) { + yuv_to_rgb(*src_y, *src_cb, *src_cr, &r, &g, &b); + dst[0] = b; + dst[1] = g; + dst[2] = r; + dst[3] = 0xff; + src_y++; + src_cb += c_inc; + src_cr += c_inc; + dst += 4; + + yuv_to_rgb(*src_y, *src_cb, *src_cr, &r, &g, &b); + dst[0] = b; + dst[1] = g; + dst[2] = r; + dst[3] = 0xff; + src_y++; + src_cb += 2; + src_cr += 2; + dst += 4; + } + } +} diff --git a/src/apps/qcam/format_converter.h b/src/apps/qcam/format_converter.h new file mode 100644 index 00000000..37dbfae2 --- /dev/null +++ b/src/apps/qcam/format_converter.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * format_convert.h - qcam - Convert buffer to RGB + */ + +#pragma once + +#include + +#include + +#include + +class Image; +class QImage; + +class FormatConverter +{ +public: + int configure(const libcamera::PixelFormat &format, const QSize &size, + unsigned int stride); + + void convert(const Image *src, size_t size, QImage *dst); + +private: + enum FormatFamily { + MJPEG, + RGB, + YUVPacked, + YUVPlanar, + YUVSemiPlanar, + }; + + void convertRGB(const Image *src, unsigned char *dst); + void convertYUVPacked(const Image *src, unsigned char *dst); + void convertYUVPlanar(const Image *src, unsigned char *dst); + void convertYUVSemiPlanar(const Image *src, unsigned char *dst); + + libcamera::PixelFormat format_; + unsigned int width_; + unsigned int height_; + unsigned int stride_; + + enum FormatFamily formatFamily_; + + /* NV parameters */ + unsigned int horzSubSample_; + unsigned int vertSubSample_; + bool nvSwap_; + + /* RGB parameters */ + unsigned int bpp_; + unsigned int r_pos_; + unsigned int g_pos_; + unsigned int b_pos_; + + /* YUV parameters */ + unsigned int y_pos_; + unsigned int cb_pos_; +}; diff --git a/src/apps/qcam/main.cpp b/src/apps/qcam/main.cpp new file mode 100644 index 00000000..d3f01a85 --- /dev/null +++ b/src/apps/qcam/main.cpp @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * main.cpp - qcam - The libcamera GUI test application + */ + +#include +#include + +#include +#include + +#include + +#include "../cam/options.h" +#include "../cam/stream_options.h" +#include "main_window.h" +#include "message_handler.h" + +using namespace libcamera; + +void signalHandler([[maybe_unused]] int signal) +{ + qInfo() << "Exiting"; + qApp->quit(); +} + +OptionsParser::Options parseOptions(int argc, char *argv[]) +{ + StreamKeyValueParser streamKeyValue; + + OptionsParser parser; + parser.addOption(OptCamera, OptionString, + "Specify which camera to operate on", "camera", + ArgumentRequired, "camera"); + parser.addOption(OptHelp, OptionNone, "Display this help message", + "help"); + parser.addOption(OptRenderer, OptionString, + "Choose the renderer type {qt,gles} (default: qt)", + "renderer", ArgumentRequired, "renderer"); + parser.addOption(OptStream, &streamKeyValue, + "Set configuration of a camera stream", "stream", true); + parser.addOption(OptVerbose, OptionNone, + "Print verbose log messages", "verbose"); + + OptionsParser::Options options = parser.parse(argc, argv); + if (options.isSet(OptHelp)) + parser.usage(); + + return options; +} + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + int ret; + + OptionsParser::Options options = parseOptions(argc, argv); + if (!options.valid()) + return EXIT_FAILURE; + if (options.isSet(OptHelp)) + return 0; + + MessageHandler msgHandler(options.isSet(OptVerbose)); + + struct sigaction sa = {}; + sa.sa_handler = &signalHandler; + sigaction(SIGINT, &sa, nullptr); + + CameraManager *cm = new libcamera::CameraManager(); + + ret = cm->start(); + if (ret) { + qInfo() << "Failed to start camera manager:" + << strerror(-ret); + return EXIT_FAILURE; + } + + MainWindow *mainWindow = new MainWindow(cm, options); + mainWindow->show(); + ret = app.exec(); + delete mainWindow; + + cm->stop(); + delete cm; + + return ret; +} diff --git a/src/apps/qcam/main_window.cpp b/src/apps/qcam/main_window.cpp new file mode 100644 index 00000000..f553ccb0 --- /dev/null +++ b/src/apps/qcam/main_window.cpp @@ -0,0 +1,790 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * main_window.cpp - qcam - Main application window + */ + +#include "main_window.h" + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../cam/dng_writer.h" +#include "../cam/image.h" + +#include "cam_select_dialog.h" +#ifndef QT_NO_OPENGL +#include "viewfinder_gl.h" +#endif +#include "viewfinder_qt.h" + +using namespace libcamera; + +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) +/* + * Qt::fixed was introduced in v5.14, and ::fixed deprecated in v5.15. Allow + * usage of Qt::fixed unconditionally. + */ +namespace Qt { +constexpr auto fixed = ::fixed; +} /* namespace Qt */ +#endif + +/** + * \brief Custom QEvent to signal capture completion + */ +class CaptureEvent : public QEvent +{ +public: + CaptureEvent() + : QEvent(type()) + { + } + + static Type type() + { + static int type = QEvent::registerEventType(); + return static_cast(type); + } +}; + +/** + * \brief Custom QEvent to signal hotplug or unplug + */ +class HotplugEvent : public QEvent +{ +public: + enum PlugEvent { + HotPlug, + HotUnplug + }; + + HotplugEvent(std::shared_ptr camera, PlugEvent event) + : QEvent(type()), camera_(std::move(camera)), plugEvent_(event) + { + } + + static Type type() + { + static int type = QEvent::registerEventType(); + return static_cast(type); + } + + PlugEvent hotplugEvent() const { return plugEvent_; } + Camera *camera() const { return camera_.get(); } + +private: + std::shared_ptr camera_; + PlugEvent plugEvent_; +}; + +MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options) + : saveRaw_(nullptr), options_(options), cm_(cm), allocator_(nullptr), + isCapturing_(false), captureRaw_(false) +{ + int ret; + + /* + * Initialize the UI: Create the toolbar, set the window title and + * create the viewfinder widget. + */ + createToolbars(); + + title_ = "QCam " + QString::fromStdString(CameraManager::version()); + setWindowTitle(title_); + connect(&titleTimer_, SIGNAL(timeout()), this, SLOT(updateTitle())); + + /* Renderer type Qt or GLES, select Qt by default. */ + std::string renderType = "qt"; + if (options_.isSet(OptRenderer)) + renderType = options_[OptRenderer].toString(); + + if (renderType == "qt") { + ViewFinderQt *viewfinder = new ViewFinderQt(this); + connect(viewfinder, &ViewFinderQt::renderComplete, + this, &MainWindow::renderComplete); + viewfinder_ = viewfinder; + setCentralWidget(viewfinder); +#ifndef QT_NO_OPENGL + } else if (renderType == "gles") { + ViewFinderGL *viewfinder = new ViewFinderGL(this); + connect(viewfinder, &ViewFinderGL::renderComplete, + this, &MainWindow::renderComplete); + viewfinder_ = viewfinder; + setCentralWidget(viewfinder); +#endif + } else { + qWarning() << "Invalid render type" + << QString::fromStdString(renderType); + quit(); + return; + } + + adjustSize(); + + /* Hotplug/unplug support */ + cm_->cameraAdded.connect(this, &MainWindow::addCamera); + cm_->cameraRemoved.connect(this, &MainWindow::removeCamera); + + cameraSelectorDialog_ = new CameraSelectorDialog(cm_, this); + + /* Open the camera and start capture. */ + ret = openCamera(); + if (ret < 0) { + quit(); + return; + } + + startStopAction_->setChecked(true); +} + +MainWindow::~MainWindow() +{ + if (camera_) { + stopCapture(); + camera_->release(); + camera_.reset(); + } +} + +bool MainWindow::event(QEvent *e) +{ + if (e->type() == CaptureEvent::type()) { + processCapture(); + return true; + } else if (e->type() == HotplugEvent::type()) { + processHotplug(static_cast(e)); + return true; + } + + return QMainWindow::event(e); +} + +int MainWindow::createToolbars() +{ + QAction *action; + + toolbar_ = addToolBar("Main"); + + /* Disable right click context menu. */ + toolbar_->setContextMenuPolicy(Qt::PreventContextMenu); + + /* Quit action. */ + action = toolbar_->addAction(QIcon::fromTheme("application-exit", + QIcon(":x-circle.svg")), + "Quit"); + action->setShortcut(Qt::CTRL | Qt::Key_Q); + connect(action, &QAction::triggered, this, &MainWindow::quit); + + /* Camera selector. */ + cameraSelectButton_ = new QPushButton; + connect(cameraSelectButton_, &QPushButton::clicked, + this, &MainWindow::switchCamera); + + toolbar_->addWidget(cameraSelectButton_); + + toolbar_->addSeparator(); + + /* Start/Stop action. */ + iconPlay_ = QIcon::fromTheme("media-playback-start", + QIcon(":play-circle.svg")); + iconStop_ = QIcon::fromTheme("media-playback-stop", + QIcon(":stop-circle.svg")); + + action = toolbar_->addAction(iconPlay_, "Start Capture"); + action->setCheckable(true); + action->setShortcut(Qt::Key_Space); + connect(action, &QAction::toggled, this, &MainWindow::toggleCapture); + startStopAction_ = action; + + /* Save As... action. */ + action = toolbar_->addAction(QIcon::fromTheme("document-save-as", + QIcon(":save.svg")), + "Save As..."); + action->setShortcut(QKeySequence::SaveAs); + connect(action, &QAction::triggered, this, &MainWindow::saveImageAs); + +#ifdef HAVE_DNG + /* Save Raw action. */ + action = toolbar_->addAction(QIcon::fromTheme("camera-photo", + QIcon(":aperture.svg")), + "Save Raw"); + action->setEnabled(false); + connect(action, &QAction::triggered, this, &MainWindow::captureRaw); + saveRaw_ = action; +#endif + + return 0; +} + +void MainWindow::quit() +{ + QTimer::singleShot(0, QCoreApplication::instance(), + &QCoreApplication::quit); +} + +void MainWindow::updateTitle() +{ + /* Calculate the average frame rate over the last period. */ + unsigned int duration = frameRateInterval_.elapsed(); + unsigned int frames = framesCaptured_ - previousFrames_; + double fps = frames * 1000.0 / duration; + + /* Restart counters. */ + frameRateInterval_.start(); + previousFrames_ = framesCaptured_; + + setWindowTitle(title_ + " : " + QString::number(fps, 'f', 2) + " fps"); +} + +/* ----------------------------------------------------------------------------- + * Camera Selection + */ + +void MainWindow::switchCamera() +{ + /* Get and acquire the new camera. */ + std::string newCameraId = chooseCamera(); + + if (newCameraId.empty()) + return; + + if (camera_ && newCameraId == camera_->id()) + return; + + const std::shared_ptr &cam = cm_->get(newCameraId); + + if (cam->acquire()) { + qInfo() << "Failed to acquire camera" << cam->id().c_str(); + return; + } + + qInfo() << "Switching to camera" << cam->id().c_str(); + + /* + * Stop the capture session, release the current camera, replace it with + * the new camera and start a new capture session. + */ + startStopAction_->setChecked(false); + + if (camera_) + camera_->release(); + + camera_ = cam; + + startStopAction_->setChecked(true); + + /* Display the current cameraId in the toolbar .*/ + cameraSelectButton_->setText(QString::fromStdString(newCameraId)); +} + +std::string MainWindow::chooseCamera() +{ + if (cameraSelectorDialog_->exec() != QDialog::Accepted) + return std::string(); + + return cameraSelectorDialog_->getCameraId(); +} + +int MainWindow::openCamera() +{ + std::string cameraName; + + /* + * Use the camera specified on the command line, if any, or display the + * camera selection dialog box otherwise. + */ + if (options_.isSet(OptCamera)) + cameraName = static_cast(options_[OptCamera]); + else + cameraName = chooseCamera(); + + if (cameraName == "") + return -EINVAL; + + /* Get and acquire the camera. */ + camera_ = cm_->get(cameraName); + if (!camera_) { + qInfo() << "Camera" << cameraName.c_str() << "not found"; + return -ENODEV; + } + + if (camera_->acquire()) { + qInfo() << "Failed to acquire camera"; + camera_.reset(); + return -EBUSY; + } + + /* Set the camera switch button with the currently selected Camera id. */ + cameraSelectButton_->setText(QString::fromStdString(cameraName)); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Capture Start & Stop + */ + +void MainWindow::toggleCapture(bool start) +{ + if (start) { + startCapture(); + startStopAction_->setIcon(iconStop_); + startStopAction_->setText("Stop Capture"); + } else { + stopCapture(); + startStopAction_->setIcon(iconPlay_); + startStopAction_->setText("Start Capture"); + } +} + +/** + * \brief Start capture with the current camera + * + * This function shall not be called directly, use toggleCapture() instead. + */ +int MainWindow::startCapture() +{ + StreamRoles roles = StreamKeyValueParser::roles(options_[OptStream]); + int ret; + + /* Verify roles are supported. */ + switch (roles.size()) { + case 1: + if (roles[0] != StreamRole::Viewfinder) { + qWarning() << "Only viewfinder supported for single stream"; + return -EINVAL; + } + break; + case 2: + if (roles[0] != StreamRole::Viewfinder || + roles[1] != StreamRole::Raw) { + qWarning() << "Only viewfinder + raw supported for dual streams"; + return -EINVAL; + } + break; + default: + if (roles.size() != 1) { + qWarning() << "Unsupported stream configuration"; + return -EINVAL; + } + break; + } + + /* Configure the camera. */ + config_ = camera_->generateConfiguration(roles); + if (!config_) { + qWarning() << "Failed to generate configuration from roles"; + return -EINVAL; + } + + StreamConfiguration &vfConfig = config_->at(0); + + /* Use a format supported by the viewfinder if available. */ + std::vector formats = vfConfig.formats().pixelformats(); + for (const PixelFormat &format : viewfinder_->nativeFormats()) { + auto match = std::find_if(formats.begin(), formats.end(), + [&](const PixelFormat &f) { + return f == format; + }); + if (match != formats.end()) { + vfConfig.pixelFormat = format; + break; + } + } + + /* Allow user to override configuration. */ + if (StreamKeyValueParser::updateConfiguration(config_.get(), + options_[OptStream])) { + qWarning() << "Failed to update configuration"; + return -EINVAL; + } + + CameraConfiguration::Status validation = config_->validate(); + if (validation == CameraConfiguration::Invalid) { + qWarning() << "Failed to create valid camera configuration"; + return -EINVAL; + } + + if (validation == CameraConfiguration::Adjusted) + qInfo() << "Stream configuration adjusted to " + << vfConfig.toString().c_str(); + + ret = camera_->configure(config_.get()); + if (ret < 0) { + qInfo() << "Failed to configure camera"; + return ret; + } + + /* Store stream allocation. */ + vfStream_ = config_->at(0).stream(); + if (config_->size() == 2) + rawStream_ = config_->at(1).stream(); + else + rawStream_ = nullptr; + + /* + * Configure the viewfinder. If no color space is reported, default to + * sYCC. + */ + ret = viewfinder_->setFormat(vfConfig.pixelFormat, + QSize(vfConfig.size.width, vfConfig.size.height), + vfConfig.colorSpace.value_or(ColorSpace::Sycc), + vfConfig.stride); + if (ret < 0) { + qInfo() << "Failed to set viewfinder format"; + return ret; + } + + adjustSize(); + + /* Configure the raw capture button. */ + if (saveRaw_) + saveRaw_->setEnabled(config_->size() == 2); + + /* Allocate and map buffers. */ + allocator_ = new FrameBufferAllocator(camera_); + for (StreamConfiguration &config : *config_) { + Stream *stream = config.stream(); + + ret = allocator_->allocate(stream); + if (ret < 0) { + qWarning() << "Failed to allocate capture buffers"; + goto error; + } + + for (const std::unique_ptr &buffer : allocator_->buffers(stream)) { + /* Map memory buffers and cache the mappings. */ + std::unique_ptr image = + Image::fromFrameBuffer(buffer.get(), Image::MapMode::ReadOnly); + assert(image != nullptr); + mappedBuffers_[buffer.get()] = std::move(image); + + /* Store buffers on the free list. */ + freeBuffers_[stream].enqueue(buffer.get()); + } + } + + /* Create requests and fill them with buffers from the viewfinder. */ + while (!freeBuffers_[vfStream_].isEmpty()) { + FrameBuffer *buffer = freeBuffers_[vfStream_].dequeue(); + + std::unique_ptr request = camera_->createRequest(); + if (!request) { + qWarning() << "Can't create request"; + ret = -ENOMEM; + goto error; + } + + ret = request->addBuffer(vfStream_, buffer); + if (ret < 0) { + qWarning() << "Can't set buffer for request"; + goto error; + } + + requests_.push_back(std::move(request)); + } + + /* Start the title timer and the camera. */ + titleTimer_.start(2000); + frameRateInterval_.start(); + previousFrames_ = 0; + framesCaptured_ = 0; + lastBufferTime_ = 0; + + ret = camera_->start(); + if (ret) { + qInfo() << "Failed to start capture"; + goto error; + } + + camera_->requestCompleted.connect(this, &MainWindow::requestComplete); + + /* Queue all requests. */ + for (std::unique_ptr &request : requests_) { + ret = queueRequest(request.get()); + if (ret < 0) { + qWarning() << "Can't queue request"; + goto error_disconnect; + } + } + + isCapturing_ = true; + + return 0; + +error_disconnect: + camera_->requestCompleted.disconnect(this); + camera_->stop(); + +error: + requests_.clear(); + + mappedBuffers_.clear(); + + freeBuffers_.clear(); + + delete allocator_; + allocator_ = nullptr; + + return ret; +} + +/** + * \brief Stop ongoing capture + * + * This function may be called directly when tearing down the MainWindow. Use + * toggleCapture() instead in all other cases. + */ +void MainWindow::stopCapture() +{ + if (!isCapturing_) + return; + + viewfinder_->stop(); + if (saveRaw_) + saveRaw_->setEnabled(false); + captureRaw_ = false; + + int ret = camera_->stop(); + if (ret) + qInfo() << "Failed to stop capture"; + + camera_->requestCompleted.disconnect(this); + + mappedBuffers_.clear(); + + requests_.clear(); + freeQueue_.clear(); + + delete allocator_; + + isCapturing_ = false; + + config_.reset(); + + /* + * A CaptureEvent may have been posted before we stopped the camera, + * but not processed yet. Clear the queue of done buffers to avoid + * racing with the event handler. + */ + freeBuffers_.clear(); + doneQueue_.clear(); + + titleTimer_.stop(); + setWindowTitle(title_); +} + +/* ----------------------------------------------------------------------------- + * Camera hotplugging support + */ + +void MainWindow::processHotplug(HotplugEvent *e) +{ + Camera *camera = e->camera(); + QString cameraId = QString::fromStdString(camera->id()); + HotplugEvent::PlugEvent event = e->hotplugEvent(); + + if (event == HotplugEvent::HotPlug) { + cameraSelectorDialog_->addCamera(cameraId); + } else if (event == HotplugEvent::HotUnplug) { + /* Check if the currently-streaming camera is removed. */ + if (camera == camera_.get()) { + toggleCapture(false); + camera_->release(); + camera_.reset(); + } + + cameraSelectorDialog_->removeCamera(cameraId); + } +} + +void MainWindow::addCamera(std::shared_ptr camera) +{ + qInfo() << "Adding new camera:" << camera->id().c_str(); + QCoreApplication::postEvent(this, + new HotplugEvent(std::move(camera), + HotplugEvent::HotPlug)); +} + +void MainWindow::removeCamera(std::shared_ptr camera) +{ + qInfo() << "Removing camera:" << camera->id().c_str(); + QCoreApplication::postEvent(this, + new HotplugEvent(std::move(camera), + HotplugEvent::HotUnplug)); +} + +/* ----------------------------------------------------------------------------- + * Image Save + */ + +void MainWindow::saveImageAs() +{ + QImage image = viewfinder_->getCurrentImage(); + QString defaultPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); + + QString filename = QFileDialog::getSaveFileName(this, "Save Image", defaultPath, + "Image Files (*.png *.jpg *.jpeg)"); + if (filename.isEmpty()) + return; + + QImageWriter writer(filename); + writer.setQuality(95); + writer.write(image); +} + +void MainWindow::captureRaw() +{ + captureRaw_ = true; +} + +void MainWindow::processRaw(FrameBuffer *buffer, + [[maybe_unused]] const ControlList &metadata) +{ +#ifdef HAVE_DNG + QString defaultPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); + QString filename = QFileDialog::getSaveFileName(this, "Save DNG", defaultPath, + "DNG Files (*.dng)"); + + if (!filename.isEmpty()) { + uint8_t *memory = mappedBuffers_[buffer]->data(0).data(); + DNGWriter::write(filename.toStdString().c_str(), camera_.get(), + rawStream_->configuration(), metadata, buffer, + memory); + } +#endif + + { + QMutexLocker locker(&mutex_); + freeBuffers_[rawStream_].enqueue(buffer); + } +} + +/* ----------------------------------------------------------------------------- + * Request Completion Handling + */ + +void MainWindow::requestComplete(Request *request) +{ + if (request->status() == Request::RequestCancelled) + return; + + /* + * We're running in the libcamera thread context, expensive operations + * are not allowed. Add the buffer to the done queue and post a + * CaptureEvent for the application thread to handle. + */ + { + QMutexLocker locker(&mutex_); + doneQueue_.enqueue(request); + } + + QCoreApplication::postEvent(this, new CaptureEvent); +} + +void MainWindow::processCapture() +{ + /* + * Retrieve the next buffer from the done queue. The queue may be empty + * if stopCapture() has been called while a CaptureEvent was posted but + * not processed yet. Return immediately in that case. + */ + Request *request; + { + QMutexLocker locker(&mutex_); + if (doneQueue_.isEmpty()) + return; + + request = doneQueue_.dequeue(); + } + + /* Process buffers. */ + if (request->buffers().count(vfStream_)) + processViewfinder(request->buffers().at(vfStream_)); + + if (request->buffers().count(rawStream_)) + processRaw(request->buffers().at(rawStream_), request->metadata()); + + request->reuse(); + QMutexLocker locker(&mutex_); + freeQueue_.enqueue(request); +} + +void MainWindow::processViewfinder(FrameBuffer *buffer) +{ + framesCaptured_++; + + const FrameMetadata &metadata = buffer->metadata(); + + double fps = metadata.timestamp - lastBufferTime_; + fps = lastBufferTime_ && fps ? 1000000000.0 / fps : 0.0; + lastBufferTime_ = metadata.timestamp; + + QStringList bytesused; + for (const FrameMetadata::Plane &plane : metadata.planes()) + bytesused << QString::number(plane.bytesused); + + qDebug().noquote() + << QString("seq: %1").arg(metadata.sequence, 6, 10, QLatin1Char('0')) + << "bytesused: {" << bytesused.join(", ") + << "} timestamp:" << metadata.timestamp + << "fps:" << Qt::fixed << qSetRealNumberPrecision(2) << fps; + + /* Render the frame on the viewfinder. */ + viewfinder_->render(buffer, mappedBuffers_[buffer].get()); +} + +void MainWindow::renderComplete(FrameBuffer *buffer) +{ + Request *request; + { + QMutexLocker locker(&mutex_); + if (freeQueue_.isEmpty()) + return; + + request = freeQueue_.dequeue(); + } + + request->addBuffer(vfStream_, buffer); + + if (captureRaw_) { + FrameBuffer *rawBuffer = nullptr; + + { + QMutexLocker locker(&mutex_); + if (!freeBuffers_[rawStream_].isEmpty()) + rawBuffer = freeBuffers_[rawStream_].dequeue(); + } + + if (rawBuffer) { + request->addBuffer(rawStream_, rawBuffer); + captureRaw_ = false; + } else { + qWarning() << "No free buffer available for RAW capture"; + } + } + queueRequest(request); +} + +int MainWindow::queueRequest(Request *request) +{ + return camera_->queueRequest(request); +} diff --git a/src/apps/qcam/main_window.h b/src/apps/qcam/main_window.h new file mode 100644 index 00000000..95b64124 --- /dev/null +++ b/src/apps/qcam/main_window.h @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * main_window.h - qcam - Main application window + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../cam/stream_options.h" + +#include "viewfinder.h" + +class QAction; + +class CameraSelectorDialog; +class Image; +class HotplugEvent; + +enum { + OptCamera = 'c', + OptHelp = 'h', + OptRenderer = 'r', + OptStream = 's', + OptVerbose = 'v', +}; + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(libcamera::CameraManager *cm, + const OptionsParser::Options &options); + ~MainWindow(); + + bool event(QEvent *e) override; + +private Q_SLOTS: + void quit(); + void updateTitle(); + + void switchCamera(); + void toggleCapture(bool start); + + void saveImageAs(); + void captureRaw(); + void processRaw(libcamera::FrameBuffer *buffer, + const libcamera::ControlList &metadata); + + void renderComplete(libcamera::FrameBuffer *buffer); + +private: + int createToolbars(); + + std::string chooseCamera(); + int openCamera(); + + int startCapture(); + void stopCapture(); + + void addCamera(std::shared_ptr camera); + void removeCamera(std::shared_ptr camera); + + int queueRequest(libcamera::Request *request); + void requestComplete(libcamera::Request *request); + void processCapture(); + void processHotplug(HotplugEvent *e); + void processViewfinder(libcamera::FrameBuffer *buffer); + + /* UI elements */ + QToolBar *toolbar_; + QAction *startStopAction_; + QPushButton *cameraSelectButton_; + QAction *saveRaw_; + ViewFinder *viewfinder_; + + QIcon iconPlay_; + QIcon iconStop_; + + QString title_; + QTimer titleTimer_; + + CameraSelectorDialog *cameraSelectorDialog_; + + /* Options */ + const OptionsParser::Options &options_; + + /* Camera manager, camera, configuration and buffers */ + libcamera::CameraManager *cm_; + std::shared_ptr camera_; + libcamera::FrameBufferAllocator *allocator_; + + std::unique_ptr config_; + std::map> mappedBuffers_; + + /* Capture state, buffers queue and statistics */ + bool isCapturing_; + bool captureRaw_; + libcamera::Stream *vfStream_; + libcamera::Stream *rawStream_; + std::map> freeBuffers_; + QQueue doneQueue_; + QQueue freeQueue_; + QMutex mutex_; /* Protects freeBuffers_, doneQueue_, and freeQueue_ */ + + uint64_t lastBufferTime_; + QElapsedTimer frameRateInterval_; + uint32_t previousFrames_; + uint32_t framesCaptured_; + + std::vector> requests_; +}; diff --git a/src/apps/qcam/meson.build b/src/apps/qcam/meson.build new file mode 100644 index 00000000..d5916d0d --- /dev/null +++ b/src/apps/qcam/meson.build @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: CC0-1.0 + +qt5 = import('qt5') +qt5_dep = dependency('qt5', + method : 'pkg-config', + modules : ['Core', 'Gui', 'Widgets'], + required : get_option('qcam'), + version : '>=5.4') + +if not qt5_dep.found() + qcam_enabled = false + subdir_done() +endif + +qcam_enabled = true + +qcam_sources = files([ + '../cam/image.cpp', + '../cam/options.cpp', + '../cam/stream_options.cpp', + 'cam_select_dialog.cpp', + 'format_converter.cpp', + 'main.cpp', + 'main_window.cpp', + 'message_handler.cpp', + 'viewfinder_qt.cpp', +]) + +qcam_moc_headers = files([ + 'cam_select_dialog.h', + 'main_window.h', + 'viewfinder_qt.h', +]) + +qcam_resources = files([ + 'assets/feathericons/feathericons.qrc', +]) + +qt5_cpp_args = ['-DQT_NO_KEYWORDS'] + +tiff_dep = dependency('libtiff-4', required : false) +if tiff_dep.found() + qt5_cpp_args += ['-DHAVE_TIFF'] + qcam_sources += files([ + '../cam/dng_writer.cpp', + ]) +endif + +if cxx.has_header_symbol('QOpenGLWidget', 'QOpenGLWidget', + dependencies : qt5_dep, args : '-fPIC') + qcam_sources += files([ + 'viewfinder_gl.cpp', + ]) + qcam_moc_headers += files([ + 'viewfinder_gl.h', + ]) + qcam_resources += files([ + 'assets/shader/shaders.qrc' + ]) +endif + +# gcc 9 introduced a deprecated-copy warning that is triggered by Qt until +# Qt 5.13. clang 10 introduced the same warning, but detects more issues +# that are not fixed in Qt yet. Disable the warning manually in both cases. +if ((cc.get_id() == 'gcc' and cc.version().version_compare('>=9.0') and + qt5_dep.version().version_compare('<5.13')) or + (cc.get_id() == 'clang' and cc.version().version_compare('>=10.0'))) + qt5_cpp_args += ['-Wno-deprecated-copy'] +endif + +resources = qt5.preprocess(moc_headers: qcam_moc_headers, + qresources : qcam_resources, + dependencies: qt5_dep) + +qcam = executable('qcam', qcam_sources, resources, + install : true, + dependencies : [ + libatomic, + libcamera_public, + qt5_dep, + tiff_dep, + ], + cpp_args : qt5_cpp_args) diff --git a/src/apps/qcam/message_handler.cpp b/src/apps/qcam/message_handler.cpp new file mode 100644 index 00000000..261623e1 --- /dev/null +++ b/src/apps/qcam/message_handler.cpp @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Laurent Pinchart + * + * message_handler.cpp - qcam - Log message handling + */ + +#include "message_handler.h" + +QtMessageHandler MessageHandler::handler_ = nullptr; +bool MessageHandler::verbose_ = false; + +MessageHandler::MessageHandler(bool verbose) +{ + verbose_ = verbose; + handler_ = qInstallMessageHandler(&MessageHandler::handleMessage); +} + +void MessageHandler::handleMessage(QtMsgType type, + const QMessageLogContext &context, + const QString &msg) +{ + if (type == QtDebugMsg && !verbose_) + return; + + handler_(type, context, msg); +} diff --git a/src/apps/qcam/message_handler.h b/src/apps/qcam/message_handler.h new file mode 100644 index 00000000..56294d37 --- /dev/null +++ b/src/apps/qcam/message_handler.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Laurent Pinchart + * + * message_handler.cpp - qcam - Log message handling + */ + +#pragma once + +#include + +class MessageHandler +{ +public: + MessageHandler(bool verbose); + +private: + static void handleMessage(QtMsgType type, + const QMessageLogContext &context, + const QString &msg); + + static QtMessageHandler handler_; + static bool verbose_; +}; diff --git a/src/apps/qcam/viewfinder.h b/src/apps/qcam/viewfinder.h new file mode 100644 index 00000000..a57446e8 --- /dev/null +++ b/src/apps/qcam/viewfinder.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * viewfinder.h - qcam - Viewfinder base class + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include + +class Image; + +class ViewFinder +{ +public: + virtual ~ViewFinder() = default; + + virtual const QList &nativeFormats() const = 0; + + virtual int setFormat(const libcamera::PixelFormat &format, const QSize &size, + const libcamera::ColorSpace &colorSpace, + unsigned int stride) = 0; + virtual void render(libcamera::FrameBuffer *buffer, Image *image) = 0; + virtual void stop() = 0; + + virtual QImage getCurrentImage() = 0; +}; diff --git a/src/apps/qcam/viewfinder_gl.cpp b/src/apps/qcam/viewfinder_gl.cpp new file mode 100644 index 00000000..38ddad58 --- /dev/null +++ b/src/apps/qcam/viewfinder_gl.cpp @@ -0,0 +1,835 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Linaro + * + * viewfinderGL.cpp - OpenGL Viewfinder for rendering by OpenGL shader + */ + +#include "viewfinder_gl.h" + +#include + +#include +#include +#include +#include + +#include + +#include "../cam/image.h" + +static const QList supportedFormats{ + /* YUV - packed (single plane) */ + libcamera::formats::UYVY, + libcamera::formats::VYUY, + libcamera::formats::YUYV, + libcamera::formats::YVYU, + /* YUV - semi planar (two planes) */ + libcamera::formats::NV12, + libcamera::formats::NV21, + libcamera::formats::NV16, + libcamera::formats::NV61, + libcamera::formats::NV24, + libcamera::formats::NV42, + /* YUV - fully planar (three planes) */ + libcamera::formats::YUV420, + libcamera::formats::YVU420, + /* RGB */ + libcamera::formats::ABGR8888, + libcamera::formats::ARGB8888, + libcamera::formats::BGRA8888, + libcamera::formats::RGBA8888, + libcamera::formats::BGR888, + libcamera::formats::RGB888, + /* Raw Bayer 8-bit */ + libcamera::formats::SBGGR8, + libcamera::formats::SGBRG8, + libcamera::formats::SGRBG8, + libcamera::formats::SRGGB8, + /* Raw Bayer 10-bit packed */ + libcamera::formats::SBGGR10_CSI2P, + libcamera::formats::SGBRG10_CSI2P, + libcamera::formats::SGRBG10_CSI2P, + libcamera::formats::SRGGB10_CSI2P, + /* Raw Bayer 12-bit packed */ + libcamera::formats::SBGGR12_CSI2P, + libcamera::formats::SGBRG12_CSI2P, + libcamera::formats::SGRBG12_CSI2P, + libcamera::formats::SRGGB12_CSI2P, +}; + +ViewFinderGL::ViewFinderGL(QWidget *parent) + : QOpenGLWidget(parent), buffer_(nullptr), + colorSpace_(libcamera::ColorSpace::Raw), image_(nullptr), + vertexBuffer_(QOpenGLBuffer::VertexBuffer) +{ +} + +ViewFinderGL::~ViewFinderGL() +{ + removeShader(); +} + +const QList &ViewFinderGL::nativeFormats() const +{ + return supportedFormats; +} + +int ViewFinderGL::setFormat(const libcamera::PixelFormat &format, const QSize &size, + const libcamera::ColorSpace &colorSpace, + unsigned int stride) +{ + if (format != format_ || colorSpace != colorSpace_) { + /* + * If the fragment already exists, remove it and create a new + * one for the new format. + */ + if (shaderProgram_.isLinked()) { + shaderProgram_.release(); + shaderProgram_.removeShader(fragmentShader_.get()); + fragmentShader_.reset(); + } + + if (!selectFormat(format)) + return -1; + + selectColorSpace(colorSpace); + + format_ = format; + colorSpace_ = colorSpace; + } + + size_ = size; + stride_ = stride; + + updateGeometry(); + return 0; +} + +void ViewFinderGL::stop() +{ + if (buffer_) { + renderComplete(buffer_); + buffer_ = nullptr; + image_ = nullptr; + } +} + +QImage ViewFinderGL::getCurrentImage() +{ + QMutexLocker locker(&mutex_); + + return grabFramebuffer(); +} + +void ViewFinderGL::render(libcamera::FrameBuffer *buffer, Image *image) +{ + if (buffer_) + renderComplete(buffer_); + + image_ = image; + update(); + buffer_ = buffer; +} + +bool ViewFinderGL::selectFormat(const libcamera::PixelFormat &format) +{ + bool ret = true; + + /* Set min/mag filters to GL_LINEAR by default. */ + textureMinMagFilters_ = GL_LINEAR; + + /* Use identity.vert as the default vertex shader. */ + vertexShaderFile_ = ":identity.vert"; + + fragmentShaderDefines_.clear(); + + switch (format) { + case libcamera::formats::NV12: + horzSubSample_ = 2; + vertSubSample_ = 2; + fragmentShaderDefines_.append("#define YUV_PATTERN_UV"); + fragmentShaderFile_ = ":YUV_2_planes.frag"; + break; + case libcamera::formats::NV21: + horzSubSample_ = 2; + vertSubSample_ = 2; + fragmentShaderDefines_.append("#define YUV_PATTERN_VU"); + fragmentShaderFile_ = ":YUV_2_planes.frag"; + break; + case libcamera::formats::NV16: + horzSubSample_ = 2; + vertSubSample_ = 1; + fragmentShaderDefines_.append("#define YUV_PATTERN_UV"); + fragmentShaderFile_ = ":YUV_2_planes.frag"; + break; + case libcamera::formats::NV61: + horzSubSample_ = 2; + vertSubSample_ = 1; + fragmentShaderDefines_.append("#define YUV_PATTERN_VU"); + fragmentShaderFile_ = ":YUV_2_planes.frag"; + break; + case libcamera::formats::NV24: + horzSubSample_ = 1; + vertSubSample_ = 1; + fragmentShaderDefines_.append("#define YUV_PATTERN_UV"); + fragmentShaderFile_ = ":YUV_2_planes.frag"; + break; + case libcamera::formats::NV42: + horzSubSample_ = 1; + vertSubSample_ = 1; + fragmentShaderDefines_.append("#define YUV_PATTERN_VU"); + fragmentShaderFile_ = ":YUV_2_planes.frag"; + break; + case libcamera::formats::YUV420: + horzSubSample_ = 2; + vertSubSample_ = 2; + fragmentShaderFile_ = ":YUV_3_planes.frag"; + break; + case libcamera::formats::YVU420: + horzSubSample_ = 2; + vertSubSample_ = 2; + fragmentShaderFile_ = ":YUV_3_planes.frag"; + break; + case libcamera::formats::UYVY: + fragmentShaderDefines_.append("#define YUV_PATTERN_UYVY"); + fragmentShaderFile_ = ":YUV_packed.frag"; + break; + case libcamera::formats::VYUY: + fragmentShaderDefines_.append("#define YUV_PATTERN_VYUY"); + fragmentShaderFile_ = ":YUV_packed.frag"; + break; + case libcamera::formats::YUYV: + fragmentShaderDefines_.append("#define YUV_PATTERN_YUYV"); + fragmentShaderFile_ = ":YUV_packed.frag"; + break; + case libcamera::formats::YVYU: + fragmentShaderDefines_.append("#define YUV_PATTERN_YVYU"); + fragmentShaderFile_ = ":YUV_packed.frag"; + break; + case libcamera::formats::ABGR8888: + fragmentShaderDefines_.append("#define RGB_PATTERN rgb"); + fragmentShaderFile_ = ":RGB.frag"; + break; + case libcamera::formats::ARGB8888: + fragmentShaderDefines_.append("#define RGB_PATTERN bgr"); + fragmentShaderFile_ = ":RGB.frag"; + break; + case libcamera::formats::BGRA8888: + fragmentShaderDefines_.append("#define RGB_PATTERN gba"); + fragmentShaderFile_ = ":RGB.frag"; + break; + case libcamera::formats::RGBA8888: + fragmentShaderDefines_.append("#define RGB_PATTERN abg"); + fragmentShaderFile_ = ":RGB.frag"; + break; + case libcamera::formats::BGR888: + fragmentShaderDefines_.append("#define RGB_PATTERN rgb"); + fragmentShaderFile_ = ":RGB.frag"; + break; + case libcamera::formats::RGB888: + fragmentShaderDefines_.append("#define RGB_PATTERN bgr"); + fragmentShaderFile_ = ":RGB.frag"; + break; + case libcamera::formats::SBGGR8: + firstRed_.setX(1.0); + firstRed_.setY(1.0); + vertexShaderFile_ = ":bayer_8.vert"; + fragmentShaderFile_ = ":bayer_8.frag"; + textureMinMagFilters_ = GL_NEAREST; + break; + case libcamera::formats::SGBRG8: + firstRed_.setX(0.0); + firstRed_.setY(1.0); + vertexShaderFile_ = ":bayer_8.vert"; + fragmentShaderFile_ = ":bayer_8.frag"; + textureMinMagFilters_ = GL_NEAREST; + break; + case libcamera::formats::SGRBG8: + firstRed_.setX(1.0); + firstRed_.setY(0.0); + vertexShaderFile_ = ":bayer_8.vert"; + fragmentShaderFile_ = ":bayer_8.frag"; + textureMinMagFilters_ = GL_NEAREST; + break; + case libcamera::formats::SRGGB8: + firstRed_.setX(0.0); + firstRed_.setY(0.0); + vertexShaderFile_ = ":bayer_8.vert"; + fragmentShaderFile_ = ":bayer_8.frag"; + textureMinMagFilters_ = GL_NEAREST; + break; + case libcamera::formats::SBGGR10_CSI2P: + firstRed_.setX(1.0); + firstRed_.setY(1.0); + fragmentShaderDefines_.append("#define RAW10P"); + fragmentShaderFile_ = ":bayer_1x_packed.frag"; + textureMinMagFilters_ = GL_NEAREST; + break; + case libcamera::formats::SGBRG10_CSI2P: + firstRed_.setX(0.0); + firstRed_.setY(1.0); + fragmentShaderDefines_.append("#define RAW10P"); + fragmentShaderFile_ = ":bayer_1x_packed.frag"; + textureMinMagFilters_ = GL_NEAREST; + break; + case libcamera::formats::SGRBG10_CSI2P: + firstRed_.setX(1.0); + firstRed_.setY(0.0); + fragmentShaderDefines_.append("#define RAW10P"); + fragmentShaderFile_ = ":bayer_1x_packed.frag"; + textureMinMagFilters_ = GL_NEAREST; + break; + case libcamera::formats::SRGGB10_CSI2P: + firstRed_.setX(0.0); + firstRed_.setY(0.0); + fragmentShaderDefines_.append("#define RAW10P"); + fragmentShaderFile_ = ":bayer_1x_packed.frag"; + textureMinMagFilters_ = GL_NEAREST; + break; + case libcamera::formats::SBGGR12_CSI2P: + firstRed_.setX(1.0); + firstRed_.setY(1.0); + fragmentShaderDefines_.append("#define RAW12P"); + fragmentShaderFile_ = ":bayer_1x_packed.frag"; + textureMinMagFilters_ = GL_NEAREST; + break; + case libcamera::formats::SGBRG12_CSI2P: + firstRed_.setX(0.0); + firstRed_.setY(1.0); + fragmentShaderDefines_.append("#define RAW12P"); + fragmentShaderFile_ = ":bayer_1x_packed.frag"; + textureMinMagFilters_ = GL_NEAREST; + break; + case libcamera::formats::SGRBG12_CSI2P: + firstRed_.setX(1.0); + firstRed_.setY(0.0); + fragmentShaderDefines_.append("#define RAW12P"); + fragmentShaderFile_ = ":bayer_1x_packed.frag"; + textureMinMagFilters_ = GL_NEAREST; + break; + case libcamera::formats::SRGGB12_CSI2P: + firstRed_.setX(0.0); + firstRed_.setY(0.0); + fragmentShaderDefines_.append("#define RAW12P"); + fragmentShaderFile_ = ":bayer_1x_packed.frag"; + textureMinMagFilters_ = GL_NEAREST; + break; + default: + ret = false; + qWarning() << "[ViewFinderGL]:" + << "format not supported."; + break; + }; + + return ret; +} + +void ViewFinderGL::selectColorSpace(const libcamera::ColorSpace &colorSpace) +{ + std::array yuv2rgb; + + /* OpenGL stores arrays in column-major order. */ + switch (colorSpace.ycbcrEncoding) { + case libcamera::ColorSpace::YcbcrEncoding::None: + default: + yuv2rgb = { + 1.0000, 0.0000, 0.0000, + 0.0000, 1.0000, 0.0000, + 0.0000, 0.0000, 1.0000, + }; + break; + + case libcamera::ColorSpace::YcbcrEncoding::Rec601: + yuv2rgb = { + 1.0000, 1.0000, 1.0000, + 0.0000, -0.3441, 1.7720, + 1.4020, -0.7141, 0.0000, + }; + break; + + case libcamera::ColorSpace::YcbcrEncoding::Rec709: + yuv2rgb = { + 1.0000, 1.0000, 1.0000, + 0.0000, -0.1873, 1.8856, + 1.5748, -0.4681, 0.0000, + }; + break; + + case libcamera::ColorSpace::YcbcrEncoding::Rec2020: + yuv2rgb = { + 1.0000, 1.0000, 1.0000, + 0.0000, -0.1646, 1.8814, + 1.4746, -0.5714, 0.0000, + }; + break; + } + + double offset; + + switch (colorSpace.range) { + case libcamera::ColorSpace::Range::Full: + default: + offset = 0.0; + break; + + case libcamera::ColorSpace::Range::Limited: + offset = 16.0; + + for (unsigned int i = 0; i < 3; ++i) + yuv2rgb[i] *= 255.0 / 219.0; + for (unsigned int i = 4; i < 9; ++i) + yuv2rgb[i] *= 255.0 / 224.0; + break; + } + + QStringList matrix; + + for (double coeff : yuv2rgb) + matrix.append(QString::number(coeff, 'f')); + + fragmentShaderDefines_.append("#define YUV2RGB_MATRIX " + matrix.join(", ")); + fragmentShaderDefines_.append(QString("#define YUV2RGB_Y_OFFSET %1") + .arg(offset, 0, 'f', 1)); +} + +bool ViewFinderGL::createVertexShader() +{ + /* Create Vertex Shader */ + vertexShader_ = std::make_unique(QOpenGLShader::Vertex, this); + + /* Compile the vertex shader */ + if (!vertexShader_->compileSourceFile(vertexShaderFile_)) { + qWarning() << "[ViewFinderGL]:" << vertexShader_->log(); + return false; + } + + shaderProgram_.addShader(vertexShader_.get()); + return true; +} + +bool ViewFinderGL::createFragmentShader() +{ + int attributeVertex; + int attributeTexture; + + /* + * Create the fragment shader, compile it, and add it to the shader + * program. The #define macros stored in fragmentShaderDefines_, if + * any, are prepended to the source code. + */ + fragmentShader_ = std::make_unique(QOpenGLShader::Fragment, this); + + QFile file(fragmentShaderFile_); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qWarning() << "Shader" << fragmentShaderFile_ << "not found"; + return false; + } + + QString defines = fragmentShaderDefines_.join('\n') + "\n"; + QByteArray src = file.readAll(); + src.prepend(defines.toUtf8()); + + if (!fragmentShader_->compileSourceCode(src)) { + qWarning() << "[ViewFinderGL]:" << fragmentShader_->log(); + return false; + } + + shaderProgram_.addShader(fragmentShader_.get()); + + /* Link shader pipeline */ + if (!shaderProgram_.link()) { + qWarning() << "[ViewFinderGL]:" << shaderProgram_.log(); + close(); + } + + /* Bind shader pipeline for use */ + if (!shaderProgram_.bind()) { + qWarning() << "[ViewFinderGL]:" << shaderProgram_.log(); + close(); + } + + attributeVertex = shaderProgram_.attributeLocation("vertexIn"); + attributeTexture = shaderProgram_.attributeLocation("textureIn"); + + shaderProgram_.enableAttributeArray(attributeVertex); + shaderProgram_.setAttributeBuffer(attributeVertex, + GL_FLOAT, + 0, + 2, + 2 * sizeof(GLfloat)); + + shaderProgram_.enableAttributeArray(attributeTexture); + shaderProgram_.setAttributeBuffer(attributeTexture, + GL_FLOAT, + 8 * sizeof(GLfloat), + 2, + 2 * sizeof(GLfloat)); + + textureUniformY_ = shaderProgram_.uniformLocation("tex_y"); + textureUniformU_ = shaderProgram_.uniformLocation("tex_u"); + textureUniformV_ = shaderProgram_.uniformLocation("tex_v"); + textureUniformStep_ = shaderProgram_.uniformLocation("tex_step"); + textureUniformSize_ = shaderProgram_.uniformLocation("tex_size"); + textureUniformStrideFactor_ = shaderProgram_.uniformLocation("stride_factor"); + textureUniformBayerFirstRed_ = shaderProgram_.uniformLocation("tex_bayer_first_red"); + + /* Create the textures. */ + for (std::unique_ptr &texture : textures_) { + if (texture) + continue; + + texture = std::make_unique(QOpenGLTexture::Target2D); + texture->create(); + } + + return true; +} + +void ViewFinderGL::configureTexture(QOpenGLTexture &texture) +{ + glBindTexture(GL_TEXTURE_2D, texture.textureId()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + textureMinMagFilters_); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + textureMinMagFilters_); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +void ViewFinderGL::removeShader() +{ + if (shaderProgram_.isLinked()) { + shaderProgram_.release(); + shaderProgram_.removeAllShaders(); + } +} + +void ViewFinderGL::initializeGL() +{ + initializeOpenGLFunctions(); + glEnable(GL_TEXTURE_2D); + glDisable(GL_DEPTH_TEST); + + static const GLfloat coordinates[2][4][2]{ + { + /* Vertex coordinates */ + { -1.0f, -1.0f }, + { -1.0f, +1.0f }, + { +1.0f, +1.0f }, + { +1.0f, -1.0f }, + }, + { + /* Texture coordinates */ + { 0.0f, 1.0f }, + { 0.0f, 0.0f }, + { 1.0f, 0.0f }, + { 1.0f, 1.0f }, + }, + }; + + vertexBuffer_.create(); + vertexBuffer_.bind(); + vertexBuffer_.allocate(coordinates, sizeof(coordinates)); + + /* Create Vertex Shader */ + if (!createVertexShader()) + qWarning() << "[ViewFinderGL]: create vertex shader failed."; + + glClearColor(1.0f, 1.0f, 1.0f, 0.0f); +} + +void ViewFinderGL::doRender() +{ + /* Stride of the first plane, in pixels. */ + unsigned int stridePixels; + + switch (format_) { + case libcamera::formats::NV12: + case libcamera::formats::NV21: + case libcamera::formats::NV16: + case libcamera::formats::NV61: + case libcamera::formats::NV24: + case libcamera::formats::NV42: + /* Activate texture Y */ + glActiveTexture(GL_TEXTURE0); + configureTexture(*textures_[0]); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_LUMINANCE, + stride_, + size_.height(), + 0, + GL_LUMINANCE, + GL_UNSIGNED_BYTE, + image_->data(0).data()); + shaderProgram_.setUniformValue(textureUniformY_, 0); + + /* Activate texture UV/VU */ + glActiveTexture(GL_TEXTURE1); + configureTexture(*textures_[1]); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_LUMINANCE_ALPHA, + stride_ / horzSubSample_, + size_.height() / vertSubSample_, + 0, + GL_LUMINANCE_ALPHA, + GL_UNSIGNED_BYTE, + image_->data(1).data()); + shaderProgram_.setUniformValue(textureUniformU_, 1); + + stridePixels = stride_; + break; + + case libcamera::formats::YUV420: + /* Activate texture Y */ + glActiveTexture(GL_TEXTURE0); + configureTexture(*textures_[0]); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_LUMINANCE, + stride_, + size_.height(), + 0, + GL_LUMINANCE, + GL_UNSIGNED_BYTE, + image_->data(0).data()); + shaderProgram_.setUniformValue(textureUniformY_, 0); + + /* Activate texture U */ + glActiveTexture(GL_TEXTURE1); + configureTexture(*textures_[1]); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_LUMINANCE, + stride_ / horzSubSample_, + size_.height() / vertSubSample_, + 0, + GL_LUMINANCE, + GL_UNSIGNED_BYTE, + image_->data(1).data()); + shaderProgram_.setUniformValue(textureUniformU_, 1); + + /* Activate texture V */ + glActiveTexture(GL_TEXTURE2); + configureTexture(*textures_[2]); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_LUMINANCE, + stride_ / horzSubSample_, + size_.height() / vertSubSample_, + 0, + GL_LUMINANCE, + GL_UNSIGNED_BYTE, + image_->data(2).data()); + shaderProgram_.setUniformValue(textureUniformV_, 2); + + stridePixels = stride_; + break; + + case libcamera::formats::YVU420: + /* Activate texture Y */ + glActiveTexture(GL_TEXTURE0); + configureTexture(*textures_[0]); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_LUMINANCE, + stride_, + size_.height(), + 0, + GL_LUMINANCE, + GL_UNSIGNED_BYTE, + image_->data(0).data()); + shaderProgram_.setUniformValue(textureUniformY_, 0); + + /* Activate texture V */ + glActiveTexture(GL_TEXTURE2); + configureTexture(*textures_[2]); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_LUMINANCE, + stride_ / horzSubSample_, + size_.height() / vertSubSample_, + 0, + GL_LUMINANCE, + GL_UNSIGNED_BYTE, + image_->data(1).data()); + shaderProgram_.setUniformValue(textureUniformV_, 2); + + /* Activate texture U */ + glActiveTexture(GL_TEXTURE1); + configureTexture(*textures_[1]); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_LUMINANCE, + stride_ / horzSubSample_, + size_.height() / vertSubSample_, + 0, + GL_LUMINANCE, + GL_UNSIGNED_BYTE, + image_->data(2).data()); + shaderProgram_.setUniformValue(textureUniformU_, 1); + + stridePixels = stride_; + break; + + case libcamera::formats::UYVY: + case libcamera::formats::VYUY: + case libcamera::formats::YUYV: + case libcamera::formats::YVYU: + /* + * Packed YUV formats are stored in a RGBA texture to match the + * OpenGL texel size with the 4 bytes repeating pattern in YUV. + * The texture width is thus half of the image_ with. + */ + glActiveTexture(GL_TEXTURE0); + configureTexture(*textures_[0]); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA, + stride_ / 4, + size_.height(), + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + image_->data(0).data()); + shaderProgram_.setUniformValue(textureUniformY_, 0); + + /* + * The shader needs the step between two texture pixels in the + * horizontal direction, expressed in texture coordinate units + * ([0, 1]). There are exactly width - 1 steps between the + * leftmost and rightmost texels. + */ + shaderProgram_.setUniformValue(textureUniformStep_, + 1.0f / (size_.width() / 2 - 1), + 1.0f /* not used */); + + stridePixels = stride_ / 2; + break; + + case libcamera::formats::ABGR8888: + case libcamera::formats::ARGB8888: + case libcamera::formats::BGRA8888: + case libcamera::formats::RGBA8888: + glActiveTexture(GL_TEXTURE0); + configureTexture(*textures_[0]); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA, + stride_ / 4, + size_.height(), + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + image_->data(0).data()); + shaderProgram_.setUniformValue(textureUniformY_, 0); + + stridePixels = stride_ / 4; + break; + + case libcamera::formats::BGR888: + case libcamera::formats::RGB888: + glActiveTexture(GL_TEXTURE0); + configureTexture(*textures_[0]); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGB, + stride_ / 3, + size_.height(), + 0, + GL_RGB, + GL_UNSIGNED_BYTE, + image_->data(0).data()); + shaderProgram_.setUniformValue(textureUniformY_, 0); + + stridePixels = stride_ / 3; + break; + + case libcamera::formats::SBGGR8: + case libcamera::formats::SGBRG8: + case libcamera::formats::SGRBG8: + case libcamera::formats::SRGGB8: + case libcamera::formats::SBGGR10_CSI2P: + case libcamera::formats::SGBRG10_CSI2P: + case libcamera::formats::SGRBG10_CSI2P: + case libcamera::formats::SRGGB10_CSI2P: + case libcamera::formats::SBGGR12_CSI2P: + case libcamera::formats::SGBRG12_CSI2P: + case libcamera::formats::SGRBG12_CSI2P: + case libcamera::formats::SRGGB12_CSI2P: + /* + * Raw Bayer 8-bit, and packed raw Bayer 10-bit/12-bit formats + * are stored in a GL_LUMINANCE texture. The texture width is + * equal to the stride. + */ + glActiveTexture(GL_TEXTURE0); + configureTexture(*textures_[0]); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_LUMINANCE, + stride_, + size_.height(), + 0, + GL_LUMINANCE, + GL_UNSIGNED_BYTE, + image_->data(0).data()); + shaderProgram_.setUniformValue(textureUniformY_, 0); + shaderProgram_.setUniformValue(textureUniformBayerFirstRed_, + firstRed_); + shaderProgram_.setUniformValue(textureUniformSize_, + size_.width(), /* in pixels */ + size_.height()); + shaderProgram_.setUniformValue(textureUniformStep_, + 1.0f / (stride_ - 1), + 1.0f / (size_.height() - 1)); + + /* + * The stride is already taken into account in the shaders, set + * the generic stride factor to 1.0. + */ + stridePixels = size_.width(); + break; + + default: + stridePixels = size_.width(); + break; + }; + + /* + * Compute the stride factor for the vertex shader, to map the + * horizontal texture coordinate range [0.0, 1.0] to the active portion + * of the image. + */ + shaderProgram_.setUniformValue(textureUniformStrideFactor_, + static_cast(size_.width() - 1) / + (stridePixels - 1)); +} + +void ViewFinderGL::paintGL() +{ + if (!fragmentShader_) + if (!createFragmentShader()) { + qWarning() << "[ViewFinderGL]:" + << "create fragment shader failed."; + } + + if (image_) { + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + doRender(); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } +} + +void ViewFinderGL::resizeGL(int w, int h) +{ + glViewport(0, 0, w, h); +} + +QSize ViewFinderGL::sizeHint() const +{ + return size_.isValid() ? size_ : QSize(640, 480); +} diff --git a/src/apps/qcam/viewfinder_gl.h b/src/apps/qcam/viewfinder_gl.h new file mode 100644 index 00000000..68c2912d --- /dev/null +++ b/src/apps/qcam/viewfinder_gl.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Linaro + * + * viewfinder_GL.h - OpenGL Viewfinder for rendering by OpenGL shader + * + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "viewfinder.h" + +class ViewFinderGL : public QOpenGLWidget, + public ViewFinder, + protected QOpenGLFunctions +{ + Q_OBJECT + +public: + ViewFinderGL(QWidget *parent = nullptr); + ~ViewFinderGL(); + + const QList &nativeFormats() const override; + + int setFormat(const libcamera::PixelFormat &format, const QSize &size, + const libcamera::ColorSpace &colorSpace, + unsigned int stride) override; + void render(libcamera::FrameBuffer *buffer, Image *image) override; + void stop() override; + + QImage getCurrentImage() override; + +Q_SIGNALS: + void renderComplete(libcamera::FrameBuffer *buffer); + +protected: + void initializeGL() override; + void paintGL() override; + void resizeGL(int w, int h) override; + QSize sizeHint() const override; + +private: + bool selectFormat(const libcamera::PixelFormat &format); + void selectColorSpace(const libcamera::ColorSpace &colorSpace); + + void configureTexture(QOpenGLTexture &texture); + bool createFragmentShader(); + bool createVertexShader(); + void removeShader(); + void doRender(); + + /* Captured image size, format and buffer */ + libcamera::FrameBuffer *buffer_; + libcamera::PixelFormat format_; + libcamera::ColorSpace colorSpace_; + QSize size_; + unsigned int stride_; + Image *image_; + + /* Shaders */ + QOpenGLShaderProgram shaderProgram_; + std::unique_ptr vertexShader_; + std::unique_ptr fragmentShader_; + QString vertexShaderFile_; + QString fragmentShaderFile_; + QStringList fragmentShaderDefines_; + + /* Vertex buffer */ + QOpenGLBuffer vertexBuffer_; + + /* Textures */ + std::array, 3> textures_; + + /* Common texture parameters */ + GLuint textureMinMagFilters_; + + /* YUV texture parameters */ + GLuint textureUniformU_; + GLuint textureUniformV_; + GLuint textureUniformY_; + GLuint textureUniformStep_; + unsigned int horzSubSample_; + unsigned int vertSubSample_; + + /* Raw Bayer texture parameters */ + GLuint textureUniformSize_; + GLuint textureUniformStrideFactor_; + GLuint textureUniformBayerFirstRed_; + QPointF firstRed_; + + QMutex mutex_; /* Prevent concurrent access to image_ */ +}; diff --git a/src/apps/qcam/viewfinder_qt.cpp b/src/apps/qcam/viewfinder_qt.cpp new file mode 100644 index 00000000..c20fd6bc --- /dev/null +++ b/src/apps/qcam/viewfinder_qt.cpp @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * viewfinder_qt.cpp - qcam - QPainter-based ViewFinder + */ + +#include "viewfinder_qt.h" + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "../cam/image.h" + +#include "format_converter.h" + +static const QMap nativeFormats +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) + { libcamera::formats::ABGR8888, QImage::Format_RGBX8888 }, + { libcamera::formats::XBGR8888, QImage::Format_RGBX8888 }, +#endif + { libcamera::formats::ARGB8888, QImage::Format_RGB32 }, + { libcamera::formats::XRGB8888, QImage::Format_RGB32 }, +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + { libcamera::formats::RGB888, QImage::Format_BGR888 }, +#endif + { libcamera::formats::BGR888, QImage::Format_RGB888 }, +}; + +ViewFinderQt::ViewFinderQt(QWidget *parent) + : QWidget(parent), buffer_(nullptr) +{ + icon_ = QIcon(":camera-off.svg"); +} + +ViewFinderQt::~ViewFinderQt() +{ +} + +const QList &ViewFinderQt::nativeFormats() const +{ + static const QList formats = ::nativeFormats.keys(); + return formats; +} + +int ViewFinderQt::setFormat(const libcamera::PixelFormat &format, const QSize &size, + [[maybe_unused]] const libcamera::ColorSpace &colorSpace, + unsigned int stride) +{ + image_ = QImage(); + + /* + * If format conversion is needed, configure the converter and allocate + * the destination image. + */ + if (!::nativeFormats.contains(format)) { + int ret = converter_.configure(format, size, stride); + if (ret < 0) + return ret; + + image_ = QImage(size, QImage::Format_RGB32); + + qInfo() << "Using software format conversion from" << format; + } else { + qInfo() << "Zero-copy enabled"; + } + + format_ = format; + size_ = size; + + updateGeometry(); + return 0; +} + +void ViewFinderQt::render(libcamera::FrameBuffer *buffer, Image *image) +{ + size_t size = buffer->metadata().planes()[0].bytesused; + + { + QMutexLocker locker(&mutex_); + + if (::nativeFormats.contains(format_)) { + /* + * If the frame format is identical to the display + * format, create a QImage that references the frame + * and store a reference to the frame buffer. The + * previously stored frame buffer, if any, will be + * released. + * + * \todo Get the stride from the buffer instead of + * computing it naively + */ + assert(buffer->planes().size() == 1); + image_ = QImage(image->data(0).data(), size_.width(), + size_.height(), size / size_.height(), + ::nativeFormats[format_]); + std::swap(buffer, buffer_); + } else { + /* + * Otherwise, convert the format and release the frame + * buffer immediately. + */ + converter_.convert(image, size, &image_); + } + } + + update(); + + if (buffer) + renderComplete(buffer); +} + +void ViewFinderQt::stop() +{ + image_ = QImage(); + + if (buffer_) { + renderComplete(buffer_); + buffer_ = nullptr; + } + + update(); +} + +QImage ViewFinderQt::getCurrentImage() +{ + QMutexLocker locker(&mutex_); + + return image_.copy(); +} + +void ViewFinderQt::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + + /* If we have an image, draw it. */ + if (!image_.isNull()) { + painter.drawImage(rect(), image_, image_.rect()); + return; + } + + /* + * Otherwise, draw the camera stopped icon. Render it to the pixmap if + * the size has changed. + */ + constexpr int margin = 20; + + if (vfSize_ != size() || pixmap_.isNull()) { + QSize vfSize = size() - QSize{ 2 * margin, 2 * margin }; + QSize pixmapSize{ 1, 1 }; + pixmapSize.scale(vfSize, Qt::KeepAspectRatio); + pixmap_ = icon_.pixmap(pixmapSize); + + vfSize_ = size(); + } + + QPoint point{ margin, margin }; + if (pixmap_.width() < width() - 2 * margin) + point.setX((width() - pixmap_.width()) / 2); + else + point.setY((height() - pixmap_.height()) / 2); + + painter.setBackgroundMode(Qt::OpaqueMode); + painter.drawPixmap(point, pixmap_); +} + +QSize ViewFinderQt::sizeHint() const +{ + return size_.isValid() ? size_ : QSize(640, 480); +} diff --git a/src/apps/qcam/viewfinder_qt.h b/src/apps/qcam/viewfinder_qt.h new file mode 100644 index 00000000..eb3a9988 --- /dev/null +++ b/src/apps/qcam/viewfinder_qt.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * viewfinder_qt.h - qcam - QPainter-based ViewFinder + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "format_converter.h" +#include "viewfinder.h" + +class ViewFinderQt : public QWidget, public ViewFinder +{ + Q_OBJECT + +public: + ViewFinderQt(QWidget *parent); + ~ViewFinderQt(); + + const QList &nativeFormats() const override; + + int setFormat(const libcamera::PixelFormat &format, const QSize &size, + const libcamera::ColorSpace &colorSpace, + unsigned int stride) override; + void render(libcamera::FrameBuffer *buffer, Image *image) override; + void stop() override; + + QImage getCurrentImage() override; + +Q_SIGNALS: + void renderComplete(libcamera::FrameBuffer *buffer); + +protected: + void paintEvent(QPaintEvent *) override; + QSize sizeHint() const override; + +private: + FormatConverter converter_; + + libcamera::PixelFormat format_; + QSize size_; + + /* Camera stopped icon */ + QSize vfSize_; + QIcon icon_; + QPixmap pixmap_; + + /* Buffer and render image */ + libcamera::FrameBuffer *buffer_; + QImage image_; + QMutex mutex_; /* Prevent concurrent access to image_ */ +}; -- cgit v1.2.1