/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include "ccidecom.hxx"
#include <tools/stream.hxx>

//=============================== Huffman tables ========================

//---------------------------- White-Run ------------------------------

#define CCIWhiteTableSize 105

const CCIHuffmanTableEntry CCIWhiteTable[CCIWhiteTableSize]={
    {    0, 0x0035,  8 },
    {    1, 0x0007,  6 },
    {    2, 0x0007,  4 },
    {    3, 0x0008,  4 },
    {    4, 0x000b,  4 },
    {    5, 0x000c,  4 },
    {    6, 0x000e,  4 },
    {    7, 0x000f,  4 },
    {    8, 0x0013,  5 },
    {    9, 0x0014,  5 },
    {   10, 0x0007,  5 },
    {   11, 0x0008,  5 },
    {   12, 0x0008,  6 },
    {   13, 0x0003,  6 },
    {   14, 0x0034,  6 },
    {   15, 0x0035,  6 },
    {   16, 0x002a,  6 },
    {   17, 0x002b,  6 },
    {   18, 0x0027,  7 },
    {   19, 0x000c,  7 },
    {   20, 0x0008,  7 },
    {   21, 0x0017,  7 },
    {   22, 0x0003,  7 },
    {   23, 0x0004,  7 },
    {   24, 0x0028,  7 },
    {   25, 0x002b,  7 },
    {   26, 0x0013,  7 },
    {   27, 0x0024,  7 },
    {   28, 0x0018,  7 },
    {   29, 0x0002,  8 },
    {   30, 0x0003,  8 },
    {   31, 0x001a,  8 },
    {   32, 0x001b,  8 },
    {   33, 0x0012,  8 },
    {   34, 0x0013,  8 },
    {   35, 0x0014,  8 },
    {   36, 0x0015,  8 },
    {   37, 0x0016,  8 },
    {   38, 0x0017,  8 },
    {   39, 0x0028,  8 },
    {   40, 0x0029,  8 },
    {   41, 0x002a,  8 },
    {   42, 0x002b,  8 },
    {   43, 0x002c,  8 },
    {   44, 0x002d,  8 },
    {   45, 0x0004,  8 },
    {   46, 0x0005,  8 },
    {   47, 0x000a,  8 },
    {   48, 0x000b,  8 },
    {   49, 0x0052,  8 },
    {   50, 0x0053,  8 },
    {   51, 0x0054,  8 },
    {   52, 0x0055,  8 },
    {   53, 0x0024,  8 },
    {   54, 0x0025,  8 },
    {   55, 0x0058,  8 },
    {   56, 0x0059,  8 },
    {   57, 0x005a,  8 },
    {   58, 0x005b,  8 },
    {   59, 0x004a,  8 },
    {   60, 0x004b,  8 },
    {   61, 0x0032,  8 },
    {   62, 0x0033,  8 },
    {   63, 0x0034,  8 },
    {   64, 0x001b,  5 },
    {  128, 0x0012,  5 },
    {  192, 0x0017,  6 },
    {  256, 0x0037,  7 },
    {  320, 0x0036,  8 },
    {  384, 0x0037,  8 },
    {  448, 0x0064,  8 },
    {  512, 0x0065,  8 },
    {  576, 0x0068,  8 },
    {  640, 0x0067,  8 },
    {  704, 0x00cc,  9 },
    {  768, 0x00cd,  9 },
    {  832, 0x00d2,  9 },
    {  896, 0x00d3,  9 },
    {  960, 0x00d4,  9 },
    { 1024, 0x00d5,  9 },
    { 1088, 0x00d6,  9 },
    { 1152, 0x00d7,  9 },
    { 1216, 0x00d8,  9 },
    { 1280, 0x00d9,  9 },
    { 1344, 0x00da,  9 },
    { 1408, 0x00db,  9 },
    { 1472, 0x0098,  9 },
    { 1536, 0x0099,  9 },
    { 1600, 0x009a,  9 },
    { 1664, 0x0018,  6 },
    { 1728, 0x009b,  9 },
    { 1792, 0x0008, 11 },
    { 1856, 0x000c, 11 },
    { 1920, 0x000d, 11 },
    { 1984, 0x0012, 12 },
    { 2048, 0x0013, 12 },
    { 2112, 0x0014, 12 },
    { 2176, 0x0015, 12 },
    { 2240, 0x0016, 12 },
    { 2304, 0x0017, 12 },
    { 2368, 0x001c, 12 },
    { 2432, 0x001d, 12 },
    { 2496, 0x001e, 12 },
    { 2560, 0x001f, 12 },
    { 9999, 0x0001, 12 }    //  EOL
};

//---------------------------- Black-Run ------------------------------

#define CCIBlackTableSize 105

