1 /*
2     RawSpeed - RAW file decoder.
3 
4     Copyright (C) 2009-2014 Klaus Post
5 
6     This library is free software; you can redistribute it and/or
7     modify it under the terms of the GNU Lesser General Public
8     License as published by the Free Software Foundation; either
9     version 2 of the License, or (at your option) any later version.
10 
11     This library is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14     Lesser General Public License for more details.
15 
16     You should have received a copy of the GNU Lesser General Public
17     License along with this library; if not, write to the Free Software
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 
21 #include "metadata/ColorFilterArray.h"
22 #include "common/Common.h"                // for writeLog, DEBUG_PRIO_EXTRA
23 #include "common/Point.h"                 // for iPoint2D, iPoint2D::value_...
24 #include "decoders/RawDecoderException.h" // for ThrowRDE
25 #include <algorithm>                      // for fill
26 #include <cstdarg>                        // for va_arg, va_end, va_list
27 #include <cstdlib>                        // for size_t, abs
28 #include <map>                            // for map
29 #include <stdexcept>                      // for out_of_range
30 #include <string>                         // for string
31 
32 using std::vector;
33 using std::string;
34 using std::out_of_range;
35 using std::map;
36 
37 namespace rawspeed {
38 
ColorFilterArray(const iPoint2D & _size)39 ColorFilterArray::ColorFilterArray(const iPoint2D &_size) {
40   setSize(_size);
41 }
42 
setSize(const iPoint2D & _size)43 void ColorFilterArray::setSize(const iPoint2D& _size)
44 {
45   size = _size;
46 
47   if (size.area() > 36) {
48     // Bayer, FC() supports 2x8 pattern
49     // X-Trans is 6x6 pattern
50     // is there anything bigger?
51     ThrowRDE("if your CFA pattern is really %zu pixels "
52              "in area we may as well give up now",
53              size.area());
54   }
55   if (size.area() <= 0)
56     return;
57   cfa.resize(size.area());
58   fill(cfa.begin(), cfa.end(), CFA_UNKNOWN);
59 }
60 
getColorAt(int x,int y) const61 CFAColor ColorFilterArray::getColorAt( int x, int y ) const
62 {
63   if (cfa.empty())
64     ThrowRDE("No CFA size set");
65 
66   // calculate the positive modulo [0 .. size-1]
67   x = (x % size.x + size.x) % size.x;
68   y = (y % size.y + size.y) % size.y;
69 
70   return cfa[x + static_cast<size_t>(y) * size.x];
71 }
72 
setCFA(iPoint2D in_size,...)73 void ColorFilterArray::setCFA( iPoint2D in_size, ... )
74 {
75   if (in_size != size) {
76     setSize(in_size);
77   }
78   va_list arguments;
79   va_start(arguments, in_size);
80   for (auto i = 0UL; i < size.area(); i++) {
81     cfa[i] = static_cast<CFAColor>(va_arg(arguments, int));
82   }
83   va_end (arguments);
84 }
85 
shiftLeft(int n)86 void ColorFilterArray::shiftLeft(int n) {
87   if (cfa.empty())
88     ThrowRDE("No CFA size set (or set to zero)");
89 
90   writeLog(DEBUG_PRIO_EXTRA, "Shift left:%d", n);
91   n %= size.x;
92   if (n == 0)
93     return;
94 
95   vector<CFAColor> tmp(size.area());
96   for (int y = 0; y < size.y; ++y) {
97     for (int x = 0; x < size.x; ++x) {
98       tmp[x + static_cast<size_t>(y) * size.x] = getColorAt(x + n, y);
99     }
100   }
101   cfa = tmp;
102 }
103 
shiftDown(int n)104 void ColorFilterArray::shiftDown(int n) {
105   if (cfa.empty())
106     ThrowRDE("No CFA size set (or set to zero)");
107 
108   writeLog(DEBUG_PRIO_EXTRA, "Shift down:%d", n);
109   n %= size.y;
110   if (n == 0)
111     return;
112   vector<CFAColor> tmp(size.area());
113   for (int y = 0; y < size.y; ++y) {
114     for (int x = 0; x < size.x; ++x) {
115       tmp[x + static_cast<size_t>(y) * size.x] = getColorAt(x, y + n);
116     }
117   }
118   cfa = tmp;
119 }
120 
asString() const121 string ColorFilterArray::asString() const {
122   string dst;
123   for (int y = 0; y < size.y; y++) {
124     for (int x = 0; x < size.x; x++) {
125       dst += colorToString(getColorAt(x,y));
126       dst += (x == size.x - 1) ? "\n" : ",";
127     }
128   }
129   return dst;
130 }
131 
shiftDcrawFilter(uint32_t filter,int x,int y)132 uint32_t ColorFilterArray::shiftDcrawFilter(uint32_t filter, int x, int y) {
133   // filter is a series of 4 bytes that describe a 2x8 matrix. 2 is the width,
134   // 8 is the height. each byte describes a 2x2 pixel block. so each pixel has
135   // 2 bits of information. This allows to distinguish 4 different colors.
136 
137   if (std::abs(x) & 1) {
138     // A shift in x direction means swapping the first and second half of each
139     // nibble.
140     // see http://graphics.stanford.edu/~seander/bithacks.html#SwappingBitsXOR
141     for (int n = 0; n < 8; ++n) {
142       int i = n * 4;
143       int j = i + 2;
144       uint32_t t = ((filter >> i) ^ (filter >> j)) & ((1U << 2) - 1);
145       filter = filter ^ ((t << i) | (t << j));
146     }
147   }
148 
149   if (y == 0)
150     return filter;
151 
152   // A shift in y direction means rotating the whole int by 4 bits.
153   y *= 4;
154   y = y >= 0 ? y % 32 : 32 - ((-y) % 32);
155   filter = (filter >> y) | (filter << (32 - y));
156 
157   return filter;
158 }
159 
160 const map<CFAColor, string> ColorFilterArray::color2String = {
161     {CFA_RED, "RED"},         {CFA_GREEN, "GREEN"},
162     {CFA_BLUE, "BLUE"},       {CFA_CYAN, "CYAN"},
163     {CFA_MAGENTA, "MAGENTA"}, {CFA_YELLOW, "YELLOW"},
164     {CFA_WHITE, "WHITE"},     {CFA_FUJI_GREEN, "FUJIGREEN"},
165     {CFA_UNKNOWN, "UNKNOWN"}};
166 
colorToString(CFAColor c)167 string ColorFilterArray::colorToString(CFAColor c)
168 {
169   try {
170     return color2String.at(c);
171   } catch (std::out_of_range&) {
172     ThrowRDE("Unsupported CFA Color: %u", c);
173   }
174 }
175 
setColorAt(iPoint2D pos,CFAColor c)176 void ColorFilterArray::setColorAt(iPoint2D pos, CFAColor c) {
177   if (pos.x >= size.x || pos.x < 0)
178     ThrowRDE("position out of CFA pattern");
179   if (pos.y >= size.y || pos.y < 0)
180     ThrowRDE("position out of CFA pattern");
181   cfa[pos.x + static_cast<size_t>(pos.y) * size.x] = c;
182 }
183 
toDcrawColor(CFAColor c)184 static uint32_t toDcrawColor(CFAColor c) {
185   switch (c) {
186   case CFA_FUJI_GREEN:
187   case CFA_RED: return 0;
188   case CFA_MAGENTA:
189   case CFA_GREEN: return 1;
190   case CFA_CYAN:
191   case CFA_BLUE: return 2;
192   case CFA_YELLOW: return 3;
193   default:
194     throw out_of_range(ColorFilterArray::colorToString(c));
195   }
196 }
197 
getDcrawFilter() const198 uint32_t ColorFilterArray::getDcrawFilter() const {
199   //dcraw magic
200   if (size.x == 6 && size.y == 6)
201     return 9;
202 
203   if (cfa.empty() || size.x > 2 || size.y > 8 || !isPowerOfTwo(size.y))
204     return 1;
205 
206   uint32_t ret = 0;
207   for (int x = 0; x < 2; x++) {
208     for (int y = 0; y < 8; y++) {
209       uint32_t c = toDcrawColor(getColorAt(x, y));
210       int g = (x >> 1) * 8;
211       ret |= c << ((x&1)*2 + y*4 + g);
212     }
213   }
214 
215   writeLog(DEBUG_PRIO_EXTRA, "%s", asString().c_str());
216   writeLog(DEBUG_PRIO_EXTRA, "DCRAW filter:%x", ret);
217 
218   return ret;
219 }
220 
221 } // namespace rawspeed
222