1/*
2 * Copyright 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <img_utils/DngUtils.h>
18
19#include <inttypes.h>
20
21#include <algorithm>
22#include <vector>
23#include <math.h>
24
25namespace android {
26namespace img_utils {
27
28OpcodeListBuilder::OpcodeListBuilder() : mCount(0), mOpList(), mEndianOut(&mOpList, BIG) {
29    if(mEndianOut.open() != OK) {
30        ALOGE("%s: Open failed.", __FUNCTION__);
31    }
32}
33
34OpcodeListBuilder::~OpcodeListBuilder() {
35    if(mEndianOut.close() != OK) {
36        ALOGE("%s: Close failed.", __FUNCTION__);
37    }
38}
39
40size_t OpcodeListBuilder::getSize() const {
41    return mOpList.getSize() + sizeof(mCount);
42}
43
44uint32_t OpcodeListBuilder::getCount() const {
45    return mCount;
46}
47
48status_t OpcodeListBuilder::buildOpList(uint8_t* buf) const {
49    uint32_t count = convertToBigEndian(mCount);
50    memcpy(buf, &count, sizeof(count));
51    memcpy(buf + sizeof(count), mOpList.getArray(), mOpList.getSize());
52    return OK;
53}
54
55status_t OpcodeListBuilder::addGainMapsForMetadata(uint32_t lsmWidth,
56                                                   uint32_t lsmHeight,
57                                                   uint32_t activeAreaTop,
58                                                   uint32_t activeAreaLeft,
59                                                   uint32_t activeAreaBottom,
60                                                   uint32_t activeAreaRight,
61                                                   CfaLayout cfa,
62                                                   const float* lensShadingMap) {
63    uint32_t activeAreaWidth = activeAreaRight - activeAreaLeft;
64    uint32_t activeAreaHeight = activeAreaBottom - activeAreaTop;
65    double spacingV = 1.0 / std::max(1u, lsmHeight - 1);
66    double spacingH = 1.0 / std::max(1u, lsmWidth - 1);
67
68    std::vector<float> redMapVector(lsmWidth * lsmHeight);
69    float *redMap = redMapVector.data();
70
71    std::vector<float> greenEvenMapVector(lsmWidth * lsmHeight);
72    float *greenEvenMap = greenEvenMapVector.data();
73
74    std::vector<float> greenOddMapVector(lsmWidth * lsmHeight);
75    float *greenOddMap = greenOddMapVector.data();
76
77    std::vector<float> blueMapVector(lsmWidth * lsmHeight);
78    float *blueMap = blueMapVector.data();
79
80    size_t lsmMapSize = lsmWidth * lsmHeight * 4;
81
82    // Split lens shading map channels into separate arrays
83    size_t j = 0;
84    for (size_t i = 0; i < lsmMapSize; i += 4, ++j) {
85        redMap[j] = lensShadingMap[i + LSM_R_IND];
86        greenEvenMap[j] = lensShadingMap[i + LSM_GE_IND];
87        greenOddMap[j] = lensShadingMap[i + LSM_GO_IND];
88        blueMap[j] = lensShadingMap[i + LSM_B_IND];
89    }
90
91    uint32_t redTop = 0;
92    uint32_t redLeft = 0;
93    uint32_t greenEvenTop = 0;
94    uint32_t greenEvenLeft = 1;
95    uint32_t greenOddTop = 1;
96    uint32_t greenOddLeft = 0;
97    uint32_t blueTop = 1;
98    uint32_t blueLeft = 1;
99
100    switch(cfa) {
101        case CFA_RGGB:
102            redTop = 0;
103            redLeft = 0;
104            greenEvenTop = 0;
105            greenEvenLeft = 1;
106            greenOddTop = 1;
107            greenOddLeft = 0;
108            blueTop = 1;
109            blueLeft = 1;
110            break;
111        case CFA_GRBG:
112            redTop = 0;
113            redLeft = 1;
114            greenEvenTop = 0;
115            greenEvenLeft = 0;
116            greenOddTop = 1;
117            greenOddLeft = 1;
118            blueTop = 1;
119            blueLeft = 0;
120            break;
121        case CFA_GBRG:
122            redTop = 1;
123            redLeft = 0;
124            greenEvenTop = 0;
125            greenEvenLeft = 0;
126            greenOddTop = 1;
127            greenOddLeft = 1;
128            blueTop = 0;
129            blueLeft = 1;
130            break;
131        case CFA_BGGR:
132            redTop = 1;
133            redLeft = 1;
134            greenEvenTop = 0;
135            greenEvenLeft = 1;
136            greenOddTop = 1;
137            greenOddLeft = 0;
138            blueTop = 0;
139            blueLeft = 0;
140            break;
141        default:
142            ALOGE("%s: Unknown CFA layout %d", __FUNCTION__, cfa);
143            return BAD_VALUE;
144    }
145
146    status_t err = addGainMap(/*top*/redTop,
147                              /*left*/redLeft,
148                              /*bottom*/activeAreaHeight - 1,
149                              /*right*/activeAreaWidth - 1,
150                              /*plane*/0,
151                              /*planes*/1,
152                              /*rowPitch*/2,
153                              /*colPitch*/2,
154                              /*mapPointsV*/lsmHeight,
155                              /*mapPointsH*/lsmWidth,
156                              /*mapSpacingV*/spacingV,
157                              /*mapSpacingH*/spacingH,
158                              /*mapOriginV*/0,
159                              /*mapOriginH*/0,
160                              /*mapPlanes*/1,
161                              /*mapGains*/redMap);
162    if (err != OK) return err;
163
164    err = addGainMap(/*top*/greenEvenTop,
165                     /*left*/greenEvenLeft,
166                     /*bottom*/activeAreaHeight - 1,
167                     /*right*/activeAreaWidth - 1,
168                     /*plane*/0,
169                     /*planes*/1,
170                     /*rowPitch*/2,
171                     /*colPitch*/2,
172                     /*mapPointsV*/lsmHeight,
173                     /*mapPointsH*/lsmWidth,
174                     /*mapSpacingV*/spacingV,
175                     /*mapSpacingH*/spacingH,
176                     /*mapOriginV*/0,
177                     /*mapOriginH*/0,
178                     /*mapPlanes*/1,
179                     /*mapGains*/greenEvenMap);
180    if (err != OK) return err;
181
182    err = addGainMap(/*top*/greenOddTop,
183                     /*left*/greenOddLeft,
184                     /*bottom*/activeAreaHeight - 1,
185                     /*right*/activeAreaWidth - 1,
186                     /*plane*/0,
187                     /*planes*/1,
188                     /*rowPitch*/2,
189                     /*colPitch*/2,
190                     /*mapPointsV*/lsmHeight,
191                     /*mapPointsH*/lsmWidth,
192                     /*mapSpacingV*/spacingV,
193                     /*mapSpacingH*/spacingH,
194                     /*mapOriginV*/0,
195                     /*mapOriginH*/0,
196                     /*mapPlanes*/1,
197                     /*mapGains*/greenOddMap);
198    if (err != OK) return err;
199
200    err = addGainMap(/*top*/blueTop,
201                     /*left*/blueLeft,
202                     /*bottom*/activeAreaHeight - 1,
203                     /*right*/activeAreaWidth - 1,
204                     /*plane*/0,
205                     /*planes*/1,
206                     /*rowPitch*/2,
207                     /*colPitch*/2,
208                     /*mapPointsV*/lsmHeight,
209                     /*mapPointsH*/lsmWidth,
210                     /*mapSpacingV*/spacingV,
211                     /*mapSpacingH*/spacingH,
212                     /*mapOriginV*/0,
213                     /*mapOriginH*/0,
214                     /*mapPlanes*/1,
215                     /*mapGains*/blueMap);
216    return err;
217}
218
219status_t OpcodeListBuilder::addGainMap(uint32_t top,
220                                       uint32_t left,
221                                       uint32_t bottom,
222                                       uint32_t right,
223                                       uint32_t plane,
224                                       uint32_t planes,
225                                       uint32_t rowPitch,
226                                       uint32_t colPitch,
227                                       uint32_t mapPointsV,
228                                       uint32_t mapPointsH,
229                                       double mapSpacingV,
230                                       double mapSpacingH,
231                                       double mapOriginV,
232                                       double mapOriginH,
233                                       uint32_t mapPlanes,
234                                       const float* mapGains) {
235
236    status_t err = addOpcodePreamble(GAIN_MAP_ID);
237    if (err != OK) return err;
238
239    // Allow this opcode to be skipped if not supported
240    uint32_t flags = FLAG_OPTIONAL;
241
242    err = mEndianOut.write(&flags, 0, 1);
243    if (err != OK) return err;
244
245    const uint32_t NUMBER_INT_ARGS = 11;
246    const uint32_t NUMBER_DOUBLE_ARGS = 4;
247
248    uint32_t totalSize = NUMBER_INT_ARGS * sizeof(uint32_t) + NUMBER_DOUBLE_ARGS * sizeof(double) +
249            mapPointsV * mapPointsH * mapPlanes * sizeof(float);
250
251    err = mEndianOut.write(&totalSize, 0, 1);
252    if (err != OK) return err;
253
254    // Batch writes as much as possible
255    uint32_t settings1[] = { top,
256                            left,
257                            bottom,
258                            right,
259                            plane,
260                            planes,
261                            rowPitch,
262                            colPitch,
263                            mapPointsV,
264                            mapPointsH };
265
266    err = mEndianOut.write(settings1, 0, NELEMS(settings1));
267    if (err != OK) return err;
268
269    double settings2[] = { mapSpacingV,
270                          mapSpacingH,
271                          mapOriginV,
272                          mapOriginH };
273
274    err = mEndianOut.write(settings2, 0, NELEMS(settings2));
275    if (err != OK) return err;
276
277    err = mEndianOut.write(&mapPlanes, 0, 1);
278    if (err != OK) return err;
279
280    err = mEndianOut.write(mapGains, 0, mapPointsV * mapPointsH * mapPlanes);
281    if (err != OK) return err;
282
283    mCount++;
284
285    return OK;
286}
287
288status_t OpcodeListBuilder::addWarpRectilinearForMetadata(const float* kCoeffs,
289                                                          uint32_t activeArrayWidth,
290                                                          uint32_t activeArrayHeight,
291                                                          float opticalCenterX,
292                                                          float opticalCenterY) {
293    if (activeArrayWidth <= 1 || activeArrayHeight <= 1) {
294        ALOGE("%s: Cannot add opcode for active array with dimensions w=%" PRIu32 ", h=%" PRIu32,
295                __FUNCTION__, activeArrayWidth, activeArrayHeight);
296        return BAD_VALUE;
297    }
298
299    double normalizedOCX = opticalCenterX / static_cast<double>(activeArrayWidth - 1);
300    double normalizedOCY = opticalCenterY / static_cast<double>(activeArrayHeight - 1);
301
302    normalizedOCX = CLAMP(normalizedOCX, 0, 1);
303    normalizedOCY = CLAMP(normalizedOCY, 0, 1);
304
305    double coeffs[6] = {
306        kCoeffs[0],
307        kCoeffs[1],
308        kCoeffs[2],
309        kCoeffs[3],
310        kCoeffs[4],
311        kCoeffs[5]
312    };
313
314    return addWarpRectilinear(/*numPlanes*/1,
315                              /*opticalCenterX*/normalizedOCX,
316                              /*opticalCenterY*/normalizedOCY,
317                              coeffs);
318}
319
320status_t OpcodeListBuilder::addWarpRectilinear(uint32_t numPlanes,
321                                               double opticalCenterX,
322                                               double opticalCenterY,
323                                               const double* kCoeffs) {
324
325    status_t err = addOpcodePreamble(WARP_RECTILINEAR_ID);
326    if (err != OK) return err;
327
328    // Allow this opcode to be skipped if not supported
329    uint32_t flags = FLAG_OPTIONAL;
330
331    err = mEndianOut.write(&flags, 0, 1);
332    if (err != OK) return err;
333
334    const uint32_t NUMBER_CENTER_ARGS = 2;
335    const uint32_t NUMBER_COEFFS = numPlanes * 6;
336    uint32_t totalSize = (NUMBER_CENTER_ARGS + NUMBER_COEFFS) * sizeof(double) + sizeof(uint32_t);
337
338    err = mEndianOut.write(&totalSize, 0, 1);
339    if (err != OK) return err;
340
341    err = mEndianOut.write(&numPlanes, 0, 1);
342    if (err != OK) return err;
343
344    err = mEndianOut.write(kCoeffs, 0, NUMBER_COEFFS);
345    if (err != OK) return err;
346
347    err = mEndianOut.write(&opticalCenterX, 0, 1);
348    if (err != OK) return err;
349
350    err = mEndianOut.write(&opticalCenterY, 0, 1);
351    if (err != OK) return err;
352
353    mCount++;
354
355    return OK;
356}
357
358status_t OpcodeListBuilder::addBadPixelListForMetadata(const uint32_t* hotPixels,
359                                                       uint32_t xyPairCount,
360                                                       uint32_t colorFilterArrangement) {
361    if (colorFilterArrangement > 3) {
362        ALOGE("%s:  Unknown color filter arrangement %" PRIu32, __FUNCTION__,
363                colorFilterArrangement);
364        return BAD_VALUE;
365    }
366
367    return addBadPixelList(colorFilterArrangement, xyPairCount, 0, hotPixels, nullptr);
368}
369
370status_t OpcodeListBuilder::addBadPixelList(uint32_t bayerPhase,
371                                            uint32_t badPointCount,
372                                            uint32_t badRectCount,
373                                            const uint32_t* badPointRowColPairs,
374                                            const uint32_t* badRectTopLeftBottomRightTuples) {
375
376    status_t err = addOpcodePreamble(FIX_BAD_PIXELS_LIST);
377    if (err != OK) return err;
378
379    // Allow this opcode to be skipped if not supported
380    uint32_t flags = FLAG_OPTIONAL;
381
382    err = mEndianOut.write(&flags, 0, 1);
383    if (err != OK) return err;
384
385    const uint32_t NUM_NON_VARLEN_FIELDS = 3;
386    const uint32_t SIZE_OF_POINT = 2;
387    const uint32_t SIZE_OF_RECT = 4;
388
389    uint32_t totalSize =  (NUM_NON_VARLEN_FIELDS  + badPointCount * SIZE_OF_POINT +
390            badRectCount * SIZE_OF_RECT) * sizeof(uint32_t);
391    err = mEndianOut.write(&totalSize, 0, 1);
392    if (err != OK) return err;
393
394    err = mEndianOut.write(&bayerPhase, 0, 1);
395    if (err != OK) return err;
396
397    err = mEndianOut.write(&badPointCount, 0, 1);
398    if (err != OK) return err;
399
400    err = mEndianOut.write(&badRectCount, 0, 1);
401    if (err != OK) return err;
402
403    if (badPointCount > 0) {
404        err = mEndianOut.write(badPointRowColPairs, 0, SIZE_OF_POINT * badPointCount);
405        if (err != OK) return err;
406    }
407
408    if (badRectCount > 0) {
409        err = mEndianOut.write(badRectTopLeftBottomRightTuples, 0, SIZE_OF_RECT * badRectCount);
410        if (err != OK) return err;
411    }
412
413    mCount++;
414    return OK;
415}
416
417status_t OpcodeListBuilder::addOpcodePreamble(uint32_t opcodeId) {
418    status_t err = mEndianOut.write(&opcodeId, 0, 1);
419    if (err != OK) return err;
420
421    uint8_t version[] = {1, 3, 0, 0};
422    err = mEndianOut.write(version, 0, NELEMS(version));
423    if (err != OK) return err;
424    return OK;
425}
426
427} /*namespace img_utils*/
428} /*namespace android*/
429