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 | #include "SDL_sysvideo.h" |
24 | #include "SDL_surface_c.h" |
25 | #include "SDL_blit_auto.h" |
26 | #include "SDL_blit_copy.h" |
27 | #include "SDL_blit_slow.h" |
28 | #include "SDL_RLEaccel_c.h" |
29 | #include "SDL_pixels_c.h" |
30 | |
31 | // The general purpose software blit routine |
32 | static bool SDLCALL SDL_SoftBlit(SDL_Surface *src, const SDL_Rect *srcrect, |
33 | SDL_Surface *dst, const SDL_Rect *dstrect) |
34 | { |
35 | bool okay; |
36 | int src_locked; |
37 | int dst_locked; |
38 | |
39 | // Everything is okay at the beginning... |
40 | okay = true; |
41 | |
42 | // Lock the destination if it's in hardware |
43 | dst_locked = 0; |
44 | if (SDL_MUSTLOCK(dst)) { |
45 | if (!SDL_LockSurface(dst)) { |
46 | okay = false; |
47 | } else { |
48 | dst_locked = 1; |
49 | } |
50 | } |
51 | // Lock the source if it's in hardware |
52 | src_locked = 0; |
53 | if (SDL_MUSTLOCK(src)) { |
54 | if (!SDL_LockSurface(src)) { |
55 | okay = false; |
56 | } else { |
57 | src_locked = 1; |
58 | } |
59 | } |
60 | |
61 | // Set up source and destination buffer pointers, and BLIT! |
62 | if (okay) { |
63 | SDL_BlitFunc RunBlit; |
64 | SDL_BlitInfo *info = &src->map.info; |
65 | |
66 | // Set up the blit information |
67 | info->src = (Uint8 *)src->pixels + |
68 | (Uint16)srcrect->y * src->pitch + |
69 | (Uint16)srcrect->x * info->src_fmt->bytes_per_pixel; |
70 | info->src_w = srcrect->w; |
71 | info->src_h = srcrect->h; |
72 | info->src_pitch = src->pitch; |
73 | info->src_skip = |
74 | info->src_pitch - info->src_w * info->src_fmt->bytes_per_pixel; |
75 | info->dst = |
76 | (Uint8 *)dst->pixels + (Uint16)dstrect->y * dst->pitch + |
77 | (Uint16)dstrect->x * info->dst_fmt->bytes_per_pixel; |
78 | info->dst_w = dstrect->w; |
79 | info->dst_h = dstrect->h; |
80 | info->dst_pitch = dst->pitch; |
81 | info->dst_skip = |
82 | info->dst_pitch - info->dst_w * info->dst_fmt->bytes_per_pixel; |
83 | RunBlit = (SDL_BlitFunc)src->map.data; |
84 | |
85 | // Run the actual software blit |
86 | RunBlit(info); |
87 | } |
88 | |
89 | // We need to unlock the surfaces if they're locked |
90 | if (dst_locked) { |
91 | SDL_UnlockSurface(dst); |
92 | } |
93 | if (src_locked) { |
94 | SDL_UnlockSurface(src); |
95 | } |
96 | // Blit is done! |
97 | return okay; |
98 | } |
99 | |
100 | #ifdef SDL_HAVE_BLIT_AUTO |
101 | |
102 | #ifdef SDL_PLATFORM_MACOS |
103 | #include <sys/sysctl.h> |
104 | |
105 | static bool SDL_UseAltivecPrefetch(void) |
106 | { |
107 | const char key[] = "hw.l3cachesize" ; |
108 | u_int64_t result = 0; |
109 | size_t typeSize = sizeof(result); |
110 | |
111 | if (sysctlbyname(key, &result, &typeSize, NULL, 0) == 0 && result > 0) { |
112 | return true; |
113 | } else { |
114 | return false; |
115 | } |
116 | } |
117 | #else |
118 | static bool SDL_UseAltivecPrefetch(void) |
119 | { |
120 | // Just guess G4 |
121 | return true; |
122 | } |
123 | #endif // SDL_PLATFORM_MACOS |
124 | |
125 | static SDL_BlitFunc SDL_ChooseBlitFunc(SDL_PixelFormat src_format, SDL_PixelFormat dst_format, int flags, |
126 | SDL_BlitFuncEntry *entries) |
127 | { |
128 | int i, flagcheck = (flags & (SDL_COPY_MODULATE_MASK | SDL_COPY_BLEND_MASK | SDL_COPY_COLORKEY | SDL_COPY_NEAREST)); |
129 | static unsigned int features = 0x7fffffff; |
130 | |
131 | // Get the available CPU features |
132 | if (features == 0x7fffffff) { |
133 | features = SDL_CPU_ANY; |
134 | if (SDL_HasMMX()) { |
135 | features |= SDL_CPU_MMX; |
136 | } |
137 | if (SDL_HasSSE()) { |
138 | features |= SDL_CPU_SSE; |
139 | } |
140 | if (SDL_HasSSE2()) { |
141 | features |= SDL_CPU_SSE2; |
142 | } |
143 | if (SDL_HasAltiVec()) { |
144 | if (SDL_UseAltivecPrefetch()) { |
145 | features |= SDL_CPU_ALTIVEC_PREFETCH; |
146 | } else { |
147 | features |= SDL_CPU_ALTIVEC_NOPREFETCH; |
148 | } |
149 | } |
150 | } |
151 | |
152 | for (i = 0; entries[i].func; ++i) { |
153 | // Check for matching pixel formats |
154 | if (src_format != entries[i].src_format) { |
155 | continue; |
156 | } |
157 | if (dst_format != entries[i].dst_format) { |
158 | continue; |
159 | } |
160 | |
161 | // Check flags |
162 | if ((flagcheck & entries[i].flags) != flagcheck) { |
163 | continue; |
164 | } |
165 | |
166 | // Check CPU features |
167 | if ((entries[i].cpu & features) != entries[i].cpu) { |
168 | continue; |
169 | } |
170 | |
171 | // We found the best one! |
172 | return entries[i].func; |
173 | } |
174 | return NULL; |
175 | } |
176 | #endif // SDL_HAVE_BLIT_AUTO |
177 | |
178 | // Figure out which of many blit routines to set up on a surface |
179 | bool SDL_CalculateBlit(SDL_Surface *surface, SDL_Surface *dst) |
180 | { |
181 | SDL_BlitFunc blit = NULL; |
182 | SDL_BlitMap *map = &surface->map; |
183 | SDL_Colorspace src_colorspace = surface->colorspace; |
184 | SDL_Colorspace dst_colorspace = dst->colorspace; |
185 | |
186 | // We don't currently support blitting to < 8 bpp surfaces |
187 | if (SDL_BITSPERPIXEL(dst->format) < 8) { |
188 | SDL_InvalidateMap(map); |
189 | return SDL_SetError("Blit combination not supported" ); |
190 | } |
191 | |
192 | #ifdef SDL_HAVE_RLE |
193 | // Clean everything out to start |
194 | if (surface->flags & SDL_INTERNAL_SURFACE_RLEACCEL) { |
195 | SDL_UnRLESurface(surface, true); |
196 | } |
197 | #endif |
198 | |
199 | map->blit = SDL_SoftBlit; |
200 | map->info.src_surface = surface; |
201 | map->info.src_fmt = surface->fmt; |
202 | map->info.src_pal = surface->palette; |
203 | map->info.dst_surface = dst; |
204 | map->info.dst_fmt = dst->fmt; |
205 | map->info.dst_pal = dst->palette; |
206 | |
207 | #ifdef SDL_HAVE_RLE |
208 | // See if we can do RLE acceleration |
209 | if (map->info.flags & SDL_COPY_RLE_DESIRED) { |
210 | if (SDL_RLESurface(surface)) { |
211 | return true; |
212 | } |
213 | } |
214 | #endif |
215 | |
216 | // Choose a standard blit function |
217 | if (!blit) { |
218 | if (src_colorspace != dst_colorspace || |
219 | SDL_BYTESPERPIXEL(surface->format) > 4 || |
220 | SDL_BYTESPERPIXEL(dst->format) > 4) { |
221 | blit = SDL_Blit_Slow_Float; |
222 | } |
223 | } |
224 | if (!blit) { |
225 | if (map->identity && !(map->info.flags & ~SDL_COPY_RLE_DESIRED)) { |
226 | blit = SDL_BlitCopy; |
227 | } else if (SDL_ISPIXELFORMAT_10BIT(surface->format) || |
228 | SDL_ISPIXELFORMAT_10BIT(dst->format)) { |
229 | blit = SDL_Blit_Slow; |
230 | } |
231 | #ifdef SDL_HAVE_BLIT_0 |
232 | else if (SDL_BITSPERPIXEL(surface->format) < 8 && |
233 | SDL_ISPIXELFORMAT_INDEXED(surface->format)) { |
234 | blit = SDL_CalculateBlit0(surface); |
235 | } |
236 | #endif |
237 | #ifdef SDL_HAVE_BLIT_1 |
238 | else if (SDL_BYTESPERPIXEL(surface->format) == 1 && |
239 | SDL_ISPIXELFORMAT_INDEXED(surface->format)) { |
240 | blit = SDL_CalculateBlit1(surface); |
241 | } |
242 | #endif |
243 | #ifdef SDL_HAVE_BLIT_A |
244 | else if (map->info.flags & SDL_COPY_BLEND) { |
245 | blit = SDL_CalculateBlitA(surface); |
246 | } |
247 | #endif |
248 | #ifdef SDL_HAVE_BLIT_N |
249 | else { |
250 | blit = SDL_CalculateBlitN(surface); |
251 | } |
252 | #endif |
253 | } |
254 | #ifdef SDL_HAVE_BLIT_AUTO |
255 | if (!blit) { |
256 | SDL_PixelFormat src_format = surface->format; |
257 | SDL_PixelFormat dst_format = dst->format; |
258 | |
259 | blit = |
260 | SDL_ChooseBlitFunc(src_format, dst_format, map->info.flags, |
261 | SDL_GeneratedBlitFuncTable); |
262 | } |
263 | #endif |
264 | |
265 | #ifndef TEST_SLOW_BLIT |
266 | if (!blit) |
267 | #endif |
268 | { |
269 | SDL_PixelFormat src_format = surface->format; |
270 | SDL_PixelFormat dst_format = dst->format; |
271 | |
272 | if ((!SDL_ISPIXELFORMAT_INDEXED(src_format) || |
273 | (src_format == SDL_PIXELFORMAT_INDEX8 && surface->palette)) && |
274 | !SDL_ISPIXELFORMAT_FOURCC(src_format) && |
275 | (!SDL_ISPIXELFORMAT_INDEXED(dst_format) || |
276 | (dst_format == SDL_PIXELFORMAT_INDEX8 && dst->palette)) && |
277 | !SDL_ISPIXELFORMAT_FOURCC(dst_format)) { |
278 | blit = SDL_Blit_Slow; |
279 | } |
280 | } |
281 | map->data = (void *)blit; |
282 | |
283 | // Make sure we have a blit function |
284 | if (!blit) { |
285 | SDL_InvalidateMap(map); |
286 | return SDL_SetError("Blit combination not supported" ); |
287 | } |
288 | |
289 | return true; |
290 | } |
291 | |