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_blendpoint.h" |
27 | |
28 | static bool SDL_BlendPoint_RGB555(SDL_Surface *dst, int x, int y, SDL_BlendMode blendMode, Uint8 r, |
29 | Uint8 g, Uint8 b, Uint8 a) |
30 | { |
31 | unsigned inva = 0xff - a; |
32 | |
33 | switch (blendMode) { |
34 | case SDL_BLENDMODE_BLEND: |
35 | DRAW_SETPIXELXY_BLEND_RGB555(x, y); |
36 | break; |
37 | case SDL_BLENDMODE_BLEND_PREMULTIPLIED: |
38 | DRAW_SETPIXELXY_BLEND_CLAMPED_RGB555(x, y); |
39 | break; |
40 | case SDL_BLENDMODE_ADD: |
41 | case SDL_BLENDMODE_ADD_PREMULTIPLIED: |
42 | DRAW_SETPIXELXY_ADD_RGB555(x, y); |
43 | break; |
44 | case SDL_BLENDMODE_MOD: |
45 | DRAW_SETPIXELXY_MOD_RGB555(x, y); |
46 | break; |
47 | case SDL_BLENDMODE_MUL: |
48 | DRAW_SETPIXELXY_MUL_RGB555(x, y); |
49 | break; |
50 | default: |
51 | DRAW_SETPIXELXY_RGB555(x, y); |
52 | break; |
53 | } |
54 | return true; |
55 | } |
56 | |
57 | static bool SDL_BlendPoint_RGB565(SDL_Surface *dst, int x, int y, SDL_BlendMode blendMode, Uint8 r, |
58 | Uint8 g, Uint8 b, Uint8 a) |
59 | { |
60 | unsigned inva = 0xff - a; |
61 | |
62 | switch (blendMode) { |
63 | case SDL_BLENDMODE_BLEND: |
64 | DRAW_SETPIXELXY_BLEND_RGB565(x, y); |
65 | break; |
66 | case SDL_BLENDMODE_BLEND_PREMULTIPLIED: |
67 | DRAW_SETPIXELXY_BLEND_CLAMPED_RGB565(x, y); |
68 | break; |
69 | case SDL_BLENDMODE_ADD: |
70 | case SDL_BLENDMODE_ADD_PREMULTIPLIED: |
71 | DRAW_SETPIXELXY_ADD_RGB565(x, y); |
72 | break; |
73 | case SDL_BLENDMODE_MOD: |
74 | DRAW_SETPIXELXY_MOD_RGB565(x, y); |
75 | break; |
76 | case SDL_BLENDMODE_MUL: |
77 | DRAW_SETPIXELXY_MUL_RGB565(x, y); |
78 | break; |
79 | default: |
80 | DRAW_SETPIXELXY_RGB565(x, y); |
81 | break; |
82 | } |
83 | return true; |
84 | } |
85 | |
86 | static bool SDL_BlendPoint_XRGB8888(SDL_Surface *dst, int x, int y, SDL_BlendMode blendMode, Uint8 r, |
87 | Uint8 g, Uint8 b, Uint8 a) |
88 | { |
89 | unsigned inva = 0xff - a; |
90 | |
91 | switch (blendMode) { |
92 | case SDL_BLENDMODE_BLEND: |
93 | DRAW_SETPIXELXY_BLEND_XRGB8888(x, y); |
94 | break; |
95 | case SDL_BLENDMODE_BLEND_PREMULTIPLIED: |
96 | DRAW_SETPIXELXY_BLEND_CLAMPED_XRGB8888(x, y); |
97 | break; |
98 | case SDL_BLENDMODE_ADD: |
99 | case SDL_BLENDMODE_ADD_PREMULTIPLIED: |
100 | DRAW_SETPIXELXY_ADD_XRGB8888(x, y); |
101 | break; |
102 | case SDL_BLENDMODE_MOD: |
103 | DRAW_SETPIXELXY_MOD_XRGB8888(x, y); |
104 | break; |
105 | case SDL_BLENDMODE_MUL: |
106 | DRAW_SETPIXELXY_MUL_XRGB8888(x, y); |
107 | break; |
108 | default: |
109 | DRAW_SETPIXELXY_XRGB8888(x, y); |
110 | break; |
111 | } |
112 | return true; |
113 | } |
114 | |
115 | static bool SDL_BlendPoint_ARGB8888(SDL_Surface *dst, int x, int y, SDL_BlendMode blendMode, |
116 | Uint8 r, Uint8 g, Uint8 b, Uint8 a) |
117 | { |
118 | unsigned inva = 0xff - a; |
119 | |
120 | switch (blendMode) { |
121 | case SDL_BLENDMODE_BLEND: |
122 | DRAW_SETPIXELXY_BLEND_ARGB8888(x, y); |
123 | break; |
124 | case SDL_BLENDMODE_BLEND_PREMULTIPLIED: |
125 | DRAW_SETPIXELXY_BLEND_CLAMPED_ARGB8888(x, y); |
126 | break; |
127 | case SDL_BLENDMODE_ADD: |
128 | case SDL_BLENDMODE_ADD_PREMULTIPLIED: |
129 | DRAW_SETPIXELXY_ADD_ARGB8888(x, y); |
130 | break; |
131 | case SDL_BLENDMODE_MOD: |
132 | DRAW_SETPIXELXY_MOD_ARGB8888(x, y); |
133 | break; |
134 | case SDL_BLENDMODE_MUL: |
135 | DRAW_SETPIXELXY_MUL_ARGB8888(x, y); |
136 | break; |
137 | default: |
138 | DRAW_SETPIXELXY_ARGB8888(x, y); |
139 | break; |
140 | } |
141 | return true; |
142 | } |
143 | |
144 | static bool SDL_BlendPoint_RGB(SDL_Surface *dst, int x, int y, SDL_BlendMode blendMode, Uint8 r, |
145 | 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 | DRAW_SETPIXELXY2_BLEND_RGB(x, y); |
155 | break; |
156 | case SDL_BLENDMODE_BLEND_PREMULTIPLIED: |
157 | DRAW_SETPIXELXY2_BLEND_CLAMPED_RGB(x, y); |
158 | break; |
159 | case SDL_BLENDMODE_ADD: |
160 | case SDL_BLENDMODE_ADD_PREMULTIPLIED: |
161 | DRAW_SETPIXELXY2_ADD_RGB(x, y); |
162 | break; |
163 | case SDL_BLENDMODE_MOD: |
164 | DRAW_SETPIXELXY2_MOD_RGB(x, y); |
165 | break; |
166 | case SDL_BLENDMODE_MUL: |
167 | DRAW_SETPIXELXY2_MUL_RGB(x, y); |
168 | break; |
169 | default: |
170 | DRAW_SETPIXELXY2_RGB(x, y); |
171 | break; |
172 | } |
173 | return true; |
174 | case 4: |
175 | switch (blendMode) { |
176 | case SDL_BLENDMODE_BLEND: |
177 | DRAW_SETPIXELXY4_BLEND_RGB(x, y); |
178 | break; |
179 | case SDL_BLENDMODE_BLEND_PREMULTIPLIED: |
180 | DRAW_SETPIXELXY4_BLEND_CLAMPED_RGB(x, y); |
181 | break; |
182 | case SDL_BLENDMODE_ADD: |
183 | case SDL_BLENDMODE_ADD_PREMULTIPLIED: |
184 | DRAW_SETPIXELXY4_ADD_RGB(x, y); |
185 | break; |
186 | case SDL_BLENDMODE_MOD: |
187 | DRAW_SETPIXELXY4_MOD_RGB(x, y); |
188 | break; |
189 | case SDL_BLENDMODE_MUL: |
190 | DRAW_SETPIXELXY4_MUL_RGB(x, y); |
191 | break; |
192 | default: |
193 | DRAW_SETPIXELXY4_RGB(x, y); |
194 | break; |
195 | } |
196 | return true; |
197 | default: |
198 | return SDL_Unsupported(); |
199 | } |
200 | } |
201 | |
202 | static bool SDL_BlendPoint_RGBA(SDL_Surface *dst, int x, int y, SDL_BlendMode blendMode, Uint8 r, |
203 | 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 | DRAW_SETPIXELXY4_BLEND_RGBA(x, y); |
213 | break; |
214 | case SDL_BLENDMODE_BLEND_PREMULTIPLIED: |
215 | DRAW_SETPIXELXY4_BLEND_CLAMPED_RGBA(x, y); |
216 | break; |
217 | case SDL_BLENDMODE_ADD: |
218 | case SDL_BLENDMODE_ADD_PREMULTIPLIED: |
219 | DRAW_SETPIXELXY4_ADD_RGBA(x, y); |
220 | break; |
221 | case SDL_BLENDMODE_MOD: |
222 | DRAW_SETPIXELXY4_MOD_RGBA(x, y); |
223 | break; |
224 | case SDL_BLENDMODE_MUL: |
225 | DRAW_SETPIXELXY4_MUL_RGBA(x, y); |
226 | break; |
227 | default: |
228 | DRAW_SETPIXELXY4_RGBA(x, y); |
229 | break; |
230 | } |
231 | return true; |
232 | default: |
233 | return SDL_Unsupported(); |
234 | } |
235 | } |
236 | |
237 | bool SDL_BlendPoint(SDL_Surface *dst, int x, int y, SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a) |
238 | { |
239 | if (!SDL_SurfaceValid(dst)) { |
240 | return SDL_InvalidParamError("SDL_BlendPoint(): dst" ); |
241 | } |
242 | |
243 | // This function doesn't work on surfaces < 8 bpp |
244 | if (SDL_BITSPERPIXEL(dst->format) < 8) { |
245 | return SDL_SetError("SDL_BlendPoint(): Unsupported surface format" ); |
246 | } |
247 | |
248 | // Perform clipping |
249 | if (x < dst->clip_rect.x || y < dst->clip_rect.y || |
250 | x >= (dst->clip_rect.x + dst->clip_rect.w) || |
251 | y >= (dst->clip_rect.y + dst->clip_rect.h)) { |
252 | return true; |
253 | } |
254 | |
255 | if (blendMode == SDL_BLENDMODE_BLEND || blendMode == SDL_BLENDMODE_ADD) { |
256 | r = DRAW_MUL(r, a); |
257 | g = DRAW_MUL(g, a); |
258 | b = DRAW_MUL(b, a); |
259 | } |
260 | |
261 | switch (dst->fmt->bits_per_pixel) { |
262 | case 15: |
263 | switch (dst->fmt->Rmask) { |
264 | case 0x7C00: |
265 | return SDL_BlendPoint_RGB555(dst, x, y, blendMode, r, g, b, a); |
266 | } |
267 | break; |
268 | case 16: |
269 | switch (dst->fmt->Rmask) { |
270 | case 0xF800: |
271 | return SDL_BlendPoint_RGB565(dst, x, y, blendMode, r, g, b, a); |
272 | } |
273 | break; |
274 | case 32: |
275 | switch (dst->fmt->Rmask) { |
276 | case 0x00FF0000: |
277 | if (!dst->fmt->Amask) { |
278 | return SDL_BlendPoint_XRGB8888(dst, x, y, blendMode, r, g, b, a); |
279 | } else { |
280 | return SDL_BlendPoint_ARGB8888(dst, x, y, blendMode, r, g, b, a); |
281 | } |
282 | // break; -Wunreachable-code-break |
283 | } |
284 | break; |
285 | default: |
286 | break; |
287 | } |
288 | |
289 | if (!dst->fmt->Amask) { |
290 | return SDL_BlendPoint_RGB(dst, x, y, blendMode, r, g, b, a); |
291 | } else { |
292 | return SDL_BlendPoint_RGBA(dst, x, y, blendMode, r, g, b, a); |
293 | } |
294 | } |
295 | |
296 | bool SDL_BlendPoints(SDL_Surface *dst, const SDL_Point *points, int count, SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a) |
297 | { |
298 | int minx, miny; |
299 | int maxx, maxy; |
300 | int i; |
301 | int x, y; |
302 | bool (*func)(SDL_Surface * dst, int x, int y, SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a) = NULL; |
303 | bool result = true; |
304 | |
305 | if (!SDL_SurfaceValid(dst)) { |
306 | return SDL_InvalidParamError("SDL_BlendPoints(): dst" ); |
307 | } |
308 | |
309 | // This function doesn't work on surfaces < 8 bpp |
310 | if (dst->fmt->bits_per_pixel < 8) { |
311 | return SDL_SetError("SDL_BlendPoints(): Unsupported surface format" ); |
312 | } |
313 | |
314 | if (blendMode == SDL_BLENDMODE_BLEND || blendMode == SDL_BLENDMODE_ADD) { |
315 | r = DRAW_MUL(r, a); |
316 | g = DRAW_MUL(g, a); |
317 | b = DRAW_MUL(b, a); |
318 | } |
319 | |
320 | // FIXME: Does this function pointer slow things down significantly? |
321 | switch (dst->fmt->bits_per_pixel) { |
322 | case 15: |
323 | switch (dst->fmt->Rmask) { |
324 | case 0x7C00: |
325 | func = SDL_BlendPoint_RGB555; |
326 | break; |
327 | } |
328 | break; |
329 | case 16: |
330 | switch (dst->fmt->Rmask) { |
331 | case 0xF800: |
332 | func = SDL_BlendPoint_RGB565; |
333 | break; |
334 | } |
335 | break; |
336 | case 32: |
337 | switch (dst->fmt->Rmask) { |
338 | case 0x00FF0000: |
339 | if (!dst->fmt->Amask) { |
340 | func = SDL_BlendPoint_XRGB8888; |
341 | } else { |
342 | func = SDL_BlendPoint_ARGB8888; |
343 | } |
344 | break; |
345 | } |
346 | break; |
347 | default: |
348 | break; |
349 | } |
350 | |
351 | if (!func) { |
352 | if (!dst->fmt->Amask) { |
353 | func = SDL_BlendPoint_RGB; |
354 | } else { |
355 | func = SDL_BlendPoint_RGBA; |
356 | } |
357 | } |
358 | |
359 | minx = dst->clip_rect.x; |
360 | maxx = dst->clip_rect.x + dst->clip_rect.w - 1; |
361 | miny = dst->clip_rect.y; |
362 | maxy = dst->clip_rect.y + dst->clip_rect.h - 1; |
363 | |
364 | for (i = 0; i < count; ++i) { |
365 | x = points[i].x; |
366 | y = points[i].y; |
367 | |
368 | if (x < minx || x > maxx || y < miny || y > maxy) { |
369 | continue; |
370 | } |
371 | result = func(dst, x, y, blendMode, r, g, b, a); |
372 | } |
373 | return result; |
374 | } |
375 | |
376 | #endif // SDL_VIDEO_RENDER_SW |
377 | |