//*@@@+++@@@@******************************************************************
//
// Copyright © Microsoft Corp.
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 
// • Redistributions of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
// • Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//*@@@---@@@@******************************************************************
#include <JXRTest.h>
#include <time.h>


//================================================================
// Command line argument support
//================================================================
typedef struct tagWMPENCAPPARGS
{
    char* szInputFile;
    char* szOutputFile;
    
    PKPixelFormatGUID guidPixFormat;
//    Bool bFlagRGB_BGR;

    CWMIStrCodecParam wmiSCP;
    float fltImageQuality;
    Bool bOverlapSet;
    Bool bColorFormatSet;
} WMPENCAPPARGS;

//----------------------------------------------------------------
void WmpEncAppUsage(const char* szExe)
{
    printf(CRLF);
    printf("JPEG XR Encoder Utility" CRLF);
    printf("Copyright 2013 Microsoft Corporation - All Rights Reserved" CRLF); 
    printf(CRLF);
    printf("%s [options]..." CRLF, szExe);
    printf(CRLF);
    printf("  -i input.bmp/tif/hdr         Input image file name" CRLF);
    printf("                               bmp: <=8bpc, BGR" CRLF);
    printf("                               tif: >=8bpc, RGB" CRLF);
    printf("                               hdr: 32bppRGBE only" CRLF);
    printf(CRLF);

    printf("  -o output.jxr                Output JPEG XR file name" CRLF);
    printf(CRLF);

    printf("  -q quality                   [0.0 - 1.0) Default = 1.0, lossless" CRLF);
    printf("  or quantization              [1   - 255] Default = 1, lossless" CRLF);
    printf(CRLF);

    printf("  -c format                    Required to define uncompressed source pixel format" CRLF);
    printf("                                0: 24bppBGR" CRLF);
    printf("                                1: 1bppBlackWhite" CRLF);
    printf("                                2: 8bppGray" CRLF);
    printf("                                3: 16bppGray" CRLF);
    printf("                                4: 16bppGrayFixedPoint" CRLF);
    printf("                                5: 16bppGrayHalf" CRLF);
//    printf("                               6: 32bppGray" CRLF);
    printf("                                7: 32bppGrayFixedPoint" CRLF);
    printf("                                8: 32bppGrayFloat" CRLF);

    printf("                                9: 24bppRGB" CRLF);
    printf("                               10: 48bppRGB" CRLF);
    printf("                               11: 48bppRGBFixedPoint" CRLF);
    printf("                               12: 48bppRGBHalf" CRLF);
//    printf("                               13: 96bppRGB" CRLF);
    printf("                               14: 96bppRGBFixedPoint" CRLF);
    printf("                               15: 128bppRGBFloat" CRLF);

    printf("                               16: 32bppRGBE" CRLF);

    printf("                               17: 32bppCMYK" CRLF);
    printf("                               18: 64bppCMYK" CRLF);

/*
    printf("                               19 - YUV 420" CRLF);
    printf("                               20 - YUV 422" CRLF);
    printf("                               21 - YUV 444" CRLF);
*/
    printf("                               22: 32bppBGRA" CRLF);
    printf("                               23: 64bppRGBA" CRLF);
    printf("                               24: 64bppRGBAFixedPoint" CRLF);
    printf("                               25: 64bppRGBAHalf" CRLF);
//    printf("                               26 - 128bpp RGBA" CRLF);
    printf("                               27: 128bppRGBAFixedPoint" CRLF);
    printf("                               28: 128bppRGBAFloat" CRLF);

    printf("                               29: 16bppBGR555" CRLF);
    printf("                               30: 16bppBGR565" CRLF);
    printf("                               31: 32bppBGR101010" CRLF);
    //printf("                               101..116 - 1..16 channel 8bpp" CRLF);
    printf("                               32: 40bppCMYKA" CRLF);
    printf("                               33: 80bppCMYKA" CRLF);

    printf("                               34: 32bppBGR" CRLF);
/*
    printf("                               35:  32bppPBGRA" CRLF);
    printf("                               36:  64bppPRGBA" CRLF);
    printf("                               37: 128bppPRGBA Float" CRLF);
*/
    printf(CRLF);

    printf("  -d chroma sub-sampling       0: Y-only" CRLF);
    printf("                               1: YCoCg 4:2:0" CRLF);
    printf("                               2: YCoCg 4:2:2" CRLF);
    printf("                               3: YCoCg 4:4:4 (default)" CRLF);
    printf("     (if not set is 4:4:4 for quality >= 0.5 or 4:2:0 for quality < 0.5)" CRLF);
    printf(CRLF);

    printf("  -l overlapping               0: No overlapping" CRLF);
    printf("                               1: One level overlapping (default)" CRLF);
    printf("                               2: Two level overlapping" CRLF);
    printf("     (if not set is One for quality >= 0.5 or Two for quality < 0.5)" CRLF);
    printf(CRLF);

    printf("  -f                           Turn off frequency order bit stream (to spatial)" CRLF);
    printf(CRLF);    
    printf("  -p                           Turn off progressive mode (to sequential)" CRLF);
    printf(CRLF);    
    printf("  -t                           Display timing information" CRLF);
    printf(CRLF);
    printf("  -v                           Display verbose encoder information" CRLF);
    printf(CRLF);
    printf("  -V tile_wd0 [tile_wd1 ... ]  Macro block columns per tile " CRLF);
    printf(CRLF);
    printf("  -H tile_ht0 [tile_ht1 ... ]  Macro block rows per tile" CRLF);
    printf(CRLF);
    printf("  -U num_v_tiles num_h_tiles   Vertical & horizontal tile count for uniform tiling" CRLF);
    printf(CRLF);

    printf("  -b Black/White               Applies to 1bpp black/white images" CRLF);
    printf("                               0: 0 = black (default)" CRLF);
    printf("                               1: 0 = white" CRLF);
    printf(CRLF);

    printf("  -a alpha channel format      Required for any pixel format with an alpha channel" CRLF);
    printf("                               2: Planar alpha (default)" CRLF);
    printf("                               3: Interleaved alpha" CRLF);
    printf("                               Other: Reserved, do not use" CRLF);
    printf(CRLF);

    printf("  -Q quantization for alpha    [1 - 255] Default = 1, lossless" CRLF);
    printf(CRLF);

    printf("  -F trimmed flexbits          [0 - 15]  0: no trimming (default)" CRLF);    
    printf("                                        15: trim all" CRLF);    
    printf(CRLF);
    printf("  -s skip subbands             0: All subbands included (default)" CRLF);    
    printf("                               1: Skip flexbits" CRLF);    
    printf("                               2: Skip highpass" CRLF);    
    printf("                               3: Skip highpass & lowpass (DC only)" CRLF);    
    printf(CRLF);
    printf("Eg: %s -i input.bmp -o output.jxr -q 0.9" CRLF, szExe);
}

