/*
packJPG v2.5k (01/22/2016)
~~~~~~~~~~~~~~~~~~~~~~~~~~

packJPG is a compression program specially designed for further
compression of JPEG images without causing any further loss. Typically
it reduces the file size of a JPEG file by 20%.


LGPL v3 license and special permissions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

All programs in this package are free software; you can redistribute
them and/or modify them under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either version 3
of the License, or (at your option) any later version.

The package is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
General Public License for more details at
http://www.gnu.org/copyleft/lgpl.html.

If the LGPL v3 license is not compatible with your software project you
might contact us and ask for a special permission to use the packJPG
library under different conditions. In any case, usage of the packJPG
algorithm under the LGPL v3 or above is highly advised and special
permissions will only be given where necessary on a case by case basis.
This offer is aimed mainly at closed source freeware developers seeking
to add PJG support to their software projects.

Copyright 2006...2014 by HTW Aalen University and Matthias Stirner.


Usage of packJPG
~~~~~~~~~~~~~~~~

JPEG files are compressed and PJG files are decompressed using this
command:

 "packJPG [file(s)]"

packJPG recognizes file types on its own and decides whether to compress
(JPG) or decompress (PJG). For unrecognized file types no action is
taken. Files are recognized by content, not by extension.

packJPG supports wildcards like "*.*" and drag and drop of multiple
files. Filenames for output files are created automatically. In default
mode, files are never overwritten. If a filename is already in use,
packJPG creates a new filename by adding underscores.

If "-" is used as a filename input from stdin is assumed and output is
written to stdout. This can be useful for example if jpegtran is to be
used as a preprocessor.

Usage examples:

 "packJPG *.pjg"
 "packJPG lena.jpg"
 "packJPG kodim??.jpg"
 "packJPG - < sail.pjg > sail.jpg"


Command line switches
~~~~~~~~~~~~~~~~~~~~~

 -ver  verify files after processing
 -v?   level of verbosity; 0,1 or 2 is allowed (default 0)
 -np   no pause after processing files
 -o    overwrite existing files
 -p    proceed on warnings
 -d    discard meta-info

By default, compression is cancelled on warnings. If warnings are
skipped by using "-p", most files with warnings can also be compressed,
but JPEG files reconstructed from PJG files might not be bitwise
identical with the original JPEG files. There won't be any loss to
image data or quality however.

Unnecessary meta information can be discarded using "-d". This reduces
compressed files' sizes. Be warned though, reconstructed files won't be
bitwise identical with the original files and meta information will be
lost forever. As with "-p" there won't be any loss to image data or
quality.

There is no known case in which a file compressed by packJPG (without
the "-p" option, see above) couldn't be reconstructed to exactly the
state it was before. If you want an additional layer of safety you can
also use the verify option "-ver". In this mode, files are compressed,
then decompressed and the decompressed file compared to the original
file. If this test doesn't pass there will be an error message and the
compressed file won't be written to the drive.

Please note that the "-ver" option should never be used in conjunction
with the "-d" and/or "-p" options. As stated above, the "-p" and "-d"
options will most likely lead to reconstructed JPG files not being
bitwise identical to the original JPG files. In turn, the verification
process may fail on various files although nothing actually went wrong.

Usage examples:

 "packJPG -v1 -o baboon.pjg"
 "packJPG -ver lena.jpg"
 "packJPG -d tiffany.jpg"
 "packJPG -p *.jpg"


Known Limitations
~~~~~~~~~~~~~~~~~

packJPG is a compression program specially for JPEG files, so it doesn't
compress other file types.

packJPG has low error tolerance. JPEG files might not work with packJPG
even if they work perfectly with other image processing software. The
command line switch "-p" can be used to increase error tolerance and
compatibility.

If you try to drag and drop to many files at once, there might be a
windowed error message about missing privileges. In that case you can
try it again with less files or consider using the command prompt.
packJPG has been tested to work perfectly with thousands of files from
the command line. This issue also happens with drag and drop in other
applications, so it might not be a limitation of packJPG but a
limitation of Windows.

Compressed PJG files are not compatible between different packJPG
versions. You will get an error message if you try to decompress PJG
files with a different version than the one used for compression. You
may download older versions of packJPG from:
http://www.elektronik.htw-aalen.de/packJPG/binaries/old/


Open source release / developer info
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The packJPG source codes is found inside the "source" subdirectory.
Additional documents aimed to developers, containing detailed
instructions on compiling the source code and using special
functionality, are included in the "packJPG" subdirectory.


History
~~~~~~~

v1.9a (04/20/2007) (non public)
 - first released version
 - only for testing purposes

v2.0  (05/28/2007) (public)
 - first public version of packJPG
 - minor improvements to overall compression
 - minor bugfixes

v2.2  (08/05/2007) (public)
 - around 40% faster compression & decompression
 - major improvements to overall compression (around 2% on average)
 - reading from stdin, writing to stdout
 - smaller executable
 - minor bugfixes
 - various minor improvements

v2.3  (09/18/2007) (public)
 - compatibility with JPEG progressive mode
 - compatibility with JPEG extended sequential mode
 - compatibility with the CMYK color space
 - compatibility with older CPUs
 - around 15% faster compression & decompression
 - new switch: [-d] (discard meta-info)
 - various bugfixes

v2.3a (11/21/2007) (public)
 - crash issue with certain images fixed
 - compatibility with packJPG v2.3 maintained

v2.3b (12/20/2007) (public)
 - some minor errors in the packJPG library fixed
 - compatibility with packJPG v2.3 maintained

v2.4 (03/24/2010) (public)
 - major improvements (1%...2%) to overall compression
 - around 10% faster compression & decompression
 - major improvements to JPG compatibility
 - size of executable reduced to ~33%
 - new switch: [-ver] (verify file after processing)
 - new switch: [-np] (no pause after processing)
 - new progress bar output mode
 - arithmetic coding routines rewritten from scratch
 - various smaller improvements to numerous to list here
 - new SFX (self extracting) archive format

v2.5 (11/11/2011) (public)
 - improvements (~0.5%) to overall compression
 - several minor bugfixes
 - major code cleanup
 - removed packJPX from the package
 - added packARC to the package
 - packJPG is now open source!

v2.5a (11/21/11) (public)
 - source code compatibility improvements (Gerhard Seelmann)
 - avoid some compiler warnings (Gerhard Seelmann)
 - source code clean up (Gerhard Seelmann)

v2.5b (01/27/12) (public)
 - further removal of redundant code
 - some fixes for the packJPG static library
 - compiler fix for Mac OS (thanks to Sergio Lopez)
 - improved compression ratio calculation
 - eliminated the need for temp files

v2.5c (04/13/12) (public)
 - various source code optimizations

v2.5d (07/03/12) (public)
 - fixed a rare bug with progressive JPEG

v2.5e (07/03/12) (public)
 - some minor source code optimizations
 - changed packJPG licensing to LGPL
 - moved packARC to a separate package

v2.5f (02/24/13) (public)
 - fixed a minor bug in the JPG parser (thanks to Stephan Busch)

v2.5g (09/14/13) (public)
 - fixed a rare crash bug with manipulated JPEG files

v2.5h (12/07/13) (public)
 - added a warning for inefficient huffman coding (thanks to Moinak Ghosh)

v2.5i (12/26/13) (public)
 - fixed possible crash with malformed JPEG (thanks to Moinak Ghosh)

v2.5j (01/15/14) (public)
 - various source code optimizations (using cppcheck)

v2.5k (01/22/16) (public)
 - Updated contact info
 - fixed a minor bug


Acknowledgements
~~~~~~~~~~~~~~~~

packJPG is the result of countless hours of research and development. It
is part of my final year project for Hochschule Aalen.

Prof. Dr. Gerhard Seelmann from Hochschule Aalen supported my
development of packJPG with his extensive knowledge in the field of data
compression. Without his advice, packJPG would not be possible.

The official developer blog for packJPG is hosted by encode.ru.

packJPG logo and icon are designed by Michael Kaufmann.


Contact
~~~~~~~

The official developer blog for packJPG:
 http://packjpg.encode.ru/

For questions and bug reports:
 packjpg (at) matthiasstirner.com


____________________________________
packJPG by Matthias Stirner, 01/2016
*/

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <ctime>
#include <memory>
#include <stdexcept>
#include <vector>

#include "packjpg.h"


#define INIT_MODEL_S(a, b, c) new model_s(a, b, c, 255)
#define INIT_MODEL_B(a, b)   new model_b(a, b, 255)

// #define USE_PLOCOI // uncomment to use loco-i predictor instead of 1DDCT predictor
// #define DEV_BUILD // uncomment to include developer functions
// #define DEV_INFOS // uncomment to include developer information

#define QUANT(cm, bp)    (cmpnfo[cm].qtable[bp])
#define MAX_V(cm, bp)    (( QUANT(cm,bp) > 0 ) ? ( ( freqmax[bp] + QUANT(cm,bp) - 1 ) /  QUANT(cm,bp) ) : 0)
// #define QUN_V(v,cm,bp)   ( ( QUANT(cm,bp) > 0 ) ? ( ( v > 0 ) ? ( v + (QUANT(cm,bp)/2) ) /  QUANT(cm,bp) : ( v - (QUANT(cm,bp)/2) ) /  QUANT(cm,bp) ) : 0 )

#define ENVLI(s, v)      (( v > 0 ) ? v : ( v - 1 ) + ( 1 << s ))
#define DEVLI(s, n)      (( n >= ( 1 << (s - 1) ) ) ? n : n + 1 - ( 1 << s ))
#define E_ENVLI(s, v)    (v - ( 1 << s ))
#define E_DEVLI(s, n)    (n + ( 1 << s ))

#define ABS(v1)         ((v1 < 0) ? -v1 : v1)
#define ABSDIFF(v1,v2)  ((v1 > v2) ? (v1 - v2) : (v2 - v1))
#define IPOS(w,v,h)     (( v * w ) + h )
#define NPOS(n1,n2,p)   (( ( p / n1 ) * n2 ) + ( p % n1 ))
#define ROUND_F(v1)     ((v1 < 0) ? (int) (v1 - 0.5) : (int) (v1 + 0.5))
#define DIV_INT(v1,v2)  ((v1 < 0) ? (v1 - (v2 >> 1)) / v2 : (v1 + (v2 >> 1)) / v2)
#define B_SHORT(v1,v2)  (( ((int) v1) << 8 ) + ((int) v2))
#define BITLEN1024P(v)  (pbitlen_0_1024[v])
#define BITLEN2048N(v)  ((pbitlen_n2048_2047 + 2048)[v])
#define CLAMPED(l,h,v)  (( v < l ) ? l : ( v > h ) ? h : v)


// special realloc with guaranteed free() of previous memory
static inline void* frealloc(void* ptr, size_t size)
{
    void* n_ptr = realloc(ptr, (size) ? size : 1);
    if (n_ptr == nullptr)
    {
        free(ptr);
    }
    return n_ptr;
}

// Initialize static members
const unsigned char packJPG::appversion = 25;
const char* packJPG::subversion   = "k";
const char* packJPG::apptitle     = "packJPG";
const char* packJPG::appname      = "packjpg";
const char* packJPG::versiondate  = "01/22/2016";
const char* packJPG::author       = "Matthias Stirner / Se";
const char  packJPG::pjg_magic[] = { 'J', 'S' };


packJPG::packJPG()
    : lib_in_type(-1),
      lib_out_type(-1),
      grbgdata(nullptr),
      hdrdata(nullptr),
      huffdata(nullptr),
      hufs(0),
      hdrs(0),
      grbs(0),
      rstp(nullptr),
      scnp(nullptr),
      rstc(0),
      scnc(0),
      rsti(0),
      padbit(-1),
      rst_err(nullptr),
      zdstdata(),
      eobxhigh(),
      eobyhigh(),
      zdstxlow(),
      zdstylow(),
      colldata(),
      freqscan(),
      zsrtscan(),
      adpt_idct_8x8(),
      adpt_idct_1x8(),
      adpt_idct_8x1(),
      cmpnfo(),
      cmpc(0),
      imgwidth(0),
      imgheight(0),
      sfhm(0),
      sfvm(0),
      mcuv(0),
      mcuh(0),
      mcuc(0),
      cs_cmpc(0),
      cs_cmp(),
      cs_from(0),
      cs_to(0),
      cs_sah(0),
      cs_sal(0),
      jpgfilename(nullptr),
      pjgfilename(nullptr),
      jpgfilesize(0),
      pjgfilesize(0),
      jpegtype(0),
      filetype(0),
      //~ str_in, // input stream
      //~ str_out,    // output stream
      errormessage(),
      errorfunction(nullptr),
      errorlevel(0),
      err_tol(1),
      disc_meta(false),
      auto_set(true),
      action(A_COMPRESS),
      nois_trs{6, 6, 6, 6},
      segm_cnt{10, 10, 10, 10}
{
}
    
packJPG::~packJPG()
{
}

const char* packJPG::pjglib_version_info(void)
{
    static char v_info[256];

    // copy version info to string
    sprintf(v_info, "--> %s library v%i.%i%s (%s) by %s <--",
            apptitle, appversion / 10, appversion % 10, subversion, 
            versiondate, author);

    return (const char*) v_info;
}

const char* packJPG::pjglib_short_name(void)
{
    static char v_name[256];

    // copy version info to string
    sprintf(v_name, "%s v%i.%i%s",
            apptitle, appversion / 10, appversion % 10, subversion);

    return (const char*) v_name;
}

/* ------------------- Begin of library only functions --------------------- */

/* -----------------------------------------------
    DLL export converter function
    ----------------------------------------------- */
bool packJPG::pjglib_convert_stream2stream(char* msg)
{
    // process in main function
    return pjglib_convert_stream2mem(nullptr, nullptr, msg);
}

/* -----------------------------------------------
    DLL export converter function
    ----------------------------------------------- */
bool packJPG::pjglib_convert_file2file(char* in, char* out, char* msg)
{
    // init streams
    pjglib_init_streams((void*) in, 0, 0, (void*) out, 0);

    // process in main function
    return pjglib_convert_stream2mem(nullptr, nullptr, msg);
}

/* -----------------------------------------------
    DLL export converter function
    ----------------------------------------------- */
bool packJPG::pjglib_convert_stream2mem(
    unsigned char** out_file, 
    unsigned int* out_size, 
    char* msg)
{
    clock_t begin, end;
    int total;
    float cr;

    // use automatic settings
    auto_set = true;

    // (re)set buffers
    reset_buffers();
    action = A_COMPRESS;

    // main compression / decompression routines
    begin = clock();

    // process one file
    process_file();

    // fetch pointer and size of output (only for memory output)
    if ((errorlevel < err_tol) && (lib_out_type == 1) &&
            (out_file != nullptr) && (out_size != nullptr))
    {
        *out_size = str_out->num_bytes_written();
        *out_file = str_out->get_c_data();
    }

    // close iostreams
    str_in.reset(nullptr);
    str_out.reset(nullptr);

    end = clock();

    // copy errormessage / remove files if error (and output is file)
    if (errorlevel >= err_tol)
    {
        if (lib_out_type == 0)
        {
            if (filetype == F_JPG)
            {
                if (file_exists(pjgfilename))
                {
                    remove(pjgfilename);
                }
            }
            else if (filetype == F_PJG)
            {
                if (file_exists(jpgfilename))
                {
                    remove(jpgfilename);
                }
            }
        }
        if (msg != nullptr)
        {
            strcpy(msg, errormessage);
        }
        return false;
    }

    // get compression info
    total = (int)((double)((end - begin) * 1000) / CLOCKS_PER_SEC);
    cr    = (jpgfilesize > 0) ? (100.0 * pjgfilesize / jpgfilesize) : 0;

    // write success message else
    if (msg != nullptr)
    {
        switch (filetype)
        {
            case F_JPG:
                sprintf(msg, "Compressed to %s (%.2f%%) in %ims",
                        pjgfilename, cr, (total >= 0) ? total : -1);
                break;
            case F_PJG:
                sprintf(msg, "Decompressed to %s (%.2f%%) in %ims",
                        jpgfilename, cr, (total >= 0) ? total : -1);
                break;
            case F_UNK:
                sprintf(msg, "Unknown filetype");
                break;
        }
    }


    return true;
}

/* -----------------------------------------------
    DLL export init input (file/mem)
    ----------------------------------------------- */
void packJPG::pjglib_init_streams(
    void* in_src, 
    int in_type, 
    int in_size, 
    void* out_dest, 
    int out_type)
{
    /* a short reminder about input/output stream types:

    if input is file
    ----------------
    in_scr -> name of input file
    in_type -> 0
    in_size -> ignore

    if input is memory
    ------------------
    in_scr -> array containg data
    in_type -> 1
    in_size -> size of data array

    if input is *FILE (f.e. stdin)
    ------------------------------
    in_src -> stream pointer
    in_type -> 2
    in_size -> ignore

    vice versa for output streams! */

    unsigned char buffer[2];

    // (re)set errorlevel
    errorfunction = nullptr;
    errorlevel = 0;
    jpgfilesize = 0;
    pjgfilesize = 0;


    switch (in_type)
    {
        case 0:
            try
            {
                str_in = std::make_unique<FileReader>((char*)in_src);
            }
            catch (const std::runtime_error&)
            {
                sprintf(errormessage, "error opening input file %s", (char*)in_src);
                errorlevel = 2;
                return;
            }
            break;
        case 1:
            str_in = std::make_unique<MemoryReader>((unsigned char*)in_src, in_size);
            break;
        case 2:
            try
            {
                str_in = std::make_unique<StreamReader>();
            }
            catch (const std::runtime_error& e)
            {
                sprintf(errormessage, e.what());
                errorlevel = 2;
                return;
            }
            break;
        default:
            sprintf(errormessage, "Invalid input type: %i", in_type);
            errorlevel = 2;
            return;
    }

    switch (out_type)
    {
        case 0:
            try
            {
                str_out = std::make_unique<FileWriter>((char*)out_dest);
            }
            catch (const std::runtime_error&)
            {
                sprintf(errormessage, "error opening output file %s", (char*)out_dest);
                errorlevel = 2;
                return;
            }
            break;
        case 1:
            str_out = std::make_unique<MemoryWriter>();
            break;
        case 2:
            try
            {
                str_out = std::make_unique<StreamWriter>();
            }
            catch (const std::runtime_error& e)
            {
                sprintf(errormessage, e.what());
                errorlevel = 2;
                return;
            }
            break;
        default:
            sprintf(errormessage, "Invalid output type: %i", out_type);
            errorlevel = 2;
            return;
    }

    // free memory from filenames if needed
    if (jpgfilename != nullptr)
    {
        free(jpgfilename);
        jpgfilename = nullptr;
    }
    if (pjgfilename != nullptr)
    {
        free(pjgfilename);
        pjgfilename = nullptr;
    }

    // check input stream
    str_in->read(buffer, 2);
    if ((buffer[0] == 0xFF) && (buffer[1] == 0xD8))
    {
        // file is JPEG
        filetype = F_JPG;
        // copy filenames
        jpgfilename = (char*) calloc((in_type == 0) ? strlen((char*) in_src) + 1 : 32, sizeof(char));
        pjgfilename = (char*) calloc((out_type == 0) ? strlen((char*) out_dest) + 1 : 32, sizeof(char));
        strcpy(jpgfilename, (in_type == 0) ? (char*) in_src   : "JPG in memory");
        strcpy(pjgfilename, (out_type == 0) ? (char*) out_dest : "PJG in memory");
    }
    else if ((buffer[0] == pjg_magic[0]) && (buffer[1] == pjg_magic[1]))
    {
        // file is PJG
        filetype = F_PJG;
        // copy filenames
        pjgfilename = (char*) calloc((in_type == 0) ? strlen((char*) in_src) + 1 : 32, sizeof(char));
        jpgfilename = (char*) calloc((out_type == 0) ? strlen((char*) out_dest) + 1 : 32, sizeof(char));
        strcpy(pjgfilename, (in_type == 0) ? (char*) in_src   : "PJG in memory");
        strcpy(jpgfilename, (out_type == 0) ? (char*) out_dest : "JPG in memory");
    }
    else
    {
        // file is neither
        filetype = F_UNK;
        sprintf(errormessage, "filetype of input stream is unknown");
        errorlevel = 2;
        return;
    }

    // store types of in-/output
    lib_in_type  = in_type;
    lib_out_type = out_type;
}

/* -------------------- End of libary only functions ----------------------- */

/* ----------------- Begin of main interface functions --------------------- */

/* -----------------------------------------------
    processes one file
    ----------------------------------------------- */
void packJPG::process_file(void)
{
    if (filetype == F_JPG)
    {
        switch (action)
        {
            case A_COMPRESS:
                read_jpeg();
                decode_jpeg();
                check_value_range();
                adapt_icos();
                predict_dc();
                calc_zdst_lists();
                pack_pjg();
                break;

            default:
                break;
        }
    }
    else if (filetype == F_PJG)
    {
        switch (action)
        {
            case A_COMPRESS:
                unpack_pjg();
                adapt_icos();
                unpredict_dc();
                recode_jpeg();
                merge_jpeg();
                break;

            default:
                break;
        }
    }
    
    // reset buffers
    reset_buffers();
}

/* -----------------------------------------------
    main-function execution routine
    ----------------------------------------------- */
void packJPG::execute(bool (*function)())
{
    if (errorlevel < err_tol)
    {
        // call function
        (*function)();

        // store errorfunction if needed
        if ((errorlevel > 0) && (errorfunction == nullptr))
        {
            errorfunction = function;
        }
    }
}

/* ----------------------- End of main interface functions ----------------- */


