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