| 474 | } |
| 475 | |
| 476 | std::optional<std::shared_ptr<std::vector<uint8_t>>> recoverContainerRawSamples(const uint8_t* data, size_t size) { |
| 477 | const ImageContainer container = sniffImageContainer(data, size); |
| 478 | if (container == ImageContainer::kUnknown) { |
| 479 | return std::nullopt; // not a container — caller treats the bytes as already raw |
| 480 | } |
| 481 | DecodedFrame staged; |
| 482 | staged.pixels = std::make_shared<std::vector<uint8_t>>(data, data + size); |
| 483 | const Expected<DecodedFrame> decoded = |
| 484 | (container == ImageContainer::kJpeg) ? JpegCodec{}.decode(staged) : PngCodec{}.decode(staged); |
| 485 | if (!decoded.has_value() || decoded->isNull() || decoded->width <= 0 || decoded->height <= 0) { |
| 486 | return std::nullopt; |
| 487 | } |
| 488 | const DecodedFrame& frame = *decoded; |
| 489 | const size_t samples = static_cast<size_t>(frame.width) * static_cast<size_t>(frame.height); |
| 490 | auto out = std::make_shared<std::vector<uint8_t>>(); |
| 491 | switch (frame.format) { |
| 492 | case PixelFormat::kMono16: |
| 493 | // 16-bit grayscale already IS the flat 2-byte-per-sample buffer. |
| 494 | case PixelFormat::kMono8: |
| 495 | *out = *frame.pixels; |
| 496 | break; |
| 497 | case PixelFormat::kRGB888: |
| 498 | case PixelFormat::kBGR888: { |
| 499 | // PngCodec expands 8-bit grayscale to RGB (R==G==B); collapse to the luma byte. |
| 500 | if (frame.pixels->size() < samples * 3) { |
| 501 | return std::nullopt; |
| 502 | } |
| 503 | out->resize(samples); |
| 504 | for (size_t i = 0; i < samples; ++i) { |
| 505 | (*out)[i] = (*frame.pixels)[i * 3]; |
| 506 | } |
| 507 | break; |
| 508 | } |
| 509 | case PixelFormat::kRGBA8888: |
| 510 | case PixelFormat::kBGRA8888: { |
| 511 | if (frame.pixels->size() < samples * 4) { |
| 512 | return std::nullopt; |
| 513 | } |
| 514 | out->resize(samples); |
| 515 | for (size_t i = 0; i < samples; ++i) { |
| 516 | (*out)[i] = (*frame.pixels)[i * 4]; |
| 517 | } |
| 518 | break; |
| 519 | } |
| 520 | default: |
| 521 | return std::nullopt; // YUV/NV12/DepthR32F aren't container-wrapped raw frames |
| 522 | } |
| 523 | return out; |
| 524 | } |
| 525 | |
| 526 | } // namespace PJ |