Initial commit
This commit is contained in:
		| @@ -0,0 +1,29 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <platform/driver/touch/SDL2TouchController.hpp> | ||||
| #include <platform/hal/simulator/sdl2/HALSDL2.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void SDL2TouchController::init() | ||||
| { | ||||
| } | ||||
|  | ||||
| bool SDL2TouchController::sampleTouch(int32_t& x, int32_t& y) | ||||
| { | ||||
|     return static_cast<HALSDL2*>(HAL::getInstance())->doSampleTouch(x, y); //lint !e1774 | ||||
| } | ||||
| } // namespace touchgfx | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1,55 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <platform/hal/simulator/sdl2/HALSDL2.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| uint16_t HALSDL2::icon[] = | ||||
| { | ||||
|     0x0000, 0x0AC1, 0x0AC9, 0x0ACD, 0x0ACE, 0x0ACE, 0x0ACE, 0x0ACE, 0x0ACE, 0x0ACE, 0x0ACE, 0x0ACE, 0x0ACE, 0x0ACE, 0x0ACE, 0x0ACE, 0x0ACE, 0x0ACE, 0x0ACE, 0x0ACE, 0x0ACE, 0x0ACE, 0x0ACE, 0x0ACE, 0x0ACE, 0x0ACE, 0x0ACE, 0x0ACE, 0x0ACD, 0x0AC9, 0x0AC1, 0x0000, | ||||
|     0x0AC1, 0x0ACE, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACE, 0x0AC1, | ||||
|     0x0AC9, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x09C9, | ||||
|     0x0ACD, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACD, | ||||
|     0x0ACE, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x6CDF, 0x7CEF, 0x7CEF, 0x7CEF, 0x5CDF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x5CDF, 0x7CEF, 0x7CEF, 0x7CEF, 0x6CDF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACD, | ||||
|     0x0ACE, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0xEFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x5BDF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x4BDF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xEFFF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x09CD, | ||||
|     0x09CE, 0x09CF, 0x09CF, 0x09CF, 0x0ACF, 0x0ACF, 0x09CF, 0x6CDF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xDFFF, 0x0ACF, 0x0ACF, 0x0ACF, 0x0ACF, 0x09CF, 0x09CF, 0xCEFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x7CEF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CD, | ||||
|     0x09CE, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x0ACF, 0xDEFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x6CDF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x5BDF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xEFFF, 0x0ACF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CD, | ||||
|     0x09CE, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x5BDF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xDFFF, 0x09CF, 0x09CF, 0x09CF, 0x0ACF, 0xDFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x6CDF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CD, | ||||
|     0x09CE, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0xCEFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xBEEF, 0x09CF, 0x09CF, 0x09CF, 0x6CDF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xDEFF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CD, | ||||
|     0x0ACE, 0x09CF, 0x09CF, 0x0ACF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x3BDF, 0xFFFF, 0xFFFF, 0xFFFF, 0x3ACF, 0x09CF, 0x09CF, 0x0ACF, 0xEFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x4BDF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CD, | ||||
|     0x09CE, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0xBEEF, 0xFFFF, 0xADEF, 0x09CF, 0x09CF, 0x09CF, 0x7CDF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xCEFF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CD, | ||||
|     0x09CE, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x2ACF, 0xFFFF, 0x2ACF, 0x09CF, 0x09CF, 0x1ACF, 0xEFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x3BCF, 0x09CF, 0x09BF, 0x09BF, 0x09BF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CF, 0x09CD, | ||||
|     0x09CE, 0x09BF, 0x09BF, 0x09CF, 0x09CF, 0x09CF, 0x09BF, 0x09CF, 0x09BF, 0x09BF, 0x09CF, 0x09CF, 0x09BF, 0x09BF, 0x09BF, 0x8CEF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xBDEF, 0x09CF, 0x09BF, 0x09BF, 0x09CF, 0x09CF, 0x09BF, 0x09CF, 0x09BF, 0x09BF, 0x09BF, 0x09CD, | ||||
|     0x09CE, 0x09BF, 0x09BF, 0x09BF, 0x09CF, 0x09CF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x1ACF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2ACF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09CF, 0x09CF, 0x09BD, | ||||
|     0x09BE, 0x09BF, 0x09CF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x9DEF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xADEF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BD, | ||||
|     0x09BE, 0x09CF, 0x09CF, 0x09CF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x8CDF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xBEEF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BD, | ||||
|     0x09BE, 0x09BF, 0x09BF, 0x09BF, 0x09CF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x19BF, 0xEFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x4BCF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BD, | ||||
|     0x09BE, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x3ACF, 0x09BF, 0x09BF, 0x09BF, 0x6CDF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xCEFF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BD, | ||||
|     0x09BE, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x4BCF, 0xFFFF, 0x3ACF, 0x09BF, 0x09BF, 0x09BF, 0xDEFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x6BCF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BD, | ||||
|     0x09BE, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x08BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0xDEFF, 0xFFFF, 0xCEEF, 0x09BF, 0x09BF, 0x09BF, 0x4BCF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xEFFF, 0x09BF, 0x08BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BD, | ||||
|     0x09BE, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x08BF, 0x09BF, 0x09BF, 0x6BDF, 0xFFFF, 0xFFFF, 0xFFFF, 0x5BCF, 0x09BF, 0x09BF, 0x09BF, 0xBEEF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x7CDF, 0x09BF, 0x09BF, 0x08BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x09BD, | ||||
|     0x09BE, 0x09BF, 0x09BF, 0x09BF, 0x09BF, 0x08BF, 0x08BF, 0x09BF, 0x19BF, 0xEFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xDEFF, 0x09BF, 0x09BF, 0x09BF, 0x3ACF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x19BF, 0x08BF, 0x08BF, 0x08BF, 0x09BF, 0x08BF, 0x09BF, 0x09BF, 0x09BD, | ||||
|     0x08BE, 0x08BF, 0x08BF, 0x08BF, 0x08BF, 0x08BF, 0x09BF, 0x09BF, 0x8CDF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xADEF, 0x08BF, 0x08BF, 0x09BF, 0x08BF, 0x9DEF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x9DEF, 0x08AF, 0x08AF, 0x08BF, 0x08BF, 0x08BF, 0x08BF, 0x08AF, 0x08AD, | ||||
|     0x08AE, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x29BF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x29BF, 0x08AF, 0x09BF, 0x08AF, 0x08AF, 0x19BF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x3ABF, 0x08BF, 0x08AF, 0x08AF, 0x08BF, 0x08AF, 0x08AF, 0x09BD, | ||||
|     0x09BE, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0xADEF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x9CDF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x8CDF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xBEEF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AD, | ||||
|     0x09BE, 0x09BF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x29BF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xEFFF, 0x19BF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08BF, 0x08AF, 0xEFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x3ABF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AD, | ||||
|     0x09BE, 0x09BF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x29BF, 0x39BF, 0x39BF, 0x39BF, 0x19BF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08BF, 0x18AF, 0x3ABF, 0x3ABF, 0x39BF, 0x2ABF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AD, | ||||
|     0x09BD, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AD, | ||||
|     0x08A9, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08A9, | ||||
|     0x08A1, 0x08AE, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x089F, 0x089F, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x08AF, 0x079F, 0x079E, 0x0791, | ||||
|     0x0000, 0x07A1, 0x08A9, 0x08AD, 0x09BD, 0x08AD, 0x08AD, 0x079D, 0x079D, 0x079D, 0x079D, 0x08AD, 0x08AD, 0x08AD, 0x08AD, 0x089D, 0x079D, 0x079D, 0x08AD, 0x08AD, 0x08AD, 0x08AD, 0x08AD, 0x079D, 0x079D, 0x079D, 0x08AD, 0x08AD, 0x08AD, 0x0799, 0x0791, 0x0000 | ||||
| }; | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,50 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/hal/OSWrappers.hpp> | ||||
| #include <SDL2/SDL.h> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| static SDL_mutex* s_FrameBufferLock; | ||||
|  | ||||
| void OSWrappers::initialize() | ||||
| { | ||||
|     // Setup framebuffer lock | ||||
|     s_FrameBufferLock = SDL_CreateMutex(); | ||||
| } | ||||
|  | ||||
| void OSWrappers::takeFrameBufferSemaphore() | ||||
| { | ||||
|     SDL_LockMutex(s_FrameBufferLock); | ||||
| } | ||||
|  | ||||
| void OSWrappers::giveFrameBufferSemaphore() | ||||
| { | ||||
|     SDL_UnlockMutex(s_FrameBufferLock); | ||||
| } | ||||
|  | ||||
| void OSWrappers::waitForVSync() | ||||
| { | ||||
| } | ||||
|  | ||||
| void OSWrappers::tryTakeFrameBufferSemaphore() | ||||
| { | ||||
| } | ||||
|  | ||||
| void OSWrappers::giveFrameBufferSemaphoreFromISR() | ||||
| { | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,112 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/CacheableContainer.hpp> | ||||
| #include <touchgfx/hal/HAL.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| CacheableContainer::CacheableContainer() | ||||
|     : Container(), | ||||
|       cachedBitmapId(BITMAP_INVALID), | ||||
|       cachedImage(), | ||||
|       isCachedMode(false), | ||||
|       childWasInvalidated(false) | ||||
| { | ||||
| } | ||||
|  | ||||
| void CacheableContainer::setCacheBitmap(BitmapId bitmapId) | ||||
| { | ||||
|     Bitmap bitmap(bitmapId); | ||||
|  | ||||
|     cachedBitmapId = BITMAP_INVALID; | ||||
|  | ||||
|     /* Accept only dynamic bitmaps */ | ||||
|     if (!Bitmap::dynamicBitmapGetAddress(bitmapId)) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* Retrieve the auxiliary LCD implementation attached to HA */ | ||||
|     LCD* auxLCD = HAL::getInstance()->getAuxiliaryLCD(); | ||||
|  | ||||
|     /* Dynamic bitmap and framebuffer should be of the same format */ | ||||
|     if (bitmap.getFormat() == HAL::lcd().framebufferFormat() | ||||
|             || (auxLCD && bitmap.getFormat() == auxLCD->framebufferFormat())) | ||||
|     { | ||||
|         cachedBitmapId = bitmapId; | ||||
|         cachedImage.setBitmap(Bitmap(cachedBitmapId)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| BitmapId CacheableContainer::getCacheBitmap() const | ||||
| { | ||||
|     return cachedBitmapId; | ||||
| } | ||||
|  | ||||
| void CacheableContainer::updateCache() | ||||
| { | ||||
|     updateCache(Rect()); | ||||
| } | ||||
|  | ||||
| void CacheableContainer::updateCache(const Rect& invalidatedArea) | ||||
| { | ||||
|     if (isCachedMode && (cachedBitmapId != BITMAP_INVALID)) | ||||
|     { | ||||
|         /* will call Container::draw(invalidatedArea) to render all widgets into the dynamic bitmap */ | ||||
|         HAL::getInstance()->drawDrawableInDynamicBitmap(*this, cachedBitmapId, invalidatedArea); | ||||
|         childWasInvalidated = false; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void CacheableContainer::enableCachedMode(bool enable) | ||||
| { | ||||
|     isCachedMode = enable; | ||||
| } | ||||
|  | ||||
| void CacheableContainer::invalidateRect(Rect& invalidatedArea) const | ||||
| { | ||||
|     Container::invalidateRect(invalidatedArea); | ||||
|  | ||||
|     if (isCachedMode && (cachedBitmapId != BITMAP_INVALID)) | ||||
|     { | ||||
|         const_cast<CacheableContainer*>(this)->childWasInvalidated = true; | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool CacheableContainer::setSolidRect(const Rect& rect) | ||||
| { | ||||
|     return Bitmap::dynamicBitmapSetSolidRect(cachedBitmapId, rect); | ||||
| } | ||||
|  | ||||
| bool CacheableContainer::isChildInvalidated() const | ||||
| { | ||||
|     return childWasInvalidated; | ||||
| } | ||||
|  | ||||
| void CacheableContainer::setupDrawChain(const Rect& invalidatedArea, Drawable** nextPreviousElement) | ||||
| { | ||||
|     if (isCachedMode && (cachedBitmapId != BITMAP_INVALID) && isVisible()) | ||||
|     { | ||||
|         Rect r = getAbsoluteRect(); | ||||
|         cachedImage.setPosition(r.x, r.y, r.width, r.height); | ||||
|         cachedImage.setupDrawChain(invalidatedArea, nextPreviousElement); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         Container::setupDrawChain(invalidatedArea, nextPreviousElement); | ||||
|     } | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,276 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/Container.hpp> | ||||
| #include <touchgfx/Screen.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| bool Container::contains(const Drawable& d) | ||||
| { | ||||
|     bool found = false; | ||||
|     Drawable* head = firstChild; | ||||
|     while (head && !found) | ||||
|     { | ||||
|         found = (head == &d); | ||||
|         head = head->nextSibling; | ||||
|     } | ||||
|     return found; | ||||
| } | ||||
|  | ||||
| void Container::add(Drawable& d) | ||||
| { | ||||
|     assert(&d != this && "Cannot add Drawable to self"); | ||||
|     assert(d.parent == 0 && "Cannot add Drawable multiple times"); | ||||
|  | ||||
|     // Initialize d to have this as parent and no sibling. | ||||
|     d.parent = this; | ||||
|     d.nextSibling = 0; | ||||
|  | ||||
|     // Check if d is the first child to be added (container is empty) | ||||
|     if (!firstChild) | ||||
|     { | ||||
|         firstChild = &d; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         Drawable* head = firstChild; | ||||
|         // Skip to end of currently added children | ||||
|         while (head->nextSibling) | ||||
|         { | ||||
|             assert(head != &d && "Cannot add Drawable multiple times"); | ||||
|             head = head->nextSibling; | ||||
|         } | ||||
|         assert(head != &d && "Cannot add Drawable multiple times"); | ||||
|         // Make last child now point to d. | ||||
|         head->nextSibling = &d; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void Container::remove(Drawable& d) | ||||
| { | ||||
|     if (!firstChild) | ||||
|     { | ||||
|         // No children | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (&d == firstChild) | ||||
|     { | ||||
|         // d is first child. | ||||
|         d.parent = 0; | ||||
|         if (!d.nextSibling) | ||||
|         { | ||||
|             // d was only child, so now this container is empty | ||||
|             firstChild = 0; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             firstChild = d.nextSibling; | ||||
|             d.nextSibling = 0; | ||||
|         } | ||||
|         return; | ||||
|     } | ||||
|     Drawable* tmp = firstChild; | ||||
|  | ||||
|     while (tmp) | ||||
|     { | ||||
|         if (tmp->nextSibling == &d) | ||||
|         { | ||||
|             tmp->nextSibling = d.nextSibling; | ||||
|             d.parent = 0; | ||||
|             d.nextSibling = 0; | ||||
|             return; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             tmp = tmp->nextSibling; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void Container::removeAll() | ||||
| { | ||||
|     while (firstChild) | ||||
|     { | ||||
|         Drawable* d = firstChild; | ||||
|         firstChild = firstChild->nextSibling; | ||||
|         d->parent = 0; | ||||
|         d->nextSibling = 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void Container::unlink() | ||||
| { | ||||
|     firstChild = 0; | ||||
| } | ||||
|  | ||||
| void Container::draw(const Rect& invalidatedArea) const | ||||
| { | ||||
|     // The draw function of Container is not normally used. Containers do not per default | ||||
|     // appear in the draw chain, since they are normally invisible themselves. However, | ||||
|     // if someone decides to call draw on a container, at least do something useful (draw children). | ||||
|     if (!isVisible() || !firstChild) | ||||
|     { | ||||
|         // Nothing to draw | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Rect tmp = invalidatedArea; | ||||
|     Drawable* d = firstChild; | ||||
|     while (d) | ||||
|     { | ||||
|         if (d->isVisible()) | ||||
|         { | ||||
|             Rect drawableRegion = tmp & d->getRect(); | ||||
|             if (!drawableRegion.isEmpty()) | ||||
|             { | ||||
|                 // This child has a non-empty intersection with the invalidated area. | ||||
|                 // Convert region to the Drawable's coordinate system and draw. | ||||
|                 drawableRegion.x -= d->getX(); | ||||
|                 drawableRegion.y -= d->getY(); | ||||
|                 d->draw(drawableRegion); | ||||
|             } | ||||
|         } | ||||
|         d = d->nextSibling; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void Container::getLastChild(int16_t x, int16_t y, Drawable** last) | ||||
| { | ||||
|     // This function is used to obtain the drawable that should receive a click/drag/gesture event. | ||||
|     // Find the last child (ie. the last child that was added, ie. the "front-most" drawable) covering | ||||
|     // the specified coords. | ||||
|     if (isTouchable()) | ||||
|     { | ||||
|         // If the container itself is touchable, result so far is "this". Might be overridden by a child. | ||||
|         *last = this; | ||||
|     } | ||||
|  | ||||
|     Drawable* d = firstChild; | ||||
|     while (d) | ||||
|     { | ||||
|         // Iterate over children. | ||||
|         if (d->isVisible() && d->getRect().intersect(x, y)) | ||||
|         { | ||||
|             int16_t xadj = x - d->getX(); | ||||
|             int16_t yadj = y - d->getY(); | ||||
|             d->getLastChild(xadj, yadj, last); | ||||
|         } | ||||
|         d = d->nextSibling; | ||||
|     } | ||||
| } | ||||
|  | ||||
| Rect Container::getSolidRect() const | ||||
| { | ||||
|     return Rect(0, 0, 0, 0); | ||||
| } | ||||
|  | ||||
| Rect Container::getContainedArea() const | ||||
| { | ||||
|     Drawable* d = firstChild; | ||||
|     Rect contained(0, 0, 0, 0); | ||||
|     while (d) | ||||
|     { | ||||
|         contained.expandToFit(d->getRect()); | ||||
|         d = d->nextSibling; | ||||
|     } | ||||
|     return contained; | ||||
| } | ||||
|  | ||||
| void Container::moveChildrenRelative(int16_t deltaX, int16_t deltaY) | ||||
| { | ||||
|     Drawable* d = firstChild; | ||||
|     while (d) | ||||
|     { | ||||
|         d->moveRelative(deltaX, deltaY); | ||||
|         d = d->nextSibling; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void Container::forEachChild(GenericCallback<Drawable&>* function) | ||||
| { | ||||
|     Drawable* d = firstChild; | ||||
|     while (d) | ||||
|     { | ||||
|         function->execute(*d); | ||||
|         d = d->nextSibling; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void Container::insert(Drawable* previous, Drawable& d) | ||||
| { | ||||
|     if (!firstChild) | ||||
|     { | ||||
|         // Insert as only element | ||||
|         add(d); | ||||
|         return; | ||||
|     } | ||||
|     else if (!previous) | ||||
|     { | ||||
|         // Insert as head element | ||||
|         d.nextSibling = firstChild; | ||||
|         firstChild = &d; | ||||
|         d.parent = this; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         Drawable* tmp = firstChild; | ||||
|         while (tmp) | ||||
|         { | ||||
|             if (tmp == previous) | ||||
|             { | ||||
|                 d.nextSibling = tmp->nextSibling; | ||||
|                 tmp->nextSibling = &d; | ||||
|                 d.parent = this; | ||||
|                 return; | ||||
|             } | ||||
|             tmp = tmp->nextSibling; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void Container::setupDrawChain(const Rect& invalidatedArea, Drawable** nextPreviousElement) | ||||
| { | ||||
|     // This function adds the children of this container to the list of drawables to draw. | ||||
|     if (!isVisible()) | ||||
|     { | ||||
|         // If this container itself is not visible, do not add anyone to draw chain. | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!firstChild) | ||||
|     { | ||||
|         // If this container is empty, do not add anyone. | ||||
|         return; | ||||
|     } | ||||
|     Drawable* d = firstChild; | ||||
|     while (d) | ||||
|     { | ||||
|         if (d->isVisible()) | ||||
|         { | ||||
|             // Only drawables intersecting with the specified invalidated area will be added. | ||||
|             Rect drawableRegion = invalidatedArea & d->getRect(); | ||||
|             if (!drawableRegion.isEmpty()) | ||||
|             { | ||||
|                 drawableRegion.x -= d->getX(); | ||||
|                 drawableRegion.y -= d->getY(); | ||||
|                 d->setupDrawChain(drawableRegion, nextPreviousElement); | ||||
|             } | ||||
|         } | ||||
|         d = d->nextSibling; | ||||
|     } | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,248 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/ListLayout.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| class AdjustElements | ||||
| { | ||||
| public: | ||||
|     AdjustElements(Drawable* d, Direction dir) | ||||
|         : insertedCoord(0), | ||||
|           newElementPassed(false), | ||||
|           newElement(d), | ||||
|           direction(dir) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     void handleInsert(Drawable& d) | ||||
|     { | ||||
|         if (!newElementPassed) | ||||
|         { | ||||
|             if (newElement == &d) | ||||
|             { | ||||
|                 newElementPassed = true; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (direction == SOUTH) | ||||
|                 { | ||||
|                     insertedCoord = d.getY() + d.getHeight(); | ||||
|                 } | ||||
|                 else if (direction == EAST) | ||||
|                 { | ||||
|                     insertedCoord = d.getX() + d.getWidth(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (direction == SOUTH) | ||||
|             { | ||||
|                 d.setY(d.getY() + newElement->getHeight()); | ||||
|             } | ||||
|             else if (direction == EAST) | ||||
|             { | ||||
|                 d.setX(d.getX() + newElement->getWidth()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void handleRemove(Drawable& d) | ||||
|     { | ||||
|         if (!newElementPassed) | ||||
|         { | ||||
|             if (newElement == &d) | ||||
|             { | ||||
|                 newElementPassed = true; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (direction == SOUTH) | ||||
|             { | ||||
|                 d.setY(d.getY() - newElement->getHeight()); | ||||
|             } | ||||
|             else if (direction == EAST) | ||||
|             { | ||||
|                 d.setX(d.getX() - newElement->getWidth()); | ||||
|             } | ||||
|         } | ||||
|         if (newElement != &d) | ||||
|         { | ||||
|             if (direction == SOUTH) | ||||
|             { | ||||
|                 if (d.getWidth() > insertedCoord) | ||||
|                 { | ||||
|                     insertedCoord = d.getWidth(); | ||||
|                 } | ||||
|             } | ||||
|             else if (direction == EAST) | ||||
|             { | ||||
|                 if (d.getHeight() > insertedCoord) | ||||
|                 { | ||||
|                     insertedCoord = d.getHeight(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     int16_t insertedCoord; | ||||
|     bool newElementPassed; | ||||
|  | ||||
| private: | ||||
|     Drawable* newElement; | ||||
|     Direction direction; | ||||
| }; //lint !e1712 | ||||
|  | ||||
| void ListLayout::internalAddElementAt(Drawable& d, int16_t coord) | ||||
| { | ||||
|     switch (direction) | ||||
|     { | ||||
|     case SOUTH: | ||||
|         if (rect.width < d.getWidth()) | ||||
|         { | ||||
|             rect.width = d.getWidth(); | ||||
|         } | ||||
|         rect.height += d.getHeight(); | ||||
|         d.setXY(0, coord); | ||||
|         offset += d.getHeight(); | ||||
|         break; | ||||
|     case EAST: | ||||
|         if (rect.height < d.getHeight()) | ||||
|         { | ||||
|             rect.height = d.getHeight(); | ||||
|         } | ||||
|         rect.width += d.getWidth(); | ||||
|         d.setXY(coord, 0); | ||||
|         offset += d.getWidth(); | ||||
|         break; | ||||
|     case NORTH: | ||||
|     case WEST: | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ListLayout::internalAddElement(Drawable& d) | ||||
| { | ||||
|     internalAddElementAt(d, offset); | ||||
| } | ||||
|  | ||||
| void ListLayout::internalRemoveElement(Drawable& d, int16_t coord) | ||||
| { | ||||
|     switch (direction) | ||||
|     { | ||||
|     case SOUTH: | ||||
|         if (rect.width > coord) | ||||
|         { | ||||
|             rect.width = coord; | ||||
|         } | ||||
|         rect.height -= d.getHeight(); | ||||
|         d.setX(0); | ||||
|         d.setY(0); | ||||
|         offset -= d.getHeight(); | ||||
|         break; | ||||
|     case EAST: | ||||
|         if (rect.height > coord) | ||||
|         { | ||||
|             rect.height = coord; | ||||
|         } | ||||
|         rect.width -= d.getWidth(); | ||||
|         d.setX(0); | ||||
|         d.setY(0); | ||||
|         offset -= d.getWidth(); | ||||
|         break; | ||||
|     case NORTH: | ||||
|     case WEST: | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ListLayout::setDirection(const Direction d) | ||||
| { | ||||
|     assert((d == SOUTH || d == EAST) && "Chosen direction not supported"); | ||||
|     if (direction != d) | ||||
|     { | ||||
|         direction = d; | ||||
|         offset = 0; | ||||
|         setWidthHeight(0, 0); | ||||
|         Callback<ListLayout, Drawable&> function(this, &ListLayout::internalAddElement); | ||||
|         forEachChild(&function); | ||||
|         if (parent) | ||||
|         { | ||||
|             parent->childGeometryChanged(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ListLayout::add(Drawable& d) | ||||
| { | ||||
|     internalAddElement(d); | ||||
|     Container::add(d); | ||||
|     if (parent) | ||||
|     { | ||||
|         parent->childGeometryChanged(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ListLayout::removeAll() | ||||
| { | ||||
|     offset = 0; | ||||
|     setWidthHeight(0, 0); | ||||
|     Container::removeAll(); | ||||
|     if (parent) | ||||
|     { | ||||
|         parent->childGeometryChanged(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ListLayout::insert(Drawable* previous, Drawable& d) | ||||
| { | ||||
|     if (!firstChild) | ||||
|     { | ||||
|         // List is empty, just add the new entry | ||||
|         add(d); | ||||
|         return; | ||||
|     } | ||||
|     Container::insert(previous, d); | ||||
|     AdjustElements tmp(&d, direction); | ||||
|     Callback<AdjustElements, Drawable&> function(&tmp, &AdjustElements::handleInsert); | ||||
|     forEachChild(&function); | ||||
|     internalAddElementAt(d, tmp.insertedCoord); | ||||
|     if (parent) | ||||
|     { | ||||
|         parent->childGeometryChanged(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ListLayout::remove(Drawable& d) | ||||
| { | ||||
|     AdjustElements tmp(&d, direction); | ||||
|     Callback<AdjustElements, Drawable&> function(&tmp, &AdjustElements::handleRemove); | ||||
|     forEachChild(&function); | ||||
|     if (tmp.newElementPassed) | ||||
|     { | ||||
|         internalRemoveElement(d, tmp.insertedCoord); | ||||
|         Container::remove(d); | ||||
|     } | ||||
|     if (parent) | ||||
|     { | ||||
|         parent->childGeometryChanged(); | ||||
|     } | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,107 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/ModalWindow.hpp> | ||||
| #include <touchgfx/Color.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| ModalWindow::ModalWindow() | ||||
|     : Container() | ||||
| { | ||||
|     ModalWindow::setWidthHeight(HAL::DISPLAY_WIDTH, HAL::DISPLAY_HEIGHT); | ||||
|  | ||||
|     int defaultShadeAlpha = 96; | ||||
|     colortype defaultShadeColor = Color::getColorFrom24BitRGB(0x0, 0x0, 0x0); | ||||
|  | ||||
|     backgroundShade.setPosition(0, 0, getWidth(), getHeight()); | ||||
|     backgroundShade.setColor(defaultShadeColor); | ||||
|     backgroundShade.setTouchable(true); | ||||
|     ModalWindow::setShadeAlpha(defaultShadeAlpha); | ||||
|     Container::add(backgroundShade); | ||||
|  | ||||
|     Container::add(windowContainer); | ||||
|     windowContainer.add(windowBackground); | ||||
| } | ||||
|  | ||||
| void ModalWindow::setBackground(const BitmapId& bmpId) | ||||
| { | ||||
|     windowBackground.setBitmap(Bitmap(bmpId)); | ||||
|     windowBackground.setXY(0, 0); | ||||
|  | ||||
|     windowContainer.setPosition((getWidth() - windowBackground.getWidth()) / 2, (getHeight() - windowBackground.getHeight()) / 2, windowBackground.getWidth(), windowBackground.getHeight()); | ||||
|     invalidate(); | ||||
| } | ||||
|  | ||||
| void ModalWindow::setBackground(const BitmapId& bmpId, int16_t backgroundX, int16_t backgroundY) | ||||
| { | ||||
|     setBackground(bmpId); | ||||
|     windowContainer.setXY(backgroundX, backgroundY); | ||||
| } | ||||
|  | ||||
| uint16_t ModalWindow::getBackgroundWidth() const | ||||
| { | ||||
|     return windowBackground.getWidth(); | ||||
| } | ||||
|  | ||||
| uint16_t ModalWindow::getBackgroundHeight() const | ||||
| { | ||||
|     return windowBackground.getHeight(); | ||||
| } | ||||
|  | ||||
| void ModalWindow::add(Drawable& d) | ||||
| { | ||||
|     windowContainer.add(d); | ||||
| } | ||||
|  | ||||
| void ModalWindow::remove(Drawable& d) | ||||
| { | ||||
|     windowContainer.remove(d); | ||||
| } | ||||
|  | ||||
| void ModalWindow::setShadeAlpha(uint8_t alpha) | ||||
| { | ||||
|     backgroundShade.setAlpha(alpha); | ||||
|     backgroundShade.invalidate(); | ||||
| } | ||||
|  | ||||
| uint8_t ModalWindow::getShadeAlpha() const | ||||
| { | ||||
|     return backgroundShade.getAlpha(); | ||||
| } | ||||
|  | ||||
| void ModalWindow::setShadeColor(colortype color) | ||||
| { | ||||
|     backgroundShade.setColor(color); | ||||
|     backgroundShade.invalidate(); | ||||
| } | ||||
|  | ||||
| colortype ModalWindow::getShadeColor() const | ||||
| { | ||||
|     return backgroundShade.getColor(); | ||||
| } | ||||
|  | ||||
| void ModalWindow::show() | ||||
| { | ||||
|     setVisible(true); | ||||
|     invalidate(); | ||||
| } | ||||
|  | ||||
| void ModalWindow::hide() | ||||
| { | ||||
|     setVisible(false); | ||||
|     invalidate(); | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,730 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/ScrollableContainer.hpp> | ||||
| #include <touchgfx/EasingEquations.hpp> | ||||
| #include <touchgfx/Color.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| ScrollableContainer::ScrollableContainer() | ||||
|     : Container(), | ||||
|       scrollbarPadding(0), | ||||
|       scrollbarWidth(2), | ||||
|       scrollbarAlpha(120), | ||||
|       scrollbarColor(Color::getColorFrom24BitRGB(0xFF, 0xFF, 0xFF)), | ||||
|       accelDirection(GestureEvent::SWIPE_HORIZONTAL), | ||||
|       xSlider(0, 0, scrollbarColor, scrollbarAlpha), | ||||
|       ySlider(0, 0, scrollbarColor, scrollbarAlpha), | ||||
|       pressedDrawable(0), | ||||
|       lastDraggableChild(0), | ||||
|       scrolledXDistance(0), | ||||
|       scrolledYDistance(0), | ||||
|       scrollThreshold(5), | ||||
|       pressedX(0), | ||||
|       pressedY(0), | ||||
|       isPressed(false), | ||||
|       isScrolling(false), | ||||
|       scrollableX(true), | ||||
|       scrollableY(true), | ||||
|       scrollbarsVisible(true), | ||||
|       scrollbarsPermanentlyVisible(false), | ||||
|       scrollDuration(0), | ||||
|       beginningValue(0), | ||||
|       targetValue(0), | ||||
|       animationCounter(0), | ||||
|       animate(false), | ||||
|       fingerAdjustmentX(0), | ||||
|       fingerAdjustmentY(0), | ||||
|       hasIssuedCancelEvent(false), | ||||
|       scrollDurationSpeedup(7), | ||||
|       scrollDurationSlowdown(1) | ||||
| { | ||||
|     xSlider.setVisible(false); | ||||
|     ySlider.setVisible(false); | ||||
|     maxVelocity = SCROLLBAR_MAX_VELOCITY; | ||||
|     setTouchable(true); | ||||
| } | ||||
|  | ||||
| void ScrollableContainer::handleClickEvent(const ClickEvent& evt) | ||||
| { | ||||
|     if (evt.getType() == ClickEvent::PRESSED) | ||||
|     { | ||||
|         isPressed = true; | ||||
|  | ||||
|         if (animate) | ||||
|         { | ||||
|             // Stop scroll animation | ||||
|             animate = false; | ||||
|             animationCounter = 0; | ||||
|             Application::getInstance()->unregisterTimerWidget(this); | ||||
|         } | ||||
|  | ||||
|         const int fingerSize = HAL::getInstance()->getFingerSize(); | ||||
|         fingerAdjustmentX = 0; | ||||
|         fingerAdjustmentY = 0; | ||||
|  | ||||
|         const int minimumDistance = 3; | ||||
|         if ((fingerSize - 1) >= minimumDistance) | ||||
|         { | ||||
|             pressedDrawable = 0; | ||||
|  | ||||
|             const int maximumSquares = 3; | ||||
|             const int numberOfSquares = MIN(maximumSquares, (fingerSize - 1) / minimumDistance); | ||||
|             uint32_t bestDistance = 0xFFFFFFFF; | ||||
|             Drawable* last = 0; | ||||
|  | ||||
|             Rect me(0, 0, getWidth(), getHeight()); | ||||
|             Rect meAbs = me; | ||||
|             translateRectToAbsolute(meAbs); | ||||
|  | ||||
|             for (int squareNumber = 1; squareNumber <= numberOfSquares; squareNumber++) | ||||
|             { | ||||
|                 int distance = ((squareNumber * fingerSize) / numberOfSquares); | ||||
|                 const int samplePoints[10][2] = { { 0, 0 }, { -1, -1 }, { 0, -1 }, { 1, -1 }, { -1, 0 }, { 0, 0 }, { 1, 0 }, { -1, 1 }, { 0, 1 }, { 1, 1 } }; | ||||
|  | ||||
|                 for (int sampleIndex = squareNumber - 1; sampleIndex < 10; sampleIndex += 2) | ||||
|                 { | ||||
|                     Drawable* d = 0; | ||||
|                     int16_t deltaX = samplePoints[sampleIndex][0] * distance; | ||||
|                     int16_t deltaY = samplePoints[sampleIndex][1] * distance; | ||||
|                     if (me.intersect(evt.getX() + deltaX, evt.getY() + deltaY)) | ||||
|                     { | ||||
|                         Container::getLastChild(evt.getX() + deltaX, evt.getY() + deltaY, &d); | ||||
|                         if (d && d != last && d != this) | ||||
|                         { | ||||
|                             Rect absRect = d->getAbsoluteRect(); | ||||
|                             int x = meAbs.x + evt.getX() - (absRect.x + (absRect.width / 2)); | ||||
|                             int y = meAbs.y + evt.getY() - (absRect.y + (absRect.height / 2)); | ||||
|                             uint32_t dist = x * x + y * y; | ||||
|                             if (dist < bestDistance) | ||||
|                             { | ||||
|                                 last = d; | ||||
|                                 bestDistance = dist; | ||||
|                                 pressedDrawable = d; | ||||
|                                 fingerAdjustmentX = deltaX; | ||||
|                                 fingerAdjustmentY = deltaY; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             Container::getLastChild(evt.getX(), evt.getY(), &pressedDrawable); | ||||
|         } | ||||
|  | ||||
|         if (pressedDrawable == this) | ||||
|         { | ||||
|             pressedDrawable = 0; | ||||
|         } | ||||
|  | ||||
|         if (pressedDrawable) | ||||
|         { | ||||
|             hasIssuedCancelEvent = false; | ||||
|             pressedX = evt.getX(); | ||||
|             pressedY = evt.getY(); | ||||
|             Rect r = pressedDrawable->getAbsoluteRect(); | ||||
|             ClickEvent relative(evt.getType(), evt.getX() + rect.x + fingerAdjustmentX - r.x, evt.getY() + rect.y + fingerAdjustmentY - r.y); | ||||
|             pressedDrawable->handleClickEvent(relative); | ||||
|             lastDraggableChild = pressedDrawable; | ||||
|         } | ||||
|     } | ||||
|     else if (evt.getType() == ClickEvent::CANCEL) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|     else // if (evt.getType() == ClickEvent::RELEASED) | ||||
|     { | ||||
|         if (pressedDrawable) | ||||
|         { | ||||
|             Rect r = pressedDrawable->getAbsoluteRect(); | ||||
|             ClickEvent relative(evt.getType(), evt.getX() + rect.x + fingerAdjustmentX - r.x, evt.getY() + rect.y + fingerAdjustmentY - r.y); | ||||
|             pressedDrawable->handleClickEvent(relative); | ||||
|         } | ||||
|  | ||||
|         pressedDrawable = 0; | ||||
|         lastDraggableChild = 0; | ||||
|         isPressed = false; | ||||
|     } | ||||
|  | ||||
|     isScrolling = false; | ||||
|  | ||||
|     // Redraw scrollbars. | ||||
|     xSlider.setVisible((isPressed && scrollbarsVisible) || scrollbarsPermanentlyVisible); | ||||
|     ySlider.setVisible((isPressed && scrollbarsVisible) || scrollbarsPermanentlyVisible); | ||||
|     invalidateScrollbars(); | ||||
| } | ||||
|  | ||||
| void ScrollableContainer::handleDragEvent(const DragEvent& evt) | ||||
| { | ||||
|     DragEvent actualDrag = evt; | ||||
|     bool acceptInitialScroll = false; | ||||
|  | ||||
|     bool canScrollX = false; | ||||
|     bool canScrollY = false; | ||||
|     isScrollableXY(canScrollX, canScrollY); | ||||
|  | ||||
|     if ((pressedDrawable != 0) && (pressedDrawable != this)) | ||||
|     { | ||||
|         // Also send this drag event to the appropriate child widget | ||||
|         Rect r = pressedDrawable->getAbsoluteRect(); | ||||
|         int16_t oldX = evt.getOldX() + rect.x + fingerAdjustmentX - r.x; | ||||
|         int16_t oldY = evt.getOldY() + rect.y + fingerAdjustmentY - r.y; | ||||
|         int16_t newX = canScrollX ? oldX : evt.getNewX() + rect.x + fingerAdjustmentX - r.x; | ||||
|         int16_t newY = canScrollY ? oldY : evt.getNewY() + rect.y + fingerAdjustmentY - r.y; | ||||
|  | ||||
|         // but only in the direction(s) where the scrollable container itself | ||||
|         // cannot scroll. | ||||
|         if ((!canScrollX && newX != oldX) || (!canScrollY && newY != oldY)) | ||||
|         { | ||||
|             DragEvent relative(DragEvent::DRAGGED, oldX, oldY, newX, newY); | ||||
|             pressedDrawable->handleDragEvent(relative); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // If we are not currently scrolling, the drag event delta must | ||||
|     // be larger than the threshold value, otherwise the event is ignored. | ||||
|     if (!isScrolling) | ||||
|     { | ||||
|         // Only consider the delta in directions that are actually scrollable. | ||||
|         // Note: Do not use the delta from received evt since that only reflects | ||||
|         // change since last drag. What we want to check here is if the total | ||||
|         // delta from the point of click has now exceeded the threshold. | ||||
|         actualDrag = DragEvent(DragEvent::DRAGGED, pressedX + fingerAdjustmentX, pressedY + fingerAdjustmentY, evt.getNewX() + fingerAdjustmentX, evt.getNewY() + fingerAdjustmentY); | ||||
|         if (canScrollX) | ||||
|         { | ||||
|             // Can scroll in X. | ||||
|             if (abs(actualDrag.getDeltaX()) > scrollThreshold) | ||||
|             { | ||||
|                 acceptInitialScroll = true; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (canScrollY) | ||||
|         { | ||||
|             // Can scroll in Y. | ||||
|             if (abs(actualDrag.getDeltaY()) > scrollThreshold) | ||||
|             { | ||||
|                 acceptInitialScroll = true; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (acceptInitialScroll) | ||||
|         { | ||||
|             isScrolling = true; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // Discard this drag event. However, if the new coordinates no longer matches the drawable which received the PRESSED click event | ||||
|             // issue a CANCEL event to that drawable. | ||||
|             if (pressedDrawable && !hasIssuedCancelEvent) | ||||
|             { | ||||
|                 Drawable* child = 0; | ||||
|                 Container::getLastChild(evt.getNewX() + fingerAdjustmentX, evt.getNewY() + fingerAdjustmentY, &child); | ||||
|                 if (pressedDrawable != child) | ||||
|                 { | ||||
|                     ClickEvent ce(ClickEvent::CANCEL, 0, 0); | ||||
|                     pressedDrawable->handleClickEvent(ce); | ||||
|                     hasIssuedCancelEvent = true; | ||||
|                 } | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Send cancel events to child in focus | ||||
|     if (pressedDrawable && !hasIssuedCancelEvent) | ||||
|     { | ||||
|         ClickEvent ce(ClickEvent::CANCEL, 0, 0); | ||||
|         pressedDrawable->handleClickEvent(ce); | ||||
|         hasIssuedCancelEvent = true; | ||||
|     } | ||||
|  | ||||
|     int16_t deltaX = 0; | ||||
|     int16_t deltaY = 0; | ||||
|  | ||||
|     if (scrollableX) | ||||
|     { | ||||
|         if (acceptInitialScroll) | ||||
|         { | ||||
|             // Initial drag which is past threshold, only scroll one pixel in initial event. | ||||
|             if (actualDrag.getDeltaX() > 0) | ||||
|             { | ||||
|                 deltaX = 1; | ||||
|             } | ||||
|             else if (actualDrag.getDeltaX() < 0) | ||||
|             { | ||||
|                 deltaX = -1; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // Scroll entire delta | ||||
|             deltaX = actualDrag.getDeltaX(); | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // Not scrollable | ||||
|         deltaX = 0; | ||||
|     } | ||||
|  | ||||
|     if (scrollableY) | ||||
|     { | ||||
|         if (acceptInitialScroll) | ||||
|         { | ||||
|             // Initial drag which is past threshold, only scroll one pixel in initial event. | ||||
|             if (actualDrag.getDeltaY() > 0) | ||||
|             { | ||||
|                 deltaY = 1; | ||||
|             } | ||||
|             else if (actualDrag.getDeltaY() < 0) | ||||
|             { | ||||
|                 deltaY = -1; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // Scroll entire delta | ||||
|             deltaY = actualDrag.getDeltaY(); | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // Not scrollable | ||||
|         deltaY = 0; | ||||
|     } | ||||
|  | ||||
|     doScroll(deltaX, deltaY); | ||||
| } | ||||
|  | ||||
| void ScrollableContainer::handleGestureEvent(const GestureEvent& evt) | ||||
| { | ||||
|     bool canScrollX = false; | ||||
|     bool canScrollY = false; | ||||
|     isScrollableXY(canScrollX, canScrollY); | ||||
|  | ||||
|     if ((canScrollX && (evt.getType() == GestureEvent::SWIPE_HORIZONTAL)) || | ||||
|             (canScrollY && (evt.getType() == GestureEvent::SWIPE_VERTICAL))) | ||||
|     { | ||||
|         int16_t velocityAbsolute = abs(evt.getVelocity()); | ||||
|  | ||||
|         // Ignore gestures with velocity lower than threshold | ||||
|         if (velocityAbsolute < scrollThreshold) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Force velocity within limits | ||||
|         velocityAbsolute = MIN(velocityAbsolute, maxVelocity); | ||||
|         velocityAbsolute = MAX(velocityAbsolute, SCROLLBAR_MIN_VELOCITY); | ||||
|  | ||||
|         // Try to set some reasonable values for how long the resulting scroll should be, and how many ticks is should take | ||||
|         scrollDuration = velocityAbsolute * scrollDurationSpeedup / scrollDurationSlowdown; | ||||
|         targetValue = ((evt.getVelocity() > 0) ? 1 : -1) * (velocityAbsolute - 4) * 72; | ||||
|         scrollDuration = MIN(scrollDuration, abs(targetValue)); | ||||
|  | ||||
|         // Get ready to animate scroll: Initialize values | ||||
|         beginningValue = (evt.getType() == GestureEvent::SWIPE_VERTICAL) ? getContainedArea().y : getContainedArea().x; | ||||
|         animate = true; | ||||
|         Application::getInstance()->registerTimerWidget(this); | ||||
|         accelDirection = evt.getType(); | ||||
|  | ||||
|         if (pressedDrawable && !hasIssuedCancelEvent) | ||||
|         { | ||||
|             ClickEvent ce(ClickEvent::CANCEL, 0, 0); | ||||
|             pressedDrawable->handleClickEvent(ce); | ||||
|             hasIssuedCancelEvent = true; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| Rect ScrollableContainer::getXScrollbar() const | ||||
| { | ||||
|     Rect res(0, 0, 0, 0); | ||||
|     if (scrollableX) | ||||
|     { | ||||
|         Rect contained = getContainedArea(); | ||||
|         const int scrollSpace = (scrollableY && (contained.height > rect.height)) ? (2 * scrollbarPadding + scrollbarWidth + SCROLLBAR_LINE) : 0; | ||||
|  | ||||
|         if (contained.width > rect.width) | ||||
|         { | ||||
|             int leftPadding = (-1 * contained.x * rect.width) / contained.width; | ||||
|             int rightPadding = ((contained.right() - rect.width) * rect.width) / contained.width; | ||||
|             const int startWidth = rect.width - 2 * scrollbarPadding - 2 * SCROLLBAR_LINE - scrollSpace; | ||||
|             int width = startWidth; | ||||
|             width -= (leftPadding + rightPadding); | ||||
|             if (width < scrollbarWidth * 2) | ||||
|             { | ||||
|                 // If the contained area is very large, the scrollbar width may become zero or even negative. | ||||
|                 int diff = scrollbarWidth * 2 - width; | ||||
|                 width = scrollbarWidth * 2; // Force scrollbar width to a minimum | ||||
|                 // Distribute the deviation error based on current scrollbar X position (the amount subtracted from scrollbar xpos increases gradually). | ||||
|                 leftPadding -= (diff * leftPadding) / startWidth; | ||||
|             } | ||||
|             res = Rect(leftPadding + scrollbarPadding + SCROLLBAR_LINE, rect.height - scrollbarWidth - scrollbarPadding - SCROLLBAR_LINE, width, scrollbarWidth); | ||||
|         } | ||||
|     } | ||||
|     return res; | ||||
| } | ||||
|  | ||||
| Rect ScrollableContainer::getYScrollbar() const | ||||
| { | ||||
|     Rect res(0, 0, 0, 0); | ||||
|     if (scrollableY) | ||||
|     { | ||||
|         Rect contained = getContainedArea(); | ||||
|         const int scrollSpace = (scrollableX && (contained.width > rect.width)) ? (2 * scrollbarPadding + scrollbarWidth + SCROLLBAR_LINE) : 0; | ||||
|  | ||||
|         if (contained.height > rect.height) | ||||
|         { | ||||
|             int topPadding = (-1 * contained.y * rect.height) / contained.height; | ||||
|             int bottomPadding = ((contained.bottom() - rect.height) * rect.height) / contained.height; | ||||
|             const int startHeight = rect.height - 2 * scrollbarPadding - 2 * SCROLLBAR_LINE - scrollSpace; | ||||
|             int height = startHeight; | ||||
|             height -= (topPadding + bottomPadding); | ||||
|             if (height < scrollbarWidth * 2) | ||||
|             { | ||||
|                 // If the contained area is very large, the scrollbar height may become zero or even negative. | ||||
|                 int diff = scrollbarWidth * 2 - height; | ||||
|                 height = scrollbarWidth * 2; // Force scrollbar height to a minimum | ||||
|                 // Distribute the deviation error based on current scrollbar Y position (the amount subtracted from scrollbar ypos increases gradually). | ||||
|                 topPadding -= (diff * topPadding) / startHeight; | ||||
|             } | ||||
|             res = Rect(rect.width - scrollbarWidth - scrollbarPadding - 2 * SCROLLBAR_LINE, topPadding + scrollbarPadding + SCROLLBAR_LINE, scrollbarWidth, height); | ||||
|         } | ||||
|     } | ||||
|     return res; | ||||
| } | ||||
|  | ||||
| Rect ScrollableContainer::getXBorder(const Rect& xBar, const Rect& yBar) const | ||||
| { | ||||
|     Rect border(0, 0, 0, 0); | ||||
|     if (!xBar.isEmpty()) | ||||
|     { | ||||
|         const int scrollSpace = (!yBar.isEmpty()) ? (2 * scrollbarPadding + scrollbarWidth + SCROLLBAR_LINE) : 0; | ||||
|         border = Rect(scrollbarPadding, xBar.y - SCROLLBAR_LINE, rect.width - 2 * scrollbarPadding - scrollSpace, scrollbarWidth + 2 * SCROLLBAR_LINE); | ||||
|     } | ||||
|     return border; | ||||
| } | ||||
|  | ||||
| Rect ScrollableContainer::getYBorder(const Rect& xBar, const Rect& yBar) const | ||||
| { | ||||
|     Rect border(0, 0, 0, 0); | ||||
|     if (!yBar.isEmpty()) | ||||
|     { | ||||
|         const int scrollSpace = (!xBar.isEmpty()) ? (2 * scrollbarPadding + scrollbarWidth + SCROLLBAR_LINE) : 0; | ||||
|         border = Rect(yBar.x - SCROLLBAR_LINE, scrollbarPadding, scrollbarWidth + 2 * SCROLLBAR_LINE, rect.height - 2 * scrollbarPadding - scrollSpace); | ||||
|     } | ||||
|     return border; | ||||
| } | ||||
|  | ||||
| void ScrollableContainer::invalidateScrollbars() | ||||
| { | ||||
|     Rect xBar = getXScrollbar(); | ||||
|     Rect yBar = getYScrollbar(); | ||||
|  | ||||
|     Rect xBorder = getXBorder(xBar, yBar); | ||||
|     Rect yBorder = getYBorder(xBar, yBar); | ||||
|  | ||||
|     // The two if statements ensure that the two sliders is invalidates thereby hides them, before they are set to size zero. | ||||
|     if (xSlider.getY() > xBorder.y) | ||||
|     { | ||||
|         xSlider.invalidate(); | ||||
|     } | ||||
|     if (ySlider.getX() > yBorder.x) | ||||
|     { | ||||
|         ySlider.invalidate(); | ||||
|     } | ||||
|  | ||||
|     xSlider.setPosition(xBar.x, xBar.y, xBar.width, xBar.height); | ||||
|     ySlider.setPosition(yBar.x, yBar.y, yBar.width, yBar.height); | ||||
|  | ||||
|     // x-/yBorder is given the coordinates zero and the witdh of the visiable area for the scrollable container, | ||||
|     // to ensure that the entire area where for the scrollable bars is and have been is invalidated correct. | ||||
|     xBorder.x = 0; | ||||
|     xBorder.width = rect.width; | ||||
|     yBorder.height = rect.height; | ||||
|     yBorder.y = 0; | ||||
|  | ||||
|     if (!xBorder.isEmpty()) | ||||
|     { | ||||
|         invalidateRect(xBorder); | ||||
|     } | ||||
|  | ||||
|     if (!yBorder.isEmpty()) | ||||
|     { | ||||
|         invalidateRect(yBorder); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool ScrollableContainer::doScroll(int16_t deltaX, int16_t deltaY) | ||||
| { | ||||
|     if (!deltaX && !deltaY) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|     bool couldScroll = false; | ||||
|     Rect contained = getContainedArea(); | ||||
|     if (contained.width > rect.width) | ||||
|     { | ||||
|         if (deltaX > 0) | ||||
|         { | ||||
|             if (contained.x + deltaX > 0) | ||||
|             { | ||||
|                 deltaX = -contained.x; | ||||
|             } | ||||
|         } | ||||
|         else if (deltaX < 0) | ||||
|         { | ||||
|             if (contained.right() + deltaX < rect.width) | ||||
|             { | ||||
|                 deltaX = rect.width - contained.right(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         deltaX = 0; | ||||
|     } | ||||
|  | ||||
|     if (contained.height > rect.height) | ||||
|     { | ||||
|         if (deltaY > 0) | ||||
|         { | ||||
|             if (contained.y + deltaY > 0) | ||||
|             { | ||||
|                 deltaY = -contained.y; | ||||
|             } | ||||
|         } | ||||
|         else if (deltaY < 0) | ||||
|         { | ||||
|             if (contained.bottom() + deltaY < rect.height) | ||||
|             { | ||||
|                 deltaY = rect.height - contained.bottom(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         deltaY = 0; | ||||
|     } | ||||
|  | ||||
|     if (deltaX || deltaY) | ||||
|     { | ||||
|         scrolledXDistance += deltaX; | ||||
|         scrolledYDistance += deltaY; | ||||
|         moveChildrenRelative(deltaX, deltaY); | ||||
|  | ||||
|         invalidateScrollbars(); | ||||
|         couldScroll = true; | ||||
|     } | ||||
|     return couldScroll; | ||||
| } | ||||
|  | ||||
| void ScrollableContainer::childGeometryChanged() | ||||
| { | ||||
|     Rect contained = getContainedArea(); | ||||
|     // If children are not aligned top left, make sure they are | ||||
|     if (contained.y > 0) | ||||
|     { | ||||
|         moveChildrenRelative(0, -contained.y); | ||||
|     } | ||||
|     if (contained.x > 0) | ||||
|     { | ||||
|         moveChildrenRelative(-contained.x, 0); | ||||
|     } | ||||
|     // Make sure we haven't scrolled below the bottom | ||||
|     if (contained.bottom() < rect.height) | ||||
|     { | ||||
|         int16_t deltaY = contained.bottom() - rect.height; | ||||
|         if (contained.y > deltaY) | ||||
|         { | ||||
|             deltaY = contained.y; | ||||
|         } | ||||
|         scrolledYDistance -= deltaY; | ||||
|         moveChildrenRelative(0, -deltaY); | ||||
|     } | ||||
|     // Make sure we haven't scrolled too far to the right | ||||
|     if (contained.right() < rect.width) | ||||
|     { | ||||
|         int deltaX = contained.right() - rect.width; | ||||
|         if (contained.x > deltaX) | ||||
|         { | ||||
|             deltaX = contained.x; | ||||
|         } | ||||
|         scrolledXDistance -= deltaX; | ||||
|         moveChildrenRelative(-deltaX, 0); | ||||
|     } | ||||
|     invalidateScrollbars(); | ||||
| } | ||||
|  | ||||
| void ScrollableContainer::add(Drawable& d) | ||||
| { | ||||
|     remove(xSlider); | ||||
|     remove(ySlider); | ||||
|  | ||||
|     Container::add(d); | ||||
|     Container::add(xSlider); | ||||
|     Container::add(ySlider); | ||||
| } | ||||
|  | ||||
| Rect ScrollableContainer::getContainedArea() const | ||||
| { | ||||
|     Drawable* d = firstChild; | ||||
|     Rect contained(0, 0, 0, 0); | ||||
|     Rect r(0, 0, rect.width, rect.height); | ||||
|     contained.expandToFit(r); | ||||
|     while (d) | ||||
|     { | ||||
|         if ((d != &xSlider) && (d != &ySlider) && (d->isVisible())) | ||||
|         { | ||||
|             contained.expandToFit(d->getRect()); | ||||
|         } | ||||
|         d = d->getNextSibling(); | ||||
|     } | ||||
|     return contained; | ||||
| } | ||||
|  | ||||
| void ScrollableContainer::reset() | ||||
| { | ||||
|     moveChildrenRelative(-scrolledXDistance, -scrolledYDistance); | ||||
|     scrolledXDistance = 0; | ||||
|     scrolledYDistance = 0; | ||||
|     invalidateScrollbars(); | ||||
| } | ||||
|  | ||||
| void ScrollableContainer::moveChildrenRelative(int16_t deltaX, int16_t deltaY) | ||||
| { | ||||
|     Drawable* d = firstChild; | ||||
|     while (d) | ||||
|     { | ||||
|         if ((d != &xSlider) && (d != &ySlider)) | ||||
|         { | ||||
|             d->moveRelative(deltaX, deltaY); | ||||
|         } | ||||
|         d = d->getNextSibling(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ScrollableContainer::handleTickEvent() | ||||
| { | ||||
|     if (animate) | ||||
|     { | ||||
|         // Calculate new position or stop animation | ||||
|         animationCounter++; | ||||
|         if (animationCounter <= scrollDuration) | ||||
|         { | ||||
|             // Calculate value in [beginningValue ; (beginningValue+targetValue)] | ||||
|             int16_t calculatedValue = EasingEquations::cubicEaseOut(animationCounter, beginningValue, targetValue, scrollDuration); | ||||
|  | ||||
|             // Note: Result of "calculatedValue & 1" is compiler dependent for negative values of calculatedValue | ||||
|             if (calculatedValue % 2) | ||||
|             { | ||||
|                 // Optimization: calculatedValue is odd, add 1/-1 to move drawables modulo 32 bits in framebuffer | ||||
|                 calculatedValue += (calculatedValue > 0) ? 1 : -1; | ||||
|             } | ||||
|  | ||||
|             // Convert to delta value relative to current X or Y | ||||
|             int16_t scrollX = (accelDirection == GestureEvent::SWIPE_VERTICAL) ? 0 : (calculatedValue - getContainedArea().x); | ||||
|             int16_t scrollY = (accelDirection == GestureEvent::SWIPE_HORIZONTAL) ? 0 : (calculatedValue - getContainedArea().y); | ||||
|  | ||||
|             // Perform the actual animation step, stop animation if | ||||
|             // scrolling was not possible (doScroll invalidates children) | ||||
|             animate = doScroll(scrollX, scrollY); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             animate = false; | ||||
|         } | ||||
|  | ||||
|         if (!animate) | ||||
|         { | ||||
|             Application::getInstance()->unregisterTimerWidget(this); | ||||
|             animationCounter = 0; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ScrollableContainer::setScrollbarsColor(colortype color) | ||||
| { | ||||
|     scrollbarColor = color; | ||||
|     xSlider.setColor(scrollbarColor); | ||||
|     ySlider.setColor(scrollbarColor); | ||||
| } | ||||
|  | ||||
| void ScrollableContainer::setScrollbarsAlpha(uint8_t alpha) | ||||
| { | ||||
|     scrollbarAlpha = alpha; | ||||
|     xSlider.setAlpha(scrollbarAlpha); | ||||
|     ySlider.setAlpha(scrollbarAlpha); | ||||
| } | ||||
|  | ||||
| void ScrollableContainer::setScrollbarPadding(uint8_t padding) | ||||
| { | ||||
|     scrollbarPadding = padding; | ||||
| } | ||||
|  | ||||
| void ScrollableContainer::setScrollbarWidth(uint8_t width) | ||||
| { | ||||
|     scrollbarWidth = width; | ||||
| } | ||||
|  | ||||
| void ScrollableContainer::setScrollbarsVisible(bool newVisible) | ||||
| { | ||||
|     scrollbarsVisible = newVisible; | ||||
| } | ||||
|  | ||||
| void ScrollableContainer::setScrollbarsPermanentlyVisible(bool permanentlyVisible /*= true*/) | ||||
| { | ||||
|     scrollbarsPermanentlyVisible = permanentlyVisible; | ||||
|     xSlider.setVisible(true); | ||||
|     ySlider.setVisible(true); | ||||
|     invalidateScrollbars(); | ||||
| } | ||||
|  | ||||
| int16_t ScrollableContainer::getScrolledX() const | ||||
| { | ||||
|     return scrolledXDistance; | ||||
| } | ||||
|  | ||||
| int16_t ScrollableContainer::getScrolledY() const | ||||
| { | ||||
|     return scrolledYDistance; | ||||
| } | ||||
|  | ||||
| void ScrollableContainer::setScrollDurationSpeedup(uint16_t speedup) | ||||
| { | ||||
|     scrollDurationSpeedup = MAX(1, speedup); | ||||
| } | ||||
|  | ||||
| uint16_t ScrollableContainer::getScrollDurationSpeedup() const | ||||
| { | ||||
|     return scrollDurationSpeedup; | ||||
| } | ||||
|  | ||||
| void ScrollableContainer::setScrollDurationSlowdown(uint16_t slowdown) | ||||
| { | ||||
|     scrollDurationSlowdown = MAX(1, slowdown); | ||||
| } | ||||
|  | ||||
| uint16_t ScrollableContainer::getScrollDurationSlowdown() const | ||||
| { | ||||
|     return scrollDurationSlowdown; | ||||
| } | ||||
|  | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,402 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/SlideMenu.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| SlideMenu::SlideMenu() | ||||
|     : Container(), | ||||
|       onStateChangeButtonClicked(this, &SlideMenu::stateChangeButtonClickedHandler), | ||||
|       animationEndedCallback(this, &SlideMenu::animationEndedHandler), | ||||
|       stateChangedCallback(0), | ||||
|       stateChangedAnimationEndedCallback(0), | ||||
|       currentState(COLLAPSED), | ||||
|       expandDirection(EAST), | ||||
|       animationEquation(EasingEquations::cubicEaseInOut), | ||||
|       visiblePixelsWhenCollapsed(0), | ||||
|       hiddenPixelsWhenExpanded(0), | ||||
|       expandedStateTimeout(200), | ||||
|       expandedStateTimer(0), | ||||
|       animationDuration(10) | ||||
| { | ||||
|     Application::getInstance()->registerTimerWidget(this); | ||||
|  | ||||
|     stateChangeButton.setAction(onStateChangeButtonClicked); | ||||
|     menuContainer.setMoveAnimationEndedAction(animationEndedCallback); | ||||
|  | ||||
|     Container::add(menuContainer); | ||||
|     menuContainer.add(background); | ||||
|     menuContainer.add(stateChangeButton); | ||||
| } | ||||
|  | ||||
| SlideMenu::~SlideMenu() | ||||
| { | ||||
|     Application::getInstance()->unregisterTimerWidget(this); //lint !e1551 | ||||
| } | ||||
|  | ||||
| void SlideMenu::setup(ExpandDirection newExpandDirection, const Bitmap& backgroundBMP, const Bitmap& stateChangeButtonBMP, const Bitmap& stateChangeButtonPressedBMP) | ||||
| { | ||||
|     int16_t backgroundX = 0; | ||||
|     int16_t backgroundY = 0; | ||||
|     int16_t buttonX = 0; | ||||
|     int16_t buttonY = 0; | ||||
|  | ||||
|     switch (newExpandDirection) | ||||
|     { | ||||
|     case SOUTH: | ||||
|         backgroundX = 0; | ||||
|         backgroundY = 0; | ||||
|         buttonX = (backgroundBMP.getWidth() - stateChangeButtonBMP.getWidth()) / 2; | ||||
|         buttonY = backgroundBMP.getHeight(); | ||||
|  | ||||
|         setVisiblePixelsWhenCollapsed(stateChangeButtonBMP.getHeight()); | ||||
|         break; | ||||
|     case NORTH: | ||||
|         backgroundX = 0; | ||||
|         backgroundY = stateChangeButtonBMP.getHeight(); | ||||
|         buttonX = (backgroundBMP.getWidth() - stateChangeButtonBMP.getWidth()) / 2; | ||||
|         buttonY = 0; | ||||
|  | ||||
|         setVisiblePixelsWhenCollapsed(stateChangeButtonBMP.getHeight()); | ||||
|         break; | ||||
|     case EAST: | ||||
|         backgroundX = 0; | ||||
|         backgroundY = 0; | ||||
|         buttonX = backgroundBMP.getWidth(); | ||||
|         buttonY = (backgroundBMP.getHeight() - stateChangeButtonBMP.getHeight()) / 2; | ||||
|  | ||||
|         setVisiblePixelsWhenCollapsed(stateChangeButtonBMP.getWidth()); | ||||
|         break; | ||||
|     case WEST: | ||||
|         backgroundX = stateChangeButtonBMP.getWidth(); | ||||
|         backgroundY = 0; | ||||
|         buttonX = 0; | ||||
|         buttonY = (backgroundBMP.getHeight() - stateChangeButtonBMP.getHeight()) / 2; | ||||
|  | ||||
|         setVisiblePixelsWhenCollapsed(stateChangeButtonBMP.getWidth()); | ||||
|         break; | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     setup(newExpandDirection, backgroundBMP, stateChangeButtonBMP, stateChangeButtonPressedBMP, backgroundX, backgroundY, buttonX, buttonY); | ||||
| } | ||||
|  | ||||
| void SlideMenu::setup(ExpandDirection newExpandDirection, const Bitmap& backgroundBMP, int16_t backgroundX, int16_t backgroundY) | ||||
| { | ||||
|     setExpandDirection(newExpandDirection); | ||||
|  | ||||
|     background.setBitmap(backgroundBMP); | ||||
|     background.setXY(backgroundX, backgroundY); | ||||
|  | ||||
|     Rect boundingRect = background.getRect(); | ||||
|     // boundingRect.expandToFit(background.getRect()); | ||||
|  | ||||
|     menuContainer.setWidth(boundingRect.right()); | ||||
|     menuContainer.setHeight(boundingRect.bottom()); | ||||
|  | ||||
|     setWidthHeight(menuContainer); | ||||
|  | ||||
|     setExpandDirection(expandDirection); | ||||
|     setState(currentState); | ||||
|  | ||||
|     invalidate(); | ||||
| } | ||||
|  | ||||
| void SlideMenu::setup(ExpandDirection newExpandDirection, const Bitmap& backgroundBMP, const Bitmap& stateChangeButtonBMP, const Bitmap& stateChangeButtonPressedBMP, int16_t backgroundX, int16_t backgroundY, int16_t stateChangeButtonX, int16_t stateChangeButtonY) | ||||
| { | ||||
|     setExpandDirection(newExpandDirection); | ||||
|  | ||||
|     background.setBitmap(backgroundBMP); | ||||
|     background.setXY(backgroundX, backgroundY); | ||||
|  | ||||
|     stateChangeButton.setBitmaps(stateChangeButtonBMP, stateChangeButtonPressedBMP); | ||||
|     stateChangeButton.setXY(stateChangeButtonX, stateChangeButtonY); | ||||
|  | ||||
|     Rect boundingRect(0, 0, 0, 0); | ||||
|     boundingRect.expandToFit(background.getRect()); | ||||
|     boundingRect.expandToFit(stateChangeButton.getRect()); | ||||
|  | ||||
|     menuContainer.setWidth(boundingRect.right()); | ||||
|     menuContainer.setHeight(boundingRect.bottom()); | ||||
|  | ||||
|     setWidthHeight(menuContainer); | ||||
|  | ||||
|     setExpandDirection(expandDirection); | ||||
|     setState(currentState); | ||||
|  | ||||
|     invalidate(); | ||||
| } | ||||
|  | ||||
| void SlideMenu::setExpandDirection(ExpandDirection newExpandDirection) | ||||
| { | ||||
|     expandDirection = newExpandDirection; | ||||
|  | ||||
|     setState(currentState); | ||||
| } | ||||
|  | ||||
| SlideMenu::ExpandDirection SlideMenu::getExpandDirection() const | ||||
| { | ||||
|     return expandDirection; | ||||
| } | ||||
|  | ||||
| void SlideMenu::setVisiblePixelsWhenCollapsed(int16_t visiblePixels) | ||||
| { | ||||
|     visiblePixelsWhenCollapsed = visiblePixels; | ||||
|  | ||||
|     setState(currentState); | ||||
| } | ||||
|  | ||||
| int16_t SlideMenu::getVisiblePixelsWhenCollapsed() const | ||||
| { | ||||
|     return visiblePixelsWhenCollapsed; | ||||
| } | ||||
|  | ||||
| void SlideMenu::setHiddenPixelsWhenExpanded(int16_t hiddenPixels) | ||||
| { | ||||
|     hiddenPixelsWhenExpanded = hiddenPixels; | ||||
|  | ||||
|     setState(currentState); | ||||
| } | ||||
|  | ||||
| int16_t SlideMenu::getHiddenPixelsWhenExpanded() const | ||||
| { | ||||
|     return hiddenPixelsWhenExpanded; | ||||
| } | ||||
|  | ||||
| void SlideMenu::setExpandedStateTimeout(uint16_t timeout) | ||||
| { | ||||
|     expandedStateTimeout = timeout; | ||||
| } | ||||
|  | ||||
| uint16_t SlideMenu::getExpandedStateTimeout() const | ||||
| { | ||||
|     return expandedStateTimeout; | ||||
| } | ||||
|  | ||||
| void SlideMenu::setAnimationDuration(uint16_t duration) | ||||
| { | ||||
|     animationDuration = duration; | ||||
| } | ||||
|  | ||||
| uint16_t SlideMenu::getAnimationDuration() const | ||||
| { | ||||
|     return animationDuration; | ||||
| } | ||||
|  | ||||
| void SlideMenu::setAnimationEasingEquation(EasingEquation animationEasingEquation) | ||||
| { | ||||
|     animationEquation = animationEasingEquation; | ||||
| } | ||||
|  | ||||
| EasingEquation SlideMenu::getAnimationEasingEquation() const | ||||
| { | ||||
|     return animationEquation; | ||||
| } | ||||
|  | ||||
| void SlideMenu::setState(State newState) | ||||
| { | ||||
|     if (newState == COLLAPSED) | ||||
|     { | ||||
|         menuContainer.moveTo(getCollapsedXCoordinate(), getCollapsedYCoordinate()); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         menuContainer.moveTo(getExpandedXCoordinate(), getExpandedYCoordinate()); | ||||
|     } | ||||
|     currentState = newState; | ||||
| } | ||||
|  | ||||
| void SlideMenu::animateToState(State newState) | ||||
| { | ||||
|     if (animationDuration == 0) | ||||
|     { | ||||
|         setState(newState); | ||||
|     } | ||||
|     else if (newState != currentState) | ||||
|     { | ||||
|         if (currentState == COLLAPSED) | ||||
|         { | ||||
|             menuContainer.startMoveAnimation(getExpandedXCoordinate(), getExpandedYCoordinate(), animationDuration, animationEquation, animationEquation); | ||||
|             currentState = EXPANDED; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             menuContainer.startMoveAnimation(getCollapsedXCoordinate(), getCollapsedYCoordinate(), animationDuration, animationEquation, animationEquation); | ||||
|             currentState = COLLAPSED; | ||||
|         } | ||||
|  | ||||
|         // Disable stateChangeButton while animating | ||||
|         stateChangeButton.setTouchable(false); | ||||
|     } | ||||
| } | ||||
|  | ||||
| SlideMenu::State SlideMenu::getState() | ||||
| { | ||||
|     return currentState; | ||||
| } | ||||
|  | ||||
| void SlideMenu::resetExpandedStateTimer() | ||||
| { | ||||
|     expandedStateTimer = 0; | ||||
| } | ||||
|  | ||||
| uint16_t SlideMenu::getExpandedStateTimer() const | ||||
| { | ||||
|     return expandedStateTimer; | ||||
| } | ||||
|  | ||||
| int16_t SlideMenu::getBackgroundX() const | ||||
| { | ||||
|     return background.getX(); | ||||
| } | ||||
|  | ||||
| int16_t SlideMenu::getBackgroundY() const | ||||
| { | ||||
|     return background.getY(); | ||||
| } | ||||
|  | ||||
| int16_t SlideMenu::getStateChangeButtonX() const | ||||
| { | ||||
|     return stateChangeButton.getX(); | ||||
| } | ||||
|  | ||||
| int16_t SlideMenu::getStateChangeButtonY() const | ||||
| { | ||||
|     return stateChangeButton.getY(); | ||||
| } | ||||
|  | ||||
| void SlideMenu::setStateChangedCallback(GenericCallback<const SlideMenu&>& callback) | ||||
| { | ||||
|     stateChangedCallback = &callback; | ||||
| } | ||||
|  | ||||
| void SlideMenu::setStateChangedAnimationEndedCallback(GenericCallback<const SlideMenu&>& callback) | ||||
| { | ||||
|     stateChangedAnimationEndedCallback = &callback; | ||||
| } | ||||
|  | ||||
| void SlideMenu::handleTickEvent() | ||||
| { | ||||
|     if ((expandedStateTimeout != 0) && (currentState == EXPANDED) && !menuContainer.isMoveAnimationRunning()) | ||||
|     { | ||||
|         expandedStateTimer++; | ||||
|  | ||||
|         if (expandedStateTimer > expandedStateTimeout) | ||||
|         { | ||||
|             animateToState(COLLAPSED); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void SlideMenu::add(Drawable& d) | ||||
| { | ||||
|     menuContainer.add(d); | ||||
| } | ||||
|  | ||||
| void SlideMenu::remove(Drawable& d) | ||||
| { | ||||
|     menuContainer.remove(d); | ||||
| } | ||||
|  | ||||
| void SlideMenu::stateChangeButtonClickedHandler(const AbstractButton& /*button*/) | ||||
| { | ||||
|     if (currentState == COLLAPSED) | ||||
|     { | ||||
|         animateToState(EXPANDED); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         animateToState(COLLAPSED); | ||||
|     } | ||||
|  | ||||
|     if ((stateChangedCallback != 0) && stateChangedCallback->isValid()) | ||||
|     { | ||||
|         stateChangedCallback->execute(*this); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void SlideMenu::animationEndedHandler(const MoveAnimator<Container>& /*container*/) | ||||
| { | ||||
|     resetExpandedStateTimer(); | ||||
|     stateChangeButton.setTouchable(true); | ||||
|  | ||||
|     if ((stateChangedAnimationEndedCallback != 0) && stateChangedAnimationEndedCallback->isValid()) | ||||
|     { | ||||
|         stateChangedAnimationEndedCallback->execute(*this); | ||||
|     } | ||||
| } | ||||
|  | ||||
| int16_t SlideMenu::getCollapsedXCoordinate() | ||||
| { | ||||
|     switch (expandDirection) | ||||
|     { | ||||
|     case EAST: | ||||
|         return -menuContainer.getWidth() + visiblePixelsWhenCollapsed; | ||||
|     case WEST: | ||||
|         return getWidth() - visiblePixelsWhenCollapsed; | ||||
|     case SOUTH: | ||||
|     case NORTH: | ||||
|     default: | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| int16_t SlideMenu::getCollapsedYCoordinate() | ||||
| { | ||||
|     switch (expandDirection) | ||||
|     { | ||||
|     case SOUTH: | ||||
|         return -menuContainer.getHeight() + visiblePixelsWhenCollapsed; | ||||
|     case NORTH: | ||||
|         return getHeight() - visiblePixelsWhenCollapsed; | ||||
|     case EAST: | ||||
|     case WEST: | ||||
|     default: | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| int16_t SlideMenu::getExpandedXCoordinate() | ||||
| { | ||||
|     switch (expandDirection) | ||||
|     { | ||||
|     case EAST: | ||||
|         return -hiddenPixelsWhenExpanded; | ||||
|     case WEST: | ||||
|         return hiddenPixelsWhenExpanded; | ||||
|     case SOUTH: | ||||
|     case NORTH: | ||||
|     default: | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| int16_t SlideMenu::getExpandedYCoordinate() | ||||
| { | ||||
|     switch (expandDirection) | ||||
|     { | ||||
|     case SOUTH: | ||||
|         return -hiddenPixelsWhenExpanded; | ||||
|     case NORTH: | ||||
|         return hiddenPixelsWhenExpanded; | ||||
|     case EAST: | ||||
|     case WEST: | ||||
|     default: | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,295 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/Slider.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| Slider::Slider() | ||||
|     : Container(), | ||||
|       sliderOrientation(HORIZONTAL), | ||||
|       currentValue(0), | ||||
|       valueRangeMin(0), | ||||
|       valueRangeMax(1), | ||||
|       indicatorMinPosition(0), | ||||
|       indicatorMaxPosition(1), | ||||
|       startValueCallback(0), | ||||
|       stopValueCallback(0), | ||||
|       newValueCallback(0) | ||||
| { | ||||
|     setTouchable(true); | ||||
|  | ||||
|     // The backgroundSelectedViewPort is a container into which the bitmap for the "filled" background | ||||
|     // is placed. Containers are viewports, so the dimensions of this container controls how | ||||
|     // much of the filled background is visible. | ||||
|     backgroundSelectedViewPort.add(backgroundSelected); | ||||
|  | ||||
|     Container::add(background); | ||||
|     Container::add(backgroundSelectedViewPort); | ||||
|     Container::add(indicator); | ||||
|  | ||||
|     // Default value range | ||||
|     Slider::setValueRange(0, 100); | ||||
| } | ||||
|  | ||||
| void Slider::setBitmaps(const Bitmap& sliderBackground, const Bitmap& sliderBackgroundSelected, const Bitmap& indicatorBitmap) | ||||
| { | ||||
|     assert(sliderBackground.getWidth() == sliderBackgroundSelected.getWidth() && | ||||
|            sliderBackground.getHeight() == sliderBackgroundSelected.getHeight() && | ||||
|            "Slider::setBitmaps - background and backgroundFilled must have same dimensions"); | ||||
|  | ||||
|     background.setBitmap(sliderBackground); | ||||
|     backgroundSelected.setBitmap(sliderBackgroundSelected); | ||||
|     indicator.setBitmap(indicatorBitmap); | ||||
|     backgroundSelectedViewPort.setWidthHeight(backgroundSelected); | ||||
| } | ||||
|  | ||||
| void Slider::setBitmaps(const BitmapId sliderBackground, const BitmapId sliderBackgroundSelected, const BitmapId indicatorBitmap) | ||||
| { | ||||
|     setBitmaps(Bitmap(sliderBackground), Bitmap(sliderBackgroundSelected), Bitmap(indicatorBitmap)); | ||||
| } | ||||
|  | ||||
| void Slider::setupHorizontalSlider(uint16_t backgroundX, uint16_t backgroundY, uint16_t indicatorY, uint16_t indicatorMinX, uint16_t indicatorMaxX) | ||||
| { | ||||
|     assert(indicatorMinX < indicatorMaxX && "Slider::setupHorizontalSlider - indicatorMinX must be smaller than indicatorMaxX"); | ||||
|  | ||||
|     sliderOrientation = HORIZONTAL; | ||||
|  | ||||
|     background.setXY(backgroundX, backgroundY); | ||||
|     backgroundSelectedViewPort.setXY(backgroundX, backgroundY); | ||||
|     backgroundSelected.setXY(0, 0); | ||||
|     indicator.setY(indicatorY); | ||||
|  | ||||
|     uint16_t backgroundWidth = backgroundX + static_cast<uint16_t>(background.getWidth()); | ||||
|     uint16_t indicatorWidth = indicatorMaxX + static_cast<uint16_t>(indicator.getWidth()); | ||||
|     int16_t newWidth = static_cast<int16_t>(MAX(backgroundWidth, indicatorWidth)); | ||||
|  | ||||
|     uint16_t backgroundHeight = backgroundY + static_cast<uint16_t>(background.getHeight()); | ||||
|     uint16_t indicatorHeight = indicatorY + static_cast<uint16_t>(indicator.getHeight()); | ||||
|     int16_t newHeight = static_cast<int16_t>(MAX(backgroundHeight, indicatorHeight)); | ||||
|  | ||||
|     indicatorMinPosition = indicatorMinX; | ||||
|     indicatorMaxPosition = indicatorMaxX; | ||||
|  | ||||
|     setWidthHeight(newWidth, newHeight); | ||||
|  | ||||
|     setValue(currentValue); | ||||
| } | ||||
|  | ||||
| void Slider::setupVerticalSlider(uint16_t backgroundX, uint16_t backgroundY, uint16_t indicatorX, uint16_t indicatorMinY, uint16_t indicatorMaxY) | ||||
| { | ||||
|     assert(indicatorMinY < indicatorMaxY && "Slider::setupVerticalSlider - indicatorMinY must be smaller than indicatorMaxY"); | ||||
|  | ||||
|     sliderOrientation = VERTICAL; | ||||
|  | ||||
|     background.setXY(backgroundX, backgroundY); | ||||
|     backgroundSelectedViewPort.setXY(backgroundX, backgroundY); | ||||
|     indicator.setX(indicatorX); | ||||
|  | ||||
|     uint16_t backgroundWidth = backgroundX + static_cast<uint16_t>(background.getWidth()); | ||||
|     uint16_t indicatorWidth = indicatorX + static_cast<uint16_t>(indicator.getWidth()); | ||||
|     int16_t newWidth = static_cast<int16_t>(MAX(backgroundWidth, indicatorWidth)); | ||||
|  | ||||
|     uint16_t backgroundHeight = backgroundY + static_cast<uint16_t>(background.getHeight()); | ||||
|     uint16_t indicatorHeight = indicatorMaxY + static_cast<uint16_t>(indicator.getHeight()); | ||||
|     int16_t newHeight = static_cast<int16_t>(MAX(backgroundHeight, indicatorHeight)); | ||||
|  | ||||
|     indicatorMinPosition = indicatorMinY; | ||||
|     indicatorMaxPosition = indicatorMaxY; | ||||
|  | ||||
|     setWidthHeight(newWidth, newHeight); | ||||
|  | ||||
|     setValue(currentValue); | ||||
| } | ||||
|  | ||||
| void Slider::setValue(int value) | ||||
| { | ||||
|     updateIndicatorPosition(valueToPosition(value)); | ||||
| } | ||||
|  | ||||
| void Slider::handleClickEvent(const ClickEvent& evt) | ||||
| { | ||||
|     if ((evt.getType() == ClickEvent::PRESSED) || (evt.getType() == ClickEvent::RELEASED)) | ||||
|     { | ||||
|         // Communicate the start value if a listener is registered | ||||
|         if ((evt.getType() == ClickEvent::PRESSED) && (startValueCallback != 0) && startValueCallback->isValid()) | ||||
|         { | ||||
|             startValueCallback->execute(*this, currentValue); | ||||
|         } | ||||
|  | ||||
|         if (sliderOrientation == HORIZONTAL) | ||||
|         { | ||||
|             updateIndicatorPosition(evt.getX() - getIndicatorRadius()); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             updateIndicatorPosition(evt.getY() - getIndicatorRadius()); | ||||
|         } | ||||
|  | ||||
|         // Communicate the stop value if a listener is registered | ||||
|         if ((evt.getType() == ClickEvent::RELEASED) && (stopValueCallback != 0) && stopValueCallback->isValid()) | ||||
|         { | ||||
|             stopValueCallback->execute(*this, currentValue); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void Slider::handleDragEvent(const DragEvent& evt) | ||||
| { | ||||
|     if (sliderOrientation == HORIZONTAL) | ||||
|     { | ||||
|         updateIndicatorPosition(evt.getNewX() - getIndicatorRadius()); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         updateIndicatorPosition(evt.getNewY() - getIndicatorRadius()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| int16_t Slider::valueToPosition(int value) const | ||||
| { | ||||
|     value = MIN(valueRangeMax, value); | ||||
|     value = MAX(value, valueRangeMin); | ||||
|  | ||||
|     int coordinateOffset = ((value - valueRangeMin) * (getIndicatorPositionRangeSize() + 1)) / getValueRangeSize(); | ||||
|  | ||||
|     int result = indicatorMinPosition + coordinateOffset; | ||||
|  | ||||
|     if (sliderOrientation == VERTICAL) | ||||
|     { | ||||
|         // Vertical slider grows as the position decreases so invert the coordinate | ||||
|         result = indicatorMinPosition + (indicatorMaxPosition - result); | ||||
|     } | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| int Slider::positionToValue(int16_t position) const | ||||
| { | ||||
|     int result; | ||||
|  | ||||
|     if (position == indicatorMinPosition) | ||||
|     { | ||||
|         // Ensure that min coordinate always results in min value | ||||
|         result = valueRangeMin; | ||||
|     } | ||||
|     else if (position == indicatorMaxPosition) | ||||
|     { | ||||
|         // Ensure that max coordinate always results in max value | ||||
|         result = valueRangeMax; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         int rounding = getIndicatorPositionRangeSize() / 2; | ||||
|         int valueOffset = (((position - indicatorMinPosition) * getValueRangeSize()) + rounding) / getIndicatorPositionRangeSize(); | ||||
|  | ||||
|         result = valueRangeMin + valueOffset; | ||||
|     } | ||||
|  | ||||
|     if (sliderOrientation == VERTICAL) | ||||
|     { | ||||
|         // Vertical slider grows as the position decreases so invert the value | ||||
|         result = valueRangeMin + (valueRangeMax - result); | ||||
|     } | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| void Slider::updateIndicatorPosition(int16_t position) | ||||
| { | ||||
|     // Cut off positions outside the slider area | ||||
|     position = MAX(position, indicatorMinPosition); | ||||
|     position = MIN(position, indicatorMaxPosition); | ||||
|  | ||||
|     if (sliderOrientation == HORIZONTAL) | ||||
|     { | ||||
|         indicator.moveTo(position, indicator.getY()); | ||||
|  | ||||
|         backgroundSelectedViewPort.invalidate(); | ||||
|         backgroundSelectedViewPort.setWidth((position - backgroundSelectedViewPort.getX()) + getIndicatorRadius()); | ||||
|         backgroundSelectedViewPort.invalidate(); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         indicator.moveTo(indicator.getX(), position); | ||||
|  | ||||
|         backgroundSelectedViewPort.invalidate(); | ||||
|         int16_t newViewPortHeight = background.getRect().bottom() - (position + getIndicatorRadius()); | ||||
|         backgroundSelectedViewPort.setPosition(backgroundSelectedViewPort.getX(), position + getIndicatorRadius(), backgroundSelectedViewPort.getWidth(), newViewPortHeight); | ||||
|         backgroundSelected.setY(-(backgroundSelected.getHeight() - newViewPortHeight)); | ||||
|         backgroundSelectedViewPort.invalidate(); | ||||
|     } | ||||
|  | ||||
|     currentValue = positionToValue(position); | ||||
|  | ||||
|     // Communicate the new value if a listener is registered | ||||
|     if ((newValueCallback != 0) && newValueCallback->isValid()) | ||||
|     { | ||||
|         newValueCallback->execute(*this, currentValue); | ||||
|     } | ||||
| } | ||||
|  | ||||
| uint16_t Slider::getIndicatorRadius() const | ||||
| { | ||||
|     uint16_t result; | ||||
|  | ||||
|     if (sliderOrientation == HORIZONTAL) | ||||
|     { | ||||
|         result = indicator.getWidth() / 2; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         result = indicator.getHeight() / 2; | ||||
|     } | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| void Slider::setValueRange(int minValue, int maxValue, int newValue) | ||||
| { | ||||
|     assert(minValue < maxValue && "Slider::setValueRange - minValue must be smaller than maxValue"); | ||||
|  | ||||
|     valueRangeMin = minValue; | ||||
|     valueRangeMax = maxValue; | ||||
|  | ||||
|     setValue(newValue); | ||||
| } | ||||
|  | ||||
| void Slider::setValueRange(int minValue, int maxValue) | ||||
| { | ||||
|     int newValue = currentValue; | ||||
|  | ||||
|     if (currentValue < minValue) | ||||
|     { | ||||
|         newValue = minValue; | ||||
|     } | ||||
|     else if (currentValue > maxValue) | ||||
|     { | ||||
|         newValue = maxValue; | ||||
|     } | ||||
|  | ||||
|     setValueRange(minValue, maxValue, newValue); | ||||
| } | ||||
|  | ||||
| int Slider::getIndicatorPositionRangeSize() const | ||||
| { | ||||
|     return indicatorMaxPosition - indicatorMinPosition; | ||||
| } | ||||
|  | ||||
| int Slider::getValueRangeSize() const | ||||
| { | ||||
|     return valueRangeMax - valueRangeMin; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,400 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/EasingEquations.hpp> | ||||
| #include <touchgfx/containers/SwipeContainer.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| SwipeContainer::SwipeContainer() | ||||
|     : Container(), | ||||
|       currentState(NO_ANIMATION), | ||||
|       numberOfPages(0), | ||||
|       animationCounter(0), | ||||
|       swipeCutoff(80), | ||||
|       dragX(0), | ||||
|       animateDistance(0), | ||||
|       startX(0), | ||||
|       currentPage(0), | ||||
|       endElasticWidth(30), | ||||
|       pages(EAST), | ||||
|       pageIndicator() | ||||
| { | ||||
|     Application::getInstance()->registerTimerWidget(this); | ||||
|  | ||||
|     setTouchable(true); | ||||
|  | ||||
|     Container::add(pages); | ||||
|     Container::add(pageIndicator); | ||||
| } | ||||
|  | ||||
| SwipeContainer::~SwipeContainer() | ||||
| { | ||||
|     Application::getInstance()->unregisterTimerWidget(this); | ||||
| } | ||||
|  | ||||
| void SwipeContainer::add(Drawable& page) | ||||
| { | ||||
|     pages.add(page); | ||||
|     numberOfPages++; | ||||
|  | ||||
|     pageIndicator.setNumberOfPages(numberOfPages); | ||||
|  | ||||
|     setWidthHeight(page); | ||||
| } | ||||
|  | ||||
| void SwipeContainer::remove(Drawable& page) | ||||
| { | ||||
|     Drawable* tmp = pages.getFirstChild(); | ||||
|  | ||||
|     if (!numberOfPages) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // looks for the child matching page | ||||
|     // to ensure that the page indicator only counts down if a page is removed | ||||
|     while (tmp) | ||||
|     { | ||||
|         if (tmp == &page) | ||||
|         { | ||||
|             pages.remove(page); | ||||
|             numberOfPages--; | ||||
|  | ||||
|             if (!numberOfPages) | ||||
|             { | ||||
|                 setWidthHeight(0, 0); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 pageIndicator.setNumberOfPages(numberOfPages); | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             tmp = tmp->getNextSibling(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void SwipeContainer::setEndSwipeElasticWidth(uint16_t width) | ||||
| { | ||||
|     endElasticWidth = width; | ||||
| } | ||||
|  | ||||
| void SwipeContainer::setSwipeCutoff(uint16_t cutoff) | ||||
| { | ||||
|     swipeCutoff = cutoff; | ||||
| } | ||||
|  | ||||
| void SwipeContainer::setPageIndicatorBitmaps(const Bitmap& normalPage, const Bitmap& highlightedPage) | ||||
| { | ||||
|     pageIndicator.setBitmaps(normalPage, highlightedPage); | ||||
| } | ||||
|  | ||||
| void SwipeContainer::setPageIndicatorXY(int16_t x, int16_t y) | ||||
| { | ||||
|     pageIndicator.setXY(x, y); | ||||
| } | ||||
|  | ||||
| void SwipeContainer::setPageIndicatorXYWithCenteredX(int16_t x, int16_t y) | ||||
| { | ||||
|     pageIndicator.setXY(x - pageIndicator.getWidth() / 2, y); | ||||
| } | ||||
|  | ||||
| void SwipeContainer::setSelectedPage(uint8_t pageIndex) | ||||
| { | ||||
|     currentPage = pageIndex; | ||||
|     pageIndicator.setHighlightPosition(currentPage); | ||||
|     adjustPages(); | ||||
| } | ||||
|  | ||||
| uint8_t SwipeContainer::getSelectedPage() const | ||||
| { | ||||
|     return currentPage; | ||||
| } | ||||
|  | ||||
| void SwipeContainer::handleTickEvent() | ||||
| { | ||||
|     if (currentState == ANIMATE_SWIPE_CANCELLED_LEFT) | ||||
|     { | ||||
|         animateSwipeCancelledLeft(); | ||||
|     } | ||||
|     else if (currentState == ANIMATE_SWIPE_CANCELLED_RIGHT) | ||||
|     { | ||||
|         animateSwipeCancelledRight(); | ||||
|     } | ||||
|     else if (currentState == ANIMATE_LEFT) | ||||
|     { | ||||
|         animateLeft(); | ||||
|     } | ||||
|     else if (currentState == ANIMATE_RIGHT) | ||||
|     { | ||||
|         animateRight(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void SwipeContainer::handleClickEvent(const ClickEvent& evt) | ||||
| { | ||||
|     // If an animation is already in progress do not | ||||
|     // react to clicks | ||||
|     if (currentState != NO_ANIMATION) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (evt.getType() == ClickEvent::RELEASED) | ||||
|     { | ||||
|         // Save current position for use during animation | ||||
|         animateDistance = dragX; | ||||
|         startX = pages.getX(); | ||||
|  | ||||
|         if (dragX < 0) | ||||
|         { | ||||
|             if (currentPage == getNumberOfPages() - 1 || dragX > -swipeCutoff) | ||||
|             { | ||||
|                 currentState = ANIMATE_SWIPE_CANCELLED_LEFT; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 currentState = ANIMATE_LEFT; | ||||
|             } | ||||
|         } | ||||
|         else if (dragX > 0) | ||||
|         { | ||||
|             if (currentPage == 0 || dragX < swipeCutoff) | ||||
|             { | ||||
|                 currentState = ANIMATE_SWIPE_CANCELLED_RIGHT; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 currentState = ANIMATE_RIGHT; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void SwipeContainer::handleDragEvent(const DragEvent& evt) | ||||
| { | ||||
|     // If an animation is already in progress do not | ||||
|     // react to drags | ||||
|     if (currentState != NO_ANIMATION) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     dragX += evt.getDeltaX(); | ||||
|  | ||||
|     // Do not show too much background next to end pages | ||||
|     if (currentPage == 0 && dragX > endElasticWidth) | ||||
|     { | ||||
|         dragX = static_cast<int16_t>(endElasticWidth); | ||||
|     } | ||||
|     else if (currentPage == getNumberOfPages() - 1 && dragX < -endElasticWidth) | ||||
|     { | ||||
|         dragX = -static_cast<int16_t>(endElasticWidth); | ||||
|     } | ||||
|  | ||||
|     adjustPages(); | ||||
| } | ||||
|  | ||||
| void SwipeContainer::handleGestureEvent(const GestureEvent& evt) | ||||
| { | ||||
|     // Do not accept gestures while animating | ||||
|     if (currentState != NO_ANIMATION) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (evt.getType() == GestureEvent::SWIPE_HORIZONTAL) | ||||
|     { | ||||
|         // Save current position for use during animation | ||||
|         animateDistance = dragX; | ||||
|         startX = pages.getX(); | ||||
|  | ||||
|         if (evt.getVelocity() < 0 && currentPage < getNumberOfPages() - 1) | ||||
|         { | ||||
|             currentState = ANIMATE_LEFT; | ||||
|         } | ||||
|         else if (evt.getVelocity() > 0 && currentPage > 0) | ||||
|         { | ||||
|             currentState = ANIMATE_RIGHT; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void SwipeContainer::adjustPages() | ||||
| { | ||||
|     pages.moveTo(-static_cast<int16_t>(currentPage * getWidth()) + dragX, 0); | ||||
| } | ||||
|  | ||||
| void SwipeContainer::animateSwipeCancelledLeft() | ||||
| { | ||||
|     uint8_t duration = 14; | ||||
|  | ||||
|     if (animationCounter <= duration) | ||||
|     { | ||||
|         int16_t delta = EasingEquations::backEaseOut(animationCounter, 0, -animateDistance, duration); | ||||
|         dragX = animateDistance + delta; | ||||
|  | ||||
|         adjustPages(); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // Final step: stop the animation | ||||
|         currentState = NO_ANIMATION; | ||||
|         animationCounter = 0; | ||||
|         dragX = 0; | ||||
|         adjustPages(); | ||||
|     } | ||||
|     animationCounter++; | ||||
| } | ||||
|  | ||||
| void SwipeContainer::animateSwipeCancelledRight() | ||||
| { | ||||
|     uint8_t duration = 14; | ||||
|  | ||||
|     if (animationCounter <= duration) | ||||
|     { | ||||
|         int16_t delta = EasingEquations::backEaseOut(animationCounter, 0, animateDistance, duration); | ||||
|         dragX = animateDistance - delta; | ||||
|  | ||||
|         adjustPages(); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // Final step: stop the animation | ||||
|         currentState = NO_ANIMATION; | ||||
|         animationCounter = 0; | ||||
|         dragX = 0; | ||||
|         adjustPages(); | ||||
|     } | ||||
|     animationCounter++; | ||||
| } | ||||
|  | ||||
| void SwipeContainer::animateLeft() | ||||
| { | ||||
|     uint8_t duration = 10; | ||||
|  | ||||
|     if (animationCounter <= duration) | ||||
|     { | ||||
|         int16_t delta = EasingEquations::cubicEaseOut(animationCounter, 0, getWidth() + animateDistance, duration); | ||||
|         dragX = animateDistance - delta; | ||||
|  | ||||
|         adjustPages(); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // Final step: stop the animation | ||||
|         currentState = NO_ANIMATION; | ||||
|         animationCounter = 0; | ||||
|         currentPage++; | ||||
|         dragX = 0; | ||||
|         adjustPages(); | ||||
|         pageIndicator.goRight(); | ||||
|     } | ||||
|     animationCounter++; | ||||
| } | ||||
|  | ||||
| void SwipeContainer::animateRight() | ||||
| { | ||||
|     uint8_t duration = 10; | ||||
|  | ||||
|     if (animationCounter <= duration) | ||||
|     { | ||||
|         int16_t delta = EasingEquations::cubicEaseOut(animationCounter, 0, getWidth() - animateDistance, duration); | ||||
|         dragX = animateDistance + delta; | ||||
|  | ||||
|         adjustPages(); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // Final step: stop the animation | ||||
|         currentState = NO_ANIMATION; | ||||
|         animationCounter = 0; | ||||
|         currentPage--; | ||||
|         dragX = 0; | ||||
|         adjustPages(); | ||||
|         pageIndicator.goLeft(); | ||||
|     } | ||||
|     animationCounter++; | ||||
| } | ||||
|  | ||||
| SwipeContainer::PageIndicator::PageIndicator() | ||||
|     : Container(), | ||||
|       numberOfPages(0), | ||||
|       currentPage(0) | ||||
| { | ||||
|     unselectedPages.setXY(0, 0); | ||||
|     selectedPage.setXY(0, 0); | ||||
|  | ||||
|     Container::add(unselectedPages); | ||||
|     Container::add(selectedPage); | ||||
| } | ||||
|  | ||||
| void SwipeContainer::PageIndicator::setNumberOfPages(uint8_t size) | ||||
| { | ||||
|     numberOfPages = size; | ||||
|  | ||||
|     assert(numberOfPages > 0 && "At least one dot is needed"); | ||||
|  | ||||
|     numberOfPages = size; | ||||
|  | ||||
|     if (unselectedPages.getBitmapId() != BITMAP_INVALID) | ||||
|     { | ||||
|         int dotWidth = Bitmap(unselectedPages.getBitmap()).getWidth(); | ||||
|         unselectedPages.setWidth(dotWidth * size); | ||||
|  | ||||
|         // adjust size of container according to the actual bitmaps | ||||
|         setWidthHeight(unselectedPages); | ||||
|         setHighlightPosition(currentPage = 0); | ||||
|  | ||||
|         invalidate(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void SwipeContainer::PageIndicator::setBitmaps(const Bitmap& normalPage, const Bitmap& highlightedPage) | ||||
| { | ||||
|     selectedPage.setBitmap(highlightedPage); | ||||
|     unselectedPages.setBitmap(normalPage); | ||||
|     if (numberOfPages > 0) | ||||
|     { | ||||
|         setNumberOfPages(numberOfPages); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void SwipeContainer::PageIndicator::goRight() | ||||
| { | ||||
|     setHighlightPosition(currentPage = (currentPage + 1) % numberOfPages); | ||||
| } | ||||
|  | ||||
| void SwipeContainer::PageIndicator::goLeft() | ||||
| { | ||||
|     setHighlightPosition(currentPage = (currentPage + numberOfPages - 1) % numberOfPages); | ||||
| } | ||||
|  | ||||
| void SwipeContainer::PageIndicator::setHighlightPosition(uint8_t index) | ||||
| { | ||||
|     currentPage = index; | ||||
|     // note that index is unsigned | ||||
|     if (index < numberOfPages) | ||||
|     { | ||||
|         int dotWidth = Bitmap(unselectedPages.getBitmap()).getWidth(); | ||||
|         selectedPage.setX(index * dotWidth); | ||||
|     } | ||||
|     invalidate(); | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,288 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/ZoomAnimationImage.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| ZoomAnimationImage::ZoomAnimationImage() | ||||
|     : Container(), | ||||
|       currentState(NO_ANIMATION), | ||||
|       animationCounter(0), | ||||
|       zoomAnimationDelay(0), | ||||
|       currentZoomMode(FIXED_LEFT_AND_TOP), | ||||
|       zoomAnimationStartWidth(0), | ||||
|       zoomAnimationStartHeight(0), | ||||
|       zoomAnimationEndWidth(0), | ||||
|       zoomAnimationEndHeight(0), | ||||
|       zoomAnimationStartX(0), | ||||
|       zoomAnimationStartY(0), | ||||
|       zoomAnimationDeltaX(0), | ||||
|       zoomAnimationDeltaY(0), | ||||
|       moveAnimationEndX(0), | ||||
|       moveAnimationEndY(0), | ||||
|       animationDuration(0), | ||||
|       zoomAnimationWidthEquation(EasingEquations::linearEaseNone), | ||||
|       zoomAnimationHeightEquation(EasingEquations::linearEaseNone), | ||||
|       moveAnimationXEquation(EasingEquations::linearEaseNone), | ||||
|       moveAnimationYEquation(EasingEquations::linearEaseNone), | ||||
|       animationEndedAction(0) | ||||
| { | ||||
|     image.setXY(0, 0); | ||||
|     image.setVisible(false); | ||||
|  | ||||
|     scalableImage.setScalingAlgorithm(ScalableImage::NEAREST_NEIGHBOR); | ||||
|     scalableImage.setXY(0, 0); | ||||
|     scalableImage.setVisible(false); | ||||
|  | ||||
|     Container::add(image); | ||||
|     Container::add(scalableImage); | ||||
| } | ||||
|  | ||||
| void ZoomAnimationImage::startZoomAnimation(int16_t endWidth, int16_t endHeight, uint16_t duration, ZoomMode zoomMode, EasingEquation widthProgressionEquation, EasingEquation heightProgressionEquation) | ||||
| { | ||||
|     setCurrentState(ANIMATE_ZOOM); | ||||
|  | ||||
|     startTimerAndSetParameters(endWidth, endHeight, duration, zoomMode, widthProgressionEquation, heightProgressionEquation); | ||||
| } | ||||
|  | ||||
| void ZoomAnimationImage::startZoomAndMoveAnimation(int16_t endX, int16_t endY, int16_t endWidth, int16_t endHeight, uint16_t duration, ZoomMode zoomMode, EasingEquation xProgressionEquation, EasingEquation yProgressionEquation, EasingEquation widthProgressionEquation, EasingEquation heightProgressionEquation) | ||||
| { | ||||
|     moveAnimationEndX = endX; | ||||
|     moveAnimationEndY = endY; | ||||
|  | ||||
|     moveAnimationXEquation = xProgressionEquation; | ||||
|     moveAnimationYEquation = yProgressionEquation; | ||||
|  | ||||
|     setCurrentState(ANIMATE_ZOOM_AND_MOVE); | ||||
|  | ||||
|     startTimerAndSetParameters(endWidth, endHeight, duration, zoomMode, widthProgressionEquation, heightProgressionEquation); | ||||
| } | ||||
|  | ||||
| void ZoomAnimationImage::cancelZoomAnimation() | ||||
| { | ||||
|     Application::getInstance()->unregisterTimerWidget(this); | ||||
|     setCurrentState(NO_ANIMATION); | ||||
| } | ||||
|  | ||||
| void ZoomAnimationImage::handleTickEvent() | ||||
| { | ||||
|     if ((currentState == ANIMATE_ZOOM) || (currentState == ANIMATE_ZOOM_AND_MOVE)) | ||||
|     { | ||||
|         animationCounter++; | ||||
|         if (animationCounter >= zoomAnimationDelay) | ||||
|         { | ||||
|             // Adjust the used animationCounter for the startup delay | ||||
|             uint32_t actualAnimationCounter = animationCounter - zoomAnimationDelay; | ||||
|  | ||||
|             int16_t deltaWidth = zoomAnimationWidthEquation(actualAnimationCounter, 0, zoomAnimationEndWidth - zoomAnimationStartWidth, animationDuration); | ||||
|             int16_t deltaHeight = zoomAnimationHeightEquation(actualAnimationCounter, 0, zoomAnimationEndHeight - zoomAnimationStartHeight, animationDuration); | ||||
|  | ||||
|             setWidthHeight(zoomAnimationStartWidth + deltaWidth, zoomAnimationStartHeight + deltaHeight); | ||||
|  | ||||
|             int16_t deltaX; | ||||
|             int16_t deltaY; | ||||
|  | ||||
|             if (currentState == ANIMATE_ZOOM_AND_MOVE) | ||||
|             { | ||||
|                 deltaX = moveAnimationXEquation(actualAnimationCounter, 0, (moveAnimationEndX - zoomAnimationStartX) + zoomAnimationDeltaX, animationDuration); | ||||
|                 deltaY = moveAnimationYEquation(actualAnimationCounter, 0, (moveAnimationEndY - zoomAnimationStartY) + zoomAnimationDeltaY, animationDuration); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 deltaX = zoomAnimationWidthEquation(actualAnimationCounter, 0, zoomAnimationDeltaX, animationDuration); | ||||
|                 deltaY = zoomAnimationHeightEquation(actualAnimationCounter, 0, zoomAnimationDeltaY, animationDuration); | ||||
|             } | ||||
|             moveTo(zoomAnimationStartX + deltaX, zoomAnimationStartY + deltaY); | ||||
|  | ||||
|             if (animationCounter >= (uint32_t)(zoomAnimationDelay + animationDuration)) | ||||
|             { | ||||
|                 cancelZoomAnimation(); | ||||
|  | ||||
|                 if (animationEndedAction && animationEndedAction->isValid()) | ||||
|                 { | ||||
|                     animationEndedAction->execute(*this); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ZoomAnimationImage::setBitmaps(const Bitmap& smallBitmap, const Bitmap& largeBitmap) | ||||
| { | ||||
|     smallBmp = smallBitmap; | ||||
|     largeBmp = largeBitmap; | ||||
|  | ||||
|     scalableImage.setBitmap(largeBmp); | ||||
|  | ||||
|     ZoomAnimationImage::setWidthHeight(largeBmp); | ||||
| } | ||||
|  | ||||
| void ZoomAnimationImage::setWidth(int16_t width) | ||||
| { | ||||
|     invalidate(); | ||||
|     Container::setWidth(width); | ||||
|     updateRenderingMethod(); | ||||
| } | ||||
|  | ||||
| void ZoomAnimationImage::setHeight(int16_t height) | ||||
| { | ||||
|     invalidate(); | ||||
|     Container::setHeight(height); | ||||
|     updateRenderingMethod(); | ||||
| } | ||||
|  | ||||
| void ZoomAnimationImage::setDimension(int16_t width, int16_t height) | ||||
| { | ||||
|     Container::setWidthHeight(width, height); | ||||
| } | ||||
|  | ||||
| void ZoomAnimationImage::setScalingMode(ScalableImage::ScalingAlgorithm mode) | ||||
| { | ||||
|     scalableImage.setScalingAlgorithm(mode); | ||||
| } | ||||
|  | ||||
| ScalableImage::ScalingAlgorithm ZoomAnimationImage::getScalingMode() | ||||
| { | ||||
|     return scalableImage.getScalingAlgorithm(); | ||||
| } | ||||
|  | ||||
| void ZoomAnimationImage::setAlpha(const uint8_t newAlpha) | ||||
| { | ||||
|     image.setAlpha(newAlpha); | ||||
|     scalableImage.setAlpha(newAlpha); | ||||
| } | ||||
|  | ||||
| uint8_t ZoomAnimationImage::getAlpha() const | ||||
| { | ||||
|     return image.getAlpha(); | ||||
| } | ||||
|  | ||||
| void ZoomAnimationImage::setAnimationDelay(uint16_t delay) | ||||
| { | ||||
|     zoomAnimationDelay = delay; | ||||
| } | ||||
|  | ||||
| uint16_t ZoomAnimationImage::getAnimationDelay() const | ||||
| { | ||||
|     return zoomAnimationDelay; | ||||
| } | ||||
|  | ||||
| bool ZoomAnimationImage::isZoomAnimationRunning() const | ||||
| { | ||||
|     return currentState != NO_ANIMATION; | ||||
| } | ||||
|  | ||||
| void ZoomAnimationImage::updateRenderingMethod() | ||||
| { | ||||
|     if ((smallBmp.getWidth() == getWidth()) && (smallBmp.getHeight() == getHeight())) | ||||
|     { | ||||
|         image.setVisible(true); | ||||
|         scalableImage.setVisible(false); | ||||
|         image.setBitmap(smallBmp); | ||||
|         image.invalidate(); | ||||
|         scalableImage.invalidate(); | ||||
|     } | ||||
|     else if ((largeBmp.getWidth() == getWidth()) && (largeBmp.getHeight() == getHeight())) | ||||
|     { | ||||
|         image.setVisible(true); | ||||
|         scalableImage.setVisible(false); | ||||
|         image.setBitmap(largeBmp); | ||||
|         image.invalidate(); | ||||
|         scalableImage.invalidate(); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         image.setVisible(false); | ||||
|         image.invalidate(); | ||||
|         scalableImage.setVisible(true); | ||||
|         scalableImage.setWidthHeight(*this); | ||||
|         scalableImage.invalidate(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ZoomAnimationImage::setCurrentState(States state) | ||||
| { | ||||
|     currentState = state; | ||||
|     animationCounter = 0; | ||||
| } | ||||
|  | ||||
| void ZoomAnimationImage::startTimerAndSetParameters(int16_t endWidth, int16_t endHeight, uint16_t duration, ZoomMode zoomMode, EasingEquation widthProgressionEquation, EasingEquation heightProgressionEquation) | ||||
| { | ||||
|     Application::getInstance()->registerTimerWidget(this); | ||||
|  | ||||
|     currentZoomMode = zoomMode; | ||||
|  | ||||
|     zoomAnimationStartX = getX(); | ||||
|     zoomAnimationStartY = getY(); | ||||
|     zoomAnimationStartWidth = getWidth(); | ||||
|     zoomAnimationStartHeight = getHeight(); | ||||
|     zoomAnimationEndWidth = endWidth; | ||||
|     zoomAnimationEndHeight = endHeight; | ||||
|     animationDuration = duration; | ||||
|  | ||||
|     zoomAnimationWidthEquation = widthProgressionEquation; | ||||
|     zoomAnimationHeightEquation = heightProgressionEquation; | ||||
|  | ||||
|     updateZoomAnimationDeltaXY(); | ||||
|  | ||||
|     if (zoomAnimationDelay == 0 && animationDuration == 0) | ||||
|     { | ||||
|         handleTickEvent(); // Finish the zoom and move operation immediately | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ZoomAnimationImage::updateZoomAnimationDeltaXY() | ||||
| { | ||||
|     zoomAnimationDeltaX = zoomAnimationStartWidth - zoomAnimationEndWidth; | ||||
|     zoomAnimationDeltaY = zoomAnimationStartHeight - zoomAnimationEndHeight; | ||||
|  | ||||
|     switch (currentZoomMode) | ||||
|     { | ||||
|     case FIXED_CENTER: | ||||
|         zoomAnimationDeltaX /= 2; | ||||
|         zoomAnimationDeltaY /= 2; | ||||
|         break; | ||||
|     case FIXED_LEFT: | ||||
|         zoomAnimationDeltaX = 0; | ||||
|         zoomAnimationDeltaY /= 2; | ||||
|         break; | ||||
|     case FIXED_RIGHT: | ||||
|         zoomAnimationDeltaY /= 2; | ||||
|         break; | ||||
|     case FIXED_TOP: | ||||
|         zoomAnimationDeltaX /= 2; | ||||
|         zoomAnimationDeltaY = 0; | ||||
|         break; | ||||
|     case FIXED_BOTTOM: | ||||
|         zoomAnimationDeltaX /= 2; | ||||
|         break; | ||||
|     case FIXED_LEFT_AND_TOP: | ||||
|         zoomAnimationDeltaX = 0; | ||||
|         zoomAnimationDeltaY = 0; | ||||
|         break; | ||||
|     case FIXED_RIGHT_AND_TOP: | ||||
|         zoomAnimationDeltaY = 0; | ||||
|         break; | ||||
|     case FIXED_LEFT_AND_BOTTOM: | ||||
|         zoomAnimationDeltaX = 0; | ||||
|         break; | ||||
|     case FIXED_RIGHT_AND_BOTTOM: | ||||
|         break; | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,71 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/clock/AbstractClock.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| AbstractClock::AbstractClock() | ||||
|     : Container(), | ||||
|       currentHour(0), | ||||
|       currentMinute(0), | ||||
|       currentSecond(0) | ||||
| { | ||||
| } | ||||
|  | ||||
| void AbstractClock::setTime24Hour(uint8_t hour, uint8_t minute, uint8_t second) | ||||
| { | ||||
|     currentHour = hour % 24; | ||||
|     currentMinute = minute % 60; | ||||
|     currentSecond = second % 60; | ||||
|  | ||||
|     updateClock(); | ||||
| } | ||||
|  | ||||
| void AbstractClock::setTime12Hour(uint8_t hour, uint8_t minute, uint8_t second, bool am) | ||||
| { | ||||
|     setTime24Hour((hour % 12) + (am ? 0 : 12), minute, second); | ||||
| } | ||||
|  | ||||
| uint8_t AbstractClock::getCurrentHour() const | ||||
| { | ||||
|     return currentHour; | ||||
| } | ||||
|  | ||||
| uint8_t AbstractClock::getCurrentHour24() const | ||||
| { | ||||
|     return currentHour; | ||||
| } | ||||
|  | ||||
| uint8_t AbstractClock::getCurrentHour12() const | ||||
| { | ||||
|     return ((currentHour + 11) % 12) + 1; | ||||
| } | ||||
|  | ||||
| bool AbstractClock::getCurrentAM() const | ||||
| { | ||||
|     return currentHour < 12; | ||||
| } | ||||
|  | ||||
| uint8_t AbstractClock::getCurrentMinute() const | ||||
| { | ||||
|     return currentMinute; | ||||
| } | ||||
|  | ||||
| uint8_t AbstractClock::getCurrentSecond() const | ||||
| { | ||||
|     return currentSecond; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,243 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/clock/AnalogClock.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| AnalogClock::AnalogClock() | ||||
|     : AbstractClock(), | ||||
|       animationEquation(EasingEquations::linearEaseNone), | ||||
|       animationDuration(0), | ||||
|       clockRotationCenterX(0), | ||||
|       clockRotationCenterY(0), | ||||
|       lastHour(0), | ||||
|       lastMinute(0), | ||||
|       lastSecond(0), | ||||
|       hourHandMinuteCorrectionActive(false), | ||||
|       minuteHandSecondCorrectionActive(false) | ||||
| { | ||||
|     AnalogClock::add(background); | ||||
|  | ||||
|     hourHand.updateZAngle(0.f); | ||||
|     minuteHand.updateZAngle(0.f); | ||||
|     secondHand.updateZAngle(0.f); | ||||
|  | ||||
|     hourHand.setVisible(false); | ||||
|     minuteHand.setVisible(false); | ||||
|     secondHand.setVisible(false); | ||||
| } | ||||
|  | ||||
| void AnalogClock::setBackground(const BitmapId backgroundBitmapId) | ||||
| { | ||||
|     setBackground(backgroundBitmapId, Bitmap(backgroundBitmapId).getWidth() / 2, Bitmap(backgroundBitmapId).getHeight() / 2); | ||||
| } | ||||
|  | ||||
| void AnalogClock::setBackground(const BitmapId backgroundBitmapId, const int16_t rotationCenterX, const int16_t rotationCenterY) | ||||
| { | ||||
|     background.setBitmap(Bitmap(backgroundBitmapId)); | ||||
|     setWidthHeight(background); | ||||
|  | ||||
|     setRotationCenter(rotationCenterX, rotationCenterY); | ||||
| } | ||||
|  | ||||
| void AnalogClock::setRotationCenter(int16_t rotationCenterX, int16_t rotationCenterY) | ||||
| { | ||||
|     clockRotationCenterX = rotationCenterX; | ||||
|     clockRotationCenterY = rotationCenterY; | ||||
| } | ||||
|  | ||||
| void AnalogClock::setupHourHand(const BitmapId hourHandBitmapId, int16_t rotationCenterX, int16_t rotationCenterY) | ||||
| { | ||||
|     setupHand(hourHand, hourHandBitmapId, rotationCenterX, rotationCenterY); | ||||
| } | ||||
|  | ||||
| void AnalogClock::setupMinuteHand(const BitmapId minuteHandBitmapId, int16_t rotationCenterX, int16_t rotationCenterY) | ||||
| { | ||||
|     setupHand(minuteHand, minuteHandBitmapId, rotationCenterX, rotationCenterY); | ||||
| } | ||||
|  | ||||
| void AnalogClock::setupSecondHand(const BitmapId secondHandBitmapId, int16_t rotationCenterX, int16_t rotationCenterY) | ||||
| { | ||||
|     setupHand(secondHand, secondHandBitmapId, rotationCenterX, rotationCenterY); | ||||
| } | ||||
|  | ||||
| void AnalogClock::setupHand(TextureMapper& hand, const BitmapId bitmapId, int16_t rotationCenterX, int16_t rotationCenterY) | ||||
| { | ||||
|     remove(hand); | ||||
|  | ||||
|     hand.setBitmap(Bitmap(bitmapId)); | ||||
|     hand.setWidthHeight(*this); | ||||
|     hand.setXY(0, 0); | ||||
|     hand.setBitmapPosition(clockRotationCenterX - rotationCenterX, clockRotationCenterY - rotationCenterY); | ||||
|     hand.setCameraDistance(300.0f); | ||||
|     hand.setOrigo((float)clockRotationCenterX, (float)clockRotationCenterY, hand.getCameraDistance()); | ||||
|     hand.setCamera(hand.getOrigoX(), hand.getOrigoY()); | ||||
|     hand.setRenderingAlgorithm(TextureMapper::BILINEAR_INTERPOLATION); | ||||
|  | ||||
|     add(hand); | ||||
|     hand.setVisible(true); | ||||
| } | ||||
|  | ||||
| void AnalogClock::initializeTime24Hour(uint8_t hour, uint8_t minute, uint8_t second) | ||||
| { | ||||
|     lastHour = 255; | ||||
|     lastMinute = 255; | ||||
|     lastSecond = 255; | ||||
|  | ||||
|     // Disable animation and set time | ||||
|     uint16_t tempAnimationDuration = animationDuration; | ||||
|     animationDuration = 1; | ||||
|     setTime24Hour(hour, minute, second); | ||||
|  | ||||
|     animationDuration = tempAnimationDuration; | ||||
| } | ||||
|  | ||||
| void AnalogClock::initializeTime12Hour(uint8_t hour, uint8_t minute, uint8_t second, bool am) | ||||
| { | ||||
|     initializeTime24Hour((hour % 12) + (am ? 0 : 12), minute, second); | ||||
| } | ||||
|  | ||||
| void AnalogClock::setAlpha(uint8_t newAlpha) | ||||
| { | ||||
|     background.setAlpha(newAlpha); | ||||
|     hourHand.setAlpha(newAlpha); | ||||
|     minuteHand.setAlpha(newAlpha); | ||||
|     secondHand.setAlpha(newAlpha); | ||||
| } | ||||
|  | ||||
| uint8_t AnalogClock::getAlpha() const | ||||
| { | ||||
|     return background.getAlpha(); | ||||
| } | ||||
|  | ||||
| void AnalogClock::updateClock() | ||||
| { | ||||
|     // Make sure that animating to 0 will move from left to right | ||||
|     if (lastHour != 0 && currentHour == 0) | ||||
|     { | ||||
|         hourHand.updateZAngle(hourHand.getZAngle() - (2 * PI)); | ||||
|     } | ||||
|     if (lastMinute != 0 && currentMinute == 0) | ||||
|     { | ||||
|         minuteHand.updateZAngle(minuteHand.getZAngle() - (2 * PI)); | ||||
|     } | ||||
|     if (lastSecond != 0 && currentSecond == 0) | ||||
|     { | ||||
|         secondHand.updateZAngle(secondHand.getZAngle() - (2 * PI)); | ||||
|     } | ||||
|  | ||||
|     float newHandAngle; | ||||
|  | ||||
|     // Move hour hand | ||||
|     if (hourHand.isVisible() && ((currentHour != lastHour) || (hourHandMinuteCorrectionActive && (currentMinute != lastMinute)))) | ||||
|     { | ||||
|         newHandAngle = convertHandValueToAngle(12, currentHour, hourHandMinuteCorrectionActive ? currentMinute : 0); | ||||
|         if (animationEnabled() && !hourHand.isTextureMapperAnimationRunning()) | ||||
|         { | ||||
|             hourHand.setupAnimation(AnimationTextureMapper::Z_ROTATION, newHandAngle, animationDuration, 0, animationEquation); | ||||
|             hourHand.startAnimation(); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (animationEnabled()) | ||||
|             { | ||||
|                 hourHand.cancelAnimationTextureMapperAnimation(); | ||||
|             } | ||||
|             hourHand.updateZAngle(newHandAngle); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Move minute hand | ||||
|     if (minuteHand.isVisible() && ((currentMinute != lastMinute) || (minuteHandSecondCorrectionActive && (currentSecond != lastSecond)))) | ||||
|     { | ||||
|         newHandAngle = convertHandValueToAngle(60, currentMinute, minuteHandSecondCorrectionActive ? currentSecond : 0); | ||||
|         if (animationEnabled() && !minuteHand.isTextureMapperAnimationRunning()) | ||||
|         { | ||||
|             minuteHand.setupAnimation(AnimationTextureMapper::Z_ROTATION, newHandAngle, animationDuration, 0, animationEquation); | ||||
|             minuteHand.startAnimation(); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (animationEnabled()) | ||||
|             { | ||||
|                 minuteHand.cancelAnimationTextureMapperAnimation(); | ||||
|             } | ||||
|             minuteHand.updateZAngle(newHandAngle); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Move second hand | ||||
|     if (secondHand.isVisible() && (currentSecond != lastSecond)) | ||||
|     { | ||||
|         newHandAngle = convertHandValueToAngle(60, currentSecond); | ||||
|         if (animationEnabled() && !secondHand.isTextureMapperAnimationRunning()) | ||||
|         { | ||||
|             secondHand.setupAnimation(AnimationTextureMapper::Z_ROTATION, newHandAngle, animationDuration, 0, animationEquation); | ||||
|             secondHand.startAnimation(); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (animationEnabled()) | ||||
|             { | ||||
|                 secondHand.cancelAnimationTextureMapperAnimation(); | ||||
|             } | ||||
|             secondHand.updateZAngle(newHandAngle); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     lastHour = currentHour; | ||||
|     lastMinute = currentMinute; | ||||
|     lastSecond = currentSecond; | ||||
| } | ||||
|  | ||||
| float AnalogClock::convertHandValueToAngle(uint8_t steps, uint8_t handValue, uint8_t secondHandValue /*= 0*/) const | ||||
| { | ||||
|     return ((handValue / (float)steps) + (secondHandValue / (steps * 60.f))) * 2 * PI; | ||||
| } | ||||
|  | ||||
| void AnalogClock::setHourHandMinuteCorrection(bool active) | ||||
| { | ||||
|     hourHandMinuteCorrectionActive = active; | ||||
|     setTime24Hour(getCurrentHour(), getCurrentMinute(), getCurrentSecond()); | ||||
| } | ||||
|  | ||||
| bool AnalogClock::getHourHandMinuteCorrection() const | ||||
| { | ||||
|     return hourHandMinuteCorrectionActive; | ||||
| } | ||||
|  | ||||
| void AnalogClock::setMinuteHandSecondCorrection(bool active) | ||||
| { | ||||
|     minuteHandSecondCorrectionActive = active; | ||||
|     setTime24Hour(getCurrentHour(), getCurrentMinute(), getCurrentSecond()); | ||||
| } | ||||
|  | ||||
| bool AnalogClock::getMinuteHandSecondCorrection() const | ||||
| { | ||||
|     return minuteHandSecondCorrectionActive; | ||||
| } | ||||
|  | ||||
| bool AnalogClock::animationEnabled() const | ||||
| { | ||||
|     return animationDuration > 1; | ||||
| } | ||||
|  | ||||
| void AnalogClock::setAnimation(uint16_t duration, EasingEquation animationProgressionEquation) | ||||
| { | ||||
|     animationDuration = duration; | ||||
|     animationEquation = animationProgressionEquation; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,107 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/clock/DigitalClock.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| DigitalClock::DigitalClock() | ||||
|     : AbstractClock(), | ||||
|       displayMode(DISPLAY_24_HOUR), | ||||
|       useLeadingZeroForHourIndicator(false) | ||||
| { | ||||
|     buffer[0] = '\0'; | ||||
|     text.setXY(0, 0); | ||||
|     text.setWildcard(buffer); | ||||
|     Container::add(text); | ||||
| } | ||||
|  | ||||
| void DigitalClock::setWidth(int16_t width) | ||||
| { | ||||
|     Container::setWidth(width); | ||||
|     text.setWidth(width); | ||||
| } | ||||
|  | ||||
| void DigitalClock::setHeight(int16_t height) | ||||
| { | ||||
|     Container::setHeight(height); | ||||
|     text.setHeight(height); | ||||
| } | ||||
|  | ||||
| void DigitalClock::setBaselineY(int16_t baselineY) | ||||
| { | ||||
|     if (text.getTypedText().hasValidId()) | ||||
|     { | ||||
|         moveTo(getX(), baselineY - text.getTypedText().getFont()->getFontHeight()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void DigitalClock::displayLeadingZeroForHourIndicator(bool displayLeadingZero) | ||||
| { | ||||
|     useLeadingZeroForHourIndicator = displayLeadingZero; | ||||
| } | ||||
|  | ||||
| void DigitalClock::setAlpha(uint8_t newAlpha) | ||||
| { | ||||
|     text.setAlpha(newAlpha); | ||||
| } | ||||
|  | ||||
| uint8_t DigitalClock::getAlpha() const | ||||
| { | ||||
|     return text.getAlpha(); | ||||
| } | ||||
|  | ||||
| void DigitalClock::setTypedText(TypedText typedText) | ||||
| { | ||||
|     text.setTypedText(typedText); | ||||
|     text.invalidate(); | ||||
| } | ||||
|  | ||||
| void DigitalClock::setColor(colortype color) | ||||
| { | ||||
|     text.setColor(color); | ||||
|     text.invalidate(); | ||||
| } | ||||
|  | ||||
| colortype DigitalClock::getColor() const | ||||
| { | ||||
|     return text.getColor(); | ||||
| } | ||||
|  | ||||
| void DigitalClock::updateClock() | ||||
| { | ||||
|     if (displayMode == DISPLAY_12_HOUR_NO_SECONDS) | ||||
|     { | ||||
|         const char* format = useLeadingZeroForHourIndicator ? "%02d:%02d %cM" : "%d:%02d %cM"; | ||||
|         Unicode::snprintf(buffer, BUFFER_SIZE, format, getCurrentHour12(), getCurrentMinute(), getCurrentAM() ? 'A' : 'P'); | ||||
|     } | ||||
|     else if (displayMode == DISPLAY_24_HOUR_NO_SECONDS) | ||||
|     { | ||||
|         const char* format = useLeadingZeroForHourIndicator ? "%02d:%02d" : "%d:%02d"; | ||||
|         Unicode::snprintf(buffer, BUFFER_SIZE, format, getCurrentHour24(), getCurrentMinute()); | ||||
|     } | ||||
|     else if (displayMode == DISPLAY_12_HOUR) | ||||
|     { | ||||
|         const char* format = useLeadingZeroForHourIndicator ? "%02d:%02d:%02d %cM" : "%d:%02d:%02d %cM"; | ||||
|         Unicode::snprintf(buffer, BUFFER_SIZE, format, getCurrentHour12(), getCurrentMinute(), getCurrentSecond(), getCurrentAM() ? 'A' : 'P'); | ||||
|     } | ||||
|     else if (displayMode == DISPLAY_24_HOUR) | ||||
|     { | ||||
|         const char* format = useLeadingZeroForHourIndicator ? "%02d:%02d:%02d" : "%d:%02d:%02d"; | ||||
|         Unicode::snprintf(buffer, BUFFER_SIZE, format, getCurrentHour24(), getCurrentMinute(), getCurrentSecond()); | ||||
|     } | ||||
|     text.invalidate(); | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,40 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/progress_indicators/AbstractDirectionProgress.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| AbstractDirectionProgress::AbstractDirectionProgress() | ||||
|     : AbstractProgressIndicator() | ||||
| { | ||||
|     AbstractDirectionProgress::setDirection(RIGHT); | ||||
| } | ||||
|  | ||||
| void AbstractDirectionProgress::setDirection(DirectionType direction) | ||||
| { | ||||
|     if (direction != progressDirection) | ||||
|     { | ||||
|         progressDirection = direction; | ||||
|         progressIndicatorContainer.invalidate(); | ||||
|         setValue(getValue()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| AbstractDirectionProgress::DirectionType AbstractDirectionProgress::getDirection() const | ||||
| { | ||||
|     return progressDirection; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,224 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/progress_indicators/AbstractProgressIndicator.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| AbstractProgressIndicator::AbstractProgressIndicator() | ||||
|     : Container(), rangeMin(0), rangeMax(100), currentValue(0), rangeSteps(100), rangeStepsMin(0), | ||||
|       equation(&EasingEquations::linearEaseNone), animationStartValue(0), animationEndValue(0), animationDuration(0), animationStep(0), | ||||
|       valueSetCallback(0), valueUpdatedCallback(0) | ||||
| { | ||||
|     background.setXY(0, 0); | ||||
|     AbstractProgressIndicator::add(background); | ||||
|     AbstractProgressIndicator::add(progressIndicatorContainer); | ||||
| } | ||||
|  | ||||
| void AbstractProgressIndicator::setBackground(const Bitmap& bitmapBackground) | ||||
| { | ||||
|     background.setBitmap(bitmapBackground); | ||||
|     setWidthHeight(background); | ||||
| } | ||||
|  | ||||
| void AbstractProgressIndicator::setProgressIndicatorPosition(int16_t x, int16_t y, int16_t width, int16_t height) | ||||
| { | ||||
|     progressIndicatorContainer.setPosition(x, y, width, height); | ||||
|  | ||||
|     if (getWidth() < x + width) | ||||
|     { | ||||
|         AbstractProgressIndicator::setWidth(x + width); | ||||
|     } | ||||
|  | ||||
|     if (getHeight() < y + height) | ||||
|     { | ||||
|         AbstractProgressIndicator::setHeight(y + height); | ||||
|     } | ||||
| } | ||||
|  | ||||
| int16_t AbstractProgressIndicator::getProgressIndicatorX() const | ||||
| { | ||||
|     return progressIndicatorContainer.getX(); | ||||
| } | ||||
|  | ||||
| int16_t AbstractProgressIndicator::getProgressIndicatorY() const | ||||
| { | ||||
|     return progressIndicatorContainer.getY(); | ||||
| } | ||||
|  | ||||
| int16_t AbstractProgressIndicator::getProgressIndicatorWidth() const | ||||
| { | ||||
|     return progressIndicatorContainer.getWidth(); | ||||
| } | ||||
|  | ||||
| int16_t AbstractProgressIndicator::getProgressIndicatorHeight() const | ||||
| { | ||||
|     return progressIndicatorContainer.getHeight(); | ||||
| } | ||||
|  | ||||
| void AbstractProgressIndicator::setRange(int min, int max, uint16_t steps /*= 0*/, uint16_t minStep /*= 0*/) | ||||
| { | ||||
|     assert(min < max); | ||||
|     rangeMin = min; | ||||
|     rangeMax = max; | ||||
|     setValue(currentValue); | ||||
|     if (steps == 0) | ||||
|     { | ||||
|         rangeSteps = max - min; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         rangeSteps = steps; | ||||
|     } | ||||
|     rangeStepsMin = minStep; | ||||
|     assert(rangeStepsMin < rangeSteps); | ||||
| } | ||||
|  | ||||
| void AbstractProgressIndicator::getRange(int& min, int& max, uint16_t& steps, uint16_t& minStep) const | ||||
| { | ||||
|     min = rangeMin; | ||||
|     max = rangeMax; | ||||
|     steps = rangeSteps; | ||||
|     minStep = rangeStepsMin; | ||||
| } | ||||
|  | ||||
| void AbstractProgressIndicator::getRange(int& min, int& max, uint16_t& steps) const | ||||
| { | ||||
|     min = rangeMin; | ||||
|     max = rangeMax; | ||||
|     steps = rangeSteps; | ||||
| } | ||||
|  | ||||
| void AbstractProgressIndicator::getRange(int& min, int& max) const | ||||
| { | ||||
|     min = rangeMin; | ||||
|     max = rangeMax; | ||||
| } | ||||
|  | ||||
| void AbstractProgressIndicator::setValue(int value) | ||||
| { | ||||
|     value = MAX(value, rangeMin); | ||||
|     value = MIN(value, rangeMax); | ||||
|     if (value != currentValue) | ||||
|     { | ||||
|         currentValue = value; | ||||
|         if (valueSetCallback && valueSetCallback->isValid()) | ||||
|         { | ||||
|             valueSetCallback->execute(*this); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AbstractProgressIndicator::setEasingEquation(EasingEquation easingEquation) | ||||
| { | ||||
|     equation = easingEquation; | ||||
| } | ||||
|  | ||||
| void AbstractProgressIndicator::updateValue(int value, uint16_t duration) | ||||
| { | ||||
|     value = MAX(value, rangeMin); | ||||
|     value = MIN(value, rangeMax); | ||||
|     if (duration == 0) | ||||
|     { | ||||
|         setValue(value); | ||||
|         if (valueUpdatedCallback && valueUpdatedCallback->isValid()) | ||||
|         { | ||||
|             valueUpdatedCallback->execute(*this); | ||||
|         } | ||||
|         return; | ||||
|     } | ||||
|     if (animationDuration > 0) | ||||
|     { | ||||
|         // Old animation is running, stop it first | ||||
|         Application::getInstance()->unregisterTimerWidget(this); | ||||
|     } | ||||
|     animationStartValue = getValue(); | ||||
|     animationEndValue = value; | ||||
|     animationDuration = duration; | ||||
|     animationStep = 0; | ||||
|     Application::getInstance()->registerTimerWidget(this); | ||||
| } | ||||
|  | ||||
| int AbstractProgressIndicator::getValue() const | ||||
| { | ||||
|     return currentValue; | ||||
| } | ||||
|  | ||||
| uint16_t AbstractProgressIndicator::getProgress(uint16_t range /*= 100*/) const | ||||
| { | ||||
|     if (range == 0) | ||||
|     { | ||||
|         return 0; | ||||
|     } | ||||
|     int32_t remainder; // Not used here | ||||
|     // Find out at what step the current value is. | ||||
|     int32_t step = rangeStepsMin + muldiv(currentValue - rangeMin, rangeSteps - rangeStepsMin, rangeMax - rangeMin, remainder); | ||||
|     // Scale the step up to [0..range] | ||||
|     int32_t prog = muldiv(step, range, rangeSteps, remainder); | ||||
|     return (uint16_t)prog; | ||||
| } | ||||
|  | ||||
| void AbstractProgressIndicator::setValueSetAction(GenericCallback<const AbstractProgressIndicator&>& callback) | ||||
| { | ||||
|     valueSetCallback = &callback; | ||||
| } | ||||
|  | ||||
| void AbstractProgressIndicator::handleTickEvent() | ||||
| { | ||||
|     animationStep++; | ||||
|     int16_t delta = (int16_t)equation(animationStep, 0, animationEndValue - animationStartValue, animationDuration); | ||||
|     setValue(animationStartValue + delta); | ||||
|     if (animationStep >= animationDuration) | ||||
|     { | ||||
|         animationDuration = 0; | ||||
|         animationStep = 0; | ||||
|         Application::getInstance()->unregisterTimerWidget(this); | ||||
|         if (valueUpdatedCallback && valueUpdatedCallback->isValid()) | ||||
|         { | ||||
|             valueUpdatedCallback->execute(*this); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AbstractProgressIndicator::setValueUpdatedAction(GenericCallback<const AbstractProgressIndicator&>& callback) | ||||
| { | ||||
|     valueUpdatedCallback = &callback; | ||||
| } | ||||
|  | ||||
| void AbstractProgressIndicator::getRange(int16_t& min, int16_t& max, uint16_t& steps, uint16_t& minStep) const | ||||
| { | ||||
|     int imin, imax; | ||||
|     getRange(imin, imax, steps, minStep); | ||||
|     min = (int16_t)imin; | ||||
|     max = (int16_t)imax; | ||||
| } | ||||
|  | ||||
| void AbstractProgressIndicator::getRange(int16_t& min, int16_t& max, uint16_t& steps) const | ||||
| { | ||||
|     int imin, imax; | ||||
|     getRange(imin, imax, steps); | ||||
|     min = (int16_t)imin; | ||||
|     max = (int16_t)imax; | ||||
| } | ||||
|  | ||||
| void AbstractProgressIndicator::getRange(int16_t& min, int16_t& max) const | ||||
| { | ||||
|     int imin, imax; | ||||
|     getRange(imin, imax); | ||||
|     min = (int16_t)imin; | ||||
|     max = (int16_t)imax; | ||||
| } | ||||
|  | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,108 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/progress_indicators/BoxProgress.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| BoxProgress::BoxProgress() | ||||
|     : AbstractDirectionProgress(), box() | ||||
| { | ||||
|     progressIndicatorContainer.add(box); | ||||
| } | ||||
|  | ||||
| void BoxProgress::setProgressIndicatorPosition(int16_t x, int16_t y, int16_t width, int16_t height) | ||||
| { | ||||
|     box.setPosition(0, 0, width, height); | ||||
|  | ||||
|     AbstractProgressIndicator::setProgressIndicatorPosition(x, y, width, height); | ||||
| } | ||||
|  | ||||
| void BoxProgress::setColor(colortype color) | ||||
| { | ||||
|     box.setColor(color); | ||||
| } | ||||
|  | ||||
| colortype BoxProgress::getColor() const | ||||
| { | ||||
|     return box.getColor(); | ||||
| } | ||||
|  | ||||
| void BoxProgress::setAlpha(uint8_t newAlpha) | ||||
| { | ||||
|     box.setAlpha(newAlpha); | ||||
| } | ||||
|  | ||||
| uint8_t BoxProgress::getAlpha() const | ||||
| { | ||||
|     return box.getAlpha(); | ||||
| } | ||||
|  | ||||
| void BoxProgress::setValue(int value) | ||||
| { | ||||
|     AbstractProgressIndicator::setValue(value); | ||||
|     int16_t progress = 0; | ||||
|     switch (progressDirection) | ||||
|     { | ||||
|     case RIGHT: | ||||
|     case LEFT: | ||||
|         progress = AbstractProgressIndicator::getProgress(progressIndicatorContainer.getWidth()); | ||||
|         break; | ||||
|     case DOWN: | ||||
|     case UP: | ||||
|         progress = AbstractProgressIndicator::getProgress(progressIndicatorContainer.getHeight()); | ||||
|         break; | ||||
|     } | ||||
|     switch (progressDirection) | ||||
|     { | ||||
|     case RIGHT: | ||||
|         { | ||||
|             int16_t oldWidth = box.getWidth(); | ||||
|             box.setPosition(0, 0, progress, progressIndicatorContainer.getHeight()); | ||||
|             int16_t newWidth = box.getWidth(); | ||||
|             Rect rect(MIN(oldWidth, newWidth), 0, abs(oldWidth - newWidth), box.getHeight()); | ||||
|             progressIndicatorContainer.invalidateRect(rect); | ||||
|             break; | ||||
|         } | ||||
|     case LEFT: | ||||
|         { | ||||
|             int16_t oldX = box.getX(); | ||||
|             box.setPosition(getWidth() - progress, 0, progress, progressIndicatorContainer.getHeight()); | ||||
|             int16_t newX = box.getX(); | ||||
|             Rect rect(MIN(oldX, newX), 0, abs(oldX - newX), box.getHeight()); | ||||
|             progressIndicatorContainer.invalidateRect(rect); | ||||
|             break; | ||||
|         } | ||||
|     case DOWN: | ||||
|         { | ||||
|             int16_t oldHeight = box.getHeight(); | ||||
|             box.setPosition(0, 0, progressIndicatorContainer.getWidth(), progress); | ||||
|             int16_t newHeight = box.getHeight(); | ||||
|             Rect rect(0, MIN(oldHeight, newHeight), box.getWidth(), abs(oldHeight - newHeight)); | ||||
|             progressIndicatorContainer.invalidateRect(rect); | ||||
|             break; | ||||
|         } | ||||
|     case UP: | ||||
|         { | ||||
|             int16_t oldY = box.getY(); | ||||
|             box.setPosition(0, progressIndicatorContainer.getHeight() - progress, progressIndicatorContainer.getWidth(), progress); | ||||
|             int16_t newY = box.getY(); | ||||
|             Rect rect(0, MIN(oldY, newY), box.getWidth(), abs(oldY - newY)); | ||||
|             progressIndicatorContainer.invalidateRect(rect); | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,124 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/progress_indicators/CircleProgress.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| CircleProgress::CircleProgress() | ||||
|     : AbstractProgressIndicator(), circle() | ||||
| { | ||||
|     progressIndicatorContainer.add(circle); | ||||
|     circle.setPosition(0, 0, getWidth(), getHeight()); | ||||
|     CircleProgress::setStartEndAngle(0, 360); | ||||
| } | ||||
|  | ||||
| void CircleProgress::setProgressIndicatorPosition(int16_t x, int16_t y, int16_t width, int16_t height) | ||||
| { | ||||
|     circle.setPosition(0, 0, width, height); | ||||
|  | ||||
|     AbstractProgressIndicator::setProgressIndicatorPosition(x, y, width, height); | ||||
| } | ||||
|  | ||||
| void CircleProgress::setPainter(AbstractPainter& painter) | ||||
| { | ||||
|     circle.setPainter(painter); | ||||
| } | ||||
|  | ||||
| void CircleProgress::setCenter(int x, int y) | ||||
| { | ||||
|     circle.setCenter(x, y); | ||||
| } | ||||
|  | ||||
| void CircleProgress::getCenter(int& x, int& y) const | ||||
| { | ||||
|     circle.getCenter(x, y); | ||||
| } | ||||
|  | ||||
| void CircleProgress::setRadius(int r) | ||||
| { | ||||
|     circle.setRadius(r); | ||||
| } | ||||
|  | ||||
| int CircleProgress::getRadius() const | ||||
| { | ||||
|     int radius; | ||||
|     circle.getRadius(radius); | ||||
|     return radius; | ||||
| } | ||||
|  | ||||
| void CircleProgress::setLineWidth(int width) | ||||
| { | ||||
|     circle.setLineWidth(width); | ||||
| } | ||||
|  | ||||
| int CircleProgress::getLineWidth() const | ||||
| { | ||||
|     int width; | ||||
|     circle.getLineWidth(width); | ||||
|     return width; | ||||
| } | ||||
|  | ||||
| void CircleProgress::setCapPrecision(int precision) | ||||
| { | ||||
|     circle.setCapPrecision(precision); | ||||
| } | ||||
|  | ||||
| void CircleProgress::setStartEndAngle(int startAngle, int endAngle) | ||||
| { | ||||
|     assert(startAngle != endAngle); | ||||
|     circle.setArc(startAngle, endAngle); | ||||
|     circleEndAngle = endAngle; | ||||
|     CircleProgress::setValue(CircleProgress::getValue()); | ||||
| } | ||||
|  | ||||
| int CircleProgress::getStartAngle() const | ||||
| { | ||||
|     return circle.getArcStart(); | ||||
| } | ||||
|  | ||||
| int CircleProgress::getEndAngle() const | ||||
| { | ||||
|     return circleEndAngle; | ||||
| } | ||||
|  | ||||
| void CircleProgress::setAlpha(uint8_t newAlpha) | ||||
| { | ||||
|     circle.setAlpha(newAlpha); | ||||
| } | ||||
|  | ||||
| uint8_t CircleProgress::getAlpha() const | ||||
| { | ||||
|     return circle.getAlpha(); | ||||
| } | ||||
|  | ||||
| void CircleProgress::setValue(int value) | ||||
| { | ||||
|     AbstractProgressIndicator::setValue(value); | ||||
|     CWRUtil::Q5 startAngle; | ||||
|     CWRUtil::Q5 endAngle = CWRUtil::toQ5(circleEndAngle); | ||||
|     circle.getArcStart<CWRUtil::Q5>(startAngle); | ||||
|     uint16_t rangeAngleSteps = endAngle < startAngle ? (int)(startAngle - endAngle) : (int)(endAngle - startAngle); | ||||
|     CWRUtil::Q5 progress = CWRUtil::Q5(AbstractProgressIndicator::getProgress(rangeAngleSteps)); | ||||
|     if (endAngle < startAngle) | ||||
|     { | ||||
|         circle.updateArcEnd<CWRUtil::Q5>(startAngle - progress); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         circle.updateArcEnd<CWRUtil::Q5>(startAngle + progress); | ||||
|     } | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,141 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/progress_indicators/ImageProgress.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| ImageProgress::ImageProgress() | ||||
|     : AbstractDirectionProgress(), image(), fixedPosition(true) | ||||
| { | ||||
|     container.add(image); | ||||
|     progressIndicatorContainer.add(container); | ||||
| } | ||||
|  | ||||
| void ImageProgress::setProgressIndicatorPosition(int16_t x, int16_t y, int16_t width, int16_t height) | ||||
| { | ||||
|     container.setPosition(0, 0, width, height); | ||||
|  | ||||
|     AbstractProgressIndicator::setProgressIndicatorPosition(x, y, width, height); | ||||
| } | ||||
|  | ||||
| void ImageProgress::setAnchorAtZero(bool anchorAtZero) | ||||
| { | ||||
|     fixedPosition = anchorAtZero; | ||||
|     setValue(getValue()); | ||||
| } | ||||
|  | ||||
| bool ImageProgress::getAnchorAtZero() const | ||||
| { | ||||
|     return fixedPosition; | ||||
| } | ||||
|  | ||||
| void ImageProgress::setBitmap(BitmapId bitmapId) | ||||
| { | ||||
|     image.setBitmap(Bitmap(bitmapId)); | ||||
| } | ||||
|  | ||||
| BitmapId ImageProgress::getBitmap() const | ||||
| { | ||||
|     return image.getBitmapId(); | ||||
| } | ||||
|  | ||||
| void ImageProgress::setAlpha(uint8_t newAlpha) | ||||
| { | ||||
|     image.setAlpha(newAlpha); | ||||
| } | ||||
|  | ||||
| uint8_t ImageProgress::getAlpha() const | ||||
| { | ||||
|     return image.getAlpha(); | ||||
| } | ||||
|  | ||||
| void ImageProgress::setValue(int value) | ||||
| { | ||||
|     AbstractProgressIndicator::setValue(value); | ||||
|     const uint16_t maxProgress = (progressDirection == RIGHT || progressDirection == LEFT) ? progressIndicatorContainer.getWidth() : progressIndicatorContainer.getHeight(); | ||||
|     int16_t progress = AbstractProgressIndicator::getProgress(maxProgress); | ||||
|     if (fixedPosition) | ||||
|     { | ||||
|         switch (progressDirection) | ||||
|         { | ||||
|         case RIGHT: | ||||
|             { | ||||
|                 int16_t oldWidth = container.getWidth(); | ||||
|                 container.setPosition(0, 0, progress, progressIndicatorContainer.getHeight()); | ||||
|                 image.setPosition(0, 0, progress, progressIndicatorContainer.getHeight()); | ||||
|                 int16_t newWidth = container.getWidth(); | ||||
|                 Rect rect(MIN(oldWidth, newWidth), 0, abs(oldWidth - newWidth), container.getHeight()); | ||||
|                 progressIndicatorContainer.invalidateRect(rect); | ||||
|                 break; | ||||
|             } | ||||
|         case LEFT: | ||||
|             { | ||||
|                 int16_t oldX = container.getX(); | ||||
|                 container.setPosition(getWidth() - progress, 0, progress, progressIndicatorContainer.getHeight()); | ||||
|                 image.setPosition(-container.getX(), 0, progressIndicatorContainer.getWidth(), progressIndicatorContainer.getHeight()); | ||||
|                 int16_t newX = container.getX(); | ||||
|                 Rect rect(MIN(oldX, newX), 0, abs(oldX - newX), container.getHeight()); | ||||
|                 progressIndicatorContainer.invalidateRect(rect); | ||||
|                 break; | ||||
|             } | ||||
|         case DOWN: | ||||
|             { | ||||
|                 int16_t oldHeight = container.getHeight(); | ||||
|                 container.setPosition(0, 0, progressIndicatorContainer.getWidth(), progress); | ||||
|                 image.setPosition(0, 0, progressIndicatorContainer.getWidth(), progress); | ||||
|                 int16_t newHeight = container.getHeight(); | ||||
|                 Rect rect(0, MIN(oldHeight, newHeight), container.getWidth(), abs(oldHeight - newHeight)); | ||||
|                 progressIndicatorContainer.invalidateRect(rect); | ||||
|                 break; | ||||
|             } | ||||
|         case UP: | ||||
|             { | ||||
|                 int16_t oldY = container.getY(); | ||||
|                 container.setPosition(0, progressIndicatorContainer.getHeight() - progress, progressIndicatorContainer.getWidth(), progress); | ||||
|                 image.setPosition(0, -container.getY(), progressIndicatorContainer.getWidth(), progressIndicatorContainer.getHeight()); | ||||
|                 int16_t newY = container.getY(); | ||||
|                 Rect rect(0, MIN(oldY, newY), container.getWidth(), abs(oldY - newY)); | ||||
|                 progressIndicatorContainer.invalidateRect(rect); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         container.invalidate(); | ||||
|         switch (progressDirection) | ||||
|         { | ||||
|         case RIGHT: | ||||
|             container.setPosition(0, 0, progress, getHeight()); | ||||
|             image.setPosition(progress - progressIndicatorContainer.getWidth(), 0, progressIndicatorContainer.getWidth(), getHeight()); | ||||
|             break; | ||||
|         case LEFT: | ||||
|             container.setPosition(progressIndicatorContainer.getWidth() - progress, 0, progress, progressIndicatorContainer.getHeight()); | ||||
|             image.setPosition(0, 0, progress, progressIndicatorContainer.getHeight()); | ||||
|             break; | ||||
|         case DOWN: | ||||
|             container.setPosition(0, 0, progressIndicatorContainer.getWidth(), progress); | ||||
|             image.setPosition(0, progress - progressIndicatorContainer.getHeight(), progressIndicatorContainer.getWidth(), progressIndicatorContainer.getHeight()); | ||||
|             break; | ||||
|         case UP: | ||||
|             container.setPosition(0, progressIndicatorContainer.getHeight() - progress, progressIndicatorContainer.getWidth(), progress); | ||||
|             image.setPosition(0, 0, progressIndicatorContainer.getWidth(), progress); | ||||
|             break; | ||||
|         } | ||||
|         container.invalidate(); | ||||
|     } | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,109 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/progress_indicators/LineProgress.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| LineProgress::LineProgress() | ||||
|     : AbstractProgressIndicator(), line(), endX(0), endY(0) | ||||
| { | ||||
|     progressIndicatorContainer.add(line); | ||||
|     line.setPosition(0, 0, getWidth(), getHeight()); | ||||
| } | ||||
|  | ||||
| void LineProgress::setProgressIndicatorPosition(int16_t x, int16_t y, int16_t width, int16_t height) | ||||
| { | ||||
|     line.setPosition(0, 0, width, height); | ||||
|  | ||||
|     AbstractProgressIndicator::setProgressIndicatorPosition(x, y, width, height); | ||||
| } | ||||
|  | ||||
| void LineProgress::setPainter(AbstractPainter& painter) | ||||
| { | ||||
|     line.setPainter(painter); | ||||
| } | ||||
|  | ||||
| void LineProgress::setStart(int x, int y) | ||||
| { | ||||
|     startX = CWRUtil::toQ5<int>(x); | ||||
|     startY = CWRUtil::toQ5<int>(y); | ||||
|     line.setStart(x, y); | ||||
| } | ||||
|  | ||||
| void LineProgress::getStart(int& x, int& y) const | ||||
| { | ||||
|     x = startX.to<int>(); | ||||
|     y = startY.to<int>(); | ||||
| } | ||||
|  | ||||
| void LineProgress::setEnd(int x, int y) | ||||
| { | ||||
|     endX = CWRUtil::toQ5<int>(x); | ||||
|     endY = CWRUtil::toQ5<int>(y); | ||||
| } | ||||
|  | ||||
| void LineProgress::getEnd(int& x, int& y) const | ||||
| { | ||||
|     x = endX.to<int>(); | ||||
|     y = endY.to<int>(); | ||||
| } | ||||
|  | ||||
| void LineProgress::setLineWidth(int width) | ||||
| { | ||||
|     line.setLineWidth(width); | ||||
| } | ||||
|  | ||||
| int LineProgress::getLineWidth() const | ||||
| { | ||||
|     int width; | ||||
|     line.getLineWidth(width); | ||||
|     return width; | ||||
| } | ||||
|  | ||||
| void LineProgress::setLineEndingStyle(Line::LINE_ENDING_STYLE lineEndingStyle) | ||||
| { | ||||
|     line.setLineEndingStyle(lineEndingStyle); | ||||
| } | ||||
|  | ||||
| Line::LINE_ENDING_STYLE LineProgress::getLineEndingStyle() const | ||||
| { | ||||
|     return line.getLineEndingStyle(); | ||||
| } | ||||
|  | ||||
| void LineProgress::setAlpha(uint8_t newAlpha) | ||||
| { | ||||
|     line.setAlpha(newAlpha); | ||||
| } | ||||
|  | ||||
| uint8_t LineProgress::getAlpha() const | ||||
| { | ||||
|     return line.getAlpha(); | ||||
| } | ||||
|  | ||||
| void LineProgress::setValue(int value) | ||||
| { | ||||
|     if (rangeSteps > 0) | ||||
|     { | ||||
|         AbstractProgressIndicator::setValue(value); | ||||
|         int progress = (int)AbstractProgressIndicator::getProgress(rangeSteps); | ||||
|         CWRUtil::Q5 r(rangeSteps); | ||||
|         CWRUtil::Q5 p(progress); | ||||
|         CWRUtil::Q5 x = startX + (endX - startX) / r * p; | ||||
|         CWRUtil::Q5 y = startY + (endY - startY) / r * p; | ||||
|         line.updateEnd(x, y); | ||||
|     } | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,93 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/progress_indicators/TextProgress.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| TextProgress::TextProgress() | ||||
|     : AbstractProgressIndicator(), | ||||
|       textArea(), | ||||
|       decimals(0) | ||||
| { | ||||
|     textBuffer[0] = 0; | ||||
|     progressIndicatorContainer.add(textArea); | ||||
| } | ||||
|  | ||||
| void TextProgress::setProgressIndicatorPosition(int16_t x, int16_t y, int16_t width, int16_t height) | ||||
| { | ||||
|     textArea.setPosition(0, 0, width, height); | ||||
|  | ||||
|     AbstractProgressIndicator::setProgressIndicatorPosition(x, y, width, height); | ||||
| } | ||||
|  | ||||
| void TextProgress::setTypedText(const TypedText& t) | ||||
| { | ||||
|     textArea.setTypedText(t); | ||||
| } | ||||
|  | ||||
| TypedText TextProgress::getTypedText() const | ||||
| { | ||||
|     return textArea.getTypedText(); | ||||
| } | ||||
|  | ||||
| void TextProgress::setColor(colortype color) | ||||
| { | ||||
|     textArea.setColor(color); | ||||
| } | ||||
|  | ||||
| colortype TextProgress::getColor() const | ||||
| { | ||||
|     return textArea.getColor(); | ||||
| } | ||||
|  | ||||
| void TextProgress::setAlpha(uint8_t newAlpha) | ||||
| { | ||||
|     textArea.setAlpha(newAlpha); | ||||
| } | ||||
|  | ||||
| uint8_t TextProgress::getAlpha() const | ||||
| { | ||||
|     return textArea.getAlpha(); | ||||
| } | ||||
|  | ||||
| void TextProgress::setValue(int value) | ||||
| { | ||||
|     AbstractProgressIndicator::setValue(value); | ||||
|     int range[3] = { 1, 10, 100 }; | ||||
|     uint16_t progress = AbstractProgressIndicator::getProgress(100 * range[decimals]); | ||||
|     if (decimals > 0) | ||||
|     { | ||||
|         Unicode::snprintf(textBuffer, 8, "%d.%0*d", progress / range[decimals], decimals, progress % range[decimals]); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         Unicode::snprintf(textBuffer, 8, "%d", progress); | ||||
|     } | ||||
|     textArea.setWildcard(textBuffer); | ||||
|     textArea.invalidate(); | ||||
| } | ||||
|  | ||||
| void TextProgress::setNumberOfDecimals(uint16_t numberOfDecimals) | ||||
| { | ||||
|     decimals = MIN(2, numberOfDecimals); | ||||
|     setValue(getValue()); | ||||
| } | ||||
|  | ||||
| uint16_t TextProgress::getNumberOfDecimals() const | ||||
| { | ||||
|     return decimals; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,385 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/scrollers/DrawableList.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| DrawableList::DrawableList() | ||||
|     : Container(), | ||||
|       isHorizontal(false), | ||||
|       isCircular(false), | ||||
|       offset(0), | ||||
|       itemSize(0), | ||||
|       itemMargin(0), | ||||
|       numItems(0), | ||||
|       numDrawables(0), | ||||
|       firstItem(0), | ||||
|       firstDrawable(0), | ||||
|       drawablesInitialized(false), | ||||
|       firstDrawableIndex(0), | ||||
|       drawableItems(0), | ||||
|       updateDrawable(0) | ||||
| { | ||||
| } | ||||
|  | ||||
| void DrawableList::setWidth(int16_t width) | ||||
| { | ||||
|     Container::setWidth(width); | ||||
|     refreshDrawables(); | ||||
| } | ||||
|  | ||||
| void DrawableList::setHeight(int16_t height) | ||||
| { | ||||
|     Container::setHeight(height); | ||||
|     refreshDrawables(); | ||||
| } | ||||
|  | ||||
| void DrawableList::setHorizontal(bool horizontal) | ||||
| { | ||||
|     if ((horizontal && !isHorizontal) || (!horizontal && isHorizontal)) | ||||
|     { | ||||
|         isHorizontal = horizontal; | ||||
|         refreshDrawables(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool DrawableList::getHorizontal() const | ||||
| { | ||||
|     return isHorizontal; | ||||
| } | ||||
|  | ||||
| void DrawableList::setCircular(bool circular) | ||||
| { | ||||
|     if ((circular && !isCircular) || (!circular && isCircular)) | ||||
|     { | ||||
|         isCircular = circular; | ||||
|         refreshDrawables(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool DrawableList::getCircular() const | ||||
| { | ||||
|     return isCircular; | ||||
| } | ||||
|  | ||||
| void DrawableList::setDrawableSize(int16_t drawableSize, int16_t drawableMargin) | ||||
| { | ||||
|     itemSize = drawableSize + 2 * drawableMargin; | ||||
|     itemMargin = drawableMargin; | ||||
| } | ||||
|  | ||||
| void DrawableList::setDrawables(DrawableListItemsInterface& drawableListItems, | ||||
|                                 int16_t drawableItemIndexOffset, | ||||
|                                 GenericCallback<DrawableListItemsInterface*, int16_t, int16_t>& updateDrawableCallback) | ||||
| { | ||||
|     drawableItems = &drawableListItems; | ||||
|     firstDrawableIndex = drawableItemIndexOffset; | ||||
|     updateDrawable = &updateDrawableCallback; | ||||
|  | ||||
|     refreshDrawables(); | ||||
| } | ||||
|  | ||||
| int16_t DrawableList::getNumberOfDrawables() const | ||||
| { | ||||
|     return numDrawables; | ||||
| } | ||||
|  | ||||
| int16_t DrawableList::getItemSize() const | ||||
| { | ||||
|     return itemSize; | ||||
| } | ||||
|  | ||||
| int16_t DrawableList::getDrawableSize() const | ||||
| { | ||||
|     return itemSize - 2 * itemMargin; | ||||
| } | ||||
|  | ||||
| int16_t DrawableList::getDrawableMargin() const | ||||
| { | ||||
|     return itemMargin; | ||||
| } | ||||
|  | ||||
| void DrawableList::setNumberOfItems(int16_t numberOfItems) | ||||
| { | ||||
|     numItems = numberOfItems; | ||||
|     refreshDrawables(); | ||||
| } | ||||
|  | ||||
| int16_t DrawableList::getNumberOfItems() const | ||||
| { | ||||
|     return numItems; | ||||
| } | ||||
|  | ||||
| int16_t DrawableList::getRequiredNumberOfDrawables() const | ||||
| { | ||||
|     if (drawableItems == 0 || itemSize <= 0) | ||||
|     { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     // Calculate number of required drawables. Worst case is one pixel visible of drawable at top and rest stacked tightly | ||||
|     int16_t requiredDrawables = 1 + (((isHorizontal ? getWidth() : getHeight()) - 1) + (itemSize - 1)) / itemSize; | ||||
|     if (!isCircular) | ||||
|     { | ||||
|         // We never require more drawables than the number of elements on non-circular list. | ||||
|         if (requiredDrawables > numItems) | ||||
|         { | ||||
|             requiredDrawables = numItems; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     int16_t numberOfDrawables = drawableItems->getNumberOfDrawables(); | ||||
|     return MIN((numberOfDrawables - firstDrawableIndex), requiredDrawables); | ||||
| } | ||||
|  | ||||
| void DrawableList::setOffset(int32_t ofs) | ||||
| { | ||||
|     offset = ofs; | ||||
|  | ||||
|     if (numDrawables == 0 || numItems == 0 || itemSize == 0) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|     if (!updateDrawable || !updateDrawable->isValid()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // ofs is the offset of item[0] | ||||
|     // 0 => item[0] is perfectly selected, -itemSize => item[1] is perfectly selected, itemSize => item[N-1] is perfectly selected etc. | ||||
|     int16_t newFirstItem = 0; | ||||
|     if (ofs > 0) | ||||
|     { | ||||
|         int numberOfItems = ofs / itemSize + 1; | ||||
|         newFirstItem -= numberOfItems; | ||||
|         ofs -= numberOfItems * itemSize; | ||||
|     } | ||||
|     if (ofs <= -itemSize) | ||||
|     { | ||||
|         int numberOfItems = ofs / itemSize; | ||||
|         newFirstItem -= numberOfItems; | ||||
|         ofs -= numberOfItems * itemSize; | ||||
|     } | ||||
|     if (isCircular) | ||||
|     { | ||||
|         // Make sure that firstIndex is "in range" | ||||
|         newFirstItem %= numItems; | ||||
|         newFirstItem = (newFirstItem + numItems) % numItems; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         if (newFirstItem < 0) | ||||
|         { | ||||
|             ofs -= newFirstItem * itemSize; | ||||
|             newFirstItem = 0; | ||||
|         } | ||||
|         else if (newFirstItem + numDrawables > numItems) | ||||
|         { | ||||
|             int x = numItems - (newFirstItem + numDrawables); | ||||
|             ofs += x * itemSize; | ||||
|             newFirstItem += x; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     int drawableDelta = 0; | ||||
|     if (drawablesInitialized && firstItem != newFirstItem) | ||||
|     { | ||||
|         drawableDelta = numDrawables; | ||||
|         for (int i = 1; i < numDrawables; i++) | ||||
|         { | ||||
|             int fi = (firstItem + i); | ||||
|             int nfi = (newFirstItem + i); | ||||
|             if (isCircular) | ||||
|             { | ||||
|                 fi %= numItems; | ||||
|                 nfi %= numItems; | ||||
|             } | ||||
|             if (fi == newFirstItem) | ||||
|             { | ||||
|                 drawableDelta = -i; | ||||
|                 break; | ||||
|             } | ||||
|             if (nfi == firstItem) | ||||
|             { | ||||
|                 drawableDelta = i; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     firstDrawable = ((firstDrawable - drawableDelta) + numDrawables) % numDrawables; | ||||
|     firstItem = newFirstItem; | ||||
|  | ||||
|     for (int i = 0; i < numDrawables; i++) | ||||
|     { | ||||
|         int drawableIndex = (firstDrawable + i) % numDrawables; | ||||
|         Drawable* drawable = drawableItems->getDrawable(drawableIndex + firstDrawableIndex); | ||||
|         if (isHorizontal) | ||||
|         { | ||||
|             drawable->moveTo(ofs + i * itemSize + itemMargin, 0); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             drawable->moveTo(0, ofs + i * itemSize + itemMargin); | ||||
|         } | ||||
|  | ||||
|         int itemIndex = i + firstItem; | ||||
|         if (isCircular) | ||||
|         { | ||||
|             itemIndex %= numItems; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (itemIndex < 0 || itemIndex >= numItems) | ||||
|             { | ||||
|                 itemIndex = -1; | ||||
|             } | ||||
|         } | ||||
|         if (itemIndex < 0) | ||||
|         { | ||||
|             drawable->setVisible(false); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             drawable->setVisible(true); | ||||
|             // Only fill if first time or outside old range | ||||
|             if (!drawablesInitialized || (i < drawableDelta || i >= numDrawables + drawableDelta)) | ||||
|             { | ||||
|                 if (updateDrawable->isValid()) | ||||
|                 { | ||||
|                     updateDrawable->execute(drawableItems, drawableIndex + firstDrawableIndex, itemIndex); | ||||
|                     drawable->invalidate(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     drawablesInitialized = true; | ||||
| } | ||||
|  | ||||
| int32_t DrawableList::getOffset() const | ||||
| { | ||||
|     return offset; | ||||
| } | ||||
|  | ||||
| int16_t DrawableList::getItemIndex(int16_t drawableIndex) const | ||||
| { | ||||
|     if (drawableIndex < 0 || drawableIndex >= numDrawables || numDrawables == 0 || numItems == 0) | ||||
|     { | ||||
|         return -1; | ||||
|     } | ||||
|     int16_t itemNumber = ((drawableIndex + numDrawables - firstDrawable) % numDrawables) + firstItem; | ||||
|     if (isCircular) | ||||
|     { | ||||
|         itemNumber %= numItems; | ||||
|     } | ||||
|     if (itemNumber < 0 || itemNumber >= numItems) | ||||
|     { | ||||
|         return -1; | ||||
|     } | ||||
|     return itemNumber; | ||||
| } | ||||
|  | ||||
| int16_t DrawableList::getDrawableIndices(int16_t itemIndex, int16_t* drawableIndexArray, int16_t arraySize) const | ||||
| { | ||||
|     int16_t numFound = 0; | ||||
|     int16_t drawableIndex = -1; | ||||
|     while ((drawableIndex = getDrawableIndex(itemIndex, drawableIndex)) >= 0) | ||||
|     { | ||||
|         if (numFound < arraySize) | ||||
|         { | ||||
|             drawableIndexArray[numFound] = drawableIndex; | ||||
|             numFound++; | ||||
|         } | ||||
|     } | ||||
|     return numFound; | ||||
| } | ||||
|  | ||||
| int16_t DrawableList::getDrawableIndex(int16_t itemIndex, int16_t prevDrawableIndex /*= -1*/) const | ||||
| { | ||||
|     if (prevDrawableIndex < -1 || prevDrawableIndex >= numDrawables || numDrawables == 0 || numItems == 0) | ||||
|     { | ||||
|         return -1; | ||||
|     } | ||||
|     if (prevDrawableIndex >= 0) | ||||
|     { | ||||
|         prevDrawableIndex = (prevDrawableIndex - firstDrawable + numDrawables) % numDrawables; | ||||
|     } | ||||
|     for (int16_t i = prevDrawableIndex + 1; i < numDrawables; i++) | ||||
|     { | ||||
|         int16_t currentItemIndex = firstItem + i; | ||||
|         if (isCircular) | ||||
|         { | ||||
|             currentItemIndex %= numItems; | ||||
|         } | ||||
|         if (itemIndex == currentItemIndex) | ||||
|         { | ||||
|             int16_t drawableIndex = (firstDrawable + i) % numDrawables; | ||||
|             return drawableIndex; | ||||
|         } | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| void DrawableList::refreshDrawables() | ||||
| { | ||||
|     if (drawableItems == 0) | ||||
|     { | ||||
|         numDrawables = 0; | ||||
|         return; | ||||
|     } | ||||
|     numDrawables = getRequiredNumberOfDrawables(); | ||||
|     // Remove everything | ||||
|     Container::removeAll(); | ||||
|     // Add the itemDrawables | ||||
|     for (int drawableIndex = 0; drawableIndex < numDrawables; drawableIndex++) | ||||
|     { | ||||
|         Drawable* drawable = drawableItems->getDrawable(drawableIndex + firstDrawableIndex); | ||||
|         // Resize the drawables, X/Y ignored for now. | ||||
|         if (isHorizontal) | ||||
|         { | ||||
|             drawable->setPosition(0, 0, itemSize - 2 * itemMargin, getHeight()); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             drawable->setPosition(0, 0, getWidth(), itemSize - 2 * itemMargin); | ||||
|         } | ||||
|         // Add each drawable for later positioning | ||||
|         if (drawable->getParent() != 0) | ||||
|         { | ||||
|             // Remove drawable from the current parent | ||||
|             Container* parent = static_cast<Container*>(drawable->getParent()); | ||||
|             parent->remove(*drawable); | ||||
|         } | ||||
|         Container::add(*drawable); | ||||
|     } | ||||
|  | ||||
|     drawablesInitialized = false; | ||||
|     firstItem = 0; | ||||
|     firstDrawable = 0; | ||||
|     setOffset(offset); | ||||
| } | ||||
|  | ||||
| void DrawableList::itemChanged(int16_t itemIndex) | ||||
| { | ||||
|     if (updateDrawable && updateDrawable->isValid()) | ||||
|     { | ||||
|         int16_t drawableIndex = -1; | ||||
|         while ((drawableIndex = getDrawableIndex(itemIndex, drawableIndex)) != -1) | ||||
|         { | ||||
|             updateDrawable->execute(drawableItems, drawableIndex + firstDrawableIndex, itemIndex); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,336 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/scrollers/ScrollBase.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| ScrollBase::ScrollBase() | ||||
|     : Container(), | ||||
|       numberOfDrawables(0), | ||||
|       distanceBeforeAlignedItem(0), | ||||
|       itemSize(0), | ||||
|       swipeAcceleration(10), | ||||
|       dragAcceleration(10), | ||||
|       maxSwipeItems(0), | ||||
|       easingEquation(&EasingEquations::backEaseOut), | ||||
|       defaultAnimationSteps(30), | ||||
|       itemSelectedCallback(0), | ||||
|       itemLockedInCallback(0), | ||||
|       animationEndedCallback(0), | ||||
|       itemPressedCallback(0), | ||||
|       currentAnimationState(NO_ANIMATION), | ||||
|       gestureStep(0), | ||||
|       gestureStepsTotal(0), | ||||
|       gestureStart(0), | ||||
|       gestureEnd(0), | ||||
|       xClick(0), | ||||
|       yClick(0), | ||||
|       initialSwipeOffset(0), | ||||
|       draggableX(false), | ||||
|       draggableY(false) | ||||
| { | ||||
|     Container::add(list); | ||||
|     list.setXY(0, 0); | ||||
|     list.setHorizontal(false); | ||||
|     list.setCircular(false); | ||||
|     setTouchable(true); | ||||
| } | ||||
|  | ||||
| void ScrollBase::setWidth(int16_t width) | ||||
| { | ||||
|     Container::setWidth(width); | ||||
|     list.setWidth(width); | ||||
| } | ||||
|  | ||||
| void ScrollBase::setHeight(int16_t height) | ||||
| { | ||||
|     Container::setHeight(height); | ||||
|     list.setHeight(height); | ||||
| } | ||||
|  | ||||
| void ScrollBase::setHorizontal(bool horizontal) | ||||
| { | ||||
|     allowVerticalDrag(horizontal); | ||||
|     allowHorizontalDrag(!horizontal); | ||||
|     list.setHorizontal(horizontal); | ||||
| } | ||||
|  | ||||
| bool ScrollBase::getHorizontal() const | ||||
| { | ||||
|     return list.getHorizontal(); | ||||
| } | ||||
|  | ||||
| void ScrollBase::setCircular(bool circular) | ||||
| { | ||||
|     list.setCircular(circular); | ||||
| } | ||||
|  | ||||
| bool ScrollBase::getCircular() const | ||||
| { | ||||
|     return list.getCircular(); | ||||
| } | ||||
|  | ||||
| void ScrollBase::setDrawableSize(int16_t drawableSize, int16_t drawableMargin) | ||||
| { | ||||
|     itemSize = drawableSize + drawableMargin * 2; | ||||
|     list.setDrawableSize(drawableSize, drawableMargin); | ||||
| } | ||||
|  | ||||
| int16_t ScrollBase::getDrawableSize() const | ||||
| { | ||||
|     return list.getDrawableSize(); | ||||
| } | ||||
|  | ||||
| int16_t ScrollBase::getDrawableMargin() const | ||||
| { | ||||
|     return list.getDrawableMargin(); | ||||
| } | ||||
|  | ||||
| void ScrollBase::setNumberOfItems(int16_t numberOfItems) | ||||
| { | ||||
|     if (numberOfItems != getNumberOfItems()) | ||||
|     { | ||||
|         list.setNumberOfItems(numberOfItems); | ||||
|         if (!getCircular()) | ||||
|         { | ||||
|             animateToPosition(keepOffsetInsideLimits(getOffset(), 0)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| int16_t ScrollBase::getNumberOfItems() const | ||||
| { | ||||
|     return list.getNumberOfItems(); | ||||
| } | ||||
|  | ||||
| void ScrollBase::setEasingEquation(EasingEquation equation) | ||||
| { | ||||
|     easingEquation = equation; | ||||
| } | ||||
|  | ||||
| void ScrollBase::setAnimationSteps(int16_t steps) | ||||
| { | ||||
|     defaultAnimationSteps = steps; | ||||
| } | ||||
|  | ||||
| uint16_t ScrollBase::getAnimationSteps() const | ||||
| { | ||||
|     return defaultAnimationSteps; | ||||
| } | ||||
|  | ||||
| void ScrollBase::setSwipeAcceleration(uint16_t acceleration) | ||||
| { | ||||
|     swipeAcceleration = acceleration; | ||||
| } | ||||
|  | ||||
| uint16_t ScrollBase::getSwipeAcceleration() const | ||||
| { | ||||
|     return swipeAcceleration; | ||||
| } | ||||
|  | ||||
| void ScrollBase::setMaxSwipeItems(uint16_t maxItems) | ||||
| { | ||||
|     maxSwipeItems = maxItems; | ||||
| } | ||||
|  | ||||
| uint16_t ScrollBase::getMaxSwipeItems() const | ||||
| { | ||||
|     return maxSwipeItems; | ||||
| } | ||||
|  | ||||
| void ScrollBase::setDragAcceleration(uint16_t acceleration) | ||||
| { | ||||
|     dragAcceleration = acceleration; | ||||
| } | ||||
|  | ||||
| uint16_t ScrollBase::getDragAcceleration() const | ||||
| { | ||||
|     return dragAcceleration; | ||||
| } | ||||
|  | ||||
| void ScrollBase::allowHorizontalDrag(bool enable) | ||||
| { | ||||
|     draggableX = enable; | ||||
| } | ||||
|  | ||||
| void ScrollBase::allowVerticalDrag(bool enable) | ||||
| { | ||||
|     draggableY = enable; | ||||
| } | ||||
|  | ||||
| void ScrollBase::animateToItem(int16_t itemIndex, int16_t animationSteps /*= -1*/) | ||||
| { | ||||
|     int32_t position = getPositionForItem(itemIndex); | ||||
|     animateToPosition(position, animationSteps); | ||||
| } | ||||
|  | ||||
| void ScrollBase::setItemSelectedCallback(GenericCallback<int16_t>& callback) | ||||
| { | ||||
|     itemSelectedCallback = &callback; | ||||
| } | ||||
|  | ||||
| void ScrollBase::setAnimationEndedCallback(GenericCallback<>& callback) | ||||
| { | ||||
|     animationEndedCallback = &callback; | ||||
| } | ||||
|  | ||||
| void ScrollBase::setItemPressedCallback(GenericCallback<int16_t>& callback) | ||||
| { | ||||
|     itemPressedCallback = &callback; | ||||
| } | ||||
|  | ||||
| bool ScrollBase::isAnimating() const | ||||
| { | ||||
|     return currentAnimationState != NO_ANIMATION; | ||||
| } | ||||
|  | ||||
| void ScrollBase::stopAnimation() | ||||
| { | ||||
|     if (currentAnimationState == ANIMATING_GESTURE) | ||||
|     { | ||||
|         Application::getInstance()->unregisterTimerWidget(this); | ||||
|         setOffset(gestureEnd); | ||||
|     } | ||||
|     currentAnimationState = NO_ANIMATION; | ||||
| } | ||||
|  | ||||
| void ScrollBase::handleDragEvent(const DragEvent& evt) | ||||
| { | ||||
|     stopAnimation(); | ||||
|     currentAnimationState = ANIMATING_DRAG; | ||||
|     int32_t newOffset = getOffset() + (getHorizontal() ? evt.getDeltaX() : evt.getDeltaY()) * dragAcceleration / 10; | ||||
|     newOffset = keepOffsetInsideLimits(newOffset, itemSize * 3 / 4); | ||||
|     setOffset(newOffset); | ||||
| } | ||||
|  | ||||
| void ScrollBase::handleGestureEvent(const GestureEvent& evt) | ||||
| { | ||||
|     if (evt.getType() == (getHorizontal() ? GestureEvent::SWIPE_HORIZONTAL : GestureEvent::SWIPE_VERTICAL)) | ||||
|     { | ||||
|         int16_t velocity = abs(evt.getVelocity()); | ||||
|         int16_t direction = evt.getVelocity() < 0 ? -1 : 1; | ||||
|         int16_t steps = MAX(1, velocity - 4) * 7; | ||||
|         int32_t newOffset = getOffset() + direction * steps * swipeAcceleration / 10; | ||||
|         if (maxSwipeItems > 0) | ||||
|         { | ||||
|             int32_t maxDistance = maxSwipeItems * itemSize; | ||||
|             newOffset = MIN(newOffset, initialSwipeOffset + maxDistance); | ||||
|             newOffset = MAX(newOffset, initialSwipeOffset - maxDistance); | ||||
|         } | ||||
|         newOffset = keepOffsetInsideLimits(newOffset, 0); | ||||
|         steps = MIN(steps, defaultAnimationSteps); | ||||
|         animateToPosition(newOffset, steps); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ScrollBase::handleTickEvent() | ||||
| { | ||||
|     if (currentAnimationState == ANIMATING_GESTURE) | ||||
|     { | ||||
|         gestureStep++; | ||||
|         int newPosition = gestureStart + easingEquation(gestureStep, 0, gestureEnd - gestureStart, gestureStepsTotal); | ||||
|         setOffset(newPosition); | ||||
|         if (gestureStep > gestureStepsTotal) | ||||
|         { | ||||
|             currentAnimationState = NO_ANIMATION; | ||||
|             gestureStep = 0; | ||||
|             Application::getInstance()->unregisterTimerWidget(this); | ||||
|             setOffset(getNormalizedOffset(gestureEnd)); | ||||
|             // Also adjust initialSwipeOffset in case it is being used. | ||||
|             initialSwipeOffset += getOffset() - gestureEnd; | ||||
|  | ||||
|             //Item has settled, call back | ||||
|             if (animationEndedCallback && animationEndedCallback->isValid()) | ||||
|             { | ||||
|                 animationEndedCallback->execute(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ScrollBase::itemChanged(int itemIndex) | ||||
| { | ||||
|     list.itemChanged(itemIndex); | ||||
| } | ||||
|  | ||||
| void ScrollBase::setOffset(int32_t offset) | ||||
| { | ||||
|     list.setOffset(offset + distanceBeforeAlignedItem); | ||||
| } | ||||
|  | ||||
| int32_t ScrollBase::getOffset() const | ||||
| { | ||||
|     return list.getOffset() - distanceBeforeAlignedItem; | ||||
| } | ||||
|  | ||||
| int ScrollBase::getNormalizedOffset(int offset) const | ||||
| { | ||||
|     int16_t numItems = getNumberOfItems(); | ||||
|     if (numItems == 0 || itemSize == 0) | ||||
|     { | ||||
|         return offset; | ||||
|     } | ||||
|     int32_t listSize = numItems * itemSize; | ||||
|     offset %= listSize; | ||||
|     return offset > 0 ? offset - listSize : offset; | ||||
| } | ||||
|  | ||||
| int32_t ScrollBase::getNearestAlignedOffset(int32_t offset) const | ||||
| { | ||||
|     if (itemSize == 0) | ||||
|     { | ||||
|         return offset; | ||||
|     } | ||||
|     if (getCircular()) | ||||
|     { | ||||
|         if (offset < 0) | ||||
|         { | ||||
|             return (((offset - (itemSize / 2)) / itemSize) * itemSize); | ||||
|         } | ||||
|         return ((offset + (itemSize / 2)) / itemSize) * itemSize; | ||||
|     } | ||||
|     offset = keepOffsetInsideLimits(offset, 0); | ||||
|     return ((offset - (itemSize / 2)) / itemSize) * itemSize; | ||||
| } | ||||
|  | ||||
| void ScrollBase::animateToPosition(int32_t position, int16_t steps) | ||||
| { | ||||
|     int32_t currentPosition = getOffset(); | ||||
|     position = getNearestAlignedOffset(position); | ||||
|     if (steps < 0) | ||||
|     { | ||||
|         steps = defaultAnimationSteps; | ||||
|     } | ||||
|     steps = MIN(steps, abs(position - currentPosition)); | ||||
|     if (steps < 1) | ||||
|     { | ||||
|         setOffset(position); | ||||
|         currentAnimationState = NO_ANIMATION; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         gestureStart = currentPosition; | ||||
|         gestureEnd = position; | ||||
|         gestureStep = 0; | ||||
|         gestureStepsTotal = steps; | ||||
|         if (currentAnimationState != ANIMATING_GESTURE) | ||||
|         { | ||||
|             Application::getInstance()->registerTimerWidget(this); | ||||
|             currentAnimationState = ANIMATING_GESTURE; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,230 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/scrollers/ScrollList.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| ScrollList::ScrollList() | ||||
|     : ScrollBase(), | ||||
|       paddingAfterLastItem(0), | ||||
|       snapping(false), | ||||
|       windowSize(1) | ||||
| { | ||||
| } | ||||
|  | ||||
| void ScrollList::setDrawables(DrawableListItemsInterface& drawableListItems, GenericCallback<DrawableListItemsInterface*, int16_t, int16_t>& updateDrawableCallback) | ||||
| { | ||||
|     stopAnimation(); | ||||
|     numberOfDrawables = drawableListItems.getNumberOfDrawables(); | ||||
|     list.setDrawables(drawableListItems, 0, updateDrawableCallback); | ||||
|     setOffset(0); | ||||
| } | ||||
|  | ||||
| void ScrollList::setWindowSize(int16_t items) | ||||
| { | ||||
|     windowSize = MAX(1, items); | ||||
|     animateToPosition(keepOffsetInsideLimits(getOffset(), 0)); | ||||
| } | ||||
|  | ||||
| void ScrollList::setPadding(int16_t paddingBefore, int16_t paddingAfter) | ||||
| { | ||||
|     int32_t currentOffset = getOffset(); | ||||
|     distanceBeforeAlignedItem = paddingBefore; | ||||
|     paddingAfterLastItem = paddingAfter; | ||||
|     setOffset(currentOffset); | ||||
|     list.refreshDrawables(); | ||||
| } | ||||
|  | ||||
| int16_t ScrollList::getPaddingBefore() const | ||||
| { | ||||
|     return distanceBeforeAlignedItem; | ||||
| } | ||||
|  | ||||
| int16_t ScrollList::getPaddingAfter() const | ||||
| { | ||||
|     return paddingAfterLastItem; | ||||
| } | ||||
|  | ||||
| void ScrollList::setSnapping(bool snap) | ||||
| { | ||||
|     snapping = snap; | ||||
|     if (snapping) | ||||
|     { | ||||
|         setOffset(getNearestAlignedOffset(getOffset())); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool ScrollList::getSnapping() const | ||||
| { | ||||
|     return snapping; | ||||
| } | ||||
|  | ||||
| int32_t ScrollList::getPositionForItem(int16_t itemIndex) | ||||
| { | ||||
|     int32_t currentOffset = getNormalizedOffset(getOffset()); | ||||
|     if (itemIndex < 0 || itemIndex >= list.getNumberOfItems() || itemSize == 0) | ||||
|     { | ||||
|         return currentOffset; | ||||
|     } | ||||
|     int32_t itemOffset = -itemIndex * itemSize; | ||||
|     // Get the visible size | ||||
|     int16_t widgetSize = getHorizontal() ? getWidth() : getHeight(); | ||||
|     int16_t activeWidgetSize = widgetSize - (distanceBeforeAlignedItem + paddingAfterLastItem); | ||||
|     if (list.getCircular()) | ||||
|     { | ||||
|         int32_t offset = currentOffset; | ||||
|         // Important this is a do-while of visibleSize < itemSize in which case we need to check at least one time | ||||
|         do | ||||
|         { | ||||
|             int16_t i = (-getNormalizedOffset(offset)) / itemSize; // Item index of first | ||||
|             if (itemIndex == i) | ||||
|             { | ||||
|                 return currentOffset; | ||||
|             } | ||||
|             offset -= itemSize; | ||||
|         } while (offset >= currentOffset - (activeWidgetSize - itemSize)); | ||||
|         int32_t allItemsSize = list.getNumberOfItems() * itemSize; | ||||
|         // Either scroll left from the first item or right from the last item. Find out which is closest | ||||
|         int32_t leftScrollDistance = itemOffset - currentOffset; | ||||
|         int32_t leftScrollDistance2 = (itemOffset + allItemsSize) - currentOffset; | ||||
|         int32_t rightItemOffset = getNormalizedOffset(currentOffset - (activeWidgetSize - itemSize)); | ||||
|         int32_t rightScrollDistance = rightItemOffset - itemOffset; | ||||
|         int32_t rightScrollDistance2 = rightItemOffset - (itemOffset - allItemsSize); | ||||
|         if (abs(leftScrollDistance2) < abs(leftScrollDistance)) | ||||
|         { | ||||
|             leftScrollDistance = leftScrollDistance2; | ||||
|         } | ||||
|         if (abs(rightScrollDistance2) < abs(rightScrollDistance)) | ||||
|         { | ||||
|             rightScrollDistance = rightScrollDistance2; | ||||
|         } | ||||
|         if (abs(rightScrollDistance) < abs(leftScrollDistance)) | ||||
|         { | ||||
|             return currentOffset - rightScrollDistance; | ||||
|         } | ||||
|         return currentOffset + leftScrollDistance; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         if (itemOffset > currentOffset) // First item on screen is higher than the itemIndex. Scroll itemIndex to top position | ||||
|         { | ||||
|             return itemOffset; | ||||
|         } | ||||
|         int16_t numberOfVisibleItems = activeWidgetSize / itemSize; | ||||
|         int32_t itemOffsetAtEnd = itemOffset; | ||||
|         if (numberOfVisibleItems > 0) | ||||
|         { | ||||
|             if (snapping) | ||||
|             { | ||||
|                 itemOffsetAtEnd = itemOffset + (numberOfVisibleItems - 1) * itemSize; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 itemOffsetAtEnd = itemOffset + activeWidgetSize - itemSize; | ||||
|             } | ||||
|         } | ||||
|         if (itemOffsetAtEnd < currentOffset) | ||||
|         { | ||||
|             return itemOffsetAtEnd; | ||||
|         } | ||||
|     } | ||||
|     return currentOffset; | ||||
| } | ||||
|  | ||||
| void ScrollList::handleClickEvent(const ClickEvent& evt) | ||||
| { | ||||
|     ScrollBase::handleClickEvent(evt); | ||||
|     if (evt.getType() == ClickEvent::PRESSED) | ||||
|     { | ||||
|         xClick = evt.getX(); | ||||
|         yClick = evt.getY(); | ||||
|         initialSwipeOffset = getOffset(); | ||||
|  | ||||
|         setOffset(getNearestAlignedOffset(initialSwipeOffset)); | ||||
|         if (itemPressedCallback && itemPressedCallback->isValid()) | ||||
|         { | ||||
|             int16_t click = (getHorizontal() ? xClick : yClick); | ||||
|             int32_t offset = click - getOffset(); | ||||
|             int32_t listSize = getNumberOfItems() * itemSize; | ||||
|             if (getCircular()) | ||||
|             { | ||||
|                 offset += listSize; | ||||
|                 offset %= listSize; | ||||
|             } | ||||
|             if (offset >= 0 && offset < listSize) | ||||
|             { | ||||
|                 int16_t item = offset / itemSize; | ||||
|                 itemPressedCallback->execute(item); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     else if (evt.getType() == ClickEvent::RELEASED) | ||||
|     { | ||||
|         if (currentAnimationState == NO_ANIMATION) | ||||
|         { | ||||
|             // For a tiny drag, start by re-aligning (no animation(!)) | ||||
|             setOffset(getNearestAlignedOffset(getOffset())); | ||||
|             if (itemSelectedCallback && itemSelectedCallback->isValid()) | ||||
|             { | ||||
|                 int16_t click = (getHorizontal() ? xClick : yClick); | ||||
|                 int32_t offset = click - getOffset(); | ||||
|                 int32_t listSize = getNumberOfItems() * itemSize; | ||||
|                 if (getCircular()) | ||||
|                 { | ||||
|                     offset += listSize; | ||||
|                     offset %= listSize; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     offset -= distanceBeforeAlignedItem; | ||||
|                 } | ||||
|                 if (offset >= 0 && offset < listSize) | ||||
|                 { | ||||
|                     int16_t item = offset / itemSize; | ||||
|                     itemSelectedCallback->execute(item); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         else if (currentAnimationState == ANIMATING_DRAG) | ||||
|         { | ||||
|             // click + drag + release. Find best Y to scroll to | ||||
|             animateToPosition(getNearestAlignedOffset(getOffset())); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| int32_t ScrollList::getNearestAlignedOffset(int32_t offset) const | ||||
| { | ||||
|     if (snapping) | ||||
|     { | ||||
|         // ScrollBase implementation will snap | ||||
|         return ScrollBase::getNearestAlignedOffset(offset); | ||||
|     } | ||||
|  | ||||
|     return keepOffsetInsideLimits(offset, 0); | ||||
| } | ||||
|  | ||||
| int32_t ScrollList::keepOffsetInsideLimits(int32_t newOffset, int16_t overShoot) const | ||||
| { | ||||
|     if (!getCircular()) | ||||
|     { | ||||
|         newOffset = MIN(newOffset, overShoot); | ||||
|         int maxOffToTheStart = windowSize < getNumberOfItems() ? getNumberOfItems() - windowSize : 0; | ||||
|         newOffset = MAX(newOffset, -(itemSize * maxOffToTheStart) - overShoot); | ||||
|     } | ||||
|     return newOffset; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,29 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/scrollers/ScrollWheel.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void ScrollWheel::setDrawables(DrawableListItemsInterface& drawableListItems, GenericCallback<DrawableListItemsInterface*, int16_t, int16_t>& updateDrawableCallback) | ||||
| { | ||||
|     stopAnimation(); | ||||
|     numberOfDrawables = drawableListItems.getNumberOfDrawables(); | ||||
|  | ||||
|     list.setDrawables(drawableListItems, 0, updateDrawableCallback); | ||||
|  | ||||
|     setOffset(0); | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,178 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/scrollers/ScrollWheelBase.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| ScrollWheelBase::ScrollWheelBase() | ||||
|     : ScrollBase(), | ||||
|       animateToCallback(0) | ||||
| { | ||||
|     ScrollWheelBase::setHorizontal(false); | ||||
|     setTouchable(true); | ||||
| } | ||||
|  | ||||
| void ScrollWheelBase::setSelectedItemOffset(int16_t offset) | ||||
| { | ||||
|     int32_t currentOffset = getOffset(); | ||||
|     distanceBeforeAlignedItem = offset; | ||||
|     setOffset(currentOffset); | ||||
| } | ||||
|  | ||||
| int16_t ScrollWheelBase::getSelectedItemOffset() const | ||||
| { | ||||
|     return distanceBeforeAlignedItem; | ||||
| } | ||||
|  | ||||
| int32_t ScrollWheelBase::getPositionForItem(int16_t itemIndex) | ||||
| { | ||||
|     int32_t newOffset = -itemIndex * itemSize; | ||||
|     if (getCircular()) | ||||
|     { | ||||
|         // Check if it is closer to scroll backwards | ||||
|         int32_t otherOffset = newOffset + getNumberOfItems() * itemSize; | ||||
|         int32_t offset = getOffset(); | ||||
|         if (abs(otherOffset - offset) < abs(newOffset - offset)) | ||||
|         { | ||||
|             newOffset = otherOffset; | ||||
|         } | ||||
|     } | ||||
|     return newOffset; | ||||
| } | ||||
|  | ||||
| void ScrollWheelBase::animateToPosition(int32_t position, int16_t steps) | ||||
| { | ||||
|     if (itemSize == 0) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|     if (animateToCallback && animateToCallback->isValid() && itemSize > 0) | ||||
|     { | ||||
|         position = getNearestAlignedOffset(position); | ||||
|         int16_t itemIndex = (-position) / itemSize; | ||||
|         animateToCallback->execute(itemIndex); | ||||
|     } | ||||
|     ScrollBase::animateToPosition(position, steps); | ||||
| } | ||||
|  | ||||
| int ScrollWheelBase::getSelectedItem() const | ||||
| { | ||||
|     if (itemSize == 0) | ||||
|     { | ||||
|         return 0; | ||||
|     } | ||||
|     if (currentAnimationState == ANIMATING_GESTURE) | ||||
|     { | ||||
|         // Scroll in progress, get the destination value | ||||
|         return (-getNormalizedOffset(gestureEnd)) / itemSize; | ||||
|     } | ||||
|     return (-getNormalizedOffset(getOffset())) / itemSize; | ||||
| } | ||||
|  | ||||
| int32_t ScrollWheelBase::keepOffsetInsideLimits(int32_t newOffset, int16_t overShoot) const | ||||
| { | ||||
|     if (!getCircular()) | ||||
|     { | ||||
|         newOffset = MIN(newOffset, overShoot); | ||||
|         int16_t numberOfItems = getNumberOfItems(); | ||||
|         newOffset = MAX(newOffset, -(itemSize * (numberOfItems - 1)) - overShoot); | ||||
|     } | ||||
|     return newOffset; | ||||
| } | ||||
|  | ||||
| void ScrollWheelBase::handleClickEvent(const ClickEvent& evt) | ||||
| { | ||||
|     if (itemSize == 0) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|     int32_t offset = getOffset(); | ||||
|     if (evt.getType() == ClickEvent::PRESSED) | ||||
|     { | ||||
|         xClick = evt.getX(); | ||||
|         yClick = evt.getY(); | ||||
|         initialSwipeOffset = offset; | ||||
|  | ||||
|         if (itemPressedCallback && itemPressedCallback->isValid()) | ||||
|         { | ||||
|             itemPressedCallback->execute(getSelectedItem()); | ||||
|         } | ||||
|     } | ||||
|     else if (evt.getType() == ClickEvent::RELEASED) | ||||
|     { | ||||
|         if (currentAnimationState == NO_ANIMATION) | ||||
|         { | ||||
|             int16_t click = getHorizontal() ? xClick : yClick; | ||||
|             // Click => move to clicked position | ||||
|             if (click < distanceBeforeAlignedItem) | ||||
|             { | ||||
|                 animateToPosition(offset + ((distanceBeforeAlignedItem - click) / itemSize + 1) * itemSize); | ||||
|             } | ||||
|             else if (click > distanceBeforeAlignedItem + itemSize) | ||||
|             { | ||||
|                 animateToPosition(offset - ((click - distanceBeforeAlignedItem) / itemSize) * itemSize); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 animateToPosition(offset); | ||||
|             } | ||||
|         } | ||||
|         else if (currentAnimationState == ANIMATING_DRAG) | ||||
|         { | ||||
|             // click + drag + release. Find best Y to scroll to | ||||
|             animateToPosition(offset); | ||||
|         } | ||||
|  | ||||
|         if (itemSelectedCallback && itemSelectedCallback->isValid()) | ||||
|         { | ||||
|             itemSelectedCallback->execute(getSelectedItem()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ScrollWheelBase::handleDragEvent(const DragEvent& evt) | ||||
| { | ||||
|     currentAnimationState = ANIMATING_DRAG; | ||||
|     int newOffset = getOffset() + (getHorizontal() ? evt.getDeltaX() : evt.getDeltaY()) * dragAcceleration / 10; | ||||
|     if (!getCircular()) | ||||
|     { | ||||
|         newOffset = MIN(newOffset, itemSize * 3 / 4); | ||||
|         int16_t numberOfItems = getNumberOfItems(); | ||||
|         newOffset = MAX(newOffset, -(itemSize * (numberOfItems - 1)) - itemSize * 3 / 4); | ||||
|     } | ||||
|     setOffset(newOffset); | ||||
| } | ||||
|  | ||||
| void ScrollWheelBase::handleGestureEvent(const GestureEvent& evt) | ||||
| { | ||||
|     if (evt.getType() == (getHorizontal() ? GestureEvent::SWIPE_HORIZONTAL : GestureEvent::SWIPE_VERTICAL)) | ||||
|     { | ||||
|         int32_t newOffset = getOffset() + evt.getVelocity() * swipeAcceleration / 10; | ||||
|         if (maxSwipeItems > 0) | ||||
|         { | ||||
|             int32_t maxDistance = maxSwipeItems * itemSize; | ||||
|             newOffset = MIN(newOffset, initialSwipeOffset + maxDistance); | ||||
|             newOffset = MAX(newOffset, initialSwipeOffset - maxDistance); | ||||
|         } | ||||
|         animateToPosition(newOffset); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ScrollWheelBase::setAnimateToCallback(GenericCallback<int16_t>& callback) | ||||
| { | ||||
|     animateToCallback = &callback; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,203 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/containers/scrollers/ScrollWheelWithSelectionStyle.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| ScrollWheelWithSelectionStyle::ScrollWheelWithSelectionStyle() | ||||
|     : ScrollWheelBase(), | ||||
|       drawablesInFirstList(0), | ||||
|       extraSizeBeforeSelectedItem(0), | ||||
|       extraSizeAfterSelectedItem(0), | ||||
|       marginBeforeSelectedItem(0), | ||||
|       marginAfterSelectedItem(0), | ||||
|       drawables(0), | ||||
|       centerDrawables(0), | ||||
|       originalUpdateDrawableCallback(0), | ||||
|       originalUpdateCenterDrawableCallback(0) | ||||
| { | ||||
|     ScrollWheelBase::add(list2); | ||||
|     ScrollWheelBase::add(list1); // Put center list at top of the first/last list. | ||||
| } | ||||
|  | ||||
| void ScrollWheelWithSelectionStyle::setWidth(int16_t width) | ||||
| { | ||||
|     ScrollWheelBase::setWidth(width); | ||||
|     if (getHorizontal()) | ||||
|     { | ||||
|         refreshDrawableListsLayout(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ScrollWheelWithSelectionStyle::setHeight(int16_t height) | ||||
| { | ||||
|     ScrollWheelBase::setHeight(height); | ||||
|     if (!getHorizontal()) | ||||
|     { | ||||
|         refreshDrawableListsLayout(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ScrollWheelWithSelectionStyle::setHorizontal(bool horizontal) | ||||
| { | ||||
|     ScrollWheelBase::setHorizontal(horizontal); | ||||
|     list1.setHorizontal(horizontal); | ||||
|     list2.setHorizontal(horizontal); | ||||
|     refreshDrawableListsLayout(); | ||||
| } | ||||
|  | ||||
| void ScrollWheelWithSelectionStyle::setCircular(bool circular) | ||||
| { | ||||
|     ScrollWheelBase::setCircular(circular); | ||||
|     list1.setCircular(circular); | ||||
|     list2.setCircular(circular); | ||||
| } | ||||
|  | ||||
| void ScrollWheelWithSelectionStyle::setNumberOfItems(int16_t numberOfItems) | ||||
| { | ||||
|     if (numberOfItems != getNumberOfItems()) | ||||
|     { | ||||
|         ScrollWheelBase::setNumberOfItems(numberOfItems); | ||||
|         list1.setNumberOfItems(numberOfItems); | ||||
|         list2.setNumberOfItems(numberOfItems); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ScrollWheelWithSelectionStyle::setSelectedItemOffset(int16_t offset) | ||||
| { | ||||
|     ScrollWheelBase::setSelectedItemOffset(offset); | ||||
|     refreshDrawableListsLayout(); | ||||
| } | ||||
|  | ||||
| void ScrollWheelWithSelectionStyle::setSelectedItemExtraSize(int16_t extraSizeBefore, int16_t extraSizeAfter) | ||||
| { | ||||
|     extraSizeBeforeSelectedItem = extraSizeBefore; | ||||
|     extraSizeAfterSelectedItem = extraSizeAfter; | ||||
|     refreshDrawableListsLayout(); | ||||
| } | ||||
|  | ||||
| int16_t ScrollWheelWithSelectionStyle::getSelectedItemExtraSizeBefore() const | ||||
| { | ||||
|     return extraSizeBeforeSelectedItem; | ||||
| } | ||||
|  | ||||
| int16_t ScrollWheelWithSelectionStyle::getSelectedItemExtraSizeAfter() const | ||||
| { | ||||
|     return extraSizeAfterSelectedItem; | ||||
| } | ||||
|  | ||||
| void ScrollWheelWithSelectionStyle::setSelectedItemMargin(int16_t marginBefore, int16_t marginAfter) | ||||
| { | ||||
|     marginBeforeSelectedItem = marginBefore; | ||||
|     marginAfterSelectedItem = marginAfter; | ||||
|     refreshDrawableListsLayout(); | ||||
| } | ||||
|  | ||||
| int16_t ScrollWheelWithSelectionStyle::getSelectedItemMarginBefore() const | ||||
| { | ||||
|     return marginBeforeSelectedItem; | ||||
| } | ||||
|  | ||||
| int16_t ScrollWheelWithSelectionStyle::getSelectedItemMarginAfter() const | ||||
| { | ||||
|     return marginAfterSelectedItem; | ||||
| } | ||||
|  | ||||
| void ScrollWheelWithSelectionStyle::setSelectedItemPosition(int16_t offset, int16_t extraSizeBefore, int16_t extraSizeAfter, int16_t marginBefore, int16_t marginAfter) | ||||
| { | ||||
|     setSelectedItemOffset(offset); | ||||
|     setSelectedItemExtraSize(extraSizeBefore, extraSizeAfter); | ||||
|     setSelectedItemMargin(marginBefore, marginAfter); | ||||
| } | ||||
|  | ||||
| void ScrollWheelWithSelectionStyle::setDrawableSize(int16_t drawableSize, int16_t drawableMargin) | ||||
| { | ||||
|     ScrollWheelBase::setDrawableSize(drawableSize, drawableMargin); | ||||
|     list1.setDrawableSize(drawableSize, drawableMargin); | ||||
|     list2.setDrawableSize(drawableSize, drawableMargin); | ||||
|  | ||||
|     // Resize the three lists | ||||
|     setSelectedItemOffset(distanceBeforeAlignedItem); | ||||
|  | ||||
|     // Changing the drawable size alters number of required drawables, so refresh this | ||||
|     refreshDrawableListsLayout(); | ||||
| } | ||||
|  | ||||
| void ScrollWheelWithSelectionStyle::setDrawables(DrawableListItemsInterface& drawableListItems, GenericCallback<DrawableListItemsInterface*, int16_t, int16_t>& updateDrawableCallback, | ||||
|                                                  DrawableListItemsInterface& centerDrawableListItems, GenericCallback<DrawableListItemsInterface*, int16_t, int16_t>& updateCenterDrawableCallback) | ||||
| { | ||||
|     drawables = &drawableListItems; | ||||
|     centerDrawables = ¢erDrawableListItems; | ||||
|  | ||||
|     currentAnimationState = NO_ANIMATION; | ||||
|  | ||||
|     originalUpdateDrawableCallback = &updateDrawableCallback; | ||||
|     originalUpdateCenterDrawableCallback = &updateCenterDrawableCallback; | ||||
|  | ||||
|     refreshDrawableListsLayout(); | ||||
|  | ||||
|     setOffset(0); | ||||
| } | ||||
|  | ||||
| void ScrollWheelWithSelectionStyle::setOffset(int32_t offset) | ||||
| { | ||||
|     ScrollWheelBase::setOffset(offset); | ||||
|     list1.setOffset((distanceBeforeAlignedItem - (distanceBeforeAlignedItem - extraSizeBeforeSelectedItem)) + offset); | ||||
|     list2.setOffset((distanceBeforeAlignedItem - (distanceBeforeAlignedItem + itemSize + extraSizeAfterSelectedItem + marginAfterSelectedItem)) + offset); | ||||
| } | ||||
|  | ||||
| void ScrollWheelWithSelectionStyle::itemChanged(int itemIndex) | ||||
| { | ||||
|     ScrollWheelBase::itemChanged(itemIndex); | ||||
|     list1.itemChanged(itemIndex); | ||||
|     list2.itemChanged(itemIndex); | ||||
| } | ||||
|  | ||||
| void ScrollWheelWithSelectionStyle::refreshDrawableListsLayout() | ||||
| { | ||||
|     if (drawables != 0 && centerDrawables != 0) | ||||
|     { | ||||
|         int32_t currentOffset = getOffset(); | ||||
|  | ||||
|         int16_t list1Pos = distanceBeforeAlignedItem - extraSizeBeforeSelectedItem; | ||||
|         int16_t list2Pos = distanceBeforeAlignedItem + itemSize + (extraSizeAfterSelectedItem + marginAfterSelectedItem); | ||||
|         int16_t list0Size = list1Pos - marginBeforeSelectedItem; | ||||
|         int16_t list1Size = itemSize + extraSizeBeforeSelectedItem + extraSizeAfterSelectedItem; | ||||
|  | ||||
|         if (getHorizontal()) | ||||
|         { | ||||
|             int16_t list2Size = getWidth() - list2Pos; | ||||
|             list.setPosition(0, 0, list0Size, getHeight()); | ||||
|             list1.setPosition(list1Pos, 0, list1Size, getHeight()); | ||||
|             list2.setPosition(list2Pos, 0, list2Size, getHeight()); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             int16_t list2Size = getHeight() - list2Pos; | ||||
|             list.setPosition(0, 0, getWidth(), list0Size); | ||||
|             list1.setPosition(0, list1Pos, getWidth(), list1Size); | ||||
|             list2.setPosition(0, list2Pos, getWidth(), list2Size); | ||||
|         } | ||||
|  | ||||
|         list.setDrawables(*drawables, 0, *originalUpdateDrawableCallback); | ||||
|         drawablesInFirstList = list.getNumberOfDrawables(); | ||||
|         list1.setDrawables(*centerDrawables, 0, *originalUpdateCenterDrawableCallback); | ||||
|         list2.setDrawables(*drawables, drawablesInFirstList, *originalUpdateDrawableCallback); | ||||
|  | ||||
|         setOffset(keepOffsetInsideLimits(currentOffset, 0)); | ||||
|     } | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,35 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/AbstractButton.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void AbstractButton::handleClickEvent(const ClickEvent& event) | ||||
| { | ||||
|     bool wasPressed = pressed; | ||||
|     pressed = (event.getType() == ClickEvent::PRESSED); | ||||
|     if ((pressed && !wasPressed) || (!pressed && wasPressed)) | ||||
|     { | ||||
|         // Pressed state changed, so invalidate | ||||
|         invalidate(); | ||||
|     } | ||||
|     if (wasPressed && (event.getType() == ClickEvent::RELEASED)) | ||||
|     { | ||||
|         // This is a click. Fire callback. | ||||
|         executeAction(); | ||||
|     } | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,150 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/AnimatedImage.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void AnimatedImage::handleTickEvent() | ||||
| { | ||||
|     if (running) | ||||
|     { | ||||
|         ++ticksSinceUpdate; | ||||
|         if (ticksSinceUpdate != updateTicksInterval) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         ticksSinceUpdate = 0; | ||||
|         BitmapId currentId = getBitmap(); | ||||
|  | ||||
|         if (((currentId == endId) && !reverse) || ((currentId == startId) && reverse)) | ||||
|         { | ||||
|             if (!loopAnimation) | ||||
|             { | ||||
|                 Application::getInstance()->unregisterTimerWidget(this); | ||||
|                 running = false; | ||||
|             } | ||||
|  | ||||
|             if (animationDoneAction && animationDoneAction->isValid()) | ||||
|             { | ||||
|                 animationDoneAction->execute(*this); | ||||
|             } | ||||
|  | ||||
|             if (running && loopAnimation) | ||||
|             { | ||||
|                 if (reverse) | ||||
|                 { | ||||
|                     Image::setBitmap(Bitmap(endId)); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     Image::setBitmap(Bitmap(startId)); | ||||
|                 } | ||||
|                 invalidate(); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (reverse) | ||||
|             { | ||||
|                 --currentId; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 ++currentId; | ||||
|             } | ||||
|             Image::setBitmap(Bitmap(currentId)); | ||||
|             invalidate(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AnimatedImage::startAnimation(const bool rev, const bool reset /*= false*/, const bool loop /*= false*/) | ||||
| { | ||||
|     if ((startId != BITMAP_INVALID) && (endId != BITMAP_INVALID)) | ||||
|     { | ||||
|         reverse = rev; | ||||
|         loopAnimation = loop; | ||||
|         if (reverse && reset) | ||||
|         { | ||||
|             Image::setBitmap(Bitmap(endId)); | ||||
|             invalidate(); | ||||
|         } | ||||
|         else if (!reverse && reset) | ||||
|         { | ||||
|             Image::setBitmap(Bitmap(startId)); | ||||
|             invalidate(); | ||||
|         } | ||||
|         Application::getInstance()->registerTimerWidget(this); | ||||
|         running = true; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AnimatedImage::stopAnimation() | ||||
| { | ||||
|     if (running) | ||||
|     { | ||||
|         Application::getInstance()->unregisterTimerWidget(this); | ||||
|         running = false; | ||||
|     } | ||||
|     if (reverse) | ||||
|     { | ||||
|         Image::setBitmap(Bitmap(endId)); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         Image::setBitmap(Bitmap(startId)); | ||||
|     } | ||||
|     invalidate(); | ||||
| } | ||||
|  | ||||
| void AnimatedImage::pauseAnimation() | ||||
| { | ||||
|     if (running) | ||||
|     { | ||||
|         Application::getInstance()->unregisterTimerWidget(this); | ||||
|         running = false; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         Application::getInstance()->registerTimerWidget(this); | ||||
|         running = true; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AnimatedImage::setBitmap(const Bitmap& bitmap) | ||||
| { | ||||
|     startId = bitmap.getId(); | ||||
|     Image::setBitmap(bitmap); | ||||
| } | ||||
|  | ||||
| void AnimatedImage::setBitmapEnd(const Bitmap& bitmap) | ||||
| { | ||||
|     endId = bitmap.getId(); | ||||
| } | ||||
|  | ||||
| void AnimatedImage::setBitmaps(BitmapId start, BitmapId end) | ||||
| { | ||||
|     setBitmap(start); | ||||
|     setBitmapEnd(end); | ||||
| } | ||||
|  | ||||
| void AnimatedImage::setUpdateTicksInterval(uint8_t updateInterval) | ||||
| { | ||||
|     updateTicksInterval = updateInterval; | ||||
|     ticksSinceUpdate = 0; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,190 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/AnimationTextureMapper.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| AnimationTextureMapper::AnimationTextureMapper() | ||||
|     : TextureMapper(), | ||||
|       textureMapperAnimationStepCallback(0), | ||||
|       textureMapperAnimationEndedCallback(0), | ||||
|       animationCounter(0), | ||||
|       animationRunning(false) | ||||
| { | ||||
|     for (int i = 0; i < NUMBER_OF_ANIMATION_PARAMETERS; i++) | ||||
|     { | ||||
|         animations[i].animationActive = false; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AnimationTextureMapper::setTextureMapperAnimationStepAction(GenericCallback<const AnimationTextureMapper&>& callback) | ||||
| { | ||||
|     textureMapperAnimationStepCallback = &callback; | ||||
| } | ||||
|  | ||||
| void AnimationTextureMapper::setTextureMapperAnimationEndedAction(GenericCallback<const AnimationTextureMapper&>& callback) | ||||
| { | ||||
|     textureMapperAnimationEndedCallback = &callback; | ||||
| } | ||||
|  | ||||
| bool AnimationTextureMapper::isTextureMapperAnimationRunning() const | ||||
| { | ||||
|     return animationRunning; | ||||
| } | ||||
|  | ||||
| void AnimationTextureMapper::setupAnimation(AnimationParameter parameter, float endValue, uint16_t duration, uint16_t delay, EasingEquation progressionEquation /*= &EasingEquations::linearEaseNone*/) | ||||
| { | ||||
|     animations[parameter].animationActive = true; | ||||
|     animations[parameter].animationEnd = endValue; | ||||
|     animations[parameter].animationDuration = duration; | ||||
|     animations[parameter].animationDelay = delay; | ||||
|     animations[parameter].animationProgressionEquation = progressionEquation; | ||||
| } | ||||
|  | ||||
| void AnimationTextureMapper::startAnimation() | ||||
| { | ||||
|     Application::getInstance()->registerTimerWidget(this); | ||||
|  | ||||
|     animationCounter = 0; | ||||
|  | ||||
|     animations[X_ROTATION].animationStart = xAngle; | ||||
|     animations[Y_ROTATION].animationStart = yAngle; | ||||
|     animations[Z_ROTATION].animationStart = zAngle; | ||||
|     animations[SCALE].animationStart = scale; | ||||
|  | ||||
|     animationRunning = true; | ||||
|  | ||||
|     for (int i = 0; i < NUMBER_OF_ANIMATION_PARAMETERS; i++) | ||||
|     { | ||||
|         if (animations[i].animationActive && animations[i].animationDelay + animations[i].animationDuration > 0) | ||||
|         { | ||||
|             return; // Animation needs to run, return | ||||
|         } | ||||
|     } | ||||
|     // No active animations or all active animations have zero steps, execute now! | ||||
|     handleTickEvent(); | ||||
| } | ||||
|  | ||||
| void AnimationTextureMapper::cancelAnimationTextureMapperAnimation() | ||||
| { | ||||
|     Application::getInstance()->unregisterTimerWidget(this); | ||||
|     animationRunning = false; | ||||
|  | ||||
|     for (int i = 0; i < NUMBER_OF_ANIMATION_PARAMETERS; i++) | ||||
|     { | ||||
|         animations[i].animationActive = false; | ||||
|     } | ||||
| } | ||||
|  | ||||
| uint16_t AnimationTextureMapper::getAnimationStep() | ||||
| { | ||||
|     return animationCounter; | ||||
| } | ||||
|  | ||||
| void AnimationTextureMapper::handleTickEvent() | ||||
| { | ||||
|     if (animationRunning) | ||||
|     { | ||||
|         bool newValuesAssigned = false; | ||||
|         bool activeAnimationExists = false; | ||||
|  | ||||
|         animationCounter++; | ||||
|  | ||||
|         float newXAngle = xAngle; | ||||
|         float newYAngle = yAngle; | ||||
|         float newZAngle = zAngle; | ||||
|         float newScale = scale; | ||||
|  | ||||
|         for (int i = 0; i < NUMBER_OF_ANIMATION_PARAMETERS; i++) | ||||
|         { | ||||
|             if (!(animations[i].animationActive)) | ||||
|             { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             if (animationCounter >= animations[i].animationDelay) | ||||
|             { | ||||
|                 // Adjust the used animationCounter for the startup delay | ||||
|                 uint32_t actualAnimationCounter = animationCounter - animations[i].animationDelay; | ||||
|  | ||||
|                 int directionModifier; | ||||
|                 int16_t distance; | ||||
|  | ||||
|                 if (animations[i].animationEnd > animations[i].animationStart) | ||||
|                 { | ||||
|                     directionModifier = 1; | ||||
|                     distance = (int16_t)((animations[i].animationEnd - animations[i].animationStart) * 1000); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     directionModifier = -1; | ||||
|                     distance = (int16_t)((animations[i].animationStart - animations[i].animationEnd) * 1000); | ||||
|                 } | ||||
|  | ||||
|                 float delta = directionModifier * (animations[i].animationProgressionEquation(actualAnimationCounter, 0, distance, animations[i].animationDuration) / 1000.f); | ||||
|  | ||||
|                 switch ((AnimationParameter)i) | ||||
|                 { | ||||
|                 case X_ROTATION: | ||||
|                     newXAngle = animations[X_ROTATION].animationStart + delta; | ||||
|                     break; | ||||
|                 case Y_ROTATION: | ||||
|                     newYAngle = animations[Y_ROTATION].animationStart + delta; | ||||
|                     break; | ||||
|                 case Z_ROTATION: | ||||
|                     newZAngle = animations[Z_ROTATION].animationStart + delta; | ||||
|                     break; | ||||
|                 case SCALE: | ||||
|                     newScale = animations[SCALE].animationStart + delta; | ||||
|                     break; | ||||
|                 default: | ||||
|                     break; | ||||
|                 } | ||||
|                 newValuesAssigned = true; | ||||
|             } | ||||
|             if (animationCounter >= (uint32_t)(animations[i].animationDelay + animations[i].animationDuration)) | ||||
|             { | ||||
|                 animations[i].animationActive = false; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 activeAnimationExists = true; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (newValuesAssigned) | ||||
|         { | ||||
|             updateAngles(newXAngle, newYAngle, newZAngle); | ||||
|             setScale(newScale); | ||||
|  | ||||
|             if (textureMapperAnimationStepCallback && textureMapperAnimationStepCallback->isValid()) | ||||
|             { | ||||
|                 textureMapperAnimationStepCallback->execute(*this); | ||||
|             } | ||||
|         } | ||||
|         if (!activeAnimationExists) | ||||
|         { | ||||
|             // End of animation | ||||
|             cancelAnimationTextureMapperAnimation(); | ||||
|  | ||||
|             if (textureMapperAnimationEndedCallback && textureMapperAnimationEndedCallback->isValid()) | ||||
|             { | ||||
|                 textureMapperAnimationEndedCallback->execute(*this); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,38 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/Box.hpp> | ||||
| #include <touchgfx/hal/HAL.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| Rect Box::getSolidRect() const | ||||
| { | ||||
|     Rect solidRect(0, 0, 0, 0); | ||||
|     if (alpha == 255) | ||||
|     { | ||||
|         solidRect.width = rect.width; | ||||
|         solidRect.height = rect.height; | ||||
|     } | ||||
|     return solidRect; | ||||
| } | ||||
|  | ||||
| void Box::draw(const Rect& area) const | ||||
| { | ||||
|     Rect dirty = area; | ||||
|     translateRectToAbsolute(dirty); | ||||
|     HAL::lcd().fillRect(dirty, color, alpha); | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,57 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/BoxWithBorder.hpp> | ||||
| #include <touchgfx/hal/HAL.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void BoxWithBorder::draw(const Rect& area) const | ||||
| { | ||||
|     const Rect center = Rect(borderSize, borderSize, getWidth() - (2 * borderSize), getHeight() - (2 * borderSize)); | ||||
|     if (center.isEmpty()) | ||||
|     { | ||||
|         Rect dirty = area; | ||||
|         translateRectToAbsolute(dirty); | ||||
|         HAL::lcd().fillRect(dirty, borderColor, alpha); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Rect dirty = area & center; | ||||
|     Box::draw(dirty); | ||||
|  | ||||
|     if (borderSize == 0) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Rect borders[4] = | ||||
|     { | ||||
|         Rect(0, 0, getWidth(), borderSize),                                                   // Upper | ||||
|         Rect(0, getHeight() - borderSize, getWidth(), borderSize),                            // lower | ||||
|         Rect(0, borderSize, borderSize, getHeight() - (2 * borderSize)),                      // left | ||||
|         Rect(getWidth() - borderSize, borderSize, borderSize, getHeight() - (2 * borderSize)) // right | ||||
|     }; | ||||
|     for (int i = 0; i < 4; i++) | ||||
|     { | ||||
|         Rect borderDirty = borders[i] & area; | ||||
|         if (!borderDirty.isEmpty()) | ||||
|         { | ||||
|             translateRectToAbsolute(borderDirty); | ||||
|             HAL::lcd().fillRect(borderDirty, borderColor, alpha); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,52 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/Button.hpp> | ||||
| #include <touchgfx/hal/HAL.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void Button::draw(const Rect& invalidatedArea) const | ||||
| { | ||||
|     Bitmap bmp(pressed ? down : up); | ||||
|     Rect dirty(0, 0, bmp.getWidth(), bmp.getHeight()); | ||||
|     dirty &= invalidatedArea; | ||||
|     if ((bmp.getId() != BITMAP_INVALID) && !dirty.isEmpty()) | ||||
|     { | ||||
|         Rect r; | ||||
|         translateRectToAbsolute(r); | ||||
|         HAL::lcd().drawPartialBitmap(bmp, r.x, r.y, dirty, alpha); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void Button::setBitmaps(const Bitmap& bitmapReleased, const Bitmap& bitmapPressed) | ||||
| { | ||||
|     up = bitmapReleased; | ||||
|     down = bitmapPressed; | ||||
|     // Adjust width and height of this widget to match bitmap. It is assumed | ||||
|     // that the two bitmaps have same dimensions. | ||||
|     Button::setWidthHeight(down); | ||||
| } | ||||
|  | ||||
| Rect Button::getSolidRect() const | ||||
| { | ||||
|     if (alpha < 255) | ||||
|     { | ||||
|         return Rect(0, 0, 0, 0); | ||||
|     } | ||||
|  | ||||
|     return (pressed ? down.getSolidRect() : up.getSolidRect()); | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,56 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/ButtonWithIcon.hpp> | ||||
| #include <touchgfx/hal/HAL.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| ButtonWithIcon::ButtonWithIcon() | ||||
|     : Button(), | ||||
|       iconX(0), | ||||
|       iconY(0) | ||||
| { | ||||
| } | ||||
|  | ||||
| void ButtonWithIcon::setBitmaps(const Bitmap& newBackgroundReleased, const Bitmap& newBackgroundPressed, | ||||
|                                 const Bitmap& newIconReleased, const Bitmap& newIconPressed) | ||||
| { | ||||
|     Button::setBitmaps(newBackgroundReleased, newBackgroundPressed); | ||||
|  | ||||
|     iconReleased = newIconReleased; | ||||
|     iconPressed = newIconPressed; | ||||
|  | ||||
|     iconX = (getWidth() / 2) - (newIconPressed.getWidth() / 2); | ||||
|     iconY = (getHeight() / 2) - (newIconPressed.getHeight() / 2); | ||||
| } | ||||
|  | ||||
| void ButtonWithIcon::draw(const Rect& invalidatedArea) const | ||||
| { | ||||
|     Button::draw(invalidatedArea); | ||||
|  | ||||
|     Bitmap bmp((pressed ? iconPressed : iconReleased)); | ||||
|     Rect iconRect(iconX, iconY, bmp.getWidth(), bmp.getHeight()); | ||||
|     Rect dirty = invalidatedArea & iconRect; | ||||
|     if ((bmp.getId() != BITMAP_INVALID) && !dirty.isEmpty()) | ||||
|     { | ||||
|         Rect r; | ||||
|         translateRectToAbsolute(r); | ||||
|         dirty.x -= iconX; | ||||
|         dirty.y -= iconY; | ||||
|         HAL::lcd().drawPartialBitmap(bmp, r.x + iconX, r.y + iconY, dirty, alpha); | ||||
|     } | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,63 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/ButtonWithLabel.hpp> | ||||
| #include <touchgfx/FontManager.hpp> | ||||
| #include <touchgfx/hal/HAL.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| ButtonWithLabel::ButtonWithLabel() | ||||
|     : Button(), color(0), colorPressed(0), rotation(TEXT_ROTATE_0), textHeightIncludingSpacing(0) | ||||
| { | ||||
| } | ||||
|  | ||||
| void ButtonWithLabel::draw(const Rect& area) const | ||||
| { | ||||
|     Button::draw(area); | ||||
|  | ||||
|     if (typedText.hasValidId()) | ||||
|     { | ||||
|         const Font* fontToDraw = typedText.getFont(); //never return 0 | ||||
|         uint8_t height = textHeightIncludingSpacing; | ||||
|         int16_t offset; | ||||
|         Rect labelRect; | ||||
|         switch (rotation) | ||||
|         { | ||||
|         default: | ||||
|         case TEXT_ROTATE_0: | ||||
|         case TEXT_ROTATE_180: | ||||
|             offset = (this->getHeight() - height) / 2; | ||||
|             labelRect = Rect(0, offset, this->getWidth(), height); | ||||
|             break; | ||||
|         case TEXT_ROTATE_90: | ||||
|         case TEXT_ROTATE_270: | ||||
|             offset = (this->getWidth() - height) / 2; | ||||
|             labelRect = Rect(offset, 0, height, this->getHeight()); | ||||
|             break; | ||||
|         } | ||||
|         Rect dirty = labelRect & area; | ||||
|  | ||||
|         if (!dirty.isEmpty()) | ||||
|         { | ||||
|             dirty.x -= labelRect.x; | ||||
|             dirty.y -= labelRect.y; | ||||
|             translateRectToAbsolute(labelRect); | ||||
|             LCD::StringVisuals visuals(fontToDraw, pressed ? colorPressed : color, alpha, typedText.getAlignment(), 0, rotation, typedText.getTextDirection(), 0, WIDE_TEXT_NONE); | ||||
|             HAL::lcd().drawString(labelRect, dirty, visuals, typedText.getText()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,183 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/Gauge.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| Gauge::Gauge() | ||||
|     : AbstractProgressIndicator(), | ||||
|       algorithmMoving(TextureMapper::BILINEAR_INTERPOLATION), | ||||
|       algorithmSteady(TextureMapper::BILINEAR_INTERPOLATION), | ||||
|       needleStartAngle(0), | ||||
|       needleEndAngle(0), | ||||
|       gaugeCenterX(0), | ||||
|       gaugeCenterY(0), | ||||
|       needleCenterX(0), | ||||
|       needleCenterY(0) | ||||
| { | ||||
|     remove(progressIndicatorContainer); | ||||
|     add(arc); | ||||
|     add(needle); | ||||
|     arc.setVisible(false); | ||||
| } | ||||
|  | ||||
| void Gauge::setWidth(int16_t width) | ||||
| { | ||||
|     AbstractProgressIndicator::setWidth(width); | ||||
|     needle.setWidth(width); | ||||
|     arc.setWidth(width); | ||||
| } | ||||
|  | ||||
| void Gauge::setHeight(int16_t height) | ||||
| { | ||||
|     AbstractProgressIndicator::setHeight(height); | ||||
|     needle.setHeight(height); | ||||
|     arc.setHeight(height); | ||||
| } | ||||
|  | ||||
| void Gauge::setBackgroundOffset(int16_t offsetX, int16_t offsetY) | ||||
| { | ||||
|     background.setXY(offsetX, offsetY); | ||||
| } | ||||
|  | ||||
| void Gauge::setCenter(int x, int y) | ||||
| { | ||||
|     gaugeCenterX = x; | ||||
|     gaugeCenterY = y; | ||||
|     setupNeedleTextureMapper(); | ||||
|     arc.setPixelCenter(x - arc.getX(), y - arc.getY()); | ||||
| } | ||||
|  | ||||
| void Gauge::setArcPosition(int16_t x, int16_t y, int16_t width, int16_t height) | ||||
| { | ||||
|     arc.setPosition(x, y, width, height); | ||||
|     arc.setPixelCenter(gaugeCenterX - x, gaugeCenterY - y); | ||||
| } | ||||
|  | ||||
| void Gauge::setNeedle(const BitmapId bitmapId, int16_t rotationCenterX, int16_t rotationCenterY) | ||||
| { | ||||
|     needle.setBitmap(Bitmap(bitmapId)); | ||||
|     needleCenterX = rotationCenterX; | ||||
|     needleCenterY = rotationCenterY; | ||||
|     setupNeedleTextureMapper(); | ||||
| } | ||||
|  | ||||
| void Gauge::setMovingNeedleRenderingAlgorithm(TextureMapper::RenderingAlgorithm algorithm) | ||||
| { | ||||
|     algorithmMoving = algorithm; | ||||
| } | ||||
|  | ||||
| void Gauge::setSteadyNeedleRenderingAlgorithm(TextureMapper::RenderingAlgorithm algorithm) | ||||
| { | ||||
|     algorithmSteady = algorithm; | ||||
| } | ||||
|  | ||||
| void Gauge::setStartEndAngle(int startAngle, int endAngle) | ||||
| { | ||||
|     assert(startAngle != endAngle); | ||||
|     needleStartAngle = startAngle; | ||||
|     needleEndAngle = endAngle; | ||||
|     arc.setArc(startAngle, endAngle); | ||||
|     Gauge::setValue(Gauge::getValue()); | ||||
| } | ||||
|  | ||||
| int Gauge::getStartAngle() const | ||||
| { | ||||
|     return needleStartAngle; | ||||
| } | ||||
|  | ||||
| int Gauge::getEndAngle() const | ||||
| { | ||||
|     return needleEndAngle; | ||||
| } | ||||
|  | ||||
| void Gauge::setArcVisible(bool show /*= true*/) | ||||
| { | ||||
|     arc.setVisible(show); | ||||
| } | ||||
|  | ||||
| void Gauge::putArcOnTop(bool onTop /*= true*/) | ||||
| { | ||||
|     if (onTop) | ||||
|     { | ||||
|         remove(arc); | ||||
|         add(arc); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         remove(needle); | ||||
|         add(needle); | ||||
|     } | ||||
| } | ||||
|  | ||||
| touchgfx::Circle& Gauge::getArc() | ||||
| { | ||||
|     return arc; | ||||
| } | ||||
|  | ||||
| void Gauge::setValue(int value) | ||||
| { | ||||
|     AbstractProgressIndicator::setValue(value); | ||||
|     if (animationStep >= animationDuration) | ||||
|     { | ||||
|         needle.setRenderingAlgorithm(algorithmSteady); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         needle.setRenderingAlgorithm(algorithmMoving); | ||||
|     } | ||||
|     uint16_t progress = AbstractProgressIndicator::getProgress(abs(needleEndAngle - needleStartAngle)); | ||||
|     if (needleEndAngle < needleStartAngle) | ||||
|     { | ||||
|         needle.updateZAngle((needleStartAngle - progress) / 180.0f * PI); | ||||
|         arc.updateArcEnd(needleStartAngle - progress); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         needle.updateZAngle((needleStartAngle + progress) / 180.0f * PI); | ||||
|         arc.updateArcEnd(needleStartAngle + progress); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void Gauge::setAlpha(uint8_t newAlpha) | ||||
| { | ||||
|     background.setAlpha(newAlpha); | ||||
|     needle.setAlpha(newAlpha); | ||||
|     arc.setAlpha(newAlpha); | ||||
| } | ||||
|  | ||||
| uint8_t Gauge::getAlpha() const | ||||
| { | ||||
|     return needle.getAlpha(); | ||||
| } | ||||
|  | ||||
| void Gauge::setupNeedleTextureMapper() | ||||
| { | ||||
|     needle.setWidthHeight(*this); | ||||
|     needle.setXY(0, 0); | ||||
|     needle.setBitmapPosition(gaugeCenterX - needleCenterX, gaugeCenterY - needleCenterY); | ||||
|     needle.setCameraDistance(300.0f); | ||||
|     needle.setOrigo((float)gaugeCenterX, (float)gaugeCenterY, needle.getCameraDistance()); | ||||
|     needle.setCamera(needle.getOrigoX(), needle.getOrigoY()); | ||||
|     needle.setRenderingAlgorithm(TextureMapper::BILINEAR_INTERPOLATION); | ||||
| } | ||||
|  | ||||
|  | ||||
| void Gauge::setProgressIndicatorPosition(int16_t /*x*/, int16_t /*y*/, int16_t /*width*/, int16_t /*height*/) | ||||
| { | ||||
| } | ||||
|  | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,52 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/Image.hpp> | ||||
| #include <touchgfx/hal/HAL.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void Image::setBitmap(const Bitmap& bitmap) | ||||
| { | ||||
|     this->bitmap = bitmap; | ||||
|     // When setting bitmap, adjust size of this widget to match. | ||||
|     setWidthHeight(bitmap); | ||||
| } | ||||
|  | ||||
| void Image::draw(const Rect& invalidatedArea) const | ||||
| { | ||||
|     Rect meAbs; | ||||
|     translateRectToAbsolute(meAbs); //To find our x and y coords in absolute. | ||||
|  | ||||
|     // Calculate intersection between bitmap rect and invalidated area. | ||||
|     Rect dirtyBitmapArea = bitmap.getRect() & invalidatedArea; | ||||
|  | ||||
|     if (!dirtyBitmapArea.isEmpty()) | ||||
|     { | ||||
|         HAL::lcd().drawPartialBitmap(bitmap, meAbs.x, meAbs.y, dirtyBitmapArea, alpha); | ||||
|     } | ||||
| } | ||||
|  | ||||
| Rect Image::getSolidRect() const | ||||
| { | ||||
|     // If alpha is less than solid, we have an empty solid rect. | ||||
|     if (alpha < 255) | ||||
|     { | ||||
|         return Rect(0, 0, 0, 0); | ||||
|     } | ||||
|     // Return solid rect from bitmap (precalculated). | ||||
|     return bitmap.getSolidRect(); | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,296 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/hal/Types.hpp> | ||||
| #include <touchgfx/widgets/Keyboard.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| Keyboard::Keyboard() | ||||
|     : Container(), keyListener(0), bufferSize(0), bufferPosition(0), highlightImage(), cancelIsEmitted(false) | ||||
| { | ||||
|     setTouchable(true); | ||||
|  | ||||
|     keyMappingList = static_cast<KeyMappingList*>(0); | ||||
|     buffer = static_cast<Unicode::UnicodeChar*>(0); | ||||
|     layout = static_cast<Layout*>(0); | ||||
|  | ||||
|     image.setXY(0, 0); | ||||
|     Container::add(image); | ||||
|  | ||||
|     highlightImage.setVisible(false); | ||||
|     Container::add(highlightImage); | ||||
|  | ||||
|     enteredText.setColor(Color::getColorFrom24BitRGB(0, 0, 0)); | ||||
|     Container::add(enteredText); | ||||
| } | ||||
|  | ||||
| void Keyboard::setBuffer(Unicode::UnicodeChar* newBuffer, uint16_t newBufferSize) | ||||
| { | ||||
|     buffer = newBuffer; | ||||
|     bufferSize = newBufferSize; | ||||
|  | ||||
|     enteredText.setWildcard(buffer); | ||||
|  | ||||
|     // Place cursor at end of string if we already have something | ||||
|     // in the edit buffer. | ||||
|     bufferPosition = Unicode::strlen(buffer); | ||||
| } | ||||
|  | ||||
| void Keyboard::setLayout(const Layout* newLayout) | ||||
| { | ||||
|     layout = newLayout; | ||||
|     if (newLayout != 0) | ||||
|     { | ||||
|         image.setBitmap(Bitmap(newLayout->bitmap)); | ||||
|  | ||||
|         enteredText.setTypedText(newLayout->textAreaFont); | ||||
|         enteredText.setColor(newLayout->textAreaFontColor); | ||||
|         enteredText.setPosition(newLayout->textAreaPosition.x, newLayout->textAreaPosition.y, | ||||
|                                 newLayout->textAreaPosition.width, newLayout->textAreaPosition.height); | ||||
|     } | ||||
|     invalidate(); | ||||
| } | ||||
|  | ||||
| void Keyboard::setTextIndentation() | ||||
| { | ||||
|     if (layout != 0) | ||||
|     { | ||||
|         uint8_t indentation = layout->textAreaFont.getFont()->getMaxPixelsLeft(); | ||||
|         enteredText.setPosition(layout->textAreaPosition.x - indentation, layout->textAreaPosition.y, | ||||
|                                 layout->textAreaPosition.width + indentation * 2, layout->textAreaPosition.height); | ||||
|         enteredText.setIndentation(indentation); | ||||
|     } | ||||
| } | ||||
|  | ||||
| Keyboard::Key Keyboard::getKeyForCoordinates(int16_t x, int16_t y) const | ||||
| { | ||||
|     Key key; | ||||
|     key.keyId = 0; // No key | ||||
|     if (layout != 0) | ||||
|     { | ||||
|         for (uint8_t i = 0; i < layout->numberOfKeys; i++) | ||||
|         { | ||||
|             if (layout->keyArray[i].keyArea.intersect(x, y)) | ||||
|             { | ||||
|                 key = layout->keyArray[i]; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return key; | ||||
| } | ||||
|  | ||||
| Keyboard::CallbackArea Keyboard::getCallbackAreaForCoordinates(int16_t x, int16_t y) const | ||||
| { | ||||
|     CallbackArea area; | ||||
|     area.callback = reinterpret_cast<GenericCallback<>*>(0); | ||||
|     if (layout != 0) | ||||
|     { | ||||
|         for (uint8_t i = 0; i < layout->numberOfCallbackAreas; i++) | ||||
|         { | ||||
|             if (layout->callbackAreaArray[i].keyArea.intersect(x, y)) | ||||
|             { | ||||
|                 area = layout->callbackAreaArray[i]; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return area; | ||||
| } | ||||
|  | ||||
| void Keyboard::draw(const Rect& invalidatedArea) const | ||||
| { | ||||
|     assert(layout && "No layout configured for Keyboard"); | ||||
|     if (layout != 0) | ||||
|     { | ||||
|         Font* font = FontManager::getFont(layout->keyFont); | ||||
|         assert(font && "Keyboard::draw: Unable to find font, is font db initialized?"); | ||||
|         if (font != 0) | ||||
|         { | ||||
|             // Setup visuals for h-center of "string" | ||||
|             LCD::StringVisuals visuals; | ||||
|             visuals.font = font; | ||||
|             visuals.alignment = CENTER; | ||||
|             visuals.color = layout->keyFontColor; | ||||
|             // String with room for a single character | ||||
|             Unicode::UnicodeChar character[2] = { 0, 0 }; // The last is important as string terminator. | ||||
|  | ||||
|             uint16_t fontHeight = font->getMinimumTextHeight(); | ||||
|  | ||||
|             for (uint8_t i = 0; i < layout->numberOfKeys; i++) | ||||
|             { | ||||
|                 const Key& key = layout->keyArray[i]; | ||||
|                 if (key.keyArea.intersect(invalidatedArea)) | ||||
|                 { | ||||
|                     uint8_t keyId = key.keyId; | ||||
|                     Unicode::UnicodeChar c = getCharForKey(keyId); | ||||
|                     if (c != 0) | ||||
|                     { | ||||
|                         // Get a copy of the keyArea and v-center the area for the character | ||||
|                         Rect keyArea = key.keyArea; | ||||
|                         uint16_t offset = (keyArea.height - fontHeight) / 2; | ||||
|                         keyArea.y += offset; | ||||
|                         keyArea.height -= offset; | ||||
|                         // Calculate the invalidated area relative to the key | ||||
|                         Rect invalidatedAreaRelative = key.keyArea & invalidatedArea; | ||||
|                         invalidatedAreaRelative.x -= keyArea.x; | ||||
|                         invalidatedAreaRelative.y -= keyArea.y; | ||||
|                         // Set up string with one character | ||||
|                         character[0] = c; | ||||
|                         translateRectToAbsolute(keyArea); | ||||
|                         HAL::lcd().drawString(keyArea, invalidatedAreaRelative, visuals, character); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void Keyboard::handleClickEvent(const ClickEvent& evt) | ||||
| { | ||||
|     ClickEvent::ClickEventType type = evt.getType(); | ||||
|     if (type == ClickEvent::RELEASED && cancelIsEmitted) | ||||
|     { | ||||
|         cancelIsEmitted = false; | ||||
|         return; | ||||
|     } | ||||
|     int16_t x = evt.getX(); | ||||
|     int16_t y = evt.getY(); | ||||
|     Rect toDraw; | ||||
|  | ||||
|     Keyboard::CallbackArea callbackArea = getCallbackAreaForCoordinates(x, y); | ||||
|     if (callbackArea.callback != 0) | ||||
|     { | ||||
|         if (type == ClickEvent::PRESSED) | ||||
|         { | ||||
|             highlightImage.setXY(callbackArea.keyArea.x, callbackArea.keyArea.y); | ||||
|             highlightImage.setBitmap(Bitmap(callbackArea.highlightBitmapId)); | ||||
|             highlightImage.setVisible(true); | ||||
|             toDraw = highlightImage.getRect(); | ||||
|             invalidateRect(toDraw); | ||||
|         } | ||||
|  | ||||
|         if ((type == ClickEvent::RELEASED) && callbackArea.callback->isValid()) | ||||
|         { | ||||
|             callbackArea.callback->execute(); | ||||
|             if (keyListener) | ||||
|             { | ||||
|                 keyListener->execute(0); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         Keyboard::Key key = getKeyForCoordinates(x, y); | ||||
|  | ||||
|         if (type == ClickEvent::PRESSED) | ||||
|         { | ||||
|             highlightImage.setXY(key.keyArea.x, key.keyArea.y); | ||||
|             highlightImage.setBitmap(Bitmap(key.highlightBitmapId)); | ||||
|             highlightImage.setVisible(true); | ||||
|             toDraw = highlightImage.getRect(); | ||||
|             invalidateRect(toDraw); | ||||
|         } | ||||
|  | ||||
|         if (type == ClickEvent::RELEASED) | ||||
|         { | ||||
|             if (key.keyId != 0 && buffer) | ||||
|             { | ||||
|                 Unicode::UnicodeChar c = getCharForKey(key.keyId); | ||||
|                 if (c != 0) | ||||
|                 { | ||||
|                     uint16_t prevBufferPosition = bufferPosition; | ||||
|                     if (bufferPosition < (bufferSize - 1)) | ||||
|                     { | ||||
|                         buffer[bufferPosition++] = c; | ||||
|                         buffer[bufferPosition] = 0; | ||||
|                     } | ||||
|                     if (prevBufferPosition != bufferPosition) | ||||
|                     { | ||||
|                         enteredText.invalidate(); | ||||
|                         if (keyListener) | ||||
|                         { | ||||
|                             keyListener->execute(c); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (type == ClickEvent::RELEASED || type == ClickEvent::CANCEL) | ||||
|     { | ||||
|         toDraw = highlightImage.getRect(); | ||||
|         highlightImage.setVisible(false); | ||||
|         invalidateRect(toDraw); | ||||
|  | ||||
|         if (type == ClickEvent::CANCEL) | ||||
|         { | ||||
|             cancelIsEmitted = true; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void Keyboard::handleDragEvent(const DragEvent& event) | ||||
| { | ||||
|     if (highlightImage.isVisible() && (!highlightImage.getRect().intersect(static_cast<int16_t>(event.getNewX()), static_cast<int16_t>(event.getNewY()))) && (!cancelIsEmitted)) | ||||
|     { | ||||
|         // Send a CANCEL click event, if user has dragged out of currently pressed/highlighted key. | ||||
|         ClickEvent cancelEvent(ClickEvent::CANCEL, static_cast<int16_t>(event.getOldX()), static_cast<int16_t>(event.getOldY())); | ||||
|         handleClickEvent(cancelEvent); | ||||
|     } | ||||
| } | ||||
|  | ||||
| Unicode::UnicodeChar Keyboard::getCharForKey(uint8_t keyId) const | ||||
| { | ||||
|     Unicode::UnicodeChar ch = 0; | ||||
|     if (keyMappingList != 0) | ||||
|     { | ||||
|         for (uint8_t i = 0; i < keyMappingList->numberOfKeys; i++) | ||||
|         { | ||||
|             if (keyMappingList->keyMappingArray[i].keyId == keyId) | ||||
|             { | ||||
|                 ch = keyMappingList->keyMappingArray[i].keyValue; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return ch; | ||||
| } | ||||
|  | ||||
| void Keyboard::setupDrawChain(const Rect& invalidatedArea, Drawable** nextPreviousElement) | ||||
| { | ||||
|     // Keyboard is a Container, and they do not normally appear in the draw chain (they just draw children). | ||||
|     // But this particular container actually has a draw() function implementation, so we must change default | ||||
|     // behavior. | ||||
|     // First, add children | ||||
|     Container::setupDrawChain(invalidatedArea, nextPreviousElement); | ||||
|     // Then add yourself | ||||
|     Drawable::setupDrawChain(invalidatedArea, nextPreviousElement); | ||||
| } | ||||
|  | ||||
| void Keyboard::setBufferPosition(uint16_t newPos) | ||||
| { | ||||
|     bufferPosition = newPos; | ||||
|     enteredText.invalidate(); | ||||
| } | ||||
|  | ||||
| void Keyboard::setKeymappingList(const KeyMappingList* newKeyMappingList) | ||||
| { | ||||
|     keyMappingList = newKeyMappingList; | ||||
|     invalidate(); | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,92 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/hal/HAL.hpp> | ||||
| #include <touchgfx/widgets/PixelDataWidget.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| PixelDataWidget::PixelDataWidget() | ||||
|     : Widget(), | ||||
|       buffer(0), | ||||
|       format(Bitmap::RGB888), | ||||
|       alpha(255) | ||||
| { | ||||
| } | ||||
|  | ||||
| void PixelDataWidget::setPixelData(uint8_t* const data) | ||||
| { | ||||
|     buffer = data; | ||||
| } | ||||
|  | ||||
| void PixelDataWidget::setBitmapFormat(Bitmap::BitmapFormat f) | ||||
| { | ||||
|     format = f; | ||||
| } | ||||
|  | ||||
| void PixelDataWidget::setAlpha(uint8_t newAlpha) | ||||
| { | ||||
|     alpha = newAlpha; | ||||
| } | ||||
|  | ||||
| uint8_t PixelDataWidget::getAlpha() const | ||||
| { | ||||
|     return alpha; | ||||
| } | ||||
|  | ||||
| void PixelDataWidget::draw(const Rect& invalidatedArea) const | ||||
| { | ||||
|     if (buffer != 0) | ||||
|     { | ||||
|         //convert to lcd coordinates | ||||
|         const Rect absolute = getAbsoluteRect(); | ||||
|         //copy to LCD | ||||
|         HAL::lcd().blitCopy(buffer, format, absolute, invalidatedArea, alpha, false); | ||||
|     } | ||||
| } | ||||
|  | ||||
| Rect PixelDataWidget::getSolidRect() const | ||||
| { | ||||
|     Rect solidRect(0, 0, 0, 0); | ||||
|     if (alpha == 255) | ||||
|     { | ||||
|         // There are at least some solid pixels | ||||
|         switch (format) | ||||
|         { | ||||
|         case Bitmap::BW:     ///< 1-bit, black / white, no alpha channel | ||||
|         case Bitmap::BW_RLE: ///< 1-bit, black / white, no alpha channel compressed with horizontal RLE | ||||
|         case Bitmap::GRAY2:  ///< 2-bit, gray scale, no alpha channel | ||||
|         case Bitmap::GRAY4:  ///< 4-bit, gray scale, no alpha channel | ||||
|         case Bitmap::RGB565: ///< 16-bit, 5 bits for red, 6 bits for green, 5 bits for blue, no alpha channel | ||||
|         case Bitmap::RGB888: ///< 16-bit, 5 bits for red, 6 bits for green, 5 bits for blue, no alpha channel | ||||
|             //All solid pixels | ||||
|             solidRect.width = getWidth(); | ||||
|             solidRect.height = getHeight(); | ||||
|             break; | ||||
|         case Bitmap::ARGB8888: ///< 32-bit, 8 bits for each of red, green, blue and alpha channel | ||||
|         case Bitmap::ARGB2222: ///< 8-bit color | ||||
|         case Bitmap::ABGR2222: ///< 8-bit color | ||||
|         case Bitmap::RGBA2222: ///< 8-bit color | ||||
|         case Bitmap::BGRA2222: ///< 8-bit color | ||||
|         case Bitmap::L8:       ///< 8-bit indexed color | ||||
|         case Bitmap::A4:       ///< 4-bit alpha level | ||||
|         case Bitmap::CUSTOM:   ///< Custom pixelformat | ||||
|             //No knowledge about solid pixels | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     return solidRect; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,97 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/RadioButton.hpp> | ||||
| #include <touchgfx/hal/HAL.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void RadioButton::draw(const Rect& invalidatedArea) const | ||||
| { | ||||
|     Bitmap bitmap = getCurrentlyDisplayedBitmap(); | ||||
|     if (bitmap.getId() != BITMAP_INVALID) | ||||
|     { | ||||
|         Rect meAbs; | ||||
|         translateRectToAbsolute(meAbs); //To find our x and y coords in absolute. | ||||
|  | ||||
|         // Calculate intersection between bitmap rect and invalidated area. | ||||
|         Rect dirtyBitmapArea = bitmap.getRect() & invalidatedArea; | ||||
|  | ||||
|         if (!dirtyBitmapArea.isEmpty()) | ||||
|         { | ||||
|             HAL::lcd().drawPartialBitmap(bitmap, meAbs.x, meAbs.y, dirtyBitmapArea, alpha); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void RadioButton::handleClickEvent(const ClickEvent& event) | ||||
| { | ||||
|     bool wasPressed = pressed; | ||||
|     pressed = (event.getType() == ClickEvent::PRESSED); | ||||
|     if ((pressed && !wasPressed) || (!pressed && wasPressed)) | ||||
|     { | ||||
|         invalidate(); | ||||
|     } | ||||
|     if (wasPressed && (event.getType() == ClickEvent::RELEASED)) | ||||
|     { | ||||
|         if (deselectionEnabled) | ||||
|         { | ||||
|             setSelected(!getSelected()); | ||||
|         } | ||||
|         else if (!getSelected()) | ||||
|         { | ||||
|             setSelected(true); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void RadioButton::setBitmaps(const Bitmap& bmpUnselected, const Bitmap& bmpUnselectedPressed, const Bitmap& bmpSelected, const Bitmap& bmpSelectedPressed) | ||||
| { | ||||
|     bitmapUnselected = bmpUnselected; | ||||
|     bitmapUnselectedPressed = bmpUnselectedPressed; | ||||
|     bitmapSelected = bmpSelected; | ||||
|     bitmapSelectedPressed = bmpSelectedPressed; | ||||
|  | ||||
|     RadioButton::setWidthHeight(bitmapUnselected); | ||||
| } | ||||
|  | ||||
| Rect RadioButton::getSolidRect() const | ||||
| { | ||||
|     if (alpha < 255) | ||||
|     { | ||||
|         return Rect(0, 0, 0, 0); | ||||
|     } | ||||
|  | ||||
|     return getCurrentlyDisplayedBitmap().getSolidRect(); | ||||
| } | ||||
|  | ||||
| void RadioButton::setSelected(bool newSelected) | ||||
| { | ||||
|     bool wasSelected = selected; | ||||
|     selected = newSelected; | ||||
|  | ||||
|     if (wasSelected && !newSelected) | ||||
|     { | ||||
|         executeDeselectedAction(); | ||||
|     } | ||||
|  | ||||
|     if (!wasSelected && newSelected) | ||||
|     { | ||||
|         executeAction(); | ||||
|     } | ||||
|  | ||||
|     invalidate(); | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,83 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/RepeatButton.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| RepeatButton::RepeatButton() | ||||
|     : Button(), ticksDelay(30), ticksInterval(15), ticks(0), ticksBeforeContinuous(0) | ||||
| { | ||||
| } | ||||
|  | ||||
| void RepeatButton::setDelay(int delay) | ||||
| { | ||||
|     ticksDelay = delay; | ||||
| } | ||||
|  | ||||
| int RepeatButton::getDelay() | ||||
| { | ||||
|     return ticksDelay; | ||||
| } | ||||
|  | ||||
| void RepeatButton::setInterval(int interval) | ||||
| { | ||||
|     ticksInterval = interval; | ||||
| } | ||||
|  | ||||
| int RepeatButton::getInterval() | ||||
| { | ||||
|     return ticksInterval; | ||||
| } | ||||
|  | ||||
| void RepeatButton::handleClickEvent(const ClickEvent& event) | ||||
| { | ||||
|     pressed = false; // To prevent AbstractButton from calling action->execute(). | ||||
|     invalidate();    // Force redraw after forced state change | ||||
|     Button::handleClickEvent(event); | ||||
|     if (event.getType() == ClickEvent::PRESSED) | ||||
|     { | ||||
|         executeAction(); | ||||
|  | ||||
|         ticks = 0; | ||||
|         ticksBeforeContinuous = ticksDelay; | ||||
|         Application::getInstance()->registerTimerWidget(this); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         Application::getInstance()->unregisterTimerWidget(this); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void RepeatButton::handleTickEvent() | ||||
| { | ||||
|     Button::handleTickEvent(); | ||||
|  | ||||
|     if (pressed) | ||||
|     { | ||||
|         if (ticks == ticksBeforeContinuous) | ||||
|         { | ||||
|             executeAction(); | ||||
|  | ||||
|             ticks = 0; | ||||
|             ticksBeforeContinuous = ticksInterval; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             ticks++; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,194 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/TextureMapTypes.hpp> | ||||
| #include <touchgfx/hal/HAL.hpp> | ||||
| #include <touchgfx/transforms/DisplayTransformation.hpp> | ||||
| #include <touchgfx/widgets/ScalableImage.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| ScalableImage::ScalableImage(const Bitmap& bitmap /*= Bitmap() */) | ||||
|     : Image(bitmap), | ||||
|       currentScalingAlgorithm(BILINEAR_INTERPOLATION) | ||||
| { | ||||
| } | ||||
|  | ||||
| void ScalableImage::setScalingAlgorithm(ScalingAlgorithm algorithm) | ||||
| { | ||||
|     currentScalingAlgorithm = algorithm; | ||||
| } | ||||
|  | ||||
| ScalableImage::ScalingAlgorithm ScalableImage::getScalingAlgorithm() | ||||
| { | ||||
|     return currentScalingAlgorithm; | ||||
| } | ||||
|  | ||||
| void ScalableImage::drawQuad(const Rect& invalidatedArea, uint16_t* fb, const float* triangleXs, const float* triangleYs, const float* triangleZs, const float* triangleUs, const float* triangleVs) const | ||||
| { | ||||
|     // Area to redraw. Relative to the scalableImage. | ||||
|     Rect dirtyArea = Rect(0, 0, getWidth(), getHeight()) & invalidatedArea; | ||||
|  | ||||
|     // Absolute position of the scalableImage. | ||||
|     Rect dirtyAreaAbsolute = dirtyArea; | ||||
|     translateRectToAbsolute(dirtyAreaAbsolute); | ||||
|  | ||||
|     Rect absoluteRect = getAbsoluteRect(); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(absoluteRect); | ||||
|  | ||||
|     // Transform rects to match framebuffer coordinates | ||||
|     // This is needed if the display is rotated compared to the framebuffer | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(dirtyArea, this->getRect()); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(dirtyAreaAbsolute); | ||||
|  | ||||
|     // Get a pointer to the bitmap data, return if no bitmap found | ||||
|     const uint16_t* textmap = (const uint16_t*)bitmap.getData(); | ||||
|     if (!textmap) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     float x0 = triangleXs[0]; | ||||
|     float x1 = triangleXs[1]; | ||||
|     float x2 = triangleXs[2]; | ||||
|     float x3 = triangleXs[3]; | ||||
|     float y0 = triangleYs[0]; //lint !e578 | ||||
|     float y1 = triangleYs[1]; //lint !e578 | ||||
|     float y2 = triangleYs[2]; | ||||
|     float y3 = triangleYs[3]; | ||||
|  | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(x0, y0, this->getRect()); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(x1, y1, this->getRect()); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(x2, y2, this->getRect()); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(x3, y3, this->getRect()); | ||||
|  | ||||
|     Point3D vertices[4]; | ||||
|     Point3D point0 = { floatToFixed28_4(x0), floatToFixed28_4(y0), (float)(triangleZs[0]), (float)(triangleUs[0]), (float)(triangleVs[0]) }; | ||||
|     Point3D point1 = { floatToFixed28_4(x1), floatToFixed28_4(y1), (float)(triangleZs[1]), (float)(triangleUs[1]), (float)(triangleVs[1]) }; | ||||
|     Point3D point2 = { floatToFixed28_4(x2), floatToFixed28_4(y2), (float)(triangleZs[2]), (float)(triangleUs[2]), (float)(triangleVs[2]) }; | ||||
|     Point3D point3 = { floatToFixed28_4(x3), floatToFixed28_4(y3), (float)(triangleZs[3]), (float)(triangleUs[3]), (float)(triangleVs[3]) }; | ||||
|  | ||||
|     vertices[0] = point0; | ||||
|     vertices[1] = point1; | ||||
|     vertices[2] = point2; | ||||
|     vertices[3] = point3; | ||||
|  | ||||
|     DrawingSurface dest = { fb, HAL::FRAME_BUFFER_WIDTH }; | ||||
|     TextureSurface src = { textmap, bitmap.getExtraData(), bitmap.getWidth(), bitmap.getHeight(), bitmap.getWidth() }; | ||||
|  | ||||
|     HAL::lcd().drawTextureMapQuad(dest, vertices, src, absoluteRect, dirtyAreaAbsolute, lookupRenderVariant(), alpha, 0xFFFF); | ||||
| } | ||||
|  | ||||
| RenderingVariant ScalableImage::lookupRenderVariant() const | ||||
| { | ||||
|     RenderingVariant renderVariant; | ||||
|     if (currentScalingAlgorithm == NEAREST_NEIGHBOR) | ||||
|     { | ||||
|         renderVariant = lookupNearestNeighborRenderVariant(bitmap); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         renderVariant = lookupBilinearRenderVariant(bitmap); | ||||
|     } | ||||
|     return renderVariant; | ||||
| } | ||||
|  | ||||
| void ScalableImage::draw(const Rect& invalidatedArea) const | ||||
| { | ||||
|     if (!alpha) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|     uint16_t* fb = 0; | ||||
|  | ||||
|     float triangleXs[4]; | ||||
|     float triangleYs[4]; | ||||
|     float triangleZs[4]; | ||||
|     float triangleUs[4]; | ||||
|     float triangleVs[4]; | ||||
|  | ||||
|     float imageX0 = 0; | ||||
|     float imageY0 = 0; | ||||
|     float imageX1 = imageX0 + getWidth(); | ||||
|     float imageY1 = imageY0; | ||||
|     float imageX2 = imageX1; | ||||
|     float imageY2 = imageY0 + getHeight(); | ||||
|     float imageX3 = imageX0; | ||||
|     float imageY3 = imageY2; | ||||
|  | ||||
|     triangleZs[0] = 100.f; | ||||
|     triangleZs[1] = 100.f; | ||||
|     triangleZs[2] = 100.f; | ||||
|     triangleZs[3] = 100.f; | ||||
|  | ||||
|     // Setup texture coordinates | ||||
|     float right = (float)(bitmap.getWidth()); | ||||
|     float bottom = (float)(bitmap.getHeight()); | ||||
|     float textureU0 = 0.0f; | ||||
|     float textureV0 = 0.0f; | ||||
|     float textureU1 = right; | ||||
|     float textureV1 = 0.0f; | ||||
|     float textureU2 = right; | ||||
|     float textureV2 = bottom; | ||||
|     float textureU3 = 0.0f; | ||||
|     float textureV3 = bottom; | ||||
|     if (HAL::DISPLAY_ROTATION == rotate90) | ||||
|     { | ||||
|         textureU0 = 0.0f; | ||||
|         textureV0 = right; | ||||
|         textureU1 = 0.0f; | ||||
|         textureV1 = 0.0f; | ||||
|         textureU2 = bottom; | ||||
|         textureV2 = 0.0f; | ||||
|         textureU3 = bottom; | ||||
|         textureV3 = right; | ||||
|     } | ||||
|  | ||||
|     triangleXs[0] = imageX0; | ||||
|     triangleXs[1] = imageX1; | ||||
|     triangleXs[2] = imageX2; | ||||
|     triangleXs[3] = imageX3; | ||||
|     triangleYs[0] = imageY0; | ||||
|     triangleYs[1] = imageY1; | ||||
|     triangleYs[2] = imageY2; | ||||
|     triangleYs[3] = imageY3; | ||||
|  | ||||
|     triangleUs[0] = textureU0; | ||||
|     triangleUs[1] = textureU1; | ||||
|     triangleUs[2] = textureU2; | ||||
|     triangleUs[3] = textureU3; | ||||
|     triangleVs[0] = textureV0; | ||||
|     triangleVs[1] = textureV1; | ||||
|     triangleVs[2] = textureV2; | ||||
|     triangleVs[3] = textureV3; | ||||
|  | ||||
|     drawQuad(invalidatedArea, fb, triangleXs, triangleYs, triangleZs, triangleUs, triangleVs); | ||||
| } | ||||
|  | ||||
| Rect ScalableImage::getSolidRect() const | ||||
| { | ||||
|     if (alpha < 255) | ||||
|     { | ||||
|         return Rect(0, 0, 0, 0); | ||||
|     } | ||||
|  | ||||
|     // If original image is completely solid the scaled image will also be | ||||
|     if ((bitmap.getSolidRect().width == bitmap.getWidth()) && (bitmap.getSolidRect().height == bitmap.getHeight())) | ||||
|     { | ||||
|         return bitmap.getSolidRect(); | ||||
|     } | ||||
|     return Rect(0, 0, 0, 0); | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,62 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/SnapshotWidget.hpp> | ||||
| #include <touchgfx/hal/HAL.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| SnapshotWidget::SnapshotWidget() | ||||
|     : Widget(), bitmapId(BITMAP_INVALID), alpha(255) | ||||
| { | ||||
| } | ||||
|  | ||||
| void SnapshotWidget::draw(const Rect& invalidatedArea) const | ||||
| { | ||||
|     if (alpha == 0 || bitmapId == BITMAP_INVALID) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Rect absRect(0, 0, Bitmap(bitmapId).getWidth(), rect.height); | ||||
|     translateRectToAbsolute(absRect); | ||||
|     HAL::lcd().blitCopy((const uint16_t*)Bitmap(bitmapId).getData(), absRect, invalidatedArea, alpha, false); | ||||
| } | ||||
|  | ||||
| Rect SnapshotWidget::getSolidRect() const | ||||
| { | ||||
|     if (alpha < 255 || bitmapId == BITMAP_INVALID) | ||||
|     { | ||||
|         return Rect(0, 0, 0, 0); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         return Rect(0, 0, getWidth(), getHeight()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void SnapshotWidget::makeSnapshot() | ||||
| { | ||||
|     makeSnapshot(BITMAP_ANIMATION_STORAGE); | ||||
| } | ||||
|  | ||||
| void SnapshotWidget::makeSnapshot(const BitmapId bmp) | ||||
| { | ||||
|     Rect visRect(0, 0, rect.width, rect.height); | ||||
|     getVisibleRect(visRect); | ||||
|     Rect absRect = getAbsoluteRect(); | ||||
|     bitmapId = (HAL::lcd().copyFrameBufferRegionToMemory(visRect, absRect, bmp)) ? bmp : BITMAP_INVALID; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,193 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/TextArea.hpp> | ||||
| #include <touchgfx/hal/HAL.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| int16_t TextArea::getTextHeight() | ||||
| { | ||||
|     return typedText.hasValidId() ? calculateTextHeight(typedText.getText(), 0, 0) : 0; | ||||
| } | ||||
|  | ||||
| uint16_t TextArea::getTextWidth() const | ||||
| { | ||||
|     return typedText.hasValidId() ? typedText.getFont()->getStringWidth(typedText.getTextDirection(), typedText.getText(), 0, 0) : 0; | ||||
| } | ||||
|  | ||||
| void TextArea::draw(const Rect& area) const | ||||
| { | ||||
|     if (typedText.hasValidId()) | ||||
|     { | ||||
|         const Font* fontToDraw = typedText.getFont(); | ||||
|         if (fontToDraw != 0) | ||||
|         { | ||||
|             LCD::StringVisuals visuals(fontToDraw, color, alpha, typedText.getAlignment(), linespace, rotation, typedText.getTextDirection(), indentation, wideTextAction); | ||||
|             HAL::lcd().drawString(getAbsoluteRect(), area, visuals, typedText.getText(), 0, 0); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void TextArea::setTypedText(TypedText t) | ||||
| { | ||||
|     typedText = t; | ||||
|     // If this TextArea does not yet have a width and height, | ||||
|     // just assign the smallest possible size to fit current text. | ||||
|     if (getWidth() == 0 && getHeight() == 0) | ||||
|     { | ||||
|         resizeToCurrentText(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void TextArea::resizeToCurrentText() | ||||
| { | ||||
|     if (typedText.hasValidId()) | ||||
|     { | ||||
|         uint16_t w = getTextWidth(); | ||||
|         uint16_t h = getTextHeight(); | ||||
|         if (rotation == TEXT_ROTATE_0 || rotation == TEXT_ROTATE_180) | ||||
|         { | ||||
|             setWidthHeight(w, h); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             setWidthHeight(h, w); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void TextArea::resizeToCurrentTextWithAlignment() | ||||
| { | ||||
|     if (typedText.hasValidId()) | ||||
|     { | ||||
|         Alignment alignment = typedText.getAlignment(); | ||||
|         uint16_t text_width = getTextWidth(); | ||||
|         uint16_t text_height = getTextHeight(); | ||||
|         if (rotation == TEXT_ROTATE_0 || rotation == TEXT_ROTATE_180) | ||||
|         { | ||||
|             // (rotate-0 && left-align) or (rotate-180 && right-align) places text to the left | ||||
|             if (!((rotation == TEXT_ROTATE_0 && alignment == LEFT) || (rotation == TEXT_ROTATE_180 && alignment == RIGHT))) | ||||
|             { | ||||
|                 uint16_t old_width = getWidth(); | ||||
|                 uint16_t old_x = getX(); | ||||
|                 if (alignment == CENTER) | ||||
|                 { | ||||
|                     setX(old_x + (old_width - text_width) / 2); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     setX(old_x + (old_width - text_width)); | ||||
|                 } | ||||
|             } | ||||
|             if (rotation == TEXT_ROTATE_180) | ||||
|             { | ||||
|                 uint16_t old_height = getHeight(); | ||||
|                 uint16_t old_y = getY(); | ||||
|                 setY(old_y + (old_height - text_height)); | ||||
|             } | ||||
|             setWidthHeight(text_width, text_height); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // 90+left or 270+right places text at the same y coordinate | ||||
|             if (!((rotation == TEXT_ROTATE_90 && alignment == LEFT) || (rotation == TEXT_ROTATE_270 && alignment == RIGHT))) | ||||
|             { | ||||
|                 uint16_t old_height = getHeight(); | ||||
|                 uint16_t old_y = getY(); | ||||
|                 if (alignment == CENTER) | ||||
|                 { | ||||
|                     setY(old_y + (old_height - text_width) / 2); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     setY(old_y + (old_height - text_width)); | ||||
|                 } | ||||
|             } | ||||
|             if (rotation == TEXT_ROTATE_90) | ||||
|             { | ||||
|                 uint16_t old_width = getWidth(); | ||||
|                 uint16_t old_x = getX(); | ||||
|                 setX(old_x + (old_width - text_height)); | ||||
|             } | ||||
|             setWidthHeight(text_height, text_width); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void TextArea::resizeHeightToCurrentText() | ||||
| { | ||||
|     if (typedText.hasValidId()) | ||||
|     { | ||||
|         uint16_t h = getTextHeight(); | ||||
|         if (rotation == TEXT_ROTATE_0 || rotation == TEXT_ROTATE_180) | ||||
|         { | ||||
|             setHeight(h); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             setWidth(h); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void TextArea::resizeHeightToCurrentTextWithRotation() | ||||
| { | ||||
|     if (typedText.hasValidId()) | ||||
|     { | ||||
|         uint16_t h = getTextHeight(); | ||||
|         switch (rotation) | ||||
|         { | ||||
|         default: | ||||
|         case TEXT_ROTATE_0: | ||||
|             setHeight(h); | ||||
|             break; | ||||
|         case TEXT_ROTATE_90: | ||||
|             setX(rect.right() - h); | ||||
|             setWidth(h); | ||||
|             break; | ||||
|         case TEXT_ROTATE_180: | ||||
|             setY(rect.bottom() - h); | ||||
|             setHeight(h); | ||||
|             break; | ||||
|         case TEXT_ROTATE_270: | ||||
|             setWidth(h); | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| int16_t TextArea::calculateTextHeight(const Unicode::UnicodeChar* format, ...) const | ||||
| { | ||||
|     if (!typedText.hasValidId()) | ||||
|     { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     va_list pArg; | ||||
|     va_start(pArg, format); | ||||
|  | ||||
|     const Font* fontToDraw = typedText.getFont(); | ||||
|     int16_t textHeight = fontToDraw->getMinimumTextHeight(); | ||||
|  | ||||
|     TextProvider textProvider; | ||||
|     textProvider.initialize(format, pArg, fontToDraw->getGSUBTable()); | ||||
|  | ||||
|     int16_t numLines = LCD::getNumLines(textProvider, wideTextAction, typedText.getTextDirection(), typedText.getFont(), getWidth()); | ||||
|  | ||||
|     va_end(pArg); | ||||
|     return (textHeight + linespace > 0) ? (numLines * textHeight + (numLines - 1) * linespace) : (numLines > 0) ? (textHeight) : 0; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,46 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/TextAreaWithWildcard.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void TextAreaWithOneWildcard::draw(const Rect& area) const | ||||
| { | ||||
|     if (typedText.hasValidId()) | ||||
|     { | ||||
|         const Font* fontToDraw = typedText.getFont(); | ||||
|         if (fontToDraw != 0) | ||||
|         { | ||||
|             LCD::StringVisuals visuals(fontToDraw, color, alpha, typedText.getAlignment(), linespace, rotation, typedText.getTextDirection(), indentation, wideTextAction); | ||||
|             HAL::lcd().drawString(getAbsoluteRect(), area, visuals, typedText.getText(), wildcard, 0); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void TextAreaWithTwoWildcards::draw(const Rect& area) const | ||||
| { | ||||
|     if (typedText.hasValidId()) | ||||
|     { | ||||
|         const Font* fontToDraw = typedText.getFont(); | ||||
|         if (fontToDraw != 0) | ||||
|         { | ||||
|             LCD::StringVisuals visuals(fontToDraw, color, alpha, typedText.getAlignment(), linespace, rotation, typedText.getTextDirection(), indentation, wideTextAction); | ||||
|             HAL::lcd().drawString(getAbsoluteRect(), area, visuals, typedText.getText(), wc1, wc2); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,350 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/Math3D.hpp> | ||||
| #include <touchgfx/TextureMapTypes.hpp> | ||||
| #include <touchgfx/hal/HAL.hpp> | ||||
| #include <touchgfx/transforms/DisplayTransformation.hpp> | ||||
| #include <touchgfx/widgets/TextureMapper.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| TextureMapper::TextureMapper(const Bitmap& bitmap /*= Bitmap()*/) | ||||
|     : Image(bitmap), | ||||
|       currentRenderingAlgorithm(NEAREST_NEIGHBOR), | ||||
|       xBitmapPosition(0.0f), | ||||
|       yBitmapPosition(0.0f), | ||||
|       xAngle(0.0f), | ||||
|       yAngle(0.0f), | ||||
|       zAngle(0.0f), | ||||
|       scale(1.0f), | ||||
|       xOrigo(0.0f), | ||||
|       yOrigo(0.0f), | ||||
|       zOrigo(1000.0f), | ||||
|       xCamera(0.0f), | ||||
|       yCamera(0.0f), | ||||
|       cameraDistance(1000.0f), | ||||
|       imageX0(0.0f), | ||||
|       imageY0(0.0f), | ||||
|       imageZ0(1.0f), | ||||
|       imageX1(0.0f), | ||||
|       imageY1(0.0f), | ||||
|       imageZ1(1.0f), | ||||
|       imageX2(0.0f), | ||||
|       imageY2(0.0f), | ||||
|       imageZ2(1.0f), | ||||
|       imageX3(0.0f), | ||||
|       imageY3(0.0f), | ||||
|       imageZ3(1.0f), | ||||
|       subDivisionSize(12) | ||||
| { | ||||
| } | ||||
|  | ||||
| void TextureMapper::setBitmap(const Bitmap& bitmap) | ||||
| { | ||||
|     Image::setBitmap(bitmap); | ||||
|     applyTransformation(); | ||||
| } | ||||
|  | ||||
| void TextureMapper::applyTransformation() | ||||
| { | ||||
|     const uint8_t n = 4; | ||||
|  | ||||
|     int imgWidth = Bitmap(bitmap).getWidth() + 1; | ||||
|     int imgHeight = Bitmap(bitmap).getHeight() + 1; | ||||
|  | ||||
|     Point4 vertices[n] = | ||||
|     { | ||||
|         Point4(xBitmapPosition - 1, yBitmapPosition - 1, cameraDistance), | ||||
|         Point4(xBitmapPosition - 1 + imgWidth, yBitmapPosition - 1, cameraDistance), | ||||
|         Point4(xBitmapPosition - 1 + imgWidth, yBitmapPosition - 1 + imgHeight, cameraDistance), | ||||
|         Point4(xBitmapPosition - 1, yBitmapPosition - 1 + imgHeight, cameraDistance), | ||||
|     }; | ||||
|     Point4 transformed[n]; | ||||
|  | ||||
|     Vector4 center(xOrigo, yOrigo, zOrigo); | ||||
|  | ||||
|     Matrix4x4 translateToCenter; | ||||
|     translateToCenter.concatenateXTranslation(-center.getX()).concatenateYTranslation(-center.getY()).concatenateZTranslation(-center.getZ()); | ||||
|  | ||||
|     Matrix4x4 rotateAroundCenter; | ||||
|     rotateAroundCenter.concatenateXRotation(xAngle).concatenateYRotation(yAngle).concatenateZRotation(zAngle); | ||||
|  | ||||
|     Matrix4x4 scaleAroundCenter; | ||||
|     scaleAroundCenter.concatenateXScale(scale).concatenateYScale(scale).concatenateZScale(scale); | ||||
|  | ||||
|     Matrix4x4 translateFromCenter; | ||||
|     translateFromCenter.concatenateXTranslation(center.getX()).concatenateYTranslation(center.getY()).concatenateZTranslation(center.getZ()); | ||||
|  | ||||
|     Matrix4x4 transform = translateFromCenter * scaleAroundCenter * rotateAroundCenter * translateToCenter; | ||||
|  | ||||
|     Matrix4x4 translateToCamera; | ||||
|     translateToCamera.concatenateXTranslation(-xCamera); | ||||
|     translateToCamera.concatenateYTranslation(-yCamera); | ||||
|  | ||||
|     Matrix4x4 perspectiveProject; | ||||
|     perspectiveProject.setViewDistance(cameraDistance); | ||||
|  | ||||
|     Matrix4x4 translateFromCamera; | ||||
|     translateFromCamera.concatenateXTranslation(xCamera).concatenateYTranslation(yCamera); | ||||
|  | ||||
|     transform = translateFromCamera * perspectiveProject * translateToCamera * transform; | ||||
|  | ||||
|     for (int i = 0; i < n; i++) | ||||
|     { | ||||
|         transformed[i] = transform * vertices[i]; | ||||
|     } | ||||
|  | ||||
|     imageX0 = ((float)transformed[0].getX() * cameraDistance / (float)transformed[0].getZ()); | ||||
|     imageY0 = ((float)transformed[0].getY() * cameraDistance / (float)transformed[0].getZ()); | ||||
|     imageZ0 = ((float)transformed[0].getZ()); | ||||
|  | ||||
|     imageX1 = ((float)transformed[1].getX() * cameraDistance / (float)transformed[1].getZ()); | ||||
|     imageY1 = ((float)transformed[1].getY() * cameraDistance / (float)transformed[1].getZ()); | ||||
|     imageZ1 = ((float)transformed[1].getZ()); | ||||
|  | ||||
|     imageX2 = ((float)transformed[2].getX() * cameraDistance / (float)transformed[2].getZ()); | ||||
|     imageY2 = ((float)transformed[2].getY() * cameraDistance / (float)transformed[2].getZ()); | ||||
|     imageZ2 = ((float)transformed[2].getZ()); | ||||
|  | ||||
|     imageX3 = ((float)transformed[3].getX() * cameraDistance / (float)transformed[3].getZ()); | ||||
|     imageY3 = ((float)transformed[3].getY() * cameraDistance / (float)transformed[3].getZ()); | ||||
|     imageZ3 = ((float)transformed[3].getZ()); | ||||
| } | ||||
|  | ||||
| Rect TextureMapper::getBoundingRect() const | ||||
| { | ||||
|     // MIN and MAX are macros so do not insert them into each other | ||||
|     float minXf = MIN(imageX0, imageX1); | ||||
|     minXf = MIN(minXf, imageX2); | ||||
|     minXf = floorf(MIN(minXf, imageX3)); | ||||
|     int16_t minX = (int16_t)(MAX(0, minXf)); | ||||
|  | ||||
|     float maxXf = MAX(imageX0, imageX1); | ||||
|     maxXf = MAX(maxXf, imageX2); | ||||
|     maxXf = ceilf(MAX(maxXf, imageX3)); | ||||
|     int16_t maxX = getWidth(); | ||||
|     maxX = (int16_t)(MIN(maxX, maxXf)); | ||||
|  | ||||
|     float minYf = MIN(imageY0, imageY1); | ||||
|     minYf = MIN(minYf, imageY2); | ||||
|     minYf = floorf(MIN(minYf, imageY3)); | ||||
|     int16_t minY = (int16_t)(MAX(0, minYf)); | ||||
|  | ||||
|     float maxYf = MAX(imageY0, imageY1); | ||||
|     maxYf = MAX(maxYf, imageY2); | ||||
|     maxYf = ceilf(MAX(maxYf, imageY3)); | ||||
|     int16_t maxY = getHeight(); | ||||
|     maxY = (int16_t)(MIN(maxY, maxYf)); | ||||
|  | ||||
|     return Rect(minX, minY, maxX - minX, maxY - minY); | ||||
| } | ||||
|  | ||||
| void TextureMapper::updateAngles(float newXAngle, float newYAngle, float newZAngle) | ||||
| { | ||||
|     Rect rBefore = getBoundingRect(); | ||||
|  | ||||
|     xAngle = newXAngle; | ||||
|     yAngle = newYAngle; | ||||
|     zAngle = newZAngle; | ||||
|  | ||||
|     applyTransformation(); | ||||
|  | ||||
|     Rect rAfter = getBoundingRect(); | ||||
|     rAfter.expandToFit(rBefore); | ||||
|     invalidateRect(rAfter); | ||||
| } | ||||
|  | ||||
| void TextureMapper::setScale(float _scale) | ||||
| { | ||||
|     this->scale = _scale; | ||||
|  | ||||
|     applyTransformation(); | ||||
| } | ||||
|  | ||||
| void TextureMapper::draw(const Rect& invalidatedArea) const | ||||
| { | ||||
|     if (!alpha) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|     uint16_t* fb = 0; | ||||
|  | ||||
|     // Setup texture coordinates | ||||
|     float right = (float)(bitmap.getWidth()); | ||||
|     float bottom = (float)(bitmap.getHeight()); | ||||
|     float textureU0 = -1.0f; | ||||
|     float textureV0 = -1.0f; | ||||
|     float textureU1 = right; | ||||
|     float textureV1 = -1.0f; | ||||
|     float textureU2 = right; | ||||
|     float textureV2 = bottom; | ||||
|     float textureU3 = -1.0f; | ||||
|     float textureV3 = bottom; | ||||
|     if (HAL::DISPLAY_ROTATION == rotate90) | ||||
|     { | ||||
|         textureU0 = -1.0f; | ||||
|         textureV0 = right; | ||||
|         textureU1 = -1.0f; | ||||
|         textureV1 = -1.0f; | ||||
|         textureU2 = bottom; | ||||
|         textureV2 = -1.0f; | ||||
|         textureU3 = bottom; | ||||
|         textureV3 = right; | ||||
|     } | ||||
|  | ||||
|     float triangleXs[4]; | ||||
|     float triangleYs[4]; | ||||
|     float triangleZs[4]; | ||||
|     float triangleUs[4]; | ||||
|     float triangleVs[4]; | ||||
|  | ||||
|     // Determine winding order | ||||
|     Vector4 zeroToOne(imageX1 - imageX0, imageY1 - imageY0, imageZ1 - imageZ0); | ||||
|     Vector4 zeroToTwo(imageX2 - imageX0, imageY2 - imageY0, imageZ2 - imageZ0); | ||||
|     Vector4 normal = zeroToOne.crossProduct(zeroToTwo); | ||||
|  | ||||
|     if (normal.getZ() > 0) | ||||
|     { | ||||
|         triangleXs[0] = imageX0; | ||||
|         triangleXs[1] = imageX1; | ||||
|         triangleXs[2] = imageX2; | ||||
|         triangleXs[3] = imageX3; | ||||
|         triangleYs[0] = imageY0; | ||||
|         triangleYs[1] = imageY1; | ||||
|         triangleYs[2] = imageY2; | ||||
|         triangleYs[3] = imageY3; | ||||
|         triangleZs[0] = imageZ0; | ||||
|         triangleZs[1] = imageZ1; | ||||
|         triangleZs[2] = imageZ2; | ||||
|         triangleZs[3] = imageZ3; | ||||
|  | ||||
|         triangleUs[0] = textureU0; | ||||
|         triangleUs[1] = textureU1; | ||||
|         triangleUs[2] = textureU2; | ||||
|         triangleUs[3] = textureU3; | ||||
|         triangleVs[0] = textureV0; | ||||
|         triangleVs[1] = textureV1; | ||||
|         triangleVs[2] = textureV2; | ||||
|         triangleVs[3] = textureV3; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // invert due to the triangles winding order (showing backface of the triangle) | ||||
|         triangleXs[1] = imageX0; | ||||
|         triangleXs[0] = imageX1; | ||||
|         triangleXs[2] = imageX3; | ||||
|         triangleXs[3] = imageX2; | ||||
|         triangleYs[1] = imageY0; | ||||
|         triangleYs[0] = imageY1; | ||||
|         triangleYs[2] = imageY3; | ||||
|         triangleYs[3] = imageY2; | ||||
|         triangleZs[1] = imageZ0; | ||||
|         triangleZs[0] = imageZ1; | ||||
|         triangleZs[2] = imageZ3; | ||||
|         triangleZs[3] = imageZ2; | ||||
|  | ||||
|         triangleUs[1] = textureU0; | ||||
|         triangleUs[0] = textureU1; | ||||
|         triangleUs[2] = textureU3; | ||||
|         triangleUs[3] = textureU2; | ||||
|         triangleVs[1] = textureV0; | ||||
|         triangleVs[0] = textureV1; | ||||
|         triangleVs[2] = textureV3; | ||||
|         triangleVs[3] = textureV2; | ||||
|     } | ||||
|  | ||||
|     drawQuad(invalidatedArea, fb, triangleXs, triangleYs, triangleZs, triangleUs, triangleVs); | ||||
| } | ||||
|  | ||||
| void TextureMapper::drawQuad(const Rect& invalidatedArea, uint16_t* fb, const float* triangleXs, const float* triangleYs, const float* triangleZs, const float* triangleUs, const float* triangleVs) const | ||||
| { | ||||
|     // Area to redraw. Relative to the TextureMapper. | ||||
|     Rect dirtyArea = Rect(0, 0, getWidth(), getHeight()) & invalidatedArea; | ||||
|  | ||||
|     // Absolute position of the TextureMapper. | ||||
|     Rect dirtyAreaAbsolute = dirtyArea; | ||||
|     translateRectToAbsolute(dirtyAreaAbsolute); | ||||
|  | ||||
|     Rect absoluteRect = getAbsoluteRect(); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(absoluteRect); | ||||
|  | ||||
|     // Transform rects to match framebuffer coordinates | ||||
|     // This is needed if the display is rotated compared to the framebuffer | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(dirtyArea, this->getRect()); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(dirtyAreaAbsolute); | ||||
|  | ||||
|     // Get a pointer to the bitmap data, return if no bitmap found | ||||
|     const uint16_t* textmap = (const uint16_t*)bitmap.getData(); | ||||
|     if (!textmap) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     float x0 = triangleXs[0]; | ||||
|     float x1 = triangleXs[1]; | ||||
|     float x2 = triangleXs[2]; | ||||
|     float x3 = triangleXs[3]; | ||||
|     float y0 = triangleYs[0]; //lint !e578 | ||||
|     float y1 = triangleYs[1]; //lint !e578 | ||||
|     float y2 = triangleYs[2]; | ||||
|     float y3 = triangleYs[3]; | ||||
|  | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(x0, y0, this->getRect()); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(x1, y1, this->getRect()); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(x2, y2, this->getRect()); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(x3, y3, this->getRect()); | ||||
|  | ||||
|     Point3D vertices[4]; | ||||
|     Point3D point0 = { floatToFixed28_4(x0), floatToFixed28_4(y0), (float)(triangleZs[0]), (float)(triangleUs[0]), (float)(triangleVs[0]) }; | ||||
|     Point3D point1 = { floatToFixed28_4(x1), floatToFixed28_4(y1), (float)(triangleZs[1]), (float)(triangleUs[1]), (float)(triangleVs[1]) }; | ||||
|     Point3D point2 = { floatToFixed28_4(x2), floatToFixed28_4(y2), (float)(triangleZs[2]), (float)(triangleUs[2]), (float)(triangleVs[2]) }; | ||||
|     Point3D point3 = { floatToFixed28_4(x3), floatToFixed28_4(y3), (float)(triangleZs[3]), (float)(triangleUs[3]), (float)(triangleVs[3]) }; | ||||
|  | ||||
|     vertices[0] = point0; | ||||
|     vertices[1] = point1; | ||||
|     vertices[2] = point2; | ||||
|     vertices[3] = point3; | ||||
|  | ||||
|     DrawingSurface dest = { fb, HAL::FRAME_BUFFER_WIDTH }; | ||||
|     TextureSurface src = { textmap, bitmap.getExtraData(), bitmap.getWidth(), bitmap.getHeight(), bitmap.getWidth() }; | ||||
|  | ||||
|     uint16_t subDivs = subDivisionSize; | ||||
|     if (point0.Z == point1.Z && point1.Z == point2.Z) | ||||
|     { | ||||
|         subDivs = 0xFFFF; // Max: One sweep | ||||
|     } | ||||
|     HAL::lcd().drawTextureMapQuad(dest, vertices, src, absoluteRect, dirtyAreaAbsolute, lookupRenderVariant(), alpha, subDivs); | ||||
| } | ||||
|  | ||||
| RenderingVariant TextureMapper::lookupRenderVariant() const | ||||
| { | ||||
|     RenderingVariant renderVariant; | ||||
|     if (currentRenderingAlgorithm == NEAREST_NEIGHBOR) | ||||
|     { | ||||
|         renderVariant = lookupNearestNeighborRenderVariant(bitmap); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         renderVariant = lookupBilinearRenderVariant(bitmap); | ||||
|     } | ||||
|     return renderVariant; | ||||
| } | ||||
|  | ||||
| Rect TextureMapper::getSolidRect() const | ||||
| { | ||||
|     return Rect(0, 0, 0, 0); | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,155 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/TiledImage.hpp> | ||||
| #include <touchgfx/hal/HAL.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void TiledImage::setBitmap(const Bitmap& bitmap) | ||||
| { | ||||
|     Image::setBitmap(bitmap); | ||||
|     // Make sure the xOffset and yOffset are correct (in range) | ||||
|     setOffset(xOffset, yOffset); | ||||
| } | ||||
|  | ||||
| void TiledImage::setOffset(int16_t x, int16_t y) | ||||
| { | ||||
|     setXOffset(x); | ||||
|     setYOffset(y); | ||||
| } | ||||
|  | ||||
| void TiledImage::setXOffset(int16_t x) | ||||
| { | ||||
|     xOffset = x; | ||||
|     if (bitmap.getWidth() != 0) | ||||
|     { | ||||
|         xOffset = ((xOffset % bitmap.getWidth()) + bitmap.getWidth()) % bitmap.getWidth(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void TiledImage::setYOffset(int16_t y) | ||||
| { | ||||
|     yOffset = y; | ||||
|     if (bitmap.getHeight() != 0) | ||||
|     { | ||||
|         yOffset = ((yOffset % bitmap.getHeight()) + bitmap.getHeight()) % bitmap.getHeight(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void TiledImage::getOffset(int16_t& x, int16_t& y) | ||||
| { | ||||
|     x = getXOffset(); | ||||
|     y = getYOffset(); | ||||
| } | ||||
|  | ||||
| int16_t TiledImage::getXOffset() | ||||
| { | ||||
|     return xOffset; | ||||
| } | ||||
|  | ||||
| int16_t TiledImage::getYOffset() | ||||
| { | ||||
|     return yOffset; | ||||
| } | ||||
|  | ||||
| void TiledImage::draw(const Rect& invalidatedArea) const | ||||
| { | ||||
|     uint16_t bitmapWidth = bitmap.getWidth(); | ||||
|     uint16_t bitmapHeight = bitmap.getHeight(); | ||||
|  | ||||
|     if (bitmapWidth == 0 || bitmapHeight == 0) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Rect meAbs; | ||||
|     translateRectToAbsolute(meAbs); | ||||
|  | ||||
|     const int16_t minX = ((invalidatedArea.x + xOffset) / bitmapWidth) * bitmapWidth - xOffset; | ||||
|     const int16_t maxX = (((invalidatedArea.right() + xOffset) - 1) / bitmapWidth) * bitmapWidth; | ||||
|     const int16_t minY = ((invalidatedArea.y + yOffset) / bitmapHeight) * bitmapHeight - yOffset; | ||||
|     const int16_t maxY = (((invalidatedArea.bottom() + yOffset) - 1) / bitmapHeight) * bitmapHeight; | ||||
|     for (int16_t x = minX; x <= maxX; x += bitmapWidth) | ||||
|     { | ||||
|         for (int16_t y = minY; y <= maxY; y += bitmapHeight) | ||||
|         { | ||||
|             Rect dirty = Rect(x, y, bitmapWidth, bitmapHeight) & invalidatedArea; | ||||
|             dirty.x -= x; | ||||
|             dirty.y -= y; | ||||
|             HAL::lcd().drawPartialBitmap(bitmap, meAbs.x + x, meAbs.y + y, dirty, alpha); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| Rect TiledImage::getSolidRect() const | ||||
| { | ||||
|     if (alpha < 255) | ||||
|     { | ||||
|         return Rect(0, 0, 0, 0); | ||||
|     } | ||||
|  | ||||
|     Rect solidRect = bitmap.getSolidRect(); | ||||
|     if (solidRect.width == bitmap.getWidth()) | ||||
|     { | ||||
|         solidRect.width = getWidth(); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         solidRect.x -= xOffset; | ||||
|         Rect solidRect2 = solidRect; | ||||
|         solidRect2.x += bitmap.getWidth(); | ||||
|         if (solidRect.x < 0) | ||||
|         { | ||||
|             int16_t right = solidRect.right(); | ||||
|             solidRect.width = MAX(right, 0); | ||||
|             solidRect.x = 0; | ||||
|         } | ||||
|         if (solidRect2.right() > getWidth()) | ||||
|         { | ||||
|             solidRect2.width = solidRect2.right() - getWidth(); | ||||
|         } | ||||
|         if (solidRect2.width > solidRect.width) | ||||
|         { | ||||
|             solidRect = solidRect2; | ||||
|         } | ||||
|     } | ||||
|     if (solidRect.height == bitmap.getHeight()) | ||||
|     { | ||||
|         solidRect.height = getHeight(); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         solidRect.y -= yOffset; | ||||
|         Rect solidRect2 = solidRect; | ||||
|         solidRect2.y += bitmap.getHeight(); | ||||
|         if (solidRect.y < 0) | ||||
|         { | ||||
|             int16_t bottom = solidRect.bottom(); | ||||
|             solidRect.height = MAX(bottom, 0); | ||||
|             solidRect.y = 0; | ||||
|         } | ||||
|         if (solidRect2.bottom() > getHeight()) | ||||
|         { | ||||
|             solidRect2.height = solidRect2.bottom() - getHeight(); | ||||
|         } | ||||
|         if (solidRect2.height > solidRect.height) | ||||
|         { | ||||
|             solidRect = solidRect2; | ||||
|         } | ||||
|     } | ||||
|     return solidRect; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,52 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/ToggleButton.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void ToggleButton::forceState(bool activeState) | ||||
| { | ||||
|     if (activeState) | ||||
|     { | ||||
|         // up should equal originalPressed | ||||
|         if (up.getId() != originalPressed.getId()) | ||||
|         { | ||||
|             down = up; | ||||
|             up = originalPressed; | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // down should equal originalPressed | ||||
|         if (down.getId() != originalPressed.getId()) | ||||
|         { | ||||
|             up = down; | ||||
|             down = originalPressed; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ToggleButton::handleClickEvent(const ClickEvent& event) | ||||
| { | ||||
|     if (pressed && (event.getType() == ClickEvent::RELEASED)) | ||||
|     { | ||||
|         Bitmap tmp = up; | ||||
|         up = down; | ||||
|         down = tmp; | ||||
|     } | ||||
|     Button::handleClickEvent(event); | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,28 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/TouchArea.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void TouchArea::handleClickEvent(const ClickEvent& event) | ||||
| { | ||||
|     AbstractButton::handleClickEvent(event); | ||||
|     if (pressedAction && pressedAction->isValid() && pressed) | ||||
|     { | ||||
|         pressedAction->execute(*this); | ||||
|     } | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,69 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/Color.hpp> | ||||
| #include <touchgfx/widgets/canvas/AbstractPainterABGR2222.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void AbstractPainterABGR2222::render(uint8_t* ptr, | ||||
|                                      int x, | ||||
|                                      int xAdjust, | ||||
|                                      int y, | ||||
|                                      unsigned count, | ||||
|                                      const uint8_t* covers) | ||||
| { | ||||
|     uint8_t* p = ptr + (x + xAdjust); | ||||
|  | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|     if (renderInit()) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             uint8_t red, green, blue, alpha; | ||||
|             if (renderNext(red, green, blue, alpha)) | ||||
|             { | ||||
|                 const uint8_t combinedAlpha = LCD::div255((*covers) * LCD::div255(alpha * widgetAlpha)); | ||||
|  | ||||
|                 if (combinedAlpha == 0xFF) // max alpha=0xFF on "*covers" and max alpha=0xFF on "widgetAlpha" | ||||
|                 { | ||||
|                     // Render a solid pixel | ||||
|                     renderPixel(p, red, green, blue); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     const uint8_t ialpha = 0xFF - combinedAlpha; | ||||
|                     const uint8_t p_red = LCD8bpp_ABGR2222::getRedFromColor(*p); | ||||
|                     const uint8_t p_green = LCD8bpp_ABGR2222::getGreenFromColor(*p); | ||||
|                     const uint8_t p_blue = LCD8bpp_ABGR2222::getBlueFromColor(*p); | ||||
|                     renderPixel(p, | ||||
|                                 LCD::div255(red * combinedAlpha + p_red * ialpha), | ||||
|                                 LCD::div255(green * combinedAlpha + p_green * ialpha), | ||||
|                                 LCD::div255(blue * combinedAlpha + p_blue * ialpha)); | ||||
|                 } | ||||
|             } | ||||
|             covers++; | ||||
|             p++; | ||||
|             currentX++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AbstractPainterABGR2222::renderPixel(uint8_t* p, uint8_t red, uint8_t green, uint8_t blue) | ||||
| { | ||||
|     *p = LCD8bpp_ABGR2222::getColorFromRGB(red, green, blue); | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,69 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/Color.hpp> | ||||
| #include <touchgfx/widgets/canvas/AbstractPainterARGB2222.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void AbstractPainterARGB2222::render(uint8_t* ptr, | ||||
|                                      int x, | ||||
|                                      int xAdjust, | ||||
|                                      int y, | ||||
|                                      unsigned count, | ||||
|                                      const uint8_t* covers) | ||||
| { | ||||
|     uint8_t* p = ptr + (x + xAdjust); | ||||
|  | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|     if (renderInit()) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             uint8_t red, green, blue, alpha; | ||||
|             if (renderNext(red, green, blue, alpha)) | ||||
|             { | ||||
|                 const uint8_t combinedAlpha = LCD::div255((*covers) * LCD::div255(alpha * widgetAlpha)); | ||||
|  | ||||
|                 if (combinedAlpha == 0xFF) // max alpha=255 on "*covers" and max alpha=255 on "widgetAlpha" | ||||
|                 { | ||||
|                     // Render a solid pixel | ||||
|                     renderPixel(p, red, green, blue); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     const uint8_t ialpha = 0xFF - combinedAlpha; | ||||
|                     const uint8_t p_red = LCD8bpp_ARGB2222::getRedFromColor(*p); | ||||
|                     const uint8_t p_green = LCD8bpp_ARGB2222::getGreenFromColor(*p); | ||||
|                     const uint8_t p_blue = LCD8bpp_ARGB2222::getBlueFromColor(*p); | ||||
|                     renderPixel(p, | ||||
|                                 LCD::div255(red * combinedAlpha + p_red * ialpha), | ||||
|                                 LCD::div255(green * combinedAlpha + p_green * ialpha), | ||||
|                                 LCD::div255(blue * combinedAlpha + p_blue * ialpha)); | ||||
|                 } | ||||
|             } | ||||
|             covers++; | ||||
|             p++; | ||||
|             currentX++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AbstractPainterARGB2222::renderPixel(uint8_t* p, uint8_t red, uint8_t green, uint8_t blue) | ||||
| { | ||||
|     *p = LCD8bpp_ARGB2222::getColorFromRGB(red, green, blue); | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,80 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/Color.hpp> | ||||
| #include <touchgfx/widgets/canvas/AbstractPainterARGB8888.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void AbstractPainterARGB8888::render(uint8_t* ptr, | ||||
|                                      int x, | ||||
|                                      int xAdjust, | ||||
|                                      int y, | ||||
|                                      unsigned count, | ||||
|                                      const uint8_t* covers) | ||||
| { | ||||
|     uint8_t* p = ptr + ((x + xAdjust) * 4); | ||||
|  | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|     if (renderInit()) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             uint8_t red, green, blue, alpha; | ||||
|             if (renderNext(red, green, blue, alpha)) | ||||
|             { | ||||
|                 const uint8_t combinedAlpha = LCD::div255((*covers) * LCD::div255(alpha * widgetAlpha)); | ||||
|  | ||||
|                 if (combinedAlpha == 0xFF) // max alpha=0xFF on "*covers" and max alpha=0xFF on "widgetAlpha" | ||||
|                 { | ||||
|                     // Render a solid pixel | ||||
|                     renderPixel(reinterpret_cast<uint16_t*>(p), red, green, blue); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     const uint8_t ialpha = 0xFF - combinedAlpha; | ||||
|                     const uint8_t p_blue = p[0]; | ||||
|                     const uint8_t p_green = p[1]; | ||||
|                     const uint8_t p_red = p[2]; | ||||
|                     const uint8_t p_alpha = p[3]; | ||||
|                     renderPixel(reinterpret_cast<uint16_t*>(p), | ||||
|                                 LCD::div255(red * combinedAlpha + p_red * ialpha), | ||||
|                                 LCD::div255(green * combinedAlpha + p_green * ialpha), | ||||
|                                 LCD::div255(blue * combinedAlpha + p_blue * ialpha), | ||||
|                                 p_alpha + combinedAlpha - LCD::div255(p_alpha * combinedAlpha)); | ||||
|                 } | ||||
|             } | ||||
|             covers++; | ||||
|             p += 4; | ||||
|             currentX++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AbstractPainterARGB8888::renderPixel(uint16_t* p, uint8_t red, uint8_t green, uint8_t blue) | ||||
| { | ||||
|     renderPixel(p, red, green, blue, 0xFF); | ||||
| } | ||||
|  | ||||
| void AbstractPainterARGB8888::renderPixel(uint16_t* p, uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) | ||||
| { | ||||
|     uint8_t* p8 = reinterpret_cast<uint8_t*>(p); | ||||
|     p8[0] = blue; | ||||
|     p8[1] = green; | ||||
|     p8[2] = red; | ||||
|     p8[3] = alpha; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,69 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/Color.hpp> | ||||
| #include <touchgfx/widgets/canvas/AbstractPainterBGRA2222.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void AbstractPainterBGRA2222::render(uint8_t* ptr, | ||||
|                                      int x, | ||||
|                                      int xAdjust, | ||||
|                                      int y, | ||||
|                                      unsigned count, | ||||
|                                      const uint8_t* covers) | ||||
| { | ||||
|     uint8_t* p = ptr + (x + xAdjust); | ||||
|  | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|     if (renderInit()) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             uint8_t red, green, blue, alpha; | ||||
|             if (renderNext(red, green, blue, alpha)) | ||||
|             { | ||||
|                 const uint8_t combinedAlpha = LCD::div255((*covers) * LCD::div255(alpha * widgetAlpha)); | ||||
|  | ||||
|                 if (combinedAlpha == 0xFF) // max alpha=255 on "*covers" and max alpha=255 on "widgetAlpha" | ||||
|                 { | ||||
|                     // Render a solid pixel | ||||
|                     renderPixel(p, red, green, blue); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     const uint8_t ialpha = 0xFF - combinedAlpha; | ||||
|                     const uint8_t p_red = LCD8bpp_BGRA2222::getRedFromColor(*p); | ||||
|                     const uint8_t p_green = LCD8bpp_BGRA2222::getGreenFromColor(*p); | ||||
|                     const uint8_t p_blue = LCD8bpp_BGRA2222::getBlueFromColor(*p); | ||||
|                     renderPixel(p, | ||||
|                                 LCD::div255(red * combinedAlpha + p_red * ialpha), | ||||
|                                 LCD::div255(green * combinedAlpha + p_green * ialpha), | ||||
|                                 LCD::div255(blue * combinedAlpha + p_blue * ialpha)); | ||||
|                 } | ||||
|             } | ||||
|             covers++; | ||||
|             p++; | ||||
|             currentX++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AbstractPainterBGRA2222::renderPixel(uint8_t* p, uint8_t red, uint8_t green, uint8_t blue) | ||||
| { | ||||
|     *p = LCD8bpp_BGRA2222::getColorFromRGB(red, green, blue); | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,95 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/canvas/AbstractPainterBW.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void AbstractPainterBW::render(uint8_t* ptr, | ||||
|                                int x, | ||||
|                                int xAdjust, | ||||
|                                int y, | ||||
|                                unsigned count, | ||||
|                                const uint8_t* covers) | ||||
| { | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|     x += xAdjust; | ||||
|     unsigned char* p = ptr + (x / 8); | ||||
|  | ||||
|     if (!renderInit()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     if (totalAlpha == 0xFF) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             uint8_t color; | ||||
|             if (renderNext(color)) | ||||
|             { | ||||
|                 if (*covers >= 0x80) | ||||
|                 { | ||||
|                     unsigned pixel = 1 << (7 - (x % 8)); | ||||
|                     if (!color) | ||||
|                     { | ||||
|                         *p &= ~pixel; | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         *p |= pixel; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             if (((++x) % 8) == 0) | ||||
|             { | ||||
|                 p++; | ||||
|             } | ||||
|             covers++; | ||||
|             currentX++; | ||||
|         } while (--count); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             uint8_t color; | ||||
|             if (renderNext(color)) | ||||
|             { | ||||
|                 if (totalAlpha * *covers >= 0xFF * 0x80) | ||||
|                 { | ||||
|                     unsigned pixel = 1 << (7 - (x % 8)); | ||||
|                     if (!color) | ||||
|                     { | ||||
|                         *p &= ~pixel; | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         *p |= pixel; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             if (((++x) % 8) == 0) | ||||
|             { | ||||
|                 p++; | ||||
|             } | ||||
|             covers++; | ||||
|             currentX++; | ||||
|         } while (--count); | ||||
|     } | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,64 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <platform/driver/lcd/LCD2bpp.hpp> | ||||
| #include <touchgfx/Color.hpp> | ||||
| #include <touchgfx/widgets/canvas/AbstractPainterGRAY2.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void AbstractPainterGRAY2::render(uint8_t* ptr, | ||||
|                                   int x, | ||||
|                                   int xAdjust, | ||||
|                                   int y, | ||||
|                                   unsigned count, | ||||
|                                   const uint8_t* covers) | ||||
| { | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|     x += xAdjust; | ||||
|     if (renderInit()) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             uint8_t gray, alpha; | ||||
|             if (renderNext(gray, alpha)) | ||||
|             { | ||||
|                 const uint8_t combinedAlpha = LCD::div255((*covers) * LCD::div255(alpha * widgetAlpha)); | ||||
|  | ||||
|                 if (combinedAlpha == 0xFF) // max alpha=0xFF on "*covers" and max alpha=0xFF on "widgetAlpha" | ||||
|                 { | ||||
|                     // Render a solid pixel | ||||
|                     renderPixel(ptr, x, gray); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     const uint8_t p_gray = LCD2bpp::getPixel(ptr, x) * 0x55; | ||||
|                     const uint8_t ialpha = 0xFF - combinedAlpha; | ||||
|                     renderPixel(ptr, x, LCD::div255((gray * combinedAlpha + p_gray * ialpha) * 0x55) >> 6); | ||||
|                 } | ||||
|             } | ||||
|             covers++; | ||||
|             x++; | ||||
|             currentX++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AbstractPainterGRAY2::renderPixel(uint8_t* p, uint16_t offset, uint8_t gray) | ||||
| { | ||||
|     LCD2bpp::setPixel(p, offset, gray); | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,64 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <platform/driver/lcd/LCD4bpp.hpp> | ||||
| #include <touchgfx/Color.hpp> | ||||
| #include <touchgfx/widgets/canvas/AbstractPainterGRAY4.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void AbstractPainterGRAY4::render(uint8_t* ptr, | ||||
|                                   int x, | ||||
|                                   int xAdjust, | ||||
|                                   int y, | ||||
|                                   unsigned count, | ||||
|                                   const uint8_t* covers) | ||||
| { | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|     x += xAdjust; | ||||
|     if (renderInit()) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             uint8_t gray, alpha; | ||||
|             if (renderNext(gray, alpha)) | ||||
|             { | ||||
|                 const uint8_t combinedAlpha = LCD::div255((*covers) * LCD::div255(alpha * widgetAlpha)); | ||||
|  | ||||
|                 if (combinedAlpha == 0xFF) // max alpha=0xFF on "*covers" and max alpha=0xFF on "widgetAlpha" | ||||
|                 { | ||||
|                     // Render a solid pixel | ||||
|                     renderPixel(ptr, x, gray); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     const uint8_t p_gray = LCD4bpp::getPixel(ptr, x); | ||||
|                     const uint8_t ialpha = 0xFF - combinedAlpha; | ||||
|                     renderPixel(ptr, x, LCD::div255((gray * combinedAlpha + p_gray * ialpha) * 0x11) >> 4); | ||||
|                 } | ||||
|             } | ||||
|             covers++; | ||||
|             x++; | ||||
|             currentX++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AbstractPainterGRAY4::renderPixel(uint8_t* p, uint16_t offset, uint8_t gray) | ||||
| { | ||||
|     LCD4bpp::setPixel(p, offset, gray & 0x0F); | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,69 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/Color.hpp> | ||||
| #include <touchgfx/widgets/canvas/AbstractPainterRGB565.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void AbstractPainterRGB565::render(uint8_t* ptr, | ||||
|                                    int x, | ||||
|                                    int xAdjust, | ||||
|                                    int y, | ||||
|                                    unsigned count, | ||||
|                                    const uint8_t* covers) | ||||
| { | ||||
|     uint16_t* p = reinterpret_cast<uint16_t*>(ptr) + (x + xAdjust); | ||||
|  | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|     if (renderInit()) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             uint8_t red, green, blue, alpha; | ||||
|             if (renderNext(red, green, blue, alpha)) | ||||
|             { | ||||
|                 const uint8_t combinedAlpha = LCD::div255((*covers) * LCD::div255(alpha * widgetAlpha)); | ||||
|  | ||||
|                 if (combinedAlpha == 0xFF) // max alpha=0xFF on "*covers" and max alpha=0xFF on "widgetAlpha" | ||||
|                 { | ||||
|                     // Render a solid pixel | ||||
|                     renderPixel(p, red, green, blue); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     const uint8_t ialpha = 0xFF - combinedAlpha; | ||||
|                     const uint8_t p_red = (*p & RMASK) >> 8; | ||||
|                     const uint8_t p_green = (*p & GMASK) >> 3; | ||||
|                     const uint8_t p_blue = (*p & BMASK) << 3; | ||||
|                     renderPixel(p, | ||||
|                                 LCD::div255(red * combinedAlpha + p_red * ialpha), | ||||
|                                 LCD::div255(green * combinedAlpha + p_green * ialpha), | ||||
|                                 LCD::div255(blue * combinedAlpha + p_blue * ialpha)); | ||||
|                 } | ||||
|             } | ||||
|             covers++; | ||||
|             p++; | ||||
|             currentX++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AbstractPainterRGB565::renderPixel(uint16_t* p, uint8_t red, uint8_t green, uint8_t blue) | ||||
| { | ||||
|     *p = ((red << 8) & RMASK) | ((green << 3) & GMASK) | ((blue >> 3) & BMASK); | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,72 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/Color.hpp> | ||||
| #include <touchgfx/widgets/canvas/AbstractPainterRGB888.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void AbstractPainterRGB888::render(uint8_t* ptr, | ||||
|                                    int x, | ||||
|                                    int xAdjust, | ||||
|                                    int y, | ||||
|                                    unsigned count, | ||||
|                                    const uint8_t* covers) | ||||
| { | ||||
|     uint8_t* p = ptr + ((x + xAdjust) * 3); | ||||
|  | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|     if (renderInit()) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             uint8_t red, green, blue, alpha; | ||||
|             if (renderNext(red, green, blue, alpha)) | ||||
|             { | ||||
|                 const uint8_t combinedAlpha = LCD::div255((*covers) * LCD::div255(alpha * widgetAlpha)); | ||||
|  | ||||
|                 if (combinedAlpha == 0xFF) // max alpha=0xFF on "*covers" and max alpha=0xFF on "widgetAlpha" | ||||
|                 { | ||||
|                     // Render a solid pixel | ||||
|                     renderPixel(reinterpret_cast<uint16_t*>(p), red, green, blue); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     const uint8_t ialpha = 0xFF - combinedAlpha; | ||||
|                     const uint8_t p_blue = p[0]; | ||||
|                     const uint8_t p_green = p[1]; | ||||
|                     const uint8_t p_red = p[2]; | ||||
|                     renderPixel(reinterpret_cast<uint16_t*>(p), | ||||
|                                 LCD::div255(red * combinedAlpha + p_red * ialpha), | ||||
|                                 LCD::div255(green * combinedAlpha + p_green * ialpha), | ||||
|                                 LCD::div255(blue * combinedAlpha + p_blue * ialpha)); | ||||
|                 } | ||||
|             } | ||||
|             covers++; | ||||
|             p += 3; | ||||
|             currentX++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AbstractPainterRGB888::renderPixel(uint16_t* p, uint8_t red, uint8_t green, uint8_t blue) | ||||
| { | ||||
|     uint8_t* p8 = reinterpret_cast<uint8_t*>(p); | ||||
|     p8[0] = blue; | ||||
|     p8[1] = green; | ||||
|     p8[2] = red; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,69 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/Color.hpp> | ||||
| #include <touchgfx/widgets/canvas/AbstractPainterRGBA2222.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void AbstractPainterRGBA2222::render(uint8_t* ptr, | ||||
|                                      int x, | ||||
|                                      int xAdjust, | ||||
|                                      int y, | ||||
|                                      unsigned count, | ||||
|                                      const uint8_t* covers) | ||||
| { | ||||
|     uint8_t* p = ptr + (x + xAdjust); | ||||
|  | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|     if (renderInit()) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             uint8_t red, green, blue, alpha; | ||||
|             if (renderNext(red, green, blue, alpha)) | ||||
|             { | ||||
|                 const uint8_t combinedAlpha = LCD::div255((*covers) * LCD::div255(alpha * widgetAlpha)); | ||||
|  | ||||
|                 if (combinedAlpha == 0xFF) // max alpha=0xFF on "*covers" and max alpha=0xFF on "widgetAlpha" | ||||
|                 { | ||||
|                     // Render a solid pixel | ||||
|                     renderPixel(p, red, green, blue); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     const uint8_t ialpha = 0xFF - combinedAlpha; | ||||
|                     const uint8_t p_red = LCD8bpp_RGBA2222::getRedFromColor(*p); | ||||
|                     const uint8_t p_green = LCD8bpp_RGBA2222::getGreenFromColor(*p); | ||||
|                     const uint8_t p_blue = LCD8bpp_RGBA2222::getBlueFromColor(*p); | ||||
|                     renderPixel(p, | ||||
|                                 LCD::div255(red * combinedAlpha + p_red * ialpha), | ||||
|                                 LCD::div255(green * combinedAlpha + p_green * ialpha), | ||||
|                                 LCD::div255(blue * combinedAlpha + p_blue * ialpha)); | ||||
|                 } | ||||
|             } | ||||
|             covers++; | ||||
|             p++; | ||||
|             currentX++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AbstractPainterRGBA2222::renderPixel(uint8_t* p, uint8_t red, uint8_t green, uint8_t blue) | ||||
| { | ||||
|     *p = LCD8bpp_RGBA2222::getColorFromRGB(red, green, blue); | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,88 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/canvas/AbstractShape.hpp> | ||||
| #include <touchgfx/widgets/canvas/Canvas.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| AbstractShape::AbstractShape() | ||||
|     : CanvasWidget(), | ||||
|       dx(0), dy(0), shapeAngle(0), | ||||
|       xScale(CWRUtil::toQ10<int>(1)), yScale(CWRUtil::toQ10<int>(1)), | ||||
|       minimalRect(Rect()) | ||||
| { | ||||
|     Drawable::setWidthHeight(0, 0); | ||||
| } | ||||
|  | ||||
| bool AbstractShape::drawCanvasWidget(const Rect& invalidatedArea) const | ||||
| { | ||||
|     Canvas canvas(this, invalidatedArea); | ||||
|     int numPoints = getNumPoints(); | ||||
|     if (numPoints == 0) | ||||
|     { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     canvas.moveTo(getCacheX(0), getCacheY(0)); | ||||
|     for (int i = 1; i < numPoints; i++) | ||||
|     { | ||||
|         canvas.lineTo(getCacheX(i), getCacheY(i)); | ||||
|     } | ||||
|     return canvas.render(); | ||||
| } | ||||
|  | ||||
| void AbstractShape::updateAbstractShapeCache() | ||||
| { | ||||
|     int numPoints = getNumPoints(); | ||||
|  | ||||
|     int xMin = 0; | ||||
|     int xMax = 0; | ||||
|     int yMin = 0; | ||||
|     int yMax = 0; | ||||
|  | ||||
|     for (int i = 0; i < numPoints; i++) | ||||
|     { | ||||
|         CWRUtil::Q5 xCorner = getCornerX(i); | ||||
|         CWRUtil::Q5 yCorner = getCornerY(i); | ||||
|  | ||||
|         CWRUtil::Q5 xCache = dx + ((CWRUtil::mulQ5(xCorner, xScale) * CWRUtil::cosine(shapeAngle))) - ((CWRUtil::mulQ5(yCorner, yScale) * CWRUtil::sine(shapeAngle))); | ||||
|         if (i == 0 || xCache.to<int>() > xMax) | ||||
|         { | ||||
|             xMax = xCache.to<int>(); | ||||
|         } | ||||
|         if (i == 0 || xCache.to<int>() < xMin) | ||||
|         { | ||||
|             xMin = xCache.to<int>(); | ||||
|         } | ||||
|         CWRUtil::Q5 yCache = dy + ((CWRUtil::mulQ5(yCorner, yScale) * CWRUtil::cosine(shapeAngle))) + ((CWRUtil::mulQ5(xCorner, xScale) * CWRUtil::sine(shapeAngle))); | ||||
|         if (i == 0 || yCache.to<int>() > yMax) | ||||
|         { | ||||
|             yMax = yCache.to<int>(); | ||||
|         } | ||||
|         if (i == 0 || yCache.to<int>() < yMin) | ||||
|         { | ||||
|             yMin = yCache.to<int>(); | ||||
|         } | ||||
|         setCache(i, xCache, yCache); | ||||
|     } | ||||
|     minimalRect = Rect(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1); | ||||
| } | ||||
|  | ||||
| Rect AbstractShape::getMinimalRect() const | ||||
| { | ||||
|     return minimalRect; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,277 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/canvas/Canvas.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| Canvas::Canvas(const CanvasWidget* _widget, const Rect& invalidatedArea) | ||||
|     : widget(_widget), | ||||
|       enoughMemory(false), penUp(true), penHasBeenDown(false), previousOutside(0), penDownOutside(0) | ||||
| { | ||||
|     assert(CanvasWidgetRenderer::hasBuffer() && "No buffer allocated for CanvasWidgetRenderer drawing"); | ||||
|     assert(Rasterizer::POLY_BASE_SHIFT == 5 && "CanvasWidget assumes Q5 but Rasterizer uses a different setting"); | ||||
|  | ||||
|     // Area to redraw (relative coordinates) | ||||
|     Rect dirtyArea = Rect(0, 0, widget->getWidth(), widget->getHeight()) & invalidatedArea; | ||||
|  | ||||
|     // Absolute position of the scalableImage. | ||||
|     Rect dirtyAreaAbsolute = dirtyArea; | ||||
|     widget->translateRectToAbsolute(dirtyAreaAbsolute); | ||||
|  | ||||
|     // Transform rects to match framebuffer coordinates | ||||
|     // This is needed if the display is rotated compared to the framebuffer | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(dirtyArea, widget->getRect()); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(dirtyAreaAbsolute); | ||||
|  | ||||
|     // Re-size buffers for optimum memory buffer layout. | ||||
|     enoughMemory = CanvasWidgetRenderer::setScanlineWidth(dirtyArea.width); | ||||
|     ras.reset(); | ||||
|  | ||||
|     offsetX = dirtyArea.x; | ||||
|     offsetY = dirtyArea.y; | ||||
|     invalidatedAreaX = CWRUtil::toQ5<int>(dirtyArea.x); | ||||
|     invalidatedAreaY = CWRUtil::toQ5<int>(dirtyArea.y); | ||||
|     invalidatedAreaWidth = CWRUtil::toQ5<int>(dirtyArea.width); | ||||
|     invalidatedAreaHeight = CWRUtil::toQ5<int>(dirtyArea.height); | ||||
|  | ||||
|     // Create the rendering buffer | ||||
|     uint8_t* RESTRICT buf = reinterpret_cast<uint8_t*>(HAL::getInstance()->lockFrameBuffer()); | ||||
|     int stride = HAL::lcd().framebufferStride(); | ||||
|     uint8_t offset = 0; | ||||
|     switch (HAL::lcd().framebufferFormat()) | ||||
|     { | ||||
|     case Bitmap::BW: | ||||
|         buf += (dirtyAreaAbsolute.x / 8) + dirtyAreaAbsolute.y * stride; | ||||
|         offset = dirtyAreaAbsolute.x % 8; | ||||
|         break; | ||||
|     case Bitmap::GRAY2: | ||||
|         buf += (dirtyAreaAbsolute.x / 4) + dirtyAreaAbsolute.y * stride; | ||||
|         offset = dirtyAreaAbsolute.x % 4; | ||||
|         break; | ||||
|     case Bitmap::GRAY4: | ||||
|         buf += (dirtyAreaAbsolute.x / 2) + dirtyAreaAbsolute.y * stride; | ||||
|         offset = dirtyAreaAbsolute.x % 2; | ||||
|         break; | ||||
|     case Bitmap::RGB565: | ||||
|         buf += dirtyAreaAbsolute.x * 2 + dirtyAreaAbsolute.y * stride; | ||||
|         break; | ||||
|     case Bitmap::RGB888: | ||||
|         buf += dirtyAreaAbsolute.x * 3 + dirtyAreaAbsolute.y * stride; | ||||
|         break; | ||||
|     case Bitmap::RGBA2222: | ||||
|     case Bitmap::BGRA2222: | ||||
|     case Bitmap::ARGB2222: | ||||
|     case Bitmap::ABGR2222: | ||||
|     case Bitmap::L8: | ||||
|         buf += dirtyAreaAbsolute.x + dirtyAreaAbsolute.y * stride; | ||||
|         break; | ||||
|     case Bitmap::ARGB8888: | ||||
|         buf += dirtyAreaAbsolute.x * 4 + dirtyAreaAbsolute.y * stride; | ||||
|         break; | ||||
|     case Bitmap::BW_RLE: | ||||
|     case Bitmap::A4: | ||||
|     case Bitmap::CUSTOM: | ||||
|         assert(0 && "Unsupported bit depth"); | ||||
|         break; | ||||
|     } | ||||
|     ras.setMaxRenderY(dirtyAreaAbsolute.height); | ||||
|     rbuf.attach(buf, offset, dirtyAreaAbsolute.width, dirtyAreaAbsolute.height, stride); | ||||
| } | ||||
|  | ||||
| Canvas::~Canvas() | ||||
| { | ||||
|     HAL::getInstance()->unlockFrameBuffer(); //lint !e1551 | ||||
| } | ||||
|  | ||||
| void Canvas::moveTo(CWRUtil::Q5 x, CWRUtil::Q5 y) | ||||
| { | ||||
|     if (!enoughMemory) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!penUp) | ||||
|     { | ||||
|         close(); | ||||
|     } | ||||
|  | ||||
|     transformFrameBufferToDisplay(x, y); | ||||
|     x = x - invalidatedAreaX; | ||||
|     y = y - invalidatedAreaY; | ||||
|  | ||||
|     uint8_t outside = isOutside(x, y, invalidatedAreaWidth, invalidatedAreaHeight); | ||||
|  | ||||
|     if (outside) | ||||
|     { | ||||
|         penUp = true; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         penDownOutside = outside; | ||||
|         ras.moveTo(x, y); | ||||
|         penUp = false; | ||||
|         penHasBeenDown = true; | ||||
|     } | ||||
|  | ||||
|     initialX = x; | ||||
|     initialY = y; | ||||
|  | ||||
|     previousX = x; | ||||
|     previousY = y; | ||||
|     previousOutside = outside; | ||||
| } | ||||
|  | ||||
| void Canvas::lineTo(CWRUtil::Q5 x, CWRUtil::Q5 y) | ||||
| { | ||||
|     if (!enoughMemory) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     transformFrameBufferToDisplay(x, y); | ||||
|     x = x - invalidatedAreaX; | ||||
|     y = y - invalidatedAreaY; | ||||
|  | ||||
|     uint8_t outside = isOutside(x, y, invalidatedAreaWidth, invalidatedAreaHeight); | ||||
|  | ||||
|     if (!previousOutside) | ||||
|     { | ||||
|         ras.lineTo(x, y); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         if (!outside || !(previousOutside & outside)) | ||||
|         { | ||||
|             // x,y is inside, or on another side compared to previous | ||||
|             if (penUp) | ||||
|             { | ||||
|                 penDownOutside = previousOutside; | ||||
|                 ras.moveTo(previousX, previousY); | ||||
|                 penUp = false; | ||||
|                 penHasBeenDown = true; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 ras.lineTo(previousX, previousY); | ||||
|             } | ||||
|             ras.lineTo(x, y); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // Restrict "outside" to the same side as previous point. | ||||
|             outside &= previousOutside; | ||||
|         } | ||||
|     } | ||||
|     previousX = x; | ||||
|     previousY = y; | ||||
|     previousOutside = outside; | ||||
| } | ||||
|  | ||||
| bool Canvas::render(uint8_t customAlpha) | ||||
| { | ||||
|     // If the invalidated rect is too wide compared to the allocated buffer for CWR, | ||||
|     // redrawing will not help. The CanvasWidget needs to know about this situation | ||||
|     // and maybe try to divide the area vertically instead, but this has not been | ||||
|     // implemented. And probably should not. | ||||
|     if (!enoughMemory) | ||||
|     { | ||||
|         return true; // Redrawing a rect with fewer scanlines will not help, fake "ok" to move on | ||||
|     } | ||||
|  | ||||
|     if (ras.wasOutlineTooComplex()) | ||||
|     { | ||||
|         return false; // Try again with fewer scanlines | ||||
|     } | ||||
|  | ||||
|     if (!penHasBeenDown) | ||||
|     { | ||||
|         return true; // Nothing drawn. Done | ||||
|     } | ||||
|  | ||||
|     const uint8_t alpha = LCD::div255(widget->getAlpha() * customAlpha); | ||||
|     if (alpha == 0) | ||||
|     { | ||||
|         return true; // Invisible. Done | ||||
|     } | ||||
|  | ||||
|     close(); | ||||
|  | ||||
|     widget->getPainter().setOffset(offsetX /*+widget->getX()*/, offsetY /*+widget->getY()*/); | ||||
|     widget->getPainter().setWidgetAlpha(alpha); | ||||
|     Renderer renderer(rbuf, widget->getPainter()); | ||||
|     return ras.render(renderer); | ||||
| } | ||||
|  | ||||
| uint8_t Canvas::isOutside(const CWRUtil::Q5& x, const CWRUtil::Q5& y, const CWRUtil::Q5& width, const CWRUtil::Q5& height) const | ||||
| { | ||||
|     uint8_t outside = 0; | ||||
|     // Find out if (x,y) is above/below of current area | ||||
|     if (y < 0) | ||||
|     { | ||||
|         outside = POINT_IS_ABOVE; | ||||
|     } | ||||
|     else if (y >= height) | ||||
|     { | ||||
|         outside = POINT_IS_BELOW; | ||||
|     } | ||||
|     // Find out if (x,y) is left/right of current area | ||||
|     if (x < 0) | ||||
|     { | ||||
|         outside |= POINT_IS_LEFT; | ||||
|     } | ||||
|     else if (x >= width) | ||||
|     { | ||||
|         outside |= POINT_IS_RIGHT; | ||||
|     } | ||||
|     return outside; | ||||
| } | ||||
|  | ||||
| void Canvas::transformFrameBufferToDisplay(CWRUtil::Q5& x, CWRUtil::Q5& y) const | ||||
| { | ||||
|     switch (HAL::DISPLAY_ROTATION) | ||||
|     { | ||||
|     case rotate0: | ||||
|         break; | ||||
|     case rotate90: | ||||
|         CWRUtil::Q5 tmpY = y; | ||||
|         y = CWRUtil::toQ5<int>(widget->getWidth()) - x; | ||||
|         x = tmpY; | ||||
|         break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void Canvas::close() | ||||
| { | ||||
|     if (!penUp) | ||||
|     { | ||||
|         if (previousOutside & penDownOutside) | ||||
|         { | ||||
|             // We are outside on the same side as we started. No need | ||||
|             //  to close the path, CWR will do this for us. | ||||
|             //lineTo(penDownX, penDownY); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (previousOutside) | ||||
|             { | ||||
|                 ras.lineTo(previousX, previousY); | ||||
|             } | ||||
|             ras.lineTo(initialX, initialY); | ||||
|         } | ||||
|     } | ||||
|     penUp = false; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,146 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/canvas/CanvasWidget.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| CanvasWidget::CanvasWidget() | ||||
|     : Widget(), | ||||
|       canvasPainter(0), | ||||
|       maxRenderLines(0x7FFF), | ||||
|       alpha(255) | ||||
| { | ||||
| } | ||||
|  | ||||
| void CanvasWidget::setPainter(AbstractPainter& painter) | ||||
| { | ||||
|     canvasPainter = &painter; | ||||
| } | ||||
|  | ||||
| AbstractPainter& CanvasWidget::getPainter() const | ||||
| { | ||||
|     assert(canvasPainter != 0 && "No painter set"); | ||||
|     return *canvasPainter; //lint !e613 | ||||
| } //lint !e1763 | ||||
|  | ||||
| void CanvasWidget::draw(const Rect& invalidatedArea) const | ||||
| { | ||||
|     Rect area = invalidatedArea; | ||||
|  | ||||
|     int16_t* offset; | ||||
|     int16_t* lines; | ||||
|     int16_t* width; | ||||
|     int16_t* height; | ||||
|  | ||||
|     int16_t wantedRenderLines; | ||||
|  | ||||
|     switch (HAL::DISPLAY_ROTATION) | ||||
|     { | ||||
|     default: | ||||
|     case rotate0: | ||||
|         offset = &area.y; | ||||
|         lines = &area.height; | ||||
|         width = &area.width; | ||||
|         height = &wantedRenderLines; | ||||
|         break; | ||||
|     case rotate90: | ||||
|         offset = &area.x; | ||||
|         lines = &area.width; | ||||
|         width = &wantedRenderLines; | ||||
|         height = &area.height; | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     Rect minimalRect = getMinimalRect(); | ||||
|  | ||||
|     bool failedAtLeastOnce = false; | ||||
|     while (*lines) | ||||
|     { | ||||
|         wantedRenderLines = MIN(maxRenderLines, *lines); | ||||
|  | ||||
|         while (wantedRenderLines > 0) | ||||
|         { | ||||
|             Rect smallArea(area.x, area.y, *width, *height); | ||||
|             if (!smallArea.intersect(minimalRect)) | ||||
|             { | ||||
|                 break; | ||||
|             } | ||||
|             if (drawCanvasWidget(smallArea)) | ||||
|             { | ||||
|                 break; | ||||
|             } | ||||
| #ifdef SIMULATOR | ||||
|             if (CanvasWidgetRenderer::getWriteMemoryUsageReport()) | ||||
|             { | ||||
|                 if (wantedRenderLines > 1) | ||||
|                 { | ||||
|                     touchgfx_printf("CWR will split draw into multiple draws due to limited memory.\n"); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     touchgfx_printf("CWR was unable to complete a draw operation due to limited memory.\n"); | ||||
|                 } | ||||
|             } | ||||
| #endif | ||||
|             wantedRenderLines >>= 1; | ||||
|             failedAtLeastOnce = true; | ||||
|         } | ||||
|         if (wantedRenderLines == 0) | ||||
|         { | ||||
|             // We did not manage to draw anything. Set wantedHeight to | ||||
|             // one to skip a single raster line and try to render the | ||||
|             // rest of the CanvasWidget. | ||||
|             wantedRenderLines = 1; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (failedAtLeastOnce && maxRenderLines == 0x7FFF) | ||||
|             { | ||||
|                 // Only adjust maxRenderLines if it is the first draw for the CanvasWidget | ||||
|                 maxRenderLines = wantedRenderLines; | ||||
|             } | ||||
|         } | ||||
|         *offset += wantedRenderLines; | ||||
|         *lines -= wantedRenderLines; | ||||
|     } | ||||
|     if (maxRenderLines == 0x7FFF) | ||||
|     { | ||||
|         maxRenderLines--; // 0x7FFF means first draw | ||||
|     } | ||||
| } | ||||
|  | ||||
| void CanvasWidget::invalidate() const | ||||
| { | ||||
|     Rect minimalRect = getMinimalRect(); | ||||
|     minimalRect.intersect(CanvasWidget::getMinimalRect()); | ||||
|     invalidateRect(minimalRect); | ||||
| } | ||||
|  | ||||
| Rect CanvasWidget::getMinimalRect() const | ||||
| { | ||||
|     return Rect(0, 0, getWidth(), getHeight()); | ||||
| } | ||||
|  | ||||
| Rect CanvasWidget::getSolidRect() const | ||||
| { | ||||
|     return Rect(0, 0, 0, 0); | ||||
| } | ||||
|  | ||||
| void CanvasWidget::resetMaxRenderLines() | ||||
| { | ||||
|     maxRenderLines = 0x7FFF; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,554 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/canvas/Circle.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| Circle::Circle() | ||||
|     : CanvasWidget(), | ||||
|       circleCenterX(0), circleCenterY(0), circleRadius(0), | ||||
|       circleArcAngleStart(CWRUtil::toQ5<int>(0)), circleArcAngleEnd(CWRUtil::toQ5<int>(360)), | ||||
|       circleLineWidth(0), circleArcIncrement(5), | ||||
|       circleCapArcIncrement(180) | ||||
| { | ||||
|     Drawable::setWidthHeight(0, 0); | ||||
| } | ||||
|  | ||||
| void Circle::setPrecision(int precision) | ||||
| { | ||||
|     if (precision < 1) | ||||
|     { | ||||
|         precision = 1; | ||||
|     } | ||||
|     if (precision > 120) | ||||
|     { | ||||
|         precision = 120; | ||||
|     } | ||||
|     circleArcIncrement = precision; | ||||
| } | ||||
|  | ||||
| int Circle::getPrecision() const | ||||
| { | ||||
|     return circleArcIncrement; | ||||
| } | ||||
|  | ||||
| void Circle::setCapPrecision(int precision) | ||||
| { | ||||
|     if (precision < 1) | ||||
|     { | ||||
|         precision = 1; | ||||
|     } | ||||
|     if (precision > 180) | ||||
|     { | ||||
|         precision = 180; | ||||
|     } | ||||
|     circleCapArcIncrement = precision; | ||||
| } | ||||
|  | ||||
| int Circle::getCapPrecision() const | ||||
| { | ||||
|     return circleCapArcIncrement; | ||||
| } | ||||
|  | ||||
| bool Circle::drawCanvasWidget(const Rect& invalidatedArea) const | ||||
| { | ||||
|     CWRUtil::Q5 arcStart = circleArcAngleStart; | ||||
|     CWRUtil::Q5 arcEnd = circleArcAngleEnd; | ||||
|  | ||||
|     CWRUtil::Q5 _360 = CWRUtil::toQ5<int>(360); | ||||
|  | ||||
|     // Put start before end by swapping | ||||
|     if (arcStart > arcEnd) | ||||
|     { | ||||
|         CWRUtil::Q5 tmp = arcStart; | ||||
|         arcStart = arcEnd; | ||||
|         arcEnd = tmp; | ||||
|     } | ||||
|  | ||||
|     if ((arcEnd - arcStart) >= _360) | ||||
|     { | ||||
|         // The entire circle has to be drawn | ||||
|         arcStart = CWRUtil::toQ5<int>(0); | ||||
|         arcEnd = _360; | ||||
|     } | ||||
|  | ||||
|     if (circleLineWidth != 0) | ||||
|     { | ||||
|         // Check if invalidated area is completely inside the circle | ||||
|         int32_t x1 = int(CWRUtil::toQ5(invalidatedArea.x)); // Take the corners of the invalidated area | ||||
|         int32_t x2 = int(CWRUtil::toQ5(invalidatedArea.right())); | ||||
|         int32_t y1 = int(CWRUtil::toQ5(invalidatedArea.y)); | ||||
|         int32_t y2 = int(CWRUtil::toQ5(invalidatedArea.bottom())); | ||||
|         int32_t dx1 = abs(int(circleCenterX) - x1); // Find distances between each corner and circle center | ||||
|         int32_t dx2 = abs(int(circleCenterX) - x2); | ||||
|         int32_t dy1 = abs(int(circleCenterY) - y1); | ||||
|         int32_t dy2 = abs(int(circleCenterY) - y2); | ||||
|         int32_t dx = CWRUtil::Q5(MAX(dx1, dx2)).to<int>() + 1; // Largest hor/vert distance (round up) | ||||
|         int32_t dy = CWRUtil::Q5(MAX(dy1, dy2)).to<int>() + 1; | ||||
|         int32_t dsqr = (dx * dx) + (dy * dy); // Pythagoras | ||||
|  | ||||
|         // From https://www.mathopenref.com/polygonincircle.html | ||||
|         int32_t rmin = ((circleRadius - (circleLineWidth / 2)) * CWRUtil::cosine((circleArcIncrement + 1) / 2)).to<int>(); | ||||
|  | ||||
|         // Check if invalidatedArea is completely inside circle | ||||
|         if (dsqr < rmin * rmin) | ||||
|         { | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Canvas canvas(this, invalidatedArea); | ||||
|  | ||||
|     CWRUtil::Q5 radius = circleRadius; | ||||
|     CWRUtil::Q5 lineWidth = circleLineWidth; | ||||
|     if (circleLineWidth > circleRadius * 2) | ||||
|     { | ||||
|         lineWidth = (circleRadius + circleLineWidth / 2); | ||||
|         radius = lineWidth / 2; | ||||
|     } | ||||
|  | ||||
|     CWRUtil::Q5 arc = arcStart; | ||||
|     CWRUtil::Q5 circleArcIncrementQ5 = CWRUtil::toQ5<int>(circleArcIncrement); | ||||
|     moveToAR2(canvas, arc, (radius * 2) + lineWidth); | ||||
|     CWRUtil::Q5 nextArc = CWRUtil::Q5(ROUNDUP((int)(arc + CWRUtil::toQ5<int>(1)), (int)circleArcIncrementQ5)); | ||||
|     while (nextArc <= arcEnd) | ||||
|     { | ||||
|         arc = nextArc; | ||||
|         lineToAR2(canvas, arc, (radius * 2) + lineWidth); | ||||
|         nextArc = nextArc + circleArcIncrementQ5; | ||||
|     } | ||||
|     if (arc < arcEnd) | ||||
|     { | ||||
|         // "arc" is not updated. It is the last arc in steps of "circleArcIncrement" | ||||
|         lineToAR2(canvas, arcEnd, (radius * 2) + lineWidth); | ||||
|     } | ||||
|  | ||||
|     if (lineWidth == CWRUtil::toQ5<int>(0)) | ||||
|     { | ||||
|         // Draw a filled circle / pie / pacman | ||||
|         if (arcEnd - arcStart < _360) | ||||
|         { | ||||
|             // Not a complete circle, line to center | ||||
|             canvas.lineTo(circleCenterX, circleCenterY); | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         CWRUtil::Q5 circleCapArcIncrementQ5 = CWRUtil::toQ5<int>(circleCapArcIncrement); | ||||
|         CWRUtil::Q5 _180 = CWRUtil::toQ5<int>(180); | ||||
|         if (arcEnd - arcStart < _360) | ||||
|         { | ||||
|             // Draw the circle cap | ||||
|             CWRUtil::Q5 capX = circleCenterX + (radius * CWRUtil::sine(arcEnd)); | ||||
|             CWRUtil::Q5 capY = circleCenterY - (radius * CWRUtil::cosine(arcEnd)); | ||||
|             for (CWRUtil::Q5 capAngle = arcEnd + circleCapArcIncrementQ5; capAngle < arcEnd + _180; capAngle = capAngle + circleCapArcIncrementQ5) | ||||
|             { | ||||
|                 lineToXYAR2(canvas, capX, capY, capAngle, lineWidth); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Not a filled circle, draw the path on the inside of the circle | ||||
|         if (arc < arcEnd) | ||||
|         { | ||||
|             lineToAR2(canvas, arcEnd, (radius * 2) - lineWidth); | ||||
|         } | ||||
|  | ||||
|         nextArc = arc; | ||||
|         while (nextArc >= arcStart) | ||||
|         { | ||||
|             arc = nextArc; | ||||
|             lineToAR2(canvas, arc, (radius * 2) - lineWidth); | ||||
|             nextArc = nextArc - circleArcIncrementQ5; | ||||
|         } | ||||
|  | ||||
|         if (arc > arcStart) | ||||
|         { | ||||
|             lineToAR2(canvas, arcStart, (radius * 2) - lineWidth); | ||||
|         } | ||||
|  | ||||
|         if (arcEnd - arcStart < _360) | ||||
|         { | ||||
|             // Draw the circle cap | ||||
|             CWRUtil::Q5 capX = circleCenterX + (radius * CWRUtil::sine(arcStart)); | ||||
|             CWRUtil::Q5 capY = circleCenterY - (radius * CWRUtil::cosine(arcStart)); | ||||
|             for (CWRUtil::Q5 capAngle = arcStart - _180 + circleCapArcIncrementQ5; capAngle < arcStart; capAngle = capAngle + circleCapArcIncrementQ5) | ||||
|             { | ||||
|                 lineToXYAR2(canvas, capX, capY, capAngle, lineWidth); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return canvas.render(); | ||||
| } | ||||
|  | ||||
| Rect Circle::getMinimalRect() const | ||||
| { | ||||
|     return getMinimalRect(circleArcAngleStart, circleArcAngleEnd); | ||||
| } | ||||
|  | ||||
| Rect Circle::getMinimalRect(int16_t arcStart, int16_t arcEnd) const | ||||
| { | ||||
|     return getMinimalRect(CWRUtil::toQ5<int>(arcStart), CWRUtil::toQ5<int>(arcEnd)); | ||||
| } | ||||
|  | ||||
| Rect Circle::getMinimalRect(CWRUtil::Q5 arcStart, CWRUtil::Q5 arcEnd) const | ||||
| { | ||||
|     CWRUtil::Q5 xMin = CWRUtil::toQ5<int>(getWidth()); | ||||
|     CWRUtil::Q5 xMax = CWRUtil::toQ5<int>(0); | ||||
|     CWRUtil::Q5 yMin = CWRUtil::toQ5<int>(getHeight()); | ||||
|     CWRUtil::Q5 yMax = CWRUtil::toQ5<int>(0); | ||||
|     calculateMinimalRect(arcStart, arcEnd, xMin, xMax, yMin, yMax); | ||||
|     return Rect(xMin.to<int>() - 1, yMin.to<int>() - 1, | ||||
|                 xMax.to<int>() - xMin.to<int>() + 2, yMax.to<int>() - yMin.to<int>() + 2); | ||||
| } | ||||
|  | ||||
| void Circle::updateArc(CWRUtil::Q5 setStartAngleQ5, CWRUtil::Q5 setEndAngleQ5) | ||||
| { | ||||
|     CWRUtil::Q5 startAngleQ5 = setStartAngleQ5; | ||||
|     CWRUtil::Q5 endAngleQ5 = setEndAngleQ5; | ||||
|     if (circleArcAngleStart == startAngleQ5 && circleArcAngleEnd == endAngleQ5) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // Make sure old start < end | ||||
|     if (circleArcAngleStart > circleArcAngleEnd) | ||||
|     { | ||||
|         CWRUtil::Q5 tmp = circleArcAngleStart; | ||||
|         circleArcAngleStart = circleArcAngleEnd; | ||||
|         circleArcAngleEnd = tmp; | ||||
|     } | ||||
|     // Make sure new start < end | ||||
|     if (startAngleQ5 > endAngleQ5) | ||||
|     { | ||||
|         CWRUtil::Q5 tmp = startAngleQ5; | ||||
|         startAngleQ5 = endAngleQ5; | ||||
|         endAngleQ5 = tmp; | ||||
|     } | ||||
|  | ||||
|     // Nice constant | ||||
|     const CWRUtil::Q5 _360 = CWRUtil::toQ5<int>(360); | ||||
|  | ||||
|     // Get old circle range start in [0..360[ | ||||
|     if (circleArcAngleStart >= _360) | ||||
|     { | ||||
|         int x = (circleArcAngleStart / _360).to<int>(); | ||||
|         circleArcAngleStart = circleArcAngleStart - _360 * x; | ||||
|         circleArcAngleEnd = circleArcAngleEnd - _360 * x; | ||||
|     } | ||||
|     else if (circleArcAngleStart < 0) | ||||
|     { | ||||
|         int x = 1 + ((-circleArcAngleStart) / _360).to<int>(); | ||||
|         circleArcAngleStart = circleArcAngleStart + _360 * x; | ||||
|         circleArcAngleEnd = circleArcAngleEnd + _360 * x; | ||||
|     } | ||||
|     // Detect full circle | ||||
|     if ((circleArcAngleEnd - circleArcAngleStart) > _360) | ||||
|     { | ||||
|         circleArcAngleEnd = circleArcAngleStart + _360; | ||||
|     } | ||||
|  | ||||
|     // Get new circle range start in [0..360[ | ||||
|     if (startAngleQ5 >= _360) | ||||
|     { | ||||
|         int x = (startAngleQ5 / _360).to<int>(); | ||||
|         startAngleQ5 = startAngleQ5 - _360 * x; | ||||
|         endAngleQ5 = endAngleQ5 - _360 * x; | ||||
|     } | ||||
|     else if (startAngleQ5 < 0) | ||||
|     { | ||||
|         int x = 1 + (-startAngleQ5 / _360).to<int>(); | ||||
|         startAngleQ5 = startAngleQ5 + _360 * x; | ||||
|         endAngleQ5 = endAngleQ5 + _360 * x; | ||||
|     } | ||||
|     // Detect full circle | ||||
|     if ((endAngleQ5 - startAngleQ5) >= _360) | ||||
|     { | ||||
|         // Align full new circle with old start. | ||||
|         // So old[90..270] -> new[0..360] becomes new[90..450] for smaller invalidated area | ||||
|         startAngleQ5 = circleArcAngleStart; | ||||
|         endAngleQ5 = startAngleQ5 + _360; | ||||
|     } | ||||
|     else if ((circleArcAngleEnd - circleArcAngleStart) >= _360) | ||||
|     { | ||||
|         // New circle is not full, but old is. Align old circle with new. | ||||
|         // So old[0..360] -> new[90..270] becomes old[90..450] for smaller invalidated area | ||||
|         circleArcAngleStart = startAngleQ5; | ||||
|         circleArcAngleEnd = circleArcAngleStart + _360; | ||||
|     } | ||||
|  | ||||
|     // New start is after old end. Could be overlap | ||||
|     // if old[10..30]->new[350..380] becomes new[-10..20] | ||||
|     if (startAngleQ5 > circleArcAngleEnd && endAngleQ5 - _360 >= circleArcAngleStart) | ||||
|     { | ||||
|         startAngleQ5 = startAngleQ5 - _360; | ||||
|         endAngleQ5 = endAngleQ5 - _360; | ||||
|     } | ||||
|     // Same as above but for old instead of new | ||||
|     if (circleArcAngleStart > endAngleQ5 && circleArcAngleEnd - _360 >= startAngleQ5) | ||||
|     { | ||||
|         circleArcAngleStart = circleArcAngleStart - _360; | ||||
|         circleArcAngleEnd = circleArcAngleEnd - _360; | ||||
|     } | ||||
|  | ||||
|     Rect r; | ||||
|     if (startAngleQ5 > circleArcAngleEnd || endAngleQ5 < circleArcAngleStart) | ||||
|     { | ||||
|         // Arcs do not overlap. Invalidate both arcs. | ||||
|         r = getMinimalRect(circleArcAngleStart, circleArcAngleEnd); | ||||
|         invalidateRect(r); | ||||
|  | ||||
|         r = getMinimalRect(startAngleQ5, endAngleQ5); | ||||
|         invalidateRect(r); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // Arcs overlap. Invalidate both ends. | ||||
|         if (circleArcAngleStart != startAngleQ5) | ||||
|         { | ||||
|             r = getMinimalRectForUpdatedStartAngle(startAngleQ5); | ||||
|             invalidateRect(r); | ||||
|         } | ||||
|         if (circleArcAngleEnd != endAngleQ5) | ||||
|         { | ||||
|             r = getMinimalRectForUpdatedEndAngle(endAngleQ5); | ||||
|             invalidateRect(r); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     circleArcAngleStart = setStartAngleQ5; | ||||
|     circleArcAngleEnd = setEndAngleQ5; | ||||
| } | ||||
|  | ||||
| void Circle::moveToAR2(Canvas& canvas, const CWRUtil::Q5& angle, const CWRUtil::Q5& r2) const | ||||
| { | ||||
|     canvas.moveTo(circleCenterX + ((r2 * CWRUtil::sine(angle)) / 2), circleCenterY - ((r2 * CWRUtil::cosine(angle)) / 2)); | ||||
| } | ||||
|  | ||||
| void Circle::lineToAR2(Canvas& canvas, const CWRUtil::Q5& angle, const CWRUtil::Q5& r2) const | ||||
| { | ||||
|     lineToXYAR2(canvas, circleCenterX, circleCenterY, angle, r2); | ||||
| } | ||||
|  | ||||
| void Circle::lineToXYAR2(Canvas& canvas, const CWRUtil::Q5& x, const CWRUtil::Q5& y, const CWRUtil::Q5& angle, const CWRUtil::Q5& r2) const | ||||
| { | ||||
|     canvas.lineTo(x + ((r2 * CWRUtil::sine(angle)) / 2), y - ((r2 * CWRUtil::cosine(angle)) / 2)); | ||||
| } | ||||
|  | ||||
| void Circle::updateMinMaxAR(const CWRUtil::Q5& a, const CWRUtil::Q5& r2, CWRUtil::Q5& xMin, CWRUtil::Q5& xMax, CWRUtil::Q5& yMin, CWRUtil::Q5& yMax) const | ||||
| { | ||||
|     CWRUtil::Q5 xNew = circleCenterX + ((r2 * CWRUtil::sine(a)) / 2); | ||||
|     CWRUtil::Q5 yNew = circleCenterY - ((r2 * CWRUtil::cosine(a)) / 2); | ||||
|     updateMinMaxXY(xNew, yNew, xMin, xMax, yMin, yMax); | ||||
| } | ||||
|  | ||||
| void Circle::updateMinMaxXY(const CWRUtil::Q5& xNew, const CWRUtil::Q5& yNew, CWRUtil::Q5& xMin, CWRUtil::Q5& xMax, CWRUtil::Q5& yMin, CWRUtil::Q5& yMax) const | ||||
| { | ||||
|     if (xNew < xMin) | ||||
|     { | ||||
|         xMin = xNew; | ||||
|     } | ||||
|     if (xNew > xMax) | ||||
|     { | ||||
|         xMax = xNew; | ||||
|     } | ||||
|     if (yNew < yMin) | ||||
|     { | ||||
|         yMin = yNew; | ||||
|     } | ||||
|     if (yNew > yMax) | ||||
|     { | ||||
|         yMax = yNew; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void Circle::calculateMinimalRect(CWRUtil::Q5 arcStart, CWRUtil::Q5 arcEnd, CWRUtil::Q5& xMin, CWRUtil::Q5& xMax, CWRUtil::Q5& yMin, CWRUtil::Q5& yMax) const | ||||
| { | ||||
|     // Put start before end by swapping | ||||
|     if (arcStart > arcEnd) | ||||
|     { | ||||
|         CWRUtil::Q5 tmp = arcStart; | ||||
|         arcStart = arcEnd; | ||||
|         arcEnd = tmp; | ||||
|     } | ||||
|  | ||||
|     CWRUtil::Q5 _90 = CWRUtil::toQ5<int>(90); | ||||
|     CWRUtil::Q5 _360 = CWRUtil::toQ5<int>(360); | ||||
|  | ||||
|     if ((arcEnd - arcStart) >= _360) | ||||
|     { | ||||
|         // The entire circle has to be drawn | ||||
|         arcStart = CWRUtil::toQ5<int>(0); | ||||
|         arcEnd = _360; | ||||
|     } | ||||
|  | ||||
|     // Check start angle | ||||
|     updateMinMaxAR(arcStart, (circleRadius * 2) + circleLineWidth, xMin, xMax, yMin, yMax); | ||||
|     // Here we have a up to 4 approximation steps on angles divisible by 90 | ||||
|     CWRUtil::Q5 i; | ||||
|     for (i = CWRUtil::Q5(ROUNDUP((int)(arcStart + CWRUtil::toQ5<int>(1)), (int)_90)); i <= arcEnd; i = i + _90) | ||||
|     { | ||||
|         updateMinMaxAR(i, (circleRadius * 2) + circleLineWidth, xMin, xMax, yMin, yMax); | ||||
|     } | ||||
|     // Check end angle | ||||
|     if ((i - _90) < arcEnd) | ||||
|     { | ||||
|         updateMinMaxAR(arcEnd, (circleRadius * 2) + circleLineWidth, xMin, xMax, yMin, yMax); | ||||
|     } | ||||
|  | ||||
|     if (circleLineWidth == CWRUtil::toQ5<int>(0)) | ||||
|     { | ||||
|         // A filled circle / pie / pacman | ||||
|         if ((arcEnd - arcStart) < _360) | ||||
|         { | ||||
|             // Not a complete circle, check center | ||||
|             updateMinMaxAR(CWRUtil::toQ5<int>(0), CWRUtil::toQ5<int>(0), xMin, xMax, yMin, yMax); | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // Not a filled circle, check the inside of the circle. Only start and/or end can cause new min/max values | ||||
|         updateMinMaxAR(arcStart, (circleRadius * 2) - circleLineWidth, xMin, xMax, yMin, yMax); | ||||
|         updateMinMaxAR(arcEnd, (circleRadius * 2) - circleLineWidth, xMin, xMax, yMin, yMax); | ||||
|     } | ||||
|  | ||||
|     // Check if circle cap extends the min/max further | ||||
|     if ((circleCapArcIncrement < 180) && (arcEnd - arcStart < _360)) | ||||
|     { | ||||
|         // Round caps | ||||
|         CWRUtil::Q5 capX = circleCenterX + (circleRadius * CWRUtil::sine(arcStart)); | ||||
|         CWRUtil::Q5 capY = circleCenterY - (circleRadius * CWRUtil::cosine(arcStart)); | ||||
|         updateMinMaxXY(capX - (circleLineWidth / 2), capY - (circleLineWidth / 2), xMin, xMax, yMin, yMax); | ||||
|         updateMinMaxXY(capX + (circleLineWidth / 2), capY + (circleLineWidth / 2), xMin, xMax, yMin, yMax); | ||||
|         capX = circleCenterX + (circleRadius * CWRUtil::sine(arcEnd)); | ||||
|         capY = circleCenterY - (circleRadius * CWRUtil::cosine(arcEnd)); | ||||
|         updateMinMaxXY(capX - (circleLineWidth / 2), capY - (circleLineWidth / 2), xMin, xMax, yMin, yMax); | ||||
|         updateMinMaxXY(capX + (circleLineWidth / 2), capY + (circleLineWidth / 2), xMin, xMax, yMin, yMax); | ||||
|     } | ||||
| } | ||||
|  | ||||
| Rect Circle::getMinimalRectForUpdatedStartAngle(const CWRUtil::Q5& startAngleQ5) const | ||||
| { | ||||
|     CWRUtil::Q5 minAngle = CWRUtil::Q5(0); // Unused default value | ||||
|     CWRUtil::Q5 maxAngle = CWRUtil::Q5(0); // Unused default value | ||||
|     int circleArcIncrementQ5int = (int)CWRUtil::toQ5<int>(circleArcIncrement); | ||||
|     if (circleArcAngleStart < circleArcAngleEnd) | ||||
|     { | ||||
|         // start is smaller than end | ||||
|         if (startAngleQ5 < circleArcAngleStart) | ||||
|         { | ||||
|             // start moved even lower | ||||
|             minAngle = startAngleQ5; | ||||
|             maxAngle = CWRUtil::Q5(ROUNDUP((int)circleArcAngleStart, circleArcIncrementQ5int)); | ||||
|             maxAngle = MIN(maxAngle, circleArcAngleEnd); // No need to go higher than end | ||||
|         } | ||||
|         else if (startAngleQ5 < circleArcAngleEnd) | ||||
|         { | ||||
|             // start moved higher, but not higher than end | ||||
|             minAngle = circleArcAngleStart; | ||||
|             maxAngle = CWRUtil::Q5(ROUNDUP((int)startAngleQ5, circleArcIncrementQ5int)); | ||||
|             maxAngle = MIN(maxAngle, circleArcAngleEnd); // No need to go higher than end | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // start moved past end | ||||
|             minAngle = circleArcAngleStart; | ||||
|             maxAngle = startAngleQ5; | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // start is higher than end | ||||
|         if (startAngleQ5 > circleArcAngleStart) | ||||
|         { | ||||
|             // start moved even higher | ||||
|             minAngle = CWRUtil::Q5(ROUNDDOWN((int)circleArcAngleStart, circleArcIncrementQ5int)); | ||||
|             minAngle = MAX(minAngle, circleArcAngleEnd); // No need to go lower then end | ||||
|             maxAngle = startAngleQ5; | ||||
|         } | ||||
|         else if (startAngleQ5 > circleArcAngleEnd) | ||||
|         { | ||||
|             // start moved lower, but not lower than end | ||||
|             minAngle = CWRUtil::Q5(ROUNDDOWN((int)startAngleQ5, circleArcIncrementQ5int)); | ||||
|             minAngle = MAX(minAngle, circleArcAngleEnd); // No need to go lower than end | ||||
|             maxAngle = circleArcAngleStart; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // start moved lower past end | ||||
|             minAngle = startAngleQ5; | ||||
|             maxAngle = circleArcAngleStart; | ||||
|         } | ||||
|     } | ||||
|     return getMinimalRect(minAngle, maxAngle); | ||||
| } | ||||
|  | ||||
| Rect Circle::getMinimalRectForUpdatedEndAngle(const CWRUtil::Q5& endAngleQ5) const | ||||
| { | ||||
|     CWRUtil::Q5 minAngle = CWRUtil::Q5(0); // Unused default value | ||||
|     CWRUtil::Q5 maxAngle = CWRUtil::Q5(0); // Unused default value | ||||
|     int circleArcIncrementQ5int = (int)CWRUtil::toQ5<int>(circleArcIncrement); | ||||
|     if (circleArcAngleStart < circleArcAngleEnd) | ||||
|     { | ||||
|         // start is smaller than end | ||||
|         if (endAngleQ5 > circleArcAngleEnd) | ||||
|         { | ||||
|             // end moved even higher | ||||
|             minAngle = CWRUtil::Q5(ROUNDDOWN((int)circleArcAngleEnd, circleArcIncrementQ5int)); | ||||
|             minAngle = MAX(minAngle, circleArcAngleStart); | ||||
|             maxAngle = endAngleQ5; | ||||
|         } | ||||
|         else if (endAngleQ5 > circleArcAngleStart) | ||||
|         { | ||||
|             // end moved lower, but not past start | ||||
|             minAngle = CWRUtil::Q5(ROUNDDOWN((int)endAngleQ5, circleArcIncrementQ5int)); | ||||
|             minAngle = MAX(minAngle, circleArcAngleStart); // No need to go lower than start | ||||
|             maxAngle = circleArcAngleEnd; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // end move past start | ||||
|             minAngle = endAngleQ5; | ||||
|             maxAngle = circleArcAngleEnd; | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // start is higher than end | ||||
|         if (endAngleQ5 < circleArcAngleEnd) | ||||
|         { | ||||
|             // end moved even lower | ||||
|             minAngle = endAngleQ5; | ||||
|             maxAngle = CWRUtil::Q5(ROUNDUP((int)circleArcAngleEnd, circleArcIncrementQ5int)); | ||||
|             maxAngle = MIN(maxAngle, circleArcAngleStart); // No need to go higher than start | ||||
|         } | ||||
|         else if (endAngleQ5 < circleArcAngleStart) | ||||
|         { | ||||
|             // end moved higher, but not higher than start | ||||
|             minAngle = circleArcAngleEnd; | ||||
|             maxAngle = CWRUtil::Q5(ROUNDUP((int)endAngleQ5, circleArcIncrementQ5int)); | ||||
|             maxAngle = MIN(maxAngle, circleArcAngleStart); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // end moved past start | ||||
|             minAngle = circleArcAngleEnd; | ||||
|             maxAngle = endAngleQ5; | ||||
|         } | ||||
|     } | ||||
|     return getMinimalRect(minAngle, maxAngle); | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,285 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/canvas/Line.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| Line::Line() | ||||
|     : CanvasWidget(), | ||||
|       startX(0), startY(0), endX(0), endY(0), | ||||
|       lineWidth(CWRUtil::toQ5<int>(1)), | ||||
|       lineEnding(BUTT_CAP_ENDING), | ||||
|       lineCapArcIncrement(18) | ||||
| { | ||||
|     Drawable::setWidthHeight(0, 0); | ||||
| } | ||||
|  | ||||
| void Line::setStart(CWRUtil::Q5 xQ5, CWRUtil::Q5 yQ5) | ||||
| { | ||||
|     if (startX == xQ5 && startY == yQ5) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     startX = xQ5; | ||||
|     startY = yQ5; | ||||
|  | ||||
|     updateCachedShape(); | ||||
| } | ||||
|  | ||||
| void Line::updateStart(CWRUtil::Q5 xQ5, CWRUtil::Q5 yQ5) | ||||
| { | ||||
|     if (startX == xQ5 && startY == yQ5) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Rect rectBefore = getMinimalRect(); | ||||
|  | ||||
|     startX = xQ5; | ||||
|     startY = yQ5; | ||||
|  | ||||
|     updateCachedShape(); | ||||
|  | ||||
|     Rect rectAfter = getMinimalRect(); | ||||
|     rectBefore.expandToFit(rectAfter); | ||||
|     invalidateRect(rectBefore); | ||||
| } | ||||
|  | ||||
| void Line::setEnd(CWRUtil::Q5 xQ5, CWRUtil::Q5 yQ5) | ||||
| { | ||||
|     if (endX == xQ5 && endY == yQ5) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     endX = xQ5; | ||||
|     endY = yQ5; | ||||
|  | ||||
|     updateCachedShape(); | ||||
| } | ||||
|  | ||||
| void Line::updateEnd(CWRUtil::Q5 xQ5, CWRUtil::Q5 yQ5) | ||||
| { | ||||
|     if (endX == xQ5 && endY == yQ5) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Rect rectBefore = getMinimalRect(); | ||||
|  | ||||
|     endX = xQ5; | ||||
|     endY = yQ5; | ||||
|  | ||||
|     updateCachedShape(); | ||||
|  | ||||
|     Rect rectAfter = getMinimalRect(); | ||||
|     rectBefore.expandToFit(rectAfter); | ||||
|     invalidateRect(rectBefore); | ||||
| } | ||||
|  | ||||
| void Line::setLineEndingStyle(Line::LINE_ENDING_STYLE lineEndingStyle) | ||||
| { | ||||
|     lineEnding = lineEndingStyle; | ||||
|     updateCachedShape(); | ||||
| } | ||||
|  | ||||
| Line::LINE_ENDING_STYLE Line::getLineEndingStyle() const | ||||
| { | ||||
|     return lineEnding; | ||||
| } | ||||
|  | ||||
| void Line::setCapPrecision(int precision) | ||||
| { | ||||
|     if (precision < 1) | ||||
|     { | ||||
|         precision = 1; | ||||
|     } | ||||
|     if (precision > 180) | ||||
|     { | ||||
|         precision = 180; | ||||
|     } | ||||
|     lineCapArcIncrement = precision; | ||||
| } | ||||
|  | ||||
| bool Line::drawCanvasWidget(const Rect& invalidatedArea) const | ||||
| { | ||||
|     Canvas canvas(this, invalidatedArea); | ||||
|  | ||||
|     CWRUtil::Q5 radius; | ||||
|     int angleInDegrees = CWRUtil::angle(xCorner[0] - startX, yCorner[0] - startY, radius); | ||||
|  | ||||
|     canvas.moveTo(xCorner[0], yCorner[0]); | ||||
|     canvas.lineTo(xCorner[1], yCorner[1]); | ||||
|     if (lineEnding == ROUND_CAP_ENDING) | ||||
|     { | ||||
|         // Fixed 10 steps (steps 0 and 9 are at Corner[1] and [2]) | ||||
|         for (int i = lineCapArcIncrement; i < 180; i += lineCapArcIncrement) | ||||
|         { | ||||
|             canvas.lineTo(endX + radius * CWRUtil::sine(angleInDegrees - i), endY - radius * CWRUtil::cosine(angleInDegrees - i)); | ||||
|         } | ||||
|     } | ||||
|     canvas.lineTo(xCorner[2], yCorner[2]); | ||||
|     canvas.lineTo(xCorner[3], yCorner[3]); | ||||
|     if (lineEnding == ROUND_CAP_ENDING) | ||||
|     { | ||||
|         // Fixed 10 steps (steps 0 and 9 are at Corner[3] and [0]) | ||||
|         for (int i = 180 - lineCapArcIncrement; i > 0; i -= lineCapArcIncrement) | ||||
|         { | ||||
|             canvas.lineTo(startX + radius * CWRUtil::sine(angleInDegrees + i), startY - radius * CWRUtil::cosine(angleInDegrees + i)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return canvas.render(); | ||||
| } | ||||
|  | ||||
| void Line::updateCachedShape() | ||||
| { | ||||
|     CWRUtil::Q5 dx = endX - startX; | ||||
|     CWRUtil::Q5 dy = endY - startY; | ||||
|     CWRUtil::Q5 d = CWRUtil::toQ5<int>(0); | ||||
|     // Look for horizontal, vertical or no-line | ||||
|     if ((int32_t)dx == 0) | ||||
|     { | ||||
|         if ((int32_t)dy == 0) | ||||
|         { | ||||
|             xCorner[0] = xCorner[1] = xCorner[2] = xCorner[3] = startX; | ||||
|             yCorner[0] = yCorner[1] = yCorner[2] = yCorner[3] = startY; | ||||
|             return; | ||||
|         } | ||||
|         d = abs(dy); | ||||
|     } | ||||
|     else if ((int32_t)dy == 0) | ||||
|     { | ||||
|         d = abs(dx); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // We want to hit as close to the limit inside 32bits. | ||||
|         // sqrt(0x7FFFFFFF / 2) = 46340, which is roughly toQ5(1448) | ||||
|         static const int32_t MAXVAL = 46340; | ||||
|         int32_t common_divisor = gcd(abs((int32_t)dx), abs((int32_t)dy)); | ||||
|         // First try to scale down | ||||
|         if (common_divisor != 1) | ||||
|         { | ||||
|             dx = CWRUtil::Q5((int32_t)dx / common_divisor); | ||||
|             dy = CWRUtil::Q5((int32_t)dy / common_divisor); | ||||
|         } | ||||
|         // Neither dx or dy is zero, find the largest multiplier / smallest divisor to stay within limit | ||||
|         if (abs((int32_t)dx) <= MAXVAL || abs((int32_t)dy) <= MAXVAL) | ||||
|         { | ||||
|             // Look for largest multiplier | ||||
|             int32_t mulx = MAXVAL / abs((int32_t)dx); | ||||
|             int32_t muly = MAXVAL / abs((int32_t)dy); | ||||
|             int32_t mult = MIN(mulx, muly); | ||||
|             dx = CWRUtil::Q5(mult * (int32_t)dx); | ||||
|             dy = CWRUtil::Q5(mult * (int32_t)dy); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // Look for smallest divisor | ||||
|             int32_t divx = abs((int32_t)dx) / MAXVAL; | ||||
|             int32_t divy = abs((int32_t)dy) / MAXVAL; | ||||
|             int32_t divi = MAX(divx, divy) + 1; | ||||
|             dx = CWRUtil::Q5((int32_t)dx / divi); | ||||
|             dy = CWRUtil::Q5((int32_t)dy / divi); | ||||
|         } | ||||
|         d = CWRUtil::sqrtQ10(dy * dy + dx * dx); | ||||
|     } | ||||
|  | ||||
|     dy = CWRUtil::muldivQ5(lineWidth, dy, d) / 2; | ||||
|     dx = CWRUtil::muldivQ5(lineWidth, dx, d) / 2; | ||||
|  | ||||
|     switch (lineEnding) | ||||
|     { | ||||
|     case BUTT_CAP_ENDING: | ||||
|         xCorner[0] = startX - dy; | ||||
|         yCorner[0] = startY + dx; | ||||
|         xCorner[1] = endX - dy; | ||||
|         yCorner[1] = endY + dx; | ||||
|         xCorner[2] = endX + dy; | ||||
|         yCorner[2] = endY - dx; | ||||
|         xCorner[3] = startX + dy; | ||||
|         yCorner[3] = startY - dx; | ||||
|         break; | ||||
|     case ROUND_CAP_ENDING: | ||||
|     // Nothing cached, calculated on each draw, but extremes are same as SQUARE_CAP_ENDING, so | ||||
|     // Fall Through (for calculations) | ||||
|     default: | ||||
|     case SQUARE_CAP_ENDING: | ||||
|         xCorner[0] = startX - dy - dx; | ||||
|         yCorner[0] = startY + dx - dy; | ||||
|         xCorner[1] = endX - dy + dx; | ||||
|         yCorner[1] = endY + dx + dy; | ||||
|         xCorner[2] = endX + dy + dx; | ||||
|         yCorner[2] = endY - dx + dy; | ||||
|         xCorner[3] = startX + dy - dx; | ||||
|         yCorner[3] = startY - dx - dy; | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     CWRUtil::Q5 xMin = xCorner[0]; | ||||
|     CWRUtil::Q5 xMax = xCorner[0]; | ||||
|     CWRUtil::Q5 yMin = yCorner[0]; | ||||
|     CWRUtil::Q5 yMax = yCorner[0]; | ||||
|     for (int i = 1; i < 4; i++) | ||||
|     { | ||||
|         if (xCorner[i] < xMin) | ||||
|         { | ||||
|             xMin = xCorner[i]; | ||||
|         } | ||||
|         if (xCorner[i] > xMax) | ||||
|         { | ||||
|             xMax = xCorner[i]; | ||||
|         } | ||||
|         if (yCorner[i] < yMin) | ||||
|         { | ||||
|             yMin = yCorner[i]; | ||||
|         } | ||||
|         if (yCorner[i] > yMax) | ||||
|         { | ||||
|             yMax = yCorner[i]; | ||||
|         } | ||||
|     } | ||||
|     int16_t minX = xMin.to<int>(); | ||||
|     int16_t minY = yMin.to<int>(); | ||||
|     int16_t maxX = xMax.to<int>(); | ||||
|     int16_t maxY = yMax.to<int>(); | ||||
|     minimalRect = Rect(minX, minY, maxX - minX + 1, maxY - minY + 1); | ||||
|  | ||||
|     if (lineEnding == ROUND_CAP_ENDING) | ||||
|     { | ||||
|         xCorner[0] = startX - dy; | ||||
|         yCorner[0] = startY + dx; | ||||
|         xCorner[1] = endX - dy; | ||||
|         yCorner[1] = endY + dx; | ||||
|         xCorner[2] = endX + dy; | ||||
|         yCorner[2] = endY - dx; | ||||
|         xCorner[3] = startX + dy; | ||||
|         yCorner[3] = startY - dx; | ||||
|     } | ||||
| } | ||||
|  | ||||
| Rect Line::getMinimalRect() const | ||||
| { | ||||
|     return minimalRect; | ||||
| } | ||||
|  | ||||
| void Line::updateLengthAndAngle(CWRUtil::Q5 length, CWRUtil::Q5 angle) | ||||
| { | ||||
|     updateEnd(startX + length * CWRUtil::sine(angle), startY - length * CWRUtil::cosine(angle)); | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,68 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <platform/driver/lcd/LCD8bpp_ABGR2222.hpp> | ||||
| #include <touchgfx/Color.hpp> | ||||
| #include <touchgfx/widgets/canvas/PainterABGR2222.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterABGR2222::render(uint8_t* ptr, int x, int xAdjust, int /*y*/, unsigned count, const uint8_t* covers) | ||||
| { | ||||
|     uint8_t* p = ptr + (x + xAdjust); | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     if (totalAlpha == 0xFF) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t alpha = *covers++; | ||||
|             if (alpha == 0xFF) | ||||
|             { | ||||
|                 *p = painterColor; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 *p = mixColors(painterRed, painterGreen, painterBlue, *p, alpha); | ||||
|             } | ||||
|             p++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t alpha = LCD::div255((*covers++) * totalAlpha); | ||||
|             if (alpha == 0xFF) | ||||
|             { | ||||
|                 *p = painterColor; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 *p = mixColors(painterRed, painterGreen, painterBlue, *p, alpha); | ||||
|             } | ||||
|             p++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterABGR2222::renderNext(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha) | ||||
| { | ||||
|     red = painterRed; | ||||
|     green = painterGreen; | ||||
|     blue = painterBlue; | ||||
|     alpha = painterAlpha; | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,139 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/canvas/PainterABGR2222Bitmap.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterABGR2222Bitmap::setBitmap(const Bitmap& bmp) | ||||
| { | ||||
|     bitmap = bmp; | ||||
|     assert((bitmap.getId() == BITMAP_INVALID || bitmap.getFormat() == Bitmap::ABGR2222) && "The chosen painter only works with ABGR2222 bitmaps"); | ||||
|     bitmapRectToFrameBuffer = bitmap.getRect(); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(bitmapRectToFrameBuffer); | ||||
| } | ||||
|  | ||||
| void PainterABGR2222Bitmap::render(uint8_t* ptr, | ||||
|                                    int x, | ||||
|                                    int xAdjust, | ||||
|                                    int y, | ||||
|                                    unsigned count, | ||||
|                                    const uint8_t* covers) | ||||
| { | ||||
|     uint8_t* p = ptr + (x + xAdjust); | ||||
|  | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|  | ||||
|     if (!renderInit()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (currentX + (int)count > bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         count = bitmapRectToFrameBuffer.width - currentX; | ||||
|     } | ||||
|  | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     const uint8_t* src = bitmapABGR2222Pointer; | ||||
|     if (totalAlpha == 0xFF) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t srcAlpha = ((*src) >> 6) * 0x55; | ||||
|             const uint8_t alpha = LCD::div255((*covers++) * srcAlpha); | ||||
|             if (alpha == 0xFF) | ||||
|             { | ||||
|                 // Solid pixel | ||||
|                 *p = *src; | ||||
|             } | ||||
|             else if (alpha) | ||||
|             { | ||||
|                 // Non-Transparent pixel | ||||
|                 *p = mixColors(*src, *p, alpha); | ||||
|             } | ||||
|             p++; | ||||
|             src++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t srcAlpha = ((*src) >> 6) * 0x55; | ||||
|             const uint8_t alpha = LCD::div255((*covers++) * srcAlpha); | ||||
|             if (alpha) // This can never get to max=0xFF*0xFF as totalAlpha<255 | ||||
|             { | ||||
|                 // Non-Transparent pixel | ||||
|                 *p = mixColors(*src, *p, alpha); | ||||
|             } | ||||
|             p++; | ||||
|             src++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterABGR2222Bitmap::renderInit() | ||||
| { | ||||
|     bitmapABGR2222Pointer = 0; | ||||
|  | ||||
|     if (bitmap.getId() == BITMAP_INVALID) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if ((currentX >= bitmapRectToFrameBuffer.width) || (currentY >= bitmapRectToFrameBuffer.height)) | ||||
|     { | ||||
|         // Outside bitmap area, do not draw anything | ||||
|         // Consider the following instead of "return" to get a tiled image: | ||||
|         //   currentX %= bitmapRectToFrameBuffer.width | ||||
|         //   currentY %= bitmapRectToFrameBuffer.height | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (bitmap.getFormat() == Bitmap::ABGR2222) | ||||
|     { | ||||
|         bitmapABGR2222Pointer = bitmap.getData(); | ||||
|         if (!bitmapABGR2222Pointer) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         bitmapABGR2222Pointer += currentX + currentY * bitmapRectToFrameBuffer.width; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool PainterABGR2222Bitmap::renderNext(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha) | ||||
| { | ||||
|     if (currentX >= bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|     else if (bitmapABGR2222Pointer != 0) | ||||
|     { | ||||
|         uint16_t abgr2222 = *bitmapABGR2222Pointer++; | ||||
|         red = LCD8bpp_ABGR2222::getRedFromColor(abgr2222); | ||||
|         green = LCD8bpp_ABGR2222::getGreenFromColor(abgr2222); | ||||
|         blue = LCD8bpp_ABGR2222::getBlueFromColor(abgr2222); | ||||
|         alpha = (abgr2222 >> 6) * 0x55; // To get full range 0-0xFF | ||||
|     } | ||||
|     // Apply given alpha from setAlpha() | ||||
|     alpha = LCD::div255(alpha * painterAlpha); | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,68 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <platform/driver/lcd/LCD8bpp_ARGB2222.hpp> | ||||
| #include <touchgfx/Color.hpp> | ||||
| #include <touchgfx/widgets/canvas/PainterARGB2222.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterARGB2222::render(uint8_t* ptr, int x, int xAdjust, int /*y*/, unsigned count, const uint8_t* covers) | ||||
| { | ||||
|     uint8_t* p = ptr + (x + xAdjust); | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     if (totalAlpha == 0xFF) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t alpha = *covers++; | ||||
|             if (alpha == 0xFF) | ||||
|             { | ||||
|                 *p = painterColor; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 *p = mixColors(painterRed, painterGreen, painterBlue, *p, alpha); | ||||
|             } | ||||
|             p++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t alpha = LCD::div255((*covers++) * totalAlpha); | ||||
|             if (alpha == 0xFF) | ||||
|             { | ||||
|                 *p = painterColor; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 *p = mixColors(painterRed, painterGreen, painterBlue, *p, alpha); | ||||
|             } | ||||
|             p++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterARGB2222::renderNext(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha) | ||||
| { | ||||
|     red = painterRed; | ||||
|     green = painterGreen; | ||||
|     blue = painterBlue; | ||||
|     alpha = painterAlpha; | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,139 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/canvas/PainterARGB2222Bitmap.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterARGB2222Bitmap::setBitmap(const Bitmap& bmp) | ||||
| { | ||||
|     bitmap = bmp; | ||||
|     assert((bitmap.getId() == BITMAP_INVALID || bitmap.getFormat() == Bitmap::ARGB2222) && "The chosen painter only works with ARGB2222 bitmaps"); | ||||
|     bitmapRectToFrameBuffer = bitmap.getRect(); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(bitmapRectToFrameBuffer); | ||||
| } | ||||
|  | ||||
| void PainterARGB2222Bitmap::render(uint8_t* ptr, | ||||
|                                    int x, | ||||
|                                    int xAdjust, | ||||
|                                    int y, | ||||
|                                    unsigned count, | ||||
|                                    const uint8_t* covers) | ||||
| { | ||||
|     uint8_t* p = ptr + (x + xAdjust); | ||||
|  | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|  | ||||
|     if (!renderInit()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (currentX + (int)count > bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         count = bitmapRectToFrameBuffer.width - currentX; | ||||
|     } | ||||
|  | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     const uint8_t* src = bitmapARGB2222Pointer; | ||||
|     if (totalAlpha == 0xFF) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t srcAlpha = ((*src) >> 6) * 0x55; | ||||
|             const uint8_t alpha = LCD::div255((*covers++) * srcAlpha); | ||||
|             if (alpha == 0xFF) | ||||
|             { | ||||
|                 // Solid pixel | ||||
|                 *p = *src; | ||||
|             } | ||||
|             else if (alpha) | ||||
|             { | ||||
|                 // Non-Transparent pixel | ||||
|                 *p = mixColors(*src, *p, alpha); | ||||
|             } | ||||
|             p++; | ||||
|             src++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t srcAlpha = ((*src) >> 6) * 0x55; | ||||
|             const uint8_t alpha = LCD::div255((*covers++) * srcAlpha); | ||||
|             if (alpha) // This can never get to max=0xFF*0xFF as totalAlpha<255 | ||||
|             { | ||||
|                 // Non-Transparent pixel | ||||
|                 *p = mixColors(*src, *p, alpha); | ||||
|             } | ||||
|             p++; | ||||
|             src++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterARGB2222Bitmap::renderInit() | ||||
| { | ||||
|     bitmapARGB2222Pointer = 0; | ||||
|  | ||||
|     if (bitmap.getId() == BITMAP_INVALID) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if ((currentX >= bitmapRectToFrameBuffer.width) || (currentY >= bitmapRectToFrameBuffer.height)) | ||||
|     { | ||||
|         // Outside bitmap area, do not draw anything | ||||
|         // Consider the following instead of "return" to get a tiled image: | ||||
|         //   currentX %= bitmapRectToFrameBuffer.width | ||||
|         //   currentY %= bitmapRectToFrameBuffer.height | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (bitmap.getFormat() == Bitmap::ARGB2222) | ||||
|     { | ||||
|         bitmapARGB2222Pointer = bitmap.getData(); | ||||
|         if (!bitmapARGB2222Pointer) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         bitmapARGB2222Pointer += currentX + currentY * bitmapRectToFrameBuffer.width; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool PainterARGB2222Bitmap::renderNext(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha) | ||||
| { | ||||
|     if (currentX >= bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|     else if (bitmapARGB2222Pointer != 0) | ||||
|     { | ||||
|         uint16_t ARGB2222 = *bitmapARGB2222Pointer++; | ||||
|         red = LCD8bpp_ARGB2222::getRedFromColor(ARGB2222); | ||||
|         green = LCD8bpp_ARGB2222::getGreenFromColor(ARGB2222); | ||||
|         blue = LCD8bpp_ARGB2222::getBlueFromColor(ARGB2222); | ||||
|         alpha = (ARGB2222 >> 6) * 0x55; // To get full range 0-0xFF | ||||
|     } | ||||
|     // Apply given alpha from setAlpha() | ||||
|     alpha = LCD::div255(alpha * painterAlpha); | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,77 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/canvas/PainterARGB8888.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterARGB8888::render(uint8_t* ptr, int x, int xAdjust, int /*y*/, unsigned count, const uint8_t* covers) | ||||
| { | ||||
|     uint8_t* p = reinterpret_cast<uint8_t*>(ptr) + ((x + xAdjust) * 4); | ||||
|     uint8_t pByte; | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     if (totalAlpha == 0xFF) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t alpha = *covers++; | ||||
|             if (alpha == 0xFF) | ||||
|             { | ||||
|                 *p++ = painterBlue; | ||||
|                 *p++ = painterGreen; | ||||
|                 *p++ = painterRed; | ||||
|                 *p++ = 0xff; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 const uint8_t ialpha = 0xFF - alpha; | ||||
|                 pByte = *p; | ||||
|                 *p++ = LCD::div255(painterBlue * alpha + pByte * ialpha); | ||||
|                 pByte = *p; | ||||
|                 *p++ = LCD::div255(painterGreen * alpha + pByte * ialpha); | ||||
|                 pByte = *p; | ||||
|                 *p++ = LCD::div255(painterRed * alpha + pByte * ialpha); | ||||
|                 pByte = *p; | ||||
|                 *p++ = pByte + alpha - LCD::div255(pByte * alpha); | ||||
|             } | ||||
|         } while (--count != 0); | ||||
|     } | ||||
|     else if (totalAlpha != 0) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t alpha = LCD::div255(*covers++ * totalAlpha); | ||||
|             const uint8_t ialpha = 0xFF - alpha; | ||||
|             pByte = *p; | ||||
|             *p++ = LCD::div255(painterBlue * alpha + pByte * ialpha); | ||||
|             pByte = *p; | ||||
|             *p++ = LCD::div255(painterGreen * alpha + pByte * ialpha); | ||||
|             pByte = *p; | ||||
|             *p++ = LCD::div255(painterRed * alpha + pByte * ialpha); | ||||
|             pByte = *p; | ||||
|             *p++ = pByte + alpha - LCD::div255(pByte * alpha); | ||||
|         } while (--count != 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterARGB8888::renderNext(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha) | ||||
| { | ||||
|     red = painterRed; | ||||
|     green = painterGreen; | ||||
|     blue = painterBlue; | ||||
|     alpha = painterAlpha; | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,340 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/canvas/PainterARGB8888Bitmap.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterARGB8888Bitmap::setBitmap(const Bitmap& bmp) | ||||
| { | ||||
|     bitmap = bmp; | ||||
|     assert((bitmap.getId() == BITMAP_INVALID || bitmap.getFormat() == Bitmap::RGB565 || bitmap.getFormat() == Bitmap::RGB888 || bitmap.getFormat() == Bitmap::ARGB8888) && "The chosen painter only works with RGB565, RGB888 and ARGB8888 bitmaps"); | ||||
|     bitmapRectToFrameBuffer = bitmap.getRect(); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(bitmapRectToFrameBuffer); | ||||
| } | ||||
|  | ||||
| void PainterARGB8888Bitmap::render(uint8_t* ptr, int x, int xAdjust, int y, unsigned count, const uint8_t* covers) | ||||
| { | ||||
|     uint8_t* RESTRICT p = ptr + ((x + xAdjust) * 4); | ||||
|  | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|  | ||||
|     if (!renderInit()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (currentX + (int)count > bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         count = bitmapRectToFrameBuffer.width - currentX; | ||||
|     } | ||||
|  | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     if (bitmap.getFormat() == Bitmap::ARGB8888) | ||||
|     { | ||||
|         const uint32_t* RESTRICT src = bitmapARGB8888Pointer; | ||||
|         if (totalAlpha == 0xFF) | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t srcAlpha = (*src) >> 24; | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * srcAlpha); | ||||
|                 if (alpha == 0xFF) | ||||
|                 { | ||||
|                     // Solid pixel | ||||
|                     *p++ = (*src);       // Blue | ||||
|                     *p++ = (*src) >> 8;  // Green | ||||
|                     *p++ = (*src) >> 16; // Red | ||||
|                     *p++ = 0xff;         // Alpha | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     // Non-Transparent pixel | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     uint8_t pByte = *p; | ||||
|                     uint8_t cByte = (*src); | ||||
|                     *p++ = LCD::div255(cByte * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     cByte = (*src) >> 8; | ||||
|                     *p++ = LCD::div255(cByte * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     cByte = (*src) >> 16; | ||||
|                     *p++ = LCD::div255(cByte * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     cByte = (*src) >> 24; | ||||
|                     *p++ = pByte + alpha - LCD::div255(pByte * alpha); | ||||
|                 } | ||||
|                 src++; | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t srcAlpha = (*src) >> 24; | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * LCD::div255(srcAlpha * totalAlpha)); | ||||
|                 if (alpha) | ||||
|                 { | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     uint8_t pByte = *p; | ||||
|                     uint8_t cByte = (*src); | ||||
|                     *p++ = LCD::div255(cByte * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     cByte = (*src) >> 8; | ||||
|                     *p++ = LCD::div255(cByte * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     cByte = (*src) >> 16; | ||||
|                     *p++ = LCD::div255(cByte * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     cByte = (*src) >> 24; | ||||
|                     *p++ = pByte + alpha - LCD::div255(pByte * alpha); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     p += 4; | ||||
|                 } | ||||
|                 src++; | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|     } | ||||
|     else if (bitmap.getFormat() == Bitmap::RGB888) | ||||
|     { | ||||
|         const uint8_t* RESTRICT src = bitmapRGB888Pointer; | ||||
|         if (totalAlpha == 0xFF) | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t alpha = *covers++; | ||||
|                 if (alpha == 0xFF) | ||||
|                 { | ||||
|                     // Solid pixel | ||||
|                     *p++ = *src++; // Blue | ||||
|                     *p++ = *src++; // Green | ||||
|                     *p++ = *src++; // Red | ||||
|                     *p++ = 0xff;   // Alpha | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     // Non-Transparent pixel | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     uint8_t pByte = *p; | ||||
|                     uint8_t cByte = *src++; | ||||
|                     *p++ = LCD::div255(cByte * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     cByte = *src++; | ||||
|                     *p++ = LCD::div255(cByte * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     cByte = *src++; | ||||
|                     *p++ = LCD::div255(cByte * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = pByte + alpha - LCD::div255(pByte * alpha); | ||||
|                 } | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * totalAlpha); | ||||
|                 if (alpha) | ||||
|                 { | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     uint8_t pByte = *p; | ||||
|                     uint8_t cByte = *src++; | ||||
|                     *p++ = LCD::div255(cByte * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     cByte = *src++; | ||||
|                     *p++ = LCD::div255(cByte * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     cByte = *src++; | ||||
|                     *p++ = LCD::div255(cByte * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = pByte + alpha - LCD::div255(pByte * alpha); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     p += 4; | ||||
|                     src += 3; | ||||
|                 } | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|     } | ||||
|     else if (bitmap.getFormat() == Bitmap::RGB565) | ||||
|     { | ||||
|         const uint16_t* RESTRICT src = bitmapRGB565Pointer; | ||||
|         if (totalAlpha == 0xFF) | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t alpha = *covers++; | ||||
|                 const uint16_t srcpix = *src++; | ||||
|                 uint16_t red = (srcpix & 0xF800) >> 11; | ||||
|                 uint16_t green = (srcpix & 0x07E0) >> 5; | ||||
|                 uint16_t blue = srcpix & 0x001F; | ||||
|                 red = (red * 527 + 23) >> 6; | ||||
|                 green = (green * 259 + 33) >> 6; | ||||
|                 blue = (blue * 527 + 23) >> 6; | ||||
|                 if (alpha == 0xFF) | ||||
|                 { | ||||
|                     // Solid pixel | ||||
|                     *p++ = (uint8_t)blue; | ||||
|                     *p++ = (uint8_t)green; | ||||
|                     *p++ = (uint8_t)red; | ||||
|                     *p++ = 0xff; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     // Non-Transparent pixel | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     uint8_t pByte = *p; | ||||
|                     *p++ = LCD::div255(blue * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(green * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(red * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = pByte + alpha - LCD::div255(pByte * alpha); | ||||
|                 } | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * totalAlpha); | ||||
|                 uint16_t srcpix = *src++; | ||||
|                 uint16_t red = (srcpix & 0xF800) >> 11; | ||||
|                 uint16_t green = (srcpix & 0x07E0) >> 5; | ||||
|                 uint16_t blue = srcpix & 0x001F; | ||||
|                 red = (red * 527 + 23) >> 6; | ||||
|                 green = (green * 259 + 33) >> 6; | ||||
|                 blue = (blue * 527 + 23) >> 6; | ||||
|                 if (alpha) | ||||
|                 { | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     uint8_t pByte = *p; | ||||
|                     *p++ = LCD::div255(blue * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(green * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(red * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = pByte + alpha - LCD::div255(pByte * alpha); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     p += 4; | ||||
|                 } | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterARGB8888Bitmap::renderInit() | ||||
| { | ||||
|     bitmapARGB8888Pointer = 0; | ||||
|     bitmapRGB565Pointer = 0; | ||||
|     bitmapRGB888Pointer = 0; | ||||
|  | ||||
|     if (bitmap.getId() == BITMAP_INVALID) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if ((currentX >= bitmapRectToFrameBuffer.width) || (currentY >= bitmapRectToFrameBuffer.height)) | ||||
|     { | ||||
|         // Outside bitmap area, do not draw anything | ||||
|         // Consider the following instead of "return" to get a tiled image: | ||||
|         //   currentX %= bitmapRectToFrameBuffer.width | ||||
|         //   currentY %= bitmapRectToFrameBuffer.height | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (bitmap.getFormat() == Bitmap::ARGB8888) | ||||
|     { | ||||
|         bitmapARGB8888Pointer = (const uint32_t*)bitmap.getData(); | ||||
|         if (!bitmapARGB8888Pointer) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         bitmapARGB8888Pointer += currentX + currentY * bitmapRectToFrameBuffer.width; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     if (bitmap.getFormat() == Bitmap::RGB888) | ||||
|     { | ||||
|         bitmapRGB888Pointer = (const uint8_t*)bitmap.getData(); | ||||
|         if (!bitmapRGB888Pointer) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         bitmapRGB888Pointer += (currentX + currentY * bitmapRectToFrameBuffer.width) * 3; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     if (bitmap.getFormat() == Bitmap::RGB565) | ||||
|     { | ||||
|         bitmapRGB565Pointer = (const uint16_t*)bitmap.getData(); | ||||
|         if (!bitmapRGB565Pointer) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         bitmapRGB565Pointer += currentX + currentY * bitmapRectToFrameBuffer.width; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool PainterARGB8888Bitmap::renderNext(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha) | ||||
| { | ||||
|     if (currentX >= bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (bitmapARGB8888Pointer != 0) | ||||
|     { | ||||
|         uint32_t argb8888 = *bitmapARGB8888Pointer++; | ||||
|         alpha = (argb8888 >> 24) & 0xFF; | ||||
|         red = (argb8888 >> 16) & 0xFF; | ||||
|         green = (argb8888 >> 8) & 0xFF; | ||||
|         blue = (argb8888) & 0xFF; | ||||
|     } | ||||
|     else if (bitmapRGB888Pointer != 0) | ||||
|     { | ||||
|         blue = *bitmapRGB888Pointer++; | ||||
|         green = *bitmapRGB888Pointer++; | ||||
|         red = *bitmapRGB888Pointer++; | ||||
|         alpha = 0xff; | ||||
|     } | ||||
|     else if (bitmapRGB565Pointer != 0) | ||||
|     { | ||||
|         uint16_t srcpix = *bitmapRGB565Pointer++; | ||||
|         red = (srcpix & 0xF800) >> 11; | ||||
|         green = (srcpix & 0x07E0) >> 5; | ||||
|         blue = srcpix & 0x001F; | ||||
|         red = (red * 527 + 23) >> 6; | ||||
|         green = (green * 259 + 33) >> 6; | ||||
|         blue = (blue * 527 + 23) >> 6; | ||||
|         alpha = 0xff; | ||||
|     } | ||||
|  | ||||
|     // Apply given alpha from setAlpha() | ||||
|     alpha = LCD::div255(alpha * painterAlpha); | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,311 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/canvas/PainterARGB8888L8Bitmap.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterARGB8888L8Bitmap::setBitmap(const Bitmap& bmp) | ||||
| { | ||||
|     bitmap = bmp; | ||||
|     assert((bitmap.getId() == BITMAP_INVALID || bitmap.getFormat() == Bitmap::L8) && "The chosen painter only works with L8 bitmaps"); | ||||
|     bitmapRectToFrameBuffer = bitmap.getRect(); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(bitmapRectToFrameBuffer); | ||||
| } | ||||
|  | ||||
| void PainterARGB8888L8Bitmap::render(uint8_t* ptr, int x, int xAdjust, int y, unsigned count, const uint8_t* covers) | ||||
| { | ||||
|     uint8_t* RESTRICT p = ptr + ((x + xAdjust) * 4); | ||||
|  | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|  | ||||
|     if (!renderInit()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (currentX + (int)count > bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         count = bitmapRectToFrameBuffer.width - currentX; | ||||
|     } | ||||
|  | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     switch ((Bitmap::ClutFormat)((const uint16_t*)bitmapExtraPointer)[-2]) | ||||
|     { | ||||
|     case Bitmap::CLUT_FORMAT_L8_RGB565: | ||||
|         if (totalAlpha == 0xFF) | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint16_t srcpix = ((const uint16_t*)bitmapExtraPointer)[*bitmapPointer++]; | ||||
|                 const uint8_t alpha = *covers++; | ||||
|                 uint16_t red = (srcpix & 0xF800) >> 11; | ||||
|                 uint16_t green = (srcpix & 0x07E0) >> 5; | ||||
|                 uint16_t blue = srcpix & 0x001F; | ||||
|                 red = (red * 527 + 23) >> 6; | ||||
|                 green = (green * 259 + 33) >> 6; | ||||
|                 blue = (blue * 527 + 23) >> 6; | ||||
|                 if (alpha == 0xFF) | ||||
|                 { | ||||
|                     // Solid pixel | ||||
|                     *p++ = (uint8_t)blue; | ||||
|                     *p++ = (uint8_t)green; | ||||
|                     *p++ = (uint8_t)red; | ||||
|                     *p++ = 0xff; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     // Non-Transparent pixel | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     uint8_t pByte = *p; | ||||
|                     *p++ = LCD::div255(blue * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(green * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(red * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = pByte + alpha - LCD::div255(pByte * alpha); | ||||
|                 } | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint16_t srcpix = ((const uint16_t*)bitmapExtraPointer)[*bitmapPointer++]; | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * totalAlpha); | ||||
|                 uint16_t red = (srcpix & 0xF800) >> 11; | ||||
|                 uint16_t green = (srcpix & 0x07E0) >> 5; | ||||
|                 uint16_t blue = srcpix & 0x001F; | ||||
|                 red = (red * 527 + 23) >> 6; | ||||
|                 green = (green * 259 + 33) >> 6; | ||||
|                 blue = (blue * 527 + 23) >> 6; | ||||
|                 if (alpha) | ||||
|                 { | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     uint8_t pByte = *p; | ||||
|                     *p++ = LCD::div255(blue * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(green * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(red * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = pByte + alpha - LCD::div255(pByte * alpha); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     p += 4; | ||||
|                 } | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|         break; | ||||
|     case Bitmap::CLUT_FORMAT_L8_RGB888: | ||||
|         if (totalAlpha == 0xFF) | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t* src = bitmapExtraPointer + *bitmapPointer++ * 3; | ||||
|                 const uint8_t alpha = *covers++; | ||||
|                 if (alpha == 0xFF) | ||||
|                 { | ||||
|                     // Solid pixel | ||||
|                     *p++ = *src++; // Blue | ||||
|                     *p++ = *src++; // Green | ||||
|                     *p++ = *src;   // Red | ||||
|                     *p++ = 0xff;   // Alpha | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     // Non-Transparent pixel | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     uint8_t pByte = *p; | ||||
|                     *p++ = LCD::div255(*src++ * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(*src++ * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(*src * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = pByte + alpha - LCD::div255(pByte * alpha); | ||||
|                 } | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * totalAlpha); | ||||
|                 if (alpha) | ||||
|                 { | ||||
|                     const uint8_t* src = bitmapExtraPointer + *bitmapPointer++ * 3; | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     uint8_t pByte = *p; | ||||
|                     *p++ = LCD::div255(*src++ * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(*src++ * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(*src * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = pByte + alpha - LCD::div255(pByte * alpha); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     p += 4; | ||||
|                 } | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|         break; | ||||
|     case Bitmap::CLUT_FORMAT_L8_ARGB8888: | ||||
|         if (totalAlpha == 0xFF) | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 uint32_t src = ((const uint32_t*)bitmapExtraPointer)[*bitmapPointer++]; | ||||
|                 const uint8_t srcAlpha = src >> 24; | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * srcAlpha); | ||||
|                 if (alpha == 0xFF) | ||||
|                 { | ||||
|                     // Solid pixel | ||||
|                     *p++ = src;       // Blue | ||||
|                     *p++ = src >> 8;  // Green | ||||
|                     *p++ = src >> 16; // Red | ||||
|                     *p++ = 0xff;      // Alpha | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     // Non-Transparent pixel | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     uint8_t pByte = *p; | ||||
|                     *p++ = LCD::div255((src & 0xFF) * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(((src >> 8) & 0xFF) * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(((src >> 16) & 0xFF) * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = pByte + alpha - LCD::div255(pByte * alpha); | ||||
|                 } | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 uint32_t src = ((const uint32_t*)bitmapExtraPointer)[*bitmapPointer++]; | ||||
|                 const uint8_t srcAlpha = src >> 24; | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * LCD::div255(srcAlpha * totalAlpha)); | ||||
|                 if (alpha) | ||||
|                 { | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     uint8_t pByte = *p; | ||||
|                     *p++ = LCD::div255((src & 0xFF) * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(((src >> 8) & 0xFF) * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(((src >> 16) & 0xFF) * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = pByte + alpha - LCD::div255(pByte * alpha); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     p += 4; | ||||
|                 } | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterARGB8888L8Bitmap::renderInit() | ||||
| { | ||||
|     bitmapPointer = 0; | ||||
|     bitmapExtraPointer = 0; | ||||
|  | ||||
|     if (bitmap.getId() == BITMAP_INVALID) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if ((currentX >= bitmapRectToFrameBuffer.width) || (currentY >= bitmapRectToFrameBuffer.height)) | ||||
|     { | ||||
|         // Outside bitmap area, do not draw anything | ||||
|         // Consider the following instead of "return" to get a tiled image: | ||||
|         //   currentX %= bitmapRectToFrameBuffer.width | ||||
|         //   currentY %= bitmapRectToFrameBuffer.height | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (bitmap.getFormat() == Bitmap::L8) | ||||
|     { | ||||
|         bitmapPointer = bitmap.getData(); | ||||
|         if (!bitmapPointer) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         bitmapPointer += currentX + currentY * bitmapRectToFrameBuffer.width; | ||||
|         bitmapExtraPointer = bitmap.getExtraData(); | ||||
|         assert((bitmapExtraPointer != 0 && (*(const uint16_t*)bitmapExtraPointer == Bitmap::CLUT_FORMAT_L8_RGB565 || *(const uint16_t*)bitmapExtraPointer == Bitmap::CLUT_FORMAT_L8_RGB888 || *(const uint16_t*)bitmapExtraPointer == Bitmap::CLUT_FORMAT_L8_ARGB8888))); | ||||
|         bitmapExtraPointer += 4; // Skip header | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool PainterARGB8888L8Bitmap::renderNext(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha) | ||||
| { | ||||
|     if (currentX >= bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     switch ((Bitmap::ClutFormat)((const uint16_t*)bitmapExtraPointer)[-2]) | ||||
|     { | ||||
|     case Bitmap::CLUT_FORMAT_L8_RGB565: | ||||
|         { | ||||
|             uint16_t srcpix = ((const uint16_t*)bitmapExtraPointer)[*bitmapPointer++]; | ||||
|             red = (srcpix & 0xF800) >> 11; | ||||
|             green = (srcpix & 0x07E0) >> 5; | ||||
|             blue = srcpix & 0x001F; | ||||
|             red = (red * 527 + 23) >> 6; | ||||
|             green = (green * 259 + 33) >> 6; | ||||
|             blue = (blue * 527 + 23) >> 6; | ||||
|             alpha = 0xff; | ||||
|         } | ||||
|         break; | ||||
|     case Bitmap::CLUT_FORMAT_L8_RGB888: | ||||
|         { | ||||
|             const uint8_t* clut = bitmapExtraPointer + *bitmapPointer++ * 3; | ||||
|             blue = *clut++; | ||||
|             green = *clut++; | ||||
|             red = *clut; | ||||
|             alpha = 0xff; | ||||
|         } | ||||
|         break; | ||||
|     case Bitmap::CLUT_FORMAT_L8_ARGB8888: | ||||
|         { | ||||
|             uint32_t argb8888 = ((const uint32_t*)bitmapExtraPointer)[*bitmapPointer++]; | ||||
|             alpha = (argb8888 >> 24) & 0xFF; | ||||
|             red = (argb8888 >> 16) & 0xFF; | ||||
|             green = (argb8888 >> 8) & 0xFF; | ||||
|             blue = (argb8888) & 0xFF; | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     // Apply given alpha from setAlpha() | ||||
|     alpha = LCD::div255(alpha * painterAlpha); | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,68 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <platform/driver/lcd/LCD8bpp_BGRA2222.hpp> | ||||
| #include <touchgfx/Color.hpp> | ||||
| #include <touchgfx/widgets/canvas/PainterBGRA2222.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterBGRA2222::render(uint8_t* ptr, int x, int xAdjust, int /*y*/, unsigned count, const uint8_t* covers) | ||||
| { | ||||
|     uint8_t* p = ptr + (x + xAdjust); | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     if (totalAlpha == 0xFF) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t alpha = *covers++; | ||||
|             if (alpha == 0xFF) | ||||
|             { | ||||
|                 *p = painterColor; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 *p = mixColors(painterRed, painterGreen, painterBlue, *p, alpha); | ||||
|             } | ||||
|             p++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t alpha = LCD::div255((*covers++) * totalAlpha); | ||||
|             if (alpha == 0xFF) | ||||
|             { | ||||
|                 *p = painterColor; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 *p = mixColors(painterRed, painterGreen, painterBlue, *p, alpha); | ||||
|             } | ||||
|             p++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterBGRA2222::renderNext(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha) | ||||
| { | ||||
|     red = painterRed; | ||||
|     green = painterGreen; | ||||
|     blue = painterBlue; | ||||
|     alpha = painterAlpha; | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,139 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/canvas/PainterBGRA2222Bitmap.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterBGRA2222Bitmap::setBitmap(const Bitmap& bmp) | ||||
| { | ||||
|     bitmap = bmp; | ||||
|     assert((bitmap.getId() == BITMAP_INVALID || bitmap.getFormat() == Bitmap::BGRA2222) && "The chosen painter only works with BGRA2222 bitmaps"); | ||||
|     bitmapRectToFrameBuffer = bitmap.getRect(); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(bitmapRectToFrameBuffer); | ||||
| } | ||||
|  | ||||
| void PainterBGRA2222Bitmap::render(uint8_t* ptr, | ||||
|                                    int x, | ||||
|                                    int xAdjust, | ||||
|                                    int y, | ||||
|                                    unsigned count, | ||||
|                                    const uint8_t* covers) | ||||
| { | ||||
|     uint8_t* p = ptr + (x + xAdjust); | ||||
|  | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|  | ||||
|     if (!renderInit()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (currentX + (int)count > bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         count = bitmapRectToFrameBuffer.width - currentX; | ||||
|     } | ||||
|  | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     const uint8_t* src = bitmapBGRA2222Pointer; | ||||
|     if (totalAlpha == 0xFF) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t srcAlpha = ((*src) & 0x03) * 0x55; | ||||
|             const uint8_t alpha = LCD::div255((*covers++) * srcAlpha); | ||||
|             if (alpha == 0xFF) | ||||
|             { | ||||
|                 // Solid pixel | ||||
|                 *p = *src; | ||||
|             } | ||||
|             else if (alpha) | ||||
|             { | ||||
|                 // Non-Transparent pixel | ||||
|                 *p = mixColors(*src, *p, alpha); | ||||
|             } | ||||
|             p++; | ||||
|             src++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t srcAlpha = ((*src) & 0x03) * 0x55; | ||||
|             const uint8_t alpha = LCD::div255((*covers++) * srcAlpha); | ||||
|             if (alpha) // This can never get to max=0xFF*0xFF as totalAlpha<255 | ||||
|             { | ||||
|                 // Non-Transparent pixel | ||||
|                 *p = mixColors(*src, *p, alpha); | ||||
|             } | ||||
|             p++; | ||||
|             src++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterBGRA2222Bitmap::renderInit() | ||||
| { | ||||
|     bitmapBGRA2222Pointer = 0; | ||||
|  | ||||
|     if (bitmap.getId() == BITMAP_INVALID) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if ((currentX >= bitmapRectToFrameBuffer.width) || (currentY >= bitmapRectToFrameBuffer.height)) | ||||
|     { | ||||
|         // Outside bitmap area, do not draw anything | ||||
|         // Consider the following instead of "return" to get a tiled image: | ||||
|         //   currentX %= bitmapRectToFrameBuffer.width | ||||
|         //   currentY %= bitmapRectToFrameBuffer.height | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (bitmap.getFormat() == Bitmap::BGRA2222) | ||||
|     { | ||||
|         bitmapBGRA2222Pointer = bitmap.getData(); | ||||
|         if (!bitmapBGRA2222Pointer) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         bitmapBGRA2222Pointer += currentX + currentY * bitmapRectToFrameBuffer.width; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool PainterBGRA2222Bitmap::renderNext(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha) | ||||
| { | ||||
|     if (currentX >= bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|     else if (bitmapBGRA2222Pointer != 0) | ||||
|     { | ||||
|         uint16_t BGRA2222 = *bitmapBGRA2222Pointer++; | ||||
|         red = LCD8bpp_BGRA2222::getRedFromColor(BGRA2222); | ||||
|         green = LCD8bpp_BGRA2222::getGreenFromColor(BGRA2222); | ||||
|         blue = LCD8bpp_BGRA2222::getBlueFromColor(BGRA2222); | ||||
|         alpha = (BGRA2222 & 0x03) * 0x55; // To get full range 0-0xFF | ||||
|     } | ||||
|     // Apply given alpha from setAlpha() | ||||
|     alpha = LCD::div255(alpha * painterAlpha); | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,81 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/canvas/PainterBW.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterBW::render(uint8_t* ptr, int x, int xAdjust, int y, unsigned count, const uint8_t* covers) | ||||
| { | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|     x += xAdjust; | ||||
|     unsigned char* p = ptr + (x / 8); | ||||
|  | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     if (totalAlpha == 0xFF) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             if (*covers++ >= 0x80) | ||||
|             { | ||||
|                 unsigned pixel = 1 << (7 - (x % 8)); | ||||
|                 if (!painterColor) | ||||
|                 { | ||||
|                     *p &= ~pixel; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     *p |= pixel; | ||||
|                 } | ||||
|             } | ||||
|             if (((++x) % 8) == 0) | ||||
|             { | ||||
|                 p++; | ||||
|             } | ||||
|             currentX++; | ||||
|         } while (--count); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             if (totalAlpha * *covers++ >= 0xFF * 0x80) | ||||
|             { | ||||
|                 unsigned pixel = 1 << (7 - (x % 8)); | ||||
|                 if (!painterColor) | ||||
|                 { | ||||
|                     *p &= ~pixel; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     *p |= pixel; | ||||
|                 } | ||||
|             } | ||||
|             if (((++x) % 8) == 0) | ||||
|             { | ||||
|                 p++; | ||||
|             } | ||||
|             currentX++; | ||||
|         } while (--count); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterBW::renderNext(uint8_t& color) | ||||
| { | ||||
|     color = painterColor; | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,210 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/canvas/PainterBWBitmap.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterBWBitmap::setBitmap(const Bitmap& bmp) | ||||
| { | ||||
|     bitmap = bmp; | ||||
|     assert((bitmap.getId() == BITMAP_INVALID || bitmap.getFormat() == Bitmap::BW || bitmap.getFormat() == Bitmap::BW_RLE) && "The chosen painter only works with BW and BW_RLE bitmaps"); | ||||
|     bitmapRectToFrameBuffer = bitmap.getRect(); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(bitmapRectToFrameBuffer); | ||||
| } | ||||
|  | ||||
| // Found in LCD1bpp | ||||
| void fillBits(uint8_t* fb, uint16_t startX, uint16_t startY, uint16_t stride, uint32_t count, uint8_t color); | ||||
|  | ||||
| void PainterBWBitmap::render(uint8_t* ptr, int x, int xAdjust, int y, unsigned count, const uint8_t* /*covers*/) | ||||
| { | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|     x += xAdjust; | ||||
|  | ||||
|     if (!renderInit()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (currentX + (int)count > bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         count = bitmapRectToFrameBuffer.width - currentX; | ||||
|     } | ||||
|  | ||||
|     if (bitmap.getFormat() == Bitmap::BW_RLE) | ||||
|     { | ||||
|         while (count) | ||||
|         { | ||||
|             uint32_t length = bw_rle.getLength(); | ||||
|             uint32_t bitsToDraw = MIN(length, (uint32_t)count); | ||||
|  | ||||
|             fillBits(ptr, x, 0, 0 /* not used */, bitsToDraw, bw_rle.getColor()); | ||||
|             x += bitsToDraw; | ||||
|             count -= bitsToDraw; | ||||
|             bw_rle.skipNext(bitsToDraw); | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         unsigned char* p = ptr + (x / 8); | ||||
|         const uint8_t* src = bitmapBWPointer + currentX / 8; | ||||
|         uint8_t* RESTRICT dst = p; | ||||
|         uint16_t srcBitX = currentX % 8; // & 7 | ||||
|         uint16_t dstBitX = x % 8;        // & 7 | ||||
|  | ||||
|         uint16_t remainingBits = count; | ||||
|  | ||||
|         if (dstBitX > 0) | ||||
|         { | ||||
|             // Start by getting (dst-)aligned for faster transfer | ||||
|             uint16_t neededBits = 8 - dstBitX; | ||||
|             if (neededBits > remainingBits) | ||||
|             { | ||||
|                 neededBits = remainingBits; // Very narrow src inside same word | ||||
|             } | ||||
|             const uint16_t availableBits = 8 - srcBitX; | ||||
|             uint8_t mask = (1u << neededBits) - 1u; | ||||
|             const uint8_t dstShift = static_cast<uint8_t>(8u - (dstBitX + neededBits)); | ||||
|             mask <<= dstShift; | ||||
|  | ||||
|             uint8_t word = *src; | ||||
|  | ||||
|             if (availableBits > neededBits) | ||||
|             { | ||||
|                 word >>= availableBits - neededBits; | ||||
|             } | ||||
|             else if (availableBits < neededBits) | ||||
|             { | ||||
|                 // Get the last required bits from src[1] | ||||
|                 word <<= neededBits - availableBits; | ||||
|                 word |= src[1] >> (8u - (neededBits - availableBits)); | ||||
|             } | ||||
|  | ||||
|             word <<= dstShift; | ||||
|             *dst = (*dst & ~mask) | (word & mask); | ||||
|  | ||||
|             srcBitX = (srcBitX + neededBits) % 8; // & 7 | ||||
|  | ||||
|             if (availableBits <= neededBits) | ||||
|             { | ||||
|                 src++; | ||||
|             } | ||||
|             dst++; | ||||
|             remainingBits -= neededBits; | ||||
|         } | ||||
|  | ||||
|         // dstX is now word aligned (or we have transferred everything of a narrow image and remainingBits==0) | ||||
|         if (remainingBits >= 8) | ||||
|         { | ||||
|             uint16_t bytesPerLine = remainingBits / 8; | ||||
|             if (srcBitX == 0) | ||||
|             { | ||||
|                 HAL::getInstance()->blockCopy(dst, src, bytesPerLine); | ||||
|                 src += bytesPerLine; | ||||
|                 dst += bytesPerLine; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 uint16_t _remainingBits = remainingBits; | ||||
|  | ||||
|                 remainingBits = _remainingBits; | ||||
|                 while (remainingBits >= 8) | ||||
|                 { | ||||
|                     uint8_t word = *src++; | ||||
|                     word <<= srcBitX; | ||||
|                     word |= (*src) >> (8 - srcBitX); | ||||
|                     *dst++ = word; | ||||
|                     remainingBits -= 8; | ||||
|                 } | ||||
|             } | ||||
|             remainingBits %= 8; // &= 7 | ||||
|         } | ||||
|  | ||||
|         // Take the last bits, again we need to mask dst | ||||
|         if (remainingBits > 0) | ||||
|         { | ||||
|             uint8_t word = *src; | ||||
|             if (srcBitX != 0) | ||||
|             { | ||||
|                 word <<= srcBitX; | ||||
|                 word |= src[1] >> (8u - srcBitX); | ||||
|             } | ||||
|             const uint8_t mask = ((1u << remainingBits) - 1u) << (8u - remainingBits); | ||||
|             *dst = (*dst & ~mask) | (word & mask); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterBWBitmap::renderInit() | ||||
| { | ||||
|     bw_rle = 0; // Used to remember if format is BW or BW_RLE | ||||
|     bitmapBWPointer = 0; | ||||
|  | ||||
|     if (bitmap.getId() == BITMAP_INVALID) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (currentX >= bitmapRectToFrameBuffer.width || currentY >= bitmapRectToFrameBuffer.height) | ||||
|     { | ||||
|         // Outside bitmap area, do not draw anything | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     // Common for BW and BW_RLE | ||||
|     bitmapBWPointer = (const uint8_t*)bitmap.getData(); | ||||
|     if (!bitmapBWPointer) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (bitmap.getFormat() == Bitmap::BW_RLE) | ||||
|     { | ||||
|         bw_rle.init(bitmapBWPointer); | ||||
|         uint32_t skip = (int32_t)currentY * (int32_t)bitmapRectToFrameBuffer.width + (int32_t)currentX; | ||||
|         bw_rle.skipNext(skip); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     if (bitmap.getFormat() == Bitmap::BW) | ||||
|     { | ||||
|         bitmapBWPointer += currentY * ((bitmapRectToFrameBuffer.width + 7) / 8); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool PainterBWBitmap::renderNext(uint8_t& color) | ||||
| { | ||||
|     if (currentX >= bitmapRectToFrameBuffer.width || bitmapBWPointer == 0) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (bitmap.getFormat() == Bitmap::BW_RLE) | ||||
|     { | ||||
|         color = bw_rle.getColor(); | ||||
|         bw_rle.skipNext(1); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         const uint8_t* src = bitmapBWPointer + currentX / 8; | ||||
|         color = ((*src) >> (7 - (currentX % 8))) & 1; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,75 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <platform/driver/lcd/LCD2bpp.hpp> | ||||
| #include <touchgfx/Color.hpp> | ||||
| #include <touchgfx/widgets/canvas/PainterGRAY2.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterGRAY2::render(uint8_t* ptr, | ||||
|                           int x, | ||||
|                           int xAdjust, | ||||
|                           int y, | ||||
|                           unsigned count, | ||||
|                           const uint8_t* covers) | ||||
| { | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|     x += xAdjust; | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     if (totalAlpha == 0xFF) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t alpha = *covers++; | ||||
|  | ||||
|             if (alpha == 0xFF) | ||||
|             { | ||||
|                 // Render a solid pixel | ||||
|                 LCD2bpp::setPixel(ptr, x, painterGray); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 const uint8_t ialpha = 0xFF - alpha; | ||||
|                 const uint8_t p_gray = LCD2bpp::getPixel(ptr, x); | ||||
|                 LCD2bpp::setPixel(ptr, x, LCD::div255((painterGray * alpha + p_gray * ialpha) * 0x55) >> 6); | ||||
|             } | ||||
|             currentX++; | ||||
|             x++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
|     else if (totalAlpha != 0) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t alpha = LCD::div255((*covers++) * totalAlpha); | ||||
|             const uint8_t ialpha = 0xFF - alpha; | ||||
|  | ||||
|             const uint8_t p_gray = LCD2bpp::getPixel(ptr, x); | ||||
|             LCD2bpp::setPixel(ptr, x, LCD::div255((painterGray * alpha + p_gray * ialpha) * 0x55) >> 6); | ||||
|             currentX++; | ||||
|             x++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterGRAY2::renderNext(uint8_t& gray, uint8_t& alpha) | ||||
| { | ||||
|     gray = painterGray; | ||||
|     alpha = painterAlpha; | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,185 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <platform/driver/lcd/LCD2bpp.hpp> | ||||
| #include <touchgfx/widgets/canvas/PainterGRAY2Bitmap.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterGRAY2Bitmap::setBitmap(const Bitmap& bmp) | ||||
| { | ||||
|     bitmap = bmp; | ||||
|     assert((bitmap.getId() == BITMAP_INVALID || bitmap.getFormat() == Bitmap::GRAY2) && "The chosen painter only works with GRAY2 bitmaps"); | ||||
|     bitmapRectToFrameBuffer = bitmap.getRect(); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(bitmapRectToFrameBuffer); | ||||
| } | ||||
|  | ||||
| void PainterGRAY2Bitmap::render(uint8_t* ptr, int x, int xAdjust, int y, unsigned count, const uint8_t* covers) | ||||
| { | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|     x += xAdjust; | ||||
|  | ||||
|     if (!renderInit()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (currentX + (int)count > bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         count = bitmapRectToFrameBuffer.width - currentX; | ||||
|     } | ||||
|  | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     if (bitmapAlphaPointer) | ||||
|     { | ||||
|         if (totalAlpha == 0xFF) | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t gray = LCD2bpp::getPixel(bitmapGRAY2Pointer, currentX); | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * (LCD2bpp::getPixel(bitmapAlphaPointer, currentX) * 0x55)); | ||||
|  | ||||
|                 if (alpha == 0xFF) | ||||
|                 { | ||||
|                     // Render a solid pixel | ||||
|                     LCD2bpp::setPixel(ptr, x, gray); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     const uint8_t p_gray = LCD2bpp::getPixel(ptr, x); | ||||
|                     LCD2bpp::setPixel(ptr, x, LCD::div255((gray * alpha + p_gray * ialpha) * 0x55) >> 6); | ||||
|                 } | ||||
|                 currentX++; | ||||
|                 x++; | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t gray = LCD2bpp::getPixel(bitmapGRAY2Pointer, currentX); | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * LCD::div255(totalAlpha * (LCD2bpp::getPixel(bitmapAlphaPointer, currentX) * 0x55))); | ||||
|                 const uint8_t ialpha = 0xFF - alpha; | ||||
|  | ||||
|                 const uint8_t p_gray = LCD2bpp::getPixel(ptr, x); | ||||
|                 LCD2bpp::setPixel(ptr, x, LCD::div255((gray * alpha + p_gray * ialpha) * 0x55) >> 6); | ||||
|                 currentX++; | ||||
|                 x++; | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         if (totalAlpha == 255) | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t gray = LCD2bpp::getPixel(bitmapGRAY2Pointer, currentX); | ||||
|                 const uint8_t alpha = *covers++; | ||||
|  | ||||
|                 if (alpha == 255) | ||||
|                 { | ||||
|                     // Render a solid pixel | ||||
|                     LCD2bpp::setPixel(ptr, x, gray); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     const uint8_t p_gray = LCD2bpp::getPixel(ptr, x); | ||||
|                     LCD2bpp::setPixel(ptr, x, LCD::div255((gray * alpha + p_gray * ialpha) * 0x55) >> 6); | ||||
|                 } | ||||
|                 currentX++; | ||||
|                 x++; | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t gray = LCD2bpp::getPixel(bitmapGRAY2Pointer, currentX); | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * totalAlpha); | ||||
|                 const uint8_t ialpha = 0xFF - alpha; | ||||
|  | ||||
|                 const uint8_t p_gray = LCD2bpp::getPixel(ptr, x); | ||||
|                 LCD2bpp::setPixel(ptr, x, LCD::div255((gray * alpha + p_gray * ialpha) * 0x55) >> 6); | ||||
|                 currentX++; | ||||
|                 x++; | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterGRAY2Bitmap::renderInit() | ||||
| { | ||||
|     bitmapGRAY2Pointer = 0; | ||||
|     bitmapAlphaPointer = 0; | ||||
|  | ||||
|     if (bitmap.getId() == BITMAP_INVALID) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if ((currentX >= bitmapRectToFrameBuffer.width) || (currentY >= bitmapRectToFrameBuffer.height)) | ||||
|     { | ||||
|         // Outside bitmap area, do not draw anything | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (bitmap.getFormat() == Bitmap::GRAY2) | ||||
|     { | ||||
|         bitmapGRAY2Pointer = (const uint8_t*)bitmap.getData(); | ||||
|         if (!bitmapGRAY2Pointer) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         bitmapGRAY2Pointer += currentY * ((bitmapRectToFrameBuffer.width + 3) / 4); | ||||
|         bitmapAlphaPointer = (const uint8_t*)bitmap.getExtraData(); | ||||
|         if (bitmapAlphaPointer) | ||||
|         { | ||||
|             bitmapAlphaPointer += currentY * ((bitmapRectToFrameBuffer.width + 3) / 4); | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool PainterGRAY2Bitmap::renderNext(uint8_t& gray, uint8_t& alpha) | ||||
| { | ||||
|     if (currentX >= bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (bitmapGRAY2Pointer != 0) | ||||
|     { | ||||
|         gray = LCD2bpp::getPixel(bitmapGRAY2Pointer, currentX); | ||||
|         if (bitmapAlphaPointer) | ||||
|         { | ||||
|             alpha = LCD2bpp::getPixel(bitmapAlphaPointer, currentX); | ||||
|             alpha *= 0x55; // Upscale from 00-03 to 00-FF | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             alpha = 0xFF; // No alpha per pixel in the image, it is solid | ||||
|         } | ||||
|     } | ||||
|     // Apply given alpha from setAlpha() | ||||
|     alpha = LCD::div255(alpha * painterAlpha); | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,68 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <platform/driver/lcd/LCD4bpp.hpp> | ||||
| #include <touchgfx/Color.hpp> | ||||
| #include <touchgfx/widgets/canvas/PainterGRAY4.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterGRAY4::render(uint8_t* ptr, int x, int xAdjust, int y, unsigned count, const uint8_t* covers) | ||||
| { | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|     x += xAdjust; | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     if (totalAlpha == 0xFF) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t alpha = *covers++; | ||||
|             if (alpha == 0xFF) // max alpha=0xFF on "*covers" and max alpha=0xFF on "widgetAlpha" | ||||
|             { | ||||
|                 // Render a solid pixel | ||||
|                 LCD4bpp::setPixel(ptr, x, painterGray); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 const uint8_t ialpha = 0xFF - alpha; | ||||
|                 const uint8_t p_gray = LCD4bpp::getPixel(ptr, x); | ||||
|                 LCD4bpp::setPixel(ptr, x, LCD::div255((painterGray * alpha + p_gray * ialpha) * 0x11) >> 4); | ||||
|             } | ||||
|             currentX++; | ||||
|             x++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
|     else if (totalAlpha != 0) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t alpha = LCD::div255((*covers++) * totalAlpha); | ||||
|             const uint8_t ialpha = 0xFF - alpha; | ||||
|             const uint8_t p_gray = LCD4bpp::getPixel(ptr, x); | ||||
|             LCD4bpp::setPixel(ptr, x, LCD::div255((painterGray * alpha + p_gray * ialpha) * 0x11) >> 4); | ||||
|             currentX++; | ||||
|             x++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterGRAY4::renderNext(uint8_t& gray, uint8_t& alpha) | ||||
| { | ||||
|     gray = painterGray; | ||||
|     alpha = painterAlpha; | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,186 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <platform/driver/lcd/LCD4bpp.hpp> | ||||
| #include <touchgfx/widgets/canvas/PainterGRAY4Bitmap.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterGRAY4Bitmap::setBitmap(const Bitmap& bmp) | ||||
| { | ||||
|     bitmap = bmp; | ||||
|     assert((bitmap.getId() == BITMAP_INVALID || bitmap.getFormat() == Bitmap::GRAY4) && "The chosen painter only works with GRAY4 bitmaps"); | ||||
|     bitmapRectToFrameBuffer = bitmap.getRect(); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(bitmapRectToFrameBuffer); | ||||
| } | ||||
|  | ||||
| void PainterGRAY4Bitmap::render(uint8_t* ptr, int x, int xAdjust, int y, unsigned count, const uint8_t* covers) | ||||
| { | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|     x += xAdjust; | ||||
|  | ||||
|     if (!renderInit()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (currentX + (int)count > bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         count = bitmapRectToFrameBuffer.width - currentX; | ||||
|     } | ||||
|  | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     // Get alpha data (GRAY4 format) | ||||
|     if (bitmapAlphaPointer) | ||||
|     { | ||||
|         if (totalAlpha == 0xFF) | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t gray = LCD4bpp::getPixel(bitmapGRAY4Pointer, currentX); | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * (LCD4bpp::getPixel(bitmapAlphaPointer, currentX) * 0x11)); | ||||
|  | ||||
|                 if (alpha == 0xFF) | ||||
|                 { | ||||
|                     // Render a solid pixel | ||||
|                     LCD4bpp::setPixel(ptr, x, gray); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     const uint8_t p_gray = LCD4bpp::getPixel(ptr, x); | ||||
|                     LCD4bpp::setPixel(ptr, x, LCD::div255((gray * alpha + p_gray * ialpha) * 0x11) >> 4); | ||||
|                 } | ||||
|                 currentX++; | ||||
|                 x++; | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t gray = LCD4bpp::getPixel(bitmapGRAY4Pointer, currentX); | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * LCD::div255(totalAlpha * (LCD4bpp::getPixel(bitmapAlphaPointer, currentX) * 0x11))); | ||||
|                 const uint8_t ialpha = 0xFF - alpha; | ||||
|  | ||||
|                 const uint8_t p_gray = LCD4bpp::getPixel(ptr, x); | ||||
|                 LCD4bpp::setPixel(ptr, x, LCD::div255((gray * alpha + p_gray * ialpha) * 0x11) >> 4); | ||||
|                 currentX++; | ||||
|                 x++; | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         if (totalAlpha == 255) | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t gray = LCD4bpp::getPixel(bitmapGRAY4Pointer, currentX); | ||||
|                 const uint8_t alpha = *covers++; | ||||
|  | ||||
|                 if (alpha == 0xFF) | ||||
|                 { | ||||
|                     // Render a solid pixel | ||||
|                     LCD4bpp::setPixel(ptr, x, gray); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     const uint8_t p_gray = LCD4bpp::getPixel(ptr, x); | ||||
|                     LCD4bpp::setPixel(ptr, x, LCD::div255((gray * alpha + p_gray * ialpha) * 0x11) >> 4); | ||||
|                 } | ||||
|                 currentX++; | ||||
|                 x++; | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t gray = LCD4bpp::getPixel(bitmapGRAY4Pointer, currentX); | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * totalAlpha); | ||||
|                 const uint8_t ialpha = 0xFF - alpha; | ||||
|  | ||||
|                 const uint8_t p_gray = LCD4bpp::getPixel(ptr, x); | ||||
|                 LCD4bpp::setPixel(ptr, x, LCD::div255((gray * alpha + p_gray * ialpha) * 0x11) >> 4); | ||||
|                 currentX++; | ||||
|                 x++; | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterGRAY4Bitmap::renderInit() | ||||
| { | ||||
|     bitmapGRAY4Pointer = 0; | ||||
|     bitmapAlphaPointer = 0; | ||||
|  | ||||
|     if (bitmap.getId() == BITMAP_INVALID) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if ((currentX >= bitmapRectToFrameBuffer.width) || (currentY >= bitmapRectToFrameBuffer.height)) | ||||
|     { | ||||
|         // Outside bitmap area, do not draw anything | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (bitmap.getFormat() == Bitmap::GRAY4) | ||||
|     { | ||||
|         bitmapGRAY4Pointer = (const uint8_t*)bitmap.getData(); | ||||
|         if (!bitmapGRAY4Pointer) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         bitmapGRAY4Pointer += currentY * ((bitmapRectToFrameBuffer.width + 1) / 2); | ||||
|         bitmapAlphaPointer = (const uint8_t*)bitmap.getExtraData(); | ||||
|         if (bitmapAlphaPointer) | ||||
|         { | ||||
|             bitmapAlphaPointer += currentY * ((bitmapRectToFrameBuffer.width + 1) / 2); | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool PainterGRAY4Bitmap::renderNext(uint8_t& gray, uint8_t& alpha) | ||||
| { | ||||
|     if (currentX >= bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (bitmapGRAY4Pointer != 0) | ||||
|     { | ||||
|         gray = LCD4bpp::getPixel(bitmapGRAY4Pointer, currentX); | ||||
|         if (bitmapAlphaPointer) | ||||
|         { | ||||
|             alpha = LCD4bpp::getPixel(bitmapAlphaPointer, currentX); | ||||
|             alpha |= alpha << 4; // Upscale from 0-15 to 0-255 | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             alpha = 0xFF; // No alpha per pixel in the image, it is solid | ||||
|         } | ||||
|     } | ||||
|     // Apply given alpha from setAlpha() | ||||
|     alpha = LCD::div255(alpha * painterAlpha); | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,67 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/Color.hpp> | ||||
| #include <touchgfx/widgets/canvas/PainterRGB565.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterRGB565::render(uint8_t* ptr, int x, int xAdjust, int /*y*/, unsigned count, const uint8_t* covers) | ||||
| { | ||||
|     uint16_t* p = reinterpret_cast<uint16_t*>(ptr) + (x + xAdjust); | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     if (totalAlpha == 0xFF) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t alpha = *covers++; | ||||
|             if (alpha == 0xFF) | ||||
|             { | ||||
|                 *p = painterColor; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 *p = mixColors(painterRed, painterGreen, painterBlue, *p, alpha); | ||||
|             } | ||||
|             p++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t alpha = LCD::div255((*covers++) * totalAlpha); | ||||
|             if (alpha == 0xFF) | ||||
|             { | ||||
|                 *p = painterColor; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 *p = mixColors(painterRed, painterGreen, painterBlue, *p, alpha); | ||||
|             } | ||||
|             p++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterRGB565::renderNext(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha) | ||||
| { | ||||
|     red = painterRed >> 8; | ||||
|     green = painterGreen >> 3; | ||||
|     blue = painterBlue << 3; | ||||
|     alpha = painterAlpha; | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,258 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/canvas/PainterRGB565Bitmap.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterRGB565Bitmap::setBitmap(const Bitmap& bmp) | ||||
| { | ||||
|     bitmap = bmp; | ||||
|     assert((bitmap.getId() == BITMAP_INVALID || bitmap.getFormat() == Bitmap::RGB565 || bitmap.getFormat() == Bitmap::ARGB8888) && "The chosen painter only works with RGB565 and ARGB8888 bitmaps"); | ||||
|     bitmapRectToFrameBuffer = bitmap.getRect(); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(bitmapRectToFrameBuffer); | ||||
| } | ||||
|  | ||||
| void PainterRGB565Bitmap::render(uint8_t* ptr, int x, int xAdjust, int y, unsigned count, const uint8_t* covers) | ||||
| { | ||||
|     uint16_t* p = reinterpret_cast<uint16_t*>(ptr) + (x + xAdjust); | ||||
|  | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|  | ||||
|     if (!renderInit()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (currentX + (int)count > bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         count = bitmapRectToFrameBuffer.width - currentX; | ||||
|     } | ||||
|  | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     if (bitmap.getFormat() == Bitmap::RGB565) | ||||
|     { | ||||
|         const uint16_t* src = bitmapRGB565Pointer; | ||||
|         const uint8_t* srcAlpha = bitmapAlphaPointer; | ||||
|         if (srcAlpha) | ||||
|         { | ||||
|             if (totalAlpha == 0xFF) | ||||
|             { | ||||
|                 do | ||||
|                 { | ||||
|                     const uint8_t alpha = LCD::div255((*covers++) * (*srcAlpha++)); | ||||
|                     if (alpha == 0xFF) | ||||
|                     { | ||||
|                         // Solid pixel | ||||
|                         *p = *src; | ||||
|                     } | ||||
|                     else if (alpha) | ||||
|                     { | ||||
|                         // Non-Transparent pixel | ||||
|                         *p = mixColors(*src, *p, alpha); | ||||
|                     } | ||||
|                     p++; | ||||
|                     src++; | ||||
|                 } while (--count != 0); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 do | ||||
|                 { | ||||
|                     const uint8_t alpha = LCD::div255((*covers++) * LCD::div255((*srcAlpha++) * totalAlpha)); | ||||
|                     if (alpha) // This can never get to max=0XFF as totalAlpha<0xFF | ||||
|                     { | ||||
|                         // Non-Transparent pixel | ||||
|                         *p = mixColors(*src, *p, alpha); | ||||
|                     } | ||||
|                     p++; | ||||
|                     src++; | ||||
|                 } while (--count != 0); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (totalAlpha == 0xFF) | ||||
|             { | ||||
|                 do | ||||
|                 { | ||||
|                     //use alpha from covers directly | ||||
|                     const uint8_t alpha = *covers++; | ||||
|                     if (alpha == 0xFF) | ||||
|                     { | ||||
|                         // Solid pixel | ||||
|                         *p = *src; | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         // Non-Transparent pixel | ||||
|                         *p = mixColors(*src, *p, alpha); | ||||
|                     } | ||||
|                     p++; | ||||
|                     src++; | ||||
|                 } while (--count != 0); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 do | ||||
|                 { | ||||
|                     const uint8_t alpha = LCD::div255((*covers++) * totalAlpha); | ||||
|  | ||||
|                     *p = mixColors(*src, *p, alpha); | ||||
|  | ||||
|                     p++; | ||||
|                     src++; | ||||
|                 } while (--count != 0); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     else if (bitmap.getFormat() == Bitmap::ARGB8888) | ||||
|     { | ||||
|         const uint32_t* src = bitmapARGB8888Pointer; | ||||
|         if (totalAlpha == 0xFF) | ||||
|         { | ||||
|             uint32_t newpix; | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t srcAlpha = (*src) >> 24; | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * srcAlpha); | ||||
|                 newpix = *src; | ||||
|                 if (alpha == 0xFF) | ||||
|                 { | ||||
|                     // Solid pixel | ||||
|                     *p = ((newpix >> 8) & RMASK) | ((newpix >> 5) & GMASK) | ((newpix >> 3) & BMASK); | ||||
|                 } | ||||
|                 else if (alpha) | ||||
|                 { | ||||
|                     // Non-Transparent pixel | ||||
|                     *p = mixColors((newpix >> 8) & RMASK, (newpix >> 5) & GMASK, (newpix >> 3) & BMASK, *p, alpha); | ||||
|                 } | ||||
|                 p++; | ||||
|                 src++; | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             uint32_t newpix; | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t srcAlpha = (*src) >> 24; | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * LCD::div255(srcAlpha * totalAlpha)); | ||||
|                 if (alpha) | ||||
|                 { | ||||
|                     // Non-Transparent pixel | ||||
|                     newpix = *src; | ||||
|                     *p = mixColors((newpix >> 8) & RMASK, (newpix >> 5) & GMASK, (newpix >> 3) & BMASK, *p, alpha); | ||||
|                 } | ||||
|                 p++; | ||||
|                 src++; | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterRGB565Bitmap::renderInit() | ||||
| { | ||||
|     bitmapARGB8888Pointer = 0; | ||||
|     bitmapRGB565Pointer = 0; | ||||
|     bitmapAlphaPointer = 0; | ||||
|  | ||||
|     if (bitmap.getId() == BITMAP_INVALID) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if ((currentX >= bitmapRectToFrameBuffer.width) || (currentY >= bitmapRectToFrameBuffer.height)) | ||||
|     { | ||||
|         // Outside bitmap area, do not draw anything | ||||
|         // Consider the following instead of "return" to get a tiled image: | ||||
|         //   currentX %= bitmapRectToFrameBuffer.width | ||||
|         //   currentY %= bitmapRectToFrameBuffer.height | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (bitmap.getFormat() == Bitmap::ARGB8888) | ||||
|     { | ||||
|         bitmapARGB8888Pointer = (const uint32_t*)bitmap.getData(); | ||||
|         if (!bitmapARGB8888Pointer) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         bitmapARGB8888Pointer += currentX + currentY * bitmapRectToFrameBuffer.width; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     if (bitmap.getFormat() == Bitmap::RGB565) | ||||
|     { | ||||
|         bitmapRGB565Pointer = (const uint16_t*)bitmap.getData(); | ||||
|         if (!bitmapRGB565Pointer) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         bitmapRGB565Pointer += currentX + currentY * bitmapRectToFrameBuffer.width; | ||||
|         // Get alpha data (RGB565 format) | ||||
|         bitmapAlphaPointer = (const uint8_t*)bitmap.getExtraData(); | ||||
|         if (bitmapAlphaPointer) | ||||
|         { | ||||
|             bitmapAlphaPointer += currentX + currentY * bitmapRectToFrameBuffer.width; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool PainterRGB565Bitmap::renderNext(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha) | ||||
| { | ||||
|     if (currentX >= bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (bitmapARGB8888Pointer != 0) | ||||
|     { | ||||
|         uint32_t argb8888 = *bitmapARGB8888Pointer++; | ||||
|         alpha = (argb8888 >> 24) & 0xFF; | ||||
|         red = (argb8888 >> 16) & 0xF8; | ||||
|         red |= red >> 5; // To get full range 0-0xFF, not just 0-0xF8 | ||||
|         green = (argb8888 >> 8) & 0xFC; | ||||
|         green |= green >> 6; // To get full range 0-0xFF, not just 0-0xFC | ||||
|         blue = argb8888 & 0xF8; | ||||
|         blue |= blue >> 5; // To get full range 0-0xFF, not just 0-0xF8 | ||||
|     } | ||||
|     else if (bitmapRGB565Pointer != 0) | ||||
|     { | ||||
|         uint16_t rgb565 = *bitmapRGB565Pointer++; | ||||
|         red = (rgb565 & RMASK) >> 8; | ||||
|         red |= red >> 5; // To get full range 0-0xFF, not just 0-0xF8 | ||||
|         green = (rgb565 & GMASK) >> 3; | ||||
|         green |= green >> 6; // To get full range 0-0xFF, not just 0-0xFC | ||||
|         blue = (rgb565 & BMASK) << 3; | ||||
|         blue |= blue >> 5; // To get full range 0-0xFF, not just 0-0xF8 | ||||
|         if (bitmapAlphaPointer) | ||||
|         { | ||||
|             alpha = *bitmapAlphaPointer++; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             alpha = 0xFF; // No alpha per pixel in the image, it is solid | ||||
|         } | ||||
|     } | ||||
|     // Apply given alpha from setAlpha() | ||||
|     alpha = LCD::div255(alpha * painterAlpha); | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,257 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/canvas/PainterRGB565L8Bitmap.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterRGB565L8Bitmap::setBitmap(const Bitmap& bmp) | ||||
| { | ||||
|     bitmap = bmp; | ||||
|     assert((bitmap.getId() == BITMAP_INVALID || bitmap.getFormat() == Bitmap::L8) && "The chosen painter only works with appropriate L8 bitmaps"); | ||||
|     bitmapRectToFrameBuffer = bitmap.getRect(); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(bitmapRectToFrameBuffer); | ||||
| } | ||||
|  | ||||
| void PainterRGB565L8Bitmap::render(uint8_t* ptr, int x, int xAdjust, int y, unsigned count, const uint8_t* covers) | ||||
| { | ||||
|     uint16_t* p = reinterpret_cast<uint16_t*>(ptr) + (x + xAdjust); | ||||
|  | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|  | ||||
|     if (!renderInit()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (currentX + (int)count > bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         count = bitmapRectToFrameBuffer.width - currentX; | ||||
|     } | ||||
|  | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     if ((Bitmap::ClutFormat)((const uint16_t*)bitmapExtraPointer)[-2] == Bitmap::CLUT_FORMAT_L8_RGB565) | ||||
|     { | ||||
|         const uint8_t* src = bitmapPointer; | ||||
|         if (totalAlpha == 0xFF) | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 //use alpha from covers directly | ||||
|                 const uint8_t alpha = *covers++; | ||||
|                 if (alpha == 0xFF) | ||||
|                 { | ||||
|                     // Solid pixel | ||||
|                     *p = ((const uint16_t*)bitmapExtraPointer)[*src]; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     // Non-Transparent pixel | ||||
|                     *p = mixColors(((const uint16_t*)bitmapExtraPointer)[*src], *p, alpha); | ||||
|                 } | ||||
|                 p++; | ||||
|                 src++; | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * totalAlpha); | ||||
|                 *p = mixColors(((const uint16_t*)bitmapExtraPointer)[*src], *p, alpha); | ||||
|                 p++; | ||||
|                 src++; | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|     } | ||||
|     else if ((Bitmap::ClutFormat)((const uint16_t*)bitmapExtraPointer)[-2] == Bitmap::CLUT_FORMAT_L8_RGB888) | ||||
|     { | ||||
|         if (totalAlpha == 0xFF) | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t* src = &bitmapExtraPointer[*bitmapPointer++ * 3]; | ||||
|                 // Use alpha from covers directly | ||||
|                 const uint8_t alpha = *covers++; | ||||
|                 const uint8_t blue = *src++; | ||||
|                 const uint8_t green = *src++; | ||||
|                 const uint8_t red = *src; | ||||
|                 if (alpha == 0xFF) | ||||
|                 { | ||||
|                     // Solid pixel | ||||
|                     *p++ = ((red << 8) & RMASK) | ((green << 3) & GMASK) | ((blue >> 3) & BMASK); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     const uint16_t bufpix = *p; | ||||
|                     uint8_t fbr = (bufpix & RMASK) >> 11; | ||||
|                     uint8_t fbg = (bufpix & GMASK) >> 5; | ||||
|                     uint8_t fbb = bufpix & BMASK; | ||||
|                     fbr = (fbr * 527 + 23) >> 6; | ||||
|                     fbg = (fbg * 259 + 33) >> 6; | ||||
|                     fbb = (fbb * 527 + 23) >> 6; | ||||
|                     *p++ = ((LCD::div255(red * alpha + fbr * ialpha) << 8) & RMASK) | ((LCD::div255(green * alpha + fbg * ialpha) << 3) & GMASK) | ((LCD::div255(blue * alpha + fbb * ialpha) >> 3) & BMASK); | ||||
|                 } | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t* src = &bitmapExtraPointer[*bitmapPointer++ * 3]; | ||||
|                 const uint8_t blue = *src++; | ||||
|                 const uint8_t green = *src++; | ||||
|                 const uint8_t red = *src; | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * totalAlpha); | ||||
|                 const uint8_t ialpha = 0xFF - alpha; | ||||
|                 const uint16_t bufpix = *p; | ||||
|                 uint8_t fbr = (bufpix & RMASK) >> 11; | ||||
|                 uint8_t fbg = (bufpix & GMASK) >> 5; | ||||
|                 uint8_t fbb = bufpix & BMASK; | ||||
|                 fbr = (fbr * 527 + 23) >> 6; | ||||
|                 fbg = (fbg * 259 + 33) >> 6; | ||||
|                 fbb = (fbb * 527 + 23) >> 6; | ||||
|                 *p++ = ((LCD::div255(red * alpha + fbr * ialpha) << 8) & RMASK) | ((LCD::div255(green * alpha + fbg * ialpha) << 3) & GMASK) | ((LCD::div255(blue * alpha + fbb * ialpha) >> 3) & BMASK); | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|     } | ||||
|     else // Bitmap::CLUT_FORMAT_L8_ARGB8888 | ||||
|     { | ||||
|         const uint8_t* src = bitmapPointer; | ||||
|         if (totalAlpha == 0xFF) | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint32_t newpix = ((const uint32_t*)bitmapExtraPointer)[*src]; | ||||
|                 const uint8_t srcAlpha = newpix >> 24; | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * srcAlpha); | ||||
|                 if (alpha == 0xFF) | ||||
|                 { | ||||
|                     // Solid pixel | ||||
|                     *p = ((newpix >> 8) & RMASK) | ((newpix >> 5) & GMASK) | ((newpix >> 3) & BMASK); | ||||
|                 } | ||||
|                 else if (alpha) | ||||
|                 { | ||||
|                     // Non-Transparent pixel | ||||
|                     *p = mixColors((newpix >> 8) & RMASK, (newpix >> 5) & GMASK, (newpix >> 3) & BMASK, *p, alpha); | ||||
|                 } | ||||
|                 p++; | ||||
|                 src++; | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint32_t newpix = ((const uint32_t*)bitmapExtraPointer)[*src]; | ||||
|                 const uint8_t srcAlpha = newpix >> 24; | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * LCD::div255(srcAlpha * totalAlpha)); | ||||
|                 if (alpha) | ||||
|                 { | ||||
|                     // Non-Transparent pixel | ||||
|                     *p = mixColors((newpix >> 8) & RMASK, (newpix >> 5) & GMASK, (newpix >> 3) & BMASK, *p, alpha); | ||||
|                 } | ||||
|                 p++; | ||||
|                 src++; | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterRGB565L8Bitmap::renderInit() | ||||
| { | ||||
|     bitmapPointer = 0; | ||||
|     bitmapExtraPointer = 0; | ||||
|  | ||||
|     if (bitmap.getId() == BITMAP_INVALID) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if ((currentX >= bitmapRectToFrameBuffer.width) || (currentY >= bitmapRectToFrameBuffer.height)) | ||||
|     { | ||||
|         // Outside bitmap area, do not draw anything | ||||
|         // Consider the following instead of "return" to get a tiled image: | ||||
|         //   currentX %= bitmapRectToFrameBuffer.width | ||||
|         //   currentY %= bitmapRectToFrameBuffer.height | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (bitmap.getFormat() == Bitmap::L8) | ||||
|     { | ||||
|         bitmapPointer = bitmap.getData(); | ||||
|         if (!bitmapPointer) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         bitmapPointer += currentX + currentY * bitmapRectToFrameBuffer.width; | ||||
|         bitmapExtraPointer = bitmap.getExtraData(); | ||||
|         assert((bitmapExtraPointer != 0 && (*(const uint16_t*)bitmapExtraPointer == Bitmap::CLUT_FORMAT_L8_RGB565 || *(const uint16_t*)bitmapExtraPointer == Bitmap::CLUT_FORMAT_L8_ARGB8888 || *(const uint16_t*)bitmapExtraPointer == Bitmap::CLUT_FORMAT_L8_RGB888))); | ||||
|         bitmapExtraPointer += 4; // Skip header | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool PainterRGB565L8Bitmap::renderNext(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha) | ||||
| { | ||||
|     if (currentX >= bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if ((Bitmap::ClutFormat)((const uint16_t*)bitmapExtraPointer)[-2] == Bitmap::CLUT_FORMAT_L8_RGB565) | ||||
|     { | ||||
|         const uint16_t* clut = ((const uint16_t*)bitmapExtraPointer) + (*bitmapPointer++); | ||||
|         uint16_t rgb565 = *clut; | ||||
|         red = (rgb565 & RMASK) >> 8; | ||||
|         red |= red >> 5; // To get full range 0-0xFF, not just 0-0xF8 | ||||
|         green = (rgb565 & GMASK) >> 3; | ||||
|         green |= green >> 6; // To get full range 0-0xFF, not just 0-0xFC | ||||
|         blue = (rgb565 & BMASK) << 3; | ||||
|         blue |= (blue >> 5); // To get full range 0-0xFF, not just 0-0xF8 | ||||
|         alpha = 0xFF; | ||||
|     } | ||||
|     else if ((Bitmap::ClutFormat)((const uint16_t*)bitmapExtraPointer)[-2] == Bitmap::CLUT_FORMAT_L8_RGB888) | ||||
|     { | ||||
|         const uint8_t* clut = ((const uint8_t*)bitmapExtraPointer) + (*bitmapPointer++) * 3; | ||||
|         alpha = 0xFF; | ||||
|         blue = *clut++ & 0xF8; | ||||
|         blue |= (blue >> 5); // To get full range 0-0xFF, not just 0-0xF8 | ||||
|         green = *clut++ & 0xFC; | ||||
|         green |= green >> 6; // To get full range 0-0xFF, not just 0-0xFC | ||||
|         red = *clut & 0xF8; | ||||
|         red |= red >> 5; // To get full range 0-0xFF, not just 0-0xF8 | ||||
|     } | ||||
|     else // Bitmap::CLUT_FORMAT_L8_ARGB8888 | ||||
|     { | ||||
|         const uint32_t* clut = ((const uint32_t*)bitmapExtraPointer) + (*bitmapPointer++); | ||||
|         uint32_t argb8888 = *(const uint32_t*)clut; | ||||
|         alpha = (argb8888 >> 24) & 0xFF; | ||||
|         red = (argb8888 >> 16) & 0xF8; | ||||
|         red |= red >> 5; // To get full range 0-0xFF, not just 0-0xF8 | ||||
|         green = (argb8888 >> 8) & 0xFC; | ||||
|         green |= green >> 6; // To get full range 0-0xFF, not just 0-0xFC | ||||
|         blue = argb8888 & 0xF8; | ||||
|         blue |= (blue >> 5); // To get full range 0-0xFF, not just 0-0xF8 | ||||
|     } | ||||
|     // Apply given alpha from setAlpha() | ||||
|     alpha = LCD::div255(alpha * painterAlpha); | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,72 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/canvas/PainterRGB888.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterRGB888::render(uint8_t* ptr, int x, int xAdjust, int /*y*/, unsigned count, const uint8_t* covers) | ||||
| { | ||||
|     uint8_t* p = reinterpret_cast<uint8_t*>(ptr) + ((x + xAdjust) * 3); | ||||
|     uint8_t pByte; | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     if (totalAlpha == 0xFF) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t alpha = *covers++; | ||||
|             if (alpha == 0xFF) | ||||
|             { | ||||
|                 *p++ = painterBlue; | ||||
|                 *p++ = painterGreen; | ||||
|                 *p++ = painterRed; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 const uint8_t ialpha = 0xFF - alpha; | ||||
|                 pByte = *p; | ||||
|                 *p++ = LCD::div255(painterBlue * alpha + pByte * ialpha); | ||||
|                 pByte = *p; | ||||
|                 *p++ = LCD::div255(painterGreen * alpha + pByte * ialpha); | ||||
|                 pByte = *p; | ||||
|                 *p++ = LCD::div255(painterRed * alpha + pByte * ialpha); | ||||
|             } | ||||
|         } while (--count != 0); | ||||
|     } | ||||
|     else if (totalAlpha != 0) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t alpha = LCD::div255(*covers++ * totalAlpha); | ||||
|             const uint8_t ialpha = 0xFF - alpha; | ||||
|             pByte = *p; | ||||
|             *p++ = LCD::div255(painterBlue * alpha + pByte * ialpha); | ||||
|             pByte = *p; | ||||
|             *p++ = LCD::div255(painterGreen * alpha + pByte * ialpha); | ||||
|             pByte = *p; | ||||
|             *p++ = LCD::div255(painterRed * alpha + pByte * ialpha); | ||||
|         } while (--count != 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterRGB888::renderNext(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha) | ||||
| { | ||||
|     red = painterRed; | ||||
|     green = painterGreen; | ||||
|     blue = painterBlue; | ||||
|     alpha = painterAlpha; | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,222 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/canvas/PainterRGB888Bitmap.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterRGB888Bitmap::setBitmap(const Bitmap& bmp) | ||||
| { | ||||
|     bitmap = bmp; | ||||
|     assert((bitmap.getId() == BITMAP_INVALID || bitmap.getFormat() == Bitmap::RGB888 || bitmap.getFormat() == Bitmap::ARGB8888) && "The chosen painter only works with RGB888 and ARGB8888 bitmaps"); | ||||
|     bitmapRectToFrameBuffer = bitmap.getRect(); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(bitmapRectToFrameBuffer); | ||||
| } | ||||
|  | ||||
| void PainterRGB888Bitmap::render(uint8_t* ptr, int x, int xAdjust, int y, unsigned count, const uint8_t* covers) | ||||
| { | ||||
|     uint8_t* p = ptr + ((x + xAdjust) * 3); | ||||
|  | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|  | ||||
|     if (!renderInit()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (currentX + (int)count > bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         count = bitmapRectToFrameBuffer.width - currentX; | ||||
|     } | ||||
|  | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     if (bitmapRGB888Pointer) | ||||
|     { | ||||
|         const uint8_t* src = bitmapRGB888Pointer; | ||||
|         uint8_t pByte; | ||||
|         if (totalAlpha == 0xFF) | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 // Use alpha from covers directly | ||||
|                 const uint8_t alpha = *covers++; | ||||
|                 if (alpha == 0xFF) | ||||
|                 { | ||||
|                     // Solid pixel | ||||
|                     *p++ = *src++; | ||||
|                     *p++ = *src++; | ||||
|                     *p++ = *src++; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(*src++ * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(*src++ * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(*src++ * alpha + pByte * ialpha); | ||||
|                 } | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * totalAlpha); | ||||
|                 const uint8_t ialpha = 0xFF - alpha; | ||||
|                 pByte = *p; | ||||
|                 *p++ = LCD::div255(*src++ * alpha + pByte * ialpha); | ||||
|                 pByte = *p; | ||||
|                 *p++ = LCD::div255(*src++ * alpha + pByte * ialpha); | ||||
|                 pByte = *p; | ||||
|                 *p++ = LCD::div255(*src++ * alpha + pByte * ialpha); | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|     } | ||||
|     else if (bitmapARGB8888Pointer) | ||||
|     { | ||||
|         const uint32_t* src = bitmapARGB8888Pointer; | ||||
|         if (totalAlpha == 0xFF) | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t srcAlpha = (*src) >> 24; | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * srcAlpha); | ||||
|                 if (alpha == 0xFF) | ||||
|                 { | ||||
|                     // Solid pixel | ||||
|                     *p++ = (*src);       // Blue | ||||
|                     *p++ = (*src) >> 8;  // Green | ||||
|                     *p++ = (*src) >> 16; // Red | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     // Non-Transparent pixel | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     uint8_t pByte = *p; | ||||
|                     uint8_t cByte = (*src); | ||||
|                     *p++ = LCD::div255(cByte * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     cByte = (*src) >> 8; | ||||
|                     *p++ = LCD::div255(cByte * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     cByte = (*src) >> 16; | ||||
|                     *p++ = LCD::div255(cByte * alpha + pByte * ialpha); | ||||
|                 } | ||||
|                 src++; | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t srcAlpha = (*src) >> 24; | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * LCD::div255(srcAlpha * totalAlpha)); | ||||
|                 if (alpha) | ||||
|                 { | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     uint8_t pByte = *p; | ||||
|                     uint8_t cByte = (*src); | ||||
|                     *p++ = LCD::div255(cByte * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     cByte = (*src) >> 8; | ||||
|                     *p++ = LCD::div255(cByte * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     cByte = (*src) >> 16; | ||||
|                     *p++ = LCD::div255(cByte * alpha + pByte * ialpha); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     p += 3; | ||||
|                 } | ||||
|                 src++; | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterRGB888Bitmap::renderInit() | ||||
| { | ||||
|     bitmapARGB8888Pointer = 0; | ||||
|     bitmapRGB888Pointer = 0; | ||||
|  | ||||
|     if (bitmap.getId() == BITMAP_INVALID) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if ((currentX >= bitmapRectToFrameBuffer.width) || (currentY >= bitmapRectToFrameBuffer.height)) | ||||
|     { | ||||
|         // Outside bitmap area, do not draw anything | ||||
|         // Consider the following instead of "return" to get a tiled image: | ||||
|         //   currentX %= bitmapRectToFrameBuffer.width | ||||
|         //   currentY %= bitmapRectToFrameBuffer.height | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (bitmap.getFormat() == Bitmap::ARGB8888) | ||||
|     { | ||||
|         bitmapARGB8888Pointer = (const uint32_t*)bitmap.getData(); | ||||
|         if (!bitmapARGB8888Pointer) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         bitmapARGB8888Pointer += currentX + currentY * bitmapRectToFrameBuffer.width; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     if (bitmap.getFormat() == Bitmap::RGB888) | ||||
|     { | ||||
|         bitmapRGB888Pointer = bitmap.getData(); | ||||
|         if (!bitmapRGB888Pointer) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         bitmapRGB888Pointer += (currentX + currentY * bitmapRectToFrameBuffer.width) * 3; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool PainterRGB888Bitmap::renderNext(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha) | ||||
| { | ||||
|     if (currentX >= bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (bitmapARGB8888Pointer != 0) | ||||
|     { | ||||
|         uint32_t argb8888 = *bitmapARGB8888Pointer++; | ||||
|         alpha = (argb8888 >> 24) & 0xFF; | ||||
|         red = (argb8888 >> 16) & 0xFF; | ||||
|         green = (argb8888 >> 8) & 0xFF; | ||||
|         blue = (argb8888) & 0xFF; | ||||
|     } | ||||
|     else if (bitmapRGB888Pointer != 0) | ||||
|     { | ||||
|         blue = *bitmapRGB888Pointer++; | ||||
|         green = *bitmapRGB888Pointer++; | ||||
|         red = *bitmapRGB888Pointer++; | ||||
|         alpha = 0xFF; // No alpha per pixel in the image, it is solid | ||||
|     } | ||||
|     // Apply given alpha from setAlpha() | ||||
|     alpha = LCD::div255(alpha * painterAlpha); | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,209 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/canvas/PainterRGB888L8Bitmap.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterRGB888L8Bitmap::setBitmap(const Bitmap& bmp) | ||||
| { | ||||
|     bitmap = bmp; | ||||
|     assert((bitmap.getId() == BITMAP_INVALID || bitmap.getFormat() == Bitmap::L8) && "The chosen painter only works with appropriate L8 bitmaps"); | ||||
|     bitmapRectToFrameBuffer = bitmap.getRect(); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(bitmapRectToFrameBuffer); | ||||
| } | ||||
|  | ||||
| void PainterRGB888L8Bitmap::render(uint8_t* ptr, int x, int xAdjust, int y, unsigned count, const uint8_t* covers) | ||||
| { | ||||
|     uint8_t* p = ptr + ((x + xAdjust) * 3); | ||||
|  | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|  | ||||
|     if (!renderInit()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (currentX + (int)count > bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         count = bitmapRectToFrameBuffer.width - currentX; | ||||
|     } | ||||
|  | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     if ((Bitmap::ClutFormat)((const uint16_t*)bitmapExtraPointer)[-2] == Bitmap::CLUT_FORMAT_L8_RGB888) | ||||
|     { | ||||
|         if (totalAlpha == 0xFF) | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t* src = &bitmapExtraPointer[*bitmapPointer++ * 3]; | ||||
|                 // Use alpha from covers directly | ||||
|                 const uint8_t alpha = *covers++; | ||||
|                 if (alpha == 0xFF) | ||||
|                 { | ||||
|                     // Solid pixel | ||||
|                     *p++ = *src++; | ||||
|                     *p++ = *src++; | ||||
|                     *p++ = *src; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     uint8_t pByte = *p; | ||||
|                     *p++ = LCD::div255(*src++ * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(*src++ * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(*src * alpha + pByte * ialpha); | ||||
|                 } | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 const uint8_t* src = &bitmapExtraPointer[*bitmapPointer++ * 3]; | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * totalAlpha); | ||||
|                 const uint8_t ialpha = 0xFF - alpha; | ||||
|                 uint8_t pByte = *p; | ||||
|                 *p++ = LCD::div255(*src++ * alpha + pByte * ialpha); | ||||
|                 pByte = *p; | ||||
|                 *p++ = LCD::div255(*src++ * alpha + pByte * ialpha); | ||||
|                 pByte = *p; | ||||
|                 *p++ = LCD::div255(*src * alpha + pByte * ialpha); | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|     } | ||||
|     else // Bitmap::CLUT_FORMAT_L8_ARGB8888 | ||||
|     { | ||||
|         if (totalAlpha == 0xFF) | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 uint32_t src = ((const uint32_t*)bitmapExtraPointer)[*bitmapPointer++]; | ||||
|                 const uint8_t srcAlpha = src >> 24; | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * srcAlpha); | ||||
|                 if (alpha == 0xFF) | ||||
|                 { | ||||
|                     // Solid pixel | ||||
|                     *p++ = src;       // Blue | ||||
|                     *p++ = src >> 8;  // Green | ||||
|                     *p++ = src >> 16; // Red | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     // Non-Transparent pixel | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     uint8_t pByte = *p; | ||||
|                     *p++ = LCD::div255((src & 0xFF) * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(((src >> 8) & 0xFF) * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(((src >> 16) & 0xFF) * alpha + pByte * ialpha); | ||||
|                 } | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             do | ||||
|             { | ||||
|                 uint32_t src = ((const uint32_t*)bitmapExtraPointer)[*bitmapPointer++]; | ||||
|                 const uint8_t srcAlpha = src >> 24; | ||||
|                 const uint8_t alpha = LCD::div255((*covers++) * LCD::div255(srcAlpha * totalAlpha)); | ||||
|                 if (alpha) | ||||
|                 { | ||||
|                     const uint8_t ialpha = 0xFF - alpha; | ||||
|                     uint8_t pByte = *p; | ||||
|                     *p++ = LCD::div255((src & 0xFF) * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(((src >> 8) & 0xFF) * alpha + pByte * ialpha); | ||||
|                     pByte = *p; | ||||
|                     *p++ = LCD::div255(((src >> 16) & 0xFF) * alpha + pByte * ialpha); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     p += 3; | ||||
|                 } | ||||
|             } while (--count != 0); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterRGB888L8Bitmap::renderInit() | ||||
| { | ||||
|     bitmapPointer = 0; | ||||
|     bitmapExtraPointer = 0; | ||||
|  | ||||
|     if (bitmap.getId() == BITMAP_INVALID) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if ((currentX >= bitmapRectToFrameBuffer.width) || (currentY >= bitmapRectToFrameBuffer.height)) | ||||
|     { | ||||
|         // Outside bitmap area, do not draw anything | ||||
|         // Consider the following instead of "return" to get a tiled image: | ||||
|         //   currentX %= bitmapRectToFrameBuffer.width | ||||
|         //   currentY %= bitmapRectToFrameBuffer.height | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (bitmap.getFormat() == Bitmap::L8) | ||||
|     { | ||||
|         bitmapPointer = bitmap.getData(); | ||||
|         if (!bitmapPointer) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         bitmapPointer += currentX + currentY * bitmapRectToFrameBuffer.width; | ||||
|         bitmapExtraPointer = bitmap.getExtraData(); | ||||
|         assert((bitmapExtraPointer != 0 && (*(const uint16_t*)bitmapExtraPointer == Bitmap::CLUT_FORMAT_L8_RGB888 || *(const uint16_t*)bitmapExtraPointer == Bitmap::CLUT_FORMAT_L8_ARGB8888))); | ||||
|         bitmapExtraPointer += 4; // Skip header | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool PainterRGB888L8Bitmap::renderNext(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha) | ||||
| { | ||||
|     if (currentX >= bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if ((Bitmap::ClutFormat)((const uint16_t*)bitmapExtraPointer)[-2] == Bitmap::CLUT_FORMAT_L8_RGB888) | ||||
|     { | ||||
|         const uint8_t* clut = bitmapExtraPointer + (*bitmapPointer++) * 3; | ||||
|         blue = *clut++; | ||||
|         green = *clut++; | ||||
|         red = *clut; | ||||
|         alpha = 0xFF; // No alpha per pixel in the image, it is solid | ||||
|     } | ||||
|     else // L8_ARGB8888 | ||||
|     { | ||||
|         const uint32_t* clut = ((const uint32_t*)bitmapExtraPointer) + (*bitmapPointer++); | ||||
|         uint32_t argb8888 = *(const uint32_t*)clut; | ||||
|         alpha = (argb8888 >> 24) & 0xFF; | ||||
|         red = (argb8888 >> 16) & 0xFF; | ||||
|         green = (argb8888 >> 8) & 0xFF; | ||||
|         blue = (argb8888) & 0xFF; | ||||
|     } | ||||
|     // Apply given alpha from setAlpha() | ||||
|     alpha = LCD::div255(alpha * painterAlpha); | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,68 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <platform/driver/lcd/LCD8bpp_RGBA2222.hpp> | ||||
| #include <touchgfx/Color.hpp> | ||||
| #include <touchgfx/widgets/canvas/PainterRGBA2222.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterRGBA2222::render(uint8_t* ptr, int x, int xAdjust, int /*y*/, unsigned count, const uint8_t* covers) | ||||
| { | ||||
|     uint8_t* p = ptr + (x + xAdjust); | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     if (totalAlpha == 0xFF) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t alpha = *covers++; | ||||
|             if (alpha == 0xFF) | ||||
|             { | ||||
|                 *p = painterColor; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 *p = mixColors(painterRed, painterGreen, painterBlue, *p, alpha); | ||||
|             } | ||||
|             p++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t alpha = LCD::div255((*covers++) * totalAlpha); | ||||
|             if (alpha == 0xFF) | ||||
|             { | ||||
|                 *p = painterColor; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 *p = mixColors(painterRed, painterGreen, painterBlue, *p, alpha); | ||||
|             } | ||||
|             p++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterRGBA2222::renderNext(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha) | ||||
| { | ||||
|     red = painterRed; | ||||
|     green = painterGreen; | ||||
|     blue = painterBlue; | ||||
|     alpha = painterAlpha; | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,139 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/canvas/PainterRGBA2222Bitmap.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| void PainterRGBA2222Bitmap::setBitmap(const Bitmap& bmp) | ||||
| { | ||||
|     bitmap = bmp; | ||||
|     assert((bitmap.getId() == BITMAP_INVALID || bitmap.getFormat() == Bitmap::RGBA2222) && "The chosen painter only works with RGBA2222 bitmaps"); | ||||
|     bitmapRectToFrameBuffer = bitmap.getRect(); | ||||
|     DisplayTransformation::transformDisplayToFrameBuffer(bitmapRectToFrameBuffer); | ||||
| } | ||||
|  | ||||
| void PainterRGBA2222Bitmap::render(uint8_t* ptr, | ||||
|                                    int x, | ||||
|                                    int xAdjust, | ||||
|                                    int y, | ||||
|                                    unsigned count, | ||||
|                                    const uint8_t* covers) | ||||
| { | ||||
|     uint8_t* p = ptr + (x + xAdjust); | ||||
|  | ||||
|     currentX = x + areaOffsetX; | ||||
|     currentY = y + areaOffsetY; | ||||
|  | ||||
|     if (!renderInit()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (currentX + (int)count > bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         count = bitmapRectToFrameBuffer.width - currentX; | ||||
|     } | ||||
|  | ||||
|     const uint8_t totalAlpha = LCD::div255(widgetAlpha * painterAlpha); | ||||
|     const uint8_t* src = bitmapRGBA2222Pointer; | ||||
|     if (totalAlpha == 0xFF) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t srcAlpha = ((*src) & 0x03) * 0x55; | ||||
|             const uint8_t alpha = LCD::div255((*covers++) * srcAlpha); | ||||
|             if (alpha == 0xFF) | ||||
|             { | ||||
|                 // Solid pixel | ||||
|                 *p = *src; | ||||
|             } | ||||
|             else if (alpha) | ||||
|             { | ||||
|                 // Non-Transparent pixel | ||||
|                 *p = mixColors(*src, *p, alpha); | ||||
|             } | ||||
|             p++; | ||||
|             src++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             const uint8_t srcAlpha = ((*src) & 0x03) * 0x55; | ||||
|             const uint8_t alpha = LCD::div255((*covers++) * srcAlpha); | ||||
|             if (alpha) // This can never get to max=0xFF*0xFF as totalAlpha<255 | ||||
|             { | ||||
|                 // Non-Transparent pixel | ||||
|                 *p = mixColors(*src, *p, alpha); | ||||
|             } | ||||
|             p++; | ||||
|             src++; | ||||
|         } while (--count != 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PainterRGBA2222Bitmap::renderInit() | ||||
| { | ||||
|     bitmapRGBA2222Pointer = 0; | ||||
|  | ||||
|     if (bitmap.getId() == BITMAP_INVALID) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if ((currentX >= bitmapRectToFrameBuffer.width) || (currentY >= bitmapRectToFrameBuffer.height)) | ||||
|     { | ||||
|         // Outside bitmap area, do not draw anything | ||||
|         // Consider the following instead of "return" to get a tiled image: | ||||
|         //   currentX %= bitmapRectToFrameBuffer.width | ||||
|         //   currentY %= bitmapRectToFrameBuffer.height | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (bitmap.getFormat() == Bitmap::RGBA2222) | ||||
|     { | ||||
|         bitmapRGBA2222Pointer = bitmap.getData(); | ||||
|         if (!bitmapRGBA2222Pointer) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         bitmapRGBA2222Pointer += currentX + currentY * bitmapRectToFrameBuffer.width; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool PainterRGBA2222Bitmap::renderNext(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha) | ||||
| { | ||||
|     if (currentX >= bitmapRectToFrameBuffer.width) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|     else if (bitmapRGBA2222Pointer != 0) | ||||
|     { | ||||
|         uint16_t RGBA2222 = *bitmapRGBA2222Pointer++; | ||||
|         red = LCD8bpp_RGBA2222::getRedFromColor(RGBA2222); | ||||
|         green = LCD8bpp_RGBA2222::getGreenFromColor(RGBA2222); | ||||
|         blue = LCD8bpp_RGBA2222::getBlueFromColor(RGBA2222); | ||||
|         alpha = (RGBA2222 & 0x03) * 0x55; // To get full range 0-0xFF | ||||
|     } | ||||
|     // Apply given alpha from setAlpha() | ||||
|     alpha = LCD::div255(alpha * painterAlpha); | ||||
|     return true; | ||||
| } | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,757 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/graph/AbstractDataGraph.hpp> | ||||
| #include <touchgfx/widgets/graph/GraphElements.hpp> | ||||
| #include <touchgfx/widgets/graph/GraphLabels.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| AbstractDataGraph::AbstractDataGraph(int16_t capacity) | ||||
|     : dataScale(1), alpha(255), | ||||
|       graphArea(), leftArea(), rightArea(), topArea(), bottomArea(), | ||||
|       topPadding(0), leftPadding(0), rightPadding(0), bottomPadding(0), | ||||
|       maxCapacity(capacity), usedCapacity(0), gapBeforeIndex(0), clickAction(0), dragAction(0) | ||||
| { | ||||
|     add(graphArea); | ||||
|     add(topArea); | ||||
|     add(leftArea); | ||||
|     add(rightArea); | ||||
|     add(bottomArea); | ||||
|     // Place all areas properly: | ||||
|     setGraphAreaMargin(0, 0, 0, 0); | ||||
|     setTouchable(true); | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::setScale(int scale) | ||||
| { | ||||
|     dataScale = scale; | ||||
| } | ||||
|  | ||||
| int AbstractDataGraph::getScale() const | ||||
| { | ||||
|     return dataScale; | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::setAlpha(uint8_t newAlpha) | ||||
| { | ||||
|     alpha = newAlpha; | ||||
| } | ||||
|  | ||||
| uint8_t AbstractDataGraph::getAlpha() const | ||||
| { | ||||
|     return alpha; | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::setWidth(int16_t width) | ||||
| { | ||||
|     topArea.setWidth(width); | ||||
|     bottomArea.setWidth(width); | ||||
|     rightArea.setX(width - rightArea.getWidth()); | ||||
|     graphArea.setWidth(width - (leftArea.getWidth() + rightArea.getWidth())); | ||||
|     Container::setWidth(width); | ||||
|     updateAreasPosition(); | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::setHeight(int16_t height) | ||||
| { | ||||
|     leftArea.setHeight(height); | ||||
|     rightArea.setHeight(height); | ||||
|     bottomArea.setY(height - bottomArea.getHeight()); | ||||
|     graphArea.setHeight(height - (topArea.getHeight() + bottomArea.getHeight())); | ||||
|     Container::setHeight(height); | ||||
|     updateAreasPosition(); | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::setGraphAreaMargin(int16_t top, int16_t left, int16_t right, int16_t bottom) | ||||
| { | ||||
|     graphArea.setPosition(left, top, getWidth() - (left + right), getHeight() - (top + bottom)); | ||||
|     topArea.setPosition(0, 0, getWidth(), top); | ||||
|     leftArea.setPosition(0, 0, left, getHeight()); | ||||
|     rightArea.setPosition(getWidth() - right, 0, right, getHeight()); | ||||
|     bottomArea.setPosition(0, getHeight() - bottom, getWidth(), bottom); | ||||
|     updateAreasPosition(); | ||||
| } | ||||
|  | ||||
| int16_t AbstractDataGraph::getGraphAreaMarginTop() const | ||||
| { | ||||
|     return topArea.getHeight(); | ||||
| } | ||||
|  | ||||
| int16_t AbstractDataGraph::getGraphAreaMarginLeft() const | ||||
| { | ||||
|     return leftArea.getWidth(); | ||||
| } | ||||
|  | ||||
| int16_t AbstractDataGraph::getGraphAreaMarginRight() const | ||||
| { | ||||
|     return rightArea.getWidth(); | ||||
| } | ||||
|  | ||||
| int16_t AbstractDataGraph::getGraphAreaMarginBottom() const | ||||
| { | ||||
|     return bottomArea.getHeight(); | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::setGraphAreaPadding(int16_t top, int16_t left, int16_t right, int16_t bottom) | ||||
| { | ||||
|     topPadding = top; | ||||
|     leftPadding = left; | ||||
|     rightPadding = right; | ||||
|     bottomPadding = bottom; | ||||
| } | ||||
|  | ||||
| int16_t AbstractDataGraph::getGraphAreaPaddingTop() const | ||||
| { | ||||
|     return topPadding; | ||||
| } | ||||
|  | ||||
| int16_t AbstractDataGraph::getGraphAreaPaddingLeft() const | ||||
| { | ||||
|     return leftPadding; | ||||
| } | ||||
|  | ||||
| int16_t AbstractDataGraph::getGraphAreaPaddingRight() const | ||||
| { | ||||
|     return rightPadding; | ||||
| } | ||||
|  | ||||
| int16_t AbstractDataGraph::getGraphAreaPaddingBottom() const | ||||
| { | ||||
|     return bottomPadding; | ||||
| } | ||||
|  | ||||
| int16_t AbstractDataGraph::getGraphAreaWidth() const | ||||
| { | ||||
|     return graphArea.getWidth() - (leftPadding + rightPadding); | ||||
| } | ||||
|  | ||||
| int16_t AbstractDataGraph::getGraphAreaWidthIncludingPadding() const | ||||
| { | ||||
|     return graphArea.getWidth(); | ||||
| } | ||||
|  | ||||
| int16_t AbstractDataGraph::getGraphAreaHeight() const | ||||
| { | ||||
|     return graphArea.getHeight() - (topPadding + bottomPadding); | ||||
| } | ||||
|  | ||||
| int16_t AbstractDataGraph::getGraphAreaHeightIncludingPadding() const | ||||
| { | ||||
|     return graphArea.getHeight(); | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::setGraphRange(int xMin, int xMax, int yMin, int yMax) | ||||
| { | ||||
|     setGraphRangeX(xMin, xMax); | ||||
|     setGraphRangeY(yMin, yMax); | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::setGraphRange(int xMin, int xMax, float yMin, float yMax) | ||||
| { | ||||
|     setGraphRangeX(xMin, xMax); | ||||
|     setGraphRangeY(yMin, yMax); | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::clear() | ||||
| { | ||||
|     usedCapacity = 0; | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::setGapBeforeIndex(int16_t index) | ||||
| { | ||||
|     gapBeforeIndex = index; | ||||
| } | ||||
|  | ||||
| int16_t AbstractDataGraph::getGapBeforeIndex() const | ||||
| { | ||||
|     return gapBeforeIndex; | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::addGraphElement(AbstractGraphElement& d) | ||||
| { | ||||
|     graphArea.add(d); | ||||
|     d.setPosition(0, 0, graphArea.getWidth(), graphArea.getHeight()); | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::addTopElement(AbstractGraphDecoration& d) | ||||
| { | ||||
|     topArea.add(d); | ||||
|     d.setPosition(0, 0, topArea.getWidth(), topArea.getHeight()); | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::addLeftElement(AbstractGraphDecoration& d) | ||||
| { | ||||
|     leftArea.add(d); | ||||
|     d.setPosition(0, 0, leftArea.getWidth(), leftArea.getHeight()); | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::addRightElement(AbstractGraphDecoration& d) | ||||
| { | ||||
|     rightArea.add(d); | ||||
|     d.setPosition(0, 0, rightArea.getWidth(), rightArea.getHeight()); | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::addBottomElement(AbstractGraphDecoration& d) | ||||
| { | ||||
|     bottomArea.add(d); | ||||
|     d.setPosition(0, 0, bottomArea.getWidth(), bottomArea.getHeight()); | ||||
| } | ||||
|  | ||||
| int16_t AbstractDataGraph::getUsedCapacity() const | ||||
| { | ||||
|     return usedCapacity; | ||||
| } | ||||
|  | ||||
| int16_t AbstractDataGraph::getMaxCapacity() const | ||||
| { | ||||
|     return maxCapacity; | ||||
| } | ||||
|  | ||||
| bool AbstractDataGraph::getNearestIndexForScreenXY(int16_t x, int16_t y, int16_t& index) | ||||
| { | ||||
|     if (usedCapacity == 0) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|     int32_t bestDist = 0x7FFFFFFF; | ||||
|     for (int16_t ix = 0; ix < usedCapacity; ix++) | ||||
|     { | ||||
|         const int16_t xDist = abs(indexToScreenX(ix) - x); | ||||
|         const int16_t yDist = abs(indexToScreenY(ix) - y); | ||||
|         const int32_t dist = xDist * xDist + yDist * yDist; | ||||
|         if (dist < bestDist) | ||||
|         { | ||||
|             index = ix; | ||||
|             bestDist = dist; | ||||
|         } | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool AbstractDataGraph::getNearestIndexForScreenX(int16_t x, int16_t& index) const | ||||
| { | ||||
|     if (usedCapacity == 0) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|     int16_t indexLow; | ||||
|     int16_t indexHigh; | ||||
|     xScreenRangeToIndexRange(x, x, indexLow, indexHigh); | ||||
|     const int16_t xLow = indexToScreenX(indexLow); | ||||
|     const int16_t xHigh = indexToScreenX(indexHigh); | ||||
|     index = abs(xLow - x) <= abs(xHigh - x) ? indexLow : indexHigh; | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| int16_t AbstractDataGraph::indexToScreenX(int16_t index) const | ||||
| { | ||||
|     return indexToScreenXQ5(index).round(); | ||||
| } | ||||
|  | ||||
| int16_t AbstractDataGraph::indexToScreenY(int16_t index) const | ||||
| { | ||||
|     return indexToScreenYQ5(index).round(); | ||||
| } | ||||
|  | ||||
| int AbstractDataGraph::indexToDataPointXAsInt(int16_t index) const | ||||
| { | ||||
|     return scaled2int(indexToDataPointXScaled(index)); | ||||
| } | ||||
|  | ||||
| float AbstractDataGraph::indexToDataPointXAsFloat(int16_t index) const | ||||
| { | ||||
|     return scaled2float(indexToDataPointXScaled(index)); | ||||
| } | ||||
|  | ||||
| int AbstractDataGraph::indexToDataPointYAsInt(int16_t index) const | ||||
| { | ||||
|     return scaled2int(indexToDataPointYScaled(index)); | ||||
| } | ||||
|  | ||||
| float AbstractDataGraph::indexToDataPointYAsFloat(int16_t index) const | ||||
| { | ||||
|     return scaled2float(indexToDataPointYScaled(index)); | ||||
| } | ||||
|  | ||||
| int32_t AbstractDataGraph::indexToGlobalIndex(int16_t index) const | ||||
| { | ||||
|     return (int32_t)index; | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::setClickAction(GenericCallback<const AbstractDataGraph&, const GraphClickEvent&>& callback) | ||||
| { | ||||
|     clickAction = &callback; | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::setDragAction(GenericCallback<const AbstractDataGraph&, const GraphDragEvent&>& callback) | ||||
| { | ||||
|     dragAction = &callback; | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::handleClickEvent(const ClickEvent& event) | ||||
| { | ||||
|     if (event.getType() == ClickEvent::CANCEL) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|     const int16_t x = event.getX() - graphArea.getX(); | ||||
|     int16_t index; | ||||
|     if (getNearestIndexForScreenX(x, index)) | ||||
|     { | ||||
|         if (event.getType() == ClickEvent::PRESSED || event.getType() == ClickEvent::RELEASED) | ||||
|         { | ||||
|             if (clickAction && clickAction->isValid()) | ||||
|             { | ||||
|                 GraphClickEvent graphClickEvent(index, event); | ||||
|                 clickAction->execute(*this, graphClickEvent); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::handleDragEvent(const DragEvent& event) | ||||
| { | ||||
|     const int16_t x = event.getNewX() - graphArea.getX(); | ||||
|     int16_t index; | ||||
|     if (getNearestIndexForScreenX(x, index)) | ||||
|     { | ||||
|         if (event.getType() == DragEvent::DRAGGED) | ||||
|         { | ||||
|             if (dragAction && dragAction->isValid()) | ||||
|             { | ||||
|                 GraphDragEvent graphDragEvent(index, event); | ||||
|                 dragAction->execute(*this, graphDragEvent); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::invalidateGraphPointAt(int16_t index) | ||||
| { | ||||
|     if (index < usedCapacity) | ||||
|     { | ||||
|         AbstractGraphElement* d = (AbstractGraphElement*)graphArea.getFirstChild(); | ||||
|         while (d) | ||||
|         { | ||||
|             d->invalidateGraphPointAt(index); | ||||
|             d = (AbstractGraphElement*)d->getNextSibling(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::invalidateGraphArea() | ||||
| { | ||||
|     graphArea.invalidate(); | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::invalidateXAxisPointAt(int16_t index) | ||||
| { | ||||
|     AbstractGraphElement* d = (AbstractGraphElement*)topArea.getFirstChild(); | ||||
|     while (d) | ||||
|     { | ||||
|         d->invalidateGraphPointAt(index); | ||||
|         d = (AbstractGraphElement*)d->getNextSibling(); | ||||
|     } | ||||
|     d = (AbstractGraphElement*)bottomArea.getFirstChild(); | ||||
|     while (d) | ||||
|     { | ||||
|         d->invalidateGraphPointAt(index); | ||||
|         d = (AbstractGraphElement*)d->getNextSibling(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::invalidateAllXAxisPoints() | ||||
| { | ||||
|     int min = getGraphRangeXMin(); | ||||
|     int max = getGraphRangeXMax(); | ||||
|     if (max < min) | ||||
|     { | ||||
|         const int16_t tmp = min; | ||||
|         min = max; | ||||
|         max = tmp; | ||||
|     } | ||||
|     for (int index = min; index <= max; index++) | ||||
|     { | ||||
|         invalidateXAxisPointAt(index); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::updateAreasPosition() | ||||
| { | ||||
|     Drawable* d = graphArea.getFirstChild(); | ||||
|     while (d) | ||||
|     { | ||||
|         d->setPosition(0, 0, graphArea.getWidth(), graphArea.getHeight()); | ||||
|         d = d->getNextSibling(); | ||||
|     } | ||||
|  | ||||
|     d = topArea.getFirstChild(); | ||||
|     while (d) | ||||
|     { | ||||
|         d->setPosition(0, 0, topArea.getWidth(), topArea.getHeight()); | ||||
|         d = d->getNextSibling(); | ||||
|     } | ||||
|  | ||||
|     d = leftArea.getFirstChild(); | ||||
|     while (d) | ||||
|     { | ||||
|         d->setPosition(0, 0, leftArea.getWidth(), leftArea.getHeight()); | ||||
|         d = d->getNextSibling(); | ||||
|     } | ||||
|  | ||||
|     d = rightArea.getFirstChild(); | ||||
|     while (d) | ||||
|     { | ||||
|         d->setPosition(0, 0, rightArea.getWidth(), rightArea.getHeight()); | ||||
|         d = d->getNextSibling(); | ||||
|     } | ||||
|  | ||||
|     d = bottomArea.getFirstChild(); | ||||
|     while (d) | ||||
|     { | ||||
|         d->setPosition(0, 0, bottomArea.getWidth(), bottomArea.getHeight()); | ||||
|         d = d->getNextSibling(); | ||||
|     } | ||||
|  | ||||
|     invalidate(); | ||||
| } | ||||
|  | ||||
| void AbstractDataGraph::setGraphRangeScaled(int xMin, int xMax, int yMin, int yMax) | ||||
| { | ||||
|     setGraphRangeX(xMin, xMax); | ||||
|     setGraphRangeYScaled(yMin, yMax); | ||||
| } | ||||
|  | ||||
| int AbstractDataGraph::convertToGraphScale(int value, int scale) const | ||||
| { | ||||
|     if (scale == dataScale) | ||||
|     { | ||||
|         return value; | ||||
|     } | ||||
|     return muldiv(value, dataScale, scale); | ||||
| } | ||||
|  | ||||
| int AbstractDataGraph::getXAxisScaleScaled() const | ||||
| { | ||||
|     return dataScale; | ||||
| } | ||||
|  | ||||
| int AbstractDataGraph::getXAxisOffsetScaled() const | ||||
| { | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| AbstractDataGraphWithY::AbstractDataGraphWithY(int16_t capacity, int* values) | ||||
|     : AbstractDataGraph(capacity), yValues(values), dataCounter(0), xOffset(0), xScale(1), | ||||
|       graphRangeMinX(0), graphRangeMaxX(0), graphRangeMinY(0), graphRangeMaxY(0) | ||||
| { | ||||
|     assert(capacity > 0); | ||||
|     setGraphRangeX(0, capacity - 1); | ||||
| } | ||||
|  | ||||
| int16_t AbstractDataGraphWithY::addDataPoint(int y) | ||||
| { | ||||
|     return addDataPointScaled(y * dataScale); | ||||
| } | ||||
|  | ||||
| int16_t AbstractDataGraphWithY::addDataPoint(float y) | ||||
| { | ||||
|     return addDataPointScaled(float2scaled(y)); | ||||
| } | ||||
|  | ||||
| void AbstractDataGraphWithY::setGraphRangeX(int min, int max) | ||||
| { | ||||
|     assert(min != max); | ||||
|     if (max < min) | ||||
|     { | ||||
|         const int tmp = min; | ||||
|         min = max; | ||||
|         max = tmp; | ||||
|     } | ||||
|     if (min != graphRangeMinX || max != graphRangeMaxX) | ||||
|     { | ||||
|         graphRangeMinX = min; | ||||
|         graphRangeMaxX = max; | ||||
|         topArea.invalidate(); | ||||
|         graphArea.invalidate(); | ||||
|         bottomArea.invalidate(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AbstractDataGraphWithY::setGraphRangeY(int min, int max) | ||||
| { | ||||
|     setGraphRangeYScaled(int2scaled(min), int2scaled(max)); | ||||
| } | ||||
|  | ||||
| void AbstractDataGraphWithY::setGraphRangeY(float min, float max) | ||||
| { | ||||
|     setGraphRangeYScaled(float2scaled(min), float2scaled(max)); | ||||
| } | ||||
|  | ||||
| int AbstractDataGraphWithY::getGraphRangeXMin() const | ||||
| { | ||||
|     return graphRangeMinX; | ||||
| } | ||||
|  | ||||
| int AbstractDataGraphWithY::getGraphRangeXMax() const | ||||
| { | ||||
|     return graphRangeMaxX; | ||||
| } | ||||
|  | ||||
| int AbstractDataGraphWithY::getGraphRangeYMinAsInt() const | ||||
| { | ||||
|     return scaled2int(graphRangeMinY); | ||||
| } | ||||
|  | ||||
| float AbstractDataGraphWithY::getGraphRangeYMinAsFloat() const | ||||
| { | ||||
|     return scaled2float(graphRangeMinY); | ||||
| } | ||||
|  | ||||
| int AbstractDataGraphWithY::getGraphRangeYMaxAsInt() const | ||||
| { | ||||
|     return scaled2int(graphRangeMaxY); | ||||
| } | ||||
|  | ||||
| float AbstractDataGraphWithY::getGraphRangeYMaxAsFloat() const | ||||
| { | ||||
|     return scaled2float(graphRangeMaxY); | ||||
| } | ||||
|  | ||||
| void AbstractDataGraphWithY::setGraphRangeYAuto(bool showXaxis, int margin) | ||||
| { | ||||
|     if (usedCapacity > 0) | ||||
|     { | ||||
|         int loX = getGraphRangeXMin(); | ||||
|         int hiX = getGraphRangeXMax(); | ||||
|         if (loX > hiX) | ||||
|         { | ||||
|             const int16_t tmp = loX; | ||||
|             loX = hiX; | ||||
|             hiX = tmp; | ||||
|         } | ||||
|         loX = MAX(loX, 0); | ||||
|         hiX = MIN(hiX, usedCapacity); | ||||
|         if (loX < usedCapacity && hiX >= 0) | ||||
|         { | ||||
|             int loYnew = showXaxis ? margin : yValues[loX]; | ||||
|             int hiYnew = showXaxis ? -margin : yValues[loX]; | ||||
|             for (int16_t i = loX; i < hiX; i++) | ||||
|             { | ||||
|                 int y = yValues[i]; | ||||
|                 if (loYnew > y) | ||||
|                 { | ||||
|                     loYnew = y; | ||||
|                 } | ||||
|                 if (hiYnew < y) | ||||
|                 { | ||||
|                     hiYnew = y; | ||||
|                 } | ||||
|             } | ||||
|             loYnew -= margin; | ||||
|             hiYnew += margin; | ||||
|             if (loYnew != hiYnew) | ||||
|             { | ||||
|                 setGraphRangeYScaled(loYnew, hiYnew); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AbstractDataGraphWithY::setScale(int scale) | ||||
| { | ||||
|     const int oldScale = getScale(); | ||||
|     AbstractDataGraph::setScale(scale); | ||||
|     xScale = convertToGraphScale(xScale, oldScale); | ||||
|     xOffset = convertToGraphScale(xOffset, oldScale); | ||||
| } | ||||
|  | ||||
| void AbstractDataGraphWithY::setXAxisScale(int scale) | ||||
| { | ||||
|     setXAxisScaleScaled(scale * dataScale); | ||||
| } | ||||
|  | ||||
| void AbstractDataGraphWithY::setXAxisScale(float scale) | ||||
| { | ||||
|     setXAxisScaleScaled(float2scaled(scale)); | ||||
| } | ||||
|  | ||||
| int AbstractDataGraphWithY::getXAxisScaleAsInt() const | ||||
| { | ||||
|     return scaled2int(getXAxisScaleScaled()); | ||||
| } | ||||
|  | ||||
| float AbstractDataGraphWithY::getXAxisScaleAsFloat() const | ||||
| { | ||||
|     return scaled2float(getXAxisScaleScaled()); | ||||
| } | ||||
|  | ||||
| void AbstractDataGraphWithY::setXAxisOffset(int offset) | ||||
| { | ||||
|     setXAxisOffsetScaled(offset * dataScale); | ||||
| } | ||||
|  | ||||
| void AbstractDataGraphWithY::setXAxisOffset(float offset) | ||||
| { | ||||
|     setXAxisOffsetScaled(float2scaled(offset)); | ||||
| } | ||||
|  | ||||
| int AbstractDataGraphWithY::getXAxisOffsetAsInt() const | ||||
| { | ||||
|     return scaled2int(getXAxisOffsetScaled()); | ||||
| } | ||||
|  | ||||
| float AbstractDataGraphWithY::getXAxisOffsetAsFloat() const | ||||
| { | ||||
|     return scaled2float(getXAxisOffsetScaled()); | ||||
| } | ||||
|  | ||||
| int16_t AbstractDataGraphWithY::addDataPointScaled(int y) | ||||
| { | ||||
|     beforeAddValue(); | ||||
|     dataCounter++; | ||||
|     return addValue(y); | ||||
| } | ||||
|  | ||||
| void AbstractDataGraphWithY::beforeAddValue() | ||||
| { | ||||
| } | ||||
|  | ||||
| int16_t AbstractDataGraphWithY::realIndex(int16_t index) const | ||||
| { | ||||
|     return index; | ||||
| } | ||||
|  | ||||
| int AbstractDataGraphWithY::indexToDataPointXScaled(int16_t index) const | ||||
| { | ||||
|     return (indexToGlobalIndex(index) * getXAxisScaleScaled()) + getXAxisOffsetScaled(); | ||||
| } | ||||
|  | ||||
| int AbstractDataGraphWithY::indexToDataPointYScaled(int16_t index) const | ||||
| { | ||||
|     return yValues[realIndex(index)]; | ||||
| } | ||||
|  | ||||
| bool AbstractDataGraphWithY::xScreenRangeToIndexRange(int16_t xLo, int16_t xHi, int16_t& indexLow, int16_t& indexHigh) const | ||||
| { | ||||
|     if (usedCapacity == 0) | ||||
|     { | ||||
|         indexLow = indexHigh = -1; | ||||
|         return false; | ||||
|     } | ||||
|     if (getGraphAreaWidth() == 1) | ||||
|     { | ||||
|         indexLow = 0; | ||||
|         indexHigh = usedCapacity - 1; | ||||
|         return true; | ||||
|     } | ||||
|     CWRUtil::Q5 loQ5 = CWRUtil::muldivQ5(CWRUtil::toQ5(xLo - leftPadding), CWRUtil::Q5(graphRangeMaxX - graphRangeMinX), CWRUtil::Q5(getGraphAreaWidth() - 1)) + CWRUtil::toQ5(graphRangeMinX); | ||||
|     CWRUtil::Q5 hiQ5 = CWRUtil::muldivQ5(CWRUtil::toQ5(xHi - leftPadding), CWRUtil::Q5(graphRangeMaxX - graphRangeMinX), CWRUtil::Q5(getGraphAreaWidth() - 1)) + CWRUtil::toQ5(graphRangeMinX); | ||||
|     if (loQ5 > hiQ5) | ||||
|     { | ||||
|         const CWRUtil::Q5 tmp = loQ5; | ||||
|         loQ5 = hiQ5; | ||||
|         hiQ5 = tmp; | ||||
|     } | ||||
|     const int low = loQ5.to<int>(); | ||||
|     const int high = hiQ5.ceil(); | ||||
|     const int lowValid = 0; | ||||
|     const int highValid = usedCapacity - 1; | ||||
|     if (high < lowValid) | ||||
|     { | ||||
|         indexLow = indexHigh = lowValid; | ||||
|         return false; | ||||
|     } | ||||
|     if (low > highValid) | ||||
|     { | ||||
|         indexLow = indexHigh = highValid; | ||||
|         return false; | ||||
|     } | ||||
|     indexLow = MAX(lowValid, low); | ||||
|     indexHigh = MIN(highValid, high); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| void AbstractDataGraphWithY::setXAxisScaleScaled(int scale) | ||||
| { | ||||
|     xScale = scale; | ||||
| } | ||||
|  | ||||
| int AbstractDataGraphWithY::getXAxisScaleScaled() const | ||||
| { | ||||
|     return xScale; | ||||
| } | ||||
|  | ||||
| void AbstractDataGraphWithY::setGraphRangeYScaled(int min, int max) | ||||
| { | ||||
|     assert(min != max); | ||||
|     if (max < min) | ||||
|     { | ||||
|         const int tmp = min; | ||||
|         min = max; | ||||
|         max = tmp; | ||||
|     } | ||||
|     if (min != graphRangeMinY || max != graphRangeMaxY) | ||||
|     { | ||||
|         graphRangeMinY = min; | ||||
|         graphRangeMaxY = max; | ||||
|         leftArea.invalidate(); | ||||
|         graphArea.invalidate(); | ||||
|         rightArea.invalidate(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| int AbstractDataGraphWithY::getGraphRangeYMinScaled() const | ||||
| { | ||||
|     return graphRangeMinY; | ||||
| } | ||||
|  | ||||
| int AbstractDataGraphWithY::getGraphRangeYMaxScaled() const | ||||
| { | ||||
|     return graphRangeMaxY; | ||||
| } | ||||
|  | ||||
| void AbstractDataGraphWithY::setXAxisOffsetScaled(int offset) | ||||
| { | ||||
|     xOffset = offset; | ||||
| } | ||||
|  | ||||
| int AbstractDataGraphWithY::getXAxisOffsetScaled() const | ||||
| { | ||||
|     return xOffset; | ||||
| } | ||||
|  | ||||
| CWRUtil::Q5 AbstractDataGraphWithY::valueToScreenXQ5(int x) const | ||||
| { | ||||
|     return CWRUtil::muldiv_toQ5(x - graphRangeMinX, getGraphAreaWidth() - 1, graphRangeMaxX - graphRangeMinX) + CWRUtil::toQ5(leftPadding); | ||||
| } | ||||
|  | ||||
| CWRUtil::Q5 AbstractDataGraphWithY::valueToScreenYQ5(int y) const | ||||
| { | ||||
|     return CWRUtil::toQ5(getGraphAreaHeight() + topPadding - 1) - CWRUtil::muldiv_toQ5(y - graphRangeMinY, getGraphAreaHeight() - 1, graphRangeMaxY - graphRangeMinY); | ||||
| } | ||||
|  | ||||
| CWRUtil::Q5 AbstractDataGraphWithY::indexToScreenXQ5(int16_t index) const | ||||
| { | ||||
|     return valueToScreenXQ5(index); | ||||
| } | ||||
|  | ||||
| CWRUtil::Q5 AbstractDataGraphWithY::indexToScreenYQ5(int16_t index) const | ||||
| { | ||||
|     return valueToScreenYQ5(yValues[realIndex(index)]); | ||||
| } | ||||
|  | ||||
| } // namespace touchgfx | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1,551 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/graph/GraphLabels.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| GraphLabelsBase::GraphLabelsBase() | ||||
|     : labelInterval(0), labelTypedText(TypedText(TYPED_TEXT_INVALID)), labelRotation(TEXT_ROTATE_0), labelDecimals(0), labelDecimalPoint('.'), | ||||
|       majorLabel(0) | ||||
| { | ||||
| } | ||||
|  | ||||
| int GraphLabelsBase::getCorrectlyScaledMajorInterval(const AbstractDataGraph* graph) const | ||||
| { | ||||
|     return majorLabel == 0 ? 0 : convertToGraphScale(graph, majorLabel->getIntervalScaled(), majorLabel->getScale()); | ||||
| } | ||||
|  | ||||
| void GraphLabelsBase::setIntervalScaled(int interval) | ||||
| { | ||||
|     labelInterval = abs(interval); | ||||
| } | ||||
|  | ||||
| void GraphLabelsBase::setInterval(int interval) | ||||
| { | ||||
|     setIntervalScaled(abs(interval) * dataScale); | ||||
| } | ||||
|  | ||||
| void GraphLabelsBase::setInterval(float interval) | ||||
| { | ||||
|     setIntervalScaled(AbstractDataGraph::float2scaled(abs(interval), dataScale)); | ||||
| } | ||||
|  | ||||
| int GraphLabelsBase::getIntervalScaled() const | ||||
| { | ||||
|     return labelInterval; | ||||
| } | ||||
|  | ||||
| int GraphLabelsBase::getIntervalAsInt() const | ||||
| { | ||||
|     return AbstractDataGraph::scaled2int(getIntervalScaled(), dataScale); | ||||
| } | ||||
|  | ||||
| float GraphLabelsBase::getIntervalAsFloat() const | ||||
| { | ||||
|     return AbstractDataGraph::scaled2float(getIntervalScaled(), dataScale); | ||||
| } | ||||
|  | ||||
| void GraphLabelsBase::setMajorLabel(const GraphLabelsBase& major) | ||||
| { | ||||
|     majorLabel = &major; | ||||
| } | ||||
|  | ||||
| void GraphLabelsBase::setLabelTypedText(const TypedText& typedText) | ||||
| { | ||||
|     labelTypedText = typedText; | ||||
| } | ||||
|  | ||||
| TypedText GraphLabelsBase::getLabelTypedText() const | ||||
| { | ||||
|     return labelTypedText; | ||||
| } | ||||
|  | ||||
| void GraphLabelsBase::setLabelRotation(TextRotation rotation) | ||||
| { | ||||
|     labelRotation = rotation; | ||||
| } | ||||
|  | ||||
| TextRotation GraphLabelsBase::getLabelRotation() const | ||||
| { | ||||
|     return labelRotation; | ||||
| } | ||||
|  | ||||
| void GraphLabelsBase::setLabelDecimals(uint16_t decimals) | ||||
| { | ||||
|     labelDecimals = decimals; | ||||
| } | ||||
|  | ||||
| uint16_t GraphLabelsBase::getLabelDecimals() const | ||||
| { | ||||
|     return labelDecimals; | ||||
| } | ||||
|  | ||||
| void GraphLabelsBase::setLabelDecimalPoint(Unicode::UnicodeChar decimalPoint) | ||||
| { | ||||
|     labelDecimalPoint = decimalPoint; | ||||
| } | ||||
|  | ||||
| Unicode::UnicodeChar GraphLabelsBase::getLabelDecimalPoint() const | ||||
| { | ||||
|     return labelDecimalPoint; | ||||
| } | ||||
|  | ||||
| void GraphLabelsBase::invalidateGraphPointAt(int16_t index) | ||||
| { | ||||
| } | ||||
|  | ||||
| void GraphLabelsBase::formatLabel(Unicode::UnicodeChar* buffer, int16_t bufferSize, int label, int decimals, Unicode::UnicodeChar decimalPoint, int scale) const | ||||
| { | ||||
|     int length = 0; | ||||
|     if (label < 0 && length < bufferSize - 1) | ||||
|     { | ||||
|         buffer[length++] = '-'; | ||||
|         label = -label; | ||||
|     } | ||||
|     if (decimals == 0) | ||||
|     { | ||||
|         Unicode::snprintf(buffer + length, bufferSize - length, "%d", (label + scale / 2) / scale); | ||||
|     } | ||||
|     else if (decimals > 0) | ||||
|     { | ||||
|         Unicode::snprintf(buffer + length, bufferSize - length, "%d", label / scale); | ||||
|         int length = Unicode::strlen(buffer); | ||||
|         if (length < bufferSize - 1) | ||||
|         { | ||||
|             buffer[length++] = decimalPoint; | ||||
|             int32_t remainder = label % scale; | ||||
|             for (int i = 0; i < decimals && length < bufferSize - 1; i++) | ||||
|             { | ||||
|                 remainder *= 10; | ||||
|                 if (i == decimals - 1 || length == bufferSize - 1) | ||||
|                 { | ||||
|                     remainder += scale / 2; // Rounding on the last (visible) digit | ||||
|                 } | ||||
|                 const int digit = (remainder / scale); | ||||
|                 buffer[length++] = (Unicode::UnicodeChar)('0' + digit); | ||||
|                 remainder %= scale; | ||||
|             } | ||||
|             buffer[length] = (Unicode::UnicodeChar)0; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void GraphLabelsX::draw(const Rect& invalidatedArea) const | ||||
| { | ||||
|     if (!labelTypedText.hasValidId()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|     const Font* fontToDraw = labelTypedText.getFont(); | ||||
|     if (!fontToDraw) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const AbstractDataGraph* graph = getGraph(); | ||||
|     const uint8_t alpha = LCD::div255(getAlpha() * graph->getAlpha()); | ||||
|     if (alpha == 0) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const int minorInterval = getIntervalAsInt(); | ||||
|     const int majorInterval = (majorLabel == 0) ? 0 : majorLabel->getIntervalAsInt(); | ||||
|  | ||||
|     if (minorInterval == 0 && majorInterval == 0) | ||||
|     { | ||||
|         drawString(invalidatedArea, fontToDraw, graph, 0, alpha); | ||||
|     } | ||||
|     else if (minorInterval > 0) | ||||
|     { | ||||
|         int rangeMin = graph->getGraphRangeXMin(); | ||||
|         int rangeMax = graph->getGraphRangeXMax(); | ||||
|         if (rangeMin > rangeMax) | ||||
|         { | ||||
|             const int tmp = rangeMin; | ||||
|             rangeMin = rangeMax; | ||||
|             rangeMax = tmp; | ||||
|         } | ||||
|  | ||||
|         const int16_t gapIndex = graph->getGapBeforeIndex(); | ||||
|         if (gapIndex <= 0 || gapIndex <= rangeMin || gapIndex > rangeMax) | ||||
|         { | ||||
|             drawIndexRange(invalidatedArea, fontToDraw, graph, rangeMin, rangeMax, alpha); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             drawIndexRange(invalidatedArea, fontToDraw, graph, rangeMin, (int)gapIndex - 1, alpha); | ||||
|             drawIndexRange(invalidatedArea, fontToDraw, graph, (int)gapIndex, rangeMax, alpha); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void GraphLabelsX::invalidateGraphPointAt(int16_t index) | ||||
| { | ||||
|     if (!labelTypedText.hasValidId()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|     const Font* fontToDraw = labelTypedText.getFont(); | ||||
|     if (!fontToDraw) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const AbstractDataGraph* graph = getGraph(); | ||||
|     const uint8_t alpha = LCD::div255(getAlpha() * graph->getAlpha()); | ||||
|     if (alpha == 0) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const int minorInterval = getIntervalAsInt(); | ||||
|     const int majorInterval = (majorLabel == 0) ? 0 : majorLabel->getIntervalAsInt(); | ||||
|  | ||||
|     bool isOnMinor = (minorInterval > 0 && index == minorInterval * (int)(index / minorInterval)); | ||||
|     bool isOnMajor = (majorInterval > 0 && index == majorInterval * (int)(index / majorInterval)); | ||||
|     if ((majorInterval == 0 && minorInterval == 0 && index == 0) || (isOnMinor && !isOnMajor)) | ||||
|     { | ||||
|         Unicode::UnicodeChar wildcard[20]; | ||||
|         formatLabel(wildcard, 20, (graph->indexToGlobalIndex((int)index) * getGraphXAxisScaleScaled(graph)) + getGraphXAxisOffsetScaled(graph), labelDecimals, labelDecimalPoint, graph->getScale()); | ||||
|         // Adjust to make label centered | ||||
|         uint16_t labelWidth; | ||||
|         if (labelRotation == TEXT_ROTATE_0 || labelRotation == TEXT_ROTATE_180) | ||||
|         { | ||||
|             labelWidth = fontToDraw->getStringWidth(labelTypedText.getText(), wildcard); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             labelWidth = fontToDraw->getMaxTextHeight(labelTypedText.getText(), wildcard) * fontToDraw->getNumberOfLines(labelTypedText.getText(), wildcard) + fontToDraw->getSpacingAbove(labelTypedText.getText(), wildcard); | ||||
|         } | ||||
|         Rect dirty((graph->getGraphAreaMarginLeft() + valueToScreenXQ5(graph, index).round()) - labelWidth / 2, 0, labelWidth, getHeight()); | ||||
|         invalidateRect(dirty); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void GraphLabelsX::drawIndexRange(const Rect& invalidatedArea, const Font* fontToDraw, const AbstractDataGraph* graph, int indexLow, int indexHigh, const uint8_t alpha) const | ||||
| { | ||||
|     if (indexLow > indexHigh) | ||||
|     { | ||||
|         const int tmp = indexLow; | ||||
|         indexLow = indexHigh; | ||||
|         indexHigh = tmp; | ||||
|     } | ||||
|     // Now indexHigh is higher than indexLow | ||||
|  | ||||
|     const int minorInterval = getIntervalAsInt(); | ||||
|     if (minorInterval > 0 && abs(indexHigh - indexLow) / minorInterval > 100) | ||||
|     { | ||||
|         return; // Too many labels | ||||
|     } | ||||
|  | ||||
|     if (minorInterval == 0) | ||||
|     { | ||||
|         if ((0 >= indexLow && 0 <= indexHigh) || (0 >= indexHigh && 0 <= indexLow)) | ||||
|         { | ||||
|             drawString(invalidatedArea, fontToDraw, graph, 0, alpha); | ||||
|         } | ||||
|         return; | ||||
|     } | ||||
|     const int majorInterval = (majorLabel == 0) ? 0 : majorLabel->getIntervalAsInt(); | ||||
|  | ||||
|     const int minorLo = (int)(indexLow / minorInterval) - 1; | ||||
|     const int minorHi = (int)(indexHigh / minorInterval) + 1; | ||||
|  | ||||
|     if (majorInterval == 0) | ||||
|     { | ||||
|         for (int minorIndex = minorLo; minorIndex != minorHi + 1; minorIndex++) | ||||
|         { | ||||
|             const int index = (int)(minorInterval * minorIndex); | ||||
|             if ((index >= indexLow && index <= indexHigh) || (index >= indexHigh && index <= indexLow)) | ||||
|             { | ||||
|                 drawString(invalidatedArea, fontToDraw, graph, index, alpha); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         const int majorLo = (int)(indexLow / majorInterval) - 1; | ||||
|         const int majorHi = (int)(indexHigh / majorInterval) + 1; | ||||
|         int majorIndex = majorLo; | ||||
|         int32_t majorValue = majorInterval * majorLo; | ||||
|         int32_t minorValue = minorInterval * minorLo; | ||||
|         for (;;) | ||||
|         { | ||||
|             // Draw strings lines up to the major line | ||||
|             while (minorValue < majorValue) | ||||
|             { | ||||
|                 if ((minorValue >= indexLow && minorValue <= indexHigh) || (minorValue >= indexHigh && minorValue <= indexLow)) | ||||
|                 { | ||||
|                     drawString(invalidatedArea, fontToDraw, graph, minorValue, alpha); | ||||
|                 } | ||||
|                 minorValue += minorInterval; | ||||
|             } | ||||
|             // Advance minor past the major line we are about to draw | ||||
|             while (minorValue <= majorValue) | ||||
|             { | ||||
|                 minorValue += minorInterval; | ||||
|             } | ||||
|             if (majorValue < minorValue) | ||||
|             { | ||||
|                 majorIndex++; | ||||
|                 if (majorIndex == majorHi + 1) | ||||
|                 { | ||||
|                     break; | ||||
|                 } | ||||
|                 majorValue += majorInterval; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void GraphLabelsX::drawString(const Rect& invalidatedArea, const Font* fontToDraw, const AbstractDataGraph* graph, int index, const uint8_t alpha) const | ||||
| { | ||||
|     const int16_t labelX = valueToScreenXQ5(graph, index).round() - graph->getGraphAreaPaddingLeft(); | ||||
|     if (labelX < 0 || labelX >= graph->getGraphAreaWidth()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Unicode::UnicodeChar wildcard[20]; | ||||
|     formatLabel(wildcard, 20, (graph->indexToGlobalIndex(index) * getGraphXAxisScaleScaled(graph)) + getGraphXAxisOffsetScaled(graph), labelDecimals, labelDecimalPoint, graph->getScale()); | ||||
|  | ||||
|     // Adjust to make label centered | ||||
|     uint16_t labelWidth; | ||||
|     if (labelRotation == TEXT_ROTATE_0 || labelRotation == TEXT_ROTATE_180) | ||||
|     { | ||||
|         labelWidth = fontToDraw->getStringWidth(labelTypedText.getText(), wildcard); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         labelWidth = fontToDraw->getMaxTextHeight(labelTypedText.getText(), wildcard) * fontToDraw->getNumberOfLines(labelTypedText.getText(), wildcard) + fontToDraw->getSpacingAbove(labelTypedText.getText(), wildcard); | ||||
|     } | ||||
|     Rect labelRect((graph->getGraphAreaMarginLeft() + valueToScreenXQ5(graph, index).round()) - labelWidth / 2, 0, labelWidth, getHeight()); | ||||
|  | ||||
|     Rect dirty = labelRect & invalidatedArea; | ||||
|     if (!dirty.isEmpty()) | ||||
|     { | ||||
|         dirty.x -= labelRect.x; | ||||
|         dirty.y -= labelRect.y; | ||||
|         translateRectToAbsolute(labelRect); | ||||
|         LCD::StringVisuals visuals(fontToDraw, color, alpha, labelTypedText.getAlignment(), 0, labelRotation, labelTypedText.getTextDirection(), 0, WIDE_TEXT_NONE); | ||||
|         HAL::lcd().drawString(labelRect, dirty, visuals, labelTypedText.getText(), wildcard, 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void GraphLabelsY::draw(const Rect& invalidatedArea) const | ||||
| { | ||||
|     if (!labelTypedText.hasValidId()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|     const Font* fontToDraw = labelTypedText.getFont(); | ||||
|     if (!fontToDraw) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const AbstractDataGraph* graph = getGraph(); | ||||
|     const uint8_t alpha = LCD::div255(getAlpha() * graph->getAlpha()); | ||||
|     if (alpha == 0) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const int minorInterval = convertToGraphScale(graph, labelInterval, dataScale); | ||||
|     int majorInterval = getCorrectlyScaledMajorInterval(graph); | ||||
|  | ||||
|     if (majorInterval == 0 && minorInterval == 0) | ||||
|     { | ||||
|         drawString(invalidatedArea, fontToDraw, graph, 0, 0, alpha); | ||||
|     } | ||||
|     else if (minorInterval > 0) | ||||
|     { | ||||
|         int rangeMin = getGraphRangeYMinScaled(graph); | ||||
|         int rangeMax = getGraphRangeYMaxScaled(graph); | ||||
|         if (abs(rangeMax - rangeMin) / minorInterval > 100) | ||||
|         { | ||||
|             return; // Too many labels | ||||
|         } | ||||
|  | ||||
|         if (rangeMin > rangeMax) | ||||
|         { | ||||
|             const int tmp = rangeMin; | ||||
|             rangeMin = rangeMax; | ||||
|             rangeMax = tmp; | ||||
|         } | ||||
|         // Now rangeMax is higher than rangeMin | ||||
|  | ||||
|         const int minorLo = (int)(rangeMin / minorInterval) - 1; | ||||
|         const int minorHi = (int)(rangeMax / minorInterval) + 1; | ||||
|         if (majorInterval == 0) | ||||
|         { | ||||
|             for (int minorIndex = minorLo; minorIndex != minorHi + 1; minorIndex++) | ||||
|             { | ||||
|                 drawString(invalidatedArea, fontToDraw, graph, minorInterval * minorIndex, labelInterval * minorIndex, alpha); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             const int majorLo = (int)(rangeMin / majorInterval) - 1; | ||||
|             const int majorHi = (int)(rangeMax / majorInterval) + 1; | ||||
|             int majorIndex = majorLo; | ||||
|             int32_t majorValue = majorInterval * majorIndex; | ||||
|             int minorIndex = minorLo; | ||||
|             int32_t minorValue = minorInterval * minorIndex; | ||||
|             for (;;) | ||||
|             { | ||||
|                 // Draw strings lines up to the major line | ||||
|                 while (minorValue < majorValue) | ||||
|                 { | ||||
|                     drawString(invalidatedArea, fontToDraw, graph, minorValue, labelInterval * minorIndex, alpha); | ||||
|                     minorIndex++; | ||||
|                     minorValue += minorInterval; | ||||
|                 } | ||||
|                 // Advance minor past the major line we are about to draw | ||||
|                 while (minorValue <= majorValue) | ||||
|                 { | ||||
|                     minorIndex++; | ||||
|                     minorValue += minorInterval; | ||||
|                 } | ||||
|                 if (majorValue < minorValue) | ||||
|                 { | ||||
|                     majorIndex++; | ||||
|                     if (majorIndex == majorHi + 1) | ||||
|                     { | ||||
|                         break; | ||||
|                     } | ||||
|                     majorValue += majorInterval; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void GraphLabelsY::drawString(const Rect& invalidatedArea, const Font* fontToDraw, const AbstractDataGraph* graph, int valueScaled, int labelScaled, const uint8_t alpha) const | ||||
| { | ||||
|     const int16_t labelCoord = valueToScreenYQ5(graph, valueScaled).round() - graph->getGraphAreaPaddingTop(); | ||||
|     if (labelCoord < 0 || labelCoord >= graph->getGraphAreaHeight()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Unicode::UnicodeChar wildcard[20]; | ||||
|     formatLabel(wildcard, 20, labelScaled, labelDecimals, labelDecimalPoint, dataScale); | ||||
|  | ||||
|     // Adjust to make label centered | ||||
|     uint16_t labelHeight; | ||||
|     if (labelRotation == TEXT_ROTATE_0 || labelRotation == TEXT_ROTATE_180) | ||||
|     { | ||||
|         labelHeight = fontToDraw->getMaxTextHeight(labelTypedText.getText(), wildcard) * fontToDraw->getNumberOfLines(labelTypedText.getText(), wildcard) + fontToDraw->getSpacingAbove(labelTypedText.getText(), wildcard); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         labelHeight = fontToDraw->getStringWidth(labelTypedText.getText(), wildcard); | ||||
|     } | ||||
|     Rect labelRect(0, (graph->getGraphAreaMarginTop() + valueToScreenYQ5(graph, valueScaled).round()) - labelHeight / 2, getWidth(), labelHeight); | ||||
|  | ||||
|     Rect dirty = labelRect & invalidatedArea; | ||||
|     if (!dirty.isEmpty()) | ||||
|     { | ||||
|         dirty.x -= labelRect.x; | ||||
|         dirty.y -= labelRect.y; | ||||
|         translateRectToAbsolute(labelRect); | ||||
|         LCD::StringVisuals visuals(fontToDraw, color, alpha, labelTypedText.getAlignment(), 0, labelRotation, labelTypedText.getTextDirection(), 0, WIDE_TEXT_NONE); | ||||
|         HAL::lcd().drawString(labelRect, dirty, visuals, labelTypedText.getText(), wildcard, 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| GraphTitle::GraphTitle() | ||||
|     : titleTypedText(TypedText(TYPED_TEXT_INVALID)), titleRotation(TEXT_ROTATE_0) | ||||
| { | ||||
| } | ||||
|  | ||||
| void GraphTitle::setTitleTypedText(const TypedText& typedText) | ||||
| { | ||||
|     titleTypedText = typedText; | ||||
| } | ||||
|  | ||||
| TypedText GraphTitle::getTitleTypedText() const | ||||
| { | ||||
|     return titleTypedText; | ||||
| } | ||||
|  | ||||
| void GraphTitle::setTitleRotation(TextRotation rotation) | ||||
| { | ||||
|     titleRotation = rotation; | ||||
| } | ||||
|  | ||||
| TextRotation GraphTitle::getTitleRotation() const | ||||
| { | ||||
|     return titleRotation; | ||||
| } | ||||
|  | ||||
| void GraphTitle::draw(const Rect& invalidatedArea) const | ||||
| { | ||||
|     if (!titleTypedText.hasValidId()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|     const Font* fontToDraw = titleTypedText.getFont(); | ||||
|     if (!fontToDraw) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const AbstractDataGraph* graph = getGraph(); | ||||
|     const uint8_t alpha = LCD::div255(getAlpha() * graph->getAlpha()); | ||||
|     if (alpha == 0) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const uint16_t lineHeight = fontToDraw->getMaxTextHeight(titleTypedText.getText()) * fontToDraw->getNumberOfLines(titleTypedText.getText()) + fontToDraw->getSpacingAbove(titleTypedText.getText()); | ||||
|  | ||||
|     Rect labelRect(rect); | ||||
|     // Adjust to make label centered | ||||
|     if (titleRotation == TEXT_ROTATE_0 || titleRotation == TEXT_ROTATE_180) | ||||
|     { | ||||
|         labelRect.y += (labelRect.height - lineHeight) / 2; | ||||
|         labelRect.height = lineHeight; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         labelRect.x += (labelRect.width - lineHeight) / 2; | ||||
|         labelRect.width = lineHeight; | ||||
|     } | ||||
|  | ||||
|     Rect dirty = labelRect & invalidatedArea; | ||||
|     if (!dirty.isEmpty()) | ||||
|     { | ||||
|         dirty.x -= labelRect.x; | ||||
|         dirty.y -= labelRect.y; | ||||
|         translateRectToAbsolute(labelRect); | ||||
|         LCD::StringVisuals visuals(fontToDraw, getColor(), alpha, titleTypedText.getAlignment(), 0, titleRotation, titleTypedText.getTextDirection(), 0, WIDE_TEXT_NONE); | ||||
|         HAL::lcd().drawString(labelRect, dirty, visuals, titleTypedText.getText(), 0, 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool GraphTitle::drawCanvasWidget(const Rect& /*invalidatedArea*/) const | ||||
| { | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| void GraphTitle::invalidateGraphPointAt(int16_t index) | ||||
| { | ||||
| } | ||||
|  | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,80 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/graph/GraphScroll.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| DataGraphScroll::DataGraphScroll(int16_t capacity, int* values) | ||||
|     : AbstractDataGraphWithY(capacity, values), current(0) | ||||
| { | ||||
| } | ||||
|  | ||||
| void DataGraphScroll::clear() | ||||
| { | ||||
|     AbstractDataGraphWithY::clear(); | ||||
|     current = 0; | ||||
| } | ||||
|  | ||||
| int32_t DataGraphScroll::indexToGlobalIndex(int16_t index) const | ||||
| { | ||||
|     if (usedCapacity < maxCapacity) | ||||
|     { | ||||
|         return realIndex(index); | ||||
|     } | ||||
|     return (dataCounter - maxCapacity) + index; | ||||
| } | ||||
|  | ||||
| void DataGraphScroll::beforeAddValue() | ||||
| { | ||||
|     if (usedCapacity == maxCapacity) | ||||
|     { | ||||
|         invalidateAllXAxisPoints(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| int16_t DataGraphScroll::addValue(int value) | ||||
| { | ||||
|     const bool graphFull = usedCapacity == maxCapacity; | ||||
|     const int16_t index = current++; | ||||
|     current %= maxCapacity; | ||||
|     if (index == usedCapacity) | ||||
|     { | ||||
|         usedCapacity++; | ||||
|     } | ||||
|     yValues[index] = value; | ||||
|     if (graphFull) | ||||
|     { | ||||
|         invalidateGraphArea(); | ||||
|         invalidateAllXAxisPoints(); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         invalidateGraphPointAt(index); | ||||
|     } | ||||
|     return index; | ||||
| } | ||||
|  | ||||
| int16_t DataGraphScroll::realIndex(int16_t index) const | ||||
| { | ||||
|     return usedCapacity < maxCapacity ? index : (index + current) % maxCapacity; | ||||
| } | ||||
|  | ||||
| CWRUtil::Q5 DataGraphScroll::indexToXQ5(int16_t index) const | ||||
| { | ||||
|     return CWRUtil::toQ5(index); | ||||
| } | ||||
|  | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,55 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/graph/GraphWrapAndClear.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| DataGraphWrapAndClear::DataGraphWrapAndClear(int16_t capacity, int* values) | ||||
|     : AbstractDataGraphWithY(capacity, values) | ||||
| { | ||||
| } | ||||
|  | ||||
| int32_t DataGraphWrapAndClear::indexToGlobalIndex(int16_t index) const | ||||
| { | ||||
|     return (this->dataCounter - this->usedCapacity) + index; | ||||
| } | ||||
|  | ||||
| void DataGraphWrapAndClear::beforeAddValue() | ||||
| { | ||||
|     if (usedCapacity >= maxCapacity) | ||||
|     { | ||||
|         invalidateAllXAxisPoints(); | ||||
|         clear(); | ||||
|         invalidateGraphArea(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| int16_t DataGraphWrapAndClear::addValue(int value) | ||||
| { | ||||
|     const bool clearGraph = (usedCapacity == 0); | ||||
|     const int16_t index = usedCapacity; | ||||
|     usedCapacity++; | ||||
|     yValues[realIndex(index)] = value; | ||||
|     if (clearGraph) | ||||
|     { | ||||
|         // Label sizes might have grown, also invalidate new sizes | ||||
|         invalidateAllXAxisPoints(); | ||||
|     } | ||||
|     invalidateGraphPointAt(index); | ||||
|     return index; | ||||
| } | ||||
|  | ||||
| } // namespace touchgfx | ||||
| @@ -0,0 +1,98 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * This file is part of the TouchGFX 4.16.1 distribution. | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under Ultimate Liberty license | ||||
|   * SLA0044, the "License"; You may not use this file except in compliance with | ||||
|   * the License. You may obtain a copy of the License at: | ||||
|   *                             www.st.com/SLA0044 | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| #include <touchgfx/widgets/graph/GraphWrapAndOverwrite.hpp> | ||||
|  | ||||
| namespace touchgfx | ||||
| { | ||||
| DataGraphWrapAndOverwrite::DataGraphWrapAndOverwrite(int16_t capacity, int* values) | ||||
|     : AbstractDataGraphWithY(capacity, values), current(0) | ||||
| { | ||||
| } | ||||
|  | ||||
| void DataGraphWrapAndOverwrite::clear() | ||||
| { | ||||
|     AbstractDataGraphWithY::clear(); | ||||
|     current = 0; | ||||
| } | ||||
|  | ||||
| int32_t DataGraphWrapAndOverwrite::indexToGlobalIndex(int16_t index) const | ||||
| { | ||||
|     if (this->usedCapacity < this->maxCapacity) | ||||
|     { | ||||
|         return index; | ||||
|     } | ||||
|     const int16_t gapIndex = this->getGapBeforeIndex(); | ||||
|     if (index < gapIndex) | ||||
|     { | ||||
|         return (this->dataCounter - gapIndex) + index; | ||||
|     } | ||||
|     return ((this->dataCounter - gapIndex) - this->maxCapacity) + index; | ||||
| } | ||||
|  | ||||
| void DataGraphWrapAndOverwrite::beforeAddValue() | ||||
| { | ||||
|     if (current == 0 && usedCapacity >= maxCapacity) | ||||
|     { | ||||
|         int xMin = getGraphRangeXMin(); | ||||
|         int xMax = getGraphRangeXMax(); | ||||
|         for (int i = xMin; i < 0; i++) | ||||
|         { | ||||
|             invalidateXAxisPointAt(i); | ||||
|         } | ||||
|         for (int i = maxCapacity; i <= xMax; i++) | ||||
|         { | ||||
|             invalidateXAxisPointAt(i); | ||||
|         } | ||||
|     } | ||||
|     if (usedCapacity >= maxCapacity) | ||||
|     { | ||||
|         invalidateGraphPointAt(current); | ||||
|         invalidateXAxisPointAt(current); | ||||
|     } | ||||
| } | ||||
|  | ||||
| int16_t DataGraphWrapAndOverwrite::addValue(int value) | ||||
| { | ||||
|     const int16_t index = current++; | ||||
|     current %= maxCapacity; | ||||
|     if (index == usedCapacity) | ||||
|     { | ||||
|         usedCapacity++; | ||||
|     } | ||||
|     yValues[realIndex(index)] = value; | ||||
|     setGapBeforeIndex(index + 1); | ||||
|     invalidateGraphPointAt(index); | ||||
|     if (usedCapacity >= maxCapacity) | ||||
|     { | ||||
|         invalidateXAxisPointAt(index); | ||||
|     } | ||||
|     if (index == 0 && usedCapacity >= maxCapacity) | ||||
|     { | ||||
|         int xMin = getGraphRangeXMin(); | ||||
|         int xMax = getGraphRangeXMax(); | ||||
|         for (int i = xMin; i < 0; i++) | ||||
|         { | ||||
|             invalidateXAxisPointAt(i); | ||||
|         } | ||||
|         for (int i = maxCapacity; i <= xMax; i++) | ||||
|         { | ||||
|             invalidateXAxisPointAt(i); | ||||
|         } | ||||
|     } | ||||
|     return index; | ||||
| } | ||||
|  | ||||
| } // namespace touchgfx | ||||
		Reference in New Issue
	
	Block a user