/* ----------------------- Begin of main functions ------------------------- */

/* -----------------------------------------------
    set each variable to its initial value
    ----------------------------------------------- */
bool packJPG::reset_buffers(void)
{
    int cmp, bpos;
    int i;

    // -- free buffers --

    // free buffers & set pointers nullptr
    if (hdrdata  != nullptr)
    {
        free(hdrdata);
    }
    if (huffdata != nullptr)
    {
        free(huffdata);
    }
    if (grbgdata != nullptr)
    {
        free(grbgdata);
    }
    if (rst_err  != nullptr)
    {
        free(rst_err);
    }
    if (rstp     != nullptr)
    {
        free(rstp);
    }
    if (scnp     != nullptr)
    {
        free(scnp);
    }
    hdrdata   = nullptr;
    huffdata  = nullptr;
    grbgdata  = nullptr;
    rst_err   = nullptr;
    rstp      = nullptr;
    scnp      = nullptr;

    // free image arrays
    for (cmp = 0; cmp < 4; cmp++)
    {
        if (zdstdata[cmp] != nullptr)
        {
            free(zdstdata[cmp]);
        }
        if (eobxhigh[cmp] != nullptr)
        {
            free(eobxhigh[cmp]);
        }
        if (eobyhigh[cmp] != nullptr)
        {
            free(eobyhigh[cmp]);
        }
        if (zdstxlow[cmp] != nullptr)
        {
            free(zdstxlow[cmp]);
        }
        if (zdstylow[cmp] != nullptr)
        {
            free(zdstylow[cmp]);
        }
        zdstdata[cmp] = nullptr;
        eobxhigh[cmp] = nullptr;
        eobyhigh[cmp] = nullptr;
        zdstxlow[cmp] = nullptr;
        zdstylow[cmp] = nullptr;
        freqscan[cmp] = (unsigned char*) stdscan;

        for (bpos = 0; bpos < 64; bpos++)
        {
            if (colldata[cmp][bpos] != nullptr)
            {
                free(colldata[cmp][bpos]);
            }
            colldata[cmp][bpos] = nullptr;
        }
    }

    // -- set variables --

    // preset componentinfo
    for (cmp = 0; cmp < 4; cmp++)
    {
        cmpnfo[cmp].sfv = -1;
        cmpnfo[cmp].sfh = -1;
        cmpnfo[cmp].mbs = -1;
        cmpnfo[cmp].bcv = -1;
        cmpnfo[cmp].bch = -1;
        cmpnfo[cmp].bc  = -1;
        cmpnfo[cmp].ncv = -1;
        cmpnfo[cmp].nch = -1;
        cmpnfo[cmp].nc  = -1;
        cmpnfo[cmp].sid = -1;
        cmpnfo[cmp].jid = -1;
        cmpnfo[cmp].qtable = nullptr;
        cmpnfo[cmp].huffdc = -1;
        cmpnfo[cmp].huffac = -1;
    }

    // preset imgwidth / imgheight / component count
    imgwidth  = 0;
    imgheight = 0;
    cmpc      = 0;

    // preset mcu info variables / restart interval
    sfhm      = 0;
    sfvm      = 0;
    mcuc      = 0;
    mcuh      = 0;
    mcuv      = 0;
    rsti      = 0;

    // reset quantization / huffman tables
    for (i = 0; i < 4; i++)
    {
        htset[0][i] = 0;
        htset[1][i] = 0;
        for (bpos = 0; bpos < 64; bpos++)
        {
            qtables[i][bpos] = 0;
        }
    }

    // preset jpegtype
    jpegtype  = 0;

    // reset padbit
    padbit = -1;

    return true;
}

/* -----------------------------------------------
    Read in header & image data
    ----------------------------------------------- */
bool packJPG::read_jpeg(void)
{
    unsigned char* segment = nullptr; // storage for current segment
    unsigned int   ssize = 1024; // current size of segment array
    unsigned char  type = 0x00; // type of current marker segment
    unsigned int   len  = 0; // length of current marker segment
    unsigned int   crst = 0; // current rst marker counter
    unsigned int   cpos = 0; // rst marker counter
    unsigned char  tmp;

    MemoryWriter* huffw;
    MemoryWriter* hdrw;
    MemoryWriter* grbgw;

    // preset count of scans
    scnc = 0;

    // start headerwriter
    hdrw = new MemoryWriter();
    hdrs = 0; // size of header data, start with 0

    // start huffman writer
    huffw = new MemoryWriter();
    hufs  = 0; // size of image data, start with 0

    // alloc memory for segment data first
    segment = (unsigned char*) calloc(ssize, sizeof(char));
    if (segment == nullptr)
    {
        sprintf(errormessage, MEM_ERRMSG);
        errorlevel = 2;
        return false;
    }

    // JPEG reader loop
    while (true)
    {
        if (type == 0xDA)     // if last marker was sos
        {
            // switch to huffman data reading mode
            cpos = 0;
            crst = 0;
            while (true)
            {
                // read byte from imagedata
                if (str_in->read_byte(&tmp) == 0)
                {
                    break;
                }

                // non-0xFF loop
                if (tmp != 0xFF)
                {
                    crst = 0;
                    while (tmp != 0xFF)
                    {
                        huffw->write_byte(tmp);
                        if (str_in->read_byte(&tmp) == 0)
                        {
                            break;
                        }
                    }
                }

                // treatment of 0xFF
                if (tmp == 0xFF)
                {
                    if (str_in->read_byte(&tmp) == 0)
                    {
                        break;    // read next byte & check
                    }
                    if (tmp == 0x00)
                    {
                        crst = 0;
                        // no zeroes needed -> ignore 0x00. write 0xFF
                        huffw->write_byte(0xFF);
                    }
                    else if (tmp == 0xD0 + (cpos % 8))       // restart marker
                    {
                        // increment rst counters
                        cpos++;
                        crst++;
                    }
                    else   // in all other cases leave it to the header parser routines
                    {
                        // store number of wrongly set rst markers
                        if (crst > 0)
                        {
                            if (rst_err == nullptr)
                            {
                                rst_err = (unsigned char*) calloc(scnc + 1, sizeof(char));
                                if (rst_err == nullptr)
                                {
                                    sprintf(errormessage, MEM_ERRMSG);
                                    errorlevel = 2;
                                    return false;
                                }
                            }
                        }
                        if (rst_err != nullptr)
                        {
                            // realloc and set only if needed
                            rst_err = (unsigned char*) frealloc(rst_err, (scnc + 1) * sizeof(char));
                            if (rst_err == nullptr)
                            {
                                sprintf(errormessage, MEM_ERRMSG);
                                errorlevel = 2;
                                return false;
                            }
                            if (crst > 255)
                            {
                                sprintf(errormessage, "Severe false use of RST markers (%i)", (int) crst);
                                errorlevel = 1;
                                crst = 255;
                            }
                            rst_err[scnc] = crst;
                        }
                        // end of current scan
                        scnc++;
                        // on with the header parser routines
                        segment[0] = 0xFF;
                        segment[1] = tmp;
                        break;
                    }
                }
                else
                {
                    // otherwise this means end-of-file, so break out
                    break;
                }
            }
        }
        else
        {
            // read in next marker
            if (str_in->read(segment, 2) != 2)
            {
                break;
            }
            if (segment[0] != 0xFF)
            {
                // ugly fix for incorrect marker segment sizes
                sprintf(errormessage, "size mismatch in marker segment FF %2X", type);
                errorlevel = 2;
                if (type == 0xFE)     //  if last marker was COM try again
                {
                    if (str_in->read(segment, 2) != 2)
                    {
                        break;
                    }
                    if (segment[0] == 0xFF)
                    {
                        errorlevel = 1;
                    }
                }
                if (errorlevel == 2)
                {
                    delete (hdrw);
                    delete (huffw);
                    free(segment);
                    return false;
                }
            }
        }

        // read segment type
        type = segment[1];

        // if EOI is encountered make a quick exit
        if (type == 0xD9)
        {
            // get pointer for header data & size
            hdrdata  = hdrw->get_c_data();
            hdrs     = hdrw->num_bytes_written();
            // get pointer for huffman data & size
            huffdata = huffw->get_c_data();
            hufs     = huffw->num_bytes_written();
            // everything is done here now
            break;
        }

        // read in next segments' length and check it
        if (str_in->read(segment + 2, 2) != 2)
        {
            break;
        }
        len = 2 + B_SHORT(segment[2], segment[3]);
        if (len < 4)
        {
            break;
        }

        // realloc segment data if needed
        if (ssize < len)
        {
            segment = (unsigned char*) frealloc(segment, len);
            if (segment == nullptr)
            {
                sprintf(errormessage, MEM_ERRMSG);
                errorlevel = 2;
                delete (hdrw);
                delete (huffw);
                return false;
            }
            ssize = len;
        }

        // read rest of segment, store back in header writer
        if (str_in->read((segment + 4), (len - 4)) !=
                (unsigned short)(len - 4))
        {
            break;
        }
        hdrw->write(segment, len);
    }
    // JPEG reader loop end

    // free writers
    delete hdrw;
    delete huffw;

    // check if everything went OK
    if ((hdrs == 0) || (hufs == 0))
    {
        sprintf(errormessage, "unexpected end of data encountered");
        errorlevel = 2;
        return false;
    }

    // store garbage after EOI if needed
    grbs = str_in->read_byte(&tmp);
    if (grbs > 0)
    {
        grbgw = new MemoryWriter();
        grbgw->write_byte(tmp);
        while (true)
        {
            len = str_in->read(segment, ssize);
            if (len == 0)
            {
                break;
            }
            grbgw->write(segment, len);
        }
        grbgdata = grbgw->get_c_data();
        grbs     = grbgw->num_bytes_written();
        delete grbgw;
    }

    // free segment
    free(segment);

    // get filesize
    jpgfilesize = str_in->get_size();

    // parse header for image info
    if (!jpg_setup_imginfo())
    {
        return false;
    }


    return true;
}

/* -----------------------------------------------
    Merges header & image data to jpeg
    ----------------------------------------------- */
bool packJPG::merge_jpeg(void)
{
    unsigned char SOI[2] = {0xFF, 0xD8}; // SOI segment
    unsigned char EOI[2] = {0xFF, 0xD9}; // EOI segment
    unsigned char mrk = 0xFF; // marker start
    unsigned char stv = 0x00; // 0xFF stuff value
    unsigned char rst = 0xD0; // restart marker

    unsigned char  type = 0x00; // type of current marker segment
    unsigned int   len  = 0; // length of current marker segment
    unsigned int   hpos = 0; // current position in header
    unsigned int   ipos = 0; // current position in imagedata
    unsigned int   rpos = 0; // current restart marker position
    unsigned int   cpos = 0; // in scan corrected rst marker position
    unsigned int   scan = 1; // number of current scan
    unsigned int   tmp; // temporary storage variable

    // write SOI
    str_out->write(SOI, 2);

    // JPEG writing loop
    while (true)
    {
        // store current header position
        tmp = hpos;

        // seek till start-of-scan
        for (type = 0x00; type != 0xDA;)
        {
            if ((int) hpos >= hdrs)
            {
                break;
            }
            type = hdrdata[hpos + 1];
            len = 2 + B_SHORT(hdrdata[hpos + 2], hdrdata[hpos + 3]);
            hpos += len;
        }

        // write header data to file
        str_out->write(hdrdata + tmp, (hpos - tmp));

        // get out if last marker segment type was not SOS
        if (type != 0xDA)
        {
            break;
        }

        // (re)set corrected rst pos
        cpos = 0;

        // write & expand huffman coded image data
        for (ipos = scnp[scan - 1]; ipos < scnp[scan]; ipos++)
        {
            // write current byte
            str_out->write_byte(huffdata[ipos]);
            // check current byte, stuff if needed
            if (huffdata[ipos] == 0xFF)
            {
                str_out->write_byte(stv);
            }
            // insert restart markers if needed
            if (rstp != nullptr)
            {
                if (ipos == rstp[rpos])
                {
                    rst = 0xD0 + (cpos % 8);
                    str_out->write_byte(mrk);
                    str_out->write_byte(rst);
                    rpos++;
                    cpos++;
                }
            }
        }
        // insert false rst markers at end if needed
        if (rst_err != nullptr)
        {
            while (rst_err[scan - 1] > 0)
            {
                rst = 0xD0 + (cpos % 8);
                str_out->write_byte(mrk);
                str_out->write_byte(rst);
                cpos++;
                rst_err[scan - 1]--;
            }
        }

        // proceed with next scan
        scan++;
    }

    // write EOI
    str_out->write(EOI, 2);

    // write garbage if needed
    if (grbs > 0)
    {
        str_out->write(grbgdata, grbs);
    }

    // errormessage if write error
    if (str_out->error())
    {
        sprintf(errormessage, "write error, possibly drive is full");
        errorlevel = 2;
        return false;
    }

    // get filesize
    jpgfilesize = str_out->num_bytes_written();

    return true;
}

/* -----------------------------------------------
    JPEG decoding routine
    ----------------------------------------------- */
bool packJPG::decode_jpeg(void)
{
    BitReader* huffr; // bitwise reader for image data

    unsigned char  type = 0x00; // type of current marker segment
    unsigned int   len  = 0; // length of current marker segment
    unsigned int   hpos = 0; // current position in header

    int lastdc[4]; // last dc for each component
    short block[64]; // store block for coeffs
    int peobrun; // previous eobrun
    int eobrun; // run of eobs
    int rstw; // restart wait counter

    int cmp, bpos, dpos;
    int mcu, sub, csc;
    int eob, sta;

    // open huffman coded image data for input in BitReader
    huffr = new BitReader(huffdata, hufs);

    // preset count of scans
    scnc = 0;

    // JPEG decompression loop
    while (true)
    {
        // seek till start-of-scan, parse only DHT, DRI and SOS
        for (type = 0x00; type != 0xDA;)
        {
            if ((int) hpos >= hdrs)
            {
                break;
            }
            type = hdrdata[hpos + 1];
            len = 2 + B_SHORT(hdrdata[hpos + 2], hdrdata[hpos + 3]);
            if ((type == 0xC4) || (type == 0xDA) || (type == 0xDD))
            {
                if (!jpg_parse_jfif(type, len, &(hdrdata[hpos])))
                {
                    return false;
                }
            }
            hpos += len;
        }

        // get out if last marker segment type was not SOS
        if (type != 0xDA)
        {
            break;
        }

        // check if huffman tables are available
        for (csc = 0; csc < cs_cmpc; csc++)
        {
            cmp = cs_cmp[csc];
            if (((cs_sal == 0) && (htset[0][cmpnfo[cmp].huffdc] == 0)) ||
                    ((cs_sah >  0) && (htset[1][cmpnfo[cmp].huffac] == 0)))
            {
                sprintf(errormessage, "huffman table missing in scan%i", scnc);
                delete huffr;
                errorlevel = 2;
                return false;
            }
        }

        // intial variables set for decoding
        cmp  = cs_cmp[0];
        csc  = 0;
        mcu  = 0;
        sub  = 0;
        dpos = 0;

        // JPEG imagedata decoding routines
        while (true)
        {
            // (re)set last DCs for diff coding
            lastdc[0] = 0;
            lastdc[1] = 0;
            lastdc[2] = 0;
            lastdc[3] = 0;

            // (re)set status
            eob = 0;
            sta = 0;

            // (re)set eobrun
            eobrun  = 0;
            peobrun = 0;

            // (re)set rst wait counter
            rstw = rsti;

            // decoding for interleaved data
            if (cs_cmpc > 1)
            {
                if (jpegtype == 1)
                {
                    // ---> sequential interleaved decoding <---
                    while (sta == 0)
                    {
                        // decode block
                        eob = jpg_decode_block_seq(huffr,
                                                   &(htrees[0][cmpnfo[cmp].huffdc]),
                                                   &(htrees[1][cmpnfo[cmp].huffdc]),
                                                   block);

                        // check for non optimal coding
                        if ((eob > 1) && (block[eob - 1] == 0))
                        {
                            sprintf(errormessage, "reconstruction of inefficient coding not supported");
                            errorlevel = 1;
                        }

                        // fix dc
                        block[0] += lastdc[cmp];
                        lastdc[cmp] = block[0];

                        // copy to colldata
                        for (bpos = 0; bpos < eob; bpos++)
                        {
                            colldata[cmp][bpos][dpos] = block[bpos];
                        }

                        // check for errors, proceed if no error encountered
                        if (eob < 0)
                        {
                            sta = -1;
                        }
                        else
                        {
                            sta = jpg_next_mcupos(&mcu, &cmp, &csc, &sub, &dpos, &rstw);
                        }
                    }
                }
                else if (cs_sah == 0)
                {
                    // ---> progressive interleaved DC decoding <---
                    // ---> succesive approximation first stage <---
                    while (sta == 0)
                    {
                        sta = jpg_decode_dc_prg_fs(huffr,
                                                   &(htrees[0][cmpnfo[cmp].huffdc]),
                                                   block);

                        // fix dc for diff coding
                        colldata[cmp][0][dpos] = block[0] + lastdc[cmp];
                        lastdc[cmp] = colldata[cmp][0][dpos];

                        // bitshift for succesive approximation
                        colldata[cmp][0][dpos] <<= cs_sal;

                        // next mcupos if no error happened
                        if (sta != -1)
                        {
                            sta = jpg_next_mcupos(&mcu, &cmp, &csc, &sub, &dpos, &rstw);
                        }
                    }
                }
                else
                {
                    // ---> progressive interleaved DC decoding <---
                    // ---> succesive approximation later stage <---
                    while (sta == 0)
                    {
                        // decode next bit
                        sta = jpg_decode_dc_prg_sa(huffr, block);

                        // shift in next bit
                        colldata[cmp][0][dpos] += block[0] << cs_sal;

                        // next mcupos if no error happened
                        if (sta != -1)
                        {
                            sta = jpg_next_mcupos(&mcu, &cmp, &csc, &sub, &dpos, &rstw);
                        }
                    }
                }
            }
            else // decoding for non interleaved data
            {
                if (jpegtype == 1)
                {
                    // ---> sequential non interleaved decoding <---
                    while (sta == 0)
                    {
                        // decode block
                        eob = jpg_decode_block_seq(huffr,
                                                   &(htrees[0][cmpnfo[cmp].huffdc]),
                                                   &(htrees[1][cmpnfo[cmp].huffdc]),
                                                   block);

                        // check for non optimal coding
                        if ((eob > 1) && (block[eob - 1] == 0))
                        {
                            sprintf(errormessage, "reconstruction of inefficient coding not supported");
                            errorlevel = 1;
                        }

                        // fix dc
                        block[0] += lastdc[cmp];
                        lastdc[cmp] = block[0];

                        // copy to colldata
                        for (bpos = 0; bpos < eob; bpos++)
                        {
                            colldata[cmp][bpos][dpos] = block[bpos];
                        }

                        // check for errors, proceed if no error encountered
                        if (eob < 0)
                        {
                            sta = -1;
                        }
                        else
                        {
                            sta = jpg_next_mcuposn(&cmp, &dpos, &rstw);
                        }
                    }
                }
                else if (cs_to == 0)
                {
                    if (cs_sah == 0)
                    {
                        // ---> progressive non interleaved DC decoding <---
                        // ---> succesive approximation first stage <---
                        while (sta == 0)
                        {
                            sta = jpg_decode_dc_prg_fs(huffr,
                                                       &(htrees[0][cmpnfo[cmp].huffdc]),
                                                       block);

                            // fix dc for diff coding
                            colldata[cmp][0][dpos] = block[0] + lastdc[cmp];
                            lastdc[cmp] = colldata[cmp][0][dpos];

                            // bitshift for succesive approximation
                            colldata[cmp][0][dpos] <<= cs_sal;

                            // check for errors, increment dpos otherwise
                            if (sta != -1)
                            {
                                sta = jpg_next_mcuposn(&cmp, &dpos, &rstw);
                            }
                        }
                    }
                    else
                    {
                        // ---> progressive non interleaved DC decoding <---
                        // ---> succesive approximation later stage <---
                        while (sta == 0)
                        {
                            // decode next bit
                            sta = jpg_decode_dc_prg_sa(huffr, block);

                            // shift in next bit
                            colldata[cmp][0][dpos] += block[0] << cs_sal;

                            // check for errors, increment dpos otherwise
                            if (sta != -1)
                            {
                                sta = jpg_next_mcuposn(&cmp, &dpos, &rstw);
                            }
                        }
                    }
                }
                else
                {
                    if (cs_sah == 0)
                    {
                        // ---> progressive non interleaved AC decoding <---
                        // ---> succesive approximation first stage <---
                        while (sta == 0)
                        {
                            if (eobrun == 0)
                            {
                                // decode block
                                eob = jpg_decode_ac_prg_fs(huffr,
                                                           &(htrees[1][cmpnfo[cmp].huffac]),
                                                           block, &eobrun, cs_from, cs_to);

                                if (eobrun > 0)
                                {
                                    // check for non optimal coding
                                    if ((eob == cs_from)  && (peobrun > 0) &&
                                            (peobrun <  hcodes[1][cmpnfo[cmp].huffac].max_eobrun - 1))
                                    {
                                        sprintf(errormessage,
                                                "reconstruction of inefficient coding not supported");
                                        errorlevel = 1;
                                    }
                                    peobrun = eobrun;
                                    eobrun--;
                                }
                                else
                                {
                                    peobrun = 0;
                                }

                                // copy to colldata
                                for (bpos = cs_from; bpos < eob; bpos++)
                                {
                                    colldata[cmp][bpos][dpos] = block[bpos] << cs_sal;
                                }
                            }
                            else
                            {
                                eobrun--;
                            }

                            // check for errors
                            if (eob < 0)
                            {
                                sta = -1;
                            }
                            else
                            {
                                sta = jpg_skip_eobrun(&cmp, &dpos, &rstw, &eobrun);
                            }

                            // proceed only if no error encountered
                            if (sta == 0)
                            {
                                sta = jpg_next_mcuposn(&cmp, &dpos, &rstw);
                            }
                        }
                    }
                    else
                    {
                        // ---> progressive non interleaved AC decoding <---
                        // ---> succesive approximation later stage <---
                        while (sta == 0)
                        {
                            // copy from colldata
                            for (bpos = cs_from; bpos <= cs_to; bpos++)
                            {
                                block[bpos] = colldata[cmp][bpos][dpos];
                            }

                            if (eobrun == 0)
                            {
                                // decode block (long routine)
                                eob = jpg_decode_ac_prg_sa(huffr,
                                                           &(htrees[1][cmpnfo[cmp].huffac]),
                                                           block, &eobrun, cs_from, cs_to);

                                if (eobrun > 0)
                                {
                                    // check for non optimal coding
                                    if ((eob == cs_from) && (peobrun > 0) &&
                                            (peobrun < hcodes[1][cmpnfo[cmp].huffac].max_eobrun - 1))
                                    {
                                        sprintf(errormessage,
                                                "reconstruction of inefficient coding not supported");
                                        errorlevel = 1;
                                    }

                                    // store eobrun
                                    peobrun = eobrun;
                                    eobrun--;
                                }
                                else
                                {
                                    peobrun = 0;
                                }
                            }
                            else
                            {
                                // decode block (short routine)
                                eob = jpg_decode_eobrun_sa(huffr,
                                                           block, &eobrun, cs_from, cs_to);
                                eobrun--;
                            }

                            // copy back to colldata
                            for (bpos = cs_from; bpos <= cs_to; bpos++)
                            {
                                colldata[cmp][bpos][dpos] += block[bpos] << cs_sal;
                            }

                            // proceed only if no error encountered
                            if (eob < 0)
                            {
                                sta = -1;
                            }
                            else
                            {
                                sta = jpg_next_mcuposn(&cmp, &dpos, &rstw);
                            }
                        }
                    }
                }
            }

            // unpad huffman reader / check padbit
            if (padbit != -1)
            {
                if (padbit != huffr->unpad(padbit))
                {
                    sprintf(errormessage, "inconsistent use of padbits");
                    padbit = 1;
                    errorlevel = 1;
                }
            }
            else
            {
                padbit = huffr->unpad(padbit);
            }

            // evaluate status
            if (sta == -1)     // status -1 means error
            {
                sprintf(errormessage, "decode error in scan%i / mcu%i",
                        scnc, (cs_cmpc > 1) ? mcu : dpos);
                delete huffr;
                errorlevel = 2;
                return false;
            }
            else if (sta == 2)     // status 2/3 means done
            {
                scnc++; // increment scan counter
                break; // leave decoding loop, everything is done here
            }
            // else if ( sta == 1 ); // status 1 means restart - so stay in the loop
        }
    }

    // check for missing data
    if (huffr->peof() > 0)
    {
        sprintf(errormessage, "coded image data truncated / too short");
        errorlevel = 1;
    }

    // check for surplus data
    if (!huffr->eof())
    {
        sprintf(errormessage, "surplus data found after coded image data");
        errorlevel = 1;
    }

    // clean up
    delete huffr;

    return true;
}

