/src/libavif/apps/shared/avifutil.c
Line | Count | Source |
1 | | // Copyright 2019 Joe Drago. All rights reserved. |
2 | | // SPDX-License-Identifier: BSD-2-Clause |
3 | | |
4 | | #include "avifutil.h" |
5 | | |
6 | | #include <ctype.h> |
7 | | #include <stdio.h> |
8 | | #include <string.h> |
9 | | |
10 | | #include "avifjpeg.h" |
11 | | #include "avifpng.h" |
12 | | #include "y4m.h" |
13 | | |
14 | | char * avifFileFormatToString(avifAppFileFormat format) |
15 | 0 | { |
16 | 0 | switch (format) { |
17 | 0 | case AVIF_APP_FILE_FORMAT_UNKNOWN: |
18 | 0 | return "unknown"; |
19 | 0 | case AVIF_APP_FILE_FORMAT_AVIF: |
20 | 0 | return "AVIF"; |
21 | 0 | case AVIF_APP_FILE_FORMAT_JPEG: |
22 | 0 | return "JPEG"; |
23 | 0 | case AVIF_APP_FILE_FORMAT_PNG: |
24 | 0 | return "PNG"; |
25 | 0 | case AVIF_APP_FILE_FORMAT_Y4M: |
26 | 0 | return "Y4M"; |
27 | 0 | } |
28 | 0 | return "unknown"; |
29 | 0 | } |
30 | | |
31 | | // |a| and |b| hold int32_t values. The int64_t type is used so that we can negate INT32_MIN without |
32 | | // overflowing int32_t. |
33 | | static int64_t calcGCD(int64_t a, int64_t b) |
34 | 0 | { |
35 | 0 | if (a < 0) { |
36 | 0 | a *= -1; |
37 | 0 | } |
38 | 0 | if (b < 0) { |
39 | 0 | b *= -1; |
40 | 0 | } |
41 | 0 | while (b != 0) { |
42 | 0 | int64_t r = a % b; |
43 | 0 | a = b; |
44 | 0 | b = r; |
45 | 0 | } |
46 | 0 | return a; |
47 | 0 | } |
48 | | |
49 | | static void printClapFraction(const char * name, int32_t n, int32_t d) |
50 | 0 | { |
51 | 0 | printf("%s: %d/%d", name, n, d); |
52 | 0 | if (d != 0) { |
53 | 0 | int64_t gcd = calcGCD(n, d); |
54 | 0 | if (gcd > 1) { |
55 | 0 | int32_t rn = (int32_t)(n / gcd); |
56 | 0 | int32_t rd = (int32_t)(d / gcd); |
57 | 0 | printf(" (%d/%d)", rn, rd); |
58 | 0 | } |
59 | 0 | } |
60 | 0 | } |
61 | | |
62 | | static void avifImageDumpInternal(const avifImage * avif, uint32_t gridCols, uint32_t gridRows, avifBool alphaPresent, avifProgressiveState progressiveState) |
63 | 0 | { |
64 | 0 | uint32_t width = avif->width; |
65 | 0 | uint32_t height = avif->height; |
66 | 0 | if (gridCols && gridRows) { |
67 | 0 | width *= gridCols; |
68 | 0 | height *= gridRows; |
69 | 0 | } |
70 | 0 | printf(" * Resolution : %ux%u\n", width, height); |
71 | 0 | printf(" * Bit Depth : %u\n", avif->depth); |
72 | 0 | printf(" * Format : %s\n", avifPixelFormatToString(avif->yuvFormat)); |
73 | 0 | if (avif->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) { |
74 | 0 | printf(" * Chroma Sam. Pos: %u\n", avif->yuvChromaSamplePosition); |
75 | 0 | } |
76 | 0 | printf(" * Alpha : %s\n", alphaPresent ? (avif->alphaPremultiplied ? "Premultiplied" : "Not premultiplied") : "Absent"); |
77 | 0 | printf(" * Range : %s\n", (avif->yuvRange == AVIF_RANGE_FULL) ? "Full" : "Limited"); |
78 | |
|
79 | 0 | printf(" * Color Primaries: %u\n", avif->colorPrimaries); |
80 | 0 | printf(" * Transfer Char. : %u\n", avif->transferCharacteristics); |
81 | 0 | printf(" * Matrix Coeffs. : %u\n", avif->matrixCoefficients); |
82 | |
|
83 | 0 | if (avif->icc.size != 0) { |
84 | 0 | printf(" * ICC Profile : Present (%" AVIF_FMT_ZU " bytes)\n", avif->icc.size); |
85 | 0 | } else { |
86 | 0 | printf(" * ICC Profile : Absent\n"); |
87 | 0 | } |
88 | 0 | if (avif->xmp.size != 0) { |
89 | 0 | printf(" * XMP Metadata : Present (%" AVIF_FMT_ZU " bytes)\n", avif->xmp.size); |
90 | 0 | } else { |
91 | 0 | printf(" * XMP Metadata : Absent\n"); |
92 | 0 | } |
93 | 0 | if (avif->exif.size != 0) { |
94 | 0 | printf(" * Exif Metadata : Present (%" AVIF_FMT_ZU " bytes)\n", avif->exif.size); |
95 | 0 | } else { |
96 | 0 | printf(" * Exif Metadata : Absent\n"); |
97 | 0 | } |
98 | |
|
99 | 0 | if (avif->transformFlags == AVIF_TRANSFORM_NONE) { |
100 | 0 | printf(" * Transformations: None\n"); |
101 | 0 | } else { |
102 | 0 | printf(" * Transformations:\n"); |
103 | |
|
104 | 0 | if (avif->transformFlags & AVIF_TRANSFORM_PASP) { |
105 | 0 | printf(" * pasp (Aspect Ratio) : %d/%d\n", (int)avif->pasp.hSpacing, (int)avif->pasp.vSpacing); |
106 | 0 | } |
107 | 0 | if (avif->transformFlags & AVIF_TRANSFORM_CLAP) { |
108 | 0 | printf(" * clap (Clean Aperture): "); |
109 | 0 | printClapFraction("W", (int32_t)avif->clap.widthN, (int32_t)avif->clap.widthD); |
110 | 0 | printf(", "); |
111 | 0 | printClapFraction("H", (int32_t)avif->clap.heightN, (int32_t)avif->clap.heightD); |
112 | 0 | printf(", "); |
113 | 0 | printClapFraction("hOff", (int32_t)avif->clap.horizOffN, (int32_t)avif->clap.horizOffD); |
114 | 0 | printf(", "); |
115 | 0 | printClapFraction("vOff", (int32_t)avif->clap.vertOffN, (int32_t)avif->clap.vertOffD); |
116 | 0 | printf("\n"); |
117 | |
|
118 | 0 | avifCropRect cropRect; |
119 | 0 | avifDiagnostics diag; |
120 | 0 | avifDiagnosticsClearError(&diag); |
121 | 0 | avifBool validClap = avifCropRectFromCleanApertureBox(&cropRect, &avif->clap, avif->width, avif->height, &diag); |
122 | 0 | if (validClap) { |
123 | 0 | printf(" * Valid, derived crop rect: X: %d, Y: %d, W: %d, H: %d%s\n", |
124 | 0 | cropRect.x, |
125 | 0 | cropRect.y, |
126 | 0 | cropRect.width, |
127 | 0 | cropRect.height, |
128 | 0 | avifCropRectRequiresUpsampling(&cropRect, avif->yuvFormat) ? " (upsample before cropping)" : ""); |
129 | 0 | } else { |
130 | 0 | printf(" * Invalid: %s\n", diag.error); |
131 | 0 | } |
132 | 0 | } |
133 | 0 | if (avif->transformFlags & AVIF_TRANSFORM_IROT) { |
134 | 0 | printf(" * irot (Rotation) : %u\n", avif->irot.angle); |
135 | 0 | } |
136 | 0 | if (avif->transformFlags & AVIF_TRANSFORM_IMIR) { |
137 | 0 | printf(" * imir (Mirror) : %u (%s)\n", avif->imir.axis, (avif->imir.axis == 0) ? "top-to-bottom" : "left-to-right"); |
138 | 0 | } |
139 | 0 | } |
140 | 0 | printf(" * Progressive : %s\n", avifProgressiveStateToString(progressiveState)); |
141 | 0 | if (avif->clli.maxCLL > 0 || avif->clli.maxPALL > 0) { |
142 | 0 | printf(" * CLLI : %hu, %hu\n", avif->clli.maxCLL, avif->clli.maxPALL); |
143 | 0 | } |
144 | |
|
145 | 0 | printf(" * Gain map : "); |
146 | 0 | avifImage * gainMapImage = avif->gainMap ? avif->gainMap->image : NULL; |
147 | 0 | if (gainMapImage != NULL) { |
148 | 0 | printf("%ux%u pixels, %u bit, %s, %s Range, Matrix Coeffs. %u, Base Headroom %.2f (%s), Alternate Headroom %.2f (%s)\n", |
149 | 0 | gainMapImage->width, |
150 | 0 | gainMapImage->height, |
151 | 0 | gainMapImage->depth, |
152 | 0 | avifPixelFormatToString(gainMapImage->yuvFormat), |
153 | 0 | (gainMapImage->yuvRange == AVIF_RANGE_FULL) ? "Full" : "Limited", |
154 | 0 | gainMapImage->matrixCoefficients, |
155 | 0 | avif->gainMap->baseHdrHeadroom.d == 0 ? 0 |
156 | 0 | : (double)avif->gainMap->baseHdrHeadroom.n / avif->gainMap->baseHdrHeadroom.d, |
157 | 0 | (avif->gainMap->baseHdrHeadroom.n == 0) ? "SDR" : "HDR", |
158 | 0 | avif->gainMap->alternateHdrHeadroom.d == 0 |
159 | 0 | ? 0 |
160 | 0 | : (double)avif->gainMap->alternateHdrHeadroom.n / avif->gainMap->alternateHdrHeadroom.d, |
161 | 0 | (avif->gainMap->alternateHdrHeadroom.n == 0) ? "SDR" : "HDR"); |
162 | 0 | printf(" * Alternate image:\n"); |
163 | 0 | printf(" * Color Primaries: %u\n", avif->gainMap->altColorPrimaries); |
164 | 0 | printf(" * Transfer Char. : %u\n", avif->gainMap->altTransferCharacteristics); |
165 | 0 | printf(" * Matrix Coeffs. : %u\n", avif->gainMap->altMatrixCoefficients); |
166 | 0 | if (avif->gainMap->altICC.size != 0) { |
167 | 0 | printf(" * ICC Profile : Present (%" AVIF_FMT_ZU " bytes)\n", avif->gainMap->altICC.size); |
168 | 0 | } else { |
169 | 0 | printf(" * ICC Profile : Absent\n"); |
170 | 0 | } |
171 | 0 | if (avif->gainMap->altDepth) { |
172 | 0 | printf(" * Bit Depth : %u\n", avif->gainMap->altDepth); |
173 | 0 | } |
174 | 0 | if (avif->gainMap->altPlaneCount) { |
175 | 0 | printf(" * Planes : %u\n", avif->gainMap->altPlaneCount); |
176 | 0 | } |
177 | 0 | if (avif->gainMap->altCLLI.maxCLL > 0 || avif->gainMap->altCLLI.maxPALL > 0) { |
178 | 0 | printf(" * CLLI : %hu, %hu\n", avif->gainMap->altCLLI.maxCLL, avif->gainMap->altCLLI.maxPALL); |
179 | 0 | } |
180 | 0 | printf("\n"); |
181 | 0 | } else if (avif->gainMap != NULL) { |
182 | 0 | printf("Present (but ignored)\n"); |
183 | 0 | } else { |
184 | 0 | printf("Absent\n"); |
185 | 0 | } |
186 | 0 | } |
187 | | |
188 | | void avifImageDump(const avifImage * avif, uint32_t gridCols, uint32_t gridRows, avifProgressiveState progressiveState) |
189 | 0 | { |
190 | 0 | const avifBool alphaPresent = avif->alphaPlane && (avif->alphaRowBytes > 0); |
191 | 0 | avifImageDumpInternal(avif, gridCols, gridRows, alphaPresent, progressiveState); |
192 | 0 | } |
193 | | |
194 | | void avifContainerDump(const avifDecoder * decoder) |
195 | 0 | { |
196 | 0 | avifImageDumpInternal(decoder->image, 0, 0, decoder->alphaPresent, decoder->progressiveState); |
197 | 0 | if (decoder->imageSequenceTrackPresent) { |
198 | 0 | if (decoder->repetitionCount == AVIF_REPETITION_COUNT_INFINITE) { |
199 | 0 | printf(" * Repeat Count : Infinite\n"); |
200 | 0 | } else if (decoder->repetitionCount == AVIF_REPETITION_COUNT_UNKNOWN) { |
201 | 0 | printf(" * Repeat Count : Unknown\n"); |
202 | 0 | } else { |
203 | 0 | printf(" * Repeat Count : %d\n", decoder->repetitionCount); |
204 | 0 | } |
205 | 0 | } |
206 | 0 | } |
207 | | |
208 | | void avifPrintVersions(void) |
209 | 0 | { |
210 | 0 | char codecVersions[256]; |
211 | 0 | avifCodecVersions(codecVersions); |
212 | 0 | printf("Version: %s (%s)\n", avifVersion(), codecVersions); |
213 | |
|
214 | 0 | unsigned int libyuvVersion = avifLibYUVVersion(); |
215 | 0 | if (libyuvVersion == 0) { |
216 | 0 | printf("libyuv : unavailable\n"); |
217 | 0 | } else { |
218 | 0 | printf("libyuv : available (%u)\n", libyuvVersion); |
219 | 0 | } |
220 | |
|
221 | 0 | printf("\n"); |
222 | 0 | } |
223 | | |
224 | | avifAppFileFormat avifGuessFileFormat(const char * filename) |
225 | 5.77k | { |
226 | | // Guess from the file header |
227 | 5.77k | FILE * f = fopen(filename, "rb"); |
228 | 5.77k | if (f) { |
229 | 5.77k | uint8_t headerBuffer[144]; |
230 | 5.77k | size_t bytesRead = fread(headerBuffer, 1, sizeof(headerBuffer), f); |
231 | 5.77k | fclose(f); |
232 | | |
233 | 5.77k | if (bytesRead > 0) { |
234 | | // If the file could be read, use the first bytes to guess the file format. |
235 | 5.74k | return avifGuessBufferFileFormat(headerBuffer, bytesRead); |
236 | 5.74k | } |
237 | 5.77k | } |
238 | | |
239 | | // If we get here, the file header couldn't be read for some reason. Guess from the extension. |
240 | | |
241 | 25 | const char * fileExt = strrchr(filename, '.'); |
242 | 25 | if (!fileExt) { |
243 | 25 | return AVIF_APP_FILE_FORMAT_UNKNOWN; |
244 | 25 | } |
245 | 0 | ++fileExt; // skip past the dot |
246 | |
|
247 | 0 | char lowercaseExt[8]; // This only needs to fit up to "jpeg", so this is plenty |
248 | 0 | const size_t fileExtLen = strlen(fileExt); |
249 | 0 | if (fileExtLen >= sizeof(lowercaseExt)) { // >= accounts for NULL terminator |
250 | 0 | return AVIF_APP_FILE_FORMAT_UNKNOWN; |
251 | 0 | } |
252 | | |
253 | 0 | for (size_t i = 0; i < fileExtLen; ++i) { |
254 | 0 | lowercaseExt[i] = (char)tolower((unsigned char)fileExt[i]); |
255 | 0 | } |
256 | 0 | lowercaseExt[fileExtLen] = 0; |
257 | |
|
258 | 0 | if (!strcmp(lowercaseExt, "avif")) { |
259 | 0 | return AVIF_APP_FILE_FORMAT_AVIF; |
260 | 0 | } else if (!strcmp(lowercaseExt, "y4m")) { |
261 | 0 | return AVIF_APP_FILE_FORMAT_Y4M; |
262 | 0 | } else if (!strcmp(lowercaseExt, "jpg") || !strcmp(lowercaseExt, "jpeg")) { |
263 | 0 | return AVIF_APP_FILE_FORMAT_JPEG; |
264 | 0 | } else if (!strcmp(lowercaseExt, "png")) { |
265 | 0 | return AVIF_APP_FILE_FORMAT_PNG; |
266 | 0 | } |
267 | 0 | return AVIF_APP_FILE_FORMAT_UNKNOWN; |
268 | 0 | } |
269 | | |
270 | | avifAppFileFormat avifGuessBufferFileFormat(const uint8_t * data, size_t size) |
271 | 6.31k | { |
272 | 6.31k | if (size == 0) { |
273 | 0 | return AVIF_APP_FILE_FORMAT_UNKNOWN; |
274 | 0 | } |
275 | | |
276 | 6.31k | avifROData header; |
277 | 6.31k | header.data = data; |
278 | 6.31k | header.size = size; |
279 | | |
280 | 6.31k | if (avifPeekCompatibleFileType(&header)) { |
281 | 372 | return AVIF_APP_FILE_FORMAT_AVIF; |
282 | 372 | } |
283 | | |
284 | 5.94k | static const uint8_t signatureJPEG[2] = { 0xFF, 0xD8 }; |
285 | 5.94k | static const uint8_t signaturePNG[8] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }; |
286 | 5.94k | static const uint8_t signatureY4M[9] = { 0x59, 0x55, 0x56, 0x34, 0x4D, 0x50, 0x45, 0x47, 0x32 }; // "YUV4MPEG2" |
287 | 5.94k | struct avifHeaderSignature |
288 | 5.94k | { |
289 | 5.94k | avifAppFileFormat format; |
290 | 5.94k | const uint8_t * magic; |
291 | 5.94k | size_t magicSize; |
292 | 5.94k | } signatures[] = { { AVIF_APP_FILE_FORMAT_JPEG, signatureJPEG, sizeof(signatureJPEG) }, |
293 | 5.94k | { AVIF_APP_FILE_FORMAT_PNG, signaturePNG, sizeof(signaturePNG) }, |
294 | 5.94k | { AVIF_APP_FILE_FORMAT_Y4M, signatureY4M, sizeof(signatureY4M) } }; |
295 | 5.94k | const size_t signaturesCount = sizeof(signatures) / sizeof(signatures[0]); |
296 | | |
297 | 9.31k | for (size_t signatureIndex = 0; signatureIndex < signaturesCount; ++signatureIndex) { |
298 | 9.07k | const struct avifHeaderSignature * const signature = &signatures[signatureIndex]; |
299 | 9.07k | if (header.size < signature->magicSize) { |
300 | 164 | continue; |
301 | 164 | } |
302 | 8.91k | if (!memcmp(header.data, signature->magic, signature->magicSize)) { |
303 | 5.70k | return signature->format; |
304 | 5.70k | } |
305 | 8.91k | } |
306 | | |
307 | 239 | return AVIF_APP_FILE_FORMAT_UNKNOWN; |
308 | 5.94k | } |
309 | | |
310 | | avifAppFileFormat avifReadImage(const char * filename, |
311 | | avifAppFileFormat inputFormat, |
312 | | avifPixelFormat requestedFormat, |
313 | | int requestedDepth, |
314 | | avifChromaDownsampling chromaDownsampling, |
315 | | avifBool ignoreColorProfile, |
316 | | avifBool ignoreExif, |
317 | | avifBool ignoreXMP, |
318 | | avifBool ignoreGainMap, |
319 | | uint32_t imageSizeLimit, |
320 | | avifImage * image, |
321 | | uint32_t * outDepth, |
322 | | avifAppSourceTiming * sourceTiming, |
323 | | struct y4mFrameIterator ** frameIter) |
324 | 5.77k | { |
325 | 5.77k | if (inputFormat == AVIF_APP_FILE_FORMAT_UNKNOWN) { |
326 | 5.77k | inputFormat = avifGuessFileFormat(filename); |
327 | 5.77k | } |
328 | | |
329 | 5.77k | if (inputFormat == AVIF_APP_FILE_FORMAT_Y4M) { |
330 | 15 | if (!y4mRead(filename, imageSizeLimit, image, sourceTiming, frameIter)) { |
331 | 12 | return AVIF_APP_FILE_FORMAT_UNKNOWN; |
332 | 12 | } |
333 | 3 | if (outDepth) { |
334 | 3 | *outDepth = image->depth; |
335 | 3 | } |
336 | 5.75k | } else if (inputFormat == AVIF_APP_FILE_FORMAT_JPEG) { |
337 | | // imageSizeLimit is also used to limit Exif and XMP metadata here. |
338 | 3.02k | if (!avifJPEGRead(filename, image, requestedFormat, requestedDepth, chromaDownsampling, ignoreColorProfile, ignoreExif, ignoreXMP, ignoreGainMap, imageSizeLimit)) { |
339 | 2.30k | return AVIF_APP_FILE_FORMAT_UNKNOWN; |
340 | 2.30k | } |
341 | 716 | if (outDepth) { |
342 | 716 | *outDepth = 8; |
343 | 716 | } |
344 | 2.73k | } else if (inputFormat == AVIF_APP_FILE_FORMAT_PNG) { |
345 | 2.48k | if (!avifPNGRead(filename, image, requestedFormat, requestedDepth, chromaDownsampling, ignoreColorProfile, ignoreExif, ignoreXMP, imageSizeLimit, outDepth)) { |
346 | 2.43k | return AVIF_APP_FILE_FORMAT_UNKNOWN; |
347 | 2.43k | } |
348 | 2.48k | } else if (inputFormat == AVIF_APP_FILE_FORMAT_UNKNOWN) { |
349 | 252 | fprintf(stderr, "Unrecognized file format for input file: %s\n", filename); |
350 | 252 | return AVIF_APP_FILE_FORMAT_UNKNOWN; |
351 | 252 | } else { |
352 | 0 | fprintf(stderr, "Unsupported file format %s for input file: %s\n", avifFileFormatToString(inputFormat), filename); |
353 | 0 | return AVIF_APP_FILE_FORMAT_UNKNOWN; |
354 | 0 | } |
355 | 771 | return inputFormat; |
356 | 5.77k | } |
357 | | |
358 | | avifBool avifReadEntireFile(const char * filename, avifRWData * raw) |
359 | 0 | { |
360 | 0 | FILE * f = fopen(filename, "rb"); |
361 | 0 | if (!f) { |
362 | 0 | return AVIF_FALSE; |
363 | 0 | } |
364 | | |
365 | 0 | fseek(f, 0, SEEK_END); |
366 | 0 | long pos = ftell(f); |
367 | 0 | if (pos <= 0) { |
368 | 0 | fclose(f); |
369 | 0 | return AVIF_FALSE; |
370 | 0 | } |
371 | 0 | size_t fileSize = (size_t)pos; |
372 | 0 | fseek(f, 0, SEEK_SET); |
373 | |
|
374 | 0 | if (avifRWDataRealloc(raw, fileSize) != AVIF_RESULT_OK) { |
375 | 0 | fclose(f); |
376 | 0 | return AVIF_FALSE; |
377 | 0 | } |
378 | 0 | size_t bytesRead = fread(raw->data, 1, fileSize, f); |
379 | 0 | fclose(f); |
380 | |
|
381 | 0 | if (bytesRead != fileSize) { |
382 | 0 | avifRWDataFree(raw); |
383 | 0 | return AVIF_FALSE; |
384 | 0 | } |
385 | 0 | return AVIF_TRUE; |
386 | 0 | } |
387 | | |
388 | | void avifImageFixXMP(avifImage * image) |
389 | 1.08k | { |
390 | | // Zero bytes are forbidden in UTF-8 XML: https://en.wikipedia.org/wiki/Valid_characters_in_XML |
391 | | // Keeping zero bytes in XMP may lead to issues at encoding or decoding. |
392 | | // For example, the PNG specification forbids null characters in XMP. See avifPNGWrite(). |
393 | | // The XMP Specification Part 3 says "When XMP is encoded as UTF-8, |
394 | | // there are no zero bytes in the XMP packet" for GIF. |
395 | | |
396 | | // Consider a single trailing null character following a non-null character |
397 | | // as a programming error. Leave other null characters as is. |
398 | | // See the discussion at https://github.com/AOMediaCodec/libavif/issues/1333. |
399 | 1.08k | if (image->xmp.size >= 2 && image->xmp.data[image->xmp.size - 1] == '\0' && image->xmp.data[image->xmp.size - 2] != '\0') { |
400 | 4 | --image->xmp.size; |
401 | 4 | } |
402 | 1.08k | } |
403 | | |
404 | | void avifDumpDiagnostics(const avifDiagnostics * diag) |
405 | 0 | { |
406 | 0 | if (!*diag->error) { |
407 | 0 | return; |
408 | 0 | } |
409 | | |
410 | 0 | printf("Diagnostics:\n"); |
411 | 0 | printf(" * %s\n", diag->error); |
412 | 0 | } |
413 | | |
414 | | // --------------------------------------------------------------------------- |
415 | | // avifQueryCPUCount (separated into OS implementations) |
416 | | |
417 | | #if defined(_WIN32) |
418 | | |
419 | | // Windows |
420 | | |
421 | | #include <windows.h> |
422 | | |
423 | | int avifQueryCPUCount(void) |
424 | | { |
425 | | int numCPU; |
426 | | SYSTEM_INFO sysinfo; |
427 | | GetSystemInfo(&sysinfo); |
428 | | numCPU = sysinfo.dwNumberOfProcessors; |
429 | | return numCPU; |
430 | | } |
431 | | |
432 | | #elif defined(__APPLE__) |
433 | | |
434 | | // Apple |
435 | | |
436 | | #include <sys/sysctl.h> |
437 | | |
438 | | int avifQueryCPUCount(void) |
439 | | { |
440 | | int mib[4]; |
441 | | int numCPU; |
442 | | size_t len = sizeof(numCPU); |
443 | | |
444 | | /* set the mib for hw.ncpu */ |
445 | | mib[0] = CTL_HW; |
446 | | mib[1] = HW_AVAILCPU; // alternatively, try HW_NCPU; |
447 | | |
448 | | /* get the number of CPUs from the system */ |
449 | | sysctl(mib, 2, &numCPU, &len, NULL, 0); |
450 | | |
451 | | if (numCPU < 1) { |
452 | | mib[1] = HW_NCPU; |
453 | | sysctl(mib, 2, &numCPU, &len, NULL, 0); |
454 | | if (numCPU < 1) |
455 | | numCPU = 1; |
456 | | } |
457 | | return numCPU; |
458 | | } |
459 | | |
460 | | #elif defined(__EMSCRIPTEN__) |
461 | | |
462 | | // Emscripten |
463 | | |
464 | | int avifQueryCPUCount(void) |
465 | | { |
466 | | return 1; |
467 | | } |
468 | | |
469 | | #else |
470 | | |
471 | | // POSIX |
472 | | |
473 | | #include <unistd.h> |
474 | | |
475 | | int avifQueryCPUCount(void) |
476 | 0 | { |
477 | 0 | int numCPU = (int)sysconf(_SC_NPROCESSORS_ONLN); |
478 | 0 | return (numCPU > 0) ? numCPU : 1; |
479 | 0 | } |
480 | | |
481 | | #endif |