void WmpEncAppShowArgs(WMPENCAPPARGS* args)
{
    const char *szCF[] = {"Y_ONLY", "YUV_420", "YUV_422", "YUV_444", "CMYK"};

	GUID guidPF = args->guidPixFormat;
    
    printf("================================" CRLF);
    printf("Input file:   %s" CRLF, args->szInputFile);
    printf("Output file:  %s" CRLF, args->szOutputFile);
    printf("Color format: %08X-%04X-%04X-%02X%02X%02X%02X%02X%02X%02X%02X" CRLF, 
        guidPF.Data1, guidPF.Data2, guidPF.Data3, guidPF.Data4[0], guidPF.Data4[1], guidPF.Data4[2],
        guidPF.Data4[3], guidPF.Data4[4], guidPF.Data4[5], guidPF.Data4[6], guidPF.Data4[7]);
    printf("Internal cf:  %s" CRLF, szCF[args->wmiSCP.cfColorFormat]);
    printf("Overlap:      %s" CRLF, 0 < args->wmiSCP.olOverlap ? "yes" : "no");
    printf("DCOverlap:    %s" CRLF, 1 < args->wmiSCP.olOverlap ? "yes" : "no");
    printf("Alpha:        %s" CRLF, 1 < args->wmiSCP.uAlphaMode ? "yes" : "no");
    printf("================================" CRLF);
}