const CCIHuffmanTableEntry CCIBlackTable[CCIBlackTableSize]={
    {    0, 0x0037, 10 },
    {    1, 0x0002,  3 },
    {    2, 0x0003,  2 },
    {    3, 0x0002,  2 },
    {    4, 0x0003,  3 },
    {    5, 0x0003,  4 },
    {    6, 0x0002,  4 },
    {    7, 0x0003,  5 },
    {    8, 0x0005,  6 },
    {    9, 0x0004,  6 },
    {   10, 0x0004,  7 },
    {   11, 0x0005,  7 },
    {   12, 0x0007,  7 },
    {   13, 0x0004,  8 },
    {   14, 0x0007,  8 },
    {   15, 0x0018,  9 },
    {   16, 0x0017, 10 },
    {   17, 0x0018, 10 },
    {   18, 0x0008, 10 },
    {   19, 0x0067, 11 },
    {   20, 0x0068, 11 },
    {   21, 0x006c, 11 },
    {   22, 0x0037, 11 },
    {   23, 0x0028, 11 },
    {   24, 0x0017, 11 },
    {   25, 0x0018, 11 },
    {   26, 0x00ca, 12 },
    {   27, 0x00cb, 12 },
    {   28, 0x00cc, 12 },
    {   29, 0x00cd, 12 },
    {   30, 0x0068, 12 },
    {   31, 0x0069, 12 },
    {   32, 0x006a, 12 },
    {   33, 0x006b, 12 },
    {   34, 0x00d2, 12 },
    {   35, 0x00d3, 12 },
    {   36, 0x00d4, 12 },
    {   37, 0x00d5, 12 },
    {   38, 0x00d6, 12 },
    {   39, 0x00d7, 12 },
    {   40, 0x006c, 12 },
    {   41, 0x006d, 12 },
    {   42, 0x00da, 12 },
    {   43, 0x00db, 12 },
    {   44, 0x0054, 12 },
    {   45, 0x0055, 12 },
    {   46, 0x0056, 12 },
    {   47, 0x0057, 12 },
    {   48, 0x0064, 12 },
    {   49, 0x0065, 12 },
    {   50, 0x0052, 12 },
    {   51, 0x0053, 12 },
    {   52, 0x0024, 12 },
    {   53, 0x0037, 12 },
    {   54, 0x0038, 12 },
    {   55, 0x0027, 12 },
    {   56, 0x0028, 12 },
    {   57, 0x0058, 12 },
    {   58, 0x0059, 12 },
    {   59, 0x002b, 12 },
    {   60, 0x002c, 12 },
    {   61, 0x005a, 12 },
    {   62, 0x0066, 12 },
    {   63, 0x0067, 12 },
    {   64, 0x000f, 10 },
    {  128, 0x00c8, 12 },
    {  192, 0x00c9, 12 },
    {  256, 0x005b, 12 },
    {  320, 0x0033, 12 },
    {  384, 0x0034, 12 },
    {  448, 0x0035, 12 },
    {  512, 0x006c, 13 },
    {  576, 0x006d, 13 },
    {  640, 0x004a, 13 },
    {  704, 0x004b, 13 },
    {  768, 0x004c, 13 },
    {  832, 0x004d, 13 },
    {  896, 0x0072, 13 },
    {  960, 0x0073, 13 },
    { 1024, 0x0074, 13 },
    { 1088, 0x0075, 13 },
    { 1152, 0x0076, 13 },
    { 1216, 0x0077, 13 },
    { 1280, 0x0052, 13 },
    { 1344, 0x0053, 13 },
    { 1408, 0x0054, 13 },
    { 1472, 0x0055, 13 },
    { 1536, 0x005a, 13 },
    { 1600, 0x005b, 13 },
    { 1664, 0x0064, 13 },
    { 1728, 0x0065, 13 },
    { 1792, 0x0008, 11 },
    { 1856, 0x000c, 11 },
    { 1920, 0x000d, 11 },
    { 1984, 0x0012, 12 },
    { 2048, 0x0013, 12 },
    { 2112, 0x0014, 12 },
    { 2176, 0x0015, 12 },
    { 2240, 0x0016, 12 },
    { 2304, 0x0017, 12 },
    { 2368, 0x001c, 12 },
    { 2432, 0x001d, 12 },
    { 2496, 0x001e, 12 },
    { 2560, 0x001f, 12 },
    { 9999, 0x0001, 12 }    //  EOL
};


//---------------------------- 2D-Mode --------------------------------

#define CCI2DMODE_UNCOMP   0
#define CCI2DMODE_PASS     1
#define CCI2DMODE_HORZ     2
#define CCI2DMODE_VERT_L3  3
#define CCI2DMODE_VERT_L2  4
#define CCI2DMODE_VERT_L1  5
#define CCI2DMODE_VERT_0   6
#define CCI2DMODE_VERT_R1  7
#define CCI2DMODE_VERT_R2  8
#define CCI2DMODE_VERT_R3  9

#define CCI2DModeTableSize 10

const CCIHuffmanTableEntry CCI2DModeTable[CCI2DModeTableSize]={
    { CCI2DMODE_UNCOMP , 0x000f, 10 },
    { CCI2DMODE_PASS   , 0x0001,  4 },
    { CCI2DMODE_HORZ   , 0x0001,  3 },
    { CCI2DMODE_VERT_L3, 0x0002,  7 },
    { CCI2DMODE_VERT_L2, 0x0002,  6 },
    { CCI2DMODE_VERT_L1, 0x0002,  3 },
    { CCI2DMODE_VERT_0 , 0x0001,  1 },
    { CCI2DMODE_VERT_R1, 0x0003,  3 },
    { CCI2DMODE_VERT_R2, 0x0003,  6 },
    { CCI2DMODE_VERT_R3, 0x0003,  7 }
};


//-------------------------- 2D-Uncompressed-Mode ----------------------

#define CCIUNCOMP_0White_1Black  0
#define CCIUNCOMP_1White_1Black  1
#define CCIUNCOMP_2White_1Black  2
#define CCIUNCOMP_3White_1Black  3
#define CCIUNCOMP_4White_1Black  4
#define CCIUNCOMP_5White         5
#define CCIUNCOMP_0White_End     6
#define CCIUNCOMP_1White_End     7
#define CCIUNCOMP_2White_End     8
#define CCIUNCOMP_3White_End     9
#define CCIUNCOMP_4White_End    10

