1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org> |
4 | |
5 | This software is provided 'as-is', without any express or implied |
6 | warranty. In no event will the authors be held liable for any damages |
7 | arising from the use of this software. |
8 | |
9 | Permission is granted to anyone to use this software for any purpose, |
10 | including commercial applications, and to alter it and redistribute it |
11 | freely, subject to the following restrictions: |
12 | |
13 | 1. The origin of this software must not be misrepresented; you must not |
14 | claim that you wrote the original software. If you use this software |
15 | in a product, an acknowledgment in the product documentation would be |
16 | appreciated but is not required. |
17 | 2. Altered source versions must be plainly marked as such, and must not be |
18 | misrepresented as being the original software. |
19 | 3. This notice may not be removed or altered from any source distribution. |
20 | */ |
21 | #include "SDL_internal.h" |
22 | |
23 | #ifdef SDL_VIDEO_RENDER_SW |
24 | |
25 | #include "SDL_draw.h" |
26 | #include "SDL_blendfillrect.h" |
27 | |
28 | static bool SDL_BlendFillRect_RGB555(SDL_Surface *dst, const SDL_Rect *rect, |
29 | SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a) |
30 | { |
31 | unsigned inva = 0xff - a; |
32 | |
33 | switch (blendMode) { |
34 | case SDL_BLENDMODE_BLEND: |
35 | FILLRECT(Uint16, DRAW_SETPIXEL_BLEND_RGB555); |
36 | break; |
37 | case SDL_BLENDMODE_BLEND_PREMULTIPLIED: |
38 | FILLRECT(Uint16, DRAW_SETPIXEL_BLEND_CLAMPED_RGB555); |
39 | break; |
40 | case SDL_BLENDMODE_ADD: |
41 | case SDL_BLENDMODE_ADD_PREMULTIPLIED: |
42 | FILLRECT(Uint16, DRAW_SETPIXEL_ADD_RGB555); |
43 | break; |
44 | case SDL_BLENDMODE_MOD: |
45 | FILLRECT(Uint16, DRAW_SETPIXEL_MOD_RGB555); |
46 | break; |
47 | case SDL_BLENDMODE_MUL: |
48 | FILLRECT(Uint16, DRAW_SETPIXEL_MUL_RGB555); |
49 | break; |
50 | default: |
51 | FILLRECT(Uint16, DRAW_SETPIXEL_RGB555); |
52 | break; |
53 | } |
54 | return true; |
55 | } |
56 | |
57 | static bool SDL_BlendFillRect_RGB565(SDL_Surface *dst, const SDL_Rect *rect, |
58 | SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a) |
59 | { |
60 | unsigned inva = 0xff - a; |
61 | |
62 | switch (blendMode) { |
63 | case SDL_BLENDMODE_BLEND: |
64 | FILLRECT(Uint16, DRAW_SETPIXEL_BLEND_RGB565); |
65 | break; |
66 | case SDL_BLENDMODE_BLEND_PREMULTIPLIED: |
67 | FILLRECT(Uint16, DRAW_SETPIXEL_BLEND_CLAMPED_RGB565); |
68 | break; |
69 | case SDL_BLENDMODE_ADD: |
70 | case SDL_BLENDMODE_ADD_PREMULTIPLIED: |
71 | FILLRECT(Uint16, DRAW_SETPIXEL_ADD_RGB565); |
72 | break; |
73 | case SDL_BLENDMODE_MOD: |
74 | FILLRECT(Uint16, DRAW_SETPIXEL_MOD_RGB565); |
75 | break; |
76 | case SDL_BLENDMODE_MUL: |
77 | FILLRECT(Uint16, DRAW_SETPIXEL_MUL_RGB565); |
78 | break; |
79 | default: |
80 | FILLRECT(Uint16, DRAW_SETPIXEL_RGB565); |
81 | break; |
82 | } |
83 | return true; |
84 | } |
85 | |
86 | static bool SDL_BlendFillRect_XRGB8888(SDL_Surface *dst, const SDL_Rect *rect, |
87 | SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a) |
88 | { |
89 | unsigned inva = 0xff - a; |
90 | |
91 | switch (blendMode) { |
92 | case SDL_BLENDMODE_BLEND: |
93 | FILLRECT(Uint32, DRAW_SETPIXEL_BLEND_XRGB8888); |
94 | break; |
95 | case SDL_BLENDMODE_BLEND_PREMULTIPLIED: |
96 | FILLRECT(Uint32, DRAW_SETPIXEL_BLEND_CLAMPED_XRGB8888); |
97 | break; |
98 | case SDL_BLENDMODE_ADD: |
99 | case SDL_BLENDMODE_ADD_PREMULTIPLIED: |
100 | FILLRECT(Uint32, DRAW_SETPIXEL_ADD_XRGB8888); |
101 | break; |
102 | case SDL_BLENDMODE_MOD: |
103 | FILLRECT(Uint32, DRAW_SETPIXEL_MOD_XRGB8888); |
104 | break; |
105 | case SDL_BLENDMODE_MUL: |
106 | FILLRECT(Uint32, DRAW_SETPIXEL_MUL_XRGB8888); |
107 | break; |
108 | default: |
109 | FILLRECT(Uint32, DRAW_SETPIXEL_XRGB8888); |
110 | break; |
111 | } |
112 | return true; |
113 | } |
114 | |
115 | static bool SDL_BlendFillRect_ARGB8888(SDL_Surface *dst, const SDL_Rect *rect, |
116 | SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a) |
117 | { |
118 | unsigned inva = 0xff - a; |
119 | |
120 | switch (blendMode) { |
121 | case SDL_BLENDMODE_BLEND: |
122 | FILLRECT(Uint32, DRAW_SETPIXEL_BLEND_ARGB8888); |
123 | break; |
124 | case SDL_BLENDMODE_BLEND_PREMULTIPLIED: |
125 | FILLRECT(Uint32, DRAW_SETPIXEL_BLEND_CLAMPED_ARGB8888); |
126 | break; |
127 | case SDL_BLENDMODE_ADD: |
128 | case SDL_BLENDMODE_ADD_PREMULTIPLIED: |
129 | FILLRECT(Uint32, DRAW_SETPIXEL_ADD_ARGB8888); |
130 | break; |
131 | case SDL_BLENDMODE_MOD: |
132 | FILLRECT(Uint32, DRAW_SETPIXEL_MOD_ARGB8888); |
133 | break; |
134 | case SDL_BLENDMODE_MUL: |
135 | FILLRECT(Uint32, DRAW_SETPIXEL_MUL_ARGB8888); |
136 | break; |
137 | default: |
138 | FILLRECT(Uint32, DRAW_SETPIXEL_ARGB8888); |
139 | break; |
140 | } |
141 | return true; |
142 | } |
143 | |
144 | static bool SDL_BlendFillRect_RGB(SDL_Surface *dst, const SDL_Rect *rect, |
145 | SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a) |
146 | { |
147 | const SDL_PixelFormatDetails *fmt = dst->fmt; |
148 | unsigned inva = 0xff - a; |
149 | |
150 | switch (fmt->bytes_per_pixel) { |
151 | case 2: |
152 | switch (blendMode) { |
153 | case SDL_BLENDMODE_BLEND: |
154 | FILLRECT(Uint16, DRAW_SETPIXEL_BLEND_RGB); |
155 | break; |
156 | case SDL_BLENDMODE_BLEND_PREMULTIPLIED: |
157 | FILLRECT(Uint16, DRAW_SETPIXEL_BLEND_CLAMPED_RGB); |
158 | break; |
159 | case SDL_BLENDMODE_ADD: |
160 | case SDL_BLENDMODE_ADD_PREMULTIPLIED: |
161 | FILLRECT(Uint16, DRAW_SETPIXEL_ADD_RGB); |
162 | break; |
163 | case SDL_BLENDMODE_MOD: |
164 | FILLRECT(Uint16, DRAW_SETPIXEL_MOD_RGB); |
165 | break; |
166 | case SDL_BLENDMODE_MUL: |
167 | FILLRECT(Uint16, DRAW_SETPIXEL_MUL_RGB); |
168 | break; |
169 | default: |
170 | FILLRECT(Uint16, DRAW_SETPIXEL_RGB); |
171 | break; |
172 | } |
173 | return true; |
174 | case 4: |
175 | switch (blendMode) { |
176 | case SDL_BLENDMODE_BLEND: |
177 | FILLRECT(Uint32, DRAW_SETPIXEL_BLEND_RGB); |
178 | break; |
179 | case SDL_BLENDMODE_BLEND_PREMULTIPLIED: |
180 | FILLRECT(Uint32, DRAW_SETPIXEL_BLEND_CLAMPED_RGB); |
181 | break; |
182 | case SDL_BLENDMODE_ADD: |
183 | case SDL_BLENDMODE_ADD_PREMULTIPLIED: |
184 | FILLRECT(Uint32, DRAW_SETPIXEL_ADD_RGB); |
185 | break; |
186 | case SDL_BLENDMODE_MOD: |
187 | FILLRECT(Uint32, DRAW_SETPIXEL_MOD_RGB); |
188 | break; |
189 | case SDL_BLENDMODE_MUL: |
190 | FILLRECT(Uint32, DRAW_SETPIXEL_MUL_RGB); |
191 | break; |
192 | default: |
193 | FILLRECT(Uint32, DRAW_SETPIXEL_RGB); |
194 | break; |
195 | } |
196 | return true; |
197 | default: |
198 | return SDL_Unsupported(); |
199 | } |
200 | } |
201 | |
202 | static bool SDL_BlendFillRect_RGBA(SDL_Surface *dst, const SDL_Rect *rect, |
203 | SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a) |
204 | { |
205 | const SDL_PixelFormatDetails *fmt = dst->fmt; |
206 | unsigned inva = 0xff - a; |
207 | |
208 | switch (fmt->bytes_per_pixel) { |
209 | case 4: |
210 | switch (blendMode) { |
211 | case SDL_BLENDMODE_BLEND: |
212 | FILLRECT(Uint32, DRAW_SETPIXEL_BLEND_RGBA); |
213 | break; |
214 | case SDL_BLENDMODE_BLEND_PREMULTIPLIED: |
215 | FILLRECT(Uint32, DRAW_SETPIXEL_BLEND_CLAMPED_RGBA); |
216 | break; |
217 | case SDL_BLENDMODE_ADD: |
218 | case SDL_BLENDMODE_ADD_PREMULTIPLIED: |
219 | FILLRECT(Uint32, DRAW_SETPIXEL_ADD_RGBA); |
220 | break; |
221 | case SDL_BLENDMODE_MOD: |
222 | FILLRECT(Uint32, DRAW_SETPIXEL_MOD_RGBA); |
223 | break; |
224 | case SDL_BLENDMODE_MUL: |
225 | FILLRECT(Uint32, DRAW_SETPIXEL_MUL_RGBA); |
226 | break; |
227 | default: |
228 | FILLRECT(Uint32, DRAW_SETPIXEL_RGBA); |
229 | break; |
230 | } |
231 | return true; |
232 | default: |
233 | return SDL_Unsupported(); |
234 | } |
235 | } |
236 | |
237 | bool SDL_BlendFillRect(SDL_Surface *dst, const SDL_Rect *rect, SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a) |
238 | { |
239 | SDL_Rect clipped; |
240 | |
241 | if (!SDL_SurfaceValid(dst)) { |
242 | return SDL_InvalidParamError("SDL_BlendFillRect(): dst" ); |
243 | } |
244 | |
245 | // This function doesn't work on surfaces < 8 bpp |
246 | if (SDL_BITSPERPIXEL(dst->format) < 8) { |
247 | return SDL_SetError("SDL_BlendFillRect(): Unsupported surface format" ); |
248 | } |
249 | |
250 | // If 'rect' == NULL, then fill the whole surface |
251 | if (rect) { |
252 | // Perform clipping |
253 | if (!SDL_GetRectIntersection(rect, &dst->clip_rect, &clipped)) { |
254 | return true; |
255 | } |
256 | rect = &clipped; |
257 | } else { |
258 | rect = &dst->clip_rect; |
259 | } |
260 | |
261 | if (blendMode == SDL_BLENDMODE_BLEND || blendMode == SDL_BLENDMODE_ADD) { |
262 | r = DRAW_MUL(r, a); |
263 | g = DRAW_MUL(g, a); |
264 | b = DRAW_MUL(b, a); |
265 | } |
266 | |
267 | switch (dst->fmt->bits_per_pixel) { |
268 | case 15: |
269 | switch (dst->fmt->Rmask) { |
270 | case 0x7C00: |
271 | return SDL_BlendFillRect_RGB555(dst, rect, blendMode, r, g, b, a); |
272 | } |
273 | break; |
274 | case 16: |
275 | switch (dst->fmt->Rmask) { |
276 | case 0xF800: |
277 | return SDL_BlendFillRect_RGB565(dst, rect, blendMode, r, g, b, a); |
278 | } |
279 | break; |
280 | case 32: |
281 | switch (dst->fmt->Rmask) { |
282 | case 0x00FF0000: |
283 | if (!dst->fmt->Amask) { |
284 | return SDL_BlendFillRect_XRGB8888(dst, rect, blendMode, r, g, b, a); |
285 | } else { |
286 | return SDL_BlendFillRect_ARGB8888(dst, rect, blendMode, r, g, b, a); |
287 | } |
288 | // break; -Wunreachable-code-break |
289 | } |
290 | break; |
291 | default: |
292 | break; |
293 | } |
294 | |
295 | if (!dst->fmt->Amask) { |
296 | return SDL_BlendFillRect_RGB(dst, rect, blendMode, r, g, b, a); |
297 | } else { |
298 | return SDL_BlendFillRect_RGBA(dst, rect, blendMode, r, g, b, a); |
299 | } |
300 | } |
301 | |
302 | bool SDL_BlendFillRects(SDL_Surface *dst, const SDL_Rect *rects, int count, SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a) |
303 | { |
304 | SDL_Rect rect; |
305 | int i; |
306 | bool (*func)(SDL_Surface * dst, const SDL_Rect *rect, SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a) = NULL; |
307 | bool result = true; |
308 | |
309 | if (!SDL_SurfaceValid(dst)) { |
310 | return SDL_InvalidParamError("SDL_BlendFillRects(): dst" ); |
311 | } |
312 | |
313 | // This function doesn't work on surfaces < 8 bpp |
314 | if (dst->fmt->bits_per_pixel < 8) { |
315 | return SDL_SetError("SDL_BlendFillRects(): Unsupported surface format" ); |
316 | } |
317 | |
318 | if (blendMode == SDL_BLENDMODE_BLEND || blendMode == SDL_BLENDMODE_ADD) { |
319 | r = DRAW_MUL(r, a); |
320 | g = DRAW_MUL(g, a); |
321 | b = DRAW_MUL(b, a); |
322 | } |
323 | |
324 | // FIXME: Does this function pointer slow things down significantly? |
325 | switch (dst->fmt->bits_per_pixel) { |
326 | case 15: |
327 | switch (dst->fmt->Rmask) { |
328 | case 0x7C00: |
329 | func = SDL_BlendFillRect_RGB555; |
330 | } |
331 | break; |
332 | case 16: |
333 | switch (dst->fmt->Rmask) { |
334 | case 0xF800: |
335 | func = SDL_BlendFillRect_RGB565; |
336 | } |
337 | break; |
338 | case 32: |
339 | switch (dst->fmt->Rmask) { |
340 | case 0x00FF0000: |
341 | if (!dst->fmt->Amask) { |
342 | func = SDL_BlendFillRect_XRGB8888; |
343 | } else { |
344 | func = SDL_BlendFillRect_ARGB8888; |
345 | } |
346 | break; |
347 | } |
348 | break; |
349 | default: |
350 | break; |
351 | } |
352 | |
353 | if (!func) { |
354 | if (!dst->fmt->Amask) { |
355 | func = SDL_BlendFillRect_RGB; |
356 | } else { |
357 | func = SDL_BlendFillRect_RGBA; |
358 | } |
359 | } |
360 | |
361 | for (i = 0; i < count; ++i) { |
362 | // Perform clipping |
363 | if (!SDL_GetRectIntersection(&rects[i], &dst->clip_rect, &rect)) { |
364 | continue; |
365 | } |
366 | result = func(dst, &rect, blendMode, r, g, b, a); |
367 | } |
368 | return result; |
369 | } |
370 | |
371 | #endif // SDL_VIDEO_RENDER_SW |
372 | |