123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- #include "QrScannerState.hpp"
- #include "../AssetManager.hpp"
- #include "../Theme.hpp"
- #include <iostream>
- #include <cpp3ds/System/Sleep.hpp>
- #include <cpp3ds/System/Lock.hpp>
- #include <cpp3ds/System/I18n.hpp>
- #include <cmath>
- #include <TweenEngine/Tween.h>
- #define WIDTH 400
- #define HEIGHT 240
- #define EVENT_RECV 0
- #define EVENT_BUFFER_ERROR 1
- //#define EVENT_CANCEL 2
- #define EVENT_COUNT 2
- #define TEXTURE_TRANSFER_FLAGS \
- (GX_TRANSFER_RAW_COPY(0) | GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | \
- GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB565) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB565) | \
- GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO))
- namespace FreeShop {
- QrScannerState::QrScannerState(StateStack &stack, Context &context, StateCallback callback)
- : State(stack, context, callback)
- , m_camThread(&QrScannerState::startCamera, this)
- , m_capturing(false)
- , m_threadRunning(false)
- , m_requestedClose(false)
- , m_displayError(false)
- , m_hideQrBorder(false)
- {
- m_cameraScreen.setTexture(&m_cameraTexture);
- m_cameraScreen.setSize(cpp3ds::Vector2f(WIDTH, HEIGHT));
- m_cameraScreen.setTextureRect(cpp3ds::IntRect(0, 0, WIDTH, HEIGHT));
- if (Theme::isQrSelector9Themed) {
- cpp3ds::Texture &texture = AssetManager<cpp3ds::Texture>::get(FREESHOP_DIR "/theme/images/qr_selector.9.png");
- texture.setSmooth(false);
- m_qrBorder.setTexture(&texture);
- } else {
- cpp3ds::Texture &texture = AssetManager<cpp3ds::Texture>::get("images/qr_selector.9.png");
- texture.setSmooth(false);
- m_qrBorder.setTexture(&texture);
- }
- m_qrBorder.setContentSize(140.f, 140.f);
- m_qrBorder.setPosition(200.f, 120.f);
- m_qrBorder.setColor(cpp3ds::Color(255, 255, 255, 100));
- m_qrBorder.setOrigin(std::round(m_qrBorder.getSize().x * 0.5f), std::round(m_qrBorder.getSize().y * 0.5f));
- TweenEngine::Tween::to(m_qrBorder, m_qrBorder.SCALE_XY, 0.3f)
- .target(0.95f, 0.95f)
- .repeatYoyo(-1, 0.f)
- .start(m_tweenManager);
- m_bottomBackground.setSize(cpp3ds::Vector2f(320.f, 240.f));
- m_closeCaption.setString(_("\uE001 Cancel"));
- m_closeCaption.setFillColor(cpp3ds::Color(150, 150, 150));
- m_closeCaption.setCharacterSize(20);
- m_closeCaption.setPosition(160.f, 200.f);
- m_closeCaption.useSystemFont();
- m_closeCaption.setOrigin(m_closeCaption.getLocalBounds().width / 2,
- m_closeCaption.getLocalBounds().height / 2);
- m_textCloseError = m_closeCaption;
- m_textCloseError.setString(_("\uE000 Retry"));
- m_textCloseError.setPosition(160.f, 200.f);
- m_textError = m_closeCaption;
- m_textError.setPosition(160.f, 120.f);
- m_textError.setCharacterSize(16);
- m_qr = quirc_new();
- quirc_resize(m_qr, WIDTH, HEIGHT);
- m_camThread.setRelativePriority(4);
- m_camThread.launch();
- }
- QrScannerState::~QrScannerState()
- {
- while (m_threadRunning && !m_capturing)
- cpp3ds::sleep(cpp3ds::milliseconds(10));
- m_displayError = false;
- m_capturing = false;
- m_camThread.wait();
- quirc_destroy(m_qr);
- }
- void QrScannerState::renderTopScreen(cpp3ds::Window& window)
- {
- cpp3ds::Lock lock(m_mutexDraw);
- #ifdef _3DS
- if (m_textureBuffer.empty())
- return;
- u32 dimTexture = GX_BUFFER_DIM(512, 256);
- GSPGPU_FlushDataCache(&m_rawTextureBuffer[0], m_rawTextureBuffer.size());
- GX_DisplayTransfer((u32*)&m_rawTextureBuffer[0], dimTexture, (u32*)&m_textureBuffer[0], dimTexture, TEXTURE_TRANSFER_FLAGS);
- gspWaitForPPF();
- GSPGPU_FlushDataCache(&m_textureBuffer[0], m_textureBuffer.size());
- #endif
- window.draw(m_cameraScreen);
- if (!m_displayError && !m_hideQrBorder)
- window.draw(m_qrBorder);
- }
- void QrScannerState::renderBottomScreen(cpp3ds::Window& window)
- {
- window.draw(m_bottomBackground);
- if (m_displayError)
- {
- window.draw(m_textError);
- window.draw(m_textCloseError);
- }
- else
- window.draw(m_closeCaption);
- }
- bool QrScannerState::update(float delta)
- {
- m_tweenManager.update(delta);
- return false;
- }
- bool QrScannerState::processEvent(const cpp3ds::Event& event)
- {
- if (event.type == cpp3ds::Event::KeyPressed)
- {
- if (m_displayError)
- {
- if (event.key.code == cpp3ds::Keyboard::A)
- m_displayError = false;
- }
- else
- {
- if (event.key.code == cpp3ds::Keyboard::B)
- close();
- }
- }
- return false;
- }
- void QrScannerState::startCamera()
- {
- cpp3ds::Thread qrThread(&QrScannerState::scanQrCode, this);
- qrThread.setRelativePriority(1);
- qrThread.setStackSize(64 * 1024);
- m_threadRunning = true;
- #ifdef _3DS
- Handle events[EVENT_COUNT] = {0};
- // events[EVENT_CANCEL] = data->cancelEvent;
- std::vector<cpp3ds::Uint8> tmpCamBuffer;
- size_t qrCounter = 0;
- Result res = 0;
- int w = WIDTH, h = HEIGHT;
- u32 dimCam = GX_BUFFER_DIM(w, h);
- m_camBuffer.resize(w * h * sizeof(u16));
- tmpCamBuffer.resize(w * h * sizeof(u16));
- int npow2w = 512, npow2h = 256;
- m_textureBuffer.resize(npow2w * npow2h * sizeof(u16));
- m_rawTextureBuffer.resize(m_textureBuffer.size());
- m_cameraTexture.loadFromPreprocessedMemory(&m_textureBuffer[0], m_textureBuffer.size(), npow2w, npow2h, GPU_RGB565, false);
- if(R_SUCCEEDED(res = camInit())) {
- if(R_SUCCEEDED(res = CAMU_SetSize(SELECT_OUT1, SIZE_CTR_TOP_LCD, CONTEXT_A))
- && R_SUCCEEDED(res = CAMU_SetOutputFormat(SELECT_OUT1, OUTPUT_RGB_565, CONTEXT_A))
- && R_SUCCEEDED(res = CAMU_SetFrameRate(SELECT_OUT1, FRAME_RATE_30))
- && R_SUCCEEDED(res = CAMU_SetNoiseFilter(SELECT_OUT1, true))
- && R_SUCCEEDED(res = CAMU_SetAutoExposure(SELECT_OUT1, true))
- && R_SUCCEEDED(res = CAMU_SetAutoWhiteBalance(SELECT_OUT1, true))
- && R_SUCCEEDED(res = CAMU_Activate(SELECT_OUT1))) {
- u32 transferUnit = 0;
- if(R_SUCCEEDED(res = CAMU_GetBufferErrorInterruptEvent(&events[EVENT_BUFFER_ERROR], PORT_CAM1))
- && R_SUCCEEDED(res = CAMU_SetTrimming(PORT_CAM1, false))
- && R_SUCCEEDED(res = CAMU_GetMaxBytes(&transferUnit, w, h))
- && R_SUCCEEDED(res = CAMU_SetTransferBytes(PORT_CAM1, transferUnit, w, h))
- && R_SUCCEEDED(res = CAMU_ClearBuffer(PORT_CAM1))
- && R_SUCCEEDED(res = CAMU_SetReceiving(&events[EVENT_RECV], &tmpCamBuffer[0], PORT_CAM1, tmpCamBuffer.size(), (s16) transferUnit))
- && R_SUCCEEDED(res = CAMU_StartCapture(PORT_CAM1)))
- {
- m_capturing = true;
- qrThread.launch();
- while (m_capturing && R_SUCCEEDED(res))
- {
- // svcWaitSynchronization(task_get_pause_event(), U64_MAX);
- s32 index = 0;
- if(R_SUCCEEDED(res = svcWaitSynchronizationN(&index, events, EVENT_COUNT, false, U64_MAX))) {
- switch(index) {
- // case EVENT_CANCEL:
- // m_capturing = false;
- // break;
- case EVENT_RECV:
- svcCloseHandle(events[EVENT_RECV]);
- events[EVENT_RECV] = 0;
- {
- cpp3ds::Lock lock(m_camMutex);
- m_camBuffer = tmpCamBuffer;
- }
- {
- cpp3ds::Lock lock(m_mutexDraw);
- for (int i = 0; i < h; ++i)
- memcpy(&m_rawTextureBuffer[i * npow2w * 2], &tmpCamBuffer[i * w * 2], w * 2);
- }
- res = CAMU_SetReceiving(&events[EVENT_RECV], &tmpCamBuffer[0], PORT_CAM1, tmpCamBuffer.size(), (s16) transferUnit);
- break;
- case EVENT_BUFFER_ERROR:
- cpp3ds::err() << "CAM ERROR" << std::endl;
- svcCloseHandle(events[EVENT_RECV]);
- events[EVENT_RECV] = 0;
- if(R_SUCCEEDED(res = CAMU_ClearBuffer(PORT_CAM1))
- && R_SUCCEEDED(res = CAMU_SetReceiving(&events[EVENT_RECV], &tmpCamBuffer[0], PORT_CAM1, tmpCamBuffer.size(), (s16) transferUnit))) {
- res = CAMU_StartCapture(PORT_CAM1);
- }
- break;
- default:
- break;
- }
- }
- }
- CAMU_StopCapture(PORT_CAM1);
- bool busy = false;
- while (R_SUCCEEDED(CAMU_IsBusy(&busy, PORT_CAM1)) && busy)
- cpp3ds::sleep(cpp3ds::milliseconds(10));
- CAMU_ClearBuffer(PORT_CAM1);
- }
- CAMU_Activate(SELECT_NONE);
- }
- camExit();
- }
- #endif
- m_threadRunning = false;
- m_capturing = false;
- qrThread.wait();
- }
- void QrScannerState::scanQrCode()
- {
- int w, h;
- std::vector<cpp3ds::Uint8> camBuffer;
- while (m_capturing && !m_requestedClose)
- {
- {
- cpp3ds::Lock lock(m_camMutex);
- camBuffer = m_camBuffer;
- }
- cpp3ds::Uint16 *pixels = reinterpret_cast<cpp3ds::Uint16*>(&camBuffer[0]);
- uint8_t *buf = quirc_begin(m_qr, &w, &h);
- for (int x = 0; x < w; x++)
- for (int y = 0; y < h; y++)
- {
- cpp3ds::Uint16 px = pixels[y * WIDTH + x];
- buf[y * w + x] = (uint8_t) (((((px >> 11) & 0x1F) << 3) + (((px >> 5) & 0x3F) << 2) + ((px & 0x1F) << 3)) / 3);
- }
- quirc_end(m_qr);
- int count = quirc_count(m_qr);
- for (int i = 0; i < count; i++)
- {
- struct quirc_code code;
- struct quirc_data data;
- quirc_extract(m_qr, i, &code);
- if (!quirc_decode(&code, &data))
- {
- // Return QR's decoded text to callback, outputs error string when true
- cpp3ds::String text(reinterpret_cast<char*>(data.payload));
- m_hideQrBorder = true;
- TweenEngine::Tween::to(m_cameraScreen, m_cameraScreen.FILL_COLOR_ALPHA, 0.4f)
- .target(100.f)
- .start(m_tweenManager);
- if (runCallback(&text))
- close();
- else
- {
- m_textError.setString(text);
- m_textError.setOrigin(m_textError.getLocalBounds().width / 2,
- m_textError.getLocalBounds().height / 2);
- m_displayError = true;
- }
- m_hideQrBorder = false;
- }
- }
- while (m_displayError)
- cpp3ds::sleep(cpp3ds::milliseconds(100));
- TweenEngine::Tween::to(m_cameraScreen, m_cameraScreen.FILL_COLOR_ALPHA, 0.4f)
- .target(255.f)
- .start(m_tweenManager);
- }
- }
- void QrScannerState::close()
- {
- cpp3ds::Lock lock(m_mutexRequest);
- if (!m_requestedClose)
- {
- m_requestedClose = true;
- requestStackPop();
- }
- }
- } // namespace FreeShop
|