#define CCIUncompTableSize 11

const CCIHuffmanTableEntry CCIUncompTable[CCIUncompTableSize]={
    { CCIUNCOMP_0White_1Black, 0x0001,  1 },
    { CCIUNCOMP_1White_1Black, 0x0001,  2 },
    { CCIUNCOMP_2White_1Black, 0x0001,  3 },
    { CCIUNCOMP_3White_1Black, 0x0001,  4 },
    { CCIUNCOMP_4White_1Black, 0x0001,  5 },
    { CCIUNCOMP_5White       , 0x0001,  6 },
    { CCIUNCOMP_0White_End   , 0x0001,  7 },
    { CCIUNCOMP_1White_End   , 0x0001,  8 },
    { CCIUNCOMP_2White_End   , 0x0001,  9 },
    { CCIUNCOMP_3White_End   , 0x0001, 10 },
    { CCIUNCOMP_4White_End   , 0x0001, 11 }
};


//================== backup of the Huffman tables ============================
// To make sure that the Huffman tables do not contain errors they were entered
// from two different sources (Phew) and compared.
// Since an error could creep into the source code while maintaining it
// (e.g. an accidental key press in the editor) the tables are listed twice
// and are compared during runtime. (If the comparison fails CCIDcompressor
// throws an error) The whole thing may appear insane, but an error within the
// tables would otherwise be really hard to discover and it's very unlikely that
// one or more sample files run through all codes.

const CCIHuffmanTableEntry CCIWhiteTableSave[CCIWhiteTableSize]={
    {    0, 0x0035,  8 },
    {    1, 0x0007,  6 },
    {    2, 0x0007,  4 },
    {    3, 0x0008,  4 },
    {    4, 0x000b,  4 },
    {    5, 0x000c,  4 },
    {    6, 0x000e,  4 },
    {    7, 0x000f,  4 },
    {    8, 0x0013,  5 },
    {    9, 0x0014,  5 },
    {   10, 0x0007,  5 },
    {   11, 0x0008,  5 },
    {   12, 0x0008,  6 },
    {   13, 0x0003,  6 },
    {   14, 0x0034,  6 },
    {   15, 0x0035,  6 },
    {   16, 0x002a,  6 },
    {   17, 0x002b,  6 },
    {   18, 0x0027,  7 },
    {   19, 0x000c,  7 },
    {   20, 0x0008,  7 },
    {   21, 0x0017,  7 },
    {   22, 0x0003,  7 },
    {   23, 0x0004,  7 },
    {   24, 0x0028,  7 },
    {   25, 0x002b,  7 },
    {   26, 0x0013,  7 },
    {   27, 0x0024,  7 },
    {   28, 0x0018,  7 },
    {   29, 0x0002,  8 },
    {   30, 0x0003,  8 },
    {   31, 0x001a,  8 },
    {   32, 0x001b,  8 },
    {   33, 0x0012,  8 },
    {   34, 0x0013,  8 },
    {   35, 0x0014,  8 },
    {   36, 0x0015,  8 },
    {   37, 0x0016,  8 },
    {   38, 0x0017,  8 },
    {   39, 0x0028,  8 },
    {   40, 0x0029,  8 },
    {   41, 0x002a,  8 },
    {   42, 0x002b,  8 },
    {   43, 0x002c,  8 },
    {   44, 0x002d,  8 },
    {   45, 0x0004,  8 },
    {   46, 0x0005,  8 },
    {   47, 0x000a,  8 },
    {   48, 0x000b,  8 },
    {   49, 0x0052,  8 },
    {   50, 0x0053,  8 },
    {   51, 0x0054,  8 },
    {   52, 0x0055,  8 },
    {   53, 0x0024,  8 },
    {   54, 0x0025,  8 },
    {   55, 0x0058,  8 },
    {   56, 0x0059,  8 },
    {   57, 0x005a,  8 },
    {   58, 0x005b,  8 },
    {   59, 0x004a,  8 },
    {   60, 0x004b,  8 },
    {   61, 0x0032,  8 },
    {   62, 0x0033,  8 },
    {   63, 0x0034,  8 },
    {   64, 0x001b,  5 },
    {  128, 0x0012,  5 },
    {  192, 0x0017,  6 },
    {  256, 0x0037,  7 },
    {  320, 0x0036,  8 },
    {  384, 0x0037,  8 },
    {  448, 0x0064,  8 },
    {  512, 0x0065,  8 },
    {  576, 0x0068,  8 },
    {  640, 0x0067,  8 },
    {  704, 0x00cc,  9 },
    {  768, 0x00cd,  9 },
    {  832, 0x00d2,  9 },
    {  896, 0x00d3,  9 },
    {  960, 0x00d4,  9 },
    { 1024, 0x00d5,  9 },
    { 1088, 0x00d6,  9 },
    { 1152, 0x00d7,  9 },
    { 1216, 0x00d8,  9 },
    { 1280, 0x00d9,  9 },
    { 1344, 0x00da,  9 },
    { 1408, 0x00db,  9 },
    { 1472, 0x0098,  9 },
    { 1536, 0x0099,  9 },
    { 1600, 0x009a,  9 },
    { 1664, 0x0018,  6 },
    { 1728, 0x009b,  9 },
    { 1792, 0x0008, 11 },
    { 1856, 0x000c, 11 },
    { 1920, 0x000d, 11 },
    { 1984, 0x0012, 12 },
    { 2048, 0x0013, 12 },
    { 2112, 0x0014, 12 },
    { 2176, 0x0015, 12 },
    { 2240, 0x0016, 12 },
    { 2304, 0x0017, 12 },
    { 2368, 0x001c, 12 },
    { 2432, 0x001d, 12 },
    { 2496, 0x001e, 12 },
    { 2560, 0x001f, 12 },
    { 9999, 0x0001, 12 }    //  EOL
};

