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
28static 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
57static 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
86static 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
115static 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
144static 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
202static 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
237bool 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
296bool 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