Skip to content

File test_img_grid.cpp

File List > grid > tests > test_img_grid.cpp

Go to the documentation of this file

#include <gtest/gtest.h>

#include "geometry/polygon.hpp"
#include "grid/grid.hpp"
#include "grid/img_grid.hpp"
#include "isom/colors.hpp"
#include "utilities/progress_tracker.hpp"

namespace {

GeoGrid<RGBColor> make_solid_color_grid(size_t width, size_t height, const RGBColor& color) {
  GeoGrid<RGBColor> grid(width, height, GeoTransform(), GeoProjection());
  for (size_t y = 0; y < height; ++y) {
    for (size_t x = 0; x < width; ++x) {
      grid[{x, y}] = color;
    }
  }
  return grid;
}

GeoImgGrid make_solid_img_grid(size_t width, size_t height, const RGBColor& color) {
  return GeoImgGrid(make_solid_color_grid(width, height, color));
}

RGBColor expected_alpha_blend(const RGBColor& foreground, const RGBColor& background) {
  const float alpha = foreground.getAlpha() / 255.0f;
  const float beta = background.getAlpha() / 255.0f;
  const float denom = alpha + beta * (1.0f - alpha);
  if (denom <= 0.001f) {
    return RGBColor(background.getRed(), background.getGreen(), background.getBlue(), 255);
  }
  const uint8_t b = static_cast<uint8_t>(
      (alpha * foreground.getBlue() + beta * background.getBlue() * (1.0f - alpha)) / denom);
  const uint8_t g = static_cast<uint8_t>(
      (alpha * foreground.getGreen() + beta * background.getGreen() * (1.0f - alpha)) / denom);
  const uint8_t r = static_cast<uint8_t>(
      (alpha * foreground.getRed() + beta * background.getRed() * (1.0f - alpha)) / denom);
  return RGBColor(r, g, b, 255);
}

}  // namespace

TEST(GeoGridRGB, DrawAlphaBlendsOverlappingPixels) {
  const RGBColor background_color(0, 0, 255, 255);
  const RGBColor foreground_color(255, 0, 0, 128);

  GeoGrid<RGBColor> background(make_solid_color_grid(2, 2, background_color));
  const GeoGrid<RGBColor> foreground(make_solid_color_grid(2, 2, foreground_color));

  background.draw(foreground, ProgressTracker());

  const RGBColor expected = expected_alpha_blend(foreground_color, background_color);
  const RGBColor blended = background[{0, 0}];
  EXPECT_EQ(blended.getRed(), expected.getRed());
  EXPECT_EQ(blended.getGreen(), expected.getGreen());
  EXPECT_EQ(blended.getBlue(), expected.getBlue());
  EXPECT_EQ(blended.getAlpha(), expected.getAlpha());
}

TEST(GeoGridRGB, DrawNonSquareGrid) {
  const RGBColor foreground_color(255, 0, 0, 255);
  GeoGrid<RGBColor> background(make_solid_color_grid(5, 3, RGBColor(0, 0, 255, 255)));
  const GeoGrid<RGBColor> foreground(make_solid_color_grid(5, 3, foreground_color));

  background.draw(foreground, ProgressTracker());

  const RGBColor blended = background[{1, 1}];
  EXPECT_EQ(blended.getRed(), foreground_color.getRed());
  EXPECT_EQ(blended.getGreen(), foreground_color.getGreen());
  EXPECT_EQ(blended.getBlue(), foreground_color.getBlue());
}

TEST(GeoGridRGB, DrawOpaqueForegroundReplacesBackground) {
  const RGBColor background_color(0, 0, 255, 128);
  const RGBColor foreground_color(255, 0, 0, 255);

  GeoGrid<RGBColor> background(make_solid_color_grid(1, 1, background_color));
  const GeoGrid<RGBColor> foreground(make_solid_color_grid(1, 1, foreground_color));

  background.draw(foreground, ProgressTracker());

  const RGBColor blended = background[{0, 0}];
  EXPECT_EQ(blended.getRed(), 255);
  EXPECT_EQ(blended.getGreen(), 0);
  EXPECT_EQ(blended.getBlue(), 0);
  EXPECT_EQ(blended.getAlpha(), 255);
}

TEST(GeoImgGrid, DrawFilledPolygonFillsInterior) {
  GeoImgGrid img(10, 10, GeoTransform({0.0, 0.0}, 1.0), GeoProjection());
  img.fill(RGBColor(0, 0, 0, 255));

  PolygonWithHoles square{{{2.0, -2.0}, {6.0, -2.0}, {6.0, -6.0}, {2.0, -6.0}, {2.0, -2.0}}, {}};
  img.draw_filled_polygon(square, RGBColor(255, 0, 0, 255));

  EXPECT_EQ((img[{4, 4}].getRed()), 255);
  EXPECT_EQ((img[{0, 0}].getRed()), 0);
}

TEST(GeoImgGrid, DrawOverlaySkipsTransparentPixels) {
  GeoImgGrid background = make_solid_img_grid(4, 4, RGBColor(255, 255, 255, 255));
  GeoImgGrid overlay = make_solid_img_grid(4, 4, RGBColor(255, 0, 0, 0));

  background.draw(overlay, ProgressTracker(), GeoGridCompositeMode::OpaqueCopy);
  EXPECT_EQ((background[{2, 2}].getRed()), 255);
  EXPECT_EQ((background[{2, 2}].getGreen()), 255);
  EXPECT_EQ((background[{2, 2}].getBlue()), 255);
}

TEST(GeoImgGrid, DrawOverlayCopiesOpaquePixels) {
  GeoImgGrid background = make_solid_img_grid(4, 4, RGBColor(255, 255, 255, 255));
  GeoImgGrid overlay = make_solid_img_grid(4, 4, RGBColor(0, 0, 0, 0));
  overlay.draw_filled_polygon(
      PolygonWithHoles{{{1.0, -1.0}, {3.0, -1.0}, {3.0, -3.0}, {1.0, -3.0}, {1.0, -1.0}}, {}},
      RGBColor(0, 0, 255, 255));

  background.draw(overlay, ProgressTracker(), GeoGridCompositeMode::OpaqueCopy);
  EXPECT_EQ((background[{2, 2}].getBlue()), 255);
  EXPECT_EQ((background[{0, 0}].getBlue()), 255);
}

TEST(GeoImgGrid, DrawSkipsFullyTransparentForeground) {
  GeoImgGrid background = make_solid_img_grid(2, 2, RGBColor(255, 255, 255, 255));
  const GeoImgGrid foreground = make_solid_img_grid(2, 2, RGBColor(255, 0, 0, 0));

  background.draw(foreground, ProgressTracker());
  EXPECT_EQ((background[{0, 0}].getRed()), 255);
  EXPECT_EQ((background[{0, 0}].getGreen()), 255);
  EXPECT_EQ((background[{0, 0}].getBlue()), 255);
}