switch (_usedGlyphs)
    {
    case cocos2d::GlyphCollection::DYNAMIC:
        break;
    case cocos2d::GlyphCollection::NEHE:
        glyphCollection = _glyphNEHE;
        break;
    case cocos2d::GlyphCollection::ASCII:
        glyphCollection = _glyphASCII;
        break;
    case cocos2d::GlyphCollection::CUSTOM:
        glyphCollection = _customGlyphs.c_str();
        break;
    default:
        break;
    }

/****************************************************************************

http://www.cocos2d-x.org

****************************************************************************/

#include "2d/CCFontFreeType.h"
#include FT_BBOX_H
#include "edtaa3func.h"
#include "2d/CCFontAtlas.h"
#include "base/CCDirector.h"
#include "base/ccUTF8.h"
#include "platform/CCFileUtils.h"

NS_CC_BEGIN

FT_Library FontFreeType::_FTlibrary;
bool       FontFreeType::_FTInitialized = false;
const int  FontFreeType::DistanceMapSpread = 3;

const char* FontFreeType::_glyphASCII = "\"!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ ";
const char* FontFreeType::_glyphNEHE = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ";

typedef struct _DataRef
{
    Data data;
    unsigned int referenceCount;
}DataRef;

static std::unordered_map<std::string, DataRef> s_cacheFontData;

FontFreeType * FontFreeType::create(const std::string &fontName, float fontSize, GlyphCollection glyphs, const char *customGlyphs,bool distanceFieldEnabled /* = false */,float outline /* = 0 */)
{
    FontFreeType *tempFont =  new (std::nothrow) FontFreeType(distanceFieldEnabled,outline);

    if (!tempFont)
        return nullptr;

    tempFont->setGlyphCollection(glyphs, customGlyphs);

    if (!tempFont->createFontObject(fontName, fontSize))
    {
        delete tempFont;
        return nullptr;
    }
    tempFont->autorelease();
    return tempFont;
}

bool FontFreeType::initFreeType()
{
    if (_FTInitialized == false)
    {
        // begin freetype
        if (FT_Init_FreeType( &_FTlibrary ))
            return false;

        _FTInitialized = true;
    }

    return  _FTInitialized;
}

void FontFreeType::shutdownFreeType()
{
    if (_FTInitialized == true)
    {
        FT_Done_FreeType(_FTlibrary);
        s_cacheFontData.clear();
        _FTInitialized = false;
    }
}

FT_Library FontFreeType::getFTLibrary()
{
    initFreeType();
    return _FTlibrary;
}

FontFreeType::FontFreeType(bool distanceFieldEnabled /* = false */, float outline /* = 0 */)
: _fontRef(nullptr)
, _stroker(nullptr)
, _encoding(FT_ENCODING_UNICODE)
, _distanceFieldEnabled(distanceFieldEnabled)
, _outlineSize(0.0f)
, _lineHeight(0)
, _fontAtlas(nullptr)
, _usedGlyphs(GlyphCollection::ASCII)
{
    if (outline > 0.0f)
    {
        _outlineSize = outline * CC_CONTENT_SCALE_FACTOR();
        FT_Stroker_New(FontFreeType::getFTLibrary(), &_stroker);
        FT_Stroker_Set(_stroker,
            (int)(_outlineSize * 64),
            FT_STROKER_LINECAP_ROUND,
            FT_STROKER_LINEJOIN_ROUND,
            0);
    }
}

bool FontFreeType::createFontObject(const std::string &fontName, float fontSize)
{
    FT_Face face;
    // save font name locally
    _fontName = fontName;

    auto it = s_cacheFontData.find(fontName);
    if (it != s_cacheFontData.end())
    {
        (*it).second.referenceCount += 1;
    }
    else
    {
        s_cacheFontData[fontName].referenceCount = 1;
        s_cacheFontData[fontName].data = FileUtils::getInstance()->getDataFromFile(fontName);

        if (s_cacheFontData[fontName].data.isNull())
        {
            return false;
        }
    }

    if (FT_New_Memory_Face(getFTLibrary(), s_cacheFontData[fontName].data.getBytes(), s_cacheFontData[fontName].data.getSize(), 0, &face ))
        return false;

    if (FT_Select_Charmap(face, FT_ENCODING_UNICODE))
    {
        int foundIndex = -1;
        for (int charmapIndex = 0; charmapIndex < face->num_charmaps; charmapIndex++)
        {
            if (face->charmaps[charmapIndex]->encoding != FT_ENCODING_NONE)
            {
                foundIndex = charmapIndex;
                break;
            }
        }

        if (foundIndex == -1)
        {
            return false;
        }

        _encoding = face->charmaps[foundIndex]->encoding;
        if (FT_Select_Charmap(face, _encoding))
        {
            return false;
        }
    }

    // set the requested font size
    int dpi = 72;
    int fontSizePoints = (int)(64.f * fontSize * CC_CONTENT_SCALE_FACTOR());
    if (FT_Set_Char_Size(face, fontSizePoints, fontSizePoints, dpi, dpi))
        return false;

    // store the face globally
    _fontRef = face;
    _lineHeight = static_cast<int>((_fontRef->size->metrics.ascender - _fontRef->size->metrics.descender) >> 6);
    // done and good
    return true;
}

FontFreeType::~FontFreeType()
{
    if (_FTInitialized)
    {
        if (_stroker)
        {
            FT_Stroker_Done(_stroker);
        }
        if (_fontRef)
        {
            FT_Done_Face(_fontRef);
        }
    }

    auto iter = s_cacheFontData.find(_fontName);
    if (iter != s_cacheFontData.end())
    {
        iter->second.referenceCount -= 1;
        if (iter->second.referenceCount == 0)
        {
            s_cacheFontData.erase(iter);
        }
    }
}

FontAtlas * FontFreeType::createFontAtlas()
{
    if (_fontAtlas == nullptr) {
        _fontAtlas = new (std::nothrow) FontAtlas(*this);
//        _fontAtlas->setAliasTexParameters(); // uncomment this line to close AntiAlias
        if (_fontAtlas && _usedGlyphs != GlyphCollection::DYNAMIC) {
            std::u32string utf32;
            if (StringUtils::UTF8ToUTF32(getGlyphCollection(), utf32)) {
                _fontAtlas->prepareLetterDefinitions(utf32);
            }
        }
    }

    return _fontAtlas;
}

int * FontFreeType::getHorizontalKerningForTextUTF32(const std::u32string& text, int &outNumLetters) const
{
    if (!_fontRef)
        return nullptr;

    outNumLetters = static_cast<int>(text.length());

    if (!outNumLetters)
        return nullptr;

    int *sizes = new (std::nothrow) int[outNumLetters];
    if (!sizes)
        return nullptr;
    memset(sizes,0,outNumLetters * sizeof(int));

    bool hasKerning = FT_HAS_KERNING( _fontRef ) != 0;
    if (hasKerning)
    {
        for (int c = 1; c < outNumLetters; ++c)
        {
            sizes[c] = getHorizontalKerningForChars(text[c-1], text[c]);
        }
    }

    return sizes;
}

int  FontFreeType::getHorizontalKerningForChars(uint64_t firstChar, uint64_t secondChar) const
{
    // get the ID to the char we need
    int glyphIndex1 = FT_Get_Char_Index(_fontRef, static_cast<FT_ULong>(firstChar));

    if (!glyphIndex1)
        return 0;

    // get the ID to the char we need
    int glyphIndex2 = FT_Get_Char_Index(_fontRef, static_cast<FT_ULong>(secondChar));

    if (!glyphIndex2)
        return 0;

    FT_Vector kerning;

    if (FT_Get_Kerning( _fontRef, glyphIndex1, glyphIndex2,  FT_KERNING_DEFAULT,  &kerning))
        return 0;

    return (static_cast<int>(kerning.x >> 6));
}

int FontFreeType::getFontAscender() const
{
    return (static_cast<int>(_fontRef->size->metrics.ascender >> 6));
}

const char* FontFreeType::getFontFamily() const
{
    if (!_fontRef)
        return nullptr;

    return _fontRef->family_name;
}

unsigned char* FontFreeType::getGlyphBitmap(uint64_t theChar, long &outWidth, long &outHeight, Rect &outRect,int &xAdvance)
{
    bool invalidChar = true;
    unsigned char* ret = nullptr;

    do
    {
        if (_fontRef == nullptr)
            break;

        // chinese char show messy in simsun.ttc with size 12, need FT_LOAD_NO_BITMAP to fix it.
        FT_Int32 flag = FT_LOAD_RENDER | FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_BITMAP;
        if (_distanceFieldEnabled) {
            flag |= FT_LOAD_NO_HINTING;
        }
        if (FT_Load_Char(_fontRef, static_cast<FT_ULong>(theChar), flag)) {
            break;
        }

        auto& metrics = _fontRef->glyph->metrics;
        outRect.origin.x = static_cast<float>(metrics.horiBearingX >> 6);
        outRect.origin.y = static_cast<float>(-(metrics.horiBearingY >> 6));
        outRect.size.width = static_cast<float>((metrics.width >> 6));
        outRect.size.height = static_cast<float>((metrics.height >> 6));

        xAdvance = (static_cast<int>(_fontRef->glyph->metrics.horiAdvance >> 6));

        outWidth  = _fontRef->glyph->bitmap.width;
        outHeight = _fontRef->glyph->bitmap.rows;
        ret = _fontRef->glyph->bitmap.buffer;

        if (_outlineSize > 0 && outWidth > 0 && outHeight > 0)
        {
            auto copyBitmap = new (std::nothrow) unsigned char[outWidth * outHeight];
            memcpy(copyBitmap,ret,outWidth * outHeight * sizeof(unsigned char));

            FT_BBox bbox;
            auto outlineBitmap = getGlyphBitmapWithOutline(theChar,bbox);
            if(outlineBitmap == nullptr)
            {
                ret = nullptr;
                delete [] copyBitmap;
                break;
            }

            int glyphMinX = (int)outRect.origin.x;
            int glyphMaxX = (int)(outRect.origin.x + outWidth);
            int glyphMinY = (int)(-outHeight - outRect.origin.y);
            int glyphMaxY = (int)-outRect.origin.y;

            auto outlineMinX = bbox.xMin >> 6;
            auto outlineMaxX = bbox.xMax >> 6;
            auto outlineMinY = bbox.yMin >> 6;
            auto outlineMaxY = bbox.yMax >> 6;
            auto outlineWidth = outlineMaxX - outlineMinX;
            auto outlineHeight = outlineMaxY - outlineMinY;

            auto blendImageMinX = MIN(outlineMinX, glyphMinX);
            auto blendImageMaxY = MAX(outlineMaxY, glyphMaxY);
            auto blendWidth = MAX(outlineMaxX, glyphMaxX) - blendImageMinX;
            auto blendHeight = blendImageMaxY - MIN(outlineMinY, glyphMinY);

            outRect.origin.x = (float)blendImageMinX;
            outRect.origin.y = -blendImageMaxY + _outlineSize;

            unsigned char *blendImage = nullptr;
            if (blendWidth > 0 && blendHeight > 0)
            {
                FT_Pos index, index2;
                auto imageSize = blendWidth * blendHeight * 2;
                blendImage = new (std::nothrow) unsigned char[imageSize];
                memset(blendImage, 0, imageSize);

                auto px = outlineMinX - blendImageMinX;
                auto py = blendImageMaxY - outlineMaxY;
                for (int x = 0; x < outlineWidth; ++x)
                {
                    for (int y = 0; y < outlineHeight; ++y)
                    {
                        index = px + x + ((py + y) * blendWidth);
                        index2 = x + (y * outlineWidth);
                        blendImage[2 * index] = outlineBitmap[index2];
                    }
                }

                px = glyphMinX - blendImageMinX;
                py = blendImageMaxY - glyphMaxY;
                for (int x = 0; x < outWidth; ++x)
                {
                    for (int y = 0; y < outHeight; ++y)
                    {
                        index = px + x + ((y + py) * blendWidth);
                        index2 = x + (y * outWidth);
                        blendImage[2 * index + 1] = copyBitmap[index2];
                    }
                }
            }

            outRect.size.width  = (float)blendWidth;
            outRect.size.height = (float)blendHeight;
            outWidth  = blendWidth;
            outHeight = blendHeight;

            delete [] outlineBitmap;
            delete [] copyBitmap;
            ret = blendImage;
        }

        invalidChar = false;
    } while (0);

    if (invalidChar)
    {
        outRect.size.width  = 0;
        outRect.size.height = 0;
        xAdvance = 0;

        return nullptr;
    }
    else
    {
       return ret;
    }
}

