/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <img_utils/DngUtils.h>
#include <inttypes.h>
#include <vector>
#include <math.h>
namespace android {
namespace img_utils {
OpcodeListBuilder::OpcodeListBuilder() : mCount(0), mOpList(), mEndianOut(&mOpList, BIG) {
if(mEndianOut.open() != OK) {
ALOGE("%s: Open failed.", __FUNCTION__);
}
}
OpcodeListBuilder::~OpcodeListBuilder() {
if(mEndianOut.close() != OK) {
ALOGE("%s: Close failed.", __FUNCTION__);
}
}
size_t OpcodeListBuilder::getSize() const {
return mOpList.getSize() + sizeof(mCount);
}
uint32_t OpcodeListBuilder::getCount() const {
return mCount;
}
status_t OpcodeListBuilder::buildOpList(uint8_t* buf) const {
uint32_t count = convertToBigEndian(mCount);
memcpy(buf, &count, sizeof(count));
memcpy(buf + sizeof(count), mOpList.getArray(), mOpList.getSize());
return OK;
}
status_t OpcodeListBuilder::addGainMapsForMetadata(uint32_t lsmWidth,
uint32_t lsmHeight,
uint32_t activeAreaTop,
uint32_t activeAreaLeft,
uint32_t activeAreaBottom,
uint32_t activeAreaRight,
CfaLayout cfa,
const float* lensShadingMap) {
uint32_t activeAreaWidth = activeAreaRight - activeAreaLeft;
uint32_t activeAreaHeight = activeAreaBottom - activeAreaTop;
double spacingV = 1.0 / lsmHeight;
double spacingH = 1.0 / lsmWidth;
std::vector<float> redMapVector(lsmWidth * lsmHeight);
float *redMap = redMapVector.data();
std::vector<float> greenEvenMapVector(lsmWidth * lsmHeight);
float *greenEvenMap = greenEvenMapVector.data();
std::vector<float> greenOddMapVector(lsmWidth * lsmHeight);
float *greenOddMap = greenOddMapVector.data();
std::vector<float> blueMapVector(lsmWidth * lsmHeight);
float *blueMap = blueMapVector.data();
size_t lsmMapSize = lsmWidth * lsmHeight * 4;
// Split lens shading map channels into separate arrays
size_t j = 0;
for (size_t i = 0; i < lsmMapSize; i += 4, ++j) {
redMap[j] = lensShadingMap[i + LSM_R_IND];
greenEvenMap[j] = lensShadingMap[i + LSM_GE_IND];
greenOddMap[j] = lensShadingMap[i + LSM_GO_IND];
blueMap[j] = lensShadingMap[i + LSM_B_IND];
}
uint32_t redTop = 0;
uint32_t redLeft = 0;
uint32_t greenEvenTop = 0;
uint32_t greenEvenLeft = 1;
uint32_t greenOddTop = 1;
uint32_t greenOddLeft = 0;
uint32_t blueTop = 1;
uint32_t blueLeft = 1;
switch(cfa) {
case CFA_RGGB:
redTop = 0;
redLeft = 0;
greenEvenTop = 0;
greenEvenLeft = 1;
greenOddTop = 1;
greenOddLeft = 0;
blueTop = 1;
blueLeft = 1;
break;
case CFA_GRBG:
redTop = 0;
redLeft = 1;
greenEvenTop = 0;
greenEvenLeft = 0;
greenOddTop = 1;
greenOddLeft = 1;
blueTop = 1;
blueLeft = 0;
break;
case CFA_GBRG:
redTop = 1;
redLeft = 0;
greenEvenTop = 0;
greenEvenLeft = 0;
greenOddTop = 1;
greenOddLeft = 1;
blueTop = 0;
blueLeft = 1;
break;
case CFA_BGGR:
redTop = 1;
redLeft = 1;
greenEvenTop = 0;
greenEvenLeft = 1;
greenOddTop = 1;
greenOddLeft = 0;
blueTop = 0;
blueLeft = 0;
break;
default:
ALOGE("%s: Unknown CFA layout %d", __FUNCTION__, cfa);
return BAD_VALUE;
}
status_t err = addGainMap(/*top*/redTop,
/*left*/redLeft,
/*bottom*/activeAreaHeight - 1,
/*right*/activeAreaWidth - 1,
/*plane*/0,
/*planes*/1,
/*rowPitch*/2,
/*colPitch*/2,
/*mapPointsV*/lsmHeight,
/*mapPointsH*/lsmWidth,
/*mapSpacingV*/spacingV,
/*mapSpacingH*/spacingH,
/*mapOriginV*/0,
/*mapOriginH*/0,
/*mapPlanes*/1,
/*mapGains*/redMap);
if (err != OK) return err;
err = addGainMap(/*top*/greenEvenTop,
/*left*/greenEvenLeft,
/*bottom*/activeAreaHeight - 1,
/*right*/activeAreaWidth - 1,
/*plane*/0,
/*planes*/1,
/*rowPitch*/2,
/*colPitch*/2,
/*mapPointsV*/lsmHeight,
/*mapPointsH*/lsmWidth,
/*mapSpacingV*/spacingV,
/*mapSpacingH*/spacingH,
/*mapOriginV*/0,
/*mapOriginH*/0,
/*mapPlanes*/1,
/*mapGains*/greenEvenMap);
if (err != OK) return err;
err = addGainMap(/*top*/greenOddTop,
/*left*/greenOddLeft,
/*bottom*/activeAreaHeight - 1,
/*right*/activeAreaWidth - 1,
/*plane*/0,
/*planes*/1,
/*rowPitch*/2,
/*colPitch*/2,
/*mapPointsV*/lsmHeight,
/*mapPointsH*/lsmWidth,
/*mapSpacingV*/spacingV,
/*mapSpacingH*/spacingH,
/*mapOriginV*/0,
/*mapOriginH*/0,
/*mapPlanes*/1,
/*mapGains*/greenOddMap);
if (err != OK) return err;
err = addGainMap(/*top*/blueTop,
/*left*/blueLeft,
/*bottom*/activeAreaHeight - 1,
/*right*/activeAreaWidth - 1,
/*plane*/0,
/*planes*/1,
/*rowPitch*/2,
/*colPitch*/2,
/*mapPointsV*/lsmHeight,
/*mapPointsH*/lsmWidth,
/*mapSpacingV*/spacingV,
/*mapSpacingH*/spacingH,
/*mapOriginV*/0,
/*mapOriginH*/0,
/*mapPlanes*/1,
/*mapGains*/blueMap);
return err;
}
status_t OpcodeListBuilder::addGainMap(uint32_t top,
uint32_t left,
uint32_t bottom,
uint32_t right,
uint32_t plane,
uint32_t planes,
uint32_t rowPitch,
uint32_t colPitch,
uint32_t mapPointsV,
uint32_t mapPointsH,
double mapSpacingV,
double mapSpacingH,
double mapOriginV,
double mapOriginH,
uint32_t mapPlanes,
const float* mapGains) {
status_t err = addOpcodePreamble(GAIN_MAP_ID);
if (err != OK) return err;
// Allow this opcode to be skipped if not supported
uint32_t flags = FLAG_OPTIONAL;
err = mEndianOut.write(&flags, 0, 1);
if (err != OK) return err;
const uint32_t NUMBER_INT_ARGS = 11;
const uint32_t NUMBER_DOUBLE_ARGS = 4;
uint32_t totalSize = NUMBER_INT_ARGS * sizeof(uint32_t) + NUMBER_DOUBLE_ARGS * sizeof(double) +
mapPointsV * mapPointsH * mapPlanes * sizeof(float);
err = mEndianOut.write(&totalSize, 0, 1);
if (err != OK) return err;
// Batch writes as much as possible
uint32_t settings1[] = { top,
left,
bottom,
right,
plane,
planes,
rowPitch,
colPitch,
mapPointsV,
mapPointsH };
err = mEndianOut.write(settings1, 0, NELEMS(settings1));
if (err != OK) return err;
double settings2[] = { mapSpacingV,
mapSpacingH,
mapOriginV,
mapOriginH };
err = mEndianOut.write(settings2, 0, NELEMS(settings2));
if (err != OK) return err;
err = mEndianOut.write(&mapPlanes, 0, 1);
if (err != OK) return err;
err = mEndianOut.write(mapGains, 0, mapPointsV * mapPointsH * mapPlanes);
if (err != OK) return err;
mCount++;
return OK;
}
status_t OpcodeListBuilder::addWarpRectilinearForMetadata(const float* kCoeffs,
uint32_t activeArrayWidth,
uint32_t activeArrayHeight,
float opticalCenterX,
float opticalCenterY) {
if (activeArrayWidth <= 1 || activeArrayHeight <= 1) {
ALOGE("%s: Cannot add opcode for active array with dimensions w=%" PRIu32 ", h=%" PRIu32,
__FUNCTION__, activeArrayWidth, activeArrayHeight);
return BAD_VALUE;
}
double normalizedOCX = opticalCenterX / static_cast<double>(activeArrayWidth - 1);
double normalizedOCY = opticalCenterY / static_cast<double>(activeArrayHeight - 1);
normalizedOCX = CLAMP(normalizedOCX, 0, 1);
normalizedOCY = CLAMP(normalizedOCY, 0, 1);
// Conversion factors from Camera2 K factors to DNG spec. K factors:
//
// Note: these are necessary because our unit system assumes a
// normalized max radius of sqrt(2), whereas the DNG spec's
// WarpRectilinear opcode assumes a normalized max radius of 1.
// Thus, each K coefficient must include the domain scaling
// factor (the DNG domain is scaled by sqrt(2) to emulate the
// domain used by the Camera2 specification).
const double c_0 = sqrt(2);
const double c_1 = 2 * sqrt(2);
const double c_2 = 4 * sqrt(2);
const double c_3 = 8 * sqrt(2);
const double c_4 = 2;
const double c_5 = 2;
const double coeffs[] = { c_0 * kCoeffs[0],
c_1 * kCoeffs[1],
c_2 * kCoeffs[2],
c_3 * kCoeffs[3],
c_4 * kCoeffs[4],
c_5 * kCoeffs[5] };
return addWarpRectilinear(/*numPlanes*/1,
/*opticalCenterX*/normalizedOCX,
/*opticalCenterY*/normalizedOCY,
coeffs);
}
status_t OpcodeListBuilder::addWarpRectilinear(uint32_t numPlanes,
double opticalCenterX,
double opticalCenterY,
const double* kCoeffs) {
status_t err = addOpcodePreamble(WARP_RECTILINEAR_ID);
if (err != OK) return err;
// Allow this opcode to be skipped if not supported
uint32_t flags = FLAG_OPTIONAL;
err = mEndianOut.write(&flags, 0, 1);
if (err != OK) return err;
const uint32_t NUMBER_CENTER_ARGS = 2;
const uint32_t NUMBER_COEFFS = numPlanes * 6;
uint32_t totalSize = (NUMBER_CENTER_ARGS + NUMBER_COEFFS) * sizeof(double) + sizeof(uint32_t);
err = mEndianOut.write(&totalSize, 0, 1);
if (err != OK) return err;
err = mEndianOut.write(&numPlanes, 0, 1);
if (err != OK) return err;
err = mEndianOut.write(kCoeffs, 0, NUMBER_COEFFS);
if (err != OK) return err;
err = mEndianOut.write(&opticalCenterX, 0, 1);
if (err != OK) return err;
err = mEndianOut.write(&opticalCenterY, 0, 1);
if (err != OK) return err;
mCount++;
return OK;
}
status_t OpcodeListBuilder::addBadPixelListForMetadata(const uint32_t* hotPixels,
uint32_t xyPairCount,
uint32_t colorFilterArrangement) {
if (colorFilterArrangement > 3) {
ALOGE("%s: Unknown color filter arrangement %" PRIu32, __FUNCTION__,
colorFilterArrangement);
return BAD_VALUE;
}
return addBadPixelList(colorFilterArrangement, xyPairCount, 0, hotPixels, nullptr);
}
status_t OpcodeListBuilder::addBadPixelList(uint32_t bayerPhase,
uint32_t badPointCount,
uint32_t badRectCount,
const uint32_t* badPointRowColPairs,
const uint32_t* badRectTopLeftBottomRightTuples) {
status_t err = addOpcodePreamble(FIX_BAD_PIXELS_LIST);
if (err != OK) return err;
// Allow this opcode to be skipped if not supported
uint32_t flags = FLAG_OPTIONAL;
err = mEndianOut.write(&flags, 0, 1);
if (err != OK) return err;
const uint32_t NUM_NON_VARLEN_FIELDS = 3;
const uint32_t SIZE_OF_POINT = 2;
const uint32_t SIZE_OF_RECT = 4;
uint32_t totalSize = (NUM_NON_VARLEN_FIELDS + badPointCount * SIZE_OF_POINT +
badRectCount * SIZE_OF_RECT) * sizeof(uint32_t);
err = mEndianOut.write(&totalSize, 0, 1);
if (err != OK) return err;
err = mEndianOut.write(&bayerPhase, 0, 1);
if (err != OK) return err;
err = mEndianOut.write(&badPointCount, 0, 1);
if (err != OK) return err;
err = mEndianOut.write(&badRectCount, 0, 1);
if (err != OK) return err;
if (badPointCount > 0) {
err = mEndianOut.write(badPointRowColPairs, 0, SIZE_OF_POINT * badPointCount);
if (err != OK) return err;
}
if (badRectCount > 0) {
err = mEndianOut.write(badRectTopLeftBottomRightTuples, 0, SIZE_OF_RECT * badRectCount);
if (err != OK) return err;
}
mCount++;
return OK;
}
status_t OpcodeListBuilder::addOpcodePreamble(uint32_t opcodeId) {
status_t err = mEndianOut.write(&opcodeId, 0, 1);
if (err != OK) return err;
uint8_t version[] = {1, 3, 0, 0};
err = mEndianOut.write(version, 0, NELEMS(version));
if (err != OK) return err;
return OK;
}
} /*namespace img_utils*/
} /*namespace android*/