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