ColorFilterArray(ColorFilterArray other)
 {
     cfa = null;
     setSize(other.size);
     if (cfa)
     {
         memcpy(cfa, other.cfa, size.area() * sizeof(CFAColor));
     }
 }
        // FC macro from dcraw outputs, given the filters definition, the dcraw color
        // number for that given position in the CFA pattern
        void FC(int filters, int row, int col)
        {
            ((filters) >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3)
        };

        ColorFilterArray(UInt32 filters)
        {
            size = new iPoint2D(8, 2);
            cfa  = null;
            setSize(size);

            for (int x = 0; x < 8; x++)
            {
                for (int y = 0; y < 2; y++)
                {
                    CFAColor c = toRawspeedColor(FC(filters, y, x));
                    setColorAt(iPoint2D(x, y), c);
                }
            }
        }

        ColorFilterArray& operator=(ColorFilterArray& other)
        {
            setSize(other.size);
            if (cfa)
            {
                memcpy(cfa, other.cfa, size.area() * sizeof(CFAColor));
            }
            return(*this);
        }

        void setSize(iPoint2D _size)
        {
            size = _size;
            if (cfa)
            {
                delete[] cfa;
            }
            cfa = null;
            if (size.area() > 100)
            {
                ThrowRDE("ColorFilterArray:setSize if your CFA pattern is really %d pixels in area we may as well give up now", size.area());
            }
            if (size.area() <= 0)
            {
                return;
            }
            cfa = new CFAColor[size.area()];
            if (!cfa)
            {
                ThrowRDE("ColorFilterArray:setSize Unable to allocate memory");
            }
            memset(cfa, CFA_UNKNOWN, size.area() * sizeof(CFAColor));
        }

        CFAColor getColorAt(UInt32 x, UInt32 y)
        {
            if (!cfa)
            {
                ThrowRDE("ColorFilterArray:getColorAt: No CFA size set");
            }
            if (x >= (UInt32)size.x || y >= (UInt32)size.y)
            {
                x = x % size.x;
                y = y % size.y;
            }
            return(cfa[x + y * size.x]);
        }

        void setCFA(iPoint2D in_size, ...)
        {
            if (in_size != size)
            {
                setSize(in_size);
            }
            va_list arguments;

            va_start(arguments, in_size);
            for (UInt32 i = 0; i < size.area(); i++)
            {
                cfa[i] = (CFAColor)va_arg(arguments, int);
            }
            va_end(arguments);
        }

        void shiftLeft(int n)
        {
            if (!size.x)
            {
                ThrowRDE("ColorFilterArray:shiftLeft: No CFA size set (or set to zero)");
            }
            writeLog(DEBUG_PRIO_EXTRA, "Shift left:%d\n", n);
            int shift = n % size.x;

            if (0 == shift)
            {
                return;
            }
            CFAColor *tmp = new CFAColor[size.x];

            for (int y = 0; y < size.y; y++)
            {
                CFAColor *old = &cfa[y * size.x];
                memcpy(tmp, &old[shift], (size.x - shift) * sizeof(CFAColor));
                memcpy(&tmp[size.x - shift], old, shift * sizeof(CFAColor));
                memcpy(old, tmp, size.x * sizeof(CFAColor));
            }
            delete[] tmp;
        }

        void shiftDown(int n)
        {
            if (!size.y)
            {
                ThrowRDE("ColorFilterArray:shiftDown: No CFA size set (or set to zero)");
            }
            writeLog(DEBUG_PRIO_EXTRA, "Shift down:%d\n", n);
            int shift = n % size.y;

            if (0 == shift)
            {
                return;
            }
            CFAColor *tmp = new CFAColor[size.y];

            for (int x = 0; x < size.x; x++)
            {
                CFAColor *old = &cfa[x];
                for (int y = 0; y < size.y; y++)
                {
                    tmp[y] = old[((y + shift) % size.y) * size.x];
                }
                for (int y = 0; y < size.y; y++)
                {
                    old[y * size.x] = tmp[y];
                }
            }
            delete[] tmp;
        }

        string asString()
        {
            string dst = string("");

            for (int y = 0; y < size.y; y++)
            {
                for (int x = 0; x < size.x; x++)
                {
                    dst += colorToString(getColorAt(x, y));
                    dst += (x == size.x - 1) ? "\n" : ",";
                }
            }
            return(dst);
        }

        string colorToString(CFAColor c)
        {
            switch (c)
            {
            case CFA_RED:
                return(string("RED"));

            case CFA_GREEN:
                return(string("GREEN"));

            case CFA_BLUE:
                return(string("BLUE"));

            case CFA_GREEN2:
                return(string("GREEN2"));

            case CFA_CYAN:
                return(string("CYAN"));

            case CFA_MAGENTA:
                return(string("MAGENTA"));

            case CFA_YELLOW:
                return(string("YELLOW"));

            case CFA_WHITE:
                return(string("WHITE"));

            case CFA_FUJI_GREEN:
                return(string("FUJIGREEN"));

            default:
                return(string("UNKNOWN"));
            }
        }

        void setColorAt(iPoint2D pos, CFAColor c)
        {
            if (pos.x >= size.x || pos.x < 0)
            {
                ThrowRDE("SetColor: position out of CFA pattern");
            }
            if (pos.y >= size.y || pos.y < 0)
            {
                ThrowRDE("SetColor: position out of CFA pattern");
            }
            cfa[pos.x + pos.y * size.x] = c;
        }

        UInt32 getDcrawFilter()
        {
            //dcraw magic
            if (size.x == 6 && size.y == 6)
            {
                return(9);
            }

            if (size.x > 8 || size.y > 2 || 0 == cfa)
            {
                return(1);
            }

            if (!isPowerOfTwo(size.x))
            {
                return(1);
            }

            UInt32 ret = 0;

            for (int x = 0; x < 8; x++)
            {
                for (int y = 0; y < 2; y++)
                {
                    UInt32 c = toDcrawColor(getColorAt(x, y));
                    int    g = (x >> 1) * 8;
                    ret |= c << ((x & 1) * 2 + y * 4 + g);
                }
            }
            for (int y = 0; y < size.y; y++)
            {
                for (int x = 0; x < size.x; x++)
                {
                    writeLog(DEBUG_PRIO_EXTRA, "%s,", colorToString((CFAColor)toDcrawColor(getColorAt(x, y))).c_str());
                }
                writeLog(DEBUG_PRIO_EXTRA, "\n");
            }
            writeLog(DEBUG_PRIO_EXTRA, "DCRAW filter:%x\n", ret);
            return(ret);
        }

        CFAColor toRawspeedColor(UInt32 dcrawColor)
        {
            switch (dcrawColor)
            {
            case 0: return(CFA_RED);

            case 1: return(CFA_GREEN);

            case 2: return(CFA_BLUE);

            case 3: return(CFA_GREEN2);
            }
            return(CFA_UNKNOWN);
        }

        UInt32 toDcrawColor(CFAColor c)
        {
            switch (c)
            {
            case CFA_FUJI_GREEN:
            case CFA_RED: return(0);

            case CFA_MAGENTA:
            case CFA_GREEN: return(1);

            case CFA_CYAN:
            case CFA_BLUE: return(2);

            case CFA_YELLOW:
            case CFA_GREEN2: return(3);

            default:
                break;
            }
            return(0);
        }
    };
} // namespace RawSpeed