unsigned char * FontFreeType::getGlyphBitmapWithOutline(uint64_t theChar, FT_BBox &bbox)
{
    unsigned char* ret = nullptr;
    if (FT_Load_Char(_fontRef, static_cast<FT_ULong>(theChar), FT_LOAD_NO_BITMAP) == 0)
    {
        if (_fontRef->glyph->format == FT_GLYPH_FORMAT_OUTLINE)
        {
            FT_Glyph glyph;
            if (FT_Get_Glyph(_fontRef->glyph, &glyph) == 0)
            {
                FT_Glyph_StrokeBorder(&glyph, _stroker, 0, 1);
                if (glyph->format == FT_GLYPH_FORMAT_OUTLINE)
                {
                    FT_Outline *outline = &reinterpret_cast<FT_OutlineGlyph>(glyph)->outline;
                    FT_Glyph_Get_CBox(glyph,FT_GLYPH_BBOX_GRIDFIT,&bbox);
                    long width = (bbox.xMax - bbox.xMin)>>6;
                    long rows = (bbox.yMax - bbox.yMin)>>6;

                    FT_Bitmap bmp;
                    bmp.buffer = new (std::nothrow) unsigned char[width * rows];
                    memset(bmp.buffer, 0, width * rows);
                    bmp.width = (int)width;
                    bmp.rows = (int)rows;
                    bmp.pitch = (int)width;
                    bmp.pixel_mode = FT_PIXEL_MODE_GRAY;
                    bmp.num_grays = 256;

                    FT_Raster_Params params;
                    memset(&params, 0, sizeof (params));
                    params.source = outline;
                    params.target = &bmp;
                    params.flags = FT_RASTER_FLAG_AA;
                    FT_Outline_Translate(outline,-bbox.xMin,-bbox.yMin);
                    FT_Outline_Render(_FTlibrary, outline, &params);

                    ret = bmp.buffer;
                }
                FT_Done_Glyph(glyph);
            }
        }
    }

    return ret;
}

unsigned char * makeDistanceMap( unsigned char *img, long width, long height)
{
    long pixelAmount = (width + 2 * FontFreeType::DistanceMapSpread) * (height + 2 * FontFreeType::DistanceMapSpread);

    short * xdist = (short *)  malloc( pixelAmount * sizeof(short) );
    short * ydist = (short *)  malloc( pixelAmount * sizeof(short) );
    double * gx   = (double *) calloc( pixelAmount, sizeof(double) );
    double * gy      = (double *) calloc( pixelAmount, sizeof(double) );
    double * data    = (double *) calloc( pixelAmount, sizeof(double) );
    double * outside = (double *) calloc( pixelAmount, sizeof(double) );
    double * inside  = (double *) calloc( pixelAmount, sizeof(double) );
    long i,j;

    // Convert img into double (data) rescale image levels between 0 and 1
    long outWidth = width + 2 * FontFreeType::DistanceMapSpread;
    for (i = 0; i < width; ++i)
    {
        for (j = 0; j < height; ++j)
        {
            data[j * outWidth + FontFreeType::DistanceMapSpread + i] = img[j * width + i] / 255.0;
        }
    }

    width += 2 * FontFreeType::DistanceMapSpread;
    height += 2 * FontFreeType::DistanceMapSpread;

    // Transform background (outside contour, in areas of 0's)
    computegradient( data, (int)width, (int)height, gx, gy);
    edtaa3(data, gx, gy, (int)width, (int)height, xdist, ydist, outside);
    for( i=0; i< pixelAmount; i++)
        if( outside[i] < 0.0 )
            outside[i] = 0.0;

    // Transform foreground (inside contour, in areas of 1's)
    for( i=0; i< pixelAmount; i++)
        data[i] = 1 - data[i];
    computegradient( data, (int)width, (int)height, gx, gy);
    edtaa3(data, gx, gy, (int)width, (int)height, xdist, ydist, inside);
    for( i=0; i< pixelAmount; i++)
        if( inside[i] < 0.0 )
            inside[i] = 0.0;

    // The bipolar distance field is now outside-inside
    double dist;
    /* Single channel 8-bit output (bad precision and range, but simple) */
    unsigned char *out = (unsigned char *) malloc( pixelAmount * sizeof(unsigned char) );
    for( i=0; i < pixelAmount; i++)
    {
        dist = outside[i] - inside[i];
        dist = 128.0 - dist*16;
        if( dist < 0 ) dist = 0;
        if( dist > 255 ) dist = 255;
        out[i] = (unsigned char) dist;
    }
    /* Dual channel 16-bit output (more complicated, but good precision and range) */
    /*unsigned char *out = (unsigned char *) malloc( pixelAmount * 3 * sizeof(unsigned char) );
    for( i=0; i< pixelAmount; i++)
    {
        dist = outside[i] - inside[i];
        dist = 128.0 - dist*16;
        if( dist < 0.0 ) dist = 0.0;
        if( dist >= 256.0 ) dist = 255.999;
        // R channel is a copy of the original grayscale image
        out[3*i] = img[i];
        // G channel is fraction
        out[3*i + 1] = (unsigned char) ( 256 - (dist - floor(dist)* 256.0 ));
        // B channel is truncated integer part
        out[3*i + 2] = (unsigned char)dist;
    }*/

    free( xdist );
    free( ydist );
    free( gx );
    free( gy );
    free( data );
    free( outside );
    free( inside );

    return out;
}

void FontFreeType::renderCharAt(unsigned char *dest,int posX, int posY, unsigned char* bitmap,long bitmapWidth,long bitmapHeight)
{
    int iX = posX;
    int iY = posY;

    if (_distanceFieldEnabled) {
        auto distanceMap = makeDistanceMap(bitmap,bitmapWidth,bitmapHeight);
        bitmapWidth += 2 * DistanceMapSpread;
        bitmapHeight += 2 * DistanceMapSpread;
        for (long y = 0; y < bitmapHeight; ++y) {
            long bitmap_y = y * bitmapWidth;
            for (long x = 0; x < bitmapWidth; ++x) {
                /* Dual channel 16-bit output (more complicated, but good precision and range) */
                /*int index = (iX + ( iY * destSize )) * 3;
                int index2 = (bitmap_y + x)*3;
                dest[index] = out[index2];
                dest[index + 1] = out[index2 + 1];
                dest[index + 2] = out[index2 + 2];*/

                //Single channel 8-bit output
                dest[iX + ( iY * FontAtlas::CacheTextureWidth )] = distanceMap[bitmap_y + x];
                iX += 1;
            }
            iX  = posX;
            iY += 1;
        }
        free(distanceMap);
    } else if(_outlineSize > 0) {
        unsigned char tempChar;
        for (long y = 0; y < bitmapHeight; ++y) {
            long bitmap_y = y * bitmapWidth;
            for (int x = 0; x < bitmapWidth; ++x) {
                tempChar = bitmap[(bitmap_y + x) * 2];
                dest[(iX + (iY * FontAtlas::CacheTextureWidth)) * 2] = tempChar;
                tempChar = bitmap[(bitmap_y + x) * 2 + 1];
                dest[(iX + (iY * FontAtlas::CacheTextureWidth)) * 2 + 1] = tempChar;
                iX += 1;
            }
            iX  = posX;
            iY += 1;
        }
        delete [] bitmap;
    } else {
        if (_fontRef->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
            int pitch = _fontRef->glyph->bitmap.pitch;
            for (long y = 0; y < bitmapHeight; ++y) {
                for (int x = 0; x < bitmapWidth; ++x) {
                    unsigned char cTemp = ((bitmap[y * pitch + x / 8] << (x % 8)) & 0x80);
                    // the final pixel
                    dest[(iX + (iY * FontAtlas::CacheTextureWidth))] = cTemp > 0 ? 0xFF : 0;
                    iX += 1;
                }
                iX  = posX;
                iY += 1;
            }
        } else {
            for (long y = 0; y < bitmapHeight; ++y) {
                long bitmap_y = y * bitmapWidth;
                for (int x = 0; x < bitmapWidth; ++x) {
                    unsigned char cTemp = bitmap[bitmap_y + x];
                    // the final pixel
                    dest[(iX + (iY * FontAtlas::CacheTextureWidth))] = cTemp;
                    iX += 1;
                }
                iX  = posX;
                iY += 1;
            }
        }
    }
}

void FontFreeType::setGlyphCollection(GlyphCollection glyphs, const char* customGlyphs /* = nullptr */)
{
    _usedGlyphs = glyphs;
    if (glyphs == GlyphCollection::CUSTOM)
    {
        _customGlyphs = customGlyphs;
    }
}

const char* FontFreeType::getGlyphCollection() const
{
    const char* glyphCollection = nullptr;

    return glyphCollection;
}

void FontFreeType::releaseFont(const std::string &fontName)
{
    auto item = s_cacheFontData.begin();
    while (s_cacheFontData.end() != item)
    {
        if (item->first.find(fontName) != std::string::npos)
            item = s_cacheFontData.erase(item);
        else
            item++;
    }
}

NS_CC_END


try {
		_JobStart();
		fHalftone = new Halftone(GetJobData()->GetSurfaceType(),
			GetJobData()->GetGamma(), GetJobData()->GetInkDensity(),
			GetJobData()->GetDitherType());
		return true;
	}
	catch (TransportException& err) {
		return false;
	}

/*
 * PCL6.cpp
 */

#include "PCL6.h"

#include <memory.h>

#include <Alert.h>
#include <Bitmap.h>
#include <File.h>

#include "DbgMsg.h"
#include "DeltaRowCompression.h"
#include "Halftone.h"
#include "JobData.h"
#include "PackBits.h"
#include "PCL6Cap.h"
#include "PCL6Config.h"
#include "PCL6Rasterizer.h"
#include "PrinterData.h"
#include "UIDriver.h"
#include "ValidRect.h"

// DeltaRowStreamCompressor writes the delta row directly to the
// in the contructor specified stream.
class DeltaRowStreamCompressor : public AbstractDeltaRowCompressor
{
public:
				DeltaRowStreamCompressor(int rowSize, uchar initialSeed,
					PCL6Writer* writer)
				:
				AbstractDeltaRowCompressor(rowSize, initialSeed),
				fWriter(writer)
				{}

protected:
	void		AppendByteToDeltaRow(uchar byte)
				{
					fWriter->Append(byte);
				}

private:
	PCL6Writer*	fWriter;
};

PCL6Driver::PCL6Driver(BMessage* message, PrinterData* printerData,
	const PrinterCap* printerCap)
	:
	GraphicsDriver(message, printerData, printerCap),
	fWriter(NULL),
	fMediaSide(PCL6Writer::kFrontMediaSide),
	fHalftone(NULL)
{
}

void
PCL6Driver::Write(const uint8* data, uint32 size)
{
	WriteSpoolData(data, size);
}

bool
PCL6Driver::StartDocument()
{

}

bool
PCL6Driver::EndDocument(bool)
{
	try {
		if (fHalftone)
			delete fHalftone;
		_JobEnd();
		return true;
	}
	catch (TransportException& err) {
		return false;
	}
}

bool
PCL6Driver::NextBand(BBitmap* bitmap, BPoint* offset)
{
	DBGMSG(("> nextBand\n"));

	try {
		int y = (int)offset->y;

		PCL6Rasterizer* rasterizer;
		if (_UseColorMode()) {
			#if COLOR_DEPTH == 8
				rasterizer = new ColorRGBRasterizer(fHalftone);
			#elif COLOR_DEPTH == 1
				rasterizer = new ColorRasterizer(fHalftone);
			#else
				#error COLOR_DEPTH must be either 1 or 8!
			#endif
		} else
			rasterizer = new MonochromeRasterizer(fHalftone);

		auto_ptr<Rasterizer> _rasterizer(rasterizer);
		bool valid = rasterizer->SetBitmap((int)offset->x, (int)offset->y,
			bitmap, GetPageHeight());

		if (valid) {
			rasterizer->InitializeBuffer();

			// Use compressor to calculate delta row size
			DeltaRowCompressor* deltaRowCompressor = NULL;
			if (_SupportsDeltaRowCompression()) {
				deltaRowCompressor =
					new DeltaRowCompressor(rasterizer->GetOutRowSize(), 0);
				if (deltaRowCompressor->InitCheck() != B_OK) {
					delete deltaRowCompressor;
					return false;
				}
			}
			auto_ptr<DeltaRowCompressor>_deltaRowCompressor(deltaRowCompressor);
			int deltaRowSize = 0;

			// remember position
			int xPage = rasterizer->GetX();
			int yPage = rasterizer->GetY();

			while (rasterizer->HasNextLine()) {
				const uchar* rowBuffer =
					static_cast<const uchar*>(rasterizer->RasterizeNextLine());

				if (deltaRowCompressor != NULL) {
					int size =
						deltaRowCompressor->CalculateSize(rowBuffer, true);
					deltaRowSize += size + 2;
						// two bytes for the row byte count
				}
			}

			y = rasterizer->GetY();

			uchar* outBuffer = rasterizer->GetOutBuffer();
			int outBufferSize = rasterizer->GetOutBufferSize();
			int outRowSize = rasterizer->GetOutRowSize();
			int width = rasterizer->GetWidth();
			int height = rasterizer->GetHeight();
			_WriteBitmap(outBuffer, outBufferSize, outRowSize, xPage, yPage,
				width, height, deltaRowSize);
		}

		if (y >= GetPageHeight()) {
			offset->x = -1.0;
			offset->y = -1.0;
		} else {
			offset->y += bitmap->Bounds().IntegerHeight()+1;
		}

		return true;
	}
	catch (TransportException& err) {
		BAlert* alert = new BAlert("", err.What(), "OK");
		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
		alert->Go();
		return false;
	}
}