//----------------------------------------------------------------
void WmpEncAppInitDefaultArgs(WMPENCAPPARGS* args)
{
    memset(args, 0, sizeof(*args));

    args->guidPixFormat = GUID_PKPixelFormatDontCare;

    args->wmiSCP.bVerbose = FALSE;
    args->wmiSCP.cfColorFormat = YUV_444;
//    args->bFlagRGB_BGR = FALSE; //default BGR
    args->wmiSCP.bdBitDepth = BD_LONG;
    args->wmiSCP.bfBitstreamFormat = FREQUENCY;
    args->wmiSCP.bProgressiveMode = TRUE;
    args->wmiSCP.olOverlap = OL_ONE;
    args->wmiSCP.cNumOfSliceMinus1H = args->wmiSCP.cNumOfSliceMinus1V = 0;
    args->wmiSCP.sbSubband = SB_ALL;
    args->wmiSCP.uAlphaMode = 0;
    args->wmiSCP.uiDefaultQPIndex = 1;
    args->wmiSCP.uiDefaultQPIndexAlpha = 1;

    args->fltImageQuality = 1.f;
    args->bOverlapSet = 0;
    args->bColorFormatSet = 0;
}

ERR WmpEncAppValidateArgs(WMPENCAPPARGS* args)
{
    ERR err = WMP_errSuccess;

    Test(NULL != args->szInputFile, WMP_errInvalidParameter);
    Test(NULL != args->szOutputFile, WMP_errInvalidParameter);

Cleanup:
    return err;
}

