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