QrScannerState.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. #include "QrScannerState.hpp"
  2. #include "../AssetManager.hpp"
  3. #include "../Theme.hpp"
  4. #include <iostream>
  5. #include <cpp3ds/System/Sleep.hpp>
  6. #include <cpp3ds/System/Lock.hpp>
  7. #include <cpp3ds/System/I18n.hpp>
  8. #include <cmath>
  9. #include <TweenEngine/Tween.h>
  10. #define WIDTH 400
  11. #define HEIGHT 240
  12. #define EVENT_RECV 0
  13. #define EVENT_BUFFER_ERROR 1
  14. //#define EVENT_CANCEL 2
  15. #define EVENT_COUNT 2
  16. #define TEXTURE_TRANSFER_FLAGS \
  17. (GX_TRANSFER_RAW_COPY(0) | GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | \
  18. GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB565) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB565) | \
  19. GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO))
  20. namespace FreeShop {
  21. QrScannerState::QrScannerState(StateStack &stack, Context &context, StateCallback callback)
  22. : State(stack, context, callback)
  23. , m_camThread(&QrScannerState::startCamera, this)
  24. , m_capturing(false)
  25. , m_threadRunning(false)
  26. , m_requestedClose(false)
  27. , m_displayError(false)
  28. , m_hideQrBorder(false)
  29. {
  30. m_cameraScreen.setTexture(&m_cameraTexture);
  31. m_cameraScreen.setSize(cpp3ds::Vector2f(WIDTH, HEIGHT));
  32. m_cameraScreen.setTextureRect(cpp3ds::IntRect(0, 0, WIDTH, HEIGHT));
  33. if (Theme::isQrSelector9Themed) {
  34. cpp3ds::Texture &texture = AssetManager<cpp3ds::Texture>::get(FREESHOP_DIR "/theme/images/qr_selector.9.png");
  35. texture.setSmooth(false);
  36. m_qrBorder.setTexture(&texture);
  37. } else {
  38. cpp3ds::Texture &texture = AssetManager<cpp3ds::Texture>::get("images/qr_selector.9.png");
  39. texture.setSmooth(false);
  40. m_qrBorder.setTexture(&texture);
  41. }
  42. m_qrBorder.setContentSize(140.f, 140.f);
  43. m_qrBorder.setPosition(200.f, 120.f);
  44. m_qrBorder.setColor(cpp3ds::Color(255, 255, 255, 100));
  45. m_qrBorder.setOrigin(std::round(m_qrBorder.getSize().x * 0.5f), std::round(m_qrBorder.getSize().y * 0.5f));
  46. TweenEngine::Tween::to(m_qrBorder, m_qrBorder.SCALE_XY, 0.3f)
  47. .target(0.95f, 0.95f)
  48. .repeatYoyo(-1, 0.f)
  49. .start(m_tweenManager);
  50. m_bottomBackground.setSize(cpp3ds::Vector2f(320.f, 240.f));
  51. m_closeCaption.setString(_("\uE001 Cancel"));
  52. m_closeCaption.setFillColor(cpp3ds::Color(150, 150, 150));
  53. m_closeCaption.setCharacterSize(20);
  54. m_closeCaption.setPosition(160.f, 200.f);
  55. m_closeCaption.useSystemFont();
  56. m_closeCaption.setOrigin(m_closeCaption.getLocalBounds().width / 2,
  57. m_closeCaption.getLocalBounds().height / 2);
  58. m_textCloseError = m_closeCaption;
  59. m_textCloseError.setString(_("\uE000 Retry"));
  60. m_textCloseError.setPosition(160.f, 200.f);
  61. m_textError = m_closeCaption;
  62. m_textError.setPosition(160.f, 120.f);
  63. m_textError.setCharacterSize(16);
  64. m_qr = quirc_new();
  65. quirc_resize(m_qr, WIDTH, HEIGHT);
  66. m_camThread.setRelativePriority(4);
  67. m_camThread.launch();
  68. }
  69. QrScannerState::~QrScannerState()
  70. {
  71. while (m_threadRunning && !m_capturing)
  72. cpp3ds::sleep(cpp3ds::milliseconds(10));
  73. m_displayError = false;
  74. m_capturing = false;
  75. m_camThread.wait();
  76. quirc_destroy(m_qr);
  77. }
  78. void QrScannerState::renderTopScreen(cpp3ds::Window& window)
  79. {
  80. cpp3ds::Lock lock(m_mutexDraw);
  81. #ifdef _3DS
  82. if (m_textureBuffer.empty())
  83. return;
  84. u32 dimTexture = GX_BUFFER_DIM(512, 256);
  85. GSPGPU_FlushDataCache(&m_rawTextureBuffer[0], m_rawTextureBuffer.size());
  86. GX_DisplayTransfer((u32*)&m_rawTextureBuffer[0], dimTexture, (u32*)&m_textureBuffer[0], dimTexture, TEXTURE_TRANSFER_FLAGS);
  87. gspWaitForPPF();
  88. GSPGPU_FlushDataCache(&m_textureBuffer[0], m_textureBuffer.size());
  89. #endif
  90. window.draw(m_cameraScreen);
  91. if (!m_displayError && !m_hideQrBorder)
  92. window.draw(m_qrBorder);
  93. }
  94. void QrScannerState::renderBottomScreen(cpp3ds::Window& window)
  95. {
  96. window.draw(m_bottomBackground);
  97. if (m_displayError)
  98. {
  99. window.draw(m_textError);
  100. window.draw(m_textCloseError);
  101. }
  102. else
  103. window.draw(m_closeCaption);
  104. }
  105. bool QrScannerState::update(float delta)
  106. {
  107. m_tweenManager.update(delta);
  108. return false;
  109. }
  110. bool QrScannerState::processEvent(const cpp3ds::Event& event)
  111. {
  112. if (event.type == cpp3ds::Event::KeyPressed)
  113. {
  114. if (m_displayError)
  115. {
  116. if (event.key.code == cpp3ds::Keyboard::A)
  117. m_displayError = false;
  118. }
  119. else
  120. {
  121. if (event.key.code == cpp3ds::Keyboard::B)
  122. close();
  123. }
  124. }
  125. return false;
  126. }
  127. void QrScannerState::startCamera()
  128. {
  129. cpp3ds::Thread qrThread(&QrScannerState::scanQrCode, this);
  130. qrThread.setRelativePriority(1);
  131. qrThread.setStackSize(64 * 1024);
  132. m_threadRunning = true;
  133. #ifdef _3DS
  134. Handle events[EVENT_COUNT] = {0};
  135. // events[EVENT_CANCEL] = data->cancelEvent;
  136. std::vector<cpp3ds::Uint8> tmpCamBuffer;
  137. size_t qrCounter = 0;
  138. Result res = 0;
  139. int w = WIDTH, h = HEIGHT;
  140. u32 dimCam = GX_BUFFER_DIM(w, h);
  141. m_camBuffer.resize(w * h * sizeof(u16));
  142. tmpCamBuffer.resize(w * h * sizeof(u16));
  143. int npow2w = 512, npow2h = 256;
  144. m_textureBuffer.resize(npow2w * npow2h * sizeof(u16));
  145. m_rawTextureBuffer.resize(m_textureBuffer.size());
  146. m_cameraTexture.loadFromPreprocessedMemory(&m_textureBuffer[0], m_textureBuffer.size(), npow2w, npow2h, GPU_RGB565, false);
  147. if(R_SUCCEEDED(res = camInit())) {
  148. if(R_SUCCEEDED(res = CAMU_SetSize(SELECT_OUT1, SIZE_CTR_TOP_LCD, CONTEXT_A))
  149. && R_SUCCEEDED(res = CAMU_SetOutputFormat(SELECT_OUT1, OUTPUT_RGB_565, CONTEXT_A))
  150. && R_SUCCEEDED(res = CAMU_SetFrameRate(SELECT_OUT1, FRAME_RATE_30))
  151. && R_SUCCEEDED(res = CAMU_SetNoiseFilter(SELECT_OUT1, true))
  152. && R_SUCCEEDED(res = CAMU_SetAutoExposure(SELECT_OUT1, true))
  153. && R_SUCCEEDED(res = CAMU_SetAutoWhiteBalance(SELECT_OUT1, true))
  154. && R_SUCCEEDED(res = CAMU_Activate(SELECT_OUT1))) {
  155. u32 transferUnit = 0;
  156. if(R_SUCCEEDED(res = CAMU_GetBufferErrorInterruptEvent(&events[EVENT_BUFFER_ERROR], PORT_CAM1))
  157. && R_SUCCEEDED(res = CAMU_SetTrimming(PORT_CAM1, false))
  158. && R_SUCCEEDED(res = CAMU_GetMaxBytes(&transferUnit, w, h))
  159. && R_SUCCEEDED(res = CAMU_SetTransferBytes(PORT_CAM1, transferUnit, w, h))
  160. && R_SUCCEEDED(res = CAMU_ClearBuffer(PORT_CAM1))
  161. && R_SUCCEEDED(res = CAMU_SetReceiving(&events[EVENT_RECV], &tmpCamBuffer[0], PORT_CAM1, tmpCamBuffer.size(), (s16) transferUnit))
  162. && R_SUCCEEDED(res = CAMU_StartCapture(PORT_CAM1)))
  163. {
  164. m_capturing = true;
  165. qrThread.launch();
  166. while (m_capturing && R_SUCCEEDED(res))
  167. {
  168. // svcWaitSynchronization(task_get_pause_event(), U64_MAX);
  169. s32 index = 0;
  170. if(R_SUCCEEDED(res = svcWaitSynchronizationN(&index, events, EVENT_COUNT, false, U64_MAX))) {
  171. switch(index) {
  172. // case EVENT_CANCEL:
  173. // m_capturing = false;
  174. // break;
  175. case EVENT_RECV:
  176. svcCloseHandle(events[EVENT_RECV]);
  177. events[EVENT_RECV] = 0;
  178. {
  179. cpp3ds::Lock lock(m_camMutex);
  180. m_camBuffer = tmpCamBuffer;
  181. }
  182. {
  183. cpp3ds::Lock lock(m_mutexDraw);
  184. for (int i = 0; i < h; ++i)
  185. memcpy(&m_rawTextureBuffer[i * npow2w * 2], &tmpCamBuffer[i * w * 2], w * 2);
  186. }
  187. res = CAMU_SetReceiving(&events[EVENT_RECV], &tmpCamBuffer[0], PORT_CAM1, tmpCamBuffer.size(), (s16) transferUnit);
  188. break;
  189. case EVENT_BUFFER_ERROR:
  190. cpp3ds::err() << "CAM ERROR" << std::endl;
  191. svcCloseHandle(events[EVENT_RECV]);
  192. events[EVENT_RECV] = 0;
  193. if(R_SUCCEEDED(res = CAMU_ClearBuffer(PORT_CAM1))
  194. && R_SUCCEEDED(res = CAMU_SetReceiving(&events[EVENT_RECV], &tmpCamBuffer[0], PORT_CAM1, tmpCamBuffer.size(), (s16) transferUnit))) {
  195. res = CAMU_StartCapture(PORT_CAM1);
  196. }
  197. break;
  198. default:
  199. break;
  200. }
  201. }
  202. }
  203. CAMU_StopCapture(PORT_CAM1);
  204. bool busy = false;
  205. while (R_SUCCEEDED(CAMU_IsBusy(&busy, PORT_CAM1)) && busy)
  206. cpp3ds::sleep(cpp3ds::milliseconds(10));
  207. CAMU_ClearBuffer(PORT_CAM1);
  208. }
  209. CAMU_Activate(SELECT_NONE);
  210. }
  211. camExit();
  212. }
  213. #endif
  214. m_threadRunning = false;
  215. m_capturing = false;
  216. qrThread.wait();
  217. }
  218. void QrScannerState::scanQrCode()
  219. {
  220. int w, h;
  221. std::vector<cpp3ds::Uint8> camBuffer;
  222. while (m_capturing && !m_requestedClose)
  223. {
  224. {
  225. cpp3ds::Lock lock(m_camMutex);
  226. camBuffer = m_camBuffer;
  227. }
  228. cpp3ds::Uint16 *pixels = reinterpret_cast<cpp3ds::Uint16*>(&camBuffer[0]);
  229. uint8_t *buf = quirc_begin(m_qr, &w, &h);
  230. for (int x = 0; x < w; x++)
  231. for (int y = 0; y < h; y++)
  232. {
  233. cpp3ds::Uint16 px = pixels[y * WIDTH + x];
  234. buf[y * w + x] = (uint8_t) (((((px >> 11) & 0x1F) << 3) + (((px >> 5) & 0x3F) << 2) + ((px & 0x1F) << 3)) / 3);
  235. }
  236. quirc_end(m_qr);
  237. int count = quirc_count(m_qr);
  238. for (int i = 0; i < count; i++)
  239. {
  240. struct quirc_code code;
  241. struct quirc_data data;
  242. quirc_extract(m_qr, i, &code);
  243. if (!quirc_decode(&code, &data))
  244. {
  245. // Return QR's decoded text to callback, outputs error string when true
  246. cpp3ds::String text(reinterpret_cast<char*>(data.payload));
  247. m_hideQrBorder = true;
  248. TweenEngine::Tween::to(m_cameraScreen, m_cameraScreen.FILL_COLOR_ALPHA, 0.4f)
  249. .target(100.f)
  250. .start(m_tweenManager);
  251. if (runCallback(&text))
  252. close();
  253. else
  254. {
  255. m_textError.setString(text);
  256. m_textError.setOrigin(m_textError.getLocalBounds().width / 2,
  257. m_textError.getLocalBounds().height / 2);
  258. m_displayError = true;
  259. }
  260. m_hideQrBorder = false;
  261. }
  262. }
  263. while (m_displayError)
  264. cpp3ds::sleep(cpp3ds::milliseconds(100));
  265. TweenEngine::Tween::to(m_cameraScreen, m_cameraScreen.FILL_COLOR_ALPHA, 0.4f)
  266. .target(255.f)
  267. .start(m_tweenManager);
  268. }
  269. }
  270. void QrScannerState::close()
  271. {
  272. cpp3ds::Lock lock(m_mutexRequest);
  273. if (!m_requestedClose)
  274. {
  275. m_requestedClose = true;
  276. requestStackPop();
  277. }
  278. }
  279. } // namespace FreeShop