/**
   * Extract blob.
   *
   * @param id ID.
   * @param fastBitmap Image to be processed.
   * @return Image with the extracted blob.
   */
  public FastBitmap Extract(int id, FastBitmap fastBitmap) {

    // Check if blobs list is null.
    if (this.blobs == null) this.blobs = new BlobDetection().ProcessImage(fastBitmap);

    FastBitmap image;

    if (fastBitmap.isGrayscale()) {
      image =
          new FastBitmap(
              fastBitmap.getWidth(), fastBitmap.getHeight(), FastBitmap.ColorSpace.Grayscale);
      for (IntPoint p : blobs.get(id).getPoints()) {
        image.setGray(p.x, p.y, fastBitmap.getGray(p.x, p.y));
      }
    } else {
      image =
          new FastBitmap(fastBitmap.getWidth(), fastBitmap.getHeight(), FastBitmap.ColorSpace.RGB);
      for (IntPoint p : blobs.get(id).getPoints()) {
        image.setRed(p.x, p.y, fastBitmap.getRed(p.x, p.y));
        image.setGreen(p.x, p.y, fastBitmap.getGreen(p.x, p.y));
        image.setBlue(p.x, p.y, fastBitmap.getBlue(p.x, p.y));
      }
    }
    return image;
  }
  @Override
  public void applyInPlace(FastBitmap fastBitmap) {
    int width = fastBitmap.getWidth();
    int height = fastBitmap.getHeight();
    int noise = (width * height * noiseAmount) / 200;

    if (fastBitmap.isGrayscale()) {
      for (int i = 0; i < noise; i++) {
        int x = random.nextInt(height);
        int y = random.nextInt(width);

        int[] c = new int[] {0, 255};
        int color = random.nextInt(2);

        fastBitmap.setGray(x, y, c[color]);
      }
    } else if (fastBitmap.isRGB()) {
      for (int i = 0; i < noise; i++) {
        int x = random.nextInt(height);
        int y = random.nextInt(width);

        int[] c = new int[] {0, 255};
        int band = random.nextInt(2);
        int color = random.nextInt(2);

        if (band == 0) {
          fastBitmap.setRed(x, y, c[color]);
        } else if (band == 1) {
          fastBitmap.setGreen(x, y, c[color]);
        } else if (band == 2) {
          fastBitmap.setBlue(x, y, c[color]);
        }
      }
    }
  }
  @Override
  public void applyInPlace(FastBitmap fastBitmap) {

    if (fastBitmap.isRGB() && overlay.isRGB()) {

      int w = fastBitmap.getWidth();
      int h = fastBitmap.getHeight();

      switch (algorithm) {
        case Lighten:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              if (overlay.getRed(i, j) > fastBitmap.getRed(i, j)) {
                fastBitmap.setRed(i, j, overlay.getRed(i, j));
              }
              if (overlay.getGreen(i, j) > fastBitmap.getGreen(i, j)) {
                fastBitmap.setGreen(i, j, overlay.getGreen(i, j));
              }
              if (overlay.getBlue(i, j) > fastBitmap.getBlue(i, j)) {
                fastBitmap.setBlue(i, j, overlay.getBlue(i, j));
              }
            }
          }
          break;
        case Darken:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              if (overlay.getRed(i, j) < fastBitmap.getRed(i, j)) {
                fastBitmap.setRed(i, j, overlay.getRed(i, j));
              }
              if (overlay.getGreen(i, j) < fastBitmap.getGreen(i, j)) {
                fastBitmap.setGreen(i, j, overlay.getGreen(i, j));
              }
              if (overlay.getBlue(i, j) < fastBitmap.getBlue(i, j)) {
                fastBitmap.setBlue(i, j, overlay.getBlue(i, j));
              }
            }
          }
          break;
        case Multiply:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              int r = fastBitmap.getRed(i, j) * overlay.getRed(i, j) / 255;
              int g = fastBitmap.getGreen(i, j) * overlay.getGreen(i, j) / 255;
              int b = fastBitmap.getBlue(i, j) * overlay.getBlue(i, j) / 255;
              fastBitmap.setRGB(i, j, r, g, b);
            }
          }
          break;
        case Average:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              int r = fastBitmap.getRed(i, j) + overlay.getRed(i, j) / 2;
              int g = fastBitmap.getGreen(i, j) + overlay.getGreen(i, j) / 2;
              int b = fastBitmap.getBlue(i, j) + overlay.getBlue(i, j) / 2;
              fastBitmap.setRGB(i, j, r, g, b);
            }
          }
          break;
        case Add:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              int r = Math.min(fastBitmap.getRed(i, j) + overlay.getRed(i, j), 255);
              int g = Math.min(fastBitmap.getGreen(i, j) + overlay.getGreen(i, j), 255);
              int b = Math.min(fastBitmap.getBlue(i, j) + overlay.getBlue(i, j), 255);
              fastBitmap.setRGB(i, j, r, g, b);
            }
          }
          break;
        case Subtract:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              int temp = fastBitmap.getRed(i, j) + overlay.getRed(i, j);
              if (temp < 255) {
                fastBitmap.setRed(i, j, 0);
              } else {
                fastBitmap.setRed(i, j, temp - 255);
              }

              temp = fastBitmap.getGreen(i, j) + overlay.getGreen(i, j);
              if (temp < 255) {
                fastBitmap.setGreen(i, j, 0);
              } else {
                fastBitmap.setGreen(i, j, temp - 255);
              }

              temp = fastBitmap.getBlue(i, j) + overlay.getBlue(i, j);
              if (temp < 255) {
                fastBitmap.setBlue(i, j, 0);
              } else {
                fastBitmap.setBlue(i, j, temp - 255);
              }
            }
          }
          break;
        case Difference:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              int r = Math.abs(fastBitmap.getRed(i, j) - overlay.getRed(i, j));
              int g = Math.abs(fastBitmap.getGreen(i, j) - overlay.getGreen(i, j));
              int b = Math.abs(fastBitmap.getBlue(i, j) - overlay.getBlue(i, j));
              fastBitmap.setRGB(i, j, r, g, b);
            }
          }
          break;
        case Negation:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              int r = 255 - Math.abs(255 - fastBitmap.getRed(i, j) - overlay.getRed(i, j));
              int g = 255 - Math.abs(255 - fastBitmap.getGreen(i, j) - overlay.getGreen(i, j));
              int b = 255 - Math.abs(255 - fastBitmap.getBlue(i, j) - overlay.getBlue(i, j));
              fastBitmap.setRGB(i, j, r, g, b);
            }
          }
          break;
        case Screen:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              int r =
                  ((255 - (((255 - fastBitmap.getRed(i, j)) * (255 - overlay.getRed(i, j))) >> 8)));
              int g =
                  ((255
                      - (((255 - fastBitmap.getGreen(i, j)) * (255 - overlay.getGreen(i, j)))
                          >> 8)));
              int b =
                  ((255
                      - (((255 - fastBitmap.getBlue(i, j)) * (255 - overlay.getBlue(i, j))) >> 8)));
              fastBitmap.setRGB(i, j, r, g, b);
            }
          }
          break;
        case Exclusion:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              int r =
                  ((fastBitmap.getRed(i, j)
                      + overlay.getRed(i, j)
                      - 2 * fastBitmap.getRed(i, j) * overlay.getRed(i, j) / 255));
              int g =
                  ((fastBitmap.getGreen(i, j)
                      + overlay.getGreen(i, j)
                      - 2 * fastBitmap.getGreen(i, j) * overlay.getGreen(i, j) / 255));
              int b =
                  ((fastBitmap.getBlue(i, j)
                      + overlay.getBlue(i, j)
                      - 2 * fastBitmap.getBlue(i, j) * overlay.getBlue(i, j) / 255));
              fastBitmap.setRGB(i, j, r, g, b);
            }
          }
          break;
        case Overlay:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {

              int temp;
              if (overlay.getRed(i, j) < 128) {
                temp = (2 * fastBitmap.getRed(i, j) * overlay.getRed(i, j) / 255);
                temp = Math.min(255, temp);
                fastBitmap.setRed(i, j, temp);
              } else {
                temp =
                    (255
                        - 2 * (255 - fastBitmap.getRed(i, j)) * (255 - overlay.getRed(i, j)) / 255);
                temp = Math.min(255, temp);
                fastBitmap.setRed(i, j, temp);
              }

              if (overlay.getGreen(i, j) < 128) {
                temp = (2 * fastBitmap.getGreen(i, j) * overlay.getGreen(i, j) / 255);
                temp = Math.min(255, temp);
                fastBitmap.setGreen(i, j, temp);
              } else {
                temp =
                    (255
                        - 2
                            * (255 - fastBitmap.getGreen(i, j))
                            * (255 - overlay.getGreen(i, j))
                            / 255);
                temp = Math.min(255, temp);
                fastBitmap.setGreen(i, j, temp);
              }

              if (overlay.getBlue(i, j) < 128) {
                temp = (2 * fastBitmap.getBlue(i, j) * overlay.getBlue(i, j) / 255);
                temp = Math.min(255, temp);
                fastBitmap.setBlue(i, j, temp);
              } else {
                temp =
                    (255
                        - 2
                            * (255 - fastBitmap.getBlue(i, j))
                            * (255 - overlay.getBlue(i, j))
                            / 255);
                temp = Math.min(255, temp);
                fastBitmap.setBlue(i, j, temp);
              }
            }
          }
          break;
        case SoftLight:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {

              int temp;
              if (fastBitmap.getRed(i, j) < 128) {
                temp = (2 * overlay.getRed(i, j) * fastBitmap.getRed(i, j) / 255);
                temp = Math.min(255, temp);
                fastBitmap.setRed(i, j, temp);
              } else {
                temp =
                    (255
                        - 2 * (255 - overlay.getRed(i, j)) * (255 - fastBitmap.getRed(i, j)) / 255);
                temp = Math.min(255, temp);
                overlay.setRed(i, j, temp);
              }

              if (fastBitmap.getGreen(i, j) < 128) {
                temp = (2 * overlay.getGreen(i, j) * fastBitmap.getGreen(i, j) / 255);
                temp = Math.min(255, temp);
                fastBitmap.setGreen(i, j, temp);
              } else {
                temp =
                    (255
                        - 2
                            * (255 - overlay.getGreen(i, j))
                            * (255 - fastBitmap.getGreen(i, j))
                            / 255);
                temp = Math.min(255, temp);
                fastBitmap.setGreen(i, j, temp);
              }

              if (fastBitmap.getBlue(i, j) < 128) {
                temp = (2 * overlay.getBlue(i, j) * fastBitmap.getBlue(i, j) / 255);
                temp = Math.min(255, temp);
                fastBitmap.setBlue(i, j, temp);
              } else {
                temp =
                    (255
                        - 2
                            * (255 - overlay.getBlue(i, j))
                            * (255 - fastBitmap.getBlue(i, j))
                            / 255);
                temp = Math.min(255, temp);
                fastBitmap.setBlue(i, j, temp);
              }
            }
          }
          break;
        case HardLight:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {

              float temp;
              if (overlay.getRed(i, j) < 128) {
                temp =
                    (2 * ((fastBitmap.getRed(i, j) >> 1) + 64))
                        * ((float) overlay.getRed(i, j) / 255);
                fastBitmap.setRed(i, j, (int) temp);
              } else {
                temp =
                    (255
                        - (2
                            * (255 - ((fastBitmap.getRed(i, j) >> 1) + 64))
                            * (float) (255 - overlay.getRed(i, j))
                            / 255));
                fastBitmap.setRed(i, j, (int) temp);
              }

              if (overlay.getGreen(i, j) < 128) {
                temp =
                    (2 * ((fastBitmap.getGreen(i, j) >> 1) + 64))
                        * ((float) overlay.getGreen(i, j) / 255);
                fastBitmap.setGreen(i, j, (int) temp);
              } else {
                temp =
                    (255
                        - (2
                            * (255 - ((fastBitmap.getGreen(i, j) >> 1) + 64))
                            * (float) (255 - overlay.getGreen(i, j))
                            / 255));
                fastBitmap.setGreen(i, j, (int) temp);
              }

              if (overlay.getBlue(i, j) < 128) {
                temp =
                    (2 * ((fastBitmap.getBlue(i, j) >> 1) + 64))
                        * ((float) overlay.getBlue(i, j) / 255);
                fastBitmap.setBlue(i, j, (int) temp);
              } else {
                temp =
                    (255
                        - (2
                            * (255 - ((fastBitmap.getBlue(i, j) >> 1) + 64))
                            * (float) (255 - overlay.getBlue(i, j))
                            / 255));
                fastBitmap.setBlue(i, j, (int) temp);
              }
            }
          }
          break;
        case ColorDodge:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {

              if (overlay.getRed(i, j) == 255) {
                fastBitmap.setRed(i, j, 255);
              } else {
                int x =
                    Math.min(255, ((fastBitmap.getRed(i, j) << 8) / (255 - overlay.getRed(i, j))));
                fastBitmap.setRed(i, j, x);
              }

              if (overlay.getGreen(i, j) == 255) {
                fastBitmap.setGreen(i, j, 255);
              } else {
                int x =
                    Math.min(
                        255, ((fastBitmap.getGreen(i, j) << 8) / (255 - overlay.getGreen(i, j))));
                fastBitmap.setGreen(i, j, x);
              }

              if (overlay.getBlue(i, j) == 255) {
                fastBitmap.setBlue(i, j, 255);
              } else {
                int x =
                    Math.min(
                        255, ((fastBitmap.getBlue(i, j) << 8) / (255 - overlay.getBlue(i, j))));
                fastBitmap.setBlue(i, j, x);
              }
            }
          }
          break;
        case ColorBurn:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {

              if (overlay.getRed(i, j) == 0) {
                fastBitmap.setRed(i, j, overlay.getRed(i, j));
              } else {
                int x =
                    Math.max(
                        0, (255 - ((255 - fastBitmap.getRed(i, j)) << 8) / overlay.getRed(i, j)));
                fastBitmap.setRed(i, j, x);
              }

              if (overlay.getGreen(i, j) == 0) {
                fastBitmap.setGreen(i, j, overlay.getGreen(i, j));
              } else {
                int x =
                    Math.max(
                        0,
                        (255 - ((255 - fastBitmap.getGreen(i, j)) << 8) / overlay.getGreen(i, j)));
                fastBitmap.setGreen(i, j, x);
              }

              if (overlay.getBlue(i, j) == 0) {
                fastBitmap.setBlue(i, j, overlay.getBlue(i, j));
              } else {
                int x =
                    Math.max(
                        0, (255 - ((255 - fastBitmap.getBlue(i, j)) << 8) / overlay.getBlue(i, j)));
                fastBitmap.setBlue(i, j, x);
              }
            }
          }
          break;
        case LinearLight:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              int temp;

              if (overlay.getRed(i, j) < 128) {
                temp = fastBitmap.getRed(i, j) + (2 * overlay.getRed(i, j));
                if (temp < 255) {
                  fastBitmap.setRed(i, j, 0);
                } else {
                  fastBitmap.setRed(i, j, (temp - 255));
                }
              } else {
                int x = Math.min(fastBitmap.getRed(i, j) + (2 * (overlay.getRed(i, j) - 128)), 255);
                fastBitmap.setRed(i, j, x);
              }

              if (overlay.getGreen(i, j) < 128) {
                temp = fastBitmap.getGreen(i, j) + (2 * overlay.getGreen(i, j));
                if (temp < 255) {
                  fastBitmap.setGreen(i, j, 0);
                } else {
                  fastBitmap.setGreen(i, j, (temp - 255));
                }
              } else {
                int x =
                    Math.min(fastBitmap.getGreen(i, j) + (2 * (overlay.getGreen(i, j) - 128)), 255);
                fastBitmap.setGreen(i, j, x);
              }

              if (overlay.getBlue(i, j) < 128) {
                temp = fastBitmap.getBlue(i, j) + (2 * overlay.getBlue(i, j));
                if (temp < 255) {
                  fastBitmap.setBlue(i, j, 0);
                } else {
                  fastBitmap.setBlue(i, j, (temp - 255));
                }
              } else {
                int x =
                    Math.min(fastBitmap.getBlue(i, j) + (2 * (overlay.getBlue(i, j) - 128)), 255);
                fastBitmap.setBlue(i, j, x);
              }
            }
          }
          break;
        case VividLight:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {

              if (overlay.getRed(i, j) < 128) {
                // Color Burn
                int o = overlay.getRed(i, j) * 2;
                if (o == 0) {
                  fastBitmap.setRed(i, j, o);
                } else {
                  int x = Math.max(0, (255 - ((255 - fastBitmap.getRed(i, j)) << 8) / o));
                  fastBitmap.setRed(i, j, x);
                }
              } else {
                // Color Dodge
                int o = 2 * (overlay.getRed(i, j) - 128);
                if (o == 255) {
                  fastBitmap.setRed(i, j, 255);
                } else {
                  int x = Math.min(255, ((fastBitmap.getRed(i, j) << 8) / (255 - o)));
                  fastBitmap.setRed(i, j, x);
                }
              }

              if (overlay.getGreen(i, j) < 128) {
                // Color Burn
                int o = overlay.getGreen(i, j) * 2;
                if (o == 0) {
                  fastBitmap.setGreen(i, j, o);
                } else {
                  int x = Math.max(0, (255 - ((255 - fastBitmap.getGreen(i, j)) << 8) / o));
                  fastBitmap.setGreen(i, j, x);
                }
              } else {
                // Color Dodge
                int o = 2 * (overlay.getGreen(i, j) - 128);
                if (o == 255) {
                  fastBitmap.setGreen(i, j, 255);
                } else {
                  int x = Math.min(255, ((fastBitmap.getGreen(i, j) << 8) / (255 - o)));
                  fastBitmap.setGreen(i, j, x);
                }
              }

              if (overlay.getBlue(i, j) < 128) {
                // Color Burn
                int o = overlay.getBlue(i, j) * 2;
                if (o == 0) {
                  fastBitmap.setBlue(i, j, o);
                } else {
                  int x = Math.max(0, (255 - ((255 - fastBitmap.getBlue(i, j)) << 8) / o));
                  fastBitmap.setBlue(i, j, x);
                }
              } else {
                // Color Dodge
                int o = 2 * (overlay.getBlue(i, j) - 128);
                if (o == 255) {
                  fastBitmap.setGreen(i, j, 255);
                } else {
                  int x = Math.min(255, ((fastBitmap.getBlue(i, j) << 8) / (255 - o)));
                  fastBitmap.setBlue(i, j, x);
                }
              }
            }
          }

          break;
        case PinLight:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {

              int o = overlay.getRed(i, j) * 2;
              if (overlay.getRed(i, j) < 128) {
                // Darken
                if (o < fastBitmap.getRed(i, j)) {
                  fastBitmap.setRed(i, j, o);
                }
              } else {
                // Lighten
                if (o > fastBitmap.getRed(i, j)) {
                  fastBitmap.setRed(i, j, o);
                }
              }

              o = overlay.getGreen(i, j) * 2;
              if (overlay.getGreen(i, j) < 128) {
                // Darken
                if (o < fastBitmap.getGreen(i, j)) {
                  fastBitmap.setGreen(i, j, o);
                }
              } else {
                // Lighten
                if (o > fastBitmap.getGreen(i, j)) {
                  fastBitmap.setGreen(i, j, o);
                }
              }

              o = overlay.getBlue(i, j) * 2;
              if (overlay.getBlue(i, j) < 128) {
                // Darken
                if (o < fastBitmap.getBlue(i, j)) {
                  fastBitmap.setBlue(i, j, o);
                }
              } else {
                // Lighten
                if (o > fastBitmap.getBlue(i, j)) {
                  fastBitmap.setBlue(i, j, o);
                }
              }
            }
          }
          break;
        case Reflect:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {

              if (overlay.getRed(i, j) == 255) {
                fastBitmap.setRed(i, j, 255);
              } else {
                int x =
                    Math.min(
                        255,
                        (fastBitmap.getRed(i, j)
                            * fastBitmap.getRed(i, j)
                            / (255 - overlay.getRed(i, j))));
                fastBitmap.setRed(i, j, x);
              }

              if (overlay.getGreen(i, j) == 255) {
                fastBitmap.setGreen(i, j, 255);
              } else {
                int x =
                    Math.min(
                        255,
                        (fastBitmap.getGreen(i, j)
                            * fastBitmap.getGreen(i, j)
                            / (255 - overlay.getGreen(i, j))));
                fastBitmap.setGreen(i, j, x);
              }

              if (overlay.getBlue(i, j) == 255) {
                fastBitmap.setBlue(i, j, 255);
              } else {
                int x =
                    Math.min(
                        255,
                        (fastBitmap.getBlue(i, j)
                            * fastBitmap.getBlue(i, j)
                            / (255 - overlay.getBlue(i, j))));
                fastBitmap.setBlue(i, j, x);
              }
            }
          }
          break;
        case Phoenix:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              int r =
                  ((Math.min(fastBitmap.getRed(i, j), overlay.getRed(i, j))
                      - Math.max(fastBitmap.getRed(i, j), overlay.getRed(i, j))
                      + 255));
              int g =
                  ((Math.min(fastBitmap.getGreen(i, j), overlay.getGreen(i, j))
                      - Math.max(fastBitmap.getGreen(i, j), overlay.getGreen(i, j))
                      + 255));
              int b =
                  ((Math.min(fastBitmap.getBlue(i, j), overlay.getBlue(i, j))
                      - Math.max(fastBitmap.getBlue(i, j), overlay.getBlue(i, j))
                      + 255));

              fastBitmap.setRGB(i, j, r, g, b);
            }
          }
          break;
      }

    } else {
      throw new IllegalArgumentException("Blend only works in RGB images.");
    }
  }