const CCIHuffmanTableEntry CCIBlackTableSave[CCIBlackTableSize]={
    {    0, 0x0037, 10 },
    {    1, 0x0002,  3 },
    {    2, 0x0003,  2 },
    {    3, 0x0002,  2 },
    {    4, 0x0003,  3 },
    {    5, 0x0003,  4 },
    {    6, 0x0002,  4 },
    {    7, 0x0003,  5 },
    {    8, 0x0005,  6 },
    {    9, 0x0004,  6 },
    {   10, 0x0004,  7 },
    {   11, 0x0005,  7 },
    {   12, 0x0007,  7 },
    {   13, 0x0004,  8 },
    {   14, 0x0007,  8 },
    {   15, 0x0018,  9 },
    {   16, 0x0017, 10 },
    {   17, 0x0018, 10 },
    {   18, 0x0008, 10 },
    {   19, 0x0067, 11 },
    {   20, 0x0068, 11 },
    {   21, 0x006c, 11 },
    {   22, 0x0037, 11 },
    {   23, 0x0028, 11 },
    {   24, 0x0017, 11 },
    {   25, 0x0018, 11 },
    {   26, 0x00ca, 12 },
    {   27, 0x00cb, 12 },
    {   28, 0x00cc, 12 },
    {   29, 0x00cd, 12 },
    {   30, 0x0068, 12 },
    {   31, 0x0069, 12 },
    {   32, 0x006a, 12 },
    {   33, 0x006b, 12 },
    {   34, 0x00d2, 12 },
    {   35, 0x00d3, 12 },
    {   36, 0x00d4, 12 },
    {   37, 0x00d5, 12 },
    {   38, 0x00d6, 12 },
    {   39, 0x00d7, 12 },
    {   40, 0x006c, 12 },
    {   41, 0x006d, 12 },
    {   42, 0x00da, 12 },
    {   43, 0x00db, 12 },
    {   44, 0x0054, 12 },
    {   45, 0x0055, 12 },
    {   46, 0x0056, 12 },
    {   47, 0x0057, 12 },
    {   48, 0x0064, 12 },
    {   49, 0x0065, 12 },
    {   50, 0x0052, 12 },
    {   51, 0x0053, 12 },
    {   52, 0x0024, 12 },
    {   53, 0x0037, 12 },
    {   54, 0x0038, 12 },
    {   55, 0x0027, 12 },
    {   56, 0x0028, 12 },
    {   57, 0x0058, 12 },
    {   58, 0x0059, 12 },
    {   59, 0x002b, 12 },
    {   60, 0x002c, 12 },
    {   61, 0x005a, 12 },
    {   62, 0x0066, 12 },
    {   63, 0x0067, 12 },
    {   64, 0x000f, 10 },
    {  128, 0x00c8, 12 },
    {  192, 0x00c9, 12 },
    {  256, 0x005b, 12 },
    {  320, 0x0033, 12 },
    {  384, 0x0034, 12 },
    {  448, 0x0035, 12 },
    {  512, 0x006c, 13 },
    {  576, 0x006d, 13 },
    {  640, 0x004a, 13 },
    {  704, 0x004b, 13 },
    {  768, 0x004c, 13 },
    {  832, 0x004d, 13 },
    {  896, 0x0072, 13 },
    {  960, 0x0073, 13 },
    { 1024, 0x0074, 13 },
    { 1088, 0x0075, 13 },
    { 1152, 0x0076, 13 },
    { 1216, 0x0077, 13 },
    { 1280, 0x0052, 13 },
    { 1344, 0x0053, 13 },
    { 1408, 0x0054, 13 },
    { 1472, 0x0055, 13 },
    { 1536, 0x005a, 13 },
    { 1600, 0x005b, 13 },
    { 1664, 0x0064, 13 },
    { 1728, 0x0065, 13 },
    { 1792, 0x0008, 11 },
    { 1856, 0x000c, 11 },
    { 1920, 0x000d, 11 },
    { 1984, 0x0012, 12 },
    { 2048, 0x0013, 12 },
    { 2112, 0x0014, 12 },
    { 2176, 0x0015, 12 },
    { 2240, 0x0016, 12 },
    { 2304, 0x0017, 12 },
    { 2368, 0x001c, 12 },
    { 2432, 0x001d, 12 },
    { 2496, 0x001e, 12 },
    { 2560, 0x001f, 12 },
    { 9999, 0x0001, 12 }    //  EOL
};


const CCIHuffmanTableEntry CCI2DModeTableSave[CCI2DModeTableSize]={
    { CCI2DMODE_UNCOMP , 0x000f, 10 },
    { CCI2DMODE_PASS   , 0x0001,  4 },
    { CCI2DMODE_HORZ   , 0x0001,  3 },
    { CCI2DMODE_VERT_L3, 0x0002,  7 },
    { CCI2DMODE_VERT_L2, 0x0002,  6 },
    { CCI2DMODE_VERT_L1, 0x0002,  3 },
    { CCI2DMODE_VERT_0 , 0x0001,  1 },
    { CCI2DMODE_VERT_R1, 0x0003,  3 },
    { CCI2DMODE_VERT_R2, 0x0003,  6 },
    { CCI2DMODE_VERT_R3, 0x0003,  7 }
};


