| /* SPDX-License-Identifier: LGPL-2.1-or-later */ | |
| /* | |
| * Copyright (C) 2020, Google Inc. | |
| * | |
| * thumbnailer.cpp - Simple image thumbnailer | |
| */ | |
| #include "thumbnailer.h" | |
| #include <libcamera/base/log.h> | |
| #include <libcamera/formats.h> | |
| #include "libcamera/internal/mapped_framebuffer.h" | |
| using namespace libcamera; | |
| LOG_DEFINE_CATEGORY(Thumbnailer) | |
| Thumbnailer::Thumbnailer() | |
| : valid_(false) | |
| { | |
| } | |
| void Thumbnailer::configure(const libcamera::StreamConfiguration &cfg, PixelFormat pixelFormat) | |
| { | |
| sourceSize_ = cfg.size; | |
| pixelFormat_ = pixelFormat; | |
| stride_ = cfg.stride; | |
| if (pixelFormat_ != formats::NV12) { | |
| LOG(Thumbnailer, Error) | |
| << "Failed to configure: Pixel Format " | |
| << pixelFormat_ << " unsupported."; | |
| return; | |
| } | |
| valid_ = true; | |
| } | |
| void Thumbnailer::createThumbnail(const FrameBuffer &source, | |
| const Size &targetSize, | |
| std::vector<unsigned char> *destination) | |
| { | |
| MappedFrameBuffer frame(&source, MappedFrameBuffer::MapFlag::Read); | |
| if (!frame.isValid()) { | |
| LOG(Thumbnailer, Error) | |
| << "Failed to map FrameBuffer : " | |
| << strerror(frame.error()); | |
| return; | |
| } | |
| if (!valid_) { | |
| LOG(Thumbnailer, Error) << "Config is unconfigured or invalid."; | |
| return; | |
| } | |
| const unsigned int stride = stride_; | |
| const unsigned int sw = sourceSize_.width; | |
| const unsigned int sh = sourceSize_.height; | |
| const unsigned int tw = targetSize.width; | |
| const unsigned int th = targetSize.height; | |
| ASSERT(frame.planes().size() == 2); | |
| ASSERT(tw % 2 == 0 && th % 2 == 0); | |
| /* Image scaling block implementing nearest-neighbour algorithm. */ | |
| unsigned char *src = frame.planes()[0].data(); | |
| unsigned char *srcC = frame.planes()[1].data(); | |
| unsigned char *srcCb, *srcCr; | |
| unsigned char *dstY, *srcY; | |
| size_t dstSize = (th * tw) + ((th / 2) * tw); | |
| destination->resize(dstSize); | |
| unsigned char *dst = destination->data(); | |
| unsigned char *dstC = dst + th * tw; | |
| for (unsigned int y = 0; y < th; y += 2) { | |
| unsigned int sourceY = (sh * y + th / 2) / th; | |
| dstY = dst + y * tw; | |
| srcY = src + stride * sourceY; | |
| srcCb = srcC + (sourceY / 2) * stride + 0; | |
| srcCr = srcC + (sourceY / 2) * stride + 1; | |
| for (unsigned int x = 0; x < tw; x += 2) { | |
| unsigned int sourceX = (sw * x + tw / 2) / tw; | |
| dstY[x] = srcY[sourceX]; | |
| dstY[tw + x] = srcY[sw + sourceX]; | |
| dstY[x + 1] = srcY[sourceX + 1]; | |
| dstY[tw + x + 1] = srcY[sw + sourceX + 1]; | |
| dstC[(y / 2) * tw + x + 0] = srcCb[(sourceX / 2) * 2]; | |
| dstC[(y / 2) * tw + x + 1] = srcCr[(sourceX / 2) * 2]; | |
| } | |
| } | |
| } |