1 /*
2 * Copyright (c) 2018 Jan Van Winkel <jan.van_winkel@dxplore.eu>
3 * Copyright (c) 2023 Nordic Semiconductor
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include "display_sdl_bottom.h"
9
10 #include <stdint.h>
11 #include <stddef.h>
12 #include <stdbool.h>
13 #include <SDL.h>
14 #include "nsi_tracing.h"
15
sdl_create_rounded_display_mask(uint16_t width,uint16_t height,uint32_t mask_color,void ** round_disp_mask,void * renderer)16 static int sdl_create_rounded_display_mask(uint16_t width, uint16_t height, uint32_t mask_color,
17 void **round_disp_mask, void *renderer)
18 {
19 *round_disp_mask = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888,
20 SDL_TEXTUREACCESS_STREAMING, width, height);
21 if (*round_disp_mask == NULL) {
22 nsi_print_warning("Failed to create SDL mask texture: %s", SDL_GetError());
23 return -1;
24 }
25 SDL_SetTextureBlendMode(*round_disp_mask, SDL_BLENDMODE_BLEND);
26
27 void *mask_data;
28 int mask_pitch;
29 int err;
30
31 err = SDL_LockTexture(*round_disp_mask, NULL, &mask_data, &mask_pitch);
32 if (err != 0) {
33 nsi_print_warning("Failed to lock mask texture: %d", err);
34 return -1;
35 }
36
37 /* Create ellipse mask */
38 float cx = width / 2.0f;
39 float cy = height / 2.0f;
40 float rx = width / 2.0f;
41 float ry = height / 2.0f;
42
43 for (int py = 0; py < height; py++) {
44 uint32_t *row = (uint32_t *)((uint8_t *)mask_data + mask_pitch * py);
45
46 for (int px = 0; px < width; px++) {
47 /* Calculate normalized distance from center */
48 float dx = (px - cx) / rx;
49 float dy = (py - cy) / ry;
50 float distance = dx * dx + dy * dy;
51
52 /* Inside ellipse: transparent, outside: mask color with full opacity */
53 if (distance <= 1.0f) {
54 row[px] = 0x00000000; /* Transparent */
55 } else {
56 uint32_t r = (mask_color >> 16) & 0xff;
57 uint32_t g = (mask_color >> 8) & 0xff;
58 uint32_t b = mask_color & 0xff;
59
60 row[px] = (0xFF << 24) | (r << 16) | (g << 8) | b;
61 }
62 }
63 }
64 SDL_UnlockTexture(*round_disp_mask);
65
66 return 0;
67 }
68
sdl_display_init_bottom(struct sdl_display_init_params * params)69 int sdl_display_init_bottom(struct sdl_display_init_params *params)
70 {
71 /* clang-format off */
72 *params->window = SDL_CreateWindow(params->title, SDL_WINDOWPOS_UNDEFINED,
73 SDL_WINDOWPOS_UNDEFINED,
74 params->width * params->zoom_pct / 100,
75 params->height * params->zoom_pct / 100, SDL_WINDOW_SHOWN);
76 /* clang-format on */
77 if (*params->window == NULL) {
78 nsi_print_warning("Failed to create SDL window %s: %s", params->title,
79 SDL_GetError());
80 return -1;
81 }
82 SDL_SetWindowData(*params->window, "zephyr_display", (void *)params->window_user_data);
83
84 if (params->use_accelerator) {
85 *params->renderer =
86 SDL_CreateRenderer(*params->window, -1, SDL_RENDERER_ACCELERATED);
87 } else {
88 *params->renderer = SDL_CreateRenderer(*params->window, -1, SDL_RENDERER_SOFTWARE);
89 }
90
91 if (*params->renderer == NULL) {
92 nsi_print_warning("Failed to create SDL renderer: %s",
93 SDL_GetError());
94 return -1;
95 }
96
97 *params->mutex = SDL_CreateMutex();
98 if (*params->mutex == NULL) {
99 nsi_print_warning("Failed to create SDL mutex: %s", SDL_GetError());
100 return -1;
101 }
102
103 SDL_RenderSetLogicalSize(*params->renderer, params->width, params->height);
104
105 *params->texture =
106 SDL_CreateTexture(*params->renderer, SDL_PIXELFORMAT_ARGB8888,
107 SDL_TEXTUREACCESS_STATIC, params->width, params->height);
108 if (*params->texture == NULL) {
109 nsi_print_warning("Failed to create SDL texture: %s", SDL_GetError());
110 return -1;
111 }
112 SDL_SetTextureBlendMode(*params->texture, SDL_BLENDMODE_BLEND);
113
114 *params->read_texture =
115 SDL_CreateTexture(*params->renderer, SDL_PIXELFORMAT_ARGB8888,
116 SDL_TEXTUREACCESS_TARGET, params->width, params->height);
117 if (*params->read_texture == NULL) {
118 nsi_print_warning("Failed to create SDL texture for read: %s", SDL_GetError());
119 return -1;
120 }
121
122 *params->background_texture =
123 SDL_CreateTexture(*params->renderer, SDL_PIXELFORMAT_ARGB8888,
124 SDL_TEXTUREACCESS_STREAMING, params->width, params->height);
125 if (*params->background_texture == NULL) {
126 nsi_print_warning("Failed to create SDL texture: %s", SDL_GetError());
127 return -1;
128 }
129
130 void *background_data;
131 int background_pitch;
132 int err;
133
134 err = SDL_LockTexture(*params->background_texture, NULL, &background_data,
135 &background_pitch);
136 if (err != 0) {
137 nsi_print_warning("Failed to lock background texture: %d", err);
138 return -1;
139 }
140 for (int y = 0; y < params->height; y++) {
141 uint32_t *row = (uint32_t *)((uint8_t *)background_data + background_pitch * y);
142
143 for (int x = 0; x < params->width; x++) {
144 bool x_cell_even = ((x / params->transparency_grid_cell_size) % 2) == 0;
145 bool y_cell_even = ((y / params->transparency_grid_cell_size) % 2) == 0;
146
147 if (x_cell_even == y_cell_even) {
148 row[x] = params->transparency_grid_color1 | 0xff000000;
149 } else {
150 row[x] = params->transparency_grid_color2 | 0xff000000;
151 }
152 }
153 }
154 SDL_UnlockTexture(*params->background_texture);
155
156 /* Create ellipse mask texture if rounded mask is enabled */
157 if (params->round_disp_mask != NULL) {
158 err = sdl_create_rounded_display_mask(params->width, params->height,
159 params->mask_color, params->round_disp_mask,
160 *params->renderer);
161 if (err != 0) {
162 nsi_print_warning("Failed to create rounded display mask");
163 return -1;
164 }
165 }
166
167 SDL_SetRenderDrawColor(*params->renderer, 0, 0, 0, 0xFF);
168 SDL_RenderClear(*params->renderer);
169 SDL_RenderCopy(*params->renderer, *params->background_texture, NULL, NULL);
170 SDL_RenderPresent(*params->renderer);
171
172 return 0;
173 }
174
sdl_display_write_bottom(const struct sdl_display_write_params * params)175 void sdl_display_write_bottom(const struct sdl_display_write_params *params)
176 {
177 SDL_Rect rect;
178 int err;
179
180 rect.x = params->x;
181 rect.y = params->y;
182 rect.w = params->width;
183 rect.h = params->height;
184
185 err = SDL_TryLockMutex(params->mutex);
186 if (err) {
187 nsi_print_warning("Failed to lock SDL mutex: %s", SDL_GetError());
188 return;
189 }
190
191 SDL_UpdateTexture(params->texture, &rect, params->buf, 4 * rect.w);
192
193 if (params->display_on && !params->frame_incomplete) {
194 SDL_RenderClear(params->renderer);
195 SDL_RenderCopy(params->renderer, params->background_texture, NULL, NULL);
196 SDL_SetTextureColorMod(params->texture,
197 (params->color_tint >> 16) & 0xff,
198 (params->color_tint >> 8) & 0xff,
199 params->color_tint & 0xff);
200 SDL_RenderCopy(params->renderer, params->texture, NULL, NULL);
201 SDL_SetTextureColorMod(params->texture, 255, 255, 255);
202
203 /* Apply ellipse mask if enabled */
204 if (params->round_disp_mask != NULL) {
205 SDL_SetRenderDrawBlendMode(params->renderer, SDL_BLENDMODE_MOD);
206 SDL_RenderCopy(params->renderer, params->round_disp_mask, NULL, NULL);
207 SDL_SetRenderDrawBlendMode(params->renderer, SDL_BLENDMODE_BLEND);
208 }
209
210 SDL_RenderPresent(params->renderer);
211 }
212
213 SDL_UnlockMutex(params->mutex);
214 }
215
sdl_display_read_bottom(const struct sdl_display_read_params * params)216 int sdl_display_read_bottom(const struct sdl_display_read_params *params)
217 {
218 SDL_Rect rect;
219 int err;
220
221 rect.x = params->x;
222 rect.y = params->y;
223 rect.w = params->width;
224 rect.h = params->height;
225
226 err = SDL_TryLockMutex(params->mutex);
227 if (err) {
228 nsi_print_warning("Failed to lock SDL mutex: %s", SDL_GetError());
229 return -1;
230 }
231
232 SDL_SetRenderTarget(params->renderer, params->read_texture);
233 SDL_SetTextureBlendMode(params->texture, SDL_BLENDMODE_NONE);
234
235 SDL_RenderClear(params->renderer);
236 SDL_RenderCopy(params->renderer, params->texture, NULL, NULL);
237 SDL_RenderReadPixels(params->renderer, &rect, SDL_PIXELFORMAT_ARGB8888, params->buf,
238 params->width * 4);
239
240 SDL_SetTextureBlendMode(params->texture, SDL_BLENDMODE_BLEND);
241 SDL_SetRenderTarget(params->renderer, NULL);
242
243 SDL_UnlockMutex(params->mutex);
244
245 return err;
246 }
247
sdl_display_blanking_off_bottom(const struct sdl_display_blanking_off_params * params)248 void sdl_display_blanking_off_bottom(const struct sdl_display_blanking_off_params *params)
249 {
250 SDL_RenderClear(params->renderer);
251 SDL_RenderCopy(params->renderer, params->background_texture, NULL, NULL);
252 SDL_SetTextureColorMod(params->texture, (params->color_tint >> 16) & 0xff,
253 (params->color_tint >> 8) & 0xff, params->color_tint & 0xff);
254 SDL_RenderCopy(params->renderer, params->texture, NULL, NULL);
255 SDL_SetTextureColorMod(params->texture, 255, 255, 255);
256
257 /* Apply ellipse mask if enabled */
258 if (params->round_disp_mask != NULL) {
259 SDL_SetRenderDrawBlendMode(params->renderer, SDL_BLENDMODE_MOD);
260 SDL_RenderCopy(params->renderer, params->round_disp_mask, NULL, NULL);
261 SDL_SetRenderDrawBlendMode(params->renderer, SDL_BLENDMODE_BLEND);
262 }
263
264 SDL_RenderPresent(params->renderer);
265 }
266
sdl_display_blanking_on_bottom(void * renderer)267 void sdl_display_blanking_on_bottom(void *renderer)
268 {
269 SDL_RenderClear(renderer);
270 SDL_RenderPresent(renderer);
271 }
272
sdl_display_cleanup_bottom(const struct sdl_display_cleanup_params * params)273 void sdl_display_cleanup_bottom(const struct sdl_display_cleanup_params *params)
274 {
275 if (*params->round_disp_mask != NULL) {
276 SDL_DestroyTexture(*params->round_disp_mask);
277 *params->round_disp_mask = NULL;
278 }
279
280 if (*params->background_texture != NULL) {
281 SDL_DestroyTexture(*params->background_texture);
282 *params->background_texture = NULL;
283 }
284
285 if (*params->read_texture != NULL) {
286 SDL_DestroyTexture(*params->read_texture);
287 *params->read_texture = NULL;
288 }
289
290 if (*params->texture != NULL) {
291 SDL_DestroyTexture(*params->texture);
292 *params->texture = NULL;
293 }
294
295 if (*params->mutex != NULL) {
296 SDL_DestroyMutex(*params->mutex);
297 *params->mutex = NULL;
298 }
299
300 if (*params->renderer != NULL) {
301 SDL_DestroyRenderer(*params->renderer);
302 *params->renderer = NULL;
303 }
304
305 if (*params->window != NULL) {
306 SDL_DestroyWindow(*params->window);
307 *params->window = NULL;
308 }
309 }
310