]>
Commit | Line | Data |
---|---|---|
fff5f7d5 VZ |
1 | |
2 | /* arm_init.c - NEON optimised filter functions | |
3 | * | |
4 | * Copyright (c) 2013 Glenn Randers-Pehrson | |
5 | * Written by Mans Rullgard, 2011. | |
6 | * Last changed in libpng 1.5.15 [March 28, 2013] | |
7 | * | |
8 | * This code is released under the libpng license. | |
9 | * For conditions of distribution and use, see the disclaimer | |
10 | * and license in png.h | |
11 | */ | |
12 | /* Below, after checking __linux__, various non-C90 POSIX 1003.1 functions are | |
13 | * called. | |
14 | */ | |
15 | #define _POSIX_SOURCE 1 | |
16 | ||
17 | #include "../pngpriv.h" | |
18 | ||
19 | #ifdef PNG_ARM_NEON_SUPPORTED | |
20 | #ifdef PNG_ARM_NEON_CHECK_SUPPORTED /* Do run-time checks */ | |
21 | #include <signal.h> /* for sig_atomic_t */ | |
22 | ||
23 | #ifdef __ANDROID__ | |
24 | /* Linux provides access to information about CPU capabilites via | |
25 | * /proc/self/auxv, however Android blocks this while still claiming to be | |
26 | * Linux. The Andoid NDK, however, provides appropriate support. | |
27 | * | |
28 | * Documentation: http://www.kandroid.org/ndk/docs/CPU-ARM-NEON.html | |
29 | */ | |
30 | #include <cpu-features.h> | |
31 | ||
32 | static int | |
33 | png_have_neon(png_structp png_ptr) | |
34 | { | |
35 | /* This is a whole lot easier than the mess below, however it is probably | |
36 | * implemented as below, therefore it is better to cache the result (these | |
37 | * function calls may be slow!) | |
38 | */ | |
39 | PNG_UNUSED(png_ptr) | |
40 | return android_getCpuFamily() == ANDROID_CPU_FAMILY_ARM && | |
41 | (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0; | |
42 | } | |
43 | #elif defined(__linux__) | |
44 | /* The generic __linux__ implementation requires reading /proc/self/auxv and | |
45 | * looking at each element for one that records NEON capabilities. | |
46 | */ | |
47 | #include <unistd.h> /* for POSIX 1003.1 */ | |
48 | #include <errno.h> /* for EINTR */ | |
49 | ||
50 | #include <sys/types.h> | |
51 | #include <sys/stat.h> | |
52 | #include <fcntl.h> | |
53 | #include <elf.h> | |
54 | #include <asm/hwcap.h> | |
55 | ||
56 | /* A read call may be interrupted, in which case it returns -1 and sets errno to | |
57 | * EINTR if nothing was done, otherwise (if something was done) a partial read | |
58 | * may result. | |
59 | */ | |
60 | static size_t | |
61 | safe_read(png_structp png_ptr, int fd, void *buffer_in, size_t nbytes) | |
62 | { | |
63 | size_t ntotal = 0; | |
64 | char *buffer = png_voidcast(char*, buffer_in); | |
65 | ||
66 | while (nbytes > 0) | |
67 | { | |
68 | unsigned int nread; | |
69 | int iread; | |
70 | ||
71 | /* Passing nread > INT_MAX to read is implementation defined in POSIX | |
72 | * 1003.1, therefore despite the unsigned argument portable code must | |
73 | * limit the value to INT_MAX! | |
74 | */ | |
75 | if (nbytes > INT_MAX) | |
76 | nread = INT_MAX; | |
77 | ||
78 | else | |
79 | nread = (unsigned int)/*SAFE*/nbytes; | |
80 | ||
81 | iread = read(fd, buffer, nread); | |
82 | ||
83 | if (iread == -1) | |
84 | { | |
85 | /* This is the devil in the details, a read can terminate early with 0 | |
86 | * bytes read because of EINTR, yet it still returns -1 otherwise end | |
87 | * of file cannot be distinguished. | |
88 | */ | |
89 | if (errno != EINTR) | |
90 | { | |
91 | png_warning(png_ptr, "/proc read failed"); | |
92 | return 0; /* I.e. a permanent failure */ | |
93 | } | |
94 | } | |
95 | ||
96 | else if (iread < 0) | |
97 | { | |
98 | /* Not a valid 'read' result: */ | |
99 | png_warning(png_ptr, "OS /proc read bug"); | |
100 | return 0; | |
101 | } | |
102 | ||
103 | else if (iread > 0) | |
104 | { | |
105 | /* Continue reading until a permanent failure, or EOF */ | |
106 | buffer += iread; | |
107 | nbytes -= (unsigned int)/*SAFE*/iread; | |
108 | ntotal += (unsigned int)/*SAFE*/iread; | |
109 | } | |
110 | ||
111 | else | |
112 | return ntotal; | |
113 | } | |
114 | ||
115 | return ntotal; /* nbytes == 0 */ | |
116 | } | |
117 | ||
118 | static int | |
119 | png_have_neon(png_structp png_ptr) | |
120 | { | |
121 | int fd = open("/proc/self/auxv", O_RDONLY); | |
122 | Elf32_auxv_t aux; | |
123 | ||
124 | /* Failsafe: failure to open means no NEON */ | |
125 | if (fd == -1) | |
126 | { | |
127 | png_warning(png_ptr, "/proc/self/auxv open failed"); | |
128 | return 0; | |
129 | } | |
130 | ||
131 | while (safe_read(png_ptr, fd, &aux, sizeof aux) == sizeof aux) | |
132 | { | |
133 | if (aux.a_type == AT_HWCAP && (aux.a_un.a_val & HWCAP_NEON) != 0) | |
134 | { | |
135 | close(fd); | |
136 | return 1; | |
137 | } | |
138 | } | |
139 | ||
140 | close(fd); | |
141 | return 0; | |
142 | } | |
143 | #else | |
144 | /* We don't know how to do a run-time check on this system */ | |
145 | # error "no support for run-time ARM NEON checks" | |
146 | #endif /* OS checks */ | |
147 | #endif /* PNG_ARM_NEON_CHECK_SUPPORTED */ | |
148 | ||
149 | #ifndef PNG_ALIGNED_MEMORY_SUPPORTED | |
150 | # error "ALIGNED_MEMORY is required; set: -DPNG_ALIGNED_MEMORY_SUPPORTED" | |
151 | #endif | |
152 | ||
153 | void | |
154 | png_init_filter_functions_neon(png_structp pp, unsigned int bpp) | |
155 | { | |
156 | #ifdef PNG_ARM_NEON_API_SUPPORTED | |
157 | switch ((pp->options >> PNG_ARM_NEON) & 3) | |
158 | { | |
159 | case PNG_OPTION_UNSET: | |
160 | /* Allow the run-time check to execute if it has been enabled - | |
161 | * thus both API and CHECK can be turned on. If it isn't supported | |
162 | * this case will fall through to the 'default' below, which just | |
163 | * returns. | |
164 | */ | |
165 | #endif /* PNG_ARM_NEON_API_SUPPORTED */ | |
166 | #ifdef PNG_ARM_NEON_CHECK_SUPPORTED | |
167 | { | |
168 | static volatile sig_atomic_t no_neon = -1; /* not checked */ | |
169 | ||
170 | if (no_neon < 0) | |
171 | no_neon = !png_have_neon(pp); | |
172 | ||
173 | if (no_neon) | |
174 | return; | |
175 | } | |
176 | #ifdef PNG_ARM_NEON_API_SUPPORTED | |
177 | break; | |
178 | #endif | |
179 | #endif /* PNG_ARM_NEON_CHECK_SUPPORTED */ | |
180 | #ifdef PNG_ARM_NEON_API_SUPPORTED | |
181 | case PNG_OPTION_ON: | |
182 | /* Option turned on */ | |
183 | break; | |
184 | ||
185 | default: /* OFF or INVALID */ | |
186 | return; | |
187 | } | |
188 | #endif | |
189 | ||
190 | /* IMPORTANT: any new external functions used here must be declared using | |
191 | * PNG_INTERNAL_FUNCTION in ../pngpriv.h. This is required so that the | |
192 | * 'prefix' option to configure works: | |
193 | * | |
194 | * ./configure --with-libpng-prefix=foobar_ | |
195 | * | |
196 | * Verify you have got this right by running the above command, doing a build | |
197 | * and examining pngprefix.h; it must contain a #define for every external | |
198 | * function you add. (Notice that this happens automatically for the | |
199 | * initialization function.) | |
200 | */ | |
201 | pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up_neon; | |
202 | ||
203 | if (bpp == 3) | |
204 | { | |
205 | pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_neon; | |
206 | pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_neon; | |
207 | pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = | |
208 | png_read_filter_row_paeth3_neon; | |
209 | } | |
210 | ||
211 | else if (bpp == 4) | |
212 | { | |
213 | pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_neon; | |
214 | pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_neon; | |
215 | pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = | |
216 | png_read_filter_row_paeth4_neon; | |
217 | } | |
218 | } | |
219 | #endif /* FILTER_OPTIMIZATIONS && __arm__ && __ARM_NEON__ */ |