/src/libavif/apps/shared/avifutil.c
Line | Count | Source (jump to first uncovered line) |
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 (gainMapImage->clli.maxCLL > 0 || gainMapImage->clli.maxPALL > 0) { |
178 | 0 | printf(" * CLLI : %hu, %hu\n", gainMapImage->clli.maxCLL, gainMapImage->clli.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 | 6.17k | { |
226 | | // Guess from the file header |
227 | 6.17k | FILE * f = fopen(filename, "rb"); |
228 | 6.17k | if (f) { |
229 | 6.17k | uint8_t headerBuffer[144]; |
230 | 6.17k | size_t bytesRead = fread(headerBuffer, 1, sizeof(headerBuffer), f); |
231 | 6.17k | fclose(f); |
232 | | |
233 | 6.17k | if (bytesRead > 0) { |
234 | | // If the file could be read, use the first bytes to guess the file format. |
235 | 6.17k | return avifGuessBufferFileFormat(headerBuffer, bytesRead); |
236 | 6.17k | } |
237 | 6.17k | } |
238 | | |
239 | | // If we get here, the file header couldn't be read for some reason. Guess from the extension. |
240 | | |
241 | 8 | const char * fileExt = strrchr(filename, '.'); |
242 | 8 | if (!fileExt) { |
243 | 8 | return AVIF_APP_FILE_FORMAT_UNKNOWN; |
244 | 8 | } |
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.71k | { |
272 | 6.71k | if (size == 0) { |
273 | 0 | return AVIF_APP_FILE_FORMAT_UNKNOWN; |
274 | 0 | } |
275 | | |
276 | 6.71k | avifROData header; |
277 | 6.71k | header.data = data; |
278 | 6.71k | header.size = size; |
279 | | |
280 | 6.71k | if (avifPeekCompatibleFileType(&header)) { |
281 | 372 | return AVIF_APP_FILE_FORMAT_AVIF; |
282 | 372 | } |
283 | | |
284 | 6.34k | static const uint8_t signatureJPEG[2] = { 0xFF, 0xD8 }; |
285 | 6.34k | static const uint8_t signaturePNG[8] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }; |
286 | 6.34k | static const uint8_t signatureY4M[9] = { 0x59, 0x55, 0x56, 0x34, 0x4D, 0x50, 0x45, 0x47, 0x32 }; // "YUV4MPEG2" |
287 | 6.34k | struct avifHeaderSignature |
288 | 6.34k | { |
289 | 6.34k | avifAppFileFormat format; |
290 | 6.34k | const uint8_t * magic; |
291 | 6.34k | size_t magicSize; |
292 | 6.34k | } signatures[] = { { AVIF_APP_FILE_FORMAT_JPEG, signatureJPEG, sizeof(signatureJPEG) }, |
293 | 6.34k | { AVIF_APP_FILE_FORMAT_PNG, signaturePNG, sizeof(signaturePNG) }, |
294 | 6.34k | { AVIF_APP_FILE_FORMAT_Y4M, signatureY4M, sizeof(signatureY4M) } }; |
295 | 6.34k | const size_t signaturesCount = sizeof(signatures) / sizeof(signatures[0]); |
296 | | |
297 | 9.11k | for (size_t signatureIndex = 0; signatureIndex < signaturesCount; ++signatureIndex) { |
298 | 8.93k | const struct avifHeaderSignature * const signature = &signatures[signatureIndex]; |
299 | 8.93k | if (header.size < signature->magicSize) { |
300 | 91 | continue; |
301 | 91 | } |
302 | 8.84k | if (!memcmp(header.data, signature->magic, signature->magicSize)) { |
303 | 6.15k | return signature->format; |
304 | 6.15k | } |
305 | 8.84k | } |
306 | | |
307 | 185 | return AVIF_APP_FILE_FORMAT_UNKNOWN; |
308 | 6.34k | } |
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 allowChangingCicp, |
319 | | avifBool ignoreGainMap, |
320 | | uint32_t imageSizeLimit, |
321 | | avifImage * image, |
322 | | uint32_t * outDepth, |
323 | | avifAppSourceTiming * sourceTiming, |
324 | | struct y4mFrameIterator ** frameIter) |
325 | 6.17k | { |
326 | 6.17k | if (inputFormat == AVIF_APP_FILE_FORMAT_UNKNOWN) { |
327 | 6.17k | inputFormat = avifGuessFileFormat(filename); |
328 | 6.17k | } |
329 | | |
330 | 6.17k | if (inputFormat == AVIF_APP_FILE_FORMAT_Y4M) { |
331 | 22 | if (!y4mRead(filename, imageSizeLimit, image, sourceTiming, frameIter)) { |
332 | 21 | return AVIF_APP_FILE_FORMAT_UNKNOWN; |
333 | 21 | } |
334 | 1 | if (outDepth) { |
335 | 1 | *outDepth = image->depth; |
336 | 1 | } |
337 | 6.15k | } else if (inputFormat == AVIF_APP_FILE_FORMAT_JPEG) { |
338 | | // imageSizeLimit is also used to limit Exif and XMP metadata here. |
339 | 3.93k | if (!avifJPEGRead(filename, image, requestedFormat, requestedDepth, chromaDownsampling, ignoreColorProfile, ignoreExif, ignoreXMP, ignoreGainMap, imageSizeLimit)) { |
340 | 2.45k | return AVIF_APP_FILE_FORMAT_UNKNOWN; |
341 | 2.45k | } |
342 | 1.47k | if (outDepth) { |
343 | 1.47k | *outDepth = 8; |
344 | 1.47k | } |
345 | 2.22k | } else if (inputFormat == AVIF_APP_FILE_FORMAT_PNG) { |
346 | 2.04k | if (!avifPNGRead(filename, |
347 | 2.04k | image, |
348 | 2.04k | requestedFormat, |
349 | 2.04k | requestedDepth, |
350 | 2.04k | chromaDownsampling, |
351 | 2.04k | ignoreColorProfile, |
352 | 2.04k | ignoreExif, |
353 | 2.04k | ignoreXMP, |
354 | 2.04k | allowChangingCicp, |
355 | 2.04k | imageSizeLimit, |
356 | 2.04k | outDepth)) { |
357 | 2.02k | return AVIF_APP_FILE_FORMAT_UNKNOWN; |
358 | 2.02k | } |
359 | 2.04k | } else if (inputFormat == AVIF_APP_FILE_FORMAT_UNKNOWN) { |
360 | 181 | fprintf(stderr, "Unrecognized file format for input file: %s\n", filename); |
361 | 181 | return AVIF_APP_FILE_FORMAT_UNKNOWN; |
362 | 181 | } else { |
363 | 0 | fprintf(stderr, "Unsupported file format %s for input file: %s\n", avifFileFormatToString(inputFormat), filename); |
364 | 0 | return AVIF_APP_FILE_FORMAT_UNKNOWN; |
365 | 0 | } |
366 | 1.49k | return inputFormat; |
367 | 6.17k | } |
368 | | |
369 | | avifBool avifReadEntireFile(const char * filename, avifRWData * raw) |
370 | 0 | { |
371 | 0 | FILE * f = fopen(filename, "rb"); |
372 | 0 | if (!f) { |
373 | 0 | return AVIF_FALSE; |
374 | 0 | } |
375 | | |
376 | 0 | fseek(f, 0, SEEK_END); |
377 | 0 | long pos = ftell(f); |
378 | 0 | if (pos <= 0) { |
379 | 0 | fclose(f); |
380 | 0 | return AVIF_FALSE; |
381 | 0 | } |
382 | 0 | size_t fileSize = (size_t)pos; |
383 | 0 | fseek(f, 0, SEEK_SET); |
384 | |
|
385 | 0 | if (avifRWDataRealloc(raw, fileSize) != AVIF_RESULT_OK) { |
386 | 0 | fclose(f); |
387 | 0 | return AVIF_FALSE; |
388 | 0 | } |
389 | 0 | size_t bytesRead = fread(raw->data, 1, fileSize, f); |
390 | 0 | fclose(f); |
391 | |
|
392 | 0 | if (bytesRead != fileSize) { |
393 | 0 | avifRWDataFree(raw); |
394 | 0 | return AVIF_FALSE; |
395 | 0 | } |
396 | 0 | return AVIF_TRUE; |
397 | 0 | } |
398 | | |
399 | | void avifImageFixXMP(avifImage * image) |
400 | 960 | { |
401 | | // Zero bytes are forbidden in UTF-8 XML: https://en.wikipedia.org/wiki/Valid_characters_in_XML |
402 | | // Keeping zero bytes in XMP may lead to issues at encoding or decoding. |
403 | | // For example, the PNG specification forbids null characters in XMP. See avifPNGWrite(). |
404 | | // The XMP Specification Part 3 says "When XMP is encoded as UTF-8, |
405 | | // there are no zero bytes in the XMP packet" for GIF. |
406 | | |
407 | | // Consider a single trailing null character following a non-null character |
408 | | // as a programming error. Leave other null characters as is. |
409 | | // See the discussion at https://github.com/AOMediaCodec/libavif/issues/1333. |
410 | 960 | if (image->xmp.size >= 2 && image->xmp.data[image->xmp.size - 1] == '\0' && image->xmp.data[image->xmp.size - 2] != '\0') { |
411 | 2 | --image->xmp.size; |
412 | 2 | } |
413 | 960 | } |
414 | | |
415 | | void avifDumpDiagnostics(const avifDiagnostics * diag) |
416 | 0 | { |
417 | 0 | if (!*diag->error) { |
418 | 0 | return; |
419 | 0 | } |
420 | | |
421 | 0 | printf("Diagnostics:\n"); |
422 | 0 | printf(" * %s\n", diag->error); |
423 | 0 | } |
424 | | |
425 | | // --------------------------------------------------------------------------- |
426 | | // avifQueryCPUCount (separated into OS implementations) |
427 | | |
428 | | #if defined(_WIN32) |
429 | | |
430 | | // Windows |
431 | | |
432 | | #include <windows.h> |
433 | | |
434 | | int avifQueryCPUCount(void) |
435 | | { |
436 | | int numCPU; |
437 | | SYSTEM_INFO sysinfo; |
438 | | GetSystemInfo(&sysinfo); |
439 | | numCPU = sysinfo.dwNumberOfProcessors; |
440 | | return numCPU; |
441 | | } |
442 | | |
443 | | #elif defined(__APPLE__) |
444 | | |
445 | | // Apple |
446 | | |
447 | | #include <sys/sysctl.h> |
448 | | |
449 | | int avifQueryCPUCount(void) |
450 | | { |
451 | | int mib[4]; |
452 | | int numCPU; |
453 | | size_t len = sizeof(numCPU); |
454 | | |
455 | | /* set the mib for hw.ncpu */ |
456 | | mib[0] = CTL_HW; |
457 | | mib[1] = HW_AVAILCPU; // alternatively, try HW_NCPU; |
458 | | |
459 | | /* get the number of CPUs from the system */ |
460 | | sysctl(mib, 2, &numCPU, &len, NULL, 0); |
461 | | |
462 | | if (numCPU < 1) { |
463 | | mib[1] = HW_NCPU; |
464 | | sysctl(mib, 2, &numCPU, &len, NULL, 0); |
465 | | if (numCPU < 1) |
466 | | numCPU = 1; |
467 | | } |
468 | | return numCPU; |
469 | | } |
470 | | |
471 | | #elif defined(__EMSCRIPTEN__) |
472 | | |
473 | | // Emscripten |
474 | | |
475 | | int avifQueryCPUCount(void) |
476 | | { |
477 | | return 1; |
478 | | } |
479 | | |
480 | | #else |
481 | | |
482 | | // POSIX |
483 | | |
484 | | #include <unistd.h> |
485 | | |
486 | | int avifQueryCPUCount(void) |
487 | 0 | { |
488 | 0 | int numCPU = (int)sysconf(_SC_NPROCESSORS_ONLN); |
489 | 0 | return (numCPU > 0) ? numCPU : 1; |
490 | 0 | } |
491 | | |
492 | | #endif |