public static void decompress(bitReader br, bmpWriter out) {
    try {
      System.out.print("Decompressing file... ");
      long initTime = System.currentTimeMillis();
      int width = out.getWidth();
      int height = out.getHeight();      
      byte[] filters = readFilters(br, width, height, filterZoneDim);
      byte[] colorFilters = readColorFilters(br, width, height, colorFilterZoneDim);
      HTreeReaderGestor hrg = new HTreeReaderGestor(maxDecisions, br);
      huffmanReader[] readers = hrg.getReaders();
      int where = 0;
      int[][] precInfo = new int[3][width];
      int[][] info = new int[3][width];
      int curFil = 0;
      int curColFil = 0;
      int xZones = ((width - 1) >> filterZoneDimBits) + 1;
      int colxZones = ((width - 1) >> colorFilterZoneDimBits) + 1;
      int[][] low = new int[3][width];
      int[][] precLow = new int[3][width];
      int[] left = new int[3];
      byte[] lowF0 = new byte[width];
      byte[] precLowF0 = new byte[width];
      byte[] lowF1 = new byte[width];
      byte[] precLowF1 = new byte[width];
      byte[] lowF2 = new byte[width];
      byte[] precLowF2 = new byte[width];
      byte leftF0 = 0;
      byte leftF1 = 0;
      byte leftF2 = 0;

      precLow = low;
      low = new int[3][width];
      precInfo = info;
      info = new int[3][width];
      filterGestor fg = null;
      if (advancedColorFilter) {
        //fg = new advancedFilterGestor();       Not implemented
      } else {
        fg = new filterGestor();
      }
      curFil = filterOfZone(0, 0, width, filters);
      curColFil = colorFilterOfZone(0, 0, width, colorFilters);
      filterer fil = fg.getFilter(curFil);
      colorFilterer cfil = fg.getColFilter(curColFil);
      int val0, val1, val2;
      //for (int i3 = 0; i3 < 3; i3 ++) {                   //        i == 0, i2 == 0
        where = decide(0, 0, 0, precInfo, info, curFil, curColFil, left[0], precLow[0][0]);
        val0 = readers[where].readVal(br);
        left[0] = val0;
        low[0][0] = val0;
        where = decide(0, 0, 1, precInfo, info, curFil, curColFil, left[1], precLow[1][0]);
        val1 = readers[where].readVal(br);
        left[1] = val1;
        low[1][0] = val1;
        where = decide(0, 0, 2, precInfo, info, curFil, curColFil, left[2], precLow[2][0]);
        val2 = readers[where].readVal(br);
        left[2] = val2;
        low[2][0] = val2;
        val0 = (byte)(val0 + cfil.colFilter((byte)val0, (byte)val1, (byte)val2, 0));
        val1 = (byte)(val1 + cfil.colFilter((byte)val0, (byte)val1, (byte)val2, 1));
        val2 = (byte)(val2 + cfil.colFilter((byte)val0, (byte)val1, (byte)val2, 2));
        leftF0 = (byte)val0;
        leftF1 = (byte)val1;
        leftF2 = (byte)val2;
        lowF0[0] = leftF0;
        lowF1[0] = leftF1;
        lowF2[0] = leftF2;
        out.writeTriplet(leftF0, leftF1, leftF2);

      //}

      for (int i = 1; i < width - 1; i ++) {             //         i2 == 0
        if ((i & (filterZoneDim - 1)) == 0) {
          curFil = filterOfZone(i, 0, width, filters);
          curColFil = colorFilterOfZone(i, 0, width, colorFilters);
          fil = fg.getFilter(curFil);
          cfil = fg.getColFilter(curColFil);
        }
        //for (int i3 = 0; i3 < 3; i3 ++) {
          where = decide(i, 0, 0, precInfo, info, curFil, curColFil, left[0], precLow[0][i]);
          val0 = readers[where].readVal(br);
          left[0] = val0;
          low[0][i] = val0;
          where = decide(i, 0, 1, precInfo, info, curFil, curColFil, left[1], precLow[1][i]);
          val1 = readers[where].readVal(br);
          left[1] = val1;
          low[1][i] = val1;
          where = decide(i, 0, 2, precInfo, info, curFil, curColFil, left[2], precLow[2][i]);
          val2 = readers[where].readVal(br);
          left[2] = val2;
          low[2][i] = val2;
          val0 = (byte)(val0 + cfil.colFilter((byte)val0, (byte)val1, (byte)val2, 0));
          val1 = (byte)(val1 + cfil.colFilter((byte)val0, (byte)val1, (byte)val2, 1));
          val2 = (byte)(val2 + cfil.colFilter((byte)val0, (byte)val1, (byte)val2, 2));
          val0 = (byte)(val0 + safeFilter(i, 0, leftF0, precLowF0[i], precLowF0[i - 1],
                                          precLowF0[i + 1], curFil, fil, width));
          val1 = (byte)(val1 + safeFilter(i, 0, leftF1, precLowF1[i], precLowF1[i - 1],
                                          precLowF1[i + 1], curFil, fil, width));
          val2 = (byte)(val2 + safeFilter(i, 0, leftF2, precLowF2[i], precLowF2[i - 1],
                                          precLowF2[i + 1], curFil, fil, width));

          leftF0 = (byte)val0;
          leftF1 = (byte)val1;
          leftF2 = (byte)val2;
          lowF0[i] = leftF0;
          lowF1[i] = leftF1;
          lowF2[i] = leftF2;
          out.writeTriplet(leftF0, leftF1, leftF2);


        //}
      }

      curFil = filterOfZone(width - 1, 0, width, filters);           // i2 == 0, i = width - 1
      curColFil = colorFilterOfZone(width - 1, 0, width, colorFilters);
      fil = fg.getFilter(curFil);
      cfil = fg.getColFilter(curColFil);

      //for (int i3 = 0; i3 < 3; i3 ++) {
        where = decide(width - 1, 0, 0, precInfo, info, curFil, curColFil, left[0], precLow[0][width - 1]);
        val0 = readers[where].readVal(br);
        left[0] = val0;
        low[0][width - 1] = val0;
        where = decide(width - 1, 0, 1, precInfo, info, curFil, curColFil, left[1], precLow[1][width - 1]);
        val1 = readers[where].readVal(br);
        left[1] = val1;
        low[1][width - 1] = val1;
        where = decide(width - 1, 0, 2, precInfo, info, curFil, curColFil, left[2], precLow[2][width - 1]);
        val2 = readers[where].readVal(br);
        left[2] = val2;
        low[2][width - 1] = val2;
        val0 = (byte)(val0 + cfil.colFilter((byte)val0, (byte)val1, (byte)val2, 0));
        val1 = (byte)(val1 + cfil.colFilter((byte)val0, (byte)val1, (byte)val2, 1));
        val2 = (byte)(val2 + cfil.colFilter((byte)val0, (byte)val1, (byte)val2, 2));
        val0 = (byte)(val0 + safeFilter(width - 1, 0, leftF0, precLowF0[width - 1], precLowF0[width - 2],
                                        (byte)0, curFil, fil, width));
        val1 = (byte)(val1 + safeFilter(width - 1, 0, leftF1, precLowF1[width - 1], precLowF1[width - 2],
                                        (byte)0, curFil, fil, width));
        val2 = (byte)(val2 + safeFilter(width - 1, 0, leftF2, precLowF2[width - 1], precLowF2[width - 2],
                                        (byte)0, curFil, fil, width));

        leftF0 = (byte)val0;
        leftF1 = (byte)val1;
        leftF2 = (byte)val2;
        lowF0[width - 1] = leftF0;
        lowF1[width - 1] = leftF1;
        lowF2[width - 1] = leftF2;
        out.writeTriplet(leftF0, leftF1, leftF2);


      //}

      for (int i2 = 1; i2 < height; i2 ++) {             // i2 > 0
        left = new int[3];
        precLow = low;
        low = new int[3][width];
        precInfo = info;
        info = new int[3][width];
        precLowF0 = lowF0;
        lowF0 = new byte[width];
        precLowF1 = lowF1;
        lowF1 = new byte[width];
        precLowF2 = lowF2;
        lowF2 = new byte[width];
        curFil = filterOfZone(0, i2, width, filters);
        curColFil = colorFilterOfZone(0, i2, width, colorFilters);
        fil = fg.getFilter(curFil);
        cfil = fg.getColFilter(curColFil);

        //for (int i3 = 0; i3 < 3; i3 ++) {                //    i == 0
        where = decide(0, i2, 0, precInfo, info, curFil, curColFil, left[0], precLow[0][0]);
        val0 = readers[where].readVal(br);
        left[0] = val0;
        low[0][0] = val0;
        where = decide(0, i2, 1, precInfo, info, curFil, curColFil, left[1], precLow[1][0]);
        val1 = readers[where].readVal(br);
        left[1] = val1;
        low[1][0] = val1;
        where = decide(0, i2, 2, precInfo, info, curFil, curColFil, left[2], precLow[2][0]);
        val2 = readers[where].readVal(br);
        left[2] = val2;
        low[2][0] = val2;
        val0 = (byte)(val0 + cfil.colFilter((byte)val0, (byte)val1, (byte)val2, 0));
        val1 = (byte)(val1 + cfil.colFilter((byte)val0, (byte)val1, (byte)val2, 1));
        val2 = (byte)(val2 + cfil.colFilter((byte)val0, (byte)val1, (byte)val2, 2));
        val0 = (byte)(val0 + safeFilter(0, i2, leftF0, precLowF0[0], (byte)0,
                                        precLowF0[1], curFil, fil, width));
        val1 = (byte)(val1 + safeFilter(0, i2, leftF1, precLowF1[0], (byte)0,
                                        precLowF1[1], curFil, fil, width));
        val2 = (byte)(val2 + safeFilter(0, i2, leftF2, precLowF2[0], (byte)0,
                                        precLowF2[1], curFil, fil, width));

        leftF0 = (byte)val0;
        leftF1 = (byte)val1;
        leftF2 = (byte)val2;
        lowF0[0] = leftF0;
        lowF1[0] = leftF1;
        lowF2[0] = leftF2;
        out.writeTriplet(leftF0, leftF1, leftF2);

        byte low0 = precLowF0[0];
        byte low1 = precLowF1[0];
        byte low2 = precLowF2[0];
        byte low0n = precLowF0[1];
        byte low1n = precLowF1[1];
        byte low2n = precLowF2[1];
        byte low0p = 0;
        byte low1p = 0;
        byte low2p = 0;
        //}

        for (int i = 1; i < width - 1; i ++) {            // i > 0, i2 > 0  -------------------> Parte intensa
          if ((i & (filterZoneDim - 1)) == 0) {
            curFil = filterOfZone(i, i2, width, filters);
            curColFil = colorFilterOfZone(i, i2, width, colorFilters);
            fil = fg.getFilter(curFil);
            cfil = fg.getColFilter(curColFil);
          }
          //for (int i3 = 0; i3 < 3; i3 ++) {
            where = decideSafe0(i, i2, precInfo, info, curFil, curColFil, left[0], precLow[0][i]);
            val0 = readers[where].readVal(br);
            left[0] = val0;
            low[0][i] = val0;
            where = decideSafe12(i, i2, 1, precInfo, info, curFil, curColFil, left[1], precLow[1][i]);
            val1 = readers[where].readVal(br);
            left[1] = val1;
            low[1][i] = val1;
            where = decideSafe12(i, i2, 2, precInfo, info, curFil, curColFil, left[2], precLow[2][i]);
            val2 = readers[where].readVal(br);
            left[2] = val2;
            low[2][i] = val2;
            val0 = (byte)(val0 + cfil.colFilter((byte)val0, (byte)val1, (byte)val2, 0));
            val1 = (byte)(val1 + cfil.colFilter((byte)val0, (byte)val1, (byte)val2, 1));
            val2 = (byte)(val2 + cfil.colFilter((byte)val0, (byte)val1, (byte)val2, 2));
            low0p = low0;
            low1p = low1;
            low2p = low2;
            low0 = low0n;
            low1 = low1n;
            low2 = low2n;
            low0n = precLowF0[i + 1];
            low1n = precLowF1[i + 1];
            low2n = precLowF2[i + 1];
            leftF0 = (byte)(val0 + fil.filter(leftF0, low0, low0p, low0n));
            leftF1 = (byte)(val1 + fil.filter(leftF1, low1, low1p, low1n));
            leftF2 = (byte)(val2 + fil.filter(leftF2, low2, low2p, low2n));
            lowF0[i] = leftF0;
            lowF1[i] = leftF1;
            lowF2[i] = leftF2;
            out.writeTriplet(leftF0, leftF1, leftF2);

          //}
        }

        curFil = filterOfZone(width - 1, i2, width, filters);
        curColFil = colorFilterOfZone(width - 1, i2, width, colorFilters);
        fil = fg.getFilter(curFil);
        cfil = fg.getColFilter(curColFil);

        //for (int i3 = 0; i3 < 3; i3 ++) {              //     i == width - 1
          where = decide(width - 1, i2, 0, precInfo, info, curFil, curColFil, left[0], precLow[0][width - 1]);
          val0 = readers[where].readVal(br);
          left[0] = val0;
          low[0][width - 1] = val0;
          where = decide(width - 1, i2, 1, precInfo, info, curFil, curColFil, left[1], precLow[1][width - 1]);
          val1 = readers[where].readVal(br);
          left[1] = val1;
          low[1][width - 1] = val1;
          where = decide(width - 1, i2, 2, precInfo, info, curFil, curColFil, left[2], precLow[2][width - 1]);
          val2 = readers[where].readVal(br);
          left[2] = val2;
          low[2][width - 1] = val2;
          val0 = (byte)(val0 + cfil.colFilter((byte)val0, (byte)val1, (byte)val2, 0));
          val1 = (byte)(val1 + cfil.colFilter((byte)val0, (byte)val1, (byte)val2, 1));
          val2 = (byte)(val2 + cfil.colFilter((byte)val0, (byte)val1, (byte)val2, 2));
          val0 = (byte)(val0 + safeFilter(width - 1, i2, leftF0, precLowF0[width - 1], precLowF0[width - 2],
                                          (byte)0, curFil, fil, width));
          val1 = (byte)(val1 + safeFilter(width - 1, i2, leftF1, precLowF1[width - 1], precLowF1[width - 2],
                                          (byte)0, curFil, fil, width));
          val2 = (byte)(val2 + safeFilter(width - 1, i2, leftF2, precLowF2[width - 1], precLowF2[width - 2],
                                          (byte)0, curFil, fil, width));

          leftF0 = (byte)val0;
          leftF1 = (byte)val1;
          leftF2 = (byte)val2;
          lowF0[width - 1] = leftF0;
          lowF1[width - 1] = leftF1;
          lowF2[width - 1] = leftF2;
          out.writeTriplet(leftF0, leftF1, leftF2);

        //}

      }
      System.out.println("done. ");
      long finalTime = System.currentTimeMillis() - initTime;
      br.close();
      out.close();
      if (timeVerbose) { System.out.println("Decompression took " + (finalTime) + " ms."); }
    } catch (Exception e) { 
      System.err.println("Error caused decompression process to abort: " + e.getMessage());
    }
  }