1 /** 2 Images suitable to be drawn on a canvas. This is an _undecoded_ image, with metadata extracted. 3 4 Copyright: Guillaume Piolat 2018. 5 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 */ 7 module printed.canvas.image; 8 9 import std.exception; 10 import std.file; 11 import std.math; 12 import std.typecons; 13 import std.base64; 14 import std.array; 15 16 import binrange; 17 18 /// Represented an encoded image (JPEG or PNG). 19 class Image 20 { 21 this(const(char)[] relativePath) 22 { 23 ubyte[] dataFromFile = cast(ubyte[])( std.file.read(relativePath) ); 24 // has been allocated, can be assumed unique 25 this( assumeUnique(dataFromFile) ); 26 } 27 28 this(immutable(ubyte)[] data) 29 { 30 // embed for future use 31 _data = data.idup; 32 33 bool isPNG = _data.length >= 8 && (_data[0..8] == pngSignature); 34 bool isJPEG = (_data.length >= 2) && (_data[0] == 0xff) && (_data[1] == 0xd8); // SOI 35 36 if (isPNG) 37 { 38 _MIME = "image/png"; 39 parsePNGMetadata(_data, _width, _height, _pixelsPerMeterX, _pixelsPerMeterY); 40 } 41 else if (isJPEG) 42 { 43 _MIME = "image/jpeg"; 44 parseJPEGMetadata(_data, _width, _height, _pixelsPerMeterX, _pixelsPerMeterY); 45 } 46 else 47 throw new Exception("Only JPEG and PNG are supported for now"); 48 } 49 50 string MIME() 51 { 52 return _MIME; 53 } 54 55 string toDataURI() 56 { 57 string r = "data:"; 58 r ~= _MIME; 59 r ~= ";charset=utf-8;base64,"; 60 r ~= Base64.encode(_data); 61 return r; 62 } 63 64 /// Width in pixels. 65 int width() 66 { 67 return _width; 68 } 69 70 /// Height in pixels. 71 int height() 72 { 73 return _height; 74 } 75 76 /// Default width when printed, in mm. 77 float printWidth() 78 { 79 return 1000 * _width / pixelsPerMeterX(); 80 } 81 82 /// Default height when printed, in mm. 83 float printHeight() 84 { 85 return 1000 * _height / pixelsPerMeterY(); 86 } 87 88 float pixelsPerMeterX() 89 { 90 if (isNaN(_pixelsPerMeterX)) 91 return defaultDPI; 92 else 93 return _pixelsPerMeterX; 94 } 95 96 float pixelsPerMeterY() 97 { 98 if (isNaN(_pixelsPerMeterY)) 99 return defaultDPI; 100 else 101 return _pixelsPerMeterY; 102 } 103 104 immutable(ubyte)[] encodedData() const 105 { 106 return _data; 107 } 108 109 private: 110 111 // Number of horizontal pixels. 112 int _width = -1; 113 114 // Number of vertical pixels. 115 int _height = -1; 116 117 // DPI and aspect ratio information, critical for print 118 float _pixelsPerMeterX = float.nan; // stays NaN if not available 119 float _pixelsPerMeterY = float.nan; // stays NaN if not available 120 121 // Encoded data. 122 immutable(ubyte)[] _data; 123 124 // Parsed MIME type. 125 string _MIME; 126 } 127 128 private: 129 130 // Default to 72 ppi if missing 131 // This is the default ppi GIMP uses when saving PNG. 132 static immutable defaultDPI = convertMetersToInches(72); 133 134 double convertMetersToInches(double x) 135 { 136 return x * 39.37007874; 137 } 138 139 double convertInchesToMeters(double x) 140 { 141 return x / 39.37007874; 142 } 143 144 static immutable ubyte[8] pngSignature = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]; 145 146 void parsePNGMetadata(immutable(ubyte)[] data, out int width, out int height, out float pixelsPerMeterX, out float pixelsPerMeterY) 147 { 148 data.skipBytes(8); 149 150 while (!data.empty) 151 { 152 uint chunkLen = popBE!uint(data); 153 uint chunkType = popBE!uint(data); 154 155 switch (chunkType) 156 { 157 case 0x49484452: // 'IHDR' 158 width = popBE!int(data); 159 height = popBE!int(data); 160 data.skipBytes(5); 161 break; 162 163 case 0x70485973: // 'pHYs' 164 int pixelsPerUnitX = popBE!int(data); 165 int pixelsPerUnitY = popBE!int(data); 166 ubyte unit = popBE!ubyte(data); 167 if (unit == 1) 168 { 169 pixelsPerMeterX = pixelsPerUnitX; 170 pixelsPerMeterY = pixelsPerUnitY; 171 } 172 else 173 { 174 // assume default DPI, but keep aspect ratio 175 pixelsPerMeterX = defaultDPI; 176 pixelsPerMeterY = (pixelsPerUnitY/cast(double)pixelsPerUnitX) * pixelsPerMeterX; 177 } 178 return; // we're done here 179 180 default: 181 data.skipBytes(chunkLen); 182 } 183 184 popBE!uint(data); // skip CRC 185 } 186 } 187 188 void parseJPEGMetadata(immutable(ubyte)[] data, out int width, out int height, out float pixelsPerMeterX, out float pixelsPerMeterY) 189 { 190 int actual_comp; 191 extract_jpeg_metadata(data, width, height, pixelsPerMeterX, pixelsPerMeterY); 192 } 193 194 // Below: a stripped down JPEG decoder for metadata only 195 196 197 // jpgd.h - C++ class for JPEG decompression. 198 // Rich Geldreich <richgel99@gmail.com> 199 // Alex Evans: Linear memory allocator (taken from jpge.h). 200 // v1.04, May. 19, 2012: Code tweaks to fix VS2008 static code analysis warnings (all looked harmless) 201 // D translation by Ketmar // Invisible Vector 202 // 203 // This is free and unencumbered software released into the public domain. 204 // 205 // Anyone is free to copy, modify, publish, use, compile, sell, or 206 // distribute this software, either in source code form or as a compiled 207 // binary, for any purpose, commercial or non-commercial, and by any 208 // means. 209 // 210 // In jurisdictions that recognize copyright laws, the author or authors 211 // of this software dedicate any and all copyright interest in the 212 // software to the public domain. We make this dedication for the benefit 213 // of the public at large and to the detriment of our heirs and 214 // successors. We intend this dedication to be an overt act of 215 // relinquishment in perpetuity of all present and future rights to this 216 // software under copyright law. 217 // 218 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 219 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 220 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 221 // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 222 // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 223 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 224 // OTHER DEALINGS IN THE SOFTWARE. 225 // 226 // For more information, please refer to <http://unlicense.org/> 227 // 228 // Supports progressive and baseline sequential JPEG image files, and the most common chroma subsampling factors: Y, H1V1, H2V1, H1V2, and H2V2. 229 // 230 // Chroma upsampling quality: H2V2 is upsampled in the frequency domain, H2V1 and H1V2 are upsampled using point sampling. 231 // Chroma upsampling reference: "Fast Scheme for Image Size Change in the Compressed Domain" 232 // http://vision.ai.uiuc.edu/~dugad/research/dct/index.html 233 /** 234 * Loads a JPEG image from a memory buffer or a file. 235 * req_comps can be 1 (grayscale), 3 (RGB), or 4 (RGBA). 236 * On return, width/height will be set to the image's dimensions, and actual_comps will be set to the either 1 (grayscale) or 3 (RGB). 237 * Requesting a 8 or 32bpp image is currently a little faster than 24bpp because the jpeg_decoder class itself currently always unpacks to either 8 or 32bpp. 238 */ 239 /// JPEG image loading. 240 241 242 /// Input stream interface. 243 /// This delegate is called when the internal input buffer is empty. 244 /// Parameters: 245 /// pBuf - input buffer 246 /// max_bytes_to_read - maximum bytes that can be written to pBuf 247 /// pEOF_flag - set this to true if at end of stream (no more bytes remaining) 248 /// Returns -1 on error, otherwise return the number of bytes actually written to the buffer (which may be 0). 249 /// Notes: This delegate will be called in a loop until you set *pEOF_flag to true or the internal buffer is full. 250 alias JpegStreamReadFunc = int delegate (void* pBuf, int max_bytes_to_read, bool* pEOF_flag); 251 252 253 // ////////////////////////////////////////////////////////////////////////// // 254 private: 255 256 import core.stdc.stdlib: malloc, free; 257 258 alias jpgd_malloc = malloc; 259 alias jpgd_free = free; 260 261 // Success/failure error codes. 262 alias jpgd_status = int; 263 enum /*jpgd_status*/ { 264 JPGD_SUCCESS = 0, JPGD_FAILED = -1, JPGD_DONE = 1, 265 JPGD_BAD_DHT_COUNTS = -256, JPGD_BAD_DHT_INDEX, JPGD_BAD_DHT_MARKER, JPGD_BAD_DQT_MARKER, JPGD_BAD_DQT_TABLE, 266 JPGD_BAD_PRECISION, JPGD_BAD_HEIGHT, JPGD_BAD_WIDTH, JPGD_TOO_MANY_COMPONENTS, 267 JPGD_BAD_SOF_LENGTH, JPGD_BAD_VARIABLE_MARKER, JPGD_BAD_DRI_LENGTH, JPGD_BAD_SOS_LENGTH, 268 JPGD_BAD_SOS_COMP_ID, JPGD_W_EXTRA_BYTES_BEFORE_MARKER, JPGD_NO_ARITHMITIC_SUPPORT, JPGD_UNEXPECTED_MARKER, 269 JPGD_NOT_JPEG, JPGD_UNSUPPORTED_MARKER, JPGD_BAD_DQT_LENGTH, JPGD_TOO_MANY_BLOCKS, 270 JPGD_UNDEFINED_QUANT_TABLE, JPGD_UNDEFINED_HUFF_TABLE, JPGD_NOT_SINGLE_SCAN, JPGD_UNSUPPORTED_COLORSPACE, 271 JPGD_UNSUPPORTED_SAMP_FACTORS, JPGD_DECODE_ERROR, JPGD_BAD_RESTART_MARKER, JPGD_ASSERTION_ERROR, 272 JPGD_BAD_SOS_SPECTRAL, JPGD_BAD_SOS_SUCCESSIVE, JPGD_STREAM_READ, JPGD_NOTENOUGHMEM, 273 } 274 275 enum { 276 JPGD_IN_BUF_SIZE = 8192, JPGD_MAX_BLOCKS_PER_MCU = 10, JPGD_MAX_HUFF_TABLES = 8, JPGD_MAX_QUANT_TABLES = 4, 277 JPGD_MAX_COMPONENTS = 4, JPGD_MAX_COMPS_IN_SCAN = 4, JPGD_MAX_BLOCKS_PER_ROW = 8192, JPGD_MAX_HEIGHT = 16384, JPGD_MAX_WIDTH = 16384, 278 } 279 280 alias JPEG_MARKER = int; 281 enum /*JPEG_MARKER*/ { 282 M_SOF0 = 0xC0, M_SOF1 = 0xC1, M_SOF2 = 0xC2, M_SOF3 = 0xC3, M_SOF5 = 0xC5, M_SOF6 = 0xC6, M_SOF7 = 0xC7, M_JPG = 0xC8, 283 M_SOF9 = 0xC9, M_SOF10 = 0xCA, M_SOF11 = 0xCB, M_SOF13 = 0xCD, M_SOF14 = 0xCE, M_SOF15 = 0xCF, M_DHT = 0xC4, M_DAC = 0xCC, 284 M_RST0 = 0xD0, M_RST1 = 0xD1, M_RST2 = 0xD2, M_RST3 = 0xD3, M_RST4 = 0xD4, M_RST5 = 0xD5, M_RST6 = 0xD6, M_RST7 = 0xD7, 285 M_SOI = 0xD8, M_EOI = 0xD9, M_SOS = 0xDA, M_DQT = 0xDB, M_DNL = 0xDC, M_DRI = 0xDD, M_DHP = 0xDE, M_EXP = 0xDF, 286 M_APP0 = 0xE0, M_APP15 = 0xEF, M_JPG0 = 0xF0, M_JPG13 = 0xFD, M_COM = 0xFE, M_TEM = 0x01, M_ERROR = 0x100, RST0 = 0xD0, 287 } 288 289 struct jpeg_decoder { 290 291 private import core.stdc..string : memcpy, memset; 292 private: 293 294 alias jpgd_quant_t = short; 295 alias jpgd_block_t = short; 296 alias pDecode_block_func = void function (ref jpeg_decoder, int, int, int); 297 298 static struct coeff_buf { 299 ubyte* pData; 300 int block_num_x, block_num_y; 301 int block_len_x, block_len_y; 302 int block_size; 303 } 304 305 int m_image_x_size; 306 int m_image_y_size; 307 JpegStreamReadFunc readfn; 308 jpgd_quant_t*[JPGD_MAX_QUANT_TABLES] m_quant; // pointer to quantization tables 309 int m_comps_in_frame; // # of components in frame 310 int[JPGD_MAX_COMPONENTS] m_comp_ident; // component's ID 311 int[JPGD_MAX_COMPONENTS] m_comp_h_blocks; 312 int[JPGD_MAX_COMPONENTS] m_comp_v_blocks; 313 int m_comps_in_scan; // # of components in scan 314 int[JPGD_MAX_COMPS_IN_SCAN] m_comp_list; // components in this scan 315 int[JPGD_MAX_COMPONENTS] m_comp_dc_tab; // component's DC Huffman coding table selector 316 int[JPGD_MAX_COMPONENTS] m_comp_ac_tab; // component's AC Huffman coding table selector 317 int m_blocks_per_mcu; 318 int m_max_blocks_per_row; 319 int m_mcus_per_row, m_mcus_per_col; 320 int[JPGD_MAX_BLOCKS_PER_MCU] m_mcu_org; 321 int m_total_lines_left; // total # lines left in image 322 int m_mcu_lines_left; // total # lines left in this MCU 323 int m_real_dest_bytes_per_scan_line; 324 int m_dest_bytes_per_scan_line; // rounded up 325 int m_dest_bytes_per_pixel; // 4 (RGB) or 1 (Y) 326 int m_eob_run; 327 int[JPGD_MAX_COMPONENTS] m_block_y_mcu; 328 ubyte* m_pIn_buf_ofs; 329 int m_in_buf_left; 330 int m_tem_flag; 331 bool m_eof_flag; 332 ubyte[128] m_in_buf_pad_start; 333 ubyte[JPGD_IN_BUF_SIZE+128] m_in_buf; 334 ubyte[128] m_in_buf_pad_end; 335 int m_bits_left; 336 uint m_bit_buf; 337 int m_restart_interval; 338 int m_restarts_left; 339 int m_next_restart_num; 340 int m_max_mcus_per_row; 341 int m_max_blocks_per_mcu; 342 int m_expanded_blocks_per_mcu; 343 int m_expanded_blocks_per_row; 344 int m_expanded_blocks_per_component; 345 bool m_freq_domain_chroma_upsample; 346 int m_max_mcus_per_col; 347 uint[JPGD_MAX_COMPONENTS] m_last_dc_val; 348 jpgd_block_t* m_pMCU_coefficients; 349 ubyte* m_pSample_buf; 350 int[256] m_crr; 351 int[256] m_cbb; 352 int[256] m_crg; 353 int[256] m_cbg; 354 ubyte* m_pScan_line_0; 355 ubyte* m_pScan_line_1; 356 jpgd_status m_error_code; 357 358 float m_pixelsPerMeterX, m_pixelsPerMeterY; 359 360 361 public: 362 // Inspect `error_code` after constructing to determine if the stream is valid or not. You may look at the `width`, `height`, etc. 363 // methods after the constructor is called. You may then either destruct the object, or begin decoding the image by calling begin_decoding(), then decode() on each scanline. 364 this (JpegStreamReadFunc rfn) 365 { 366 initit(rfn); 367 locate_sof_marker(); 368 } 369 370 jpgd_status error_code () { return m_error_code; } 371 372 private: 373 // Retrieve one character from the input stream. 374 uint get_char () { 375 // Any bytes remaining in buffer? 376 if (!m_in_buf_left) { 377 // Try to get more bytes. 378 prep_in_buffer(); 379 // Still nothing to get? 380 if (!m_in_buf_left) { 381 // Pad the end of the stream with 0xFF 0xD9 (EOI marker) 382 int t = m_tem_flag; 383 m_tem_flag ^= 1; 384 return (t ? 0xD9 : 0xFF); 385 } 386 } 387 uint c = *m_pIn_buf_ofs++; 388 --m_in_buf_left; 389 return c; 390 } 391 392 // Same as previous method, except can indicate if the character is a pad character or not. 393 uint get_char (bool* pPadding_flag) { 394 if (!m_in_buf_left) { 395 prep_in_buffer(); 396 if (!m_in_buf_left) { 397 *pPadding_flag = true; 398 int t = m_tem_flag; 399 m_tem_flag ^= 1; 400 return (t ? 0xD9 : 0xFF); 401 } 402 } 403 *pPadding_flag = false; 404 uint c = *m_pIn_buf_ofs++; 405 --m_in_buf_left; 406 return c; 407 } 408 409 // Inserts a previously retrieved character back into the input buffer. 410 void stuff_char (ubyte q) { 411 *(--m_pIn_buf_ofs) = q; 412 m_in_buf_left++; 413 } 414 415 // Retrieves one character from the input stream, but does not read past markers. Will continue to return 0xFF when a marker is encountered. 416 ubyte get_octet () { 417 bool padding_flag; 418 int c = get_char(&padding_flag); 419 if (c == 0xFF) { 420 if (padding_flag) return 0xFF; 421 c = get_char(&padding_flag); 422 if (padding_flag) { stuff_char(0xFF); return 0xFF; } 423 if (c == 0x00) return 0xFF; 424 stuff_char(cast(ubyte)(c)); 425 stuff_char(0xFF); 426 return 0xFF; 427 } 428 return cast(ubyte)(c); 429 } 430 431 // Retrieves a variable number of bits from the input stream. Does not recognize markers. 432 uint get_bits (int num_bits) { 433 if (!num_bits) return 0; 434 uint i = m_bit_buf >> (32 - num_bits); 435 if ((m_bits_left -= num_bits) <= 0) { 436 m_bit_buf <<= (num_bits += m_bits_left); 437 uint c1 = get_char(); 438 uint c2 = get_char(); 439 m_bit_buf = (m_bit_buf & 0xFFFF0000) | (c1 << 8) | c2; 440 m_bit_buf <<= -m_bits_left; 441 m_bits_left += 16; 442 assert(m_bits_left >= 0); 443 } else { 444 m_bit_buf <<= num_bits; 445 } 446 return i; 447 } 448 449 // Retrieves a variable number of bits from the input stream. Markers will not be read into the input bit buffer. Instead, an infinite number of all 1's will be returned when a marker is encountered. 450 uint get_bits_no_markers (int num_bits) { 451 if (!num_bits) return 0; 452 uint i = m_bit_buf >> (32 - num_bits); 453 if ((m_bits_left -= num_bits) <= 0) { 454 m_bit_buf <<= (num_bits += m_bits_left); 455 if (m_in_buf_left < 2 || m_pIn_buf_ofs[0] == 0xFF || m_pIn_buf_ofs[1] == 0xFF) { 456 uint c1 = get_octet(); 457 uint c2 = get_octet(); 458 m_bit_buf |= (c1 << 8) | c2; 459 } else { 460 m_bit_buf |= (cast(uint)m_pIn_buf_ofs[0] << 8) | m_pIn_buf_ofs[1]; 461 m_in_buf_left -= 2; 462 m_pIn_buf_ofs += 2; 463 } 464 m_bit_buf <<= -m_bits_left; 465 m_bits_left += 16; 466 assert(m_bits_left >= 0); 467 } else { 468 m_bit_buf <<= num_bits; 469 } 470 return i; 471 } 472 473 void word_clear (void *p, ushort c, uint n) { 474 ubyte *pD = cast(ubyte*)p; 475 immutable ubyte l = c & 0xFF, h = (c >> 8) & 0xFF; 476 while (n) 477 { 478 pD[0] = l; pD[1] = h; pD += 2; 479 n--; 480 } 481 } 482 483 // Refill the input buffer. 484 // This method will sit in a loop until (A) the buffer is full or (B) 485 // the stream's read() method reports and end of file condition. 486 void prep_in_buffer () { 487 m_in_buf_left = 0; 488 m_pIn_buf_ofs = m_in_buf.ptr; 489 490 if (m_eof_flag) 491 return; 492 493 do 494 { 495 int bytes_read = readfn(m_in_buf.ptr + m_in_buf_left, JPGD_IN_BUF_SIZE - m_in_buf_left, &m_eof_flag); 496 if (bytes_read == -1) 497 stop_decoding(JPGD_STREAM_READ); 498 499 m_in_buf_left += bytes_read; 500 } while ((m_in_buf_left < JPGD_IN_BUF_SIZE) && (!m_eof_flag)); 501 502 503 // Pad the end of the block with M_EOI (prevents the decompressor from going off the rails if the stream is invalid). 504 // (This dates way back to when this decompressor was written in C/asm, and the all-asm Huffman decoder did some fancy things to increase perf.) 505 word_clear(m_pIn_buf_ofs + m_in_buf_left, 0xD9FF, 64); 506 } 507 508 void stop_decoding(int err) 509 { 510 throw new Exception("Couldn't parse JPEG"); 511 } 512 513 // Read the start of frame (SOF) marker. 514 void read_sof_marker () { 515 int i; 516 uint num_left; 517 518 num_left = get_bits(16); 519 520 if (get_bits(8) != 8) /* precision: sorry, only 8-bit precision is supported right now */ 521 stop_decoding(JPGD_BAD_PRECISION); 522 523 m_image_y_size = get_bits(16); 524 525 if ((m_image_y_size < 1) || (m_image_y_size > JPGD_MAX_HEIGHT)) 526 stop_decoding(JPGD_BAD_HEIGHT); 527 528 m_image_x_size = get_bits(16); 529 530 if ((m_image_x_size < 1) || (m_image_x_size > JPGD_MAX_WIDTH)) 531 stop_decoding(JPGD_BAD_WIDTH); 532 533 m_comps_in_frame = get_bits(8); 534 535 if (num_left != cast(uint)(m_comps_in_frame * 3 + 8)) 536 stop_decoding(JPGD_BAD_SOF_LENGTH); 537 538 for (i = 0; i < m_comps_in_frame; i++) 539 { 540 m_comp_ident.ptr[i] = get_bits(8); 541 get_bits(4); 542 get_bits(4); 543 get_bits(8); 544 } 545 } 546 547 // Used to skip unrecognized markers. 548 void skip_variable_marker () { 549 uint num_left; 550 551 num_left = get_bits(16); 552 553 if (num_left < 2) 554 stop_decoding(JPGD_BAD_VARIABLE_MARKER); 555 556 num_left -= 2; 557 558 while (num_left) 559 { 560 get_bits(8); 561 num_left--; 562 } 563 } 564 565 // Read a start of scan (SOS) marker. 566 void read_sos_marker () { 567 uint num_left; 568 int i, ci, n, c, cc; 569 570 num_left = get_bits(16); 571 572 n = get_bits(8); 573 574 m_comps_in_scan = n; 575 576 num_left -= 3; 577 578 if ( (num_left != cast(uint)(n * 2 + 3)) || (n < 1) || (n > JPGD_MAX_COMPS_IN_SCAN) ) 579 stop_decoding(JPGD_BAD_SOS_LENGTH); 580 581 for (i = 0; i < n; i++) 582 { 583 cc = get_bits(8); 584 c = get_bits(8); 585 num_left -= 2; 586 587 for (ci = 0; ci < m_comps_in_frame; ci++) 588 if (cc == m_comp_ident.ptr[ci]) 589 break; 590 591 if (ci >= m_comps_in_frame) 592 stop_decoding(JPGD_BAD_SOS_COMP_ID); 593 594 m_comp_list.ptr[i] = ci; 595 m_comp_dc_tab.ptr[ci] = (c >> 4) & 15; 596 m_comp_ac_tab.ptr[ci] = (c & 15) + (JPGD_MAX_HUFF_TABLES >> 1); 597 } 598 599 get_bits(8); 600 get_bits(8); 601 get_bits(4); 602 get_bits(4); 603 604 num_left -= 3; 605 606 /* read past whatever is num_left */ 607 while (num_left) 608 { 609 get_bits(8); 610 num_left--; 611 } 612 } 613 614 // Finds the next marker. 615 int next_marker () { 616 uint c, bytes; 617 618 bytes = 0; 619 620 do 621 { 622 do 623 { 624 bytes++; 625 c = get_bits(8); 626 } while (c != 0xFF); 627 628 do 629 { 630 c = get_bits(8); 631 } while (c == 0xFF); 632 633 } while (c == 0); 634 635 // If bytes > 0 here, there where extra bytes before the marker (not good). 636 637 return c; 638 } 639 640 // Process markers. Returns when an SOFx, SOI, EOI, or SOS marker is 641 // encountered. 642 int process_markers () { 643 int c; 644 645 for ( ; ; ) { 646 c = next_marker(); 647 648 switch (c) 649 { 650 case M_SOF0: 651 case M_SOF1: 652 case M_SOF2: 653 case M_SOF3: 654 case M_SOF5: 655 case M_SOF6: 656 case M_SOF7: 657 //case M_JPG: 658 case M_SOF9: 659 case M_SOF10: 660 case M_SOF11: 661 case M_SOF13: 662 case M_SOF14: 663 case M_SOF15: 664 case M_SOI: 665 case M_EOI: 666 case M_SOS: 667 return c; 668 669 case M_APP0: 670 uint num_left; 671 672 num_left = get_bits(16); 673 674 if (num_left < 7) 675 stop_decoding(JPGD_BAD_VARIABLE_MARKER); 676 677 num_left -= 2; 678 679 ubyte[5] jfif_id; 680 foreach(i; 0..5) 681 jfif_id[i] = cast(ubyte) get_bits(8); 682 683 num_left -= 5; 684 static immutable ubyte[5] JFIF = [0x4A, 0x46, 0x49, 0x46, 0x00]; 685 if (jfif_id == JFIF && num_left >= 7) 686 { 687 // skip version 688 get_bits(16); 689 uint units = get_bits(8); 690 int Xdensity = get_bits(16); 691 int Ydensity = get_bits(16); 692 num_left -= 7; 693 694 switch (units) 695 { 696 case 0: // no units 697 m_pixelsPerMeterX = defaultDPI; 698 m_pixelsPerMeterY = (Ydensity/cast(double)Xdensity) * m_pixelsPerMeterX; 699 break; 700 701 case 1: // dot per inch 702 m_pixelsPerMeterX = convertMetersToInches(Xdensity); 703 m_pixelsPerMeterY = convertMetersToInches(Ydensity); 704 break; 705 706 case 2: // dot per cm 707 m_pixelsPerMeterX = Xdensity * 100.0f; 708 m_pixelsPerMeterY = Ydensity * 100.0f; 709 break; 710 default: 711 } 712 } 713 714 // skip rests of chunk 715 716 while (num_left) 717 { 718 get_bits(8); 719 num_left--; 720 } 721 break; 722 723 case M_APP0+1: // possibly EXIF data 724 uint num_left; 725 num_left = get_bits(16); 726 727 if (num_left < 2) 728 stop_decoding(JPGD_BAD_VARIABLE_MARKER); 729 num_left -= 2; 730 731 ubyte[] exifData = new ubyte[num_left]; 732 foreach(i; 0..num_left) 733 exifData[i] = cast(ubyte)(get_bits(8)); 734 735 ubyte[6] exif_id; 736 foreach(i; 0..6) 737 exif_id[i] = popBE!ubyte(exifData); 738 739 static immutable ubyte[6] ExifIdentifierCode = [0x45, 0x78, 0x69, 0x66, 0x00, 0x00]; // "Exif\0\0" 740 if (exif_id == ExifIdentifierCode) 741 { 742 // See EXIF specification: http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf 743 744 ubyte[] tiffFile = exifData.dup; // save exif chunk from "start of TIFF file" 745 746 ushort byteOrder = popBE!ushort(exifData); 747 if (byteOrder != 0x4949 && byteOrder != 0x4D4D) 748 stop_decoding(JPGD_DECODE_ERROR); 749 bool littleEndian = (byteOrder == 0x4949); 750 ushort version_ = littleEndian ? popLE!ushort(exifData) : popBE!ushort(exifData); 751 if (version_ != 42) 752 stop_decoding(JPGD_DECODE_ERROR); 753 754 uint offset = littleEndian ? popLE!uint(exifData) : popBE!uint(exifData); 755 756 double resolutionX = 72; 757 double resolutionY = 72; 758 int unit = 2; 759 760 // parse all IFDs 761 while(offset != 0) 762 { 763 if (offset > exifData.length) 764 stop_decoding(JPGD_DECODE_ERROR); 765 exifData = tiffFile[offset..$]; 766 ushort numEntries = littleEndian ? popLE!ushort(exifData) : popBE!ushort(exifData); 767 768 foreach(entry; 0..numEntries) 769 { 770 ushort tag = littleEndian ? popLE!ushort(exifData) : popBE!ushort(exifData); 771 ushort type = littleEndian ? popLE!ushort(exifData) : popBE!ushort(exifData); 772 uint count = littleEndian ? popLE!uint(exifData) : popBE!uint(exifData); 773 uint valueOffset = littleEndian ? popLE!uint(exifData) : popBE!uint(exifData); 774 775 if (tag == 282 || tag == 283) // XResolution 776 { 777 ubyte[] tagData = tiffFile[valueOffset..$]; 778 double num = littleEndian ? popLE!uint(tagData) : popBE!uint(tagData); 779 double denom = littleEndian ? popLE!uint(tagData) : popBE!uint(tagData); 780 double frac = num / denom; 781 if (tag == 282) 782 resolutionX = frac; 783 else 784 resolutionY = frac; 785 } 786 787 if (tag == 296) // unit 788 unit = valueOffset; 789 } 790 offset = littleEndian ? popLE!uint(exifData) : popBE!uint(exifData); 791 } 792 793 if (unit == 2) // inches 794 { 795 m_pixelsPerMeterX = convertMetersToInches(resolutionX); 796 m_pixelsPerMeterY = convertMetersToInches(resolutionY); 797 } 798 else if (unit == 3) 799 { 800 m_pixelsPerMeterX = resolutionX * 100; 801 m_pixelsPerMeterY = resolutionY * 100; 802 } 803 } 804 805 break; 806 807 case M_JPG: 808 case M_RST0: /* no parameters */ 809 case M_RST1: 810 case M_RST2: 811 case M_RST3: 812 case M_RST4: 813 case M_RST5: 814 case M_RST6: 815 case M_RST7: 816 case M_TEM: 817 stop_decoding(JPGD_UNEXPECTED_MARKER); 818 break; 819 default: /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 */ 820 skip_variable_marker(); 821 break; 822 } 823 } 824 } 825 826 // Finds the start of image (SOI) marker. 827 // This code is rather defensive: it only checks the first 512 bytes to avoid 828 // false positives. 829 void locate_soi_marker () { 830 uint lastchar, thischar; 831 uint bytesleft; 832 833 lastchar = get_bits(8); 834 835 thischar = get_bits(8); 836 837 /* ok if it's a normal JPEG file without a special header */ 838 839 if ((lastchar == 0xFF) && (thischar == M_SOI)) 840 return; 841 842 bytesleft = 4096; //512; 843 844 for ( ; ; ) 845 { 846 if (--bytesleft == 0) 847 stop_decoding(JPGD_NOT_JPEG); 848 849 lastchar = thischar; 850 851 thischar = get_bits(8); 852 853 if (lastchar == 0xFF) 854 { 855 if (thischar == M_SOI) 856 break; 857 else if (thischar == M_EOI) // get_bits will keep returning M_EOI if we read past the end 858 stop_decoding(JPGD_NOT_JPEG); 859 } 860 } 861 862 // Check the next character after marker: if it's not 0xFF, it can't be the start of the next marker, so the file is bad. 863 thischar = (m_bit_buf >> 24) & 0xFF; 864 865 if (thischar != 0xFF) 866 stop_decoding(JPGD_NOT_JPEG); 867 } 868 869 // Find a start of frame (SOF) marker. 870 void locate_sof_marker () { 871 locate_soi_marker(); 872 873 int c = process_markers(); 874 875 switch (c) 876 { 877 case M_SOF2: 878 goto case; 879 case M_SOF0: /* baseline DCT */ 880 case M_SOF1: /* extended sequential DCT */ 881 read_sof_marker(); 882 break; 883 case M_SOF9: /* Arithmitic coding */ 884 stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT); 885 break; 886 default: 887 stop_decoding(JPGD_UNSUPPORTED_MARKER); 888 break; 889 } 890 } 891 892 // Reset everything to default/uninitialized state. 893 void initit (JpegStreamReadFunc rfn) { 894 m_error_code = JPGD_SUCCESS; 895 m_image_x_size = m_image_y_size = 0; 896 readfn = rfn; 897 898 memset(m_quant.ptr, 0, m_quant.sizeof); 899 900 m_comps_in_frame = 0; 901 902 memset(m_comp_ident.ptr, 0, m_comp_ident.sizeof); 903 memset(m_comp_h_blocks.ptr, 0, m_comp_h_blocks.sizeof); 904 memset(m_comp_v_blocks.ptr, 0, m_comp_v_blocks.sizeof); 905 906 m_comps_in_scan = 0; 907 memset(m_comp_list.ptr, 0, m_comp_list.sizeof); 908 memset(m_comp_dc_tab.ptr, 0, m_comp_dc_tab.sizeof); 909 memset(m_comp_ac_tab.ptr, 0, m_comp_ac_tab.sizeof); 910 911 m_blocks_per_mcu = 0; 912 m_max_blocks_per_row = 0; 913 m_mcus_per_row = 0; 914 m_mcus_per_col = 0; 915 m_expanded_blocks_per_component = 0; 916 m_expanded_blocks_per_mcu = 0; 917 m_expanded_blocks_per_row = 0; 918 m_freq_domain_chroma_upsample = false; 919 920 memset(m_mcu_org.ptr, 0, m_mcu_org.sizeof); 921 922 m_total_lines_left = 0; 923 m_mcu_lines_left = 0; 924 m_real_dest_bytes_per_scan_line = 0; 925 m_dest_bytes_per_scan_line = 0; 926 m_dest_bytes_per_pixel = 0; 927 928 memset(m_block_y_mcu.ptr, 0, m_block_y_mcu.sizeof); 929 930 m_eob_run = 0; 931 932 memset(m_block_y_mcu.ptr, 0, m_block_y_mcu.sizeof); 933 934 m_pIn_buf_ofs = m_in_buf.ptr; 935 m_in_buf_left = 0; 936 m_eof_flag = false; 937 m_tem_flag = 0; 938 939 memset(m_in_buf_pad_start.ptr, 0, m_in_buf_pad_start.sizeof); 940 memset(m_in_buf.ptr, 0, m_in_buf.sizeof); 941 memset(m_in_buf_pad_end.ptr, 0, m_in_buf_pad_end.sizeof); 942 943 m_restart_interval = 0; 944 m_restarts_left = 0; 945 m_next_restart_num = 0; 946 947 m_max_mcus_per_row = 0; 948 m_max_blocks_per_mcu = 0; 949 m_max_mcus_per_col = 0; 950 951 memset(m_last_dc_val.ptr, 0, m_last_dc_val.sizeof); 952 m_pMCU_coefficients = null; 953 m_pSample_buf = null; 954 955 m_pScan_line_0 = null; 956 m_pScan_line_1 = null; 957 958 // Ready the input buffer. 959 prep_in_buffer(); 960 961 // Prime the bit buffer. 962 m_bits_left = 16; 963 m_bit_buf = 0; 964 965 get_bits(16); 966 get_bits(16); 967 } 968 969 // This method throws back into the stream any bytes that where read 970 // into the bit buffer during initial marker scanning. 971 void fix_in_buffer () { 972 // In case any 0xFF's where pulled into the buffer during marker scanning. 973 assert((m_bits_left & 7) == 0); 974 975 if (m_bits_left == 16) 976 stuff_char(cast(ubyte)(m_bit_buf & 0xFF)); 977 978 if (m_bits_left >= 8) 979 stuff_char(cast(ubyte)((m_bit_buf >> 8) & 0xFF)); 980 981 stuff_char(cast(ubyte)((m_bit_buf >> 16) & 0xFF)); 982 stuff_char(cast(ubyte)((m_bit_buf >> 24) & 0xFF)); 983 984 m_bits_left = 16; 985 get_bits_no_markers(16); 986 get_bits_no_markers(16); 987 } 988 } 989 990 991 992 // ////////////////////////////////////////////////////////////////////////// // 993 /// decompress JPEG image, what else? 994 /// you can specify required color components in `req_comps` (3 for RGB or 4 for RGBA), or leave it as is to use image value. 995 void extract_jpeg_metadata(scope JpegStreamReadFunc rfn, out int width, out int height, out float pixelsPerMeterX, out float pixelsPerMeterY) 996 { 997 auto decoder = jpeg_decoder(rfn); 998 if (decoder.error_code != JPGD_SUCCESS) 999 return; 1000 width = decoder.m_image_x_size; 1001 height = decoder.m_image_y_size; 1002 pixelsPerMeterX = decoder.m_pixelsPerMeterX; 1003 pixelsPerMeterY = decoder.m_pixelsPerMeterY; 1004 } 1005 1006 // ////////////////////////////////////////////////////////////////////////// // 1007 /// decompress JPEG image from memory buffer. 1008 /// you can specify required color components in `req_comps` (3 for RGB or 4 for RGBA), or leave it as is to use image value. 1009 void extract_jpeg_metadata(const(ubyte)[] buf, out int width, out int height, out float pixelsPerMeterX, out float pixelsPerMeterY) { 1010 bool m_eof_flag; 1011 size_t bufpos; 1012 auto b = cast(const(ubyte)*)buf.ptr; 1013 extract_jpeg_metadata( delegate int (void* pBuf, int max_bytes_to_read, bool *pEOF_flag) { 1014 import core.stdc..string : memcpy; 1015 if (bufpos >= buf.length) { 1016 *pEOF_flag = true; 1017 return 0; 1018 } 1019 if (buf.length-bufpos < max_bytes_to_read) max_bytes_to_read = cast(int)(buf.length-bufpos); 1020 memcpy(pBuf, b, max_bytes_to_read); 1021 b += max_bytes_to_read; 1022 return max_bytes_to_read; 1023 }, 1024 width, height, pixelsPerMeterX, pixelsPerMeterY); 1025 }