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
32static 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
105static 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
118static bool SDL_UseAltivecPrefetch(void)
119{
120 // Just guess G4
121 return true;
122}
123#endif // SDL_PLATFORM_MACOS
124
125static 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
179bool 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