void
PCL6Driver::_WriteBitmap(const uchar* buffer, int outSize, int rowSize, int x,
	int y, int width, int height, int deltaRowSize)
{
	// choose the best compression method
	PCL6Writer::Compression compressionMethod = PCL6Writer::kNoCompression;
	int dataSize = outSize;

#if ENABLE_DELTA_ROW_COMPRESSION
	if (_SupportsDeltaRowCompression() && deltaRowSize < dataSize) {
		compressionMethod = PCL6Writer::kDeltaRowCompression;
		dataSize = deltaRowSize;
	}
#endif

#if ENABLE_RLE_COMPRESSION
	if (_SupportsRLECompression()) {
		int rleSize = pack_bits_size(buffer, outSize);
		if (rleSize < dataSize) {
			compressionMethod = PCL6Writer::kRLECompression;
			dataSize = rleSize;
		}
	}
#endif

	// write bitmap
	_Move(x, y);

	_StartRasterGraphics(x, y, width, height, compressionMethod);

	_RasterGraphics(buffer, outSize, dataSize, rowSize, height,
		compressionMethod);

	_EndRasterGraphics();

#if DISPLAY_COMPRESSION_STATISTICS
	fprintf(stderr, "Out Size       %d %2.2f\n", (int)outSize, 100.0);
#if ENABLE_RLE_COMPRESSION
	fprintf(stderr, "RLE Size       %d %2.2f\n", (int)rleSize,
		100.0 * rleSize / outSize);
#endif
#if ENABLE_DELTA_ROW_COMPRESSION
	fprintf(stderr, "Delta Row Size %d %2.2f\n", (int)deltaRowSize,
		100.0 * deltaRowSize / outSize);
#endif
	fprintf(stderr, "Data Size      %d %2.2f\n", (int)dataSize,
		100.0 * dataSize / outSize);
#endif
}

void
PCL6Driver::_JobStart()
{
	// PCL6 begin
	fWriter = new PCL6Writer(this);
	PCL6Writer::ProtocolClass pc =
		(PCL6Writer::ProtocolClass)GetProtocolClass();
	fWriter->PJLHeader(pc, GetJobData()->GetXres(),
		"Copyright (c) 2003 - 2010 Haiku");
	fWriter->BeginSession(GetJobData()->GetXres(), GetJobData()->GetYres(),
		PCL6Writer::kInch, PCL6Writer::kBackChAndErrPage);
	fWriter->OpenDataSource();
	fMediaSide = PCL6Writer::kFrontMediaSide;
}

bool
PCL6Driver::StartPage(int)
{
	PCL6Writer::Orientation orientation = PCL6Writer::kPortrait;
	if (GetJobData()->GetOrientation() == JobData::kLandscape) {
		orientation = PCL6Writer::kLandscape;
	}

	PCL6Writer::MediaSize mediaSize =
		_MediaSize(GetJobData()->GetPaper());
	PCL6Writer::MediaSource mediaSource =
		_MediaSource(GetJobData()->GetPaperSource());
	if (GetJobData()->GetPrintStyle() == JobData::kSimplex) {
		fWriter->BeginPage(orientation, mediaSize, mediaSource);
	} else if (GetJobData()->GetPrintStyle() == JobData::kDuplex) {
		// TODO move duplex binding option to UI
		fWriter->BeginPage(orientation, mediaSize, mediaSource,
			PCL6Writer::kDuplexVerticalBinding, fMediaSide);

		if (fMediaSide == PCL6Writer::kFrontMediaSide)
			fMediaSide = PCL6Writer::kBackMediaSide;
		else
			fMediaSide = PCL6Writer::kFrontMediaSide;
	} else
		return false;

	// PageOrigin from Windows NT printer driver
	int x = 142 * GetJobData()->GetXres() / 600;
	int y = 100 * GetJobData()->GetYres() / 600;
	fWriter->SetPageOrigin(x, y);
	fWriter->SetColorSpace(_UseColorMode() ? PCL6Writer::kRGB
		: PCL6Writer::kGray);
	fWriter->SetPaintTxMode(PCL6Writer::kOpaque);
	fWriter->SetSourceTxMode(PCL6Writer::kOpaque);
	fWriter->SetROP(204);
	return true;
}

void
PCL6Driver::_StartRasterGraphics(int x, int y, int width, int height,
	PCL6Writer::Compression compressionMethod)
{
	PCL6Writer::ColorDepth colorDepth;
	if (_UseColorMode()) {
		#if COLOR_DEPTH == 8
			colorDepth = PCL6Writer::k8Bit;
		#elif COLOR_DEPTH == 1
			colorDepth = PCL6Writer::k1Bit;
		#else
			#error COLOR_DEPTH must be either 1 or 8!
		#endif
	} else
		colorDepth = PCL6Writer::k1Bit;

	fWriter->BeginImage(PCL6Writer::kDirectPixel, colorDepth, width, height,
		width, height);
	fWriter->ReadImage(compressionMethod, 0, height);
}

void
PCL6Driver::_EndRasterGraphics()
{
	fWriter->EndImage();
}

void
PCL6Driver::_RasterGraphics(const uchar* buffer, int bufferSize, int dataSize,
	int rowSize, int height, int compressionMethod)
{
	// write bitmap byte size
	fWriter->EmbeddedDataPrefix32(dataSize);

	// write data
	if (compressionMethod == PCL6Writer::kRLECompression) {
		// use RLE compression
		uchar* outBuffer = new uchar[dataSize];
		pack_bits(outBuffer, buffer, bufferSize);
		fWriter->Append(outBuffer, dataSize);
		delete[] outBuffer;
		return;
	} else if (compressionMethod == PCL6Writer::kDeltaRowCompression) {
		// use delta row compression
		DeltaRowStreamCompressor compressor(rowSize, 0, fWriter);
		if (compressor.InitCheck() != B_OK) {
			return;
		}

		const uint8* row = buffer;
		for (int i = 0; i < height; i ++) {
			// write row byte count
			int32 size = compressor.CalculateSize(row);
			fWriter->Append((uint16)size);

			if (size > 0) {
				// write delta row
				compressor.Compress(row);
			}

			row += rowSize;
		}
	} else {
		// write raw data
		fWriter->Append(buffer, bufferSize);
	}
}

bool
PCL6Driver::EndPage(int)
{
	try {
		fWriter->EndPage(GetJobData()->GetCopies());
		return true;
	}
	catch (TransportException& err) {
		return false;
	}
}

void
PCL6Driver::_JobEnd()
{
	fWriter->CloseDataSource();
	fWriter->EndSession();
	fWriter->PJLFooter();
	fWriter->Flush();
	delete fWriter;
	fWriter = NULL;
}

void
PCL6Driver::_Move(int x, int y)
{
	fWriter->SetCursor(x, y);
}

bool
PCL6Driver::_SupportsRLECompression()
{
	return GetJobData()->GetColor() != JobData::kColorCompressionDisabled;
}

bool
PCL6Driver::_SupportsDeltaRowCompression()
{
	return GetProtocolClass() >= PCL6Writer::kProtocolClass2_1
		&& GetJobData()->GetColor() != JobData::kColorCompressionDisabled;
}

bool
PCL6Driver::_UseColorMode()
{
	return GetJobData()->GetColor() != JobData::kMonochrome;
}

PCL6Writer::MediaSize
PCL6Driver::_MediaSize(JobData::Paper paper)
{
	switch (paper) {
		case JobData::kLetter:
			return PCL6Writer::kLetterPaper;
		case JobData::kLegal:
			return PCL6Writer::kLegalPaper;
		case JobData::kA4:
			return PCL6Writer::kA4Paper;
		case JobData::kExecutive:
			return PCL6Writer::kExecPaper;
		case JobData::kLedger:
			return PCL6Writer::kLedgerPaper;
		case JobData::kA3:
			return PCL6Writer::kA3Paper;
		case JobData::kB5:
			return PCL6Writer::kB5Paper;
		case JobData::kJapanesePostcard:
			return PCL6Writer::kJPostcard;
		case JobData::kA5:
			return PCL6Writer::kA5Paper;
		case JobData::kB4:
			return PCL6Writer::kJB4Paper;
/*
		case : return PCL6Writer::kCOM10Envelope;
		case : return PCL6Writer::kMonarchEnvelope;
		case : return PCL6Writer::kC5Envelope;
		case : return PCL6Writer::kDLEnvelope;
		case : return PCL6Writer::kJB4Paper;
		case : return PCL6Writer::kJB5Paper;
		case : return PCL6Writer::kB5Envelope;
		case : return PCL6Writer::kJPostcard;
		case : return PCL6Writer::kJDoublePostcard;
		case : return PCL6Writer::kA5Paper;
		case : return PCL6Writer::kA6Paper;
		case : return PCL6Writer::kJB6Paper;
		case : return PCL6Writer::kJIS8KPaper;
		case : return PCL6Writer::kJIS16KPaper;
		case : return PCL6Writer::kJISExecPaper;
*/
		default:
			return PCL6Writer::kLegalPaper;
	}
}

PCL6Writer::MediaSource
PCL6Driver::_MediaSource(JobData::PaperSource source)
{
	switch (source) {
		case JobData::kAuto:
			return PCL6Writer::kAutoSelect;
		case JobData::kCassette1:
			return PCL6Writer::kDefaultSource;
		case JobData::kCassette2:
			return PCL6Writer::kEnvelopeTray;
		case JobData::kLower:
			return PCL6Writer::kLowerCassette;
		case JobData::kUpper:
			return PCL6Writer::kUpperCassette;
		case JobData::kMiddle:
			return PCL6Writer::kThirdCassette;
		case JobData::kManual:
			return PCL6Writer::kManualFeed;
		case JobData::kCassette3:
			return PCL6Writer::kMultiPurposeTray;
		default:
			return PCL6Writer::kAutoSelect;
	}
}


RomSettings* BankHeistSettings::clone() const {

    RomSettings* rval = new BankHeistSettings();
    *rval = *this;
    return rval;
}

/* process the latest information from ALE */
void BankHeistSettings::step(const System& system) {

    // update the reward
    reward_t score = getDecimalScore(0xDA, 0xD9, 0xD8, &system);
    m_reward = score - m_score;
    m_score = score;

    // update terminal status
    int death_timer = readRam(&system, 0xCE);
    m_lives = readRam(&system, 0xD5);

    m_terminal = (death_timer == 0x01 && m_lives == 0x00);
}

/* is end of game */
bool BankHeistSettings::isTerminal() const {

    return m_terminal;
};

/* get the most recently observed reward */
reward_t BankHeistSettings::getReward() const {

    return m_reward;
}

/* is an action part of the minimal set? */
bool BankHeistSettings::isMinimal(const Action &a) const {

    switch (a) {
        case PLAYER_A_NOOP:
        case PLAYER_A_FIRE:
        case PLAYER_A_UP:
        case PLAYER_A_RIGHT:
        case PLAYER_A_LEFT:
        case PLAYER_A_DOWN:
        case PLAYER_A_UPRIGHT:
        case PLAYER_A_UPLEFT:
        case PLAYER_A_DOWNRIGHT:
        case PLAYER_A_DOWNLEFT:
        case PLAYER_A_UPFIRE:
        case PLAYER_A_RIGHTFIRE:
        case PLAYER_A_LEFTFIRE:
        case PLAYER_A_DOWNFIRE:
        case PLAYER_A_UPRIGHTFIRE:
        case PLAYER_A_UPLEFTFIRE:
        case PLAYER_A_DOWNRIGHTFIRE:
        case PLAYER_A_DOWNLEFTFIRE:
            return true;
        default:
            return false;
    }
}

/* reset the state of the game */
void BankHeistSettings::reset() {

    m_reward   = 0;
    m_score    = 0;
    m_terminal = false;
    m_lives    = 5;
}

/* saves the state of the rom settings */
void BankHeistSettings::saveState(Serializer & ser) {
  ser.putInt(m_reward);
  ser.putInt(m_score);
  ser.putBool(m_terminal);
  ser.putInt(m_lives);
}