const CCIHuffmanTableEntry CCIUncompTableSave[CCIUncompTableSize]={
    { CCIUNCOMP_0White_1Black, 0x0001,  1 },
    { CCIUNCOMP_1White_1Black, 0x0001,  2 },
    { CCIUNCOMP_2White_1Black, 0x0001,  3 },
    { CCIUNCOMP_3White_1Black, 0x0001,  4 },
    { CCIUNCOMP_4White_1Black, 0x0001,  5 },
    { CCIUNCOMP_5White       , 0x0001,  6 },
    { CCIUNCOMP_0White_End   , 0x0001,  7 },
    { CCIUNCOMP_1White_End   , 0x0001,  8 },
    { CCIUNCOMP_2White_End   , 0x0001,  9 },
    { CCIUNCOMP_3White_End   , 0x0001, 10 },
    { CCIUNCOMP_4White_End   , 0x0001, 11 }
};


CCIDecompressor::CCIDecompressor( sal_uInt32 nOpts, sal_uInt32 nImageWidth ) :
    bTableBad   ( false ),
    bStatus     ( false ),
    pIStream    ( nullptr ),
    nEOLCount   ( 0 ),
    nWidth      ( nImageWidth ),
    nOptions    ( nOpts ),
    bFirstEOL   ( false ),
    nInputBitsBuf( 0 ),
    nInputBitsBufSize( 0 ),
    nLastLineSize( 0 )
{
    if ( nOpts & CCI_OPTION_INVERSEBITORDER )
    {
        pByteSwap.reset( new sal_uInt8[ 256 ] );
        for ( int i = 0; i < 256; i++ )
        {
            pByteSwap[ i ] = sal::static_int_cast< sal_uInt8 >(
                ( i << 7 ) | ( ( i & 2 ) << 5 ) | ( ( i & 4 ) << 3 ) | ( ( i & 8 ) << 1 ) |
                ( ( i & 16 ) >> 1 ) | ( ( i & 32 ) >> 3 ) | ( ( i & 64 ) >> 5 ) | ( ( i & 128 ) >> 7 ));
        }
    }

    MakeLookUp(CCIWhiteTable,CCIWhiteTableSave,pWhiteLookUp.data(),CCIWhiteTableSize,13);
    MakeLookUp(CCIBlackTable,CCIBlackTableSave,pBlackLookUp.data(),CCIBlackTableSize,13);
    MakeLookUp(CCI2DModeTable,CCI2DModeTableSave,p2DModeLookUp.data(),CCI2DModeTableSize,10);
    MakeLookUp(CCIUncompTable,CCIUncompTableSave,pUncompLookUp.data(),CCIUncompTableSize,11);
}


CCIDecompressor::~CCIDecompressor()
{
}


void CCIDecompressor::StartDecompression( SvStream & rIStream )
{
    pIStream = &rIStream;
    nInputBitsBufSize = 0;
    bFirstEOL = true;
    bStatus = true;
    nEOLCount = 0;

    if ( bTableBad )
        return;
}

DecompressStatus CCIDecompressor::DecompressScanline( sal_uInt8 * pTarget, sal_uInt64 nTargetBits, bool bLastLine )
{
    //Read[1|2]DScanlineData take a sal_uInt16, so its either limit here or expand there
    if (nTargetBits > SAL_MAX_UINT16)
        return DecompressStatus(false, true);

    if ( nEOLCount >= 5 )   // RTC (Return To Controller)
        return DecompressStatus(true, true);

    if ( !bStatus )
        return DecompressStatus(false, true);

    // If EOL-Codes exist, the EOL-Code also appeared in front of the first line.
    // (and I thought it means 'End of Line'...)
    // Therefore we read the EOL-Code always at the beginning of each line first:
    if ( nOptions & CCI_OPTION_EOL )
    {
        if ( bFirstEOL )
        {
            sal_uInt32 nCurPos = pIStream->Tell();
            sal_uInt16 nOldInputBitsBufSize = nInputBitsBufSize;
            sal_uInt32 nOldInputBitsBuf = nInputBitsBuf;
            if ( !ReadEOL() )
            {
                nInputBitsBufSize = nOldInputBitsBufSize;
                nInputBitsBuf = nOldInputBitsBuf;
                pIStream->Seek( nCurPos );
                nOptions &=~ CCI_OPTION_EOL;                // CCITT Group 3 - Compression Type 2
            }
            bFirstEOL = false;
        }
        else
        {
            if ( !ReadEOL() )
            {
                return DecompressStatus(bStatus, true);
            }
        }
    }

    if ( nEOLCount >= 5 )   // RTC (Return To Controller)
        return DecompressStatus(true, true);

    // should the situation arise, generate a white previous line for 2D:
    if ( nOptions & CCI_OPTION_2D )
    {
        if ( pLastLine == nullptr || nLastLineSize != ( ( nTargetBits + 7 ) >> 3 ) )
        {
            nLastLineSize = ( nTargetBits + 7 ) >> 3;
            pLastLine.reset( new sal_uInt8[ nLastLineSize ] );
            memset(pLastLine.get(), 0, nLastLineSize);
        }
    }
    // conditionally align start of line to next byte:
    if ( nOptions & CCI_OPTION_BYTEALIGNROW )
        nInputBitsBufSize &= 0xfff8;

    bool b2D;
    // is it a 2D row?
    if ( nOptions & CCI_OPTION_2D )
    {
        if ( nOptions & CCI_OPTION_EOL )
            b2D = Read2DTag();
        else
            b2D = true;
    }
    else
        b2D = false;

    bool bUnchanged;
    // read scanline:
    if ( b2D )
        bUnchanged = Read2DScanlineData(pTarget, nTargetBits);
    else
        bUnchanged = Read1DScanlineData(pTarget, nTargetBits);

    // if we're in 2D mode we have to remember the line:
    if ( nOptions & CCI_OPTION_2D && bStatus )
    {
        memcpy(pLastLine.get(), pTarget, nLastLineSize);
    }

    // #i122984#
    if( !bStatus && bLastLine )
    {
        bStatus = true;
    }

    if ( pIStream->GetError() )
        bStatus = false;

    return DecompressStatus(bStatus, bUnchanged);
}


