[libcamera-devel] [PATCH 08/13] ipa: raspberrypi: Introduce IpaBase class
Jacopo Mondi
jacopo.mondi at ideasonboard.com
Thu Apr 27 12:00:37 CEST 2023
Hi Naush
On Wed, Apr 26, 2023 at 02:10:52PM +0100, Naushir Patuck via libcamera-devel wrote:
> Create a new IpaBase class that handles general purpose housekeeping
> duties for the Raspberry Pi IPA. Code the implementation of new class is
> essentially pulled from the existing ipa/rpi/vc4/raspberrypi.cpp
> file with a minimal amount of refactoring.
>
> Create a derived IpaVc4 class from IpaBase that handles the VC4 pipeline
> specific tasks of the IPA. Again, code for this class implementation is
> taken from the existing ipa/rpi/vc4/raspberrypi.cpp with a
> minimal amount of refactoring.
>
> The goal of this change is to allow third parties to implement their own
> IPA running on the Raspberry Pi without duplicating all of the IPA
> housekeeping tasks.
>
> Signed-off-by: Naushir Patuck <naush at raspberrypi.com>
> ---
> src/ipa/meson.build | 5 +-
> .../raspberrypi.cpp => common/ipa_base.cpp} | 1219 +++++------------
> src/ipa/rpi/common/ipa_base.h | 125 ++
> src/ipa/rpi/common/meson.build | 7 +
> src/ipa/rpi/vc4/meson.build | 8 +-
> src/ipa/rpi/vc4/vc4.cpp | 540 ++++++++
> 6 files changed, 1012 insertions(+), 892 deletions(-)
> rename src/ipa/rpi/{vc4/raspberrypi.cpp => common/ipa_base.cpp} (65%)
> create mode 100644 src/ipa/rpi/common/ipa_base.h
> create mode 100644 src/ipa/rpi/common/meson.build
> create mode 100644 src/ipa/rpi/vc4/vc4.cpp
>
[snip]
>
> vc4_ipa_includes += include_directories('..')
> -vc4_ipa_sources += [rpi_ipa_cam_helper_sources, rpi_ipa_controller_sources]
> +vc4_ipa_sources += [
> + rpi_ipa_cam_helper_sources,
> + rpi_ipa_common_sources,
> + rpi_ipa_controller_sources,
> +]
>
> mod = shared_module(ipa_name,
> [vc4_ipa_sources, libcamera_generated_ipa_headers],
> diff --git a/src/ipa/rpi/vc4/vc4.cpp b/src/ipa/rpi/vc4/vc4.cpp
> new file mode 100644
> index 000000000000..0d929cda6c4a
> --- /dev/null
> +++ b/src/ipa/rpi/vc4/vc4.cpp
> @@ -0,0 +1,540 @@
> +/* SPDX-License-Identifier: BSD-2-Clause */
> +/*
> + * Copyright (C) 2019-2021, Raspberry Pi Ltd
> + *
> + * rpi.cpp - Raspberry Pi VC4/BCM2835 ISP IPA.
> + */
> +
> +#include <string.h>
> +#include <sys/mman.h>
> +
> +#include <linux/bcm2835-isp.h>
> +
> +#include <libcamera/base/log.h>
> +
> +#include "common/ipa_base.h"
> +#include "controller/af_status.h"
> +#include "controller/agc_algorithm.h"
> +#include "controller/alsc_status.h"
> +#include "controller/awb_status.h"
> +#include "controller/black_level_status.h"
> +#include "controller/ccm_status.h"
> +#include "controller/contrast_status.h"
> +#include "controller/denoise_algorithm.h"
> +#include "controller/denoise_status.h"
> +#include "controller/dpc_status.h"
> +#include "controller/geq_status.h"
> +#include "controller/lux_status.h"
> +#include "controller/noise_status.h"
> +#include "controller/sharpen_status.h"
> +
> +namespace libcamera {
> +
> +LOG_DECLARE_CATEGORY(IPARPI)
> +
> +namespace ipa::RPi {
> +
> +class IpaVc4 final : public IpaBase
> +{
> +public:
> + IpaVc4()
> + : IpaBase(), lsTable_(nullptr)
> + {
> + }
> +
> + ~IpaVc4()
> + {
> + if (lsTable_)
> + munmap(lsTable_, MaxLsGridSize);
> + }
> +
> +
Additional blank line
The rest looks good, I haven't gone in detail in the code as it's
mostly copied from the existing implementations but the class desig
looks correct to me
Reviewed-by: Jacopo Mondi <jacopo.mondi at ideasonboard.com>
Thanks
j
> +private:
> + int32_t platformInit(const InitParams ¶ms, InitResult *result) override;
> + int32_t platformConfigure(const ConfigParams ¶ms, ConfigResult *result) override;
> +
> + void platformPrepareIsp(const PrepareParams ¶ms, RPiController::Metadata &rpiMetadata) override;
> + RPiController::StatisticsPtr platformProcessStats(Span<uint8_t> mem) override;
> +
> + void handleControls(const ControlList &controls) override;
> + bool validateIspControls();
> +
> + void applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls);
> + void applyDG(const struct AgcStatus *dgStatus, ControlList &ctrls);
> + void applyCCM(const struct CcmStatus *ccmStatus, ControlList &ctrls);
> + void applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus, ControlList &ctrls);
> + void applyGamma(const struct ContrastStatus *contrastStatus, ControlList &ctrls);
> + void applyGEQ(const struct GeqStatus *geqStatus, ControlList &ctrls);
> + void applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList &ctrls);
> + void applySharpen(const struct SharpenStatus *sharpenStatus, ControlList &ctrls);
> + void applyDPC(const struct DpcStatus *dpcStatus, ControlList &ctrls);
> + void applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls);
> + void applyAF(const struct AfStatus *afStatus, ControlList &lensCtrls);
> + void resampleTable(uint16_t dest[], const std::vector<double> &src, int destW, int destH);
> +
> + /* VC4 ISP controls. */
> + ControlInfoMap ispCtrls_;
> +
> + /* LS table allocation passed in from the pipeline handler. */
> + SharedFD lsTableHandle_;
> + void *lsTable_;
> +};
> +
> +int32_t IpaVc4::platformInit([[maybe_unused]] const InitParams ¶ms, [[maybe_unused]] InitResult *result)
> +{
> + const std::string &target = controller_.getTarget();
> +
> + if (target != "bcm2835") {
> + LOG(IPARPI, Error)
> + << "Tuning data file target returned \"" << target << "\""
> + << ", expected \"bcm2835\"";
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +int32_t IpaVc4::platformConfigure(const ConfigParams ¶ms, [[maybe_unused]] ConfigResult *result)
> +{
> + ispCtrls_ = params.ispControls;
> + if (!validateIspControls()) {
> + LOG(IPARPI, Error) << "ISP control validation failed.";
> + return -1;
> + }
> +
> + /* Store the lens shading table pointer and handle if available. */
> + if (params.lsTableHandle.isValid()) {
> + /* Remove any previous table, if there was one. */
> + if (lsTable_) {
> + munmap(lsTable_, MaxLsGridSize);
> + lsTable_ = nullptr;
> + }
> +
> + /* Map the LS table buffer into user space. */
> + lsTableHandle_ = std::move(params.lsTableHandle);
> + if (lsTableHandle_.isValid()) {
> + lsTable_ = mmap(nullptr, MaxLsGridSize, PROT_READ | PROT_WRITE,
> + MAP_SHARED, lsTableHandle_.get(), 0);
> +
> + if (lsTable_ == MAP_FAILED) {
> + LOG(IPARPI, Error) << "dmaHeap mmap failure for LS table.";
> + lsTable_ = nullptr;
> + }
> + }
> + }
> +
> + return 0;
> +}
> +
> +void IpaVc4::platformPrepareIsp([[maybe_unused]] const PrepareParams ¶ms,
> + RPiController::Metadata &rpiMetadata)
> +{
> + ControlList ctrls(ispCtrls_);
> +
> + /* Lock the metadata buffer to avoid constant locks/unlocks. */
> + std::unique_lock<RPiController::Metadata> lock(rpiMetadata);
> +
> + AwbStatus *awbStatus = rpiMetadata.getLocked<AwbStatus>("awb.status");
> + if (awbStatus)
> + applyAWB(awbStatus, ctrls);
> +
> + CcmStatus *ccmStatus = rpiMetadata.getLocked<CcmStatus>("ccm.status");
> + if (ccmStatus)
> + applyCCM(ccmStatus, ctrls);
> +
> + AgcStatus *dgStatus = rpiMetadata.getLocked<AgcStatus>("agc.status");
> + if (dgStatus)
> + applyDG(dgStatus, ctrls);
> +
> + AlscStatus *lsStatus = rpiMetadata.getLocked<AlscStatus>("alsc.status");
> + if (lsStatus)
> + applyLS(lsStatus, ctrls);
> +
> + ContrastStatus *contrastStatus = rpiMetadata.getLocked<ContrastStatus>("contrast.status");
> + if (contrastStatus)
> + applyGamma(contrastStatus, ctrls);
> +
> + BlackLevelStatus *blackLevelStatus = rpiMetadata.getLocked<BlackLevelStatus>("black_level.status");
> + if (blackLevelStatus)
> + applyBlackLevel(blackLevelStatus, ctrls);
> +
> + GeqStatus *geqStatus = rpiMetadata.getLocked<GeqStatus>("geq.status");
> + if (geqStatus)
> + applyGEQ(geqStatus, ctrls);
> +
> + DenoiseStatus *denoiseStatus = rpiMetadata.getLocked<DenoiseStatus>("denoise.status");
> + if (denoiseStatus)
> + applyDenoise(denoiseStatus, ctrls);
> +
> + SharpenStatus *sharpenStatus = rpiMetadata.getLocked<SharpenStatus>("sharpen.status");
> + if (sharpenStatus)
> + applySharpen(sharpenStatus, ctrls);
> +
> + DpcStatus *dpcStatus = rpiMetadata.getLocked<DpcStatus>("dpc.status");
> + if (dpcStatus)
> + applyDPC(dpcStatus, ctrls);
> +
> + const AfStatus *afStatus = rpiMetadata.getLocked<AfStatus>("af.status");
> + if (afStatus) {
> + ControlList lensctrls(lensCtrls_);
> + applyAF(afStatus, lensctrls);
> + if (!lensctrls.empty())
> + setLensControls.emit(lensctrls);
> + }
> +
> + if (!ctrls.empty())
> + setIspControls.emit(ctrls);
> +}
> +
> +RPiController::StatisticsPtr IpaVc4::platformProcessStats(Span<uint8_t> mem)
> +{
> + using namespace RPiController;
> +
> + const bcm2835_isp_stats *stats = reinterpret_cast<bcm2835_isp_stats *>(mem.data());
> + StatisticsPtr statistics = std::make_unique<Statistics>(Statistics::AgcStatsPos::PreWb,
> + Statistics::ColourStatsPos::PostLsc);
> + const Controller::HardwareConfig &hw = controller_.getHardwareConfig();
> + unsigned int i;
> +
> + /* RGB histograms are not used, so do not populate them. */
> + statistics->yHist = RPiController::Histogram(stats->hist[0].g_hist,
> + hw.numHistogramBins);
> +
> + /* All region sums are based on a 16-bit normalised pipeline bit-depth. */
> + unsigned int scale = Statistics::NormalisationFactorPow2 - hw.pipelineWidth;
> +
> + statistics->awbRegions.init(hw.awbRegions);
> + for (i = 0; i < statistics->awbRegions.numRegions(); i++)
> + statistics->awbRegions.set(i, { { stats->awb_stats[i].r_sum << scale,
> + stats->awb_stats[i].g_sum << scale,
> + stats->awb_stats[i].b_sum << scale },
> + stats->awb_stats[i].counted,
> + stats->awb_stats[i].notcounted });
> +
> + statistics->agcRegions.init(hw.agcRegions);
> + for (i = 0; i < statistics->agcRegions.numRegions(); i++)
> + statistics->agcRegions.set(i, { { stats->agc_stats[i].r_sum << scale,
> + stats->agc_stats[i].g_sum << scale,
> + stats->agc_stats[i].b_sum << scale },
> + stats->agc_stats[i].counted,
> + stats->awb_stats[i].notcounted });
> +
> + statistics->focusRegions.init(hw.focusRegions);
> + for (i = 0; i < statistics->focusRegions.numRegions(); i++)
> + statistics->focusRegions.set(i, { stats->focus_stats[i].contrast_val[1][1] / 1000,
> + stats->focus_stats[i].contrast_val_num[1][1],
> + stats->focus_stats[i].contrast_val_num[1][0] });
> +
> + return statistics;
> +}
> +
> +void IpaVc4::handleControls([[maybe_unused]] const ControlList &controls)
> +{
> + /* No controls require any special updates to the hardware configuration. */
> +}
> +
> +bool IpaVc4::validateIspControls()
> +{
> + static const uint32_t ctrls[] = {
> + V4L2_CID_RED_BALANCE,
> + V4L2_CID_BLUE_BALANCE,
> + V4L2_CID_DIGITAL_GAIN,
> + V4L2_CID_USER_BCM2835_ISP_CC_MATRIX,
> + V4L2_CID_USER_BCM2835_ISP_GAMMA,
> + V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL,
> + V4L2_CID_USER_BCM2835_ISP_GEQ,
> + V4L2_CID_USER_BCM2835_ISP_DENOISE,
> + V4L2_CID_USER_BCM2835_ISP_SHARPEN,
> + V4L2_CID_USER_BCM2835_ISP_DPC,
> + V4L2_CID_USER_BCM2835_ISP_LENS_SHADING,
> + V4L2_CID_USER_BCM2835_ISP_CDN,
> + };
> +
> + for (auto c : ctrls) {
> + if (ispCtrls_.find(c) == ispCtrls_.end()) {
> + LOG(IPARPI, Error) << "Unable to find ISP control "
> + << utils::hex(c);
> + return false;
> + }
> + }
> +
> + return true;
> +}
> +
> +void IpaVc4::applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls)
> +{
> + LOG(IPARPI, Debug) << "Applying WB R: " << awbStatus->gainR << " B: "
> + << awbStatus->gainB;
> +
> + ctrls.set(V4L2_CID_RED_BALANCE,
> + static_cast<int32_t>(awbStatus->gainR * 1000));
> + ctrls.set(V4L2_CID_BLUE_BALANCE,
> + static_cast<int32_t>(awbStatus->gainB * 1000));
> +}
> +
> +void IpaVc4::applyDG(const struct AgcStatus *dgStatus, ControlList &ctrls)
> +{
> + ctrls.set(V4L2_CID_DIGITAL_GAIN,
> + static_cast<int32_t>(dgStatus->digitalGain * 1000));
> +}
> +
> +void IpaVc4::applyCCM(const struct CcmStatus *ccmStatus, ControlList &ctrls)
> +{
> + bcm2835_isp_custom_ccm ccm;
> +
> + for (int i = 0; i < 9; i++) {
> + ccm.ccm.ccm[i / 3][i % 3].den = 1000;
> + ccm.ccm.ccm[i / 3][i % 3].num = 1000 * ccmStatus->matrix[i];
> + }
> +
> + ccm.enabled = 1;
> + ccm.ccm.offsets[0] = ccm.ccm.offsets[1] = ccm.ccm.offsets[2] = 0;
> +
> + ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&ccm),
> + sizeof(ccm) });
> + ctrls.set(V4L2_CID_USER_BCM2835_ISP_CC_MATRIX, c);
> +}
> +
> +void IpaVc4::applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus, ControlList &ctrls)
> +{
> + bcm2835_isp_black_level blackLevel;
> +
> + blackLevel.enabled = 1;
> + blackLevel.black_level_r = blackLevelStatus->blackLevelR;
> + blackLevel.black_level_g = blackLevelStatus->blackLevelG;
> + blackLevel.black_level_b = blackLevelStatus->blackLevelB;
> +
> + ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&blackLevel),
> + sizeof(blackLevel) });
> + ctrls.set(V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL, c);
> +}
> +
> +void IpaVc4::applyGamma(const struct ContrastStatus *contrastStatus, ControlList &ctrls)
> +{
> + const unsigned int numGammaPoints = controller_.getHardwareConfig().numGammaPoints;
> + struct bcm2835_isp_gamma gamma;
> +
> + for (unsigned int i = 0; i < numGammaPoints - 1; i++) {
> + int x = i < 16 ? i * 1024
> + : (i < 24 ? (i - 16) * 2048 + 16384
> + : (i - 24) * 4096 + 32768);
> + gamma.x[i] = x;
> + gamma.y[i] = std::min<uint16_t>(65535, contrastStatus->gammaCurve.eval(x));
> + }
> +
> + gamma.x[numGammaPoints - 1] = 65535;
> + gamma.y[numGammaPoints - 1] = 65535;
> + gamma.enabled = 1;
> +
> + ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&gamma),
> + sizeof(gamma) });
> + ctrls.set(V4L2_CID_USER_BCM2835_ISP_GAMMA, c);
> +}
> +
> +void IpaVc4::applyGEQ(const struct GeqStatus *geqStatus, ControlList &ctrls)
> +{
> + bcm2835_isp_geq geq;
> +
> + geq.enabled = 1;
> + geq.offset = geqStatus->offset;
> + geq.slope.den = 1000;
> + geq.slope.num = 1000 * geqStatus->slope;
> +
> + ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&geq),
> + sizeof(geq) });
> + ctrls.set(V4L2_CID_USER_BCM2835_ISP_GEQ, c);
> +}
> +
> +void IpaVc4::applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList &ctrls)
> +{
> + using RPiController::DenoiseMode;
> +
> + bcm2835_isp_denoise denoise;
> + DenoiseMode mode = static_cast<DenoiseMode>(denoiseStatus->mode);
> +
> + denoise.enabled = mode != DenoiseMode::Off;
> + denoise.constant = denoiseStatus->noiseConstant;
> + denoise.slope.num = 1000 * denoiseStatus->noiseSlope;
> + denoise.slope.den = 1000;
> + denoise.strength.num = 1000 * denoiseStatus->strength;
> + denoise.strength.den = 1000;
> +
> + /* Set the CDN mode to match the SDN operating mode. */
> + bcm2835_isp_cdn cdn;
> + switch (mode) {
> + case DenoiseMode::ColourFast:
> + cdn.enabled = 1;
> + cdn.mode = CDN_MODE_FAST;
> + break;
> + case DenoiseMode::ColourHighQuality:
> + cdn.enabled = 1;
> + cdn.mode = CDN_MODE_HIGH_QUALITY;
> + break;
> + default:
> + cdn.enabled = 0;
> + }
> +
> + ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&denoise),
> + sizeof(denoise) });
> + ctrls.set(V4L2_CID_USER_BCM2835_ISP_DENOISE, c);
> +
> + c = ControlValue(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&cdn),
> + sizeof(cdn) });
> + ctrls.set(V4L2_CID_USER_BCM2835_ISP_CDN, c);
> +}
> +
> +void IpaVc4::applySharpen(const struct SharpenStatus *sharpenStatus, ControlList &ctrls)
> +{
> + bcm2835_isp_sharpen sharpen;
> +
> + sharpen.enabled = 1;
> + sharpen.threshold.num = 1000 * sharpenStatus->threshold;
> + sharpen.threshold.den = 1000;
> + sharpen.strength.num = 1000 * sharpenStatus->strength;
> + sharpen.strength.den = 1000;
> + sharpen.limit.num = 1000 * sharpenStatus->limit;
> + sharpen.limit.den = 1000;
> +
> + ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&sharpen),
> + sizeof(sharpen) });
> + ctrls.set(V4L2_CID_USER_BCM2835_ISP_SHARPEN, c);
> +}
> +
> +void IpaVc4::applyDPC(const struct DpcStatus *dpcStatus, ControlList &ctrls)
> +{
> + bcm2835_isp_dpc dpc;
> +
> + dpc.enabled = 1;
> + dpc.strength = dpcStatus->strength;
> +
> + ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&dpc),
> + sizeof(dpc) });
> + ctrls.set(V4L2_CID_USER_BCM2835_ISP_DPC, c);
> +}
> +
> +void IpaVc4::applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls)
> +{
> + /*
> + * Program lens shading tables into pipeline.
> + * Choose smallest cell size that won't exceed 63x48 cells.
> + */
> + const int cellSizes[] = { 16, 32, 64, 128, 256 };
> + unsigned int numCells = std::size(cellSizes);
> + unsigned int i, w, h, cellSize;
> + for (i = 0; i < numCells; i++) {
> + cellSize = cellSizes[i];
> + w = (mode_.width + cellSize - 1) / cellSize;
> + h = (mode_.height + cellSize - 1) / cellSize;
> + if (w < 64 && h <= 48)
> + break;
> + }
> +
> + if (i == numCells) {
> + LOG(IPARPI, Error) << "Cannot find cell size";
> + return;
> + }
> +
> + /* We're going to supply corner sampled tables, 16 bit samples. */
> + w++, h++;
> + bcm2835_isp_lens_shading ls = {
> + .enabled = 1,
> + .grid_cell_size = cellSize,
> + .grid_width = w,
> + .grid_stride = w,
> + .grid_height = h,
> + /* .dmabuf will be filled in by pipeline handler. */
> + .dmabuf = 0,
> + .ref_transform = 0,
> + .corner_sampled = 1,
> + .gain_format = GAIN_FORMAT_U4P10
> + };
> +
> + if (!lsTable_ || w * h * 4 * sizeof(uint16_t) > MaxLsGridSize) {
> + LOG(IPARPI, Error) << "Do not have a correctly allocate lens shading table!";
> + return;
> + }
> +
> + if (lsStatus) {
> + /* Format will be u4.10 */
> + uint16_t *grid = static_cast<uint16_t *>(lsTable_);
> +
> + resampleTable(grid, lsStatus->r, w, h);
> + resampleTable(grid + w * h, lsStatus->g, w, h);
> + memcpy(grid + 2 * w * h, grid + w * h, w * h * sizeof(uint16_t));
> + resampleTable(grid + 3 * w * h, lsStatus->b, w, h);
> + }
> +
> + ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&ls),
> + sizeof(ls) });
> + ctrls.set(V4L2_CID_USER_BCM2835_ISP_LENS_SHADING, c);
> +}
> +
> +void IpaVc4::applyAF(const struct AfStatus *afStatus, ControlList &lensCtrls)
> +{
> + if (afStatus->lensSetting) {
> + ControlValue v(afStatus->lensSetting.value());
> + lensCtrls.set(V4L2_CID_FOCUS_ABSOLUTE, v);
> + }
> +}
> +
> +/*
> + * Resamples a 16x12 table with central sampling to destW x destH with corner
> + * sampling.
> + */
> +void IpaVc4::resampleTable(uint16_t dest[], const std::vector<double> &src,
> + int destW, int destH)
> +{
> + /*
> + * Precalculate and cache the x sampling locations and phases to
> + * save recomputing them on every row.
> + */
> + assert(destW > 1 && destH > 1 && destW <= 64);
> + int xLo[64], xHi[64];
> + double xf[64];
> + double x = -0.5, xInc = 16.0 / (destW - 1);
> + for (int i = 0; i < destW; i++, x += xInc) {
> + xLo[i] = floor(x);
> + xf[i] = x - xLo[i];
> + xHi[i] = xLo[i] < 15 ? xLo[i] + 1 : 15;
> + xLo[i] = xLo[i] > 0 ? xLo[i] : 0;
> + }
> +
> + /* Now march over the output table generating the new values. */
> + double y = -0.5, yInc = 12.0 / (destH - 1);
> + for (int j = 0; j < destH; j++, y += yInc) {
> + int yLo = floor(y);
> + double yf = y - yLo;
> + int yHi = yLo < 11 ? yLo + 1 : 11;
> + yLo = yLo > 0 ? yLo : 0;
> + double const *rowAbove = src.data() + yLo * 16;
> + double const *rowBelow = src.data() + yHi * 16;
> + for (int i = 0; i < destW; i++) {
> + double above = rowAbove[xLo[i]] * (1 - xf[i]) + rowAbove[xHi[i]] * xf[i];
> + double below = rowBelow[xLo[i]] * (1 - xf[i]) + rowBelow[xHi[i]] * xf[i];
> + int result = floor(1024 * (above * (1 - yf) + below * yf) + .5);
> + *(dest++) = result > 16383 ? 16383 : result; /* want u4.10 */
> + }
> + }
> +}
> +
> +} /* namespace ipa::RPi */
> +
> +/*
> + * External IPA module interface
> + */
> +extern "C" {
> +const struct IPAModuleInfo ipaModuleInfo = {
> + IPA_MODULE_API_VERSION,
> + 1,
> + "PipelineHandlerRPi",
> + "vc4",
> +};
> +
> +IPAInterface *ipaCreate()
> +{
> + return new ipa::RPi::IpaVc4();
> +}
> +
> +} /* extern "C" */
> +
> +} /* namespace libcamera */
> --
> 2.34.1
>
More information about the libcamera-devel
mailing list