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_display_init_bottom(uint16_t height,uint16_t width,uint16_t zoom_pct,bool use_accelerator,void ** window,void ** renderer,void ** mutex,void ** texture,void ** read_texture,void ** background_texture,uint32_t transparency_grid_color1,uint32_t transparency_grid_color2,uint16_t transparency_grid_cell_size)16 int sdl_display_init_bottom(uint16_t height, uint16_t width, uint16_t zoom_pct,
17 			    bool use_accelerator, void **window, void **renderer, void **mutex,
18 			    void **texture, void **read_texture, void **background_texture,
19 			    uint32_t transparency_grid_color1, uint32_t transparency_grid_color2,
20 			    uint16_t transparency_grid_cell_size)
21 {
22 	*window = SDL_CreateWindow("Zephyr Display", SDL_WINDOWPOS_UNDEFINED,
23 				   SDL_WINDOWPOS_UNDEFINED, width * zoom_pct / 100,
24 				   height * zoom_pct / 100, SDL_WINDOW_SHOWN);
25 	if (*window == NULL) {
26 		nsi_print_warning("Failed to create SDL window: %s", SDL_GetError());
27 		return -1;
28 	}
29 
30 	if (use_accelerator) {
31 		*renderer = SDL_CreateRenderer(*window, -1, SDL_RENDERER_ACCELERATED);
32 	} else {
33 		*renderer = SDL_CreateRenderer(*window, -1, SDL_RENDERER_SOFTWARE);
34 	}
35 
36 	if (*renderer == NULL) {
37 		nsi_print_warning("Failed to create SDL renderer: %s",
38 				SDL_GetError());
39 		return -1;
40 	}
41 
42 	*mutex = SDL_CreateMutex();
43 	if (*mutex == NULL) {
44 		nsi_print_warning("Failed to create SDL mutex: %s", SDL_GetError());
45 		return -1;
46 	}
47 
48 	SDL_RenderSetLogicalSize(*renderer, width, height);
49 
50 	*texture = SDL_CreateTexture(*renderer, SDL_PIXELFORMAT_ARGB8888,
51 				     SDL_TEXTUREACCESS_STATIC, width, height);
52 	if (*texture == NULL) {
53 		nsi_print_warning("Failed to create SDL texture: %s", SDL_GetError());
54 		return -1;
55 	}
56 	SDL_SetTextureBlendMode(*texture, SDL_BLENDMODE_BLEND);
57 
58 	*read_texture = SDL_CreateTexture(*renderer, SDL_PIXELFORMAT_ARGB8888,
59 					  SDL_TEXTUREACCESS_TARGET, width, height);
60 	if (*read_texture == NULL) {
61 		nsi_print_warning("Failed to create SDL texture for read: %s", SDL_GetError());
62 		return -1;
63 	}
64 
65 	*background_texture = SDL_CreateTexture(*renderer, SDL_PIXELFORMAT_ARGB8888,
66 						SDL_TEXTUREACCESS_STREAMING, width, height);
67 	if (*background_texture == NULL) {
68 		nsi_print_warning("Failed to create SDL texture: %s", SDL_GetError());
69 		return -1;
70 	}
71 
72 	void *background_data;
73 	int background_pitch;
74 	int err;
75 
76 	err = SDL_LockTexture(*background_texture, NULL, &background_data, &background_pitch);
77 	if (err != 0) {
78 		nsi_print_warning("Failed to lock background texture: %d", err);
79 		return -1;
80 	}
81 	for (int y = 0; y < height; y++) {
82 		uint32_t *row = (uint32_t *)((uint8_t *)background_data + background_pitch * y);
83 
84 		for (int x = 0; x < width; x++) {
85 			bool x_cell_even = ((x / transparency_grid_cell_size) % 2) == 0;
86 			bool y_cell_even = ((y / transparency_grid_cell_size) % 2) == 0;
87 
88 			if (x_cell_even == y_cell_even) {
89 				row[x] = transparency_grid_color1 | 0xff000000;
90 			} else {
91 				row[x] = transparency_grid_color2 | 0xff000000;
92 			}
93 		}
94 	}
95 	SDL_UnlockTexture(*background_texture);
96 
97 	SDL_SetRenderDrawColor(*renderer, 0, 0, 0, 0xFF);
98 	SDL_RenderClear(*renderer);
99 	SDL_RenderCopy(*renderer, *background_texture, NULL, NULL);
100 	SDL_RenderPresent(*renderer);
101 
102 	return 0;
103 }
104 
sdl_display_write_bottom(const uint16_t height,const uint16_t width,const uint16_t x,const uint16_t y,void * renderer,void * mutex,void * texture,void * background_texture,uint8_t * buf,bool display_on,bool frame_incomplete)105 void sdl_display_write_bottom(const uint16_t height, const uint16_t width, const uint16_t x,
106 			      const uint16_t y, void *renderer, void *mutex, void *texture,
107 			      void *background_texture, uint8_t *buf, bool display_on,
108 			      bool frame_incomplete)
109 {
110 	SDL_Rect rect;
111 	int err;
112 
113 	rect.x = x;
114 	rect.y = y;
115 	rect.w = width;
116 	rect.h = height;
117 
118 	err = SDL_TryLockMutex(mutex);
119 	if (err) {
120 		nsi_print_warning("Failed to lock SDL mutex: %s", SDL_GetError());
121 		return;
122 	}
123 
124 	SDL_UpdateTexture(texture, &rect, buf, 4 * rect.w);
125 
126 	if (display_on && !frame_incomplete) {
127 		SDL_RenderClear(renderer);
128 		SDL_RenderCopy(renderer, background_texture, NULL, NULL);
129 		SDL_RenderCopy(renderer, texture, NULL, NULL);
130 		SDL_RenderPresent(renderer);
131 	}
132 
133 	SDL_UnlockMutex(mutex);
134 }
135 
sdl_display_read_bottom(const uint16_t height,const uint16_t width,const uint16_t x,const uint16_t y,void * renderer,void * buf,uint16_t pitch,void * mutex,void * texture,void * read_texture)136 int sdl_display_read_bottom(const uint16_t height, const uint16_t width,
137 			    const uint16_t x, const uint16_t y,
138 			    void *renderer, void *buf, uint16_t pitch,
139 			    void *mutex, void *texture, void *read_texture)
140 {
141 	SDL_Rect rect;
142 	int err;
143 
144 	rect.x = x;
145 	rect.y = y;
146 	rect.w = width;
147 	rect.h = height;
148 
149 	err = SDL_TryLockMutex(mutex);
150 	if (err) {
151 		nsi_print_warning("Failed to lock SDL mutex: %s", SDL_GetError());
152 		return -1;
153 	}
154 
155 	SDL_SetRenderTarget(renderer, read_texture);
156 
157 	SDL_RenderClear(renderer);
158 	SDL_RenderCopy(renderer, texture, NULL, NULL);
159 	SDL_RenderReadPixels(renderer, &rect, SDL_PIXELFORMAT_ARGB8888, buf, width * 4);
160 
161 	SDL_SetRenderTarget(renderer, NULL);
162 
163 	SDL_UnlockMutex(mutex);
164 
165 	return err;
166 }
167 
sdl_display_blanking_off_bottom(void * renderer,void * texture,void * background_texture)168 void sdl_display_blanking_off_bottom(void *renderer, void *texture, void *background_texture)
169 {
170 	SDL_RenderClear(renderer);
171 	SDL_RenderCopy(renderer, background_texture, NULL, NULL);
172 	SDL_RenderCopy(renderer, texture, NULL, NULL);
173 	SDL_RenderPresent(renderer);
174 }
175 
sdl_display_blanking_on_bottom(void * renderer)176 void sdl_display_blanking_on_bottom(void *renderer)
177 {
178 	SDL_RenderClear(renderer);
179 	SDL_RenderPresent(renderer);
180 }
181 
sdl_display_cleanup_bottom(void ** window,void ** renderer,void ** mutex,void ** texture,void ** read_texture,void ** background_texture)182 void sdl_display_cleanup_bottom(void **window, void **renderer, void **mutex, void **texture,
183 				void **read_texture, void **background_texture)
184 {
185 	if (*background_texture != NULL) {
186 		SDL_DestroyTexture(*background_texture);
187 		*background_texture = NULL;
188 	}
189 
190 	if (*read_texture != NULL) {
191 		SDL_DestroyTexture(*read_texture);
192 		*read_texture = NULL;
193 	}
194 
195 	if (*texture != NULL) {
196 		SDL_DestroyTexture(*texture);
197 		*texture = NULL;
198 	}
199 
200 	if (*mutex != NULL) {
201 		SDL_DestroyMutex(*mutex);
202 		*mutex = NULL;
203 	}
204 
205 	if (*renderer != NULL) {
206 		SDL_DestroyRenderer(*renderer);
207 		*renderer = NULL;
208 	}
209 
210 	if (*window != NULL) {
211 		SDL_DestroyWindow(*window);
212 		*window = NULL;
213 	}
214 }
215