/* -----------------------------------------------
    JPEG encoding routine
    ----------------------------------------------- */
bool packJPG::recode_jpeg(void)
{
    BitWriter*  huffw; // bitwise writer for image data

    unsigned char  type = 0x00; // type of current marker segment
    unsigned int   len  = 0; // length of current marker segment
    unsigned int   hpos = 0; // current position in header

    int lastdc[4]; // last dc for each component0
    short block[64]; // store block for coeffs
    int eobrun; // run of eobs
    int rstw; // restart wait counter

    int cmp, bpos, dpos;
    int mcu, sub, csc;
    int eob, sta;
    int tmp;

    // open huffman coded image data in BitWriter
    huffw = new BitWriter(padbit);

    // init storage writer
    std::vector<std::uint8_t> storw; // Storage for correction bits.

    // preset count of scans and restarts
    scnc = 0;
    rstc = 0;

    // JPEG decompression loop
    while (true)
    {
        // seek till start-of-scan, parse only DHT, DRI and SOS
        for (type = 0x00; type != 0xDA;)
        {
            if ((int) hpos >= hdrs)
            {
                break;
            }
            type = hdrdata[hpos + 1];
            len = 2 + B_SHORT(hdrdata[hpos + 2], hdrdata[hpos + 3]);
            if ((type == 0xC4) || (type == 0xDA) || (type == 0xDD))
            {
                if (!jpg_parse_jfif(type, len, &(hdrdata[hpos])))
                {
                    return false;
                }
                hpos += len;
            }
            else
            {
                hpos += len;
                continue;
            }
        }

        // get out if last marker segment type was not SOS
        if (type != 0xDA)
        {
            break;
        }

        // (re)alloc scan positons array
        if (scnp == nullptr)
        {
            scnp = (unsigned int*) calloc(scnc + 2, sizeof(int));
        }
        else
        {
            scnp = (unsigned int*) frealloc(scnp, (scnc + 2) * sizeof(int));
        }
        if (scnp == nullptr)
        {
            sprintf(errormessage, MEM_ERRMSG);
            errorlevel = 2;
            return false;
        }

        // (re)alloc restart marker positons array if needed
        if (rsti > 0)
        {
            tmp = rstc + ((cs_cmpc > 1) ?
                          (mcuc / rsti) : (cmpnfo[cs_cmp[0]].bc / rsti));
            if (rstp == nullptr)
            {
                rstp = (unsigned int*) calloc(tmp + 1, sizeof(int));
            }
            else
            {
                rstp = (unsigned int*) frealloc(rstp, (tmp + 1) * sizeof(int));
            }
            if (rstp == nullptr)
            {
                sprintf(errormessage, MEM_ERRMSG);
                errorlevel = 2;
                return false;
            }
        }

        // intial variables set for encoding
        cmp  = cs_cmp[0];
        csc  = 0;
        mcu  = 0;
        sub  = 0;
        dpos = 0;

        // store scan position
        scnp[scnc] = huffw->num_bytes_written();

        // JPEG imagedata encoding routines
        while (true)
        {
            // (re)set last DCs for diff coding
            lastdc[0] = 0;
            lastdc[1] = 0;
            lastdc[2] = 0;
            lastdc[3] = 0;

            // (re)set status
            sta = 0;

            // (re)set eobrun
            eobrun = 0;

            // (re)set rst wait counter
            rstw = rsti;

            // encoding for interleaved data
            if (cs_cmpc > 1)
            {
                if (jpegtype == 1)
                {
                    // ---> sequential interleaved encoding <---
                    while (sta == 0)
                    {
                        // copy from colldata
                        for (bpos = 0; bpos < 64; bpos++)
                        {
                            block[bpos] = colldata[cmp][bpos][dpos];
                        }

                        // diff coding for dc
                        block[0] -= lastdc[cmp];
                        lastdc[cmp] = colldata[cmp][0][dpos];

                        // encode block
                        eob = jpg_encode_block_seq(huffw,
                                                   &(hcodes[0][cmpnfo[cmp].huffdc]),
                                                   &(hcodes[1][cmpnfo[cmp].huffac]),
                                                   block);

                        // check for errors, proceed if no error encountered
                        if (eob < 0)
                        {
                            sta = -1;
                        }
                        else
                        {
                            sta = jpg_next_mcupos(&mcu, &cmp, &csc, &sub, &dpos, &rstw);
                        }
                    }
                }
                else if (cs_sah == 0)
                {
                    // ---> progressive interleaved DC encoding <---
                    // ---> succesive approximation first stage <---
                    while (sta == 0)
                    {
                        // diff coding & bitshifting for dc
                        tmp = colldata[cmp][0][dpos] >> cs_sal;
                        block[0] = tmp - lastdc[cmp];
                        lastdc[cmp] = tmp;

                        // encode dc
                        sta = jpg_encode_dc_prg_fs(huffw,
                                                   &(hcodes[0][cmpnfo[cmp].huffdc]),
                                                   block);

                        // next mcupos if no error happened
                        if (sta != -1)
                        {
                            sta = jpg_next_mcupos(&mcu, &cmp, &csc, &sub, &dpos, &rstw);
                        }
                    }
                }
                else
                {
                    // ---> progressive interleaved DC encoding <---
                    // ---> succesive approximation later stage <---
                    while (sta == 0)
                    {
                        // fetch bit from current bitplane
                        block[0] = BITN(colldata[cmp][0][dpos], cs_sal);

                        // encode dc correction bit
                        sta = jpg_encode_dc_prg_sa(huffw, block);

                        // next mcupos if no error happened
                        if (sta != -1)
                        {
                            sta = jpg_next_mcupos(&mcu, &cmp, &csc, &sub, &dpos, &rstw);
                        }
                    }
                }
            }
            else // encoding for non interleaved data
            {
                if (jpegtype == 1)
                {
                    // ---> sequential non interleaved encoding <---
                    while (sta == 0)
                    {
                        // copy from colldata
                        for (bpos = 0; bpos < 64; bpos++)
                        {
                            block[bpos] = colldata[cmp][bpos][dpos];
                        }

                        // diff coding for dc
                        block[0] -= lastdc[cmp];
                        lastdc[cmp] = colldata[cmp][0][dpos];

                        // encode block
                        eob = jpg_encode_block_seq(huffw,
                                                   &(hcodes[0][cmpnfo[cmp].huffdc]),
                                                   &(hcodes[1][cmpnfo[cmp].huffac]),
                                                   block);

                        // check for errors, proceed if no error encountered
                        if (eob < 0)
                        {
                            sta = -1;
                        }
                        else
                        {
                            sta = jpg_next_mcuposn(&cmp, &dpos, &rstw);
                        }
                    }
                }
                else if (cs_to == 0)
                {
                    if (cs_sah == 0)
                    {
                        // ---> progressive non interleaved DC encoding <---
                        // ---> succesive approximation first stage <---
                        while (sta == 0)
                        {
                            // diff coding & bitshifting for dc
                            tmp = colldata[cmp][0][dpos] >> cs_sal;
                            block[0] = tmp - lastdc[cmp];
                            lastdc[cmp] = tmp;

                            // encode dc
                            sta = jpg_encode_dc_prg_fs(huffw,
                                                       &(hcodes[0][cmpnfo[cmp].huffdc]),
                                                       block);

                            // check for errors, increment dpos otherwise
                            if (sta != -1)
                            {
                                sta = jpg_next_mcuposn(&cmp, &dpos, &rstw);
                            }
                        }
                    }
                    else
                    {
                        // ---> progressive non interleaved DC encoding <---
                        // ---> succesive approximation later stage <---
                        while (sta == 0)
                        {
                            // fetch bit from current bitplane
                            block[0] = BITN(colldata[cmp][0][dpos], cs_sal);

                            // encode dc correction bit
                            sta = jpg_encode_dc_prg_sa(huffw, block);

                            // next mcupos if no error happened
                            if (sta != -1)
                            {
                                sta = jpg_next_mcuposn(&cmp, &dpos, &rstw);
                            }
                        }
                    }
                }
                else
                {
                    if (cs_sah == 0)
                    {
                        // ---> progressive non interleaved AC encoding <---
                        // ---> succesive approximation first stage <---
                        while (sta == 0)
                        {
                            // copy from colldata
                            for (bpos = cs_from; bpos <= cs_to; bpos++)
                                block[bpos] =
                                    FDIV2(colldata[cmp][bpos][dpos], cs_sal);

                            // encode block
                            eob = jpg_encode_ac_prg_fs(huffw,
                                                       &(hcodes[1][cmpnfo[cmp].huffac]),
                                                       block, &eobrun, cs_from, cs_to);

                            // check for errors, proceed if no error encountered
                            if (eob < 0)
                            {
                                sta = -1;
                            }
                            else
                            {
                                sta = jpg_next_mcuposn(&cmp, &dpos, &rstw);
                            }
                        }

                        // encode remaining eobrun
                        jpg_encode_eobrun(huffw,
                                          &(hcodes[1][cmpnfo[cmp].huffac]),
                                          &eobrun);
                    }
                    else
                    {
                        // ---> progressive non interleaved AC encoding <---
                        // ---> succesive approximation later stage <---
                        while (sta == 0)
                        {
                            // copy from colldata
                            for (bpos = cs_from; bpos <= cs_to; bpos++)
                                block[bpos] =
                                    FDIV2(colldata[cmp][bpos][dpos], cs_sal);

                            // encode block
                            eob = jpg_encode_ac_prg_sa(huffw, storw,
                                                       &(hcodes[1][cmpnfo[cmp].huffac]),
                                                       block, &eobrun, cs_from, cs_to);

                            // check for errors, proceed if no error encountered
                            if (eob < 0)
                            {
                                sta = -1;
                            }
                            else
                            {
                                sta = jpg_next_mcuposn(&cmp, &dpos, &rstw);
                            }
                        }

                        // encode remaining eobrun
                        jpg_encode_eobrun(huffw,
                                          &(hcodes[1][cmpnfo[cmp].huffac]),
                                          &eobrun);

                        // encode remaining correction bits
                        jpg_encode_crbits(huffw, storw);
                    }
                }
            }

            // pad huffman writer
            huffw->pad();

            // evaluate status
            if (sta == -1)     // status -1 means error
            {
                sprintf(errormessage, "encode error in scan%i / mcu%i",
                        scnc, (cs_cmpc > 1) ? mcu : dpos);
                delete huffw;
                errorlevel = 2;
                return false;
            }
            else if (sta == 2)     // status 2 means done
            {
                scnc++; // increment scan counter
                break; // leave decoding loop, everything is done here
            }
            else if (sta == 1)     // status 1 means restart
            {
                if (rsti > 0)   // store rstp & stay in the loop
                {
                    rstp[rstc++] = huffw->num_bytes_written() - 1;
                }
            }
        }
    }

    // get data into huffdata
    huffdata = huffw->get_c_bytes();
    hufs = huffw->num_bytes_written();
    delete huffw;

    // store last scan & restart positions
    scnp[scnc] = hufs;
    if (rstp != nullptr)
    {
        rstp[rstc] = hufs;
    }

    return true;
}

/* -----------------------------------------------
    adapt ICOS tables for quantizer tables
    ----------------------------------------------- */
bool packJPG::adapt_icos(void)
{
    unsigned short quant[64]; // local copy of quantization
    int ipos;
    int cmp;

    for (cmp = 0; cmp < cmpc; cmp++)
    {
        // make a local copy of the quantization values, check
        for (ipos = 0; ipos < 64; ipos++)
        {
            quant[ipos] = QUANT(cmp, zigzag[ipos]);
            if (quant[ipos] >= 2048)   // if this is true, it can be safely assumed (for 8 bit JPEG), that all coefficients are zero
            {
                quant[ipos] = 0;
            }
        }
        // adapt idct 8x8 table
        for (ipos = 0; ipos < 64 * 64; ipos++)
        {
            adpt_idct_8x8[cmp][ipos] = icos_idct_8x8[ipos] * quant[ipos % 64];
        }
        // adapt idct 1x8 table
        for (ipos = 0; ipos < 8 * 8; ipos++)
        {
            adpt_idct_1x8[cmp][ipos] = icos_idct_1x8[ipos] * quant[(ipos % 8) * 8];
        }
        // adapt idct 8x1 table
        for (ipos = 0; ipos < 8 * 8; ipos++)
        {
            adpt_idct_8x1[cmp][ipos] = icos_idct_1x8[ipos] * quant[ipos % 8];
        }
    }

    return true;
}

/* -----------------------------------------------
    filter DC coefficients
    ----------------------------------------------- */
bool packJPG::predict_dc(void)
{
    signed short* coef;
    int absmaxp;
    int absmaxn;
    int corr_f;
    int cmp, dpos;

    // apply prediction, store prediction error instead of DC
    for (cmp = 0; cmp < cmpc; cmp++)
    {
        absmaxp = MAX_V(cmp, 0);
        absmaxn = -absmaxp;
        corr_f = ((2 * absmaxp) + 1);

        for (dpos = cmpnfo[cmp].bc - 1; dpos > 0; dpos--)
        {
            coef = &(colldata[cmp][0][dpos]);
#if defined(USE_PLOCOI)
            (*coef) -= dc_coll_predictor(cmp, dpos);   // loco-i predictor
#else
            (*coef) -= dc_1ddct_predictor(cmp, dpos);   // 1d dct
#endif

            // fix range
            if ((*coef) > absmaxp)
            {
                (*coef) -= corr_f;
            }
            else if ((*coef) < absmaxn)
            {
                (*coef) += corr_f;
            }
        }
    }

    return true;
}

/* -----------------------------------------------
    unpredict DC coefficients
    ----------------------------------------------- */
bool packJPG::unpredict_dc(void)
{
    signed short* coef;
    int absmaxp;
    int absmaxn;
    int corr_f;
    int cmp, dpos;

    // remove prediction, store DC instead of prediction error
    for (cmp = 0; cmp < cmpc; cmp++)
    {
        absmaxp = MAX_V(cmp, 0);
        absmaxn = -absmaxp;
        corr_f = ((2 * absmaxp) + 1);

        for (dpos = 1; dpos < cmpnfo[cmp].bc; dpos++)
        {
            coef = &(colldata[cmp][0][dpos]);
#if defined( USE_PLOCOI )
            (*coef) += dc_coll_predictor(cmp, dpos);   // loco-i predictor
#else
            (*coef) += dc_1ddct_predictor(cmp, dpos);   // 1d dct predictor
#endif

            // fix range
            if ((*coef) > absmaxp)
            {
                (*coef) -= corr_f;
            }
            else if ((*coef) < absmaxn)
            {
                (*coef) += corr_f;
            }
        }
    }

    return true;
}

/* -----------------------------------------------
    checks range of values, error if out of bounds
    ----------------------------------------------- */
bool packJPG::check_value_range(void)
{
    int absmax;
    int cmp, bpos, dpos;

    // out of range should never happen with unmodified JPEGs
    for (cmp = 0; cmp < cmpc; cmp++)
        for (bpos = 0; bpos < 64; bpos++)
        {
            absmax = MAX_V(cmp, bpos);
            for (dpos = 0; dpos < cmpnfo[cmp].bc; dpos++)
                if ((colldata[cmp][bpos][dpos] > absmax) ||
                        (colldata[cmp][bpos][dpos] < -absmax))
                {
                    sprintf(errormessage, "value out of range error: cmp%i, frq%i, val %i, max %i",
                            cmp, bpos, colldata[cmp][bpos][dpos], absmax);
                    errorlevel = 2;
                    return false;
                }
        }

    return true;
}

/* -----------------------------------------------
    calculate zero distribution lists
    ----------------------------------------------- */
bool packJPG::calc_zdst_lists(void)
{
    int cmp, bpos, dpos;
    int b_x, b_y;

    // this functions counts, for each DCT block, the number of non-zero coefficients
    for (cmp = 0; cmp < cmpc; cmp++)
    {
        // preset zdstlist
        memset(zdstdata[cmp], 0, cmpnfo[cmp].bc * sizeof(char));

        // calculate # on non-zeroes per block (separately for lower 7x7 block & first row/collumn)
        for (bpos = 1; bpos < 64; bpos++)
        {
            b_x = unzigzag[bpos] % 8;
            b_y = unzigzag[bpos] / 8;
            if (b_x == 0)
            {
                for (dpos = 0; dpos < cmpnfo[cmp].bc; dpos++)
                    if (colldata[cmp][bpos][dpos] != 0)
                    {
                        zdstylow[cmp][dpos]++;
                    }
            }
            else if (b_y == 0)
            {
                for (dpos = 0; dpos < cmpnfo[cmp].bc; dpos++)
                    if (colldata[cmp][bpos][dpos] != 0)
                    {
                        zdstxlow[cmp][dpos]++;
                    }
            }
            else
            {
                for (dpos = 0; dpos < cmpnfo[cmp].bc; dpos++)
                    if (colldata[cmp][bpos][dpos] != 0)
                    {
                        zdstdata[cmp][dpos]++;
                    }
            }
        }
    }

    return true;
}

/* -----------------------------------------------
    packs all parts to compressed pjg
    ----------------------------------------------- */
