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 
23 namespace android {
24 namespace img_utils {
25 
OpcodeListBuilder()26 OpcodeListBuilder::OpcodeListBuilder() : mCount(0), mOpList(), mEndianOut(&mOpList, BIG) {
27     if(mEndianOut.open() != OK) {
28         ALOGE("%s: Open failed.", __FUNCTION__);
29     }
30 }
31 
~OpcodeListBuilder()32 OpcodeListBuilder::~OpcodeListBuilder() {
33     if(mEndianOut.close() != OK) {
34         ALOGE("%s: Close failed.", __FUNCTION__);
35     }
36 }
37 
getSize() const38 size_t OpcodeListBuilder::getSize() const {
39     return mOpList.getSize() + sizeof(mCount);
40 }
41 
getCount() const42 uint32_t OpcodeListBuilder::getCount() const {
43     return mCount;
44 }
45 
buildOpList(uint8_t * buf) const46 status_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 
addGainMapsForMetadata(uint32_t lsmWidth,uint32_t lsmHeight,uint32_t activeAreaTop,uint32_t activeAreaLeft,uint32_t activeAreaBottom,uint32_t activeAreaRight,CfaLayout cfa,const float * lensShadingMap)53 status_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 
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)210 status_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     uint32_t opcodeId = GAIN_MAP_ID;
228 
229     status_t err = mEndianOut.write(&opcodeId, 0, 1);
230     if (err != OK) return err;
231 
232     uint8_t version[] = {1, 3, 0, 0};
233     err = mEndianOut.write(version, 0, NELEMS(version));
234     if (err != OK) return err;
235 
236     // Allow this opcode to be skipped if not supported
237     uint32_t flags = FLAG_OPTIONAL;
238 
239     err = mEndianOut.write(&flags, 0, 1);
240     if (err != OK) return err;
241 
242     const uint32_t NUMBER_INT_ARGS = 11;
243     const uint32_t NUMBER_DOUBLE_ARGS = 4;
244 
245     uint32_t totalSize = NUMBER_INT_ARGS * sizeof(uint32_t) + NUMBER_DOUBLE_ARGS * sizeof(double) +
246             mapPointsV * mapPointsH * mapPlanes * sizeof(float);
247 
248     err = mEndianOut.write(&totalSize, 0, 1);
249     if (err != OK) return err;
250 
251     // Batch writes as much as possible
252     uint32_t settings1[] = { top,
253                             left,
254                             bottom,
255                             right,
256                             plane,
257                             planes,
258                             rowPitch,
259                             colPitch,
260                             mapPointsV,
261                             mapPointsH };
262 
263     err = mEndianOut.write(settings1, 0, NELEMS(settings1));
264     if (err != OK) return err;
265 
266     double settings2[] = { mapSpacingV,
267                           mapSpacingH,
268                           mapOriginV,
269                           mapOriginH };
270 
271     err = mEndianOut.write(settings2, 0, NELEMS(settings2));
272     if (err != OK) return err;
273 
274     err = mEndianOut.write(&mapPlanes, 0, 1);
275     if (err != OK) return err;
276 
277     err = mEndianOut.write(mapGains, 0, mapPointsV * mapPointsH * mapPlanes);
278     if (err != OK) return err;
279 
280     mCount++;
281 
282     return OK;
283 }
284 
addWarpRectilinearForMetadata(const float * kCoeffs,uint32_t activeArrayWidth,uint32_t activeArrayHeight,float opticalCenterX,float opticalCenterY)285 status_t OpcodeListBuilder::addWarpRectilinearForMetadata(const float* kCoeffs,
286                                                           uint32_t activeArrayWidth,
287                                                           uint32_t activeArrayHeight,
288                                                           float opticalCenterX,
289                                                           float opticalCenterY) {
290     if (activeArrayWidth <= 1 || activeArrayHeight <= 1) {
291         ALOGE("%s: Cannot add opcode for active array with dimensions w=%" PRIu32 ", h=%" PRIu32,
292                 __FUNCTION__, activeArrayWidth, activeArrayHeight);
293         return BAD_VALUE;
294     }
295 
296     double normalizedOCX = opticalCenterX / static_cast<double>(activeArrayWidth - 1);
297     double normalizedOCY = opticalCenterY / static_cast<double>(activeArrayHeight - 1);
298 
299     normalizedOCX = CLAMP(normalizedOCX, 0, 1);
300     normalizedOCY = CLAMP(normalizedOCY, 0, 1);
301 
302     // Conversion factors from Camera2 K factors to DNG spec. K factors:
303     //
304     //      Note: these are necessary because our unit system assumes a
305     //      normalized max radius of sqrt(2), whereas the DNG spec's
306     //      WarpRectilinear opcode assumes a normalized max radius of 1.
307     //      Thus, each K coefficient must include the domain scaling
308     //      factor (the DNG domain is scaled by sqrt(2) to emulate the
309     //      domain used by the Camera2 specification).
310 
311     const double c_0 = sqrt(2);
312     const double c_1 = 2 * sqrt(2);
313     const double c_2 = 4 * sqrt(2);
314     const double c_3 = 8 * sqrt(2);
315     const double c_4 = 2;
316     const double c_5 = 2;
317 
318     const double coeffs[] = { c_0 * kCoeffs[0],
319                               c_1 * kCoeffs[1],
320                               c_2 * kCoeffs[2],
321                               c_3 * kCoeffs[3],
322                               c_4 * kCoeffs[4],
323                               c_5 * kCoeffs[5] };
324 
325 
326     return addWarpRectilinear(/*numPlanes*/1,
327                               /*opticalCenterX*/normalizedOCX,
328                               /*opticalCenterY*/normalizedOCY,
329                               coeffs);
330 }
331 
addWarpRectilinear(uint32_t numPlanes,double opticalCenterX,double opticalCenterY,const double * kCoeffs)332 status_t OpcodeListBuilder::addWarpRectilinear(uint32_t numPlanes,
333                                                double opticalCenterX,
334                                                double opticalCenterY,
335                                                const double* kCoeffs) {
336 
337     uint32_t opcodeId = WARP_RECTILINEAR_ID;
338 
339     status_t err = mEndianOut.write(&opcodeId, 0, 1);
340     if (err != OK) return err;
341 
342     uint8_t version[] = {1, 3, 0, 0};
343     err = mEndianOut.write(version, 0, NELEMS(version));
344     if (err != OK) return err;
345 
346     // Allow this opcode to be skipped if not supported
347     uint32_t flags = FLAG_OPTIONAL;
348 
349     err = mEndianOut.write(&flags, 0, 1);
350     if (err != OK) return err;
351 
352     const uint32_t NUMBER_CENTER_ARGS = 2;
353     const uint32_t NUMBER_COEFFS = numPlanes * 6;
354     uint32_t totalSize = (NUMBER_CENTER_ARGS + NUMBER_COEFFS) * sizeof(double) + sizeof(uint32_t);
355 
356     err = mEndianOut.write(&totalSize, 0, 1);
357     if (err != OK) return err;
358 
359     err = mEndianOut.write(&numPlanes, 0, 1);
360     if (err != OK) return err;
361 
362     err = mEndianOut.write(kCoeffs, 0, NUMBER_COEFFS);
363     if (err != OK) return err;
364 
365     err = mEndianOut.write(&opticalCenterX, 0, 1);
366     if (err != OK) return err;
367 
368     err = mEndianOut.write(&opticalCenterY, 0, 1);
369     if (err != OK) return err;
370 
371     mCount++;
372 
373     return OK;
374 }
375 
376 } /*namespace img_utils*/
377 } /*namespace android*/
378