void CCIDecompressor::MakeLookUp(const CCIHuffmanTableEntry * pHufTab,
                                 const CCIHuffmanTableEntry * pHufTabSave,
                                 CCILookUpTableEntry * pLookUp,
                                 sal_uInt16 nHuffmanTableSize,
                                 sal_uInt16 nMaxCodeBits)
{
    sal_uInt16 nLookUpSize = 1 << nMaxCodeBits;
    memset(pLookUp, 0, nLookUpSize * sizeof(CCILookUpTableEntry));

    if (bTableBad)
        return;

    sal_uInt16 nMask = 0xffff >> (16-nMaxCodeBits);

    for (sal_uInt16 i = 0; i < nHuffmanTableSize; ++i)
    {
        if ( pHufTab[i].nValue!=pHufTabSave[i].nValue ||
             pHufTab[i].nCode!=pHufTabSave[i].nCode ||
             pHufTab[i].nCodeBits!=pHufTabSave[i].nCodeBits ||
             pHufTab[i].nCodeBits==0 ||
             pHufTab[i].nCodeBits>nMaxCodeBits )
        {
            bTableBad=true;
            return;
        }
        sal_uInt16 nMinCode = nMask & (pHufTab[i].nCode << (nMaxCodeBits-pHufTab[i].nCodeBits));
        sal_uInt16 nMaxCode = nMinCode | (nMask >> pHufTab[i].nCodeBits);
        for (sal_uInt16 j=nMinCode; j<=nMaxCode; ++j)
        {
            if (pLookUp[j].nCodeBits!=0)
            {
                bTableBad=true;
                return;
            }
            pLookUp[j].nValue=pHufTab[i].nValue;
            pLookUp[j].nCodeBits=pHufTab[i].nCodeBits;
        }
    }
}


bool CCIDecompressor::ReadEOL()
{
    sal_uInt16  nCode;
    sal_uInt8   nByte;

    // if (nOptions&CCI_OPTION_BYTEALIGNEOL) nMaxFillBits=7; else nMaxFillBits=0;
    // D'oh: Either the option in itiff.cxx is not set correctly (-> error in documentation)
    // or there exist some nasty export filter who always do align.
    // In addition files were found in which more than the necessary maximum of 7 filling
    // bits were found. Therefore we now generally accept up to 32 nonsense bits in front of the EOL-Code:
    // And I found a file in which up to ??? nonsense bits are written. Furthermore the byte order is switched in it. (SJ)

    sal_uInt32 nMaxPos = pIStream->Tell();
    nMaxPos += nWidth >> 3;

    for ( ;; )
    {
        while ( nInputBitsBufSize < 12 )
        {
            pIStream->ReadUChar( nByte );
            if (!pIStream->good())
                return false;
            if ( pIStream->Tell() > nMaxPos )
                return false;

            if ( nOptions & CCI_OPTION_INVERSEBITORDER )
                nByte = pByteSwap[ nByte ];
            nInputBitsBuf=(nInputBitsBuf<<8) | static_cast<sal_uInt64>(nByte);
            nInputBitsBufSize += 8;
        }
        nCode = static_cast<sal_uInt16>( ( nInputBitsBuf >> ( nInputBitsBufSize - 12 ) ) & 0x0fff );
        if ( nCode == 0x0001 )
        {
            nEOLCount++;
            nInputBitsBufSize -= 12;
            break;
        }
        else
            nInputBitsBufSize--;
    }
    return true;
}


bool CCIDecompressor::Read2DTag()
{
    // read a bit and return sal_True if it's 0, otherwise return sal_False
    if (nInputBitsBufSize==0) {
        sal_uInt8 nByte(0);
        pIStream->ReadUChar( nByte );
        if ( nOptions & CCI_OPTION_INVERSEBITORDER )
            nByte = pByteSwap[ nByte ];
        nInputBitsBuf = nByte;
        nInputBitsBufSize=8;
    }
    nInputBitsBufSize--;
    return ((nInputBitsBuf>>nInputBitsBufSize)&0x0001) == 0;
}


sal_uInt8 CCIDecompressor::ReadBlackOrWhite()
{
    // read a bit and deliver 0x00 if it's 0, otherwise 0xff
    if (nInputBitsBufSize==0) {
        sal_uInt8 nByte(0);
        pIStream->ReadUChar( nByte );
        if ( nOptions & CCI_OPTION_INVERSEBITORDER )
            nByte = pByteSwap[ nByte ];
        nInputBitsBuf = nByte;
        nInputBitsBufSize=8;
    }
    nInputBitsBufSize--;
    if ( (nInputBitsBuf>>nInputBitsBufSize) & 0x0001 ) return 0xff;
    else return 0x00;
}