bool packJPG::pack_pjg(void)
{
    unsigned char hcode;
    int cmp;
#if defined(DEV_INFOS)
    int dev_size = 0;
#endif

    // PJG-Header
    str_out->write(reinterpret_cast<const unsigned char*>(pjg_magic), 2);

    // store settings if not auto
    if (!auto_set)
    {
        hcode = 0x00;
        str_out->write_byte(hcode);
        str_out->write(nois_trs, 4);
        str_out->write(segm_cnt, 4);
    }

    // store version number
    hcode = appversion;
    str_out->write_byte(hcode);


    // init arithmetic compression
    auto encoder = new ArithmeticEncoder(*str_out);

    // discard meta information from header if option set
    if (disc_meta)
        if (!jpg_rebuild_header())
        {
            return false;
        }
    // optimize header for compression
    if (!pjg_optimize_header())
    {
        return false;
    }
    // set padbit to 1 if previously unset
    if (padbit == -1)
    {
        padbit = 1;
    }

    // encode JPG header
#if !defined(DEV_INFOS)
    if (!pjg_encode_generic(encoder, hdrdata, hdrs))
    {
        return false;
    }
#else
    dev_size = str_out->getpos();
    if (!pjg_encode_generic(encoder, hdrdata, hdrs))
    {
        return false;
    }
    dev_size_hdr += str_out->getpos() - dev_size;
#endif
    // store padbit (padbit can't be retrieved from the header)
    if (!pjg_encode_bit(encoder, padbit))
    {
        return false;
    }
    // also encode one bit to signal false/correct use of RST markers
    if (!pjg_encode_bit(encoder, (rst_err == nullptr) ? 0 : 1))
    {
        return false;
    }
    // encode # of false set RST markers per scan
    if (rst_err != nullptr)
        if (!pjg_encode_generic(encoder, rst_err, scnc))
        {
            return false;
        }

    // encode actual components data
    for (cmp = 0; cmp < cmpc; cmp++)
    {
#if !defined(DEV_INFOS)
        // encode frequency scan ('zero-sort-scan')
        if (!pjg_encode_zstscan(encoder, cmp))
        {
            return false;
        }
        // encode zero-distribution-lists for higher (7x7) ACs
        if (!pjg_encode_zdst_high(encoder, cmp))
        {
            return false;
        }
        // encode coefficients for higher (7x7) ACs
        if (!pjg_encode_ac_high(encoder, cmp))
        {
            return false;
        }
        // encode zero-distribution-lists for lower ACs
        if (!pjg_encode_zdst_low(encoder, cmp))
        {
            return false;
        }
        // encode coefficients for first row / collumn ACs
        if (!pjg_encode_ac_low(encoder, cmp))
        {
            return false;
        }
        // encode coefficients for DC
        if (!pjg_encode_dc(encoder, cmp))
        {
            return false;
        }
#else
        dev_size = str_out->getpos();
        // encode frequency scan ('zero-sort-scan')
        if (!pjg_encode_zstscan(encoder, cmp))
        {
            return false;
        }
        dev_size_zsr[cmp] += str_out->getpos() - dev_size;
        dev_size = str_out->getpos();
        // encode zero-distribution-lists for higher (7x7) ACs
        if (!pjg_encode_zdst_high(encoder, cmp))
        {
            return false;
        }
        dev_size_zdh[cmp] += str_out->getpos() - dev_size;
        dev_size = str_out->getpos();
        // encode coefficients for higher (7x7) ACs
        if (!pjg_encode_ac_high(encoder, cmp))
        {
            return false;
        }
        dev_size_ach[cmp] += str_out->getpos() - dev_size;
        dev_size = str_out->getpos();
        // encode zero-distribution-lists for lower ACs
        if (!pjg_encode_zdst_low(encoder, cmp))
        {
            return false;
        }
        dev_size_zdl[cmp] += str_out->getpos() - dev_size;
        dev_size = str_out->getpos();
        // encode coefficients for first row / collumn ACs
        if (!pjg_encode_ac_low(encoder, cmp))
        {
            return false;
        }
        dev_size_acl[cmp] += str_out->getpos() - dev_size;
        dev_size = str_out->getpos();
        // encode coefficients for DC
        if (!pjg_encode_dc(encoder, cmp))
        {
            return false;
        }
        dev_size_dc[cmp] += str_out->getpos() - dev_size;
        dev_size_cmp[cmp] =
            dev_size_zsr[cmp] + dev_size_zdh[cmp] + dev_size_zdl[cmp] +
            dev_size_ach[cmp] + dev_size_acl[cmp] + dev_size_dc[cmp];
#endif
    }

    // encode checkbit for garbage (0 if no garbage, 1 if garbage has to be coded)
    if (!pjg_encode_bit(encoder, (grbs > 0) ? 1 : 0))
    {
        return false;
    }
    // encode garbage data only if needed
    if (grbs > 0)
        if (!pjg_encode_generic(encoder, grbgdata, grbs))
        {
            return false;
        }

    // finalize arithmetic compression
    delete encoder;

    // errormessage if write error
    if (str_out->error())
    {
        sprintf(errormessage, "write error, possibly drive is full");
        errorlevel = 2;
        return false;
    }

    // get filesize
    pjgfilesize = str_out->num_bytes_written();

    return true;
}

/* -----------------------------------------------
    unpacks compressed pjg to colldata
    ----------------------------------------------- */
bool packJPG::unpack_pjg(void)
{
    unsigned char hcode;
    unsigned char cb;
    int cmp;

    // check header codes ( maybe position in other function ? )
    while (true)
    {
        str_in->read_byte(&hcode);
        if (hcode == 0x00)
        {
            // retrieve compression settings from file
            str_in->read(nois_trs, 4);
            str_in->read(segm_cnt, 4);
            auto_set = false;
        }
        else if (hcode >= 0x14)
        {
            // compare version number
            if (hcode != appversion)
            {
                sprintf(errormessage, "incompatible file, use %s v%i.%i",
                        appname, hcode / 10, hcode % 10);
                errorlevel = 2;
                return false;
            }
            else
            {
                break;
            }
        }
        else
        {
            sprintf(errormessage, "unknown header code, use newer version of %s", appname);
            errorlevel = 2;
            return false;
        }
    }

    // init arithmetic compression
    auto decoder = new ArithmeticDecoder(*str_in);

    // decode JPG header
    if (!pjg_decode_generic(decoder, &hdrdata, &hdrs))
    {
        return false;
    }
    // retrieve padbit from stream
    if (!pjg_decode_bit(decoder, &cb))
    {
        return false;
    }
    padbit = cb;
    // decode one bit that signals false /correct use of RST markers
    if (!pjg_decode_bit(decoder, &cb))
    {
        return false;
    }
    // decode # of false set RST markers per scan only if available
    if (cb == 1)
        if (!pjg_decode_generic(decoder, &rst_err, nullptr))
        {
            return false;
        }

    // undo header optimizations
    if (!pjg_unoptimize_header())
    {
        return false;
    }
    // discard meta information from header if option set
    if (disc_meta)
        if (!jpg_rebuild_header())
        {
            return false;
        }
    // parse header for image-info
    if (!jpg_setup_imginfo())
    {
        return false;
    }

    // decode actual components data
    for (cmp = 0; cmp < cmpc; cmp++)
    {
        // decode frequency scan ('zero-sort-scan')
        if (!pjg_decode_zstscan(decoder, cmp))
        {
            return false;
        }
        // decode zero-distribution-lists for higher (7x7) ACs
        if (!pjg_decode_zdst_high(decoder, cmp))
        {
            return false;
        }
        // decode coefficients for higher (7x7) ACs
        if (!pjg_decode_ac_high(decoder, cmp))
        {
            return false;
        }
        // decode zero-distribution-lists for lower ACs
        if (!pjg_decode_zdst_low(decoder, cmp))
        {
            return false;
        }
        // decode coefficients for first row / collumn ACs
        if (!pjg_decode_ac_low(decoder, cmp))
        {
            return false;
        }
        // decode coefficients for DC
        if (!pjg_decode_dc(decoder, cmp))
        {
            return false;
        }
    }

    // retrieve checkbit for garbage (0 if no garbage, 1 if garbage has to be coded)
    if (!pjg_decode_bit(decoder, &cb))
    {
        return false;
    }

    // decode garbage data only if available
    if (cb == 0)
    {
        grbs = 0;
    }
    else if (!pjg_decode_generic(decoder, &grbgdata, &grbs))
    {
        return false;
    }

    // finalize arithmetic compression
    delete decoder;

    // get filesize
    pjgfilesize = str_in->get_size();

    return true;
}

/* ------------------------ End of main functions -------------------------- */


/* ----------------------- Begin of JPEG specific functions ---------------- */

/* -----------------------------------------------
    Parses header for imageinfo
    ----------------------------------------------- */
bool packJPG::jpg_setup_imginfo(void)
{
    unsigned char  type = 0x00; // type of current marker segment
    unsigned int   len  = 0; // length of current marker segment
    unsigned int   hpos = 0; // position in header

    int cmp, bpos;
    int i;

    // header parser loop
    while ((int) hpos < hdrs)
    {
        type = hdrdata[hpos + 1];
        len = 2 + B_SHORT(hdrdata[hpos + 2], hdrdata[hpos + 3]);
        // do not parse DHT & DRI
        if ((type != 0xDA) && (type != 0xC4) && (type != 0xDD))
        {
            if (!jpg_parse_jfif(type, len, &(hdrdata[hpos])))
            {
                return false;
            }
        }
        hpos += len;
    }

    // check if information is complete
    if (cmpc == 0)
    {
        sprintf(errormessage, "header contains incomplete information");
        errorlevel = 2;
        return false;
    }
    for (cmp = 0; cmp < cmpc; cmp++)
    {
        if ((cmpnfo[cmp].sfv == 0) ||
                (cmpnfo[cmp].sfh == 0) ||
                (cmpnfo[cmp].qtable == nullptr) ||
                (cmpnfo[cmp].qtable[0] == 0) ||
                (jpegtype == 0))
        {
            sprintf(errormessage, "header information is incomplete");
            errorlevel = 2;
            return false;
        }
    }

    // do all remaining component info calculations
    for (cmp = 0; cmp < cmpc; cmp++)
    {
        if (cmpnfo[cmp].sfh > sfhm)
        {
            sfhm = cmpnfo[cmp].sfh;
        }
        if (cmpnfo[cmp].sfv > sfvm)
        {
            sfvm = cmpnfo[cmp].sfv;
        }
    }
    mcuv = (int) ceil((float) imgheight / (float)(8 * sfhm));
    mcuh = (int) ceil((float) imgwidth  / (float)(8 * sfvm));
    mcuc  = mcuv * mcuh;
    for (cmp = 0; cmp < cmpc; cmp++)
    {
        cmpnfo[cmp].mbs = cmpnfo[cmp].sfv * cmpnfo[cmp].sfh;
        cmpnfo[cmp].bcv = mcuv * cmpnfo[cmp].sfh;
        cmpnfo[cmp].bch = mcuh * cmpnfo[cmp].sfv;
        cmpnfo[cmp].bc  = cmpnfo[cmp].bcv * cmpnfo[cmp].bch;
        cmpnfo[cmp].ncv = (int) ceil((float) imgheight *
                                       ((float) cmpnfo[cmp].sfh / (8.0 * sfhm)));
        cmpnfo[cmp].nch = (int) ceil((float) imgwidth *
                                       ((float) cmpnfo[cmp].sfv / (8.0 * sfvm)));
        cmpnfo[cmp].nc  = cmpnfo[cmp].ncv * cmpnfo[cmp].nch;
    }

    // decide components' statistical ids
    if (cmpc <= 3)
    {
        for (cmp = 0; cmp < cmpc; cmp++)
        {
            cmpnfo[cmp].sid = cmp;
        }
    }
    else
    {
        for (cmp = 0; cmp < cmpc; cmp++)
        {
            cmpnfo[cmp].sid = 0;
        }
    }

    // alloc memory for further operations
    for (cmp = 0; cmp < cmpc; cmp++)
    {
        // alloc memory for colls
        for (bpos = 0; bpos < 64; bpos++)
        {
            colldata[cmp][bpos] = (short int*) calloc(cmpnfo[cmp].bc, sizeof(short));
            if (colldata[cmp][bpos] == nullptr)
            {
                sprintf(errormessage, MEM_ERRMSG);
                errorlevel = 2;
                return false;
            }
        }

        // alloc memory for zdstlist / eob x / eob y
        zdstdata[cmp] = (unsigned char*) calloc(cmpnfo[cmp].bc, sizeof(char));
        eobxhigh[cmp] = (unsigned char*) calloc(cmpnfo[cmp].bc, sizeof(char));
        eobyhigh[cmp] = (unsigned char*) calloc(cmpnfo[cmp].bc, sizeof(char));
        zdstxlow[cmp] = (unsigned char*) calloc(cmpnfo[cmp].bc, sizeof(char));
        zdstylow[cmp] = (unsigned char*) calloc(cmpnfo[cmp].bc, sizeof(char));
        if ((zdstdata[cmp] == nullptr) ||
                (eobxhigh[cmp] == nullptr) || (eobyhigh[cmp] == nullptr) ||
                (zdstxlow[cmp] == nullptr) || (zdstylow[cmp] == nullptr))
        {
            sprintf(errormessage, MEM_ERRMSG);
            errorlevel = 2;
            return false;
        }
    }

    // also decide automatic settings here
    if (auto_set)
    {
        for (cmp = 0; cmp < cmpc; cmp++)
        {
            for (i = 0;
                    conf_sets[i][cmpnfo[cmp].sid] > (unsigned int) cmpnfo[cmp].bc;
                    i++);
            segm_cnt[cmp] = conf_segm[i][cmpnfo[cmp].sid];
            nois_trs[cmp] = conf_ntrs[i][cmpnfo[cmp].sid];
        }
    }

    return true;
}

/* -----------------------------------------------
    Parse routines for JFIF segments
    ----------------------------------------------- */
bool packJPG::jpg_parse_jfif(
    unsigned char type, 
    unsigned int len, 
    unsigned char* segment)
{
    unsigned int hpos = 4; // current position in segment, start after segment header
    int lval, rval; // temporary variables
    int skip;
    int cmp;
    int i;

    switch (type)
    {
        case 0xC4: // DHT segment
            // build huffman trees & codes
            while (hpos < len)
            {
                lval = LBITS(segment[hpos], 4);
                rval = RBITS(segment[hpos], 4);
                if (((lval < 0) || (lval >= 2)) || ((rval < 0) || (rval >= 4)))
                {
                    break;
                }

                hpos++;
                // build huffman codes & trees
                jpg_build_huffcodes(&(segment[hpos + 0]), &(segment[hpos + 16]),
                                    &(hcodes[lval][rval]), &(htrees[lval][rval]));
                htset[lval][rval] = 1;

                skip = 16;
                for (i = 0; i < 16; i++)
                {
                    skip += (int) segment[hpos + i];
                }
                hpos += skip;
            }

            if (hpos != len)
            {
                // if we get here, something went wrong
                sprintf(errormessage, "size mismatch in dht marker");
                errorlevel = 2;
                return false;
            }
            return true;

        case 0xDB: // DQT segment
            // copy quantization tables to internal memory
            while (hpos < len)
            {
                lval = LBITS(segment[hpos], 4);
                rval = RBITS(segment[hpos], 4);
                if ((lval < 0) || (lval >= 2))
                {
                    break;
                }
                if ((rval < 0) || (rval >= 4))
                {
                    break;
                }
                hpos++;
                if (lval == 0)     // 8 bit precision
                {
                    for (i = 0; i < 64; i++)
                    {
                        qtables[rval][i] = (unsigned short) segment[hpos + i];
                        if (qtables[rval][i] == 0)
                        {
                            break;
                        }
                    }
                    hpos += 64;
                }
                else   // 16 bit precision
                {
                    for (i = 0; i < 64; i++)
                    {
                        qtables[rval][i] =
                            B_SHORT(segment[hpos + (2*i)], segment[hpos + (2*i) + 1]);
                        if (qtables[rval][i] == 0)
                        {
                            break;
                        }
                    }
                    hpos += 128;
                }
            }

            if (hpos != len)
            {
                // if we get here, something went wrong
                sprintf(errormessage, "size mismatch in dqt marker");
                errorlevel = 2;
                return false;
            }
            return true;

        case 0xDD: // DRI segment
            // define restart interval
            rsti = B_SHORT(segment[hpos], segment[hpos + 1]);
            return true;

        case 0xDA: // SOS segment
            // prepare next scan
            cs_cmpc = segment[hpos];
            if (cs_cmpc > cmpc)
            {
                sprintf(errormessage, "%i components in scan, only %i are allowed",
                        cs_cmpc, cmpc);
                errorlevel = 2;
                return false;
            }
            hpos++;
            for (i = 0; i < cs_cmpc; i++)
            {
                for (cmp = 0; (segment[hpos] != cmpnfo[cmp].jid) && (cmp < cmpc); cmp++);
                if (cmp == cmpc)
                {
                    sprintf(errormessage, "component id mismatch in start-of-scan");
                    errorlevel = 2;
                    return false;
                }
                cs_cmp[i] = cmp;
                cmpnfo[cmp].huffdc = LBITS(segment[hpos + 1], 4);
                cmpnfo[cmp].huffac = RBITS(segment[hpos + 1], 4);
                if ((cmpnfo[cmp].huffdc < 0) || (cmpnfo[cmp].huffdc >= 4) ||
                        (cmpnfo[cmp].huffac < 0) || (cmpnfo[cmp].huffac >= 4))
                {
                    sprintf(errormessage, "huffman table number mismatch");
                    errorlevel = 2;
                    return false;
                }
                hpos += 2;
            }
            cs_from = segment[hpos + 0];
            cs_to   = segment[hpos + 1];
            cs_sah  = LBITS(segment[hpos + 2], 4);
            cs_sal  = RBITS(segment[hpos + 2], 4);
            // check for errors
            if ((cs_from > cs_to) || (cs_from > 63) || (cs_to > 63))
            {
                sprintf(errormessage, "spectral selection parameter out of range");
                errorlevel = 2;
                return false;
            }
            if ((cs_sah >= 12) || (cs_sal >= 12))
            {
                sprintf(errormessage, "successive approximation parameter out of range");
                errorlevel = 2;
                return false;
            }
            return true;

        case 0xC0: // SOF0 segment
        // coding process: baseline DCT

        case 0xC1: // SOF1 segment
        // coding process: extended sequential DCT

        case 0xC2: // SOF2 segment
            // coding process: progressive DCT

            // set JPEG coding type
            if (type == 0xC2)
            {
                jpegtype = 2;
            }
            else
            {
                jpegtype = 1;
            }

            // check data precision, only 8 bit is allowed
            lval = segment[hpos];
            if (lval != 8)
            {
                sprintf(errormessage, "%i bit data precision is not supported", lval);
                errorlevel = 2;
                return false;
            }

            // image size, height & component count
            imgheight = B_SHORT(segment[hpos + 1], segment[hpos + 2]);
            imgwidth  = B_SHORT(segment[hpos + 3], segment[hpos + 4]);
            cmpc      = segment[hpos + 5];
            if ((imgwidth == 0) || (imgheight == 0))
            {
                sprintf(errormessage, "resolution is %ix%i, possible malformed JPEG", imgwidth, imgheight);
                errorlevel = 2;
                return false;
            }
            if (cmpc > 4)
            {
                sprintf(errormessage, "image has %i components, max 4 are supported", cmpc);
                errorlevel = 2;
                return false;
            }

            hpos += 6;
            // components contained in image
            for (cmp = 0; cmp < cmpc; cmp++)
            {
                cmpnfo[cmp].jid = segment[hpos];
                cmpnfo[cmp].sfv = LBITS(segment[hpos + 1], 4);
                cmpnfo[cmp].sfh = RBITS(segment[hpos + 1], 4);
                cmpnfo[cmp].qtable = qtables[segment[hpos + 2]];
                hpos += 3;
            }

            return true;

        case 0xC3: // SOF3 segment
            // coding process: lossless sequential
            sprintf(errormessage, "sof3 marker found, image is coded lossless");
            errorlevel = 2;
            return false;

        case 0xC5: // SOF5 segment
            // coding process: differential sequential DCT
            sprintf(errormessage, "sof5 marker found, image is coded diff. sequential");
            errorlevel = 2;
            return false;

        case 0xC6: // SOF6 segment
            // coding process: differential progressive DCT
            sprintf(errormessage, "sof6 marker found, image is coded diff. progressive");
            errorlevel = 2;
            return false;

        case 0xC7: // SOF7 segment
            // coding process: differential lossless
            sprintf(errormessage, "sof7 marker found, image is coded diff. lossless");
            errorlevel = 2;
            return false;

        case 0xC9: // SOF9 segment
            // coding process: arithmetic extended sequential DCT
            sprintf(errormessage, "sof9 marker found, image is coded arithm. sequential");
            errorlevel = 2;
            return false;

        case 0xCA: // SOF10 segment
            // coding process: arithmetic extended sequential DCT
            sprintf(errormessage, "sof10 marker found, image is coded arithm. progressive");
            errorlevel = 2;
            return false;

        case 0xCB: // SOF11 segment
            // coding process: arithmetic extended sequential DCT
            sprintf(errormessage, "sof11 marker found, image is coded arithm. lossless");
            errorlevel = 2;
            return false;

        case 0xCD: // SOF13 segment
            // coding process: arithmetic differntial sequential DCT
            sprintf(errormessage, "sof13 marker found, image is coded arithm. diff. sequential");
            errorlevel = 2;
            return false;

        case 0xCE: // SOF14 segment
            // coding process: arithmetic differential progressive DCT
            sprintf(errormessage, "sof14 marker found, image is coded arithm. diff. progressive");
            errorlevel = 2;
            return false;

        case 0xCF: // SOF15 segment
            // coding process: arithmetic differntial lossless
            sprintf(errormessage, "sof15 marker found, image is coded arithm. diff. lossless");
            errorlevel = 2;
            return false;

        case 0xE0: // APP0 segment
        case 0xE1: // APP1 segment
        case 0xE2: // APP2 segment
        case 0xE3: // APP3 segment
        case 0xE4: // APP4 segment
        case 0xE5: // APP5 segment
        case 0xE6: // APP6 segment
        case 0xE7: // APP7 segment
        case 0xE8: // APP8 segment
        case 0xE9: // APP9 segment
        case 0xEA: // APP10 segment
        case 0xEB: // APP11 segment
        case 0xEC: // APP12 segment
        case 0xED: // APP13 segment
        case 0xEE: // APP14 segment
        case 0xEF: // APP15 segment
        case 0xFE: // COM segment
            // do nothing - return true
            return true;

        case 0xD0: // RST0 segment
        case 0xD1: // RST1 segment
        case 0xD2: // RST2 segment
        case 0xD3: // RST3 segment
        case 0xD4: // RST4 segment
        case 0xD5: // RST5 segment
        case 0xD6: // RST6 segment
        case 0xD7: // RST7 segment
            // return errormessage - RST is out of place here
            sprintf(errormessage, "rst marker found out of place");
            errorlevel = 2;
            return false;

        case 0xD8: // SOI segment
            // return errormessage - start-of-image is out of place here
            sprintf(errormessage, "soi marker found out of place");
            errorlevel = 2;
            return false;

        case 0xD9: // EOI segment
            // return errormessage - end-of-image is out of place here
            sprintf(errormessage, "eoi marker found out of place");
            errorlevel = 2;
            return false;

        default: // unknown marker segment
            // return warning
            sprintf(errormessage, "unknown marker found: FF %2X", type);
            errorlevel = 1;
            return true;
    }
}