ERR WmpEncAppParseArgs(int argc, char* argv[], WMPENCAPPARGS* args)
{
    ERR err = WMP_errSuccess;

    static const PKPixelFormatGUID* pixelFormat[] =
    {
        &GUID_PKPixelFormat24bppBGR,

        &GUID_PKPixelFormatBlackWhite,

        &GUID_PKPixelFormat8bppGray,
        &GUID_PKPixelFormat16bppGray,
        &GUID_PKPixelFormat16bppGrayFixedPoint,
        &GUID_PKPixelFormat16bppGrayHalf,
        &GUID_PKPixelFormatDontCare, // &GUID_PKPixelFormat32bppGray,
        &GUID_PKPixelFormat32bppGrayFixedPoint,
        &GUID_PKPixelFormat32bppGrayFloat,

        &GUID_PKPixelFormat24bppRGB,
        &GUID_PKPixelFormat48bppRGB,
        &GUID_PKPixelFormat48bppRGBFixedPoint,
        &GUID_PKPixelFormat48bppRGBHalf,
        &GUID_PKPixelFormatDontCare, // &GUID_PKPixelFormat96bppRGB,
        &GUID_PKPixelFormat96bppRGBFixedPoint,
        &GUID_PKPixelFormat128bppRGBFloat,

        &GUID_PKPixelFormat32bppRGBE,
        &GUID_PKPixelFormat32bppCMYK,
        &GUID_PKPixelFormat64bppCMYK,

        &GUID_PKPixelFormat12bppYUV420, 
        &GUID_PKPixelFormat16bppYUV422,
        &GUID_PKPixelFormat24bppYUV444,

        //&GUID_PKPixelFormat32bppRGBA,
        &GUID_PKPixelFormat32bppBGRA,
        &GUID_PKPixelFormat64bppRGBA,
        &GUID_PKPixelFormat64bppRGBAFixedPoint,
        &GUID_PKPixelFormat64bppRGBAHalf,
        &GUID_PKPixelFormatDontCare, // &GUID_PKPixelFormat128bppRGBA,
        &GUID_PKPixelFormat128bppRGBAFixedPoint,
        &GUID_PKPixelFormat128bppRGBAFloat,

        //&GUID_PKPixelFormat32bppPBGRA
        &GUID_PKPixelFormat16bppRGB555,
        &GUID_PKPixelFormat16bppRGB565,
        &GUID_PKPixelFormat32bppRGB101010,
        &GUID_PKPixelFormat40bppCMYKAlpha,
        &GUID_PKPixelFormat80bppCMYKAlpha,
        &GUID_PKPixelFormat32bppBGR,
        &GUID_PKPixelFormat32bppPBGRA,
        &GUID_PKPixelFormat64bppPRGBA,
        &GUID_PKPixelFormat128bppPRGBAFloat,
    };

    size_t InvalidPF[9] = {6, 13, 19, 20, 21, 26, 35, 36, 37};
    size_t AlphaPF[8] = {22, 23, 24, 25, 27, 28, 32, 33};

    int i = 1, j = 0, k;
    char c;
    int idxPF = -1;

    WmpEncAppInitDefaultArgs(args);

    while (i < argc && argv[i][0] == '-')
    {
        switch ((c = argv[i][1])) {
            /* the no-argument switches */
            case 't':
                // NOOP - now we always print timing info
                break;

            case 'v':
                args->wmiSCP.bVerbose = !FALSE;
                break;
                
            /* simple flag argument */
            case 'f':
                args->wmiSCP.bfBitstreamFormat = SPATIAL;
                break;
            
            case 'p':
                args->wmiSCP.bProgressiveMode = FALSE;
                break;
             
            case 'u':
                args->wmiSCP.bUnscaledArith = TRUE;
                break;
           
            default:
                i ++;
                if (i == argc || argv[i][0] == '-') // need more info
                    Call(WMP_errInvalidArgument);

                switch (c)
                {
                    case 'i':
                        args->szInputFile = argv[i];
                        break;

                    case 'o':
                        args->szOutputFile = argv[i];
                        break;

                    case 'q':
                        {
                            args->fltImageQuality = (float) atof(argv[i]);
                            if (args->fltImageQuality < 0.f || args->fltImageQuality > 255.f)
                                Call(WMP_errInvalidArgument);
                        }
                        break;

                    case 'Q':
                        args->wmiSCP.uiDefaultQPIndexAlpha = (U8)(atoi(argv[i]));
                        break;

                    case 's':
                        args->wmiSCP.sbSubband = (SUBBAND)(atoi(argv[i]));
                        break;

                    case 'c':
                        idxPF = (size_t)atol(argv[i]);
                        for (k = 0; k < 9; k++)
                        {
                            if (InvalidPF[k] == (size_t) idxPF)
                            {
                                printf("*** Unsupported format in JPEG XR ***\n");
                                Call(WMP_errInvalidArgument);
                            }
                        }
                        break;

                    case 'a': 
                        args->wmiSCP.uAlphaMode = (U8)atoi(argv[i]);
                        break;

/*                    case 'R':
                        args->bFlagRGB_BGR = (Bool)atoi(argv[i]);
                        break;
*/                
                    case 'l':
                        args->wmiSCP.olOverlap = (OVERLAP)atoi(argv[i]);
                        args->bOverlapSet = 1; 
                        break;

                    case 'd':
                        args->wmiSCP.cfColorFormat = (COLORFORMAT)atoi(argv[i]);
                        args->bColorFormatSet = 1;
                        break;
                    
                    case 'H': // horizontal tiling
                        for(j = 0;;i ++, j ++){
                            args->wmiSCP.uiTileY[j] = atoi(argv[i]);
                            if(i + 1 == argc || argv[i + 1][0] == '-' || j >= MAX_TILES-1)
                                break;
                        }
                        args->wmiSCP.cNumOfSliceMinus1H = (U8)j;
                        break;

                    case 'V': // vertical tiling
                        for(j = 0;;i ++, j ++){
                            args->wmiSCP.uiTileX[j] = atoi(argv[i]);
                            if(i + 1 == argc || argv[i + 1][0] == '-' || j >= MAX_TILES-1)
                                break;
                        }
                        args->wmiSCP.cNumOfSliceMinus1V = (U8)j;
                        break;

                    case 'U': // uniform tiling
                        if(i + 1 < argc && argv[i + 1][0] != '-'){
                            if(atoi(argv[i]) > 0 && atoi(argv[i + 1]) > 0){
                                args->wmiSCP.cNumOfSliceMinus1H = atoi(argv[i]) - 1;
                                args->wmiSCP.cNumOfSliceMinus1V = atoi(argv[i + 1]) - 1;
                            }
                            i ++;
                        }
                        break;

                    case 'm':
                        args->wmiSCP.nLenMantissaOrShift = (U8)atoi(argv[i]);
                        break;

                    case 'C':
                        args->wmiSCP.nExpBias = (I8) atoi(argv[i]) + 128; // rollover arithmetic
                        break;

                    case 'b':
                        args->wmiSCP.bBlackWhite = (Bool)atoi(argv[i]);
                        break;
                        
                    case 'F':
                        args->wmiSCP.uiTrimFlexBits = (U8)atoi(argv[i]);
                        if (args->wmiSCP.uiTrimFlexBits > 15)
                            args->wmiSCP.uiTrimFlexBits = 15;
                        break;

                    default:
                        Call(WMP_errInvalidArgument);
                }
        }

        i ++;
    }

    FailIf((int) sizeof2(pixelFormat) <= idxPF, WMP_errUnsupportedFormat);
    if (idxPF >= 0)
        args->guidPixFormat = *pixelFormat[idxPF];

    if ((idxPF >= 1) && (idxPF <= 8))
        args->wmiSCP.cfColorFormat = Y_ONLY;
    else if ((idxPF == 17) || (idxPF == 18) || (idxPF == 32) || (idxPF == 33))
        args->wmiSCP.cfColorFormat = CMYK;

    for (k = 0; k < 8; k++)
    {
        if (AlphaPF[k] == (size_t) idxPF) 
        {
            if(0 == args->wmiSCP.uAlphaMode)//with Alpha and no default, set default as Planar
            {
                args->wmiSCP.uAlphaMode = 2;
            }
            break;
        }
    }

    //================================
    Call(WmpEncAppValidateArgs(args));

Cleanup:
    if (WMP_errSuccess != err)
    {
        WmpEncAppUsage(argv[0]);
    }
    return err;
}


