// Aseprite Document Library
// Copyright (c) 2024  Igara Studio S.A.
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.

#ifdef HAVE_CONFIG_H
  #include "config.h"
#endif

#include "doc/blend_image.h"

#include "doc/blend_internals.h"
#include "doc/image.h"

namespace doc {

template<typename DstTraits, typename SrcTraits>
void blend_image_templ(Image* dst,
                       const Image* src,
                       const gfx::Clip& area,
                       const Palette* pal,
                       const int opacity,
                       const BlendMode blendMode)
{
  if constexpr (DstTraits::color_mode == ColorMode::INDEXED ||
                SrcTraits::color_mode == ColorMode::INDEXED) {
    ASSERT(pal != nullptr);
    if (pal == nullptr)
      return;
  }
  BlenderHelper<DstTraits, SrcTraits> blender(dst, src, pal, blendMode, true);
  LockImageBits<DstTraits> dstBits(dst);
  const LockImageBits<SrcTraits> srcBits(src);
  auto dstIt = dstBits.begin_area(area.dstBounds());
  auto srcIt = srcBits.begin_area(area.srcBounds());
  auto dstEnd = dstBits.end_area(area.dstBounds());
  for (; dstIt < dstEnd; ++dstIt, ++srcIt)
    *dstIt = blender(*dstIt, *srcIt, opacity);
}

void blend_image(Image* dst,
                 const Image* src,
                 gfx::Clip area,
                 const Palette* pal,
                 const int opacity,
                 const BlendMode blendMode)
{
  if (!area.clip(dst->width(), dst->height(), src->width(), src->height()))
    return;

  switch (dst->pixelFormat()) {
    case IMAGE_RGB:
      switch (src->pixelFormat()) {
        case IMAGE_RGB:
          return blend_image_templ<RgbTraits, RgbTraits>(dst, src, area, pal, opacity, blendMode);
        case IMAGE_GRAYSCALE:
          return blend_image_templ<RgbTraits, GrayscaleTraits>(dst,
                                                               src,
                                                               area,
                                                               pal,
                                                               opacity,
                                                               blendMode);
        case IMAGE_INDEXED:
          return blend_image_templ<RgbTraits, IndexedTraits>(dst,
                                                             src,
                                                             area,
                                                             pal,
                                                             opacity,
                                                             blendMode);
      }
      break;

    case IMAGE_GRAYSCALE:
      switch (src->pixelFormat()) {
        case IMAGE_RGB:
          return blend_image_templ<GrayscaleTraits, RgbTraits>(dst,
                                                               src,
                                                               area,
                                                               pal,
                                                               opacity,
                                                               blendMode);
        case IMAGE_GRAYSCALE:
          return blend_image_templ<GrayscaleTraits, GrayscaleTraits>(dst,
                                                                     src,
                                                                     area,
                                                                     pal,
                                                                     opacity,
                                                                     blendMode);
        case IMAGE_INDEXED:
          return blend_image_templ<GrayscaleTraits, IndexedTraits>(dst,
                                                                   src,
                                                                   area,
                                                                   pal,
                                                                   opacity,
                                                                   blendMode);
      }
      break;

    case IMAGE_INDEXED:
      switch (src->pixelFormat()) {
        case IMAGE_RGB:
          return blend_image_templ<IndexedTraits, RgbTraits>(dst,
                                                             src,
                                                             area,
                                                             pal,
                                                             opacity,
                                                             blendMode);
        case IMAGE_GRAYSCALE:
          return blend_image_templ<IndexedTraits, GrayscaleTraits>(dst,
                                                                   src,
                                                                   area,
                                                                   pal,
                                                                   opacity,
                                                                   blendMode);
        case IMAGE_INDEXED:
          return blend_image_templ<IndexedTraits, IndexedTraits>(dst,
                                                                 src,
                                                                 area,
                                                                 pal,
                                                                 opacity,
                                                                 blendMode);
      }
      break;

    case IMAGE_TILEMAP: return dst->copy(src, area);
  }
}

} // namespace doc