/* -----------------------------------------------
    JFIF header rebuilding routine
    ----------------------------------------------- */
bool packJPG::jpg_rebuild_header(void)
{
    unsigned char  type = 0x00; // type of current marker segment
    unsigned int   len  = 0; // length of current marker segment
    unsigned int   hpos = 0; // position in header

    // start headerwriter
    MemoryWriter* hdrw = new MemoryWriter(); // new header writer

    // header parser loop
    while ((int) hpos < hdrs)
    {
        type = hdrdata[hpos + 1];
        len = 2 + B_SHORT(hdrdata[hpos + 2], hdrdata[hpos + 3]);
        // discard any unneeded meta info
        if ((type == 0xDA) || (type == 0xC4) || (type == 0xDB) ||
                (type == 0xC0) || (type == 0xC1) || (type == 0xC2) ||
                (type == 0xDD))
        {
            hdrw->write(&(hdrdata[hpos]), len);
        }
        hpos += len;
    }

    // replace current header with the new one
    free(hdrdata);
    hdrdata = hdrw->get_c_data();
    hdrs    = hdrw->num_bytes_written();
    delete hdrw;

    return true;
}

/* -----------------------------------------------
    sequential block decoding routine
    ----------------------------------------------- */
int packJPG::jpg_decode_block_seq(
    BitReader* huffr, 
    huffTree* dctree, 
    huffTree* actree, 
    short* block)
{
    unsigned short n;
    unsigned char  s;
    unsigned char  z;
    int eob = 64;
    int bpos;
    int hc;

    // decode dc
    hc = jpg_next_huffcode(huffr, dctree);
    if (hc < 0)
    {
        return -1;    // return error
    }
    else
    {
        s = (unsigned char) hc;
    }
    n = huffr->read(s);
    block[0] = DEVLI(s, n);

    // decode ac
    for (bpos = 1; bpos < 64;)
    {
        // decode next
        hc = jpg_next_huffcode(huffr, actree);
        // analyse code
        if (hc > 0)
        {
            z = LBITS(hc, 4);
            s = RBITS(hc, 4);
            n = huffr->read(s);
            if ((z + bpos) >= 64)
            {
                return -1;    // run is to long
            }
            while (z > 0)     // write zeroes
            {
                block[bpos++] = 0;
                z--;
            }
            block[bpos++] = (short) DEVLI(s, n);     // decode cvli
        }
        else if (hc == 0)     // EOB
        {
            eob = bpos;
            // while( bpos < 64 ) // fill remaining block with zeroes
            //  block[bpos++] = 0;
            break;
        }
        else
        {
            return -1; // return error
        }
    }

    // return position of eob
    return eob;
}

/* -----------------------------------------------
    sequential block encoding routine
    ----------------------------------------------- */
int packJPG::jpg_encode_block_seq(
    BitWriter* huffw, 
    huffCodes* dctbl, 
    huffCodes* actbl, 
    short* block)
{
    unsigned short n;
    unsigned char  s;
    unsigned char  z;
    int bpos;
    int hc;

    // encode DC
    s = BITLEN2048N(block[0]);
    n = ENVLI(s, block[0]);
    huffw->write_u16(dctbl->cval[s], dctbl->clen[s]);
    huffw->write_u16(n, s);

    // encode AC
    z = 0;
    for (bpos = 1; bpos < 64; bpos++)
    {
        // if nonzero is encountered
        if (block[bpos] != 0)
        {
            // write remaining zeroes
            while (z >= 16)
            {
                huffw->write_u16(actbl->cval[0xF0], actbl->clen[0xF0]);
                z -= 16;
            }
            // vli encode
            s = BITLEN2048N(block[bpos]);
            n = ENVLI(s, block[bpos]);
            hc = ((z << 4) + s);
            // write to huffman writer
            huffw->write_u16(actbl->cval[hc], actbl->clen[hc]);
            huffw->write_u16(n, s);
            // reset zeroes
            z = 0;
        }
        else   // increment zero counter
        {
            z++;
        }
    }
    // write eob if needed
    if (z > 0)
    {
        huffw->write_u16(actbl->cval[0x00], actbl->clen[0x00]);
    }

    return 64 - z;
}

/* -----------------------------------------------
    progressive DC decoding routine
    ----------------------------------------------- */
int packJPG::jpg_decode_dc_prg_fs(
    BitReader* huffr, 
    huffTree* dctree, 
    short* block)
{
    unsigned short n;
    unsigned char  s;
    int hc;

    // decode dc
    hc = jpg_next_huffcode(huffr, dctree);
    if (hc < 0)
    {
        return -1;    // return error
    }
    else
    {
        s = (unsigned char) hc;
    }
    n = huffr->read(s);
    block[0] = DEVLI(s, n);

    // return 0 if everything is ok
    return 0;
}

/* -----------------------------------------------
    progressive DC encoding routine
    ----------------------------------------------- */
int packJPG::jpg_encode_dc_prg_fs(
    BitWriter* huffw, 
    huffCodes* dctbl, 
    short* block)
{
    unsigned short n;
    unsigned char  s;

    // encode DC
    s = BITLEN2048N(block[0]);
    n = ENVLI(s, block[0]);
    huffw->write_u16(dctbl->cval[s], dctbl->clen[s]);
    huffw->write_u16(n, s);

    // return 0 if everything is ok
    return 0;
}

/* -----------------------------------------------
    progressive AC decoding routine
    ----------------------------------------------- */
int packJPG::jpg_decode_ac_prg_fs(
    BitReader* huffr, 
    huffTree* actree, 
    short* block, 
    int* eobrun, 
    int from, 
    int to)
{
    unsigned short n;
    unsigned char  s;
    unsigned char  z;
    int eob = to + 1;
    int bpos;
    int hc;
    int l;
    int r;

    // decode ac
    for (bpos = from; bpos <= to;)
    {
        // decode next
        hc = jpg_next_huffcode(huffr, actree);
        if (hc < 0)
        {
            return -1;
        }
        l = LBITS(hc, 4);
        r = RBITS(hc, 4);
        // analyse code
        if ((l == 15) || (r > 0))         // decode run/level combination
        {
            z = l;
            s = r;
            n = huffr->read(s);
            if ((z + bpos) > to)
            {
                return -1;    // run is to long
            }
            while (z > 0)     // write zeroes
            {
                block[bpos++] = 0;
                z--;
            }
            block[bpos++] = (short) DEVLI(s, n);     // decode cvli
        }
        else   // decode eobrun
        {
            eob = bpos;
            s = l;
            n = huffr->read(s);
            (*eobrun) = E_DEVLI(s, n);
            // while( bpos <= to ) // fill remaining block with zeroes
            //  block[bpos++] = 0;
            break;
        }
    }

    // return position of eob
    return eob;
}

/* -----------------------------------------------
    progressive AC encoding routine
    ----------------------------------------------- */
int packJPG::jpg_encode_ac_prg_fs(
    BitWriter* huffw, 
    huffCodes* actbl, 
    short* block, 
    int* eobrun, 
    int from, 
    int to)
{
    unsigned short n;
    unsigned char  s;
    unsigned char  z;
    int bpos;
    int hc;

    // encode AC
    z = 0;
    for (bpos = from; bpos <= to; bpos++)
    {
        // if nonzero is encountered
        if (block[bpos] != 0)
        {
            // encode eobrun
            jpg_encode_eobrun(huffw, actbl, eobrun);
            // write remaining zeroes
            while (z >= 16)
            {
                huffw->write_u16(actbl->cval[0xF0], actbl->clen[0xF0]);
                z -= 16;
            }
            // vli encode
            s = BITLEN2048N(block[bpos]);
            n = ENVLI(s, block[bpos]);
            hc = ((z << 4) + s);
            // write to huffman writer
            huffw->write_u16(actbl->cval[hc], actbl->clen[hc]);
            huffw->write_u16(n, s);
            // reset zeroes
            z = 0;
        }
        else   // increment zero counter
        {
            z++;
        }
    }

    // check eob, increment eobrun if needed
    if (z > 0)
    {
        (*eobrun)++;
        // check eobrun, encode if needed
        if ((*eobrun) == actbl->max_eobrun)
        {
            jpg_encode_eobrun(huffw, actbl, eobrun);
        }
        return 1 + to - z;
    }
    else
    {
        return 1 + to;
    }
}

/* -----------------------------------------------
    progressive DC SA decoding routine
    ----------------------------------------------- */
int packJPG::jpg_decode_dc_prg_sa(BitReader* huffr, short* block)
{
    // decode next bit of dc coefficient
    block[0] = huffr->read(1);

    // return 0 if everything is ok
    return 0;
}

/* -----------------------------------------------
    progressive DC SA encoding routine
    ----------------------------------------------- */
int packJPG::jpg_encode_dc_prg_sa(BitWriter* huffw, short* block)
{
    // enocode next bit of dc coefficient
    huffw->write_u16(block[0], 1);

    // return 0 if everything is ok
    return 0;
}

/* -----------------------------------------------
    progressive AC SA decoding routine
    ----------------------------------------------- */
int packJPG::jpg_decode_ac_prg_sa(
    BitReader* huffr, 
    huffTree* actree, 
    short* block, 
    int* eobrun, 
    int from, 
    int to)
{
    unsigned short n;
    unsigned char  s;
    signed char    z;
    signed char    v;
    int bpos = from;
    int eob = to;
    int hc;
    int l;
    int r;

    // decode AC succesive approximation bits
    if ((*eobrun) == 0) while (bpos <= to)
    {
        // decode next
        hc = jpg_next_huffcode(huffr, actree);
        if (hc < 0)
        {
            return -1;
        }
        l = LBITS(hc, 4);
        r = RBITS(hc, 4);
        // analyse code
        if ((l == 15) || (r > 0))         // decode run/level combination
        {
            z = l;
            s = r;
            if (s == 0)
            {
                v = 0;
            }
            else if (s == 1)
            {
                n = huffr->read(1);
                v = (n == 0) ? -1 : 1;   // fast decode vli
            }
            else
            {
                return -1;    // decoding error
            }
            // write zeroes / write correction bits
            while (true)
            {
                if (block[bpos] == 0)     // skip zeroes / write value
                {
                    if (z > 0)
                    {
                        z--;
                    }
                    else
                    {
                        block[bpos++] = v;
                        break;
                    }
                }
                else   // read correction bit
                {
                    n = huffr->read(1);
                    block[bpos] = (block[bpos] > 0) ? n : -n;
                }
                if (bpos++ >= to)
                {
                    return -1;    // error check
                }
            }
        }
        else   // decode eobrun
        {
            eob = bpos;
            s = l;
            n = huffr->read(s);
            (*eobrun) = E_DEVLI(s, n);
            break;
        }
    }

    // read after eob correction bits
    if ((*eobrun) > 0)
    {
        for (; bpos <= to; bpos++)
        {
            if (block[bpos] != 0)
            {
                n = huffr->read(1);
                block[bpos] = (block[bpos] > 0) ? n : -n;
            }
        }
    }

    // return eob
    return eob;
}

/* -----------------------------------------------
    progressive AC SA encoding routine
    ----------------------------------------------- */
int packJPG::jpg_encode_ac_prg_sa(
    BitWriter* huffw, 
    std::vector<std::uint8_t>& storw, 
    huffCodes* actbl, 
    short* block, 
    int* eobrun, 
    int from, 
    int to)
{
    unsigned short n;
    unsigned char  s;
    unsigned char  z;
    int eob = from;
    int bpos;
    int hc;

    // check if block contains any newly nonzero coefficients and find out position of eob
    for (bpos = to; bpos >= from; bpos--)
    {
        if ((block[bpos] == 1) || (block[bpos] == -1))
        {
            eob = bpos + 1;
            break;
        }
    }

    // encode eobrun if needed
    if ((eob > from) && ((*eobrun) > 0))
    {
        jpg_encode_eobrun(huffw, actbl, eobrun);
        jpg_encode_crbits(huffw, storw);
    }

    // encode AC
    z = 0;
    for (bpos = from; bpos < eob; bpos++)
    {
        // if zero is encountered
        if (block[bpos] == 0)
        {
            z++; // increment zero counter
            if (z == 16)     // write zeroes if needed
            {
                huffw->write_u16(actbl->cval[0xF0], actbl->clen[0xF0]);
                jpg_encode_crbits(huffw, storw);
                z = 0;
            }
        }
        // if nonzero is encountered
        else if ((block[bpos] == 1) || (block[bpos] == -1))
        {
            // vli encode
            s = BITLEN2048N(block[bpos]);
            n = ENVLI(s, block[bpos]);
            hc = ((z << 4) + s);
            // write to huffman writer
            huffw->write_u16(actbl->cval[hc], actbl->clen[hc]);
            huffw->write_u16(n, s);
            // write correction bits
            jpg_encode_crbits(huffw, storw);
            // reset zeroes
            z = 0;
        }
        else   // store correction bits
        {
            n = block[bpos] & 0x1;
            storw.emplace_back(n);
        }
    }

    // fast processing after eob
    for (; bpos <= to; bpos++)
    {
        if (block[bpos] != 0)     // store correction bits
        {
            n = block[bpos] & 0x1;
            storw.emplace_back(n);
        }
    }

    // check eob, increment eobrun if needed
    if (eob <= to)
    {
        (*eobrun)++;
        // check eobrun, encode if needed
        if ((*eobrun) == actbl->max_eobrun)
        {
            jpg_encode_eobrun(huffw, actbl, eobrun);
            jpg_encode_crbits(huffw, storw);
        }
    }

    // return eob
    return eob;
}

/* -----------------------------------------------
    run of EOB SA decoding routine
    ----------------------------------------------- */
int packJPG::jpg_decode_eobrun_sa(
    BitReader* huffr, 
    short* block, 
    int* eobrun, 
    int from, 
    int to)
{
    unsigned short n;
    int bpos;

    // fast eobrun decoding routine for succesive approximation
    for (bpos = from; bpos <= to; bpos++)
    {
        if (block[bpos] != 0)
        {
            n = huffr->read(1);
            block[bpos] = (block[bpos] > 0) ? n : -n;
        }
    }

    return 0;
}

/* -----------------------------------------------
    run of EOB encoding routine
    ----------------------------------------------- */
int packJPG::jpg_encode_eobrun(
    BitWriter* huffw, 
    huffCodes* actbl, 
    int* eobrun)
{
    unsigned short n;
    unsigned char  s;
    int hc;

    if ((*eobrun) > 0)
    {
        while ((*eobrun) > actbl->max_eobrun)
        {
            huffw->write_u16(actbl->cval[0xE0], actbl->clen[0xE0]);
            huffw->write_u16(E_ENVLI(14, 32767), 14);
            (*eobrun) -= actbl->max_eobrun;
        }
        BITLEN(s, (*eobrun));
        s--;
        n = E_ENVLI(s, (*eobrun));
        hc = (s << 4);
        huffw->write_u16(actbl->cval[hc], actbl->clen[hc]);
        huffw->write_u16(n, s);
        (*eobrun) = 0;
    }

    return 0;
}

/* -----------------------------------------------
    correction bits encoding routine
    ----------------------------------------------- */
int packJPG::jpg_encode_crbits(
    BitWriter* huffw, 
    std::vector<std::uint8_t>& storw)
{
    for (const std::uint8_t bit : storw)
    {
        huffw->write_bit(bit);
    }
    storw.clear();
    return 0;
}

/* -----------------------------------------------
    returns next code (from huffman-tree & -data)
    ----------------------------------------------- */
int packJPG::jpg_next_huffcode(BitReader* huffw, huffTree* ctree)
{
    int node = 0;

    while (node < 256)
    {
        node = (huffw->read(1) == 1) ? ctree->r[node] : ctree->l[node];
        if (node == 0)
        {
            break;
        }
    }

    return (node - 256);
}

/* -----------------------------------------------
    calculates next position for MCU
    ----------------------------------------------- */
int packJPG::jpg_next_mcupos(
    int* mcu, 
    int* cmp, 
    int* csc, 
    int* sub, 
    int* dpos, 
    int* rstw)
{
    int sta = 0; // status

    // increment all counts where needed
    if ((++(*sub)) >= cmpnfo[(*cmp)].mbs)
    {
        (*sub) = 0;

        if ((++(*csc)) >= cs_cmpc)
        {
            (*csc) = 0;
            (*cmp) = cs_cmp[0];
            (*mcu)++;
            if ((*mcu) >= mcuc)
            {
                sta = 2;
            }
            else if (rsti > 0)
                if (--(*rstw) == 0)
                {
                    sta = 1;
                }
        }
        else
        {
            (*cmp) = cs_cmp[(*csc)];
        }
    }

    // get correct position in image ( x & y )
    if (cmpnfo[(*cmp)].sfh > 1)     // to fix mcu order
    {
        (*dpos)  = ((*mcu) / mcuh) * cmpnfo[(*cmp)].sfh + ((*sub) / cmpnfo[(*cmp)].sfv);
        (*dpos) *= cmpnfo[(*cmp)].bch;
        (*dpos) += ((*mcu) % mcuh) * cmpnfo[(*cmp)].sfv + ((*sub) % cmpnfo[(*cmp)].sfv);
    }
    else if (cmpnfo[(*cmp)].sfv > 1)
    {
        // simple calculation to speed up things if simple fixing is enough
        (*dpos) = ((*mcu) * cmpnfo[(*cmp)].mbs) + (*sub);
    }
    else
    {
        // no calculations needed without subsampling
        (*dpos) = (*mcu);
    }

    return sta;
}

/* -----------------------------------------------
    calculates next position (non interleaved)
    ----------------------------------------------- */
int packJPG::jpg_next_mcuposn(int* cmp, int* dpos, int* rstw)
{
    // increment position
    (*dpos)++;

    // fix for non interleaved mcu - horizontal
    if (cmpnfo[(*cmp)].bch != cmpnfo[(*cmp)].nch)
    {
        if ((*dpos) % cmpnfo[(*cmp)].bch == cmpnfo[(*cmp)].nch)
        {
            (*dpos) += (cmpnfo[(*cmp)].bch - cmpnfo[(*cmp)].nch);
        }
    }

    // fix for non interleaved mcu - vertical
    if (cmpnfo[(*cmp)].bcv != cmpnfo[(*cmp)].ncv)
    {
        if ((*dpos) / cmpnfo[(*cmp)].bch == cmpnfo[(*cmp)].ncv)
        {
            (*dpos) = cmpnfo[(*cmp)].bc;
        }
    }

    // check position
    if ((*dpos) >= cmpnfo[(*cmp)].bc)
    {
        return 2;
    }
    else if (rsti > 0)
        if (--(*rstw) == 0)
        {
            return 1;
        }

    return 0;
}

/* -----------------------------------------------
    skips the eobrun, calculates next position
    ----------------------------------------------- */
int packJPG::jpg_skip_eobrun(int* cmp, int* dpos, int* rstw, int* eobrun)
{
    if ((*eobrun) > 0)   // error check for eobrun
    {
        // compare rst wait counter if needed
        if (rsti > 0)
        {
            if ((*eobrun) > (*rstw))
            {
                return -1;
            }
            else
            {
                (*rstw) -= (*eobrun);
            }
        }

        // fix for non interleaved mcu - horizontal
        if (cmpnfo[(*cmp)].bch != cmpnfo[(*cmp)].nch)
        {
            (*dpos) += ((((*dpos) % cmpnfo[(*cmp)].bch) + (*eobrun)) /
                        cmpnfo[(*cmp)].nch) * (cmpnfo[(*cmp)].bch - cmpnfo[(*cmp)].nch);
        }

        // fix for non interleaved mcu - vertical
        if (cmpnfo[(*cmp)].bcv != cmpnfo[(*cmp)].ncv)
        {
            if ((*dpos) / cmpnfo[(*cmp)].bch >= cmpnfo[(*cmp)].ncv)
                (*dpos) += (cmpnfo[(*cmp)].bcv - cmpnfo[(*cmp)].ncv) *
                           cmpnfo[(*cmp)].bch;
        }

        // skip blocks
        (*dpos) += (*eobrun);

        // reset eobrun
        (*eobrun) = 0;

        // check position
        if ((*dpos) == cmpnfo[(*cmp)].bc)
        {
            return 2;
        }
        else if ((*dpos) > cmpnfo[(*cmp)].bc)
        {
            return -1;
        }
        else if (rsti > 0)
            if ((*rstw) == 0)
            {
                return 1;
            }
    }

    return 0;
}

/* -----------------------------------------------
    creates huffman-codes & -trees from dht-data
    ----------------------------------------------- */