// Y, U, V, YHP, UHP, VHP
#if 1
// optimized for PSNR
int DPK_QPS_420[11][6] = {      // for 8 bit only
    { 66, 65, 70, 72, 72, 77 },
    { 59, 58, 63, 64, 63, 68 },
    { 52, 51, 57, 56, 56, 61 },
    { 48, 48, 54, 51, 50, 55 },
    { 43, 44, 48, 46, 46, 49 },
    { 37, 37, 42, 38, 38, 43 },
    { 26, 28, 31, 27, 28, 31 },
    { 16, 17, 22, 16, 17, 21 },
    { 10, 11, 13, 10, 10, 13 },
    {  5,  5,  6,  5,  5,  6 },
    {  2,  2,  3,  2,  2,  2 }
};

int DPK_QPS_8[12][6] = {
    { 67, 79, 86, 72, 90, 98 },
    { 59, 74, 80, 64, 83, 89 },
    { 53, 68, 75, 57, 76, 83 },
    { 49, 64, 71, 53, 70, 77 },
    { 45, 60, 67, 48, 67, 74 },
    { 40, 56, 62, 42, 59, 66 },
    { 33, 49, 55, 35, 51, 58 },
    { 27, 44, 49, 28, 45, 50 },
    { 20, 36, 42, 20, 38, 44 },
    { 13, 27, 34, 13, 28, 34 },
    {  7, 17, 21,  8, 17, 21 }, // Photoshop 100%
    {  2,  5,  6,  2,  5,  6 }
};
#else
// optimized for SSIM
int DPK_QPS_420[11][6] = {      // for 8 bit only
    { 67, 77, 80, 75, 82, 86 },
    { 58, 67, 71, 63, 74, 78 },
    { 50, 60, 64, 54, 66, 69 },
    { 46, 55, 59, 49, 60, 63 },
    { 41, 48, 53, 43, 52, 56 },
    { 35, 43, 48, 36, 44, 49 },
    { 29, 37, 41, 30, 38, 41 },
    { 22, 29, 33, 22, 29, 33 },
    { 15, 20, 26, 14, 20, 25 },
    {  9, 14, 18,  8, 14, 17 },
    {  4,  6,  7,  3,  5,  5 }
};

