docs: @Override
      public ImageSource apply(ImageSource source) {
          int width = source.getWidth();
          int height = source.getHeight();
  
          int[][] result = new int[height][width];
  
          if (source.isGrayscale()) {
              for (int i = 0; i < height; i++) {
  
                  int iMin = Math.max(0, i - blockRadius);
                  int iMax = Math.min(height, i + blockRadius + 1);
                  int h = iMax - iMin;
  
                  int jMin = Math.max(0, -blockRadius);
                  int jMax = Math.min(width - 1, blockRadius);
  
                  int[] hist = new int[bins + 1];
                  int[] clippedHist = new int[bins + 1];
  
                  for (int k = iMin; k < iMax; k++) {
                      for (int l = jMin; l < jMax; l++) {
                          ++hist[roundPositive(source.getGray(l, k) / 255.0f * bins)];
                      }
                  }
  
                  for (int j = 0; j < width; j++) {
                      int v = roundPositive(source.getGray(j, i) / 255.0f * bins);
  
                      int xMin = Math.max(0, j - blockRadius);
                      int xMax = j + blockRadius + 1;
                      int w = Math.min(width, xMax) - xMin;
                      int n = h * w;
  
                      int limit = (int) (slope * n / bins + 0.5f);
  
                      /* remove left behind values from histogram */
                      if (xMin > 0) {
                          int xMin1 = xMin - 1;
                          for (int yi = iMin; yi < iMax; ++yi) {
                              --hist[roundPositive(source.getGray(xMin1, yi) / 255.0f * bins)];
                          }
                      }
  
                      /* add newly included values to histogram */
                      if (xMax <= width) {
                          int xMax1 = xMax - 1;
                          for (int yi = iMin; yi < iMax; ++yi) {
                              ++hist[roundPositive(source.getGray(xMax1, yi) / 255.0f * bins)];
                          }
                      }
  
                      System.arraycopy(hist, 0, clippedHist, 0, hist.length);
                      int clippedEntries = 0, clippedEntriesBefore;
  
                      do {
                          clippedEntriesBefore = clippedEntries;
                          clippedEntries = 0;
                          for (int z = 0; z <= bins; ++z) {
                              int d = clippedHist[z] - limit;
                              if (d > 0) {
                                  clippedEntries += d;
                                  clippedHist[z] = limit;
                              }
                          }
  
                          int d = clippedEntries / (bins + 1);
                          int m = clippedEntries % (bins + 1);
                          for (int z = 0; z <= bins; ++z) {
                              clippedHist[z] += d;
                          }
  
                          if (m != 0) {
                              int s = bins / m;
                              for (int z = 0; z <= bins; z += s) {
                                  ++clippedHist[z];
                              }
                          }
                      }
                      while (clippedEntries != clippedEntriesBefore);
  
                      /* build cdf of clipped histogram */
                      int hMin = bins;
                      for (int z = 0; z < hMin; ++z) {
                          if (clippedHist[z] != 0) hMin = z;
                      }
  
                      int cdf = 0;
                      for (int z = hMin; z <= v; ++z) {
                          cdf += clippedHist[z];
                      }
  
                      int cdfMax = cdf;
                      for (int z = v + 1; z <= bins; ++z) {
                          cdfMax += clippedHist[z];
                      }
  
                      int cdfMin = clippedHist[hMin];
  
                      result[i][j] = roundPositive((cdf - cdfMin) / (float) (cdfMax - cdfMin) * 255.0f);
                  }
  
                  for (int a = 0; a < width; a++) {
                      source.setGray(a, i, result[i][a]);
                  }
              }
          } else {
              MatrixSource gray = new MatrixSource(source);
  
              Grayscale gs = new Grayscale(algorithm);
              // It is not OneBandSource
              gs.apply(gray);
  
              for (int i = 0; i < height; i++) {
  
                  int iMin = Math.max(0, i - blockRadius);
                  int iMax = Math.min(height, i + blockRadius + 1);
                  int h = iMax - iMin;
  
                  int jMin = Math.max(0, -blockRadius);
                  int jMax = Math.min(width - 1, blockRadius);
  
                  int[] hist = new int[bins + 1];
                  int[] clippedHist = new int[bins + 1];
  
                  for (int k = iMin; k < iMax; k++) {
                      for (int l = jMin; l < jMax; l++) {
                          ++hist[roundPositive(gray.getGray(l, k) / 255.0f * bins)];
                      }
                  }
  
                  for (int j = 0; j < width; j++) {
                      int v = roundPositive(gray.getGray(j, i) / 255.0f * bins);
  
                      int xMin = Math.max(0, j - blockRadius);
                      int xMax = j + blockRadius + 1;
                      int w = Math.min(width, xMax) - xMin;
                      int n = h * w;
  
                      int limit = (int) (slope * n / bins + 0.5f);
  
                      /* remove left behind values from histogram */
                      if (xMin > 0) {
                          int xMin1 = xMin - 1;
                          for (int yi = iMin; yi < iMax; ++yi) {
                              --hist[roundPositive(gray.getGray(xMin1, yi) / 255.0f * bins)];
                          }
                      }
  
                      /* add newly included values to histogram */
                      if (xMax <= width) {
                          int xMax1 = xMax - 1;
                          for (int yi = iMin; yi < iMax; ++yi) {
                              ++hist[roundPositive(gray.getGray(xMax1, yi) / 255.0f * bins)];
                          }
                      }
  
                      System.arraycopy(hist, 0, clippedHist, 0, hist.length);
                      int clippedEntries = 0, clippedEntriesBefore;
                      do {
                          clippedEntriesBefore = clippedEntries;
                          clippedEntries = 0;
                          for (int z = 0; z <= bins; ++z) {
                              int d = clippedHist[z] - limit;
                              if (d > 0) {
                                  clippedEntries += d;
                                  clippedHist[z] = limit;
                              }
                          }
  
                          int d = clippedEntries / (bins + 1);
                          int m = clippedEntries % (bins + 1);
                          for (int z = 0; z <= bins; ++z)
                              clippedHist[z] += d;
  
                          if (m != 0) {
                              int s = bins / m;
                              for (int z = 0; z <= bins; z += s)
                                  ++clippedHist[z];
                          }
                      }
                      while (clippedEntries != clippedEntriesBefore);
  
                      /* build cdf of clipped histogram */
                      int hMin = bins;
                      for (int z = 0; z < hMin; ++z) {
                          if (clippedHist[z] != 0) hMin = z;
                      }
  
                      int cdf = 0;
                      for (int z = hMin; z <= v; ++z) {
                          cdf += clippedHist[z];
                      }
  
                      int cdfMax = cdf;
                      for (int z = v + 1; z <= bins; ++z) {
                          cdfMax += clippedHist[z];
                      }
  
                      int cdfMin = clippedHist[hMin];
  
                      result[i][j] = roundPositive((cdf - cdfMin) / (float) (cdfMax - cdfMin) * 255.0f);
                  }
  
                  for (int a = 0; a < width; a++) {
                      float s = (float) result[i][a] / (float) gray.getGray(a, i);
  
                      float r = ColorHelper.clamp(roundPositive(s * source.getR(a, i)));
                      float g = ColorHelper.clamp(roundPositive(s * source.getG(a, i)));
                      float b = ColorHelper.clamp(roundPositive(s * source.getB(a, i)));
  
                      int rgb = ColorHelper.getRGB((int) r, (int) g, (int) b);
  
                      source.setRGB(a, i, rgb);
                  }
              }
          }
          return source;
      }