// loads the state of the rom settings
void BankHeistSettings::loadState(Deserializer & ser) {
  m_reward = ser.getInt();
  m_score = ser.getInt();
  m_terminal = ser.getBool();
  m_lives = ser.getInt();
}


void RawDDPSettings::on_bilinear_2_toggled(bool checked)
{
	if (checked)
	{
		workspace->setValue("FitsDDP/Interpolation", "Bilinear");
		workspace->setValue("RawDDP/SuperPixels", false);
	}
}

// RawDDPSettings.cpp : implementation file

#include <algorithm>
using std::min;
using std::max;

#define _WIN32_WINNT _WIN32_WINNT_WIN7
#include <afx.h>
#include <afxcmn.h>
#include <afxcview.h>
#include <afxwin.h>

#include <ZExcept.h>
#include <Ztrace.h>

#include <QDialog>
#include <QDialogButtonBox>
#include <QDoubleValidator>
#include <QSettings>
#include <QShowEvent>
#include <QString>

#include "RawDDPSettings.h"
#include "ui/ui_RawDDPSettings.h"

extern bool		g_bShowRefStars;

#include "DSSCommon.h"
#include "commonresource.h"
#include "BitmapExt.h"
#include "DeepStackerDlg.h"
#include "ProgressDlg.h"
#include "RegisterEngine.h"
#include "StackingDlg.h"
#include "StackSettings.h"

#include "Workspace.h"

class CDSLR
{
public:
	QString				m_strName;
	CFATYPE				m_CFAType;

private:
	void	CopyFrom(const CDSLR & cd) noexcept
	{
		m_strName = cd.m_strName;
		m_CFAType = cd.m_CFAType;
	};

public:
	CDSLR(QString name, CFATYPE CFAType) noexcept :
		m_strName(name),
		m_CFAType(CFAType)
	{
	};

	CDSLR(const CDSLR & cd)
	{
		CopyFrom(cd);
	};

	virtual ~CDSLR()
	{
	};

	const CDSLR & operator = (const CDSLR & cd)
	{
		CopyFrom(cd);
		return *this;
	};
};

