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