int DPK_QPS_8[12][6] = {
    { 67, 93, 98, 71, 98, 104 },
    { 59, 83, 88, 61, 89,  95 },
    { 50, 76, 81, 53, 85,  90 },
    { 46, 71, 77, 47, 79,  85 },
    { 41, 67, 71, 42, 75,  78 },
    { 34, 59, 65, 35, 66,  72 },
    { 30, 54, 60, 29, 60,  66 },
    { 24, 48, 53, 22, 53,  58 },
    { 18, 39, 45, 17, 43,  48 },
    { 13, 34, 38, 11, 35,  38 },
    {  8, 20, 24,  7, 22,  25 }, // Photoshop 100%
    {  2,  5,  6,  2,  5,   6 }
};
#endif

int DPK_QPS_16[11][6] = {
    { 197, 203, 210, 202, 207, 213 },
    { 174, 188, 193, 180, 189, 196 },
    { 152, 167, 173, 156, 169, 174 },
    { 135, 152, 157, 137, 153, 158 },
    { 119, 137, 141, 119, 138, 142 },
    { 102, 120, 125, 100, 120, 124 },
    {  82,  98, 104,  79,  98, 103 },
    {  60,  76,  81,  58,  76,  81 },
    {  39,  52,  58,  36,  52,  58 },
    {  16,  27,  33,  14,  27,  33 },
    {   5,   8,   9,   4,   7,   8 }
};

int DPK_QPS_16f[11][6] = {
    { 148, 177, 171, 165, 187, 191 },
    { 133, 155, 153, 147, 172, 181 },
    { 114, 133, 138, 130, 157, 167 },
    {  97, 118, 120, 109, 137, 144 },
    {  76,  98, 103,  85, 115, 121 },
    {  63,  86,  91,  62,  96,  99 },
    {  46,  68,  71,  43,  73,  75 },
    {  29,  48,  52,  27,  48,  51 },
    {  16,  30,  35,  14,  29,  34 },
    {   8,  14,  17,   7,  13,  17 },
    {   3,   5,   7,   3,   5,   6 }
};

int DPK_QPS_32f[11][6] = {
    { 194, 206, 209, 204, 211, 217 },
    { 175, 187, 196, 186, 193, 205 },
    { 157, 170, 177, 167, 180, 190 },
    { 133, 152, 156, 144, 163, 168 },
    { 116, 138, 142, 117, 143, 148 },
    {  98, 120, 123,  96, 123, 126 },
    {  80,  99, 102,  78,  99, 102 },
    {  65,  79,  84,  63,  79,  84 },
    {  48,  61,  67,  45,  60,  66 },
    {  27,  41,  46,  24,  40,  45 },
    {   3,  22,  24,   2,  21,  22 }
};