void	RawDDPSettings::fillDSLRList(std::vector<CDSLR> & vDSLRs)
{
	ZFUNCTRACE_RUNTIME();
	// Fill the four generic DSLR bayer pattern

	vDSLRs.emplace_back(tr("Generic RGGB"), CFATYPE_RGGB);
	vDSLRs.emplace_back(tr("Generic BGGR"), CFATYPE_BGGR);
	vDSLRs.emplace_back(tr("Generic GBRG"), CFATYPE_GBRG);
	vDSLRs.emplace_back(tr("Generic GRBG"), CFATYPE_GRBG);

	QString string(tr("Generic"));
	vDSLRs.emplace_back(string + " CYMG (01)", CFATYPE_CYGMCYMG);
	vDSLRs.emplace_back(string + " CYMG (02)", CFATYPE_GMCYMGCY);
	vDSLRs.emplace_back(string + " CYMG (03)", CFATYPE_CYMGCYGM);
	vDSLRs.emplace_back(string + " CYMG (04)", CFATYPE_MGCYGMCY);
	vDSLRs.emplace_back(string + " CYMG (05)", CFATYPE_GMYCGMCY);
	vDSLRs.emplace_back(string + " CYMG (06)", CFATYPE_YCGMCYGM);
	vDSLRs.emplace_back(string + " CYMG (07)", CFATYPE_GMCYGMYC);
	vDSLRs.emplace_back(string + " CYMG (08)", CFATYPE_CYGMYCGM);
	vDSLRs.emplace_back(string + " CYMG (09)", CFATYPE_YCGMYCMG);
	vDSLRs.emplace_back(string + " CYMG (10)", CFATYPE_GMYCMGYC);
	vDSLRs.emplace_back(string + " CYMG (11)", CFATYPE_YCMGYCGM);
	vDSLRs.emplace_back(string + " CYMG (12)", CFATYPE_MGYCGMYC);
	vDSLRs.emplace_back(string + " CYMG (13)", CFATYPE_MGYCMGCY);
	vDSLRs.emplace_back(string + " CYMG (14)", CFATYPE_YCMGCYMG);
	vDSLRs.emplace_back(string + " CYMG (15)", CFATYPE_MGCYMGYC);
	vDSLRs.emplace_back(string + " CYMG (16)", CFATYPE_CYMGYCMG);

	vDSLRs.emplace_back("AVT F-145C", CFATYPE_RGGB);
	vDSLRs.emplace_back("AVT F-201C", CFATYPE_RGGB);
	vDSLRs.emplace_back("AVT F-510C", CFATYPE_RGGB);
	vDSLRs.emplace_back("AVT F-810C", CFATYPE_RGGB);
	vDSLRs.emplace_back("CASIO EX-P505", CFATYPE_RGGB);
	vDSLRs.emplace_back("CASIO EX-P600", CFATYPE_RGGB);
	vDSLRs.emplace_back("CASIO EX-P700", CFATYPE_RGGB);
	vDSLRs.emplace_back("CASIO EX-S100", CFATYPE_RGGB);
	vDSLRs.emplace_back("CASIO EX-Z50", CFATYPE_RGGB);
	vDSLRs.emplace_back("CASIO EX-Z55", CFATYPE_RGGB);
	vDSLRs.emplace_back("CASIO QV-2000UX", CFATYPE_RGGB);
	vDSLRs.emplace_back("CASIO QV-3*00EX", CFATYPE_RGGB);
	vDSLRs.emplace_back("CASIO QV-4000", CFATYPE_RGGB);
	vDSLRs.emplace_back("CASIO QV-5700", CFATYPE_RGGB);
	vDSLRs.emplace_back("CASIO QV-R51", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon EOS 10D", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon EOS 20D", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon EOS 300D DIGITAL", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon EOS 30D", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon EOS 350D DIGITAL", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon EOS 40D", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon EOS 400D DIGITAL", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon EOS 50D", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon EOS 5D", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon EOS 5D Mk II", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon EOS D2000C", CFATYPE_GRBG);
	vDSLRs.emplace_back("Canon EOS D30", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon EOS D60", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon EOS DIGITAL REBEL", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon EOS DIGITAL REBEL XT", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon EOS DIGITAL REBEL XTi", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon EOS Kiss Digital", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon EOS Kiss Digital N", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon EOS-1D", CFATYPE_GRBG);
	vDSLRs.emplace_back("Canon EOS-1D Mark II", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon EOS-1D Mark II N", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon EOS-1DS", CFATYPE_GRBG);
	vDSLRs.emplace_back("Canon EOS-1Ds Mark II", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon PowerShot G2", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon PowerShot G3", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon PowerShot G5", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon PowerShot G6", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon PowerShot Pro1", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon PowerShot S30", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon PowerShot S40", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon PowerShot S45", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon PowerShot S50", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon PowerShot S60", CFATYPE_RGGB);
	vDSLRs.emplace_back("Canon PowerShot S70", CFATYPE_RGGB);
	vDSLRs.emplace_back("Contax N Digital", CFATYPE_GRBG);
	vDSLRs.emplace_back("Creative PC-CAM 600", CFATYPE_GBRG);
	vDSLRs.emplace_back("EPSON R-D1", CFATYPE_RGGB);
	vDSLRs.emplace_back("EPSON R-D1s", CFATYPE_RGGB);
	vDSLRs.emplace_back("FUJIFILM FinePix E550", CFATYPE_GBRG);
	vDSLRs.emplace_back("FUJIFILM FinePix E900", CFATYPE_GBRG);
	vDSLRs.emplace_back("FUJIFILM FinePix F700", CFATYPE_GBRG);
	vDSLRs.emplace_back("FUJIFILM FinePix F710", CFATYPE_GBRG);
	vDSLRs.emplace_back("FUJIFILM FinePix S20Pro", CFATYPE_GBRG);
	vDSLRs.emplace_back("FUJIFILM FinePix S2Pro", CFATYPE_GBRG);
	vDSLRs.emplace_back("FUJIFILM FinePix S3Pro", CFATYPE_GBRG);
	vDSLRs.emplace_back("FUJIFILM FinePix S5000", CFATYPE_GBRG);
	vDSLRs.emplace_back("FUJIFILM FinePix S5100", CFATYPE_RGGB);
	vDSLRs.emplace_back("FUJIFILM FinePix S5200", CFATYPE_GBRG);
	vDSLRs.emplace_back("FUJIFILM FinePix S5600", CFATYPE_GBRG);
	vDSLRs.emplace_back("FUJIFILM FinePix S7000", CFATYPE_GBRG);
	vDSLRs.emplace_back("FUJIFILM FinePix S9500", CFATYPE_GBRG);
	vDSLRs.emplace_back("Foculus 531C", CFATYPE_GBRG);
	vDSLRs.emplace_back("ISG 2020x1520", CFATYPE_GRBG);
	vDSLRs.emplace_back("Imacon Ixpress 16-Mp", CFATYPE_GRBG);
	vDSLRs.emplace_back("Imacon Ixpress 22-Mp", CFATYPE_GRBG);
	vDSLRs.emplace_back("Imacon Ixpress 22-Mp", CFATYPE_RGGB);
	vDSLRs.emplace_back("Imacon Ixpress 39-Mp", CFATYPE_RGGB);
	vDSLRs.emplace_back("KODAK DCS420", CFATYPE_GRBG);
	vDSLRs.emplace_back("KODAK DCS460", CFATYPE_GRBG);
	vDSLRs.emplace_back("KODAK EOSDCS1", CFATYPE_GRBG);
	vDSLRs.emplace_back("KODAK EOSDCS3B", CFATYPE_GRBG);
	vDSLRs.emplace_back("KODAK NC2000F", CFATYPE_GRBG);
	vDSLRs.emplace_back("KODAK NC2000G", CFATYPE_GRBG);
	vDSLRs.emplace_back("KODAK P850 ZOOM", CFATYPE_BGGR);
	vDSLRs.emplace_back("KODAK P880 ZOOM", CFATYPE_BGGR);
	vDSLRs.emplace_back("Kodak DC120", CFATYPE_GRBG);
	vDSLRs.emplace_back("Kodak DC40", CFATYPE_GRBG);
	vDSLRs.emplace_back("Kodak DC50", CFATYPE_GRBG);
	vDSLRs.emplace_back("Kodak DCS Pro 14N", CFATYPE_GRBG);
	vDSLRs.emplace_back("Kodak DCS Pro 14n", CFATYPE_GRBG);
	vDSLRs.emplace_back("Kodak DCS Pro 14nx", CFATYPE_GRBG);
	vDSLRs.emplace_back("Kodak DCS Pro SLR/c", CFATYPE_GRBG);
	vDSLRs.emplace_back("Kodak DCS Pro SLR/n", CFATYPE_GRBG);
	vDSLRs.emplace_back("Kodak DCS315C", CFATYPE_GRBG);
	vDSLRs.emplace_back("Kodak DCS330C", CFATYPE_GRBG);
	vDSLRs.emplace_back("Kodak DCS520C", CFATYPE_GRBG);
	vDSLRs.emplace_back("Kodak DCS560C", CFATYPE_GRBG);
	vDSLRs.emplace_back("Kodak DCS620C", CFATYPE_GRBG);
	vDSLRs.emplace_back("Kodak DCS620X", CFATYPE_GRBG);
	vDSLRs.emplace_back("Kodak DCS660C", CFATYPE_GRBG);
	vDSLRs.emplace_back("Kodak DCS720X", CFATYPE_GRBG);
	vDSLRs.emplace_back("Kodak DCS760C", CFATYPE_GRBG);
	vDSLRs.emplace_back("Kodak KAI-0340", CFATYPE_RGGB);
	vDSLRs.emplace_back("Kodak ProBack", CFATYPE_GRBG);
	vDSLRs.emplace_back("Kodak ProBack645", CFATYPE_GRBG);
	vDSLRs.emplace_back("Konica KD-400Z", CFATYPE_RGGB);
	vDSLRs.emplace_back("Konica KD-510Z", CFATYPE_GRBG);
	vDSLRs.emplace_back("LEICA D-LUX2", CFATYPE_RGGB);
	vDSLRs.emplace_back("LEICA DIGILUX 2", CFATYPE_RGGB);
	vDSLRs.emplace_back("Leaf Aptus 17", CFATYPE_RGGB);
	vDSLRs.emplace_back("Leaf Aptus 22", CFATYPE_GRBG);
	vDSLRs.emplace_back("Leaf Aptus 65", CFATYPE_GBRG);
	vDSLRs.emplace_back("Leaf Aptus 75", CFATYPE_GBRG);
	vDSLRs.emplace_back("Leaf Aptus 75", CFATYPE_RGGB);
	vDSLRs.emplace_back("Leaf CMost", CFATYPE_GBRG);
	vDSLRs.emplace_back("Leaf Cantare", CFATYPE_GRBG);
	vDSLRs.emplace_back("Leaf Valeo 11", CFATYPE_BGGR);
	vDSLRs.emplace_back("Leaf Valeo 11", CFATYPE_GBRG);
	vDSLRs.emplace_back("Leaf Valeo 11", CFATYPE_GRBG);
	vDSLRs.emplace_back("Leaf Valeo 11", CFATYPE_RGGB);
	vDSLRs.emplace_back("Leaf Valeo 17", CFATYPE_GRBG);
	vDSLRs.emplace_back("Leaf Valeo 22", CFATYPE_BGGR);
	vDSLRs.emplace_back("Leaf Valeo 22", CFATYPE_GRBG);
	vDSLRs.emplace_back("Leaf Valeo 22", CFATYPE_RGGB);
	vDSLRs.emplace_back("Leaf Valeo 6", CFATYPE_BGGR);
	vDSLRs.emplace_back("Leaf Valeo 6", CFATYPE_GBRG);
	vDSLRs.emplace_back("Leica Camera AG R8 - Digital Back DMR DNG", CFATYPE_RGGB);
	vDSLRs.emplace_back("Logitech Fotoman Pixtura", CFATYPE_GRBG);
	vDSLRs.emplace_back("Meade DSI Color", CFATYPE_CYGMCYMG);
	vDSLRs.emplace_back("Micron 2010", CFATYPE_BGGR);
	vDSLRs.emplace_back("MINOLTA ALPHA-7 DIGITAL", CFATYPE_RGGB);
	vDSLRs.emplace_back("MINOLTA DYNAX 5D", CFATYPE_RGGB);
	vDSLRs.emplace_back("MINOLTA DYNAX 7D", CFATYPE_RGGB);
	vDSLRs.emplace_back("MINOLTA DiMAGE A200", CFATYPE_GBRG);
	vDSLRs.emplace_back("MINOLTA DiMAGE G400", CFATYPE_GRBG);
	vDSLRs.emplace_back("MINOLTA DiMAGE G530", CFATYPE_GRBG);
	vDSLRs.emplace_back("Minolta DiMAGE 5", CFATYPE_RGGB);
	vDSLRs.emplace_back("Minolta DiMAGE 7", CFATYPE_RGGB);
	vDSLRs.emplace_back("Minolta DiMAGE 7Hi", CFATYPE_RGGB);
	vDSLRs.emplace_back("Minolta DiMAGE A1", CFATYPE_RGGB);
	vDSLRs.emplace_back("Minolta DiMAGE A2", CFATYPE_RGGB);
	vDSLRs.emplace_back("Minolta DiMAGE G500", CFATYPE_GRBG);
	vDSLRs.emplace_back("Minolta DiMAGE G600", CFATYPE_GRBG);
	vDSLRs.emplace_back("Minolta DiMAGE Z2 DNG", CFATYPE_BGGR);
	vDSLRs.emplace_back("Minolta DiMAGE Z2", CFATYPE_BGGR);
	vDSLRs.emplace_back("Minolta RD175", CFATYPE_GRBG);
	vDSLRs.emplace_back("NIKON D1", CFATYPE_BGGR);
	vDSLRs.emplace_back("NIKON D100", CFATYPE_GRBG);
	vDSLRs.emplace_back("NIKON D1H", CFATYPE_BGGR);
	vDSLRs.emplace_back("NIKON D1X", CFATYPE_BGGR);
	vDSLRs.emplace_back("NIKON D200", CFATYPE_RGGB);
	vDSLRs.emplace_back("NIKON D2H", CFATYPE_GBRG);
	vDSLRs.emplace_back("NIKON D2Hs", CFATYPE_GBRG);
	vDSLRs.emplace_back("NIKON D2X", CFATYPE_RGGB);
	vDSLRs.emplace_back("NIKON D2Xs", CFATYPE_RGGB);
	vDSLRs.emplace_back("NIKON D50", CFATYPE_BGGR);
	vDSLRs.emplace_back("NIKON D70", CFATYPE_BGGR);
	vDSLRs.emplace_back("NIKON D70s", CFATYPE_BGGR);
	vDSLRs.emplace_back("NIKON D80", CFATYPE_GBRG);
	vDSLRs.emplace_back("NIKON E2100", CFATYPE_RGGB);
	vDSLRs.emplace_back("NIKON E3700", CFATYPE_RGGB);
	vDSLRs.emplace_back("NIKON E4300", CFATYPE_BGGR);
	vDSLRs.emplace_back("NIKON E5400", CFATYPE_BGGR);
	vDSLRs.emplace_back("NIKON E8400", CFATYPE_BGGR);
	vDSLRs.emplace_back("NIKON E8700", CFATYPE_BGGR);
	vDSLRs.emplace_back("NIKON E8800", CFATYPE_BGGR);
	vDSLRs.emplace_back("Nucore BMQ", CFATYPE_BGGR);
	vDSLRs.emplace_back("Nucore BMQ", CFATYPE_GRBG);
	vDSLRs.emplace_back("Nucore RAW", CFATYPE_GRBG);
	vDSLRs.emplace_back("OLYMPUS C5050Z", CFATYPE_BGGR);
	vDSLRs.emplace_back("OLYMPUS C5060WZ", CFATYPE_RGGB);
	vDSLRs.emplace_back("OLYMPUS C7070WZ", CFATYPE_RGGB);
	vDSLRs.emplace_back("OLYMPUS C70Z,C7000Z", CFATYPE_RGGB);
	vDSLRs.emplace_back("OLYMPUS C740UZ", CFATYPE_RGGB);
	vDSLRs.emplace_back("OLYMPUS C770UZ", CFATYPE_BGGR);
	vDSLRs.emplace_back("OLYMPUS C8080WZ", CFATYPE_BGGR);
	vDSLRs.emplace_back("OLYMPUS E-1", CFATYPE_GRBG);
	vDSLRs.emplace_back("OLYMPUS E-10", CFATYPE_RGGB);
	vDSLRs.emplace_back("OLYMPUS E-20,E-20N,E-20P", CFATYPE_RGGB);
	vDSLRs.emplace_back("OLYMPUS E-300", CFATYPE_RGGB);
	vDSLRs.emplace_back("OLYMPUS E-330", CFATYPE_RGGB);
	vDSLRs.emplace_back("OLYMPUS E-400", CFATYPE_GRBG);
	vDSLRs.emplace_back("OLYMPUS E-500", CFATYPE_RGGB);
	vDSLRs.emplace_back("OLYMPUS SP350", CFATYPE_RGGB);
	vDSLRs.emplace_back("OLYMPUS SP500UZ", CFATYPE_GBRG);
	vDSLRs.emplace_back("OLYMPUS SP500UZ", CFATYPE_GBRG);
	vDSLRs.emplace_back("OLYMPUS SP500UZ", CFATYPE_GBRG);
	vDSLRs.emplace_back("OLYMPUS SP500UZ", CFATYPE_GBRG);
	vDSLRs.emplace_back("Orion StarShoot Deep-Space", CFATYPE_CYGMCYMG);
	vDSLRs.emplace_back("PENTAX *ist D", CFATYPE_RGGB);
	vDSLRs.emplace_back("PENTAX *ist DL", CFATYPE_RGGB);
	vDSLRs.emplace_back("PENTAX *ist DS", CFATYPE_RGGB);
	vDSLRs.emplace_back("PENTAX K100D DNG", CFATYPE_RGGB);
	vDSLRs.emplace_back("PENTAX K100D", CFATYPE_RGGB);
	vDSLRs.emplace_back("PENTAX K10D", CFATYPE_RGGB);
	vDSLRs.emplace_back("PENTAX Optio 33WR", CFATYPE_BGGR);
	vDSLRs.emplace_back("PENTAX Optio S", CFATYPE_RGGB);
	vDSLRs.emplace_back("PENTAX Optio S4", CFATYPE_RGGB);
	vDSLRs.emplace_back("PENTAX Optio S4i", CFATYPE_RGGB);
	vDSLRs.emplace_back("Panasonic DMC-FZ30", CFATYPE_RGGB);
	vDSLRs.emplace_back("Panasonic DMC-FZ50", CFATYPE_GBRG);
	vDSLRs.emplace_back("Panasonic DMC-FZ50", CFATYPE_RGGB);
	vDSLRs.emplace_back("Panasonic DMC-LC1", CFATYPE_RGGB);
	vDSLRs.emplace_back("Panasonic DMC-LX1", CFATYPE_RGGB);
	vDSLRs.emplace_back("Panasonic DMC-LX2", CFATYPE_GBRG);
	vDSLRs.emplace_back("Panasonic DMC-LX2", CFATYPE_RGGB);
	vDSLRs.emplace_back("Phase One H 10", CFATYPE_RGGB);
	vDSLRs.emplace_back("Phase One H 20", CFATYPE_RGGB);
	vDSLRs.emplace_back("Phase One H 25", CFATYPE_RGGB);
	vDSLRs.emplace_back("Phase One LightPhase", CFATYPE_RGGB);
	vDSLRs.emplace_back("Phase One P 20", CFATYPE_RGGB);
	vDSLRs.emplace_back("Phase One P 25", CFATYPE_RGGB);
	vDSLRs.emplace_back("Phase One P 30", CFATYPE_RGGB);
	vDSLRs.emplace_back("Phase One P 45", CFATYPE_RGGB);
	vDSLRs.emplace_back("Pixelink A782", CFATYPE_GRBG);
	vDSLRs.emplace_back("QHY8 CCD Camera", CFATYPE_RGGB);
	vDSLRs.emplace_back("RICOH GR Digital DNG", CFATYPE_RGGB);
	vDSLRs.emplace_back("Rollei d530flex", CFATYPE_BGGR);
	vDSLRs.emplace_back("RoverShot 3320AF", CFATYPE_GRBG);
	vDSLRs.emplace_back("SAMSUNG GX-1S", CFATYPE_RGGB);
	vDSLRs.emplace_back("SAMSUNG Pro 815 DNG", CFATYPE_GBRG);
	vDSLRs.emplace_back("SBIG ST-2000XCM", CFATYPE_BGGR);
	vDSLRs.emplace_back("SBIG ST-2001XCM", CFATYPE_BGGR);
	vDSLRs.emplace_back("SI0 SI1 DNG", CFATYPE_BGGR);
	vDSLRs.emplace_back("SI0 SI1 DNG", CFATYPE_GRBG);
	vDSLRs.emplace_back("SI0 SI1 DNG", CFATYPE_RGGB);
	vDSLRs.emplace_back("SMaL v6 1288x1024", CFATYPE_RGGB);
	vDSLRs.emplace_back("SMaL v6 643x482", CFATYPE_RGGB);
	vDSLRs.emplace_back("SMaL v9 1284x864", CFATYPE_RGGB);
	vDSLRs.emplace_back("SMaL v9 1288x864", CFATYPE_RGGB);
	vDSLRs.emplace_back("SMaL v9 1632x1220", CFATYPE_RGGB);
	vDSLRs.emplace_back("SMaL v9 2032x1520", CFATYPE_RGGB);
	vDSLRs.emplace_back("SMaL v9 816x612", CFATYPE_RGGB);
	vDSLRs.emplace_back("SONY DSC-R1", CFATYPE_GRBG);
	vDSLRs.emplace_back("SONY DSC-V3", CFATYPE_RGGB);
	vDSLRs.emplace_back("SONY DSLR-A100", CFATYPE_RGGB);
	vDSLRs.emplace_back("ST Micro STV680 VGA", CFATYPE_BGGR);
	vDSLRs.emplace_back("Sarnoff 4096x5440", CFATYPE_RGGB);
	vDSLRs.emplace_back("Sinar 3072x2048", CFATYPE_GRBG);
	vDSLRs.emplace_back("Sinar 4080x4080", CFATYPE_GRBG);
	vDSLRs.emplace_back("Sinar 4080x5440", CFATYPE_GRBG);
	vDSLRs.emplace_back("Sinar Sinarback 54 M, Hasselblad", CFATYPE_GRBG);
	vDSLRs.emplace_back("Sony XCD-SX910CR", CFATYPE_GBRG);
};

RawDDPSettings::RawDDPSettings(QWidget *parent) :
	QDialog(parent),
	ui(new Ui::RawDDPSettings),
	workspace(new CWorkspace()),
	initialised(false)
{
	ui->setupUi(this);

	// Create a validator for the scale values
	scaleValidator = new QDoubleValidator(0, 5, 4, this);
	// Apply it to the RAW Files tab
	ui->brightness->setValidator(scaleValidator);
	ui->redScale->setValidator(scaleValidator);
	ui->blueScale->setValidator(scaleValidator);
	// Apply it to the FITS Files tab
	ui->brightness_2->setValidator(scaleValidator);
	ui->redScale_2->setValidator(scaleValidator);
	ui->blueScale_2->setValidator(scaleValidator);

	// forceUnsigned is no longer used
	ui->forceUnsigned->setEnabled(false);
	ui->forceUnsigned->setVisible(false);

	// Preserve the current settings
	workspace->Push();
}

RawDDPSettings::~RawDDPSettings()
{
	delete ui;
}

void RawDDPSettings::showEvent(QShowEvent * event)
{
	if (!event->spontaneous())
	{
		if (!initialised)
		{
			initialised = true;
			onInitDialog();
		}
	}
	return Inherited::showEvent(event);
}

void RawDDPSettings::onInitDialog()
{
	QSettings settings;
	double value = 0.0;
	QString string;

	// Restore Window position etc..
	QByteArray ba = settings.value("Dialogs/RawDDPSettings/geometry").toByteArray();
	if (!ba.isEmpty())
	{
		restoreGeometry(ba);
	}
	else
	{
		// Get NATIVE windows ultimate parent
		HWND hParent = GetDeepStackerDlg(nullptr)->m_hWnd;
		RECT r;
		GetWindowRect(hParent, &r);

		QSize size = this->size();

		const int top = ((r.top + (r.bottom - r.top) / 2) - (size.height() / 2));
		const int left = ((r.left + (r.right - r.left) / 2) - (size.width() / 2));
		move(left, top);
	}

	// First initialise the controls on the Raw Files tab
	value = workspace->value("RawDDP/Brightness", "1.0").toDouble();
	ui->brightness->setText(QString("%L1").arg(value, 0, 'f', 4));

	value = workspace->value("RawDDP/RedScale", "1.0").toDouble();
	ui->redScale->setText(QString("%L1").arg(value, 0, 'f', 4));

	value = workspace->value("RawDDP/BlueScale", "1.0").toDouble();
	ui->blueScale->setText(QString("%L1").arg(value, 0, 'f', 4));

	ui->noWB->setChecked(workspace->value("RawDDP/NoWB", false).toBool());

	ui->cameraWB->setChecked(workspace->value("RawDDP/CameraWB", false).toBool());

	bool isSuperPixels;
	bool isRawBayer;

	isSuperPixels = workspace->value("RawDDP/SuperPixels", false).toBool();
	isRawBayer = workspace->value("RawDDP/RawBayer", false).toBool();

	string = workspace->value("RawDDP/Interpolation", "Bilinear").toString();

	if (isRawBayer)
		ui->rawBayer->setChecked(true);
	else if (isSuperPixels)
		ui->superPixels->setChecked(true);
	else if (string == "Bilinear")
		ui->bilinear->setChecked(true);
	else if (string == "AHD")
		ui->AHD->setChecked(true);

	ui->blackPointToZero->setChecked(workspace->value("RawDDP/BlackPointTo0", false).toBool());

	// Now populate the FITS Files tab controls
	ui->isFITSRaw->setChecked(workspace->value("FitsDDP/FITSisRAW", false).toBool());

	value = workspace->value("FitsDDP/Brightness", "1.0").toDouble();
	ui->brightness_2->setText(QString("%L1").arg(value, 0, 'f', 4));

	value = workspace->value("FitsDDP/RedScale", "1.0").toDouble();
	ui->redScale_2->setText(QString("%L1").arg(value, 0, 'f', 4));

	value = workspace->value("FitsDDP/BlueScale", "1.0").toDouble();
	ui->blueScale_2->setText(QString("%L1").arg(value, 0, 'f', 4));

	string = workspace->value("FitsDDP/Interpolation", "Bilinear").toString();

	if (string == "Bilinear")
		ui->bilinear_2->setChecked(true);
	else if (string == "RawBayer")
		ui->rawBayer_2->setChecked(true);
	else if (string == "AHD")
		ui->AHD_2->setChecked(true);
	else
		ui->superPixels_2->setChecked(true);

	fillDSLRList(vector_DSLRs);

	for (std::vector<CDSLR>::size_type i = 0; i < vector_DSLRs.size(); i++)
	{
		ui->DSLRs->addItem(vector_DSLRs[i].m_strName, static_cast<uint>(vector_DSLRs[i].m_CFAType));
	};

	string = workspace->value("FitsDDP/DSLR", "").toString();
	if (string.isEmpty())
		ui->DSLRs->setCurrentIndex(0);
	else
	{
		const int row = ui->DSLRs->findText(string);
		if (row != -1)
		{
			ui->DSLRs->setCurrentIndex(row);
		}
	}

	updateBayerPattern().updateControls();

	ui->tabWidget->setCurrentIndex(0);

}

RawDDPSettings & RawDDPSettings::updateBayerPattern()
{
	ZFUNCTRACE_RUNTIME();

	size_t index = ui->DSLRs->currentIndex();
	if (-1 != index)
	{
		CFATYPE type = static_cast<CFATYPE>(ui->DSLRs->currentData().toUInt());
		switch (type)
		{
		case CFATYPE_BGGR:
			if (bggrPix.isNull())
			{
				bggrPix.load(":/rawddpsettings/Pattern_BGGR.bmp");
			}
			ui->bayerPattern->setPixmap(bggrPix);
			break;
		case CFATYPE_GBRG:
			if (gbrgPix.isNull())
			{
				gbrgPix.load(":/rawddpsettings/Pattern_GBRG.bmp");
			}
			ui->bayerPattern->setPixmap(gbrgPix);
			break;
		case CFATYPE_GRBG:
			if (grbgPix.isNull())
			{
				grbgPix.load(":/rawddpsettings/Pattern_GRBG.bmp");
			}
			ui->bayerPattern->setPixmap(grbgPix);
			break;
		case CFATYPE_RGGB:
			if (rggbPix.isNull())
			{
				rggbPix.load(":/rawddpsettings/Pattern_RGGB.bmp");
			}
			ui->bayerPattern->setPixmap(rggbPix);
			break;
		case CFATYPE_CYGMCYMG:
			if (cygmcymgPix.isNull())
			{
				cygmcymgPix.load(":/rawddpsettings/Pattern_CYGMCYMG.bmp");
			}
			ui->bayerPattern->setPixmap(cygmcymgPix);
			break;
		default:
			ui->bayerPattern->clear();
		};
	}
	else ui->bayerPattern->clear();

	return *this;
};

RawDDPSettings & RawDDPSettings::updateControls()
{
	const bool isFITSRaw = ui->isFITSRaw->isChecked();

	const size_t index = ui->DSLRs->currentIndex();
	if (isFITSRaw && index >= 0 && index < vector_DSLRs.size())
	{
		bool		bCYMG;

		bCYMG = ::IsCYMGType(vector_DSLRs[index].m_CFAType);

		ui->colourAdjustment->setEnabled(!bCYMG);

		ui->rawBayer_2->setEnabled(!bCYMG);
		ui->superPixels_2->setEnabled(!bCYMG);
		ui->AHD_2->setEnabled(!bCYMG);

		ui->bilinear_2_desc->setEnabled(!bCYMG);
		ui->superPixels_2_desc->setEnabled(!bCYMG);
		if (bCYMG)
		{
			ui->rawBayer_2->setChecked(false);
			ui->superPixels_2->setChecked(false);
			ui->AHD_2->setChecked(false);
			ui->bilinear_2->setChecked(true);
		};
	}
	else
	{
		ui->rawBayer_2->setEnabled(true);
		ui->superPixels_2->setEnabled(true);
		ui->AHD_2->setEnabled(true);
		ui->bilinear_2_desc->setEnabled(true);
		ui->superPixels_2_desc->setEnabled(true);
	};

	ui->textDSLRs->setEnabled(isFITSRaw);
	ui->DSLRs->setEnabled(isFITSRaw);
	ui->colourAdjustment->setEnabled(true);
	ui->bayerGroup->setEnabled(isFITSRaw);

	return *this;
}

// Slots for RAW Files tab
void RawDDPSettings::on_brightness_textEdited(const QString& string)
{
	bool OK = false;
	const double value{ string.toDouble(&OK) };
	ZASSERTSTATE(OK);
	workspace->setValue("RawDDP/Brightness", value);
}

void RawDDPSettings::on_redScale_textEdited(const QString& string)
{
	bool OK = false;
	const double value{ string.toDouble(&OK) };
	ZASSERTSTATE(OK);
	workspace->setValue("RawDDP/RedScale", value);
}

void RawDDPSettings::on_blueScale_textEdited(const QString& string)
{
	bool OK = false;
	const double value{ string.toDouble(&OK) };
	ZASSERTSTATE(OK);
	workspace->setValue("RawDDP/BlueScale", value);
}

void RawDDPSettings::on_noWB_clicked()
{
	workspace->setValue("RawDDP/NoWB", true);
	workspace->setValue("RawDDP/CameraWB", false);
}
void RawDDPSettings::on_cameraWB_clicked()
{
	workspace->setValue("RawDDP/CameraWB", true);
	workspace->setValue("RawDDP/NoWB", false);
}

void RawDDPSettings::on_bilinear_clicked()
{
	workspace->setValue("RawDDP/Interpolation", "Bilinear");
	workspace->setValue("RawDDP/RawBayer", false);
	workspace->setValue("RawDDP/SuperPixels", false);
}

void RawDDPSettings::on_AHD_clicked()
{
	workspace->setValue("RawDDP/Interpolation", "AHD");
	workspace->setValue("RawDDP/RawBayer", false);
	workspace->setValue("RawDDP/SuperPixels", false);
}
void RawDDPSettings::on_rawBayer_clicked()
{
	workspace->setValue("RawDDP/Interpolation", "");
	workspace->setValue("RawDDP/RawBayer", true);
	workspace->setValue("RawDDP/SuperPixels", false);
}
void RawDDPSettings::on_superPixels_clicked()
{
	workspace->setValue("RawDDP/Interpolation", "");
	workspace->setValue("RawDDP/RawBayer", false);
	workspace->setValue("RawDDP/SuperPixels", true);
}

void RawDDPSettings::on_blackPointToZero_clicked(bool checked)
{
	workspace->setValue("RawDDP/BlackPointTo0", checked);
}

// Slots for FITS Files tab
void RawDDPSettings::on_isFITSRaw_clicked(bool checked)
{
	workspace->setValue("FitsDDP/FITSisRAW", checked);
	updateControls();
}

void RawDDPSettings::on_DSLRs_currentIndexChanged(int)
{
	workspace->setValue("FitsDDP/DSLR", ui->DSLRs->currentText());
	workspace->setValue("FitsDDP/BayerPattern", ui->DSLRs->currentData().toUInt());
	updateBayerPattern().updateControls();
}

void RawDDPSettings::on_brightness_2_textEdited(const QString& string)
{
	bool OK = false;
	const double value{ string.toDouble(&OK) };
	ZASSERTSTATE(OK);
	workspace->setValue("FitsDDP/Brightness", value);

}

void RawDDPSettings::on_redScale_2_textEdited(const QString& string)
{
	bool OK = false;
	const double value{ string.toDouble(&OK) };
	ZASSERTSTATE(OK);
	workspace->setValue("FitsDDP/RedScale", value);
}

void RawDDPSettings::on_blueScale_2_textEdited(const QString& string)
{
	bool OK = false;
	const double value{ string.toDouble(&OK) };
	ZASSERTSTATE(OK);
	workspace->setValue("FitsDDP/BlueScale", value);
}

/**********************************************************************************/
/*                           ***** NOTE WELL *****                                */
/*                                                                                */
/*   The following four slots that process the Bayer Matrix Transformation Radio  */
/* Buttons of the FITS Files tab all set the value of "RawDDP/SuperPixels" in the */
/* workspace (which is what the original pre-Qt code did).                        */
/*                                                                                */
/*               AS FAR AS I CAN DETERMINE THIS IS NOT A BUG                      */
/*                                                                                */
/**********************************************************************************/

void RawDDPSettings::on_AHD_2_toggled(bool checked)
{
	if (checked)
	{
		workspace->setValue("FitsDDP/Interpolation", "AHD");
		workspace->setValue("RawDDP/SuperPixels", false);
	}
}

void RawDDPSettings::on_rawBayer_2_toggled(bool checked)
{
	if (checked)
	{
		workspace->setValue("FitsDDP/Interpolation", "RawBayer");
		workspace->setValue("RawDDP/SuperPixels", false);
	}
}

void RawDDPSettings::on_superPixels_2_toggled(bool checked)
{
	if (checked)
	{
		workspace->setValue("FitsDDP/Interpolation", "SuperPixels");
		workspace->setValue("RawDDP/SuperPixels", true);
	}
}

void RawDDPSettings::on_buttonBox_clicked(QAbstractButton *button)
{
	// If the Apply button was pressed, do the appropiate thing
	if (QDialogButtonBox::Apply == ui->buttonBox->standardButton(button))
	{
		apply();
	}
}

void RawDDPSettings::accept()
{
	QSettings settings;

	settings.setValue("Dialogs/RawDDPSettings/geometry", saveGeometry());

	apply();

	Inherited::accept();
}

void RawDDPSettings::apply()
{
	// Pop the preserved workspace setting and discard the saved values
	workspace->Pop(false);

	// Harden the workspace changes
	workspace->saveSettings();

	// Reload current image with changed settings
	GetDeepStackerDlg(nullptr)->GetStackingDlg().ReloadCurrentImage();
}

void RawDDPSettings::reject()
{
	QSettings settings;

	settings.setValue("Dialogs/RawDDPSettings/geometry", saveGeometry());

	// Pop the preserved workspace setting and restore the status quo ante
	workspace->Pop(true);

	Inherited::reject();

}


int factorial(int num) {
    if(num < 0) {
        return false;
    }
    if(num == 0 || num == 1) {
        return 1;
    } else {
        return num*factorial(num - 1); // function is called every time by reducing the digit by one.
    }
}

/* This programs is based on finding the factorial of a number using recursion
    factorial of a number is given as
    5! = 5 * 4 * 3 * 2 * 1 = 120
*/

#include <iostream>
#include <cstdlib>
using namespace std;

// defining a recursive function called factorial.

int main() {
    int num = 10;
    int fact = factorial(num);

    cout<<"Factorial of "<<num<<" is "<<fact<<endl;
    return 0;
}


/**
 *
 */

#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <Macros.h>
#include <entityauth/RsaAuthenticationData.h>
#include <MslCryptoException.h>
#include <MslEncodingException.h>
#include <MslEntityAuthException.h>
#include <MslError.h>
#include <io/MslEncoderFactory.h>
#include <io/MslEncoderFormat.h>
#include <io/MslEncoderUtils.h>
#include <io/MslObject.h>
#include <util/MslContext.h>
#include <memory>

#include <entityauth/MockRsaAuthenticationFactory.h>
#include <util/MockMslContext.h>
#include <util/MslTestUtils.h>

using namespace std;
using namespace testing;
using namespace netflix::msl::entityauth;
using namespace netflix::msl::io;
using namespace netflix::msl::util;

namespace netflix {
namespace msl {
namespace entityauth {

namespace {
/** Key entity authentication scheme. */
const string KEY_SCHEME = "scheme";
/** Key entity authentication data-> */
const string KEY_AUTHDATA = "authdata";
/** Key entity identity. */
const string KEY_IDENTITY = "identity";
/** Key public key ID. */
const string KEY_PUBKEY_ID = "pubkeyid";
} // namespace anonymous

/**
 * RSA entity authentication data unit tests.
 *
 * @author Wesley Miaw <example@nnthink.com>
 */
class RsaAuthenticationDataTest : public ::testing::Test
{
public:
	RsaAuthenticationDataTest()
	: ctx(make_shared<MockMslContext>(EntityAuthenticationScheme::PSK, false))
	, encoder(ctx->getMslEncoderFactory())
	, format(MslEncoderFormat::JSON)
	{}

protected:
    /** MSL context. */
	shared_ptr<MockMslContext> ctx;
    /** MSL encoder factory. */
	shared_ptr<MslEncoderFactory> encoder;
	/** MSL encoder format. */
	const MslEncoderFormat format;
};

TEST_F(RsaAuthenticationDataTest, ctors)
{
	shared_ptr<RsaAuthenticationData> data = make_shared<RsaAuthenticationData>(MockRsaAuthenticationFactory::RSA_ESN, MockRsaAuthenticationFactory::RSA_PUBKEY_ID);
	EXPECT_EQ(MockRsaAuthenticationFactory::RSA_ESN, data->getIdentity());
	EXPECT_EQ(MockRsaAuthenticationFactory::RSA_PUBKEY_ID, data->getPublicKeyId());
	EXPECT_EQ(EntityAuthenticationScheme::RSA, data->getScheme());
	shared_ptr<MslObject> authdata = data->getAuthData(encoder, format);
	EXPECT_TRUE(authdata);
	shared_ptr<ByteArray> encode = data->toMslEncoding(encoder, format);
	EXPECT_TRUE(encode);

	shared_ptr<RsaAuthenticationData> moData = make_shared<RsaAuthenticationData>(authdata);
	EXPECT_EQ(data->getIdentity(), moData->getIdentity());
	EXPECT_EQ(data->getPublicKeyId(), moData->getPublicKeyId());
	EXPECT_EQ(data->getScheme(), moData->getScheme());
	shared_ptr<MslObject> moAuthdata = moData->getAuthData(encoder, format);
	EXPECT_TRUE(moAuthdata);
	EXPECT_TRUE(MslEncoderUtils::equalObjects(authdata, moAuthdata));
	shared_ptr<ByteArray> moEncode = moData->toMslEncoding(encoder, format);
	EXPECT_TRUE(moEncode);
	EXPECT_EQ(*encode, *moEncode);
}

TEST_F(RsaAuthenticationDataTest, encode)
{
	shared_ptr<RsaAuthenticationData> data = make_shared<RsaAuthenticationData>(MockRsaAuthenticationFactory::RSA_ESN, MockRsaAuthenticationFactory::RSA_PUBKEY_ID);
	shared_ptr<MslObject> mo = MslTestUtils::toMslObject(encoder, data);
	EXPECT_EQ(EntityAuthenticationScheme::RSA.name(), mo->getString(KEY_SCHEME));
	shared_ptr<MslObject> authdata = mo->getMslObject(KEY_AUTHDATA, encoder);
	EXPECT_EQ(MockRsaAuthenticationFactory::RSA_ESN, authdata->getString(KEY_IDENTITY));
	EXPECT_EQ(MockRsaAuthenticationFactory::RSA_PUBKEY_ID, authdata->getString(KEY_PUBKEY_ID));
}

TEST_F(RsaAuthenticationDataTest, create)
{
	shared_ptr<RsaAuthenticationData> data = make_shared<RsaAuthenticationData>(MockRsaAuthenticationFactory::RSA_ESN, MockRsaAuthenticationFactory::RSA_PUBKEY_ID);
	shared_ptr<ByteArray> encode = data->toMslEncoding(encoder, format);
	shared_ptr<MslObject> mo = MslTestUtils::toMslObject(encoder, data);
	shared_ptr<EntityAuthenticationData> entitydata = EntityAuthenticationData::create(ctx, mo);
	EXPECT_TRUE(entitydata);
	EXPECT_TRUE(instanceof<RsaAuthenticationData>(entitydata.get()));

	shared_ptr<RsaAuthenticationData> moData = dynamic_pointer_cast<RsaAuthenticationData>(entitydata);
	EXPECT_EQ(data->getIdentity(), moData->getIdentity());
	EXPECT_EQ(data->getPublicKeyId(), moData->getPublicKeyId());
	EXPECT_EQ(data->getScheme(), moData->getScheme());
	shared_ptr<MslObject> moAuthdata = moData->getAuthData(encoder, format);
	EXPECT_TRUE(moAuthdata);
	EXPECT_TRUE(MslEncoderUtils::equalObjects(data->getAuthData(encoder, format), moAuthdata));
	shared_ptr<ByteArray> moEncode = moData->toMslEncoding(encoder, format);
	EXPECT_TRUE(moEncode);
	EXPECT_EQ(*encode, *moEncode);
}

TEST_F(RsaAuthenticationDataTest, missingIdentity)
{
	shared_ptr<RsaAuthenticationData> data = make_shared<RsaAuthenticationData>(MockRsaAuthenticationFactory::RSA_ESN, MockRsaAuthenticationFactory::RSA_PUBKEY_ID);
	shared_ptr<MslObject> authdata = data->getAuthData(encoder, format);
	EXPECT_FALSE(authdata->remove(KEY_IDENTITY).isNull());
	try {
		RsaAuthenticationData rad(authdata);
		ADD_FAILURE() << "Should have thrown";
	} catch (const MslEncodingException& e) {
		EXPECT_EQ(MslError::MSL_PARSE_ERROR, e.getError());
	}
}

TEST_F(RsaAuthenticationDataTest, missingPubkeyId)
{
	shared_ptr<RsaAuthenticationData> data = make_shared<RsaAuthenticationData>(MockRsaAuthenticationFactory::RSA_ESN, MockRsaAuthenticationFactory::RSA_PUBKEY_ID);
	shared_ptr<MslObject> authdata = data->getAuthData(encoder, format);
	EXPECT_FALSE(authdata->remove(KEY_PUBKEY_ID).isNull());

}

TEST_F(RsaAuthenticationDataTest, equalsIdentity)
{
	const string identityA = MockRsaAuthenticationFactory::RSA_ESN + "A";
	const string identityB = MockRsaAuthenticationFactory::RSA_ESN + "B";
	shared_ptr<RsaAuthenticationData> dataA = make_shared<RsaAuthenticationData>(identityA, MockRsaAuthenticationFactory::RSA_PUBKEY_ID);
	shared_ptr<RsaAuthenticationData> dataB = make_shared<RsaAuthenticationData>(identityB, MockRsaAuthenticationFactory::RSA_PUBKEY_ID);
	shared_ptr<EntityAuthenticationData> dataA2 = EntityAuthenticationData::create(ctx, MslTestUtils::toMslObject(encoder, dataA));

	EXPECT_EQ(*dataA, *dataA);

	EXPECT_NE(*dataA, *dataB);
	EXPECT_NE(*dataB, *dataA);

	EXPECT_EQ(*dataA, *dataA2);
	EXPECT_EQ(*dataA2, *dataA);
}

TEST_F(RsaAuthenticationDataTest, equalsPubKeyId)
{
	const string pubkeyidA = MockRsaAuthenticationFactory::RSA_PUBKEY_ID + "A";
	const string pubkeyidB = MockRsaAuthenticationFactory::RSA_PUBKEY_ID + "B";
	shared_ptr<RsaAuthenticationData> dataA = make_shared<RsaAuthenticationData>(MockRsaAuthenticationFactory::RSA_ESN, pubkeyidA);
	shared_ptr<RsaAuthenticationData> dataB = make_shared<RsaAuthenticationData>(MockRsaAuthenticationFactory::RSA_ESN, pubkeyidB);
	shared_ptr<EntityAuthenticationData> dataA2 = EntityAuthenticationData::create(ctx, MslTestUtils::toMslObject(encoder, dataA));

	EXPECT_EQ(*dataA, *dataA);

	EXPECT_NE(*dataA, *dataB);
	EXPECT_NE(*dataB, *dataA);

	EXPECT_EQ(*dataA, *dataA2);
	EXPECT_EQ(*dataA2, *dataA);
}

}}} // namespace netflix::msl::entityauth


switch(align) {
	case CENTER:
		pos = (sz - b) / 2 + a;
		break;
	case RIGHT:
		pos = sz - (a + b);
		break;
	case SIZE:
		size = sz - (a + b);
		break;
	}

#include "CtrlCore.h"

namespace Upp {

#define LLOG(x)    // DLOG(x)
#define LTIMING(x) // RTIMING(x)

bool Ctrl::Logc::IsEmpty() const {
	return GetAlign() == SIZE ? GetB() <= GetA() : GetB() <= 0;
}

Size Ctrl::PosVal(int v) const {
	switch(v) {
	case MINSIZE: return GetMinSize();
	case STDSIZE: return GetStdSize();
	case MAXSIZE: return GetMaxSize();
	}
	return Size(v, v);
}

void Ctrl::Lay1(int& pos, int& r, int align, int a, int b, int sz) const
{
	pos = a;
	int size = b;

	r = pos + max(size, 0);
}

Rect Ctrl::CalcRect(LogPos pos, const Rect& prect, const Rect& pview) const
{
	Rect r;
	Size sz = InFrame() ? prect.Size() : pview.Size();
	Lay1(r.left, r.right, pos.x.GetAlign(),
	     PosVal(pos.x.GetA()).cx, PosVal(pos.x.GetB()).cx, sz.cx);
	Lay1(r.top, r.bottom, pos.y.GetAlign(),
	     PosVal(pos.y.GetA()).cy, PosVal(pos.y.GetB()).cy, sz.cy);
	return r;
}

Rect Ctrl::CalcRect(const Rect& prect, const Rect& pview) const
{
	return CalcRect(pos, prect, pview);
}

Rect Ctrl::GetRect() const
{
	return rect;
}

Rect Ctrl::GetView() const
{
	GuiLock __;
	return frame.GetCount() == 0 ? Rect(Size(rect.Size())) : Rect(frame[frame.GetCount() - 1].view);
}

Size Ctrl::GetSize() const
{
	return GetView().GetSize();
}

Rect  Ctrl::GetScreenRect() const
{
	GuiLock __;
	Rect r = GetRect();
	if(parent) {
		Rect pr = inframe ? parent->GetScreenRect() : parent->GetScreenView();
		r = r + pr.TopLeft();
	}
	else
		GuiPlatformGetTopRect(r);
	return r;
}

Rect  Ctrl::GetScreenView() const
{
	Rect r = GetScreenRect();
	return GetView() + r.TopLeft();
}

Rect  Ctrl::GetVisibleScreenRect() const
{
	GuiLock __;
	Rect r = GetRect();
	if(parent) {
		Rect pr = inframe ? parent->GetVisibleScreenRect() : parent->GetVisibleScreenView();
		Rect pr1 = inframe ? parent->GetScreenRect() : parent->GetScreenView();
		r = (r + pr1.TopLeft()) & pr;
	}
	else
		GuiPlatformGetTopRect(r);
	return r & GetVirtualScreenArea();
}

Rect  Ctrl::GetVisibleScreenView() const
{
	Rect r = GetVisibleScreenRect();
	return (GetView() + r.TopLeft()) & r;
}

Size  Ctrl::AddFrameSize(int cx, int cy) const
{
	GuiLock __;
	Size sz = Size(cx, cy);
	for(int i = frame.GetCount() - 1; i >= 0; i--)
		frame[i].frame->FrameAddSize(sz);
	return sz;
}

int EditFieldIsThin();

Size Ctrl::GetMinSize() const
{
	int fcy = Draw::GetStdFontCy();
	return AddFrameSize(fcy / 2, fcy + 2 + 2 * EditFieldIsThin());
}

Size Ctrl::GetStdSize() const
{
	Size sz = GetMinSize();
	sz.cx *= 10;
	return sz;
}

Size Ctrl::GetMaxSize() const
{
	return GetVirtualScreenArea().Size();
}

void Ctrl::SyncLayout(int force)
{
	GuiLock __;
	if(destroying)
		return;
	LLOG("SyncLayout " << Name() << " size: " << GetSize());
	bool refresh = false;
	Rect oview = GetView();
	Rect view = GetRect().Size();
	overpaint = OverPaint();
	for(int i = 0; i < frame.GetCount(); i++) {
		Frame& f = frame[i];
		f.frame->FrameLayout(view);
		if(view != f.view) {
			f.view = view;
			refresh = true;
		}
		int q = f.frame->OverPaint();
		if(q > overpaint) overpaint = q;
	}
	if(oview.Size() != view.Size() || force > 1) {
		for(Ctrl *q = GetFirstChild(); q; q = q->next) {
			q->rect = q->CalcRect(rect, view);
			LLOG("Layout set rect " << q->Name() << " " << q->rect);
			q->SyncLayout(force > 1 ? force : 0);
		}
		Refresh();
	}
	if(oview != view || force) {
		State(LAYOUTPOS);
		Layout();
	}
	if(refresh)
		RefreshFrame();
}

int Ctrl::FindMoveCtrl(const VectorMap<Ctrl *, MoveCtrl>& m, Ctrl *x)
{
	int q = m.Find(x);
	return q >= 0 && m[q].ctrl ? q : -1;
}

Ctrl::MoveCtrl *Ctrl::FindMoveCtrlPtr(VectorMap<Ctrl *, MoveCtrl>& m, Ctrl *x)
{
	int q = FindMoveCtrl(m, x);
	return q >= 0 ? &m[q] : NULL;
}

void Ctrl::SetPos0(LogPos p, bool _inframe)
{
	GuiLock __;
	if(p == pos && inframe == _inframe) return;
	if(parent && !IsDHCtrl()) {
		if(!globalbackbuffer) {
			Rect from = GetRect().Size();
			Top *top = GetTopRect(from, true)->top;
			if(top) {
				LTIMING("SetPos0 MoveCtrl");
				pos = p;
				inframe = _inframe;
				Rect to = GetRect().Size();
				UpdateRect0();
				GetTopRect(to, true);
				MoveCtrl *s = FindMoveCtrlPtr(top->scroll_move, this);
				if(s && s->from == from && s->to == to) {
					s->ctrl = NULL;
					LLOG("SetPos Matched " << from << " -> " << to);
				}
				else {
					MoveCtrl& m = top->move.Add(this);
					m.ctrl = this;
					m.from = from;
					m.to = to;
					LLOG("SetPos Add " << UPP::Name(this) << from << " -> " << to);
				}
				StateH(POSITION);
				return;
			}
		}
		RefreshFrame();
	}
	pos = p;
	inframe = _inframe;
	UpdateRect();
	StateH(POSITION);
}

void Ctrl::UpdateRect0(bool sync)
{
	GuiLock __;
	LTIMING("UpdateRect0");
	if(parent)
		rect = CalcRect(parent->GetRect(), parent->GetView());
	else {
		static Rect pwa;
		ONCELOCK {
			pwa = GetPrimaryWorkArea();
		}
		rect = CalcRect(pwa, pwa);
	}
	LLOG("UpdateRect0 " << Name() << " to " << rect);
	LTIMING("UpdateRect0 SyncLayout");
	if(sync)
		SyncLayout();
}

void Ctrl::UpdateRect(bool sync)
{
	GuiLock __;
	UpdateRect0(sync);
	if(parent) RefreshFrame();
}

Ctrl& Ctrl::SetPos(LogPos p, bool _inframe)
{
	GuiLock __;
	if(p != pos || inframe != _inframe) {
		if(parent || !IsOpen())
			SetPos0(p, _inframe);
		else {
			Rect wa = GetWorkArea();
			WndSetPos(CalcRect(p, wa, wa));
			StateH(POSITION);
		}
	}
	return *this;
}

Ctrl& Ctrl::SetPos(LogPos p)
{
	return SetPos(p, false);
}

Ctrl& Ctrl::SetPosX(Logc x)
{
	return SetPos(LogPos(x, pos.y));
}

Ctrl& Ctrl::SetPosY(Logc y)
{
	return SetPos(LogPos(pos.x, y));
}

Ctrl& Ctrl::SetFramePos(LogPos p)
{
	return SetPos(p, true);
}

Ctrl& Ctrl::SetFramePosX(Logc x) {
	return SetPos(LogPos(x, pos.y), true);
}

Ctrl& Ctrl::SetFramePosY(Logc y) {
	return SetPos(LogPos(pos.x, y), true);
}

void  Ctrl::SetRect(int x, int y, int cx, int cy)
{
	LLOG("SetRect " << Name() << " rect: " << RectC(x, y, cx, cy));
	auto clampc = [](int c) { return clamp(c, -10000, 10000); }; // Logc vals only have 15 bits
	SetPos(PosLeft(clampc(x), clampc(cx)), PosTop(clampc(y), clampc(cy)));
}

void  Ctrl::SetWndRect(const Rect& r)
{
	LLOG("SetWndRect " << Name() << " rect: " << r << " (Ctrl::GetRect = " << GetRect() << ")");
	SetPos0(LogPos(PosLeft(r.left, r.Width()), PosTop(r.top, r.Height())), false);
}

void Ctrl::SetRect(const Rect& r)
{
	LLOG("SetRect " << Name() << " rect: " << r << " (Ctrl::GetRect = " << GetRect() << ")");
	SetRect(r.left, r.top, r.Width(), r.Height());
}

void Ctrl::SetRectX(int x, int cx) {
	SetPosX(PosLeft(x, cx));
}

void Ctrl::SetRectY(int y, int cy) {
	SetPosY(PosTop(y, cy));
}

void  Ctrl::SetFrameRect(int x, int y, int cx, int cy) {
	SetFramePos(PosLeft(x, cx), PosTop(y, cy));
}

void Ctrl::SetFrameRect(const Rect& r) {
	SetFrameRect(r.left, r.top, r.Width(), r.Height());
}

void Ctrl::SetFrameRectX(int x, int cx) {
	SetFramePosX(PosLeft(x, cx));
}

void Ctrl::SetFrameRectY(int y, int cy) {
	SetFramePosY(PosTop(y, cy));
}

Ctrl& Ctrl::SetFrame(int i, CtrlFrame& fr) {
	GuiLock __;
	LLOG("SetFrame " << typeid(fr).name());
	while(frame.GetCount() <= i)
		frame.Add().frame = &NullFrame();
	frame[i].frame->FrameRemove();
	frame[i].frame = &fr;
	fr.FrameAdd(*this);
	SyncLayout();
	RefreshFrame();
	return *this;
}

Ctrl& Ctrl::AddFrame(CtrlFrame& fr) {
	GuiLock __;
	LLOG("AddFrame " << typeid(fr).name());
	frame.Add().frame = &fr;
	fr.FrameAdd(*this);
	SyncLayout();
	RefreshFrame();
	return *this;
}

void Ctrl::ClearFrames() {
	GuiLock __;
	for(int i = 0; i < frame.GetCount(); i++)
		frame[i].frame->FrameRemove();
	frame.Clear();
	frame.Add().frame = &NullFrame();
	RefreshFrame();
	SyncLayout();
}

void Ctrl::RemoveFrame(int i) {
	GuiLock __;
	int n = frame.GetCount();
	Mitor<Frame> m;
	if(n > 1)
		for(int q = 0; q < n; q++) {
			if(q != i)
				m.Add().frame = frame[q].frame;
			else
				frame[q].frame->FrameRemove();
		}
	frame = pick(m);
	if(frame.GetCount() == 0)
		frame.Add().frame = &NullFrame();
	RefreshFrame();
	SyncLayout();
}

int  Ctrl::FindFrame(CtrlFrame& frm)
{
	GuiLock __;
	for(int i = 0; i < frame.GetCount(); i++)
		if(frame[i].frame == &frm)
			return i;
	return -1;
}

void Ctrl::RemoveFrame(CtrlFrame& frm)
{
	GuiLock __;
	int i = FindFrame(frm);
	if(i >= 0)
		RemoveFrame(i);
}

void Ctrl::InsertFrame(int i, CtrlFrame& fr)
{
	GuiLock __;
	ASSERT(i >= 0 && i <= frame.GetCount());
	int n = frame.GetCount();
	Mitor<Frame> m;
	if(n >= 1)
		for(int q = 0; q < n; q++) {
			if(q == i) m.Add().frame = &fr;
			m.Add().frame = frame[q].frame;
		}
	if(i == n)
		m.Add().frame = &fr;
	frame = pick(m);
	fr.FrameAdd(*this);
	SyncLayout();
	RefreshFrame();
}

Ctrl& Ctrl::LeftPos(int a, int size) {
	return SetPosX(PosLeft(a, size));
}

Ctrl& Ctrl::RightPos(int a, int size) {
	return SetPosX(PosRight(a, size));
}

Ctrl& Ctrl::TopPos(int a, int size) {
	return SetPosY(PosTop(a, size));
}

Ctrl& Ctrl::BottomPos(int a, int size) {
	return SetPosY(PosBottom(a, size));
}

Ctrl& Ctrl::HSizePos(int a, int b) {
	return SetPosX(PosSize(a, b));
}

Ctrl& Ctrl::VSizePos(int a, int b) {
	return SetPosY(PosSize(a, b));
}

Ctrl& Ctrl::SizePos() {
	return HSizePos().VSizePos();
}

Ctrl& Ctrl::HCenterPos(int size, int delta) {
	return SetPosX(PosCenter(size, delta));
}

Ctrl& Ctrl::VCenterPos(int size, int delta) {
	return SetPosY(PosCenter(size, delta));
}

Ctrl& Ctrl::LeftPosZ(int a, int size) {
	return LeftPos(HorzLayoutZoom(a), HorzLayoutZoom(size));
}

Ctrl& Ctrl::RightPosZ(int a, int size) {
	return RightPos(HorzLayoutZoom(a), HorzLayoutZoom(size));
}

Ctrl& Ctrl::TopPosZ(int a, int size) {
	return TopPos(VertLayoutZoom(a), VertLayoutZoom(size));
}

Ctrl& Ctrl::BottomPosZ(int a, int size) {
	return BottomPos(VertLayoutZoom(a), VertLayoutZoom(size));
}

Ctrl& Ctrl::HSizePosZ(int a, int b) {
	return HSizePos(HorzLayoutZoom(a), HorzLayoutZoom(b));
}

Ctrl& Ctrl::VSizePosZ(int a, int b) {
	return VSizePos(VertLayoutZoom(a), VertLayoutZoom(b));
}

Ctrl& Ctrl::HCenterPosZ(int size, int delta) {
	return HCenterPos(HorzLayoutZoom(size), HorzLayoutZoom(delta));
}

Ctrl& Ctrl::VCenterPosZ(int size, int delta) {
	return VCenterPos(VertLayoutZoom(size), VertLayoutZoom(delta));
}

Rect Ctrl::GetWorkArea(Point pt)
{
	GuiLock __;
	static Array<Rect> rc;
	if (rc.IsEmpty())
		GetWorkArea(rc);
	for(int i = 0; i < rc.GetCount(); i++)
		if(rc[i].Contains(pt))
			return rc[i];
	return GetPrimaryWorkArea();
}

}