void packJPG::jpg_build_huffcodes(
    unsigned char* clen, 
    unsigned char* cval, 
    huffCodes* hc, 
    huffTree* ht)
{
    int nextfree;
    int code;
    int node;
    int i, j, k;

    // fill with zeroes
    memset(hc->clen, 0, 256 * sizeof(short));
    memset(hc->cval, 0, 256 * sizeof(short));
    memset(ht->l, 0, 256 * sizeof(short));
    memset(ht->r, 0, 256 * sizeof(short));

    // 1st part -> build huffman codes

    // creating huffman-codes
    k = 0;
    code = 0;

    // symbol-value of code is its position in the table
    for (i = 0; i < 16; i++)
    {
        for (j = 0; j < (int) clen[i]; j++)
        {
            hc->clen[(int) cval[k]] = 1 + i;
            hc->cval[(int) cval[k]] = code;

            k++;
            code++;
        }
        code = code << 1;
    }

    // find out eobrun max value
    hc->max_eobrun = 0;
    for (i = 14; i >= 0; i--)
    {
        if (hc->clen[i << 4] > 0)
        {
            hc->max_eobrun = (2 << i) - 1;
            break;
        }
    }

    // 2nd -> part use codes to build the coding tree

    // initial value for next free place
    nextfree = 1;

    // work through every code creating links between the nodes (represented through ints)
    for (i = 0; i < 256; i++)
    {
        // (re)set current node
        node = 0;
        // go through each code & store path
        for (j = hc->clen[i] - 1; j > 0; j--)
        {
            if (BITN(hc->cval[i], j) == 1)
            {
                if (ht->r[node] == 0)
                {
                    ht->r[node] = nextfree++;
                }
                node = ht->r[node];
            }
            else
            {
                if (ht->l[node] == 0)
                {
                    ht->l[node] = nextfree++;
                }
                node = ht->l[node];
            }
        }
        // last link is number of targetvalue + 256
        if (hc->clen[i] > 0)
        {
            if (BITN(hc->cval[i], 0) == 1)
            {
                ht->r[node] = i + 256;
            }
            else
            {
                ht->l[node] = i + 256;
            }
        }
    }
}

/* ------------------- End of JPEG specific functions ---------------------- */


/* ------------------- Begin PJG specific functions ------------------------ */

/* -----------------------------------------------
    encodes frequency scanorder to pjg
    ----------------------------------------------- */
bool packJPG::pjg_encode_zstscan(ArithmeticEncoder* enc, int cmp)
{
    model_s* model;

    unsigned char freqlist[64];
    int tpos; // true position
    int cpos; // coded position
    int c, i;

    // calculate zero sort scan
    pjg_get_zerosort_scan(zsrtscan[cmp], cmp);

    // preset freqlist
    for (i = 0; i < 64; i++)
    {
        freqlist[i] = stdscan[i];
    }

    // init model
    model = INIT_MODEL_S(64, 64, 1);

    // encode scanorder
    for (i = 1; i < 64; i++)
    {
        // reduce range of model
        model->exclude_symbols(64 - i);

        // compare remaining list to remainnig scan
        tpos = 0;
        for (c = i; c < 64; c++)
        {
            // search next val != 0 in list
            for (tpos++; freqlist[tpos] == 0; tpos++);
            // get out if not a match
            if (freqlist[tpos] != zsrtscan[cmp][c])
            {
                break;
            }
        }
        if (c == 64)
        {
            // remaining list is in sorted scanorder
            // encode zero and make a quick exit
            encode_ari(enc, model, 0);
            break;
        }

        // list is not in sorted order -> next pos hat to be encoded
        cpos = 1;
        // encode position
        for (tpos = 0; freqlist[tpos] != zsrtscan[cmp][i]; tpos++)
        {
            if (freqlist[tpos] != 0)
            {
                cpos++;
            }
        }
        // remove from list
        freqlist[tpos] = 0;

        // encode coded position in list
        encode_ari(enc, model, cpos);
        model->shift_context(cpos);
    }

    // delete model
    delete model;

    // set zero sort scan as freqscan
    freqscan[cmp] = zsrtscan[cmp];

    return true;
}

/* -----------------------------------------------
    encodes # of non zeroes to pjg (high)
    ----------------------------------------------- */
bool packJPG::pjg_encode_zdst_high(ArithmeticEncoder* enc, int cmp)
{
    model_s* model;

    unsigned char* zdstls;
    int dpos;
    int a, b;
    int bc;
    int w;

    // init model, constants
    model = INIT_MODEL_S(49 + 1, 25 + 1, 1);
    zdstls = zdstdata[cmp];
    w = cmpnfo[cmp].bch;
    bc = cmpnfo[cmp].bc;

    // arithmetic encode zero-distribution-list
    for (dpos = 0; dpos < bc; dpos++)
    {
        // context modelling - use average of above and left as context
        get_context_nnb(dpos, w, &a, &b);
        a = (a >= 0) ? zdstls[a] : 0;
        b = (b >= 0) ? zdstls[b] : 0;
        // shift context
        model->shift_context((a + b + 2) / 4);
        // encode symbol
        encode_ari(enc, model, zdstls[dpos]);
    }

    // clean up
    delete model;

    return true;
}

/* -----------------------------------------------
    encodes # of non zeroes to pjg (low)
    ----------------------------------------------- */
bool packJPG::pjg_encode_zdst_low(ArithmeticEncoder* enc, int cmp)
{
    model_s* model;

    unsigned char* zdstls_x;
    unsigned char* zdstls_y;
    unsigned char* ctx_zdst;
    unsigned char* ctx_eobx;
    unsigned char* ctx_eoby;

    int dpos;
    int bc;

    // init model, constants
    model = INIT_MODEL_S(8, 8, 2);
    zdstls_x = zdstxlow[cmp];
    zdstls_y = zdstylow[cmp];
    ctx_eobx = eobxhigh[cmp];
    ctx_eoby = eobyhigh[cmp];
    ctx_zdst = zdstdata[cmp];
    bc = cmpnfo[cmp].bc;

    // arithmetic encode zero-distribution-list (first row)
    for (dpos = 0; dpos < bc; dpos++)
    {
        model->shift_context((ctx_zdst[dpos] + 3) / 7);     // shift context
        model->shift_context(ctx_eobx[dpos]);   // shift context
        encode_ari(enc, model, zdstls_x[dpos]);   // encode symbol
    }
    // arithmetic encode zero-distribution-list (first collumn)
    for (dpos = 0; dpos < bc; dpos++)
    {
        model->shift_context((ctx_zdst[dpos] + 3) / 7);     // shift context
        model->shift_context(ctx_eoby[dpos]);   // shift context
        encode_ari(enc, model, zdstls_y[dpos]);   // encode symbol
    }

    // clean up
    delete model;

    return true;
}

/* -----------------------------------------------
    encodes DC coefficients to pjg
    ----------------------------------------------- */
bool packJPG::pjg_encode_dc(ArithmeticEncoder* enc, int cmp)
{
    unsigned char* segm_tab;

    model_s* mod_len;
    model_b* mod_sgn;
    model_b* mod_res;

    unsigned char* zdstls; // pointer to zero distribution list
    signed short* coeffs; // pointer to current coefficent data

    unsigned short* absv_store; // absolute coefficients values storage
    unsigned short* c_absc[6]; // quick access array for contexts
    int c_weight[6]; // weighting for contexts

    int ctx_avr; // 'average' context
    int ctx_len; // context for bit length

    int max_val; // max value
    int max_len; // max bitlength

    int dpos;
    int clen, absv, sgn;
    int snum;
    int bt, bp;

    int p_x, p_y;
    int r_x; //, r_y;
    int w, bc;

    // decide segmentation setting
    segm_tab = segm_tables[segm_cnt[cmp] - 1];

    // get max absolute value/bit length
    max_val = MAX_V(cmp, 0);
    max_len = BITLEN1024P(max_val);

    // init models for bitlenghts and -patterns
    mod_len = INIT_MODEL_S(max_len + 1, (segm_cnt[cmp] > max_len) ? segm_cnt[cmp] : max_len + 1, 2);
    mod_res = INIT_MODEL_B((segm_cnt[cmp] < 16) ? 1 << 4 : segm_cnt[cmp], 2);
    mod_sgn = INIT_MODEL_B(1, 0);

    // set width/height of each band
    bc = cmpnfo[cmp].bc;
    w = cmpnfo[cmp].bch;

    // allocate memory for absolute values storage
    absv_store = (unsigned short*) calloc(bc, sizeof(short));
    if (absv_store == nullptr)
    {
        sprintf(errormessage, MEM_ERRMSG);
        errorlevel = 2;
        return false;
    }

    // set up context quick access array
    pjg_aavrg_prepare(c_absc, c_weight, absv_store, cmp);

    // locally store pointer to coefficients and zero distribution list
    coeffs = colldata[cmp][0];
    zdstls = zdstdata[cmp];

    // arithmetic compression loop
    for (dpos = 0; dpos < bc; dpos++)
    {
        //calculate x/y positions in band
        p_y = dpos / w;
        // r_y = h - ( p_y + 1 );
        p_x = dpos % w;
        r_x = w - (p_x + 1);

        // get segment-number from zero distribution list and segmentation set
        snum = segm_tab[zdstls[dpos]];
        // calculate contexts (for bit length)
        ctx_avr = pjg_aavrg_context(c_absc, c_weight, dpos, p_y, p_x, r_x);   // AVERAGE context
        ctx_len = BITLEN1024P(ctx_avr);   // BITLENGTH context
        // shift context / do context modelling (segmentation is done per context)
        shift_model(mod_len, ctx_len, snum);

        // simple treatment if coefficient is zero
        if (coeffs[dpos] == 0)
        {
            // encode bit length (0) of current coefficient
            encode_ari(enc, mod_len, 0);
        }
        else
        {
            // get absolute val, sign & bit length for current coefficient
            absv = ABS(coeffs[dpos]);
            clen = BITLEN1024P(absv);
            sgn = (coeffs[dpos] > 0) ? 0 : 1;
            // encode bit length of current coefficient
            encode_ari(enc, mod_len, clen);
            // encoding of residual
            // first set bit must be 1, so we start at clen - 2
            for (bp = clen - 2; bp >= 0; bp--)
            {
                shift_model(mod_res, snum, bp);   // shift in 2 contexts
                // encode/get bit
                bt = BITN(absv, bp);
                encode_ari(enc, mod_res, bt);
            }
            // encode sign
            encode_ari(enc, mod_sgn, sgn);
            // store absolute value
            absv_store[dpos] = absv;
        }
    }

    // free memory / clear models
    free(absv_store);
    delete mod_len;
    delete mod_res;
    delete mod_sgn;

    return true;
}

/* -----------------------------------------------
    encodes high (7x7) AC coefficients to pjg
    ----------------------------------------------- */
bool packJPG::pjg_encode_ac_high(ArithmeticEncoder* enc, int cmp)
{
    unsigned char* segm_tab;

    model_s* mod_len;
    model_b* mod_sgn;
    model_b* mod_res;

    unsigned char* zdstls; // pointer to zero distribution list
    unsigned char* eob_x; // pointer to x eobs
    unsigned char* eob_y; // pointer to y eobs
    signed short* coeffs; // pointer to current coefficent data

    unsigned short* absv_store; // absolute coefficients values storage
    unsigned short* c_absc[6]; // quick access array for contexts
    int c_weight[6]; // weighting for contexts

    unsigned char* sgn_store; // sign storage for context
    unsigned char* sgn_nbh; // left signs neighbor
    unsigned char* sgn_nbv; // upper signs neighbor

    int ctx_avr; // 'average' context
    int ctx_len; // context for bit length
    int ctx_sgn; // context for sign

    int max_val; // max value
    int max_len; // max bitlength

    int bpos, dpos;
    int clen, absv, sgn;
    int snum;
    int bt, bp;
    int i;

    int b_x, b_y;
    int p_x, p_y;
    int r_x; //, r_y;
    int w, bc;

    // decide segmentation setting
    segm_tab = segm_tables[segm_cnt[cmp] - 1];

    // init models for bitlenghts and -patterns
    mod_len = INIT_MODEL_S(11, (segm_cnt[cmp] > 11) ? segm_cnt[cmp] : 11, 2);
    mod_res = INIT_MODEL_B((segm_cnt[cmp] < 16) ? 1 << 4 : segm_cnt[cmp], 2);
    mod_sgn = INIT_MODEL_B(9, 1);

    // set width/height of each band
    bc = cmpnfo[cmp].bc;
    w = cmpnfo[cmp].bch;

    // allocate memory for absolute values & signs storage
    absv_store = (unsigned short*) calloc(bc, sizeof(short));
    sgn_store = (unsigned char*) calloc(bc, sizeof(char));
    zdstls = (unsigned char*) calloc(bc, sizeof(char));
    if ((absv_store == nullptr) || (sgn_store == nullptr) || (zdstls == nullptr))
    {
        if (absv_store != nullptr)
        {
            free(absv_store);
        }
        if (sgn_store != nullptr)
        {
            free(sgn_store);
        }
        if (zdstls != nullptr)
        {
            free(zdstls);
        }
        sprintf(errormessage, MEM_ERRMSG);
        errorlevel = 2;
        return false;
    }

    // set up quick access arrays for signs context
    sgn_nbh = sgn_store - 1;
    sgn_nbv = sgn_store - w;

    // locally store pointer to eob x / eob y
    eob_x = eobxhigh[cmp];
    eob_y = eobyhigh[cmp];

    // preset x/y eobs
    memset(eob_x, 0x00, bc * sizeof(char));
    memset(eob_y, 0x00, bc * sizeof(char));

    // make a local copy of the zero distribution list
    for (dpos = 0; dpos < bc; dpos++)
    {
        zdstls[dpos] = zdstdata[cmp][dpos];
    }

    // work through lower 7x7 bands in order of freqscan
    for (i = 1; i < 64; i++)
    {
        // work through blocks in order of frequency scan
        bpos = (int) freqscan[cmp][i];
        b_x = unzigzag[bpos] % 8;
        b_y = unzigzag[bpos] / 8;

        if ((b_x == 0) || (b_y == 0))
        {
            continue;    // process remaining coefficients elsewhere
        }

        // preset absolute values/sign storage
        memset(absv_store, 0x00, bc * sizeof(short));
        memset(sgn_store, 0x00, bc * sizeof(char));

        // set up average context quick access arrays
        pjg_aavrg_prepare(c_absc, c_weight, absv_store, cmp);

        // locally store pointer to coefficients
        coeffs = colldata[cmp][bpos];

        // get max bit length
        max_val = MAX_V(cmp, bpos);
        max_len = BITLEN1024P(max_val);

        // arithmetic compression loo
        for (dpos = 0; dpos < bc; dpos++)
        {
            // skip if beyound eob
            if (zdstls[dpos] == 0)
            {
                continue;
            }

            //calculate x/y positions in band
            p_y = dpos / w;
            // r_y = h - ( p_y + 1 );
            p_x = dpos % w;
            r_x = w - (p_x + 1);

            // get segment-number from zero distribution list and segmentation set
            snum = segm_tab[zdstls[dpos]];
            // calculate contexts (for bit length)
            ctx_avr = pjg_aavrg_context(c_absc, c_weight, dpos, p_y, p_x, r_x);   // AVERAGE context
            ctx_len = BITLEN1024P(ctx_avr);   // BITLENGTH context
            // shift context / do context modelling (segmentation is done per context)
            shift_model(mod_len, ctx_len, snum);
            mod_len->exclude_symbols(max_len);

            // simple treatment if coefficient is zero
            if (coeffs[dpos] == 0)
            {
                // encode bit length (0) of current coefficien
                encode_ari(enc, mod_len, 0);
            }
            else
            {
                // get absolute val, sign & bit length for current coefficient
                absv = ABS(coeffs[dpos]);
                clen = BITLEN1024P(absv);
                sgn = (coeffs[dpos] > 0) ? 0 : 1;
                // encode bit length of current coefficient
                encode_ari(enc, mod_len, clen);
                // encoding of residual
                // first set bit must be 1, so we start at clen - 2
                for (bp = clen - 2; bp >= 0; bp--)
                {
                    shift_model(mod_res, snum, bp);   // shift in 2 contexts
                    // encode/get bit
                    bt = BITN(absv, bp);
                    encode_ari(enc, mod_res, bt);
                }
                // encode sign
                ctx_sgn = (p_x > 0) ? sgn_nbh[dpos] : 0;   // sign context
                if (p_y > 0)
                {
                    ctx_sgn += 3 * sgn_nbv[dpos];    // IMPROVE !!!!!!!!!!!
                }
                mod_sgn->shift_context(ctx_sgn);
                encode_ari(enc, mod_sgn, sgn);
                // store absolute value/sign, decrement zdst
                absv_store[dpos] = absv;
                sgn_store[dpos] = sgn + 1;
                zdstls[dpos]--;
                // recalculate x/y eob
                if (b_x > eob_x[dpos])
                {
                    eob_x[dpos] = b_x;
                }
                if (b_y > eob_y[dpos])
                {
                    eob_y[dpos] = b_y;
                }
            }
        }
        // flush models
        mod_len->flush_model();
        mod_res->flush_model();
        mod_sgn->flush_model();
    }

    // free memory / clear models
    free(absv_store);
    free(sgn_store);
    free(zdstls);
    delete mod_len;
    delete mod_res;
    delete mod_sgn;

    return true;
}

/* -----------------------------------------------
    encodes first row/col AC coefficients to pjg
    ----------------------------------------------- */
bool packJPG::pjg_encode_ac_low(ArithmeticEncoder* enc, int cmp)
{
    model_s* mod_len;
    model_b* mod_sgn;
    model_b* mod_res;
    model_b* mod_top;

    unsigned char* zdstls; // pointer to row/col # of non-zeroes
    signed short* coeffs; // pointer to current coefficent data

    signed short* coeffs_x[8]; // prediction coeffs - current block
    signed short* coeffs_a[8]; // prediction coeffs - neighboring block
    int pred_cf[8]; // prediction multipliers

    int ctx_lak; // lakhani context
    int ctx_abs; // absolute context
    int ctx_len; // context for bit length
    int ctx_res; // bit plane context for residual
    int ctx_sgn; // context for sign

    int max_valp; // max value (+)
    int max_valn; // max value (-)
    int max_len; // max bitlength
    int thrs_bp; // residual threshold bitplane
    int* edge_c; // edge criteria

    int bpos, dpos;
    int clen, absv, sgn;
    int bt, bp;
    int i;

    int b_x, b_y;
    int p_x, p_y;
    int w, bc;

    // init models for bitlenghts and -patterns
    mod_len = INIT_MODEL_S(11, (segm_cnt[cmp] > 11) ? segm_cnt[cmp] : 11, 2);
    mod_res = INIT_MODEL_B(1 << 4, 2);
    mod_top = INIT_MODEL_B((nois_trs[cmp] > 4) ? 1 << nois_trs[cmp] : 1 << 4, 3);
    mod_sgn = INIT_MODEL_B(11, 1);

    // set width/height of each band
    bc = cmpnfo[cmp].bc;
    w = cmpnfo[cmp].bch;

    // work through each first row / first collumn band
    for (i = 2; i < 16; i++)
    {
        // alternate between first row and first collumn
        b_x = (i % 2 == 0) ? i / 2 : 0;
        b_y = (i % 2 == 1) ? i / 2 : 0;
        bpos = (int) zigzag[b_x + (8*b_y)];

        // locally store pointer to band coefficients
        coeffs = colldata[cmp][bpos];
        // store pointers to prediction coefficients
        if (b_x == 0)
        {
            for (; b_x < 8; b_x++)
            {
                coeffs_x[b_x] = colldata[cmp][zigzag[b_x+(8*b_y)]];
                coeffs_a[b_x] = colldata[cmp][zigzag[b_x+(8*b_y)]] - 1;
                pred_cf[b_x] = icos_base_8x8[b_x * 8] * QUANT(cmp, zigzag[b_x+(8*b_y)]);
            }
            b_x = 0;
            zdstls = zdstylow[cmp];
            edge_c = &p_x;
        }
        else   // if ( b_y == 0 )
        {
            for (; b_y < 8; b_y++)
            {
                coeffs_x[b_y] = colldata[cmp][zigzag[b_x+(8*b_y)]];
                coeffs_a[b_y] = colldata[cmp][zigzag[b_x+(8*b_y)]] - w;
                pred_cf[b_y] = icos_base_8x8[b_y * 8] * QUANT(cmp, zigzag[b_x+(8*b_y)]);
            }
            b_y = 0;
            zdstls = zdstxlow[cmp];
            edge_c = &p_y;
        }

        // get max bit length / other info
        max_valp = MAX_V(cmp, bpos);
        max_valn = -max_valp;
        max_len = BITLEN1024P(max_valp);
        thrs_bp = (max_len > nois_trs[cmp]) ? max_len - nois_trs[cmp] : 0;

        // arithmetic compression loop
        for (dpos = 0; dpos < bc; dpos++)
        {
            // skip if beyound eob
            if (zdstls[dpos] == 0)
            {
                continue;
            }

            // calculate x/y positions in band
            p_y = dpos / w;
            p_x = dpos % w;

            // edge treatment / calculate LAKHANI context
            if ((*edge_c) > 0)
            {
                ctx_lak = pjg_lakh_context(coeffs_x, coeffs_a, pred_cf, dpos);
            }
            else
            {
                ctx_lak = 0;
            }
            ctx_lak = CLAMPED(max_valn, max_valp, ctx_lak);
            ctx_len = BITLEN2048N(ctx_lak);   // BITLENGTH context

            // shift context / do context modelling (segmentation is done per context)
            shift_model(mod_len, ctx_len, zdstls[dpos]);
            mod_len->exclude_symbols(max_len);

            // simple treatment if coefficient is zero
            if (coeffs[dpos] == 0)
            {
                // encode bit length (0) of current coefficient
                encode_ari(enc, mod_len, 0);
            }
            else
            {
                // get absolute val, sign & bit length for current coefficient
                absv = ABS(coeffs[dpos]);
                clen = BITLEN2048N(absv);
                sgn = (coeffs[dpos] > 0) ? 0 : 1;
                // encode bit length of current coefficient
                encode_ari(enc, mod_len, clen);
                // encoding of residual
                bp = clen - 2; // first set bit must be 1, so we start at clen - 2
                ctx_res = (bp >= thrs_bp) ? 1 : 0;
                ctx_abs = ABS(ctx_lak);
                ctx_sgn = (ctx_lak == 0) ? 0 : (ctx_lak > 0) ? 1 : 2;
                for (; bp >= thrs_bp; bp--)
                {
                    shift_model(mod_top, ctx_abs >> thrs_bp, ctx_res, clen - thrs_bp);   // shift in 3 contexts
                    // encode/get bit
                    bt = BITN(absv, bp);
                    encode_ari(enc, mod_top, bt);
                    // update context
                    ctx_res = ctx_res << 1;
                    if (bt)
                    {
                        ctx_res |= 1;
                    }
                }
                for (; bp >= 0; bp--)
                {
                    shift_model(mod_res, zdstls[dpos], bp);   // shift in 2 contexts
                    // encode/get bit
                    bt = BITN(absv, bp);
                    encode_ari(enc, mod_res, bt);
                }
                // encode sign
                shift_model(mod_sgn, ctx_len, ctx_sgn);
                encode_ari(enc, mod_sgn, sgn);
                // decrement # of non zeroes
                zdstls[dpos]--;
            }
        }
        // flush models
        mod_len->flush_model();
        mod_res->flush_model();
        mod_top->flush_model();
        mod_sgn->flush_model();
    }

    // free memory / clear models
    delete mod_len;
    delete mod_res;
    delete mod_top;
    delete mod_sgn;

    return true;
}