//================================================================
// main function
//================================================================
int 
#ifndef __ANSI__
__cdecl 
#endif // __ANSI__
main(int argc, char* argv[])
{
    ERR err = WMP_errSuccess;

    PKFactory* pFactory = NULL;
    struct WMPStream* pEncodeStream = NULL;
    PKCodecFactory* pCodecFactory = NULL;
    PKCodecFactory* pTestFactory = NULL;
    PKImageEncode* pEncoder = NULL;

    // clock_t start = 0, finish = 0;
    WMPENCAPPARGS args;
    char* pExt = NULL;

    //================================
    // parse command line parameters
    if (1 == argc)
    {
        WmpEncAppUsage(argv[0]);
        return 0;
    }

    Call(WmpEncAppParseArgs(argc, argv, &args));
    if (args.wmiSCP.bVerbose)
    {
        WmpEncAppShowArgs(&args);
    }

    //================================
    pExt = strrchr(args.szInputFile, '.');
    FailIf(NULL == pExt, WMP_errUnsupportedFormat);

    //================================
    Call(PKCreateFactory(&pFactory, PK_SDK_VERSION));
    Call(pFactory->CreateStreamFromFilename(&pEncodeStream, args.szOutputFile, "wb"));

    //================================
    Call(PKCreateCodecFactory(&pCodecFactory, WMP_SDK_VERSION));
    Call(pCodecFactory->CreateCodec(&IID_PKImageWmpEncode, &pEncoder));

    //----------------------------------------------------------------
    Call(PKCreateTestFactory(&pTestFactory, WMP_SDK_VERSION));

    //
    // go through each image
    //
    //for (i = 0; ; ++i)
    {
        PKImageDecode* pDecoder = NULL;
        PKFormatConverter* pConverter = NULL;
        PKPixelInfo PI;

        Float rX = 0.0, rY = 0.0;
        PKRect rect = {0, 0, 0, 0};

        //================================
        Call(pTestFactory->CreateDecoderFromFile(args.szInputFile, &pDecoder));
        if (IsEqualGUID(&args.guidPixFormat, &GUID_PKPixelFormatDontCare))
            Call(pDecoder->GetPixelFormat(pDecoder, &args.guidPixFormat));

        PI.pGUIDPixFmt = &args.guidPixFormat;
        Call(PixelFormatLookup(&PI, LOOKUP_FORWARD));
        if ((PI.grBit & PK_pixfmtHasAlpha) && args.wmiSCP.uAlphaMode == 0)
            args.wmiSCP.uAlphaMode = 2; // with Alpha and no default, set default as Planar

        FailIf(PI.uSamplePerPixel > 1 && PI.uBitsPerSample > 8 && args.wmiSCP.cfColorFormat != YUV_444,
            WMP_errInvalidArgument);

        //================================
        Call(pCodecFactory->CreateFormatConverter(&pConverter));
        Call(pConverter->Initialize(pConverter, pDecoder, pExt, args.guidPixFormat));

        //================================
        Call(pDecoder->GetSize(pDecoder, &rect.Width, &rect.Height));

        if (args.wmiSCP.cNumOfSliceMinus1H == 0 && args.wmiSCP.uiTileY[0] > 0)
        {
            // # of horizontal slices, rounded down by half tile size.
            U32 uTileY = args.wmiSCP.uiTileY[0] * MB_HEIGHT_PIXEL;
            args.wmiSCP.cNumOfSliceMinus1H = (U32) rect.Height < (uTileY >> 1) ? 0 :
                (rect.Height + (uTileY >> 1)) / uTileY - 1;
        }
        if (args.wmiSCP.cNumOfSliceMinus1V == 0 && args.wmiSCP.uiTileX[0] > 0)
        {
            // # of vertical slices, rounded down by half tile size.
            U32 uTileX = args.wmiSCP.uiTileX[0] * MB_HEIGHT_PIXEL;
            args.wmiSCP.cNumOfSliceMinus1V = (U32) rect.Width < (uTileX >> 1) ? 0 :
                (rect.Width + (uTileX >> 1)) / uTileX - 1;
        }

        Call(pEncoder->Initialize(pEncoder, pEncodeStream, &args.wmiSCP, sizeof(args.wmiSCP)));

	    //ImageQuality  Q (BD==1)  Q (BD==8)   Q (BD==16)  Q (BD==32F) Subsample   Overlap
	    //[0.0, 0.5)    8-IQ*5     (see table) (see table) (see table) 4:2:0       2
	    //[0.5, 1.0)    8-IQ*5     (see table) (see table) (see table) 4:4:4       1
	    //[1.0, 1.0]    1          1           1           1           4:4:4       0
	
        if (args.fltImageQuality < 1.0F)
	    {
            if (!args.bOverlapSet)
            {
                // Image width must be at least 2 MB wide for subsampled chroma and two levels of overlap!
		        if (args.fltImageQuality >= 0.5F || rect.Width < 2 * MB_WIDTH_PIXEL)
			        pEncoder->WMP.wmiSCP.olOverlap = OL_ONE;
		        else
			        pEncoder->WMP.wmiSCP.olOverlap = OL_TWO;
            }

            if (!args.bColorFormatSet)
            {
		        if (args.fltImageQuality >= 0.5F || PI.uBitsPerSample > 8)
			        pEncoder->WMP.wmiSCP.cfColorFormat = YUV_444;
		        else
			        pEncoder->WMP.wmiSCP.cfColorFormat = YUV_420;
            }

		    if (PI.bdBitDepth == BD_1)
		    {
			    pEncoder->WMP.wmiSCP.uiDefaultQPIndex = (U8)(8 - 5.0F *
				    args.fltImageQuality + 0.5F);
		    }
		    else
		    {
                // remap [0.8, 0.866, 0.933, 1.0] to [0.8, 0.9, 1.0, 1.1]
                // to use 8-bit DPK QP table (0.933 == Photoshop JPEG 100)
                int qi;
                float qf;
                int* pQPs;
                if (args.fltImageQuality > 0.8f && PI.bdBitDepth == BD_8 &&
                    pEncoder->WMP.wmiSCP.cfColorFormat != YUV_420 &&
                    pEncoder->WMP.wmiSCP.cfColorFormat != YUV_422)
                    args.fltImageQuality = 0.8f + (args.fltImageQuality - 0.8f) * 1.5f;

                qi = (int) (10.f * args.fltImageQuality);
                qf = 10.f * args.fltImageQuality - (float) qi;

                pQPs =
                    (pEncoder->WMP.wmiSCP.cfColorFormat == YUV_420 ||
                     pEncoder->WMP.wmiSCP.cfColorFormat == YUV_422) ?
                        DPK_QPS_420[qi] :
                    (PI.bdBitDepth == BD_8 ? DPK_QPS_8[qi] :
                    (PI.bdBitDepth == BD_16 ? DPK_QPS_16[qi] :
                    (PI.bdBitDepth == BD_16F ? DPK_QPS_16f[qi] :
                    DPK_QPS_32f[qi])));

                pEncoder->WMP.wmiSCP.uiDefaultQPIndex = (U8) (0.5f +
                        (float) pQPs[0] * (1.f - qf) + (float) (pQPs + 6)[0] * qf);
                pEncoder->WMP.wmiSCP.uiDefaultQPIndexU = (U8) (0.5f +
                        (float) pQPs[1] * (1.f - qf) + (float) (pQPs + 6)[1] * qf);
                pEncoder->WMP.wmiSCP.uiDefaultQPIndexV = (U8) (0.5f +
                        (float) pQPs[2] * (1.f - qf) + (float) (pQPs + 6)[2] * qf);
                pEncoder->WMP.wmiSCP.uiDefaultQPIndexYHP = (U8) (0.5f +
                        (float) pQPs[3] * (1.f - qf) + (float) (pQPs + 6)[3] * qf);
                pEncoder->WMP.wmiSCP.uiDefaultQPIndexUHP = (U8) (0.5f +
                        (float) pQPs[4] * (1.f - qf) + (float) (pQPs + 6)[4] * qf);
                pEncoder->WMP.wmiSCP.uiDefaultQPIndexVHP = (U8) (0.5f +
                        (float) pQPs[5] * (1.f - qf) + (float) (pQPs + 6)[5] * qf);
		    }
        }
        else
        {
            pEncoder->WMP.wmiSCP.uiDefaultQPIndex = (U8) args.fltImageQuality;
        }

        if(pEncoder->WMP.wmiSCP.uAlphaMode == 2)
            pEncoder->WMP.wmiSCP_Alpha.uiDefaultQPIndex = args.wmiSCP.uiDefaultQPIndexAlpha;

        Call(pEncoder->SetPixelFormat(pEncoder, args.guidPixFormat));

        Call(pEncoder->SetSize(pEncoder, rect.Width, rect.Height));

        Call(pDecoder->GetResolution(pDecoder, &rX, &rY));
        Call(pEncoder->SetResolution(pEncoder, rX, rY));

        //================================
        // re-encode the input source to the output
        //
		pEncoder->WriteSource = PKImageEncode_WriteSource;
        Call(pEncoder->WriteSource(pEncoder, pConverter, &rect));

        pConverter->Release(&pConverter);
        pDecoder->Release(&pDecoder);

        //if (i + 1 == 5)
        //{
        //    break;
        //}

        // multi-frame support NYI
        //Call(pEncoder->CreateNewFrame(pEncoder, &wmiSCP, sizeof(wmiSCP)));
    }

//    Call(pEncoder->Terminate(pEncoder));
    pEncoder->Release(&pEncoder);

Cleanup:
    return (int)err;
}
