| 592 | |
| 593 | namespace { |
| 594 | bool CompressInternal(const uint8* srcdata, int width, int height, |
| 595 | const CompressFlags& flags, string* output) { |
| 596 | output->clear(); |
| 597 | const int components = (static_cast<int>(flags.format) & 0xff); |
| 598 | |
| 599 | int64 total_size = static_cast<int64>(width) * static_cast<int64>(height); |
| 600 | // Some of the internal routines do not gracefully handle ridiculously |
| 601 | // large images, so fail fast. |
| 602 | if (width <= 0 || height <= 0) { |
| 603 | LOG(ERROR) << "Invalid image size: " << width << " x " << height; |
| 604 | return false; |
| 605 | } |
| 606 | if (total_size >= (1LL << 29)) { |
| 607 | LOG(ERROR) << "Image too large: " << total_size; |
| 608 | return false; |
| 609 | } |
| 610 | |
| 611 | int in_stride = flags.stride; |
| 612 | if (in_stride == 0) { |
| 613 | in_stride = width * (static_cast<int>(flags.format) & 0xff); |
| 614 | } else if (in_stride < width * components) { |
| 615 | LOG(ERROR) << "Incompatible input stride"; |
| 616 | return false; |
| 617 | } |
| 618 | |
| 619 | JOCTET* buffer = nullptr; |
| 620 | |
| 621 | // NOTE: for broader use xmp_metadata should be made a unicode string |
| 622 | CHECK(srcdata != nullptr); |
| 623 | CHECK(output != nullptr); |
| 624 | // This struct contains the JPEG compression parameters and pointers to |
| 625 | // working space |
| 626 | struct jpeg_compress_struct cinfo; |
| 627 | // This struct represents a JPEG error handler. |
| 628 | struct jpeg_error_mgr jerr; |
| 629 | jmp_buf jpeg_jmpbuf; // recovery point in case of error |
| 630 | |
| 631 | // Step 1: allocate and initialize JPEG compression object |
| 632 | // Use the usual jpeg error manager. |
| 633 | cinfo.err = jpeg_std_error(&jerr); |
| 634 | cinfo.client_data = &jpeg_jmpbuf; |
| 635 | jerr.error_exit = CatchError; |
| 636 | if (setjmp(jpeg_jmpbuf)) { |
| 637 | output->clear(); |
| 638 | delete[] buffer; |
| 639 | return false; |
| 640 | } |
| 641 | |
| 642 | jpeg_create_compress(&cinfo); |
| 643 | |
| 644 | // Step 2: specify data destination |
| 645 | // We allocate a buffer of reasonable size. If we have a small image, just |
| 646 | // estimate the size of the output using the number of bytes of the input. |
| 647 | // If this is getting too big, we will append to the string by chunks of 1MB. |
| 648 | // This seems like a reasonable compromise between performance and memory. |
| 649 | int bufsize = std::min(width * height * components, 1 << 20); |
| 650 | buffer = new JOCTET[bufsize]; |
| 651 | SetDest(&cinfo, buffer, bufsize, output); |