@@ -267,6 +267,85 @@ bool GetImageDimensions(const Image* image, int* width, int* height) {
267267 return false ;
268268}
269269
270+ static bool GetPNGDPI (const uint8_t * data, size_t size, float * dpiX, float * dpiY) {
271+ static const uint8_t kPNGSignature [8 ] = {0x89 , 0x50 , 0x4E , 0x47 , 0x0D , 0x0A , 0x1A , 0x0A };
272+ if (size < 8 || memcmp (data, kPNGSignature , 8 ) != 0 ) {
273+ return false ;
274+ }
275+ size_t offset = 8 ;
276+ while (offset + 12 <= size) {
277+ auto chunkLen = static_cast <uint32_t >((data[offset] << 24 ) | (data[offset + 1 ] << 16 ) |
278+ (data[offset + 2 ] << 8 ) | data[offset + 3 ]);
279+ if (memcmp (data + offset + 4 , " pHYs" , 4 ) == 0 ) {
280+ if (chunkLen == 9 && offset + 8 + 9 <= size) {
281+ const uint8_t * d = data + offset + 8 ;
282+ auto ppuX = static_cast <uint32_t >((d[0 ] << 24 ) | (d[1 ] << 16 ) | (d[2 ] << 8 ) | d[3 ]);
283+ auto ppuY = static_cast <uint32_t >((d[4 ] << 24 ) | (d[5 ] << 16 ) | (d[6 ] << 8 ) | d[7 ]);
284+ uint8_t unit = d[8 ];
285+ if (unit == 1 && ppuX > 0 && ppuY > 0 ) {
286+ *dpiX = static_cast <float >(ppuX) * 0 .0254f ;
287+ *dpiY = static_cast <float >(ppuY) * 0 .0254f ;
288+ return true ;
289+ }
290+ }
291+ return false ;
292+ }
293+ if (memcmp (data + offset + 4 , " IDAT" , 4 ) == 0 ) {
294+ break ;
295+ }
296+ offset += 12 + chunkLen;
297+ }
298+ return false ;
299+ }
300+
301+ static bool GetJPEGDPI (const uint8_t * data, size_t size, float * dpiX, float * dpiY) {
302+ if (size < 2 || data[0 ] != 0xFF || data[1 ] != 0xD8 ) {
303+ return false ;
304+ }
305+ size_t offset = 2 ;
306+ while (offset + 4 < size) {
307+ if (data[offset] != 0xFF ) {
308+ return false ;
309+ }
310+ uint8_t marker = data[offset + 1 ];
311+ if (marker == 0xD9 || marker == 0xDA ) {
312+ break ;
313+ }
314+ auto segLen = static_cast <uint16_t >((data[offset + 2 ] << 8 ) | data[offset + 3 ]);
315+ if (marker == 0xE0 && segLen >= 16 && offset + 2 + segLen <= size) {
316+ if (memcmp (data + offset + 4 , " JFIF\0 " , 5 ) == 0 ) {
317+ uint8_t units = data[offset + 11 ];
318+ auto xDensity = static_cast <uint16_t >((data[offset + 12 ] << 8 ) | data[offset + 13 ]);
319+ auto yDensity = static_cast <uint16_t >((data[offset + 14 ] << 8 ) | data[offset + 15 ]);
320+ if (xDensity > 0 && yDensity > 0 ) {
321+ if (units == 1 ) {
322+ *dpiX = static_cast <float >(xDensity);
323+ *dpiY = static_cast <float >(yDensity);
324+ return true ;
325+ } else if (units == 2 ) {
326+ *dpiX = static_cast <float >(xDensity) * 2 .54f ;
327+ *dpiY = static_cast <float >(yDensity) * 2 .54f ;
328+ return true ;
329+ }
330+ }
331+ }
332+ }
333+ offset += 2 + segLen;
334+ }
335+ return false ;
336+ }
337+
338+ bool GetImageDPI (const Image* image, float * dpiX, float * dpiY) {
339+ auto data = GetImageData (image);
340+ if (!data || data->size () == 0 ) {
341+ return false ;
342+ }
343+ if (GetPNGDPI (data->bytes (), data->size (), dpiX, dpiY)) {
344+ return true ;
345+ }
346+ return GetJPEGDPI (data->bytes (), data->size (), dpiX, dpiY);
347+ }
348+
270349bool IsJPEG (const uint8_t * data, size_t size) {
271350 return size >= 2 && data[0 ] == 0xFF && data[1 ] == 0xD8 ;
272351}
0 commit comments