/* -----------------------------------------------
    encodes a stream of generic (8bit) data to pjg
    ----------------------------------------------- */
bool packJPG::pjg_encode_generic(
    ArithmeticEncoder* enc, 
    unsigned char* data, 
    int len)
{
    model_s* model;
    int i;

    // arithmetic encode data
    model = INIT_MODEL_S(256 + 1, 256, 1);
    for (i = 0; i < len; i++)
    {
        encode_ari(enc, model, data[i]);
        model->shift_context(data[i]);
    }
    // encode end-of-data symbol (256)
    encode_ari(enc, model, 256);
    delete model;

    return true;
}

/* -----------------------------------------------
    encodes one bit to pjg
    ----------------------------------------------- */
bool packJPG::pjg_encode_bit(ArithmeticEncoder* enc, unsigned char bit)
{
    model_b* model;

    // encode one bit
    model = INIT_MODEL_B(1, -1);
    encode_ari(enc, model, bit);
    delete model;

    return true;
}

/* -----------------------------------------------
    encodes frequency scanorder to pjg
    ----------------------------------------------- */
bool packJPG::pjg_decode_zstscan(ArithmeticDecoder* dec, int cmp)
{
    model_s* model;;

    unsigned char freqlist[64];
    int tpos; // true position
    int cpos; // coded position
    int i;

    // set first position in zero sort scan
    zsrtscan[cmp][0] = 0;

    // preset freqlist
    for (i = 0; i < 64; i++)
    {
        freqlist[i] = stdscan[i];
    }

    // init model
    model = INIT_MODEL_S(64, 64, 1);

    // encode scanorder
    for (i = 1; i < 64; i++)
    {
        // reduce range of model
        model->exclude_symbols(64 - i);

        // decode symbol
        cpos = decode_ari(dec, model);
        model->shift_context(cpos);

        if (cpos == 0)
        {
            // remaining list is identical to scan
            // fill the scan & make a quick exit
            for (tpos = 0; i < 64; i++)
            {
                while (freqlist[++tpos] == 0);
                zsrtscan[cmp][i] = freqlist[tpos];
            }
            break;
        }

        // decode position from list
        for (tpos = 0; tpos < 64; tpos++)
        {
            if (freqlist[tpos] != 0)
            {
                cpos--;
            }
            if (cpos == 0)
            {
                break;
            }
        }

        // write decoded position to zero sort scan
        zsrtscan[cmp][i] = freqlist[tpos];
        // remove from list
        freqlist[tpos] = 0;
    }

    // delete model
    delete model;

    // set zero sort scan as freqscan
    freqscan[cmp] = zsrtscan[cmp];

    return true;
}

/* -----------------------------------------------
    decodes # of non zeroes from pjg (high)
    ----------------------------------------------- */
bool packJPG::pjg_decode_zdst_high(ArithmeticDecoder* dec, int cmp)
{
    model_s* model;

    unsigned char* zdstls;
    int dpos;
    int a, b;
    int bc;
    int w;

    // init model, constants
    model = INIT_MODEL_S(49 + 1, 25 + 1, 1);
    zdstls = zdstdata[cmp];
    w = cmpnfo[cmp].bch;
    bc = cmpnfo[cmp].bc;

    // arithmetic decode zero-distribution-list
    for (dpos = 0; dpos < bc; dpos++)
    {
        // context modelling - use average of above and left as context
        get_context_nnb(dpos, w, &a, &b);
        a = (a >= 0) ? zdstls[a] : 0;
        b = (b >= 0) ? zdstls[b] : 0;
        // shift context
        model->shift_context((a + b + 2) / 4);
        // decode symbol
        zdstls[dpos] = decode_ari(dec, model);
    }

    // clean up
    delete model;

    return true;
}

/* -----------------------------------------------
    decodes # of non zeroes from pjg (low)
    ----------------------------------------------- */
bool packJPG::pjg_decode_zdst_low(ArithmeticDecoder* dec, int cmp)
{
    model_s* model;

    unsigned char* zdstls_x;
    unsigned char* zdstls_y;
    unsigned char* ctx_zdst;
    unsigned char* ctx_eobx;
    unsigned char* ctx_eoby;

    int dpos;
    int bc;

    // init model, constants
    model = INIT_MODEL_S(8, 8, 2);
    zdstls_x = zdstxlow[cmp];
    zdstls_y = zdstylow[cmp];
    ctx_eobx = eobxhigh[cmp];
    ctx_eoby = eobyhigh[cmp];
    ctx_zdst = zdstdata[cmp];
    bc = cmpnfo[cmp].bc;

    // arithmetic encode zero-distribution-list (first row)
    for (dpos = 0; dpos < bc; dpos++)
    {
        model->shift_context((ctx_zdst[dpos] + 3) / 7);     // shift context
        model->shift_context(ctx_eobx[dpos]);   // shift context
        zdstls_x[dpos] = decode_ari(dec, model);   // decode symbol
    }
    // arithmetic encode zero-distribution-list (first collumn)
    for (dpos = 0; dpos < bc; dpos++)
    {
        model->shift_context((ctx_zdst[dpos] + 3) / 7);     // shift context
        model->shift_context(ctx_eoby[dpos]);   // shift context
        zdstls_y[dpos] = decode_ari(dec, model);   // decode symbol
    }

    // clean up
    delete model;

    return true;
}

/* -----------------------------------------------
    decodes DC coefficients from pjg
    ----------------------------------------------- */
bool packJPG::pjg_decode_dc(ArithmeticDecoder* dec, int cmp)
{
    unsigned char* segm_tab;

    model_s* mod_len;
    model_b* mod_sgn;
    model_b* mod_res;

    unsigned char* zdstls; // pointer to zero distribution list
    signed short* coeffs; // pointer to current coefficent data

    unsigned short* absv_store; // absolute coefficients values storage
    unsigned short* c_absc[6]; // quick access array for contexts
    int c_weight[6]; // weighting for contexts

    int ctx_avr; // 'average' context
    int ctx_len; // context for bit length

    int max_val; // max value
    int max_len; // max bitlength

    int dpos;
    int clen, absv, sgn;
    int snum;
    int bt, bp;

    int p_x, p_y;
    int r_x; //, r_y;
    int w, bc;

    // decide segmentation setting
    segm_tab = segm_tables[segm_cnt[cmp] - 1];

    // get max absolute value/bit length
    max_val = MAX_V(cmp, 0);
    max_len = BITLEN1024P(max_val);

    // init models for bitlenghts and -patterns
    mod_len = INIT_MODEL_S(max_len + 1, (segm_cnt[cmp] > max_len) ? segm_cnt[cmp] : max_len + 1, 2);
    mod_res = INIT_MODEL_B((segm_cnt[cmp] < 16) ? 1 << 4 : segm_cnt[cmp], 2);
    mod_sgn = INIT_MODEL_B(1, 0);

    // set width/height of each band
    bc = cmpnfo[cmp].bc;
    w = cmpnfo[cmp].bch;

    // allocate memory for absolute values storage
    absv_store = (unsigned short*) calloc(bc, sizeof(short));
    if (absv_store == nullptr)
    {
        sprintf(errormessage, MEM_ERRMSG);
        errorlevel = 2;
        return false;
    }

    // set up context quick access array
    pjg_aavrg_prepare(c_absc, c_weight, absv_store, cmp);

    // locally store pointer to coefficients and zero distribution list
    coeffs = colldata[cmp][0];
    zdstls = zdstdata[cmp];

    // arithmetic compression loop
    for (dpos = 0; dpos < bc; dpos++)
    {
        //calculate x/y positions in band
        p_y = dpos / w;
        // r_y = h - ( p_y + 1 );
        p_x = dpos % w;
        r_x = w - (p_x + 1);

        // get segment-number from zero distribution list and segmentation set
        snum = segm_tab[zdstls[dpos]];
        // calculate contexts (for bit length)
        ctx_avr = pjg_aavrg_context(c_absc, c_weight, dpos, p_y, p_x, r_x);   // AVERAGE context
        ctx_len = BITLEN1024P(ctx_avr);   // BITLENGTH context
        // shift context / do context modelling (segmentation is done per context)
        shift_model(mod_len, ctx_len, snum);
        // decode bit length of current coefficient
        clen = decode_ari(dec, mod_len);

        // simple treatment if coefficient is zero
        if (clen == 0)
        {
            // coeffs[dpos] = 0;
        }
        else
        {
            // decoding of residual
            absv = 1;
            // first set bit must be 1, so we start at clen - 2
            for (bp = clen - 2; bp >= 0; bp--)
            {
                shift_model(mod_res, snum, bp);   // shift in 2 contexts
                // decode bit
                bt = decode_ari(dec, mod_res);
                // update absv
                absv = absv << 1;
                if (bt)
                {
                    absv |= 1;
                }
            }
            // decode sign
            sgn = decode_ari(dec, mod_sgn);
            // copy to colldata
            coeffs[dpos] = (sgn == 0) ? absv : -absv;
            // store absolute value/sign
            absv_store[dpos] = absv;
        }
    }

    // free memory / clear models
    free(absv_store);
    delete mod_len;
    delete mod_res;
    delete mod_sgn;

    return true;
}

/* -----------------------------------------------
    decodes high (7x7) AC coefficients to pjg
    ----------------------------------------------- */
bool packJPG::pjg_decode_ac_high(ArithmeticDecoder* dec, int cmp)
{
    unsigned char* segm_tab;

    model_s* mod_len;
    model_b* mod_sgn;
    model_b* mod_res;

    unsigned char* zdstls; // pointer to zero distribution list
    unsigned char* eob_x; // pointer to x eobs
    unsigned char* eob_y; // pointer to y eobs
    signed short* coeffs; // pointer to current coefficent data

    unsigned short* absv_store; // absolute coefficients values storage
    unsigned short* c_absc[6]; // quick access array for contexts
    int c_weight[6]; // weighting for contexts

    unsigned char* sgn_store; // sign storage for context
    unsigned char* sgn_nbh; // left signs neighbor
    unsigned char* sgn_nbv; // upper signs neighbor

    int ctx_avr; // 'average' context
    int ctx_len; // context for bit length
    int ctx_sgn; // context for sign

    int max_val; // max value
    int max_len; // max bitlength

    int bpos, dpos;
    int clen, absv, sgn;
    int snum;
    int bt, bp;
    int i;

    int b_x, b_y;
    int p_x, p_y;
    int r_x;
    int w, bc;

    // decide segmentation setting
    segm_tab = segm_tables[segm_cnt[cmp] - 1];

    // init models for bitlenghts and -patterns
    mod_len = INIT_MODEL_S(11, (segm_cnt[cmp] > 11) ? segm_cnt[cmp] : 11, 2);
    mod_res = INIT_MODEL_B((segm_cnt[cmp] < 16) ? 1 << 4 : segm_cnt[cmp], 2);
    mod_sgn = INIT_MODEL_B(9, 1);

    // set width/height of each band
    bc = cmpnfo[cmp].bc;
    w = cmpnfo[cmp].bch;

    // allocate memory for absolute values & signs storage
    absv_store = (unsigned short*) calloc(bc, sizeof(short));
    sgn_store = (unsigned char*) calloc(bc, sizeof(char));
    zdstls = (unsigned char*) calloc(bc, sizeof(char));
    if ((absv_store == nullptr) || (sgn_store == nullptr) || (zdstls == nullptr))
    {
        if (absv_store != nullptr)
        {
            free(absv_store);
        }
        if (sgn_store != nullptr)
        {
            free(sgn_store);
        }
        if (zdstls != nullptr)
        {
            free(zdstls);
        }
        sprintf(errormessage, MEM_ERRMSG);
        errorlevel = 2;
        return false;
    }

    // set up quick access arrays for signs context
    sgn_nbh = sgn_store - 1;
    sgn_nbv = sgn_store - w;

    // locally store pointer to eob x / eob y
    eob_x = eobxhigh[cmp];
    eob_y = eobyhigh[cmp];

    // preset x/y eobs
    memset(eob_x, 0x00, bc * sizeof(char));
    memset(eob_y, 0x00, bc * sizeof(char));

    // make a local copy of the zero distribution list
    for (dpos = 0; dpos < bc; dpos++)
    {
        zdstls[dpos] = zdstdata[cmp][dpos];
    }

    // work through lower 7x7 bands in order of freqscan
    for (i = 1; i < 64; i++)
    {
        // work through blocks in order of frequency scan
        bpos = (int) freqscan[cmp][i];
        b_x = unzigzag[bpos] % 8;
        b_y = unzigzag[bpos] / 8;

        if ((b_x == 0) || (b_y == 0))
        {
            continue;    // process remaining coefficients elsewhere
        }

        // preset absolute values/sign storage
        memset(absv_store, 0x00, bc * sizeof(short));
        memset(sgn_store, 0x00, bc * sizeof(char));

        // set up average context quick access arrays
        pjg_aavrg_prepare(c_absc, c_weight, absv_store, cmp);

        // locally store pointer to coefficients
        coeffs = colldata[cmp][bpos];

        // get max bit length
        max_val = MAX_V(cmp, bpos);
        max_len = BITLEN1024P(max_val);

        // arithmetic compression loop
        for (dpos = 0; dpos < bc; dpos++)
        {
            // skip if beyound eob
            if (zdstls[dpos] == 0)
            {
                continue;
            }

            //calculate x/y positions in band
            p_y = dpos / w;
            // r_y = h - ( p_y + 1 );
            p_x = dpos % w;
            r_x = w - (p_x + 1);

            // get segment-number from zero distribution list and segmentation set
            snum = segm_tab[zdstls[dpos]];
            // calculate contexts (for bit length)
            ctx_avr = pjg_aavrg_context(c_absc, c_weight, dpos, p_y, p_x, r_x);   // AVERAGE context
            ctx_len = BITLEN1024P(ctx_avr);   // BITLENGTH context
            // shift context / do context modelling (segmentation is done per context)
            shift_model(mod_len, ctx_len, snum);
            mod_len->exclude_symbols(max_len);

            // decode bit length of current coefficient
            clen = decode_ari(dec, mod_len);
            // simple treatment if coefficient is zero
            if (clen == 0)
            {
                // coeffs[dpos] = 0;
            }
            else
            {
                // decoding of residual
                absv = 1;
                // first set bit must be 1, so we start at clen - 2
                for (bp = clen - 2; bp >= 0; bp--)
                {
                    shift_model(mod_res, snum, bp);   // shift in 2 contexts
                    // decode bit
                    bt = decode_ari(dec, mod_res);
                    // update absv
                    absv = absv << 1;
                    if (bt)
                    {
                        absv |= 1;
                    }
                }
                // decode sign
                ctx_sgn = (p_x > 0) ? sgn_nbh[dpos] : 0;   // sign context
                if (p_y > 0)
                {
                    ctx_sgn += 3 * sgn_nbv[dpos];    // IMPROVE! !!!!!!!!!!!
                }
                mod_sgn->shift_context(ctx_sgn);
                sgn = decode_ari(dec, mod_sgn);
                // copy to colldata
                coeffs[dpos] = (sgn == 0) ? absv : -absv;
                // store absolute value/sign, decrement zdst
                absv_store[dpos] = absv;
                sgn_store[dpos] = sgn + 1;
                zdstls[dpos]--;
                // recalculate x/y eob
                if (b_x > eob_x[dpos])
                {
                    eob_x[dpos] = b_x;
                }
                if (b_y > eob_y[dpos])
                {
                    eob_y[dpos] = b_y;
                }
            }
        }
        // flush models
        mod_len->flush_model();
        mod_res->flush_model();
        mod_sgn->flush_model();
    }

    // free memory / clear models
    free(absv_store);
    free(sgn_store);
    free(zdstls);
    delete mod_len;
    delete mod_res;
    delete mod_sgn;

    return true;
}

/* -----------------------------------------------
    decodes high (7x7) AC coefficients to pjg
    ----------------------------------------------- */
bool packJPG::pjg_decode_ac_low(ArithmeticDecoder* dec, int cmp)
{
    model_s* mod_len;
    model_b* mod_sgn;
    model_b* mod_res;
    model_b* mod_top;

    unsigned char* zdstls; // pointer to row/col # of non-zeroes
    signed short* coeffs; // pointer to current coefficent data

    signed short* coeffs_x[8]; // prediction coeffs - current block
    signed short* coeffs_a[8]; // prediction coeffs - neighboring block
    int pred_cf[8]; // prediction multipliers

    int ctx_lak; // lakhani context
    int ctx_abs; // absolute context
    int ctx_len; // context for bit length
    int ctx_res; // bit plane context for residual
    int ctx_sgn; // context for sign

    int max_valp; // max value (+)
    int max_valn; // max value (-)
    int max_len; // max bitlength
    int thrs_bp; // residual threshold bitplane
    int* edge_c; // edge criteria

    int bpos, dpos;
    int clen, absv, sgn;
    int bt, bp;
    int i;

    int b_x, b_y;
    int p_x, p_y;
    int w, bc;

    // init models for bitlenghts and -patterns
    mod_len = INIT_MODEL_S(11, (segm_cnt[cmp] > 11) ? segm_cnt[cmp] : 11, 2);
    mod_res = INIT_MODEL_B(1 << 4, 2);
    mod_top = INIT_MODEL_B((nois_trs[cmp] > 4) ? 1 << nois_trs[cmp] : 1 << 4, 3);
    mod_sgn = INIT_MODEL_B(11, 1);

    // set width/height of each band
    bc = cmpnfo[cmp].bc;
    w = cmpnfo[cmp].bch;

    // work through each first row / first collumn band
    for (i = 2; i < 16; i++)
    {
        // alternate between first row and first collumn
        b_x = (i % 2 == 0) ? i / 2 : 0;
        b_y = (i % 2 == 1) ? i / 2 : 0;
        bpos = (int) zigzag[b_x + (8*b_y)];

        // locally store pointer to band coefficients
        coeffs = colldata[cmp][bpos];
        // store pointers to prediction coefficients
        if (b_x == 0)
        {
            for (; b_x < 8; b_x++)
            {
                coeffs_x[b_x] = colldata[cmp][zigzag[b_x+(8*b_y)]];
                coeffs_a[b_x] = colldata[cmp][zigzag[b_x+(8*b_y)]] - 1;
                pred_cf[b_x] = icos_base_8x8[b_x * 8] * QUANT(cmp, zigzag[b_x+(8*b_y)]);
            }
            b_x = 0;
            zdstls = zdstylow[cmp];
            edge_c = &p_x;
        }
        else   // if ( b_y == 0 )
        {
            for (; b_y < 8; b_y++)
            {
                coeffs_x[b_y] = colldata[cmp][zigzag[b_x+(8*b_y)]];
                coeffs_a[b_y] = colldata[cmp][zigzag[b_x+(8*b_y)]] - w;
                pred_cf[b_y] = icos_base_8x8[b_y * 8] * QUANT(cmp, zigzag[b_x+(8*b_y)]);
            }
            b_y = 0;
            zdstls = zdstxlow[cmp];
            edge_c = &p_y;
        }

        // get max bit length / other info
        max_valp = MAX_V(cmp, bpos);
        max_valn = -max_valp;
        max_len = BITLEN1024P(max_valp);
        thrs_bp = (max_len > nois_trs[cmp]) ? max_len - nois_trs[cmp] : 0;

        // arithmetic compression loop
        for (dpos = 0; dpos < bc; dpos++)
        {
            // skip if beyound eob
            if (zdstls[dpos] == 0)
            {
                continue;
            }

            //calculate x/y positions in band
            p_y = dpos / w;
            p_x = dpos % w;

            // edge treatment / calculate LAKHANI context
            if ((*edge_c) > 0)
            {
                ctx_lak = pjg_lakh_context(coeffs_x, coeffs_a, pred_cf, dpos);
            }
            else
            {
                ctx_lak = 0;
            }
            ctx_lak = CLAMPED(max_valn, max_valp, ctx_lak);
            ctx_len = BITLEN2048N(ctx_lak);   // BITLENGTH context
            // shift context / do context modelling (segmentation is done per context)
            shift_model(mod_len, ctx_len, zdstls[dpos]);
            mod_len->exclude_symbols(max_len);

            // decode bit length of current coefficient
            clen = decode_ari(dec, mod_len);
            // simple treatment if coefficients == 0
            if (clen == 0)
            {
                // coeffs[dpos] = 0;
            }
            else
            {
                // decoding of residual
                bp = clen - 2; // first set bit must be 1, so we start at clen - 2
                ctx_res = (bp >= thrs_bp) ? 1 : 0;
                ctx_abs = ABS(ctx_lak);
                ctx_sgn = (ctx_lak == 0) ? 0 : (ctx_lak > 0) ? 1 : 2;
                for (; bp >= thrs_bp; bp--)
                {
                    shift_model(mod_top, ctx_abs >> thrs_bp, ctx_res, clen - thrs_bp);   // shift in 3 contexts
                    // decode bit
                    bt = decode_ari(dec, mod_top);
                    // update context
                    ctx_res = ctx_res << 1;
                    if (bt)
                    {
                        ctx_res |= 1;
                    }
                }
                absv = (ctx_res == 0) ? 1 : ctx_res;   // !!!!
                for (; bp >= 0; bp--)
                {
                    shift_model(mod_res, zdstls[dpos], bp);   // shift in 2 contexts
                    // decode bit
                    bt = decode_ari(dec, mod_res);
                    // update absv
                    absv = absv << 1;
                    if (bt)
                    {
                        absv |= 1;
                    }
                }
                // decode sign
                shift_model(mod_sgn, zdstls[dpos], ctx_sgn);
                sgn = decode_ari(dec, mod_sgn);
                // copy to colldata
                coeffs[dpos] = (sgn == 0) ? absv : -absv;
                // decrement # of non zeroes
                zdstls[dpos]--;
            }
        }
        // flush models
        mod_len->flush_model();
        mod_res->flush_model();
        mod_top->flush_model();
        mod_sgn->flush_model();
    }

    // free memory / clear models
    delete mod_len;
    delete mod_res;
    delete mod_top;
    delete mod_sgn;

    return true;
}