sal_uInt16 CCIDecompressor::ReadCodeAndDecode(const CCILookUpTableEntry * pLookUp,
                                          sal_uInt16 nMaxCodeBits)
{
    // read a Huffman code and decode it:
    while (nInputBitsBufSize<nMaxCodeBits)
    {
        sal_uInt8 nByte(0);
        pIStream->ReadUChar( nByte );
        if ( nOptions  & CCI_OPTION_INVERSEBITORDER )
            nByte = pByteSwap[ nByte ];
        nInputBitsBuf=(nInputBitsBuf<<8) | static_cast<sal_uInt64>(nByte);
        nInputBitsBufSize+=8;
    }
    sal_uInt16 nCode = static_cast<sal_uInt16>((nInputBitsBuf>>(nInputBitsBufSize-nMaxCodeBits))
                   &(0xffff>>(16-nMaxCodeBits)));
    sal_uInt16 nCodeBits = pLookUp[nCode].nCodeBits;
    if (nCodeBits==0) bStatus=false;
    nInputBitsBufSize = nInputBitsBufSize - nCodeBits;
    return pLookUp[nCode].nValue;
}


void CCIDecompressor::FillBits(sal_uInt8 * pTarget, sal_uInt16 nTargetBits,
                               sal_uInt16 nBitPos, sal_uInt16 nNumBits,
                               sal_uInt8 nBlackOrWhite)
{
    if ( nBitPos >= nTargetBits )
        return;
    if ( nBitPos + nNumBits > nTargetBits )
        nNumBits = nTargetBits - nBitPos;

    pTarget+=nBitPos>>3;
    nBitPos&=7;

    if (nBlackOrWhite==0x00) *pTarget &= 0xff << (8-nBitPos);
    else                     *pTarget |= 0xff >> nBitPos;
    if (nNumBits>8-nBitPos) {
        nNumBits-=8-nBitPos;
        while (nNumBits>=8) {
            *(++pTarget)=nBlackOrWhite;
            nNumBits-=8;
        }
        if (nNumBits>0) *(++pTarget)=nBlackOrWhite;
    }
}

sal_uInt16 CCIDecompressor::CountBits(const sal_uInt8 * pData, sal_uInt16 nDataSizeBits,
                                      sal_uInt16 nBitPos, sal_uInt8 nBlackOrWhite)
{
    // here the number of bits belonging together is being counted
    // which all have the color nBlackOrWhite (0xff or 0x00)
    // from the position nBitPos on
    sal_uInt32 nPos = nBitPos;
    for (;;)
    {
        if (nPos>=nDataSizeBits)
        {
            nPos=nDataSizeBits;
            break;
        }
        sal_uInt8 nData = pData[nPos>>3];
        sal_uInt16 nLo = nPos & 7;
        if (nLo==0 && nData==nBlackOrWhite)
        {
            nPos+=8;
        }
        else
        {
            if ( ((nData^nBlackOrWhite) & (0x80 >> nLo))!=0)
                break;
            ++nPos;
        }
    }
    return nPos-nBitPos;
}

bool CCIDecompressor::Read1DScanlineData(sal_uInt8 * pTarget, sal_uInt16 nBitsToRead)
{
    sal_uInt16 nTargetBits = nBitsToRead;
    sal_uInt16 nCode,nCodeBits,nDataBits,nTgtFreeByteBits;
    sal_uInt8 nBlackOrWhite; // is 0xff for black or 0x00 for white
    bool bTerminatingCode;

    // the first code is always a "white-code":
    nBlackOrWhite=0x00;

    // number of bits that aren't written in the byte *pTarget yet:
    nTgtFreeByteBits=8;

    // loop through codes from the input stream:
    do {

        // fetch next 13 bits into nCodem but don't remove them from
        // the input buffer:
        while (nInputBitsBufSize<13) {
            sal_uInt8 nByte(0);
            pIStream->ReadUChar( nByte );
            if ( nOptions & CCI_OPTION_INVERSEBITORDER )
                nByte = pByteSwap[ nByte ];
            nInputBitsBuf=(nInputBitsBuf<<8) | static_cast<sal_uInt64>(nByte);
            nInputBitsBufSize+=8;
        }
        nCode=static_cast<sal_uInt16>((nInputBitsBuf>>(nInputBitsBufSize-13))&0x1fff);

        // determine the number of DataBits CodeBits:
        if (nBlackOrWhite) {
            nCodeBits=pBlackLookUp[nCode].nCodeBits;
            nDataBits=pBlackLookUp[nCode].nValue;
        }
        else {
            nCodeBits=pWhiteLookUp[nCode].nCodeBits;
            nDataBits=pWhiteLookUp[nCode].nValue;
        }
        // is that an invalid code?
        if ( nDataBits == 9999 )
        {
            return nTargetBits == nBitsToRead;
        }
        if ( nCodeBits == 0 )
        {
            return nTargetBits == nBitsToRead;  // could be filling bits now
        }
        nEOLCount = 0;
        // too much data?
        if (nDataBits>nTargetBits) {
            // Yes, could be a subsequent error cause by an invalid code
            // Therefore continue anyhow:
            nDataBits=nTargetBits;
        }

        // is that a 'Terminating-Code'?
        bTerminatingCode = nDataBits<64;

        // remove the read bits from the input buffer:
        nInputBitsBufSize = nInputBitsBufSize - nCodeBits;

        // write the number of data bits into the scanline:
        if (nDataBits>0) {
            nTargetBits = nTargetBits - nDataBits;
            if (nBlackOrWhite==0x00) *pTarget &= 0xff << nTgtFreeByteBits;
            else                     *pTarget |= 0xff >> (8-nTgtFreeByteBits);
            if (nDataBits<=nTgtFreeByteBits) {
                if (nDataBits==nTgtFreeByteBits) {
                    pTarget++;
                    nTgtFreeByteBits=8;
                }
                else nTgtFreeByteBits = nTgtFreeByteBits - nDataBits;
            }
            else {
                nDataBits = nDataBits - nTgtFreeByteBits;
                pTarget++;
                nTgtFreeByteBits=8;
                if (nDataBits >= 8)
                {
                    const sal_uInt16 nDataBytes = nDataBits / 8;
                    memset(pTarget, nBlackOrWhite, nDataBytes);
                    pTarget += nDataBytes;
                    nDataBits -= nDataBytes * 8;
                }

                if (nDataBits>0) {
                    *pTarget=nBlackOrWhite;
                    nTgtFreeByteBits = nTgtFreeByteBits - nDataBits;
                }
            }
        }

        // should the situation arise, switch Black <-> White:
        if (bTerminatingCode) nBlackOrWhite = ~nBlackOrWhite;

    } while (nTargetBits>0 || !bTerminatingCode);

    return nTargetBits == nBitsToRead;
}