/* -----------------------------------------------
    deodes a stream of generic (8bit) data from pjg
    ----------------------------------------------- */
bool packJPG::pjg_decode_generic(
    ArithmeticDecoder* dec, 
    unsigned char** data, 
    int* len)
{
    MemoryWriter* bwrt;
    model_s* model;
    int c;

    // start byte writer
    bwrt = new MemoryWriter();

    // decode header, ending with 256 symbol
    model = INIT_MODEL_S(256 + 1, 256, 1);
    while (true)
    {
        c = decode_ari(dec, model);
        if (c == 256)
        {
            break;
        }
        bwrt->write_byte((unsigned char) c);
        model->shift_context(c);
    }
    delete model;

    // check for out of memory
    if (bwrt->error())
    {
        delete bwrt;
        sprintf(errormessage, MEM_ERRMSG);
        errorlevel = 2;
        return false;
    }

    // get data/length and close byte writer
    (*data) = bwrt->get_c_data();
    if (len != nullptr)
    {
        (*len) = bwrt->num_bytes_written();
    }
    delete bwrt;

    return true;
}

/* -----------------------------------------------
    decodes one bit from pjg
    ----------------------------------------------- */
bool packJPG::pjg_decode_bit(ArithmeticDecoder* dec, unsigned char* bit)
{
    model_b* model = INIT_MODEL_B(1, -1);
    (*bit) = decode_ari(dec, model);
    delete model;

    return true;
}

/* -----------------------------------------------
    get zero sort frequency scan vector
    ----------------------------------------------- */
void packJPG::pjg_get_zerosort_scan(unsigned char* sv, int cmp)
{
    unsigned int zdist[64]; // distributions of zeroes per band
    int bc = cmpnfo[cmp].bc;
    int bpos, dpos;
    bool done = false;
    int swap;
    int i;

    // preset sv & zdist
    for (i = 0; i < 64; i++)
    {
        sv[i] = i;
        zdist[i] = 0;
    }

    // count zeroes for each frequency
    for (bpos = 0; bpos < 64; bpos++)
    {
        for (dpos = 0; dpos < bc; dpos++)
            if (colldata[cmp][bpos][dpos] == 0)
            {
                zdist[bpos]++;
            }
    }

    // bubble sort according to count of zeroes (descending order)
    while (!done)
    {
        done = true;
        for (i = 2; i < 64; i++)
            if (zdist[i] < zdist[i - 1])
            {

                swap = zdist[i];
                zdist[i] = zdist[i - 1];
                zdist[i - 1] = swap;

                swap = sv[i];
                sv[i] = sv[i - 1];
                sv[i - 1] = swap;

                done = false;
            }
    }
}

/* -----------------------------------------------
    optimizes JFIF header for compression
    ----------------------------------------------- */
bool packJPG::pjg_optimize_header(void)
{
    unsigned char  type = 0x00; // type of current marker segment
    unsigned int   len  = 0; // length of current marker segment
    unsigned int   hpos = 0; // position in header

    unsigned int fpos; // end of marker position
    unsigned int skip; // bytes to skip
    unsigned int spos; // sub position
    int i;

    // search for DHT (0xFFC4) & DQT (0xFFDB) marker segments
    // header parser loop
    while ((int) hpos < hdrs)
    {
        type = hdrdata[hpos + 1];
        len = 2 + B_SHORT(hdrdata[hpos + 2], hdrdata[hpos + 3]);
        if (type == 0xC4)     // for DHT
        {
            fpos = hpos + len; // reassign length to end position
            hpos += 4; // skip marker & length
            while (hpos < fpos)
            {
                hpos++;
                // table found - compare with each of the four standard tables
                for (i = 0; i < 4; i++)
                {
                    for (spos = 0; spos < std_huff_lengths[i]; spos++)
                    {
                        if (hdrdata[hpos + spos] != std_huff_tables[i][spos])
                        {
                            break;
                        }
                    }
                    // check if comparison ok
                    if (spos != std_huff_lengths[i])
                    {
                        continue;
                    }

                    // if we get here, the table matches the standard table
                    // number 'i', so it can be replaced
                    hdrdata[hpos + 0] = std_huff_lengths[i] - 16 - i;
                    hdrdata[hpos + 1] = i;
                    for (spos = 2; spos < std_huff_lengths[i]; spos++)
                    {
                        hdrdata[hpos + spos] = 0x00;
                    }
                    // everything done here, so leave
                    break;
                }

                skip = 16;
                for (i = 0; i < 16; i++)
                {
                    skip += (int) hdrdata[hpos + i];
                }
                hpos += skip;
            }
        }
        else if (type == 0xDB)     // for DQT
        {
            fpos = hpos + len; // reassign length to end position
            hpos += 4; // skip marker & length
            while (hpos < fpos)
            {
                i = LBITS(hdrdata[hpos], 4);
                hpos++;
                // table found
                if (i == 1)     // get out for 16 bit precision
                {
                    hpos += 128;
                    continue;
                }
                // do diff coding for 8 bit precision
                for (spos = 63; spos > 0; spos--)
                {
                    hdrdata[hpos + spos] -= hdrdata[hpos + spos - 1];
                }

                hpos += 64;
            }
        }
        else   // skip segment
        {
            hpos += len;
        }
    }

    return true;
}

/* -----------------------------------------------
    undoes the header optimizations
    ----------------------------------------------- */
bool packJPG::pjg_unoptimize_header(void)
{
    unsigned char  type = 0x00; // type of current marker segment
    unsigned int   len  = 0; // length of current marker segment
    unsigned int   hpos = 0; // position in header

    unsigned int fpos; // end of marker position
    unsigned int skip; // bytes to skip
    unsigned int spos; // sub position
    int i;

    // search for DHT (0xFFC4) & DQT (0xFFDB) marker segments
    // header parser loop
    while ((int) hpos < hdrs)
    {
        type = hdrdata[hpos + 1];
        len = 2 + B_SHORT(hdrdata[hpos + 2], hdrdata[hpos + 3]);

        if (type == 0xC4)     // for DHT
        {
            fpos = hpos + len; // reassign length to end position
            hpos += 4; // skip marker & length
            while (hpos < fpos)
            {
                hpos++;
                // table found - check if modified
                if (hdrdata[hpos] > 2)
                {
                    // reinsert the standard table
                    i = hdrdata[hpos + 1];
                    for (spos = 0; spos < std_huff_lengths[i]; spos++)
                    {
                        hdrdata[hpos + spos] = std_huff_tables[i][spos];
                    }
                }

                skip = 16;
                for (i = 0; i < 16; i++)
                {
                    skip += (int) hdrdata[hpos + i];
                }
                hpos += skip;
            }
        }
        else if (type == 0xDB)     // for DQT
        {
            fpos = hpos + len; // reassign length to end position
            hpos += 4; // skip marker & length
            while (hpos < fpos)
            {
                i = LBITS(hdrdata[hpos], 4);
                hpos++;
                // table found
                if (i == 1)     // get out for 16 bit precision
                {
                    hpos += 128;
                    continue;
                }
                // undo diff coding for 8 bit precision
                for (spos = 1; spos < 64; spos++)
                {
                    hdrdata[hpos + spos] += hdrdata[hpos + spos - 1];
                }

                hpos += 64;
            }
        }
        else   // skip segment
        {
            hpos += len;
        }
    }

    return true;
}

/* -----------------------------------------------
    preparations for special average context
    ----------------------------------------------- */
void packJPG::pjg_aavrg_prepare(
    unsigned short** abs_coeffs, 
    int* weights, 
    unsigned short* abs_store, 
    int cmp)
{
    int w = cmpnfo[cmp].bch;

    // set up quick access arrays for all prediction positions
    abs_coeffs[0] = abs_store + (0 + ((-2)*w));    // top-top
    abs_coeffs[1] = abs_store + (-1 + ((-1)*w));   // top-left
    abs_coeffs[2] = abs_store + (0 + ((-1)*w));    // top
    abs_coeffs[3] = abs_store + (1 + ((-1)*w));    // top-right
    abs_coeffs[4] = abs_store + (-2 + ((0)*w));    // left-left
    abs_coeffs[5] = abs_store + (-1 + ((0)*w));    // left
    // copy context weighting factors
    weights[0] = abs_ctx_weights_lum[0][0][2]; // top-top
    weights[1] = abs_ctx_weights_lum[0][1][1]; // top-left
    weights[2] = abs_ctx_weights_lum[0][1][2]; // top
    weights[3] = abs_ctx_weights_lum[0][1][3]; // top-right
    weights[4] = abs_ctx_weights_lum[0][2][0]; // left-left
    weights[5] = abs_ctx_weights_lum[0][2][1]; // left
}

/* -----------------------------------------------
    special average context used in coeff encoding
    ----------------------------------------------- */
int packJPG::pjg_aavrg_context(
    unsigned short** abs_coeffs, 
    int* weights, 
    int pos, 
    int p_y, 
    int p_x, 
    int r_x)
{
    int ctx_avr = 0; // AVERAGE context
    int w_ctx = 0; // accumulated weight of context
    int w_curr; // current weight of context

    // different cases due to edge treatment
    if (p_y >= 2)
    {
        w_curr = weights[0];
        ctx_avr += abs_coeffs[0][pos] * w_curr;
        w_ctx += w_curr;
        w_curr = weights[2];
        ctx_avr += abs_coeffs[2][pos] * w_curr;
        w_ctx += w_curr;
        if (p_x >= 2)
        {
            w_curr = weights[1];
            ctx_avr += abs_coeffs[1][pos] * w_curr;
            w_ctx += w_curr;
            w_curr = weights[4];
            ctx_avr += abs_coeffs[4][pos] * w_curr;
            w_ctx += w_curr;
            w_curr = weights[5];
            ctx_avr += abs_coeffs[5][pos] * w_curr;
            w_ctx += w_curr;
        }
        else if (p_x == 1)
        {
            w_curr = weights[1];
            ctx_avr += abs_coeffs[1][pos] * w_curr;
            w_ctx += w_curr;
            w_curr = weights[5];
            ctx_avr += abs_coeffs[5][pos] * w_curr;
            w_ctx += w_curr;
        }
        if (r_x >= 1)
        {
            w_curr = weights[3];
            ctx_avr += abs_coeffs[3][pos] * w_curr;
            w_ctx += w_curr;
        }
    }
    else if (p_y == 1)
    {
        w_curr = weights[2];
        ctx_avr += abs_coeffs[2][pos] * w_curr;
        w_ctx += w_curr;
        if (p_x >= 2)
        {
            w_curr = weights[1];
            ctx_avr += abs_coeffs[1][pos] * w_curr;
            w_ctx += w_curr;
            w_curr = weights[4];
            ctx_avr += abs_coeffs[4][pos] * w_curr;
            w_ctx += w_curr;
            w_curr = weights[5];
            ctx_avr += abs_coeffs[5][pos] * w_curr;
            w_ctx += w_curr;
        }
        else if (p_x == 1)
        {
            w_curr = weights[1];
            ctx_avr += abs_coeffs[1][pos] * w_curr;
            w_ctx += w_curr;
            w_curr = weights[5];
            ctx_avr += abs_coeffs[5][pos] * w_curr;
            w_ctx += w_curr;
        }
        if (r_x >= 1)
        {
            w_curr = weights[3];
            ctx_avr += abs_coeffs[3][pos] * w_curr;
            w_ctx += w_curr;
        }
    }
    else
    {
        if (p_x >= 2)
        {
            w_curr = weights[4];
            ctx_avr += abs_coeffs[4][pos] * w_curr;
            w_ctx += w_curr;
            w_curr = weights[5];
            ctx_avr += abs_coeffs[5][pos] * w_curr;
            w_ctx += w_curr;
        }
        else if (p_x == 1)
        {
            w_curr = weights[5];
            ctx_avr += abs_coeffs[5][pos] * w_curr;
            w_ctx += w_curr;
        }
    }

    // return average context
    return (w_ctx != 0) ? (ctx_avr + (w_ctx / 2)) / w_ctx : 0;
}

/* -----------------------------------------------
    lakhani ac context used in coeff encoding
    ----------------------------------------------- */
int packJPG::pjg_lakh_context(
    signed short** coeffs_x, 
    signed short** coeffs_a, 
    int* pred_cf, 
    int pos)
{
    int pred = 0;

    // calculate partial prediction
    pred -= (coeffs_x[1][pos] + coeffs_a[1][pos]) * pred_cf[1];
    pred -= (coeffs_x[2][pos] - coeffs_a[2][pos]) * pred_cf[2];
    pred -= (coeffs_x[3][pos] + coeffs_a[3][pos]) * pred_cf[3];
    pred -= (coeffs_x[4][pos] - coeffs_a[4][pos]) * pred_cf[4];
    pred -= (coeffs_x[5][pos] + coeffs_a[5][pos]) * pred_cf[5];
    pred -= (coeffs_x[6][pos] - coeffs_a[6][pos]) * pred_cf[6];
    pred -= (coeffs_x[7][pos] + coeffs_a[7][pos]) * pred_cf[7];
    // normalize / quantize partial prediction
    pred = ((pred > 0) ? (pred + (pred_cf[0]/2)) : (pred - (pred_cf[0]/2))) / pred_cf[0];
    // complete prediction
    pred += coeffs_a[0][pos];

    return pred;
}

/* -----------------------------------------------
    Calculates coordinates for nearest neighbor context
    ----------------------------------------------- */
void packJPG::get_context_nnb(int pos, int w, int* a, int* b)
{
    // this function calculates and returns coordinates for
    // a simple 2D context
    if (pos == 0)
    {
        *a = -1;
        *b = -1;
    }
    else if ((pos % w) == 0)
    {
        *b = pos - w;
        if (pos >= (w << 1))
        {
            *a = pos - (w << 1);
        }
        else
        {
            *a = *b;
        }
    }
    else if (pos < w)
    {
        *a = pos - 1;
        if (pos >= 2)
        {
            *b = pos - 2;
        }
        else
        {
            *b = *a;
        }
    }
    else
    {
        *a = pos - 1;
        *b = pos - w;
    }
}

/* -------------------- End of PJG specific functions ---------------------- */


/* ------------------- Begin of DCT specific functions --------------------- */

/* -----------------------------------------------
    inverse DCT transform using precalc tables (fast)
    ----------------------------------------------- */
int packJPG::idct_2d_fst_8x1(int cmp, int dpos, int ix, int iy)
{
    int idct = 0;
    int ixy;

    // calculate start index
    ixy = ix << 3;

    // begin transform
    idct += colldata[cmp][ 0][dpos] * adpt_idct_8x1[cmp][ixy + 0];
    idct += colldata[cmp][ 1][dpos] * adpt_idct_8x1[cmp][ixy + 1];
    idct += colldata[cmp][ 5][dpos] * adpt_idct_8x1[cmp][ixy + 2];
    idct += colldata[cmp][ 6][dpos] * adpt_idct_8x1[cmp][ixy + 3];
    idct += colldata[cmp][14][dpos] * adpt_idct_8x1[cmp][ixy + 4];
    idct += colldata[cmp][15][dpos] * adpt_idct_8x1[cmp][ixy + 5];
    idct += colldata[cmp][27][dpos] * adpt_idct_8x1[cmp][ixy + 6];
    idct += colldata[cmp][28][dpos] * adpt_idct_8x1[cmp][ixy + 7];

    return idct;
}

/* -----------------------------------------------
    inverse DCT transform using precalc tables (fast)
    ----------------------------------------------- */
int packJPG::idct_2d_fst_1x8(int cmp, int dpos, int ix, int iy)
{
    int idct = 0;
    int ixy;

    // calculate start index
    ixy = iy << 3;

    // begin transform
    idct += colldata[cmp][ 0][dpos] * adpt_idct_1x8[cmp][ixy + 0];
    idct += colldata[cmp][ 2][dpos] * adpt_idct_1x8[cmp][ixy + 1];
    idct += colldata[cmp][ 3][dpos] * adpt_idct_1x8[cmp][ixy + 2];
    idct += colldata[cmp][ 9][dpos] * adpt_idct_1x8[cmp][ixy + 3];
    idct += colldata[cmp][10][dpos] * adpt_idct_1x8[cmp][ixy + 4];
    idct += colldata[cmp][20][dpos] * adpt_idct_1x8[cmp][ixy + 5];
    idct += colldata[cmp][21][dpos] * adpt_idct_1x8[cmp][ixy + 6];
    idct += colldata[cmp][35][dpos] * adpt_idct_1x8[cmp][ixy + 7];

    return idct;
}

/* ------------------- End of DCT specific functions ----------------------- */


/* -------------------- Begin of prediction functions ---------------------- */

/* -----------------------------------------------
    returns predictor for collection data
    ----------------------------------------------- */
//~ #if defined(USE_PLOCOI)
int packJPG::dc_coll_predictor(int cmp, int dpos)
{
    signed short* coefs = colldata[cmp][0];
    int w = cmpnfo[cmp].bch;
    int a, b, c;

    if (dpos < w)
    {
        a = coefs[dpos - 1];
        b = 0;
        c = 0;
    }
    else if ((dpos%w) == 0)
    {
        a = 0;
        b = coefs[dpos - w];
        c = 0;
    }
    else
    {
        a = coefs[dpos - 1];
        b = coefs[dpos - w];
        c = coefs[dpos - 1 - w];
    }

    return plocoi(a, b, c);
}
//~ #endif

/* -----------------------------------------------
    1D DCT predictor for DC coefficients
    ----------------------------------------------- */
//~ #if !defined(USE_PLOCOI)
int packJPG::dc_1ddct_predictor(int cmp, int dpos)
{
    int w  = cmpnfo[cmp].bch;
    int px = (dpos % w);
    int py = (dpos / w);

    int pred;
    int pa = 0;
    int pb = 0;
    int xa = 0;
    int xb = 0;
    int swap;

    // store current block DC coefficient
    swap = colldata[cmp][0][dpos];
    colldata[cmp][0][dpos] = 0;

    // calculate prediction
    if ((px > 0) && (py > 0))
    {
        pa = idct_2d_fst_8x1(cmp, dpos - 1, 7, 0);
        pb = idct_2d_fst_1x8(cmp, dpos - w, 0, 7);
        xa = idct_2d_fst_8x1(cmp, dpos, 0, 0);
        xb = idct_2d_fst_1x8(cmp, dpos, 0, 0);
        pred = ((pa - xa) + (pb - xb)) * (8 / 2);
    }
    else if (px > 0)
    {
        pa = idct_2d_fst_8x1(cmp, dpos - 1, 7, 0);
        xa = idct_2d_fst_8x1(cmp, dpos, 0, 0);
        pred = (pa - xa) * 8;
    }
    else if (py > 0)
    {
        pb = idct_2d_fst_1x8(cmp, dpos - w, 0, 7);
        xb = idct_2d_fst_1x8(cmp, dpos, 0, 0);
        pred = (pb - xb) * 8;
    }
    else
    {
        pred = 0;
    }

    // write back current DCT coefficient
    colldata[cmp][0][dpos] = swap;

    // clamp and quantize predictor
    pred = CLAMPED(-(1024 * DCT_RSC_FACTOR), (1016 * DCT_RSC_FACTOR), pred);
    pred = pred / QUANT(cmp, 0);
    pred = DCT_RESCALE(pred);


    return pred;
}
//~ #endif