bool CCIDecompressor::Read2DScanlineData(sal_uInt8 * pTarget, sal_uInt16 nTargetBits)
{
    sal_uInt16 n2DMode,nBitPos,nUncomp,nRun,nRun2,nt;
    sal_uInt8 nBlackOrWhite;

    nBlackOrWhite=0x00;
    nBitPos=0;

    while (nBitPos<nTargetBits && bStatus) {

        n2DMode=ReadCodeAndDecode(p2DModeLookUp.data(),10);
        if (!bStatus)
            return nBitPos == 0;

        if (n2DMode==CCI2DMODE_UNCOMP) {
            for (;;) {
                nUncomp=ReadCodeAndDecode(pUncompLookUp.data(),11);
                if (!bStatus)
                    break;
                if ( nUncomp <= CCIUNCOMP_4White_1Black ) {
                    nRun=nUncomp-CCIUNCOMP_0White_1Black;
                    FillBits(pTarget,nTargetBits,nBitPos,nRun,0x00);
                    nBitPos = nBitPos + nRun;
                    FillBits(pTarget,nTargetBits,nBitPos,1,0xff);
                    nBitPos++;
                }
                else if ( nUncomp == CCIUNCOMP_5White ) {
                    FillBits(pTarget,nTargetBits,nBitPos,5,0x00);
                    nBitPos = nBitPos + 5;
                }
                else {
                    nRun=nUncomp-CCIUNCOMP_0White_End;
                    FillBits(pTarget,nTargetBits,nBitPos,nRun,0x00);
                    nBitPos = nBitPos + nRun;
                    nBlackOrWhite=ReadBlackOrWhite();
                    break;
                }
            }
        }

        else if (n2DMode==CCI2DMODE_PASS) {
            if (nBitPos==0 && nBlackOrWhite==0x00 && CountBits(pLastLine.get(), nTargetBits, 0, 0xff)!=0) nRun=0;
            else {
                nRun = CountBits(pLastLine.get(), nTargetBits, nBitPos, ~nBlackOrWhite);
                nRun = nRun + CountBits(pLastLine.get(), nTargetBits, nBitPos + nRun, nBlackOrWhite);
            }
            nRun = nRun + CountBits(pLastLine.get(), nTargetBits, nBitPos + nRun, ~nBlackOrWhite);
            FillBits(pTarget,nTargetBits,nBitPos,nRun,nBlackOrWhite);
            nBitPos = nBitPos + nRun;
        }

        else if (n2DMode==CCI2DMODE_HORZ) {
            if (nBlackOrWhite==0x00) {
                nRun=0;
                do {
                    nt=ReadCodeAndDecode(pWhiteLookUp.data(),13);
                    nRun = nRun + nt;
                } while (nt>=64);
                nRun2=0;
                do {
                    nt=ReadCodeAndDecode(pBlackLookUp.data(),13);
                    nRun2 = nRun2 + nt;
                } while (nt>=64);
            }
            else {
                nRun=0;
                do {
                    nt=ReadCodeAndDecode(pBlackLookUp.data(),13);
                    nRun = nRun + nt;
                } while (nt>=64);
                nRun2=0;
                do {
                    nt=ReadCodeAndDecode(pWhiteLookUp.data(),13);
                    nRun2 = nRun2 + nt;
                } while (nt>=64);
            }
            FillBits(pTarget,nTargetBits,nBitPos,nRun,nBlackOrWhite);
            nBitPos = nBitPos + nRun;
            FillBits(pTarget,nTargetBits,nBitPos,nRun2,~nBlackOrWhite);
            nBitPos = nBitPos + nRun2;
        }

        else { // it's one of the modes CCI2DMODE_VERT_...
            if (nBitPos==0 && nBlackOrWhite==0x00 && CountBits(pLastLine.get(), nTargetBits, 0, 0xff)!=0) nRun=0;
            else {
                nRun = CountBits(pLastLine.get(), nTargetBits, nBitPos, ~nBlackOrWhite);
                nRun = nRun + CountBits(pLastLine.get(), nTargetBits, nBitPos + nRun, nBlackOrWhite);
            }
            nRun+=n2DMode-CCI2DMODE_VERT_0;
            FillBits(pTarget,nTargetBits,nBitPos,nRun,nBlackOrWhite);
            nBitPos = nBitPos + nRun;
            nBlackOrWhite=~nBlackOrWhite;
        }
    }

    return nBitPos == 0;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
