{
    Free Pascal port of the Hermes C library.
    Copyright (C) 2001-2003  Nikolay Nikolov (nickysn@users.sourceforge.net)
    Original C version by Christian Nentwich (c.nentwich@cs.ucl.ac.uk)

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version
    with the following modification:

    As a special exception, the copyright holders of this library give you
    permission to link this library with independent modules to produce an
    executable, regardless of the license terms of these independent modules,and
    to copy and distribute the resulting executable under terms of your choice,
    provided that you also meet, for each linked independent module, the terms
    and conditions of the license of that module. An independent module is a
    module which is not derived from or based on this library. If you modify
    this library, you may extend this exception to your version of the library,
    but you are not obligated to do so. If you do not wish to do so, delete this
    exception statement from your version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
}

function Hermes_ConverterInstance(flags: DWord): THermesConverterHandle;
var
  newinstance: PHermesConverter;
begin
  { Create a HermesConverter }
  New(newinstance);

  { Zero it out }
  newinstance^.loopnormal := nil;
  newinstance^.loopstretch := nil;
  newinstance^.normal := nil;
  newinstance^.stretch := nil;
  newinstance^.dither := nil;
  newinstance^.ditherstretch := nil;
  newinstance^.flags := flags;
  FillChar(newinstance^.source, 0, SizeOf(THermesFormat));
  FillChar(newinstance^.dest, 0, SizeOf(THermesFormat));

  Result := THermesConverterHandle(newinstance);
end;

procedure Hermes_ConverterReturn(handle: THermesConverterHandle);
begin
  if handle = nil then
    exit;

  Dispose(PHermesConverter(handle));
end;

function Hermes_ConverterRequest(handle: THermesConverterHandle;
                                 source, dest: PHermesFormat): Boolean;
var
  searchlist: Integer;
  i: Integer;
  found: Boolean;
  cnv: PHermesConverter;
begin
{  DebugMSG('Hermes_ConverterRequest(' + C2Str(source^.bits)
   + ',' + C2Str(source^.r) + ',' + C2Str(source^.g) + ',' +
   C2Str(source^.b) + ';' + C2Str(dest^.bits)
   + ',' + C2Str(dest^.r) + ',' + C2Str(dest^.g) + ',' +
   C2Str(dest^.b) + ')');}

  Result := False;
  searchlist := 0;
  i := 0;
  found := False;
  { Check array ranges }
  if handle = nil then
    exit;
  cnv := PHermesConverter(handle);

  { Cache repeated requests of the same conversion }
  if Hermes_FormatEquals(source, @cnv^.source) and
     Hermes_FormatEquals(dest, @cnv^.dest) then
  begin
    Result := True;
    exit;
  end;

  { Clear the generic converter flag }
  cnv^.flags := cnv^.flags and (not HERMES_CONVERT_GENERIC);

  { if the source and destination are equal, use copy routines }
  if Hermes_FormatEquals(source, dest) then
  begin
{    DebugMSG('format equals!');}
    if ((source^.bits and 7) <> 0) or (source^.bits > 32) or
       (source^.bits = 0) then
      exit;
    i := (source^.bits shr 3) - 1;
    if equalConverters[i] = nil then
      exit;
    Hermes_FormatCopy(source, @cnv^.source);
    Hermes_FormatCopy(dest, @cnv^.dest);
    cnv^.loopnormal := equalConverters[i]^.loopnormal;
    cnv^.loopstretch := equalConverters[i]^.loopstretch;
    cnv^.normal := equalConverters[i]^.normal;
    cnv^.stretch := equalConverters[i]^.stretch;
    Result := True;
    exit;
  end;

  { Start looking for specialised converters }
  searchlist := $ff;
  case source^.bits of
    32: if (source^.r = $ff0000) and (source^.g = $ff00) and (source^.b = $ff) then
           searchlist := 0
         else
           if (source^.r = ($ff shl 20)) and
              (source^.g = ($ff shl 10)) and
              (source^.b = $ff) then
             searchlist := 3;
    24: if (source^.r = $ff0000) and (source^.g = $ff00) and (source^.b = $ff) then
           searchlist := 1;
    16: if (source^.r = $f800) and (source^.g = $7e0) and (source^.b = $1f) then
           searchlist := 2;
     8: if source^.indexed then
           searchlist := 4;
  end;

  { We can use a quicker loop for 8 bit }
  if searchlist <> $ff then
    if source^.bits = 8 then
    begin
      for i := 0 to numConverters[searchlist] - 1 do
        if standardConverters[searchlist][i] <> nil then
          if dest^.bits = standardConverters[searchlist][i]^.dest.bits then
          begin
            Hermes_FormatCopy(source, @cnv^.source);
            Hermes_FormatCopy(dest, @cnv^.dest);

            cnv^.loopnormal := standardConverters[searchlist][i]^.loopnormal;
            cnv^.loopstretch := standardConverters[searchlist][i]^.loopstretch;

            cnv^.normal := standardConverters[searchlist][i]^.normal;
            cnv^.stretch := standardConverters[searchlist][i]^.stretch;

            cnv^.dither := standardConverters[searchlist][i]^.dither;
            cnv^.ditherstretch := standardConverters[searchlist][i]^.ditherstretch;

            Result := True;
            exit;
          end;
    end
    else
      for i := 0 to numConverters[searchlist] - 1 do
        if standardConverters[searchlist][i] <> nil then
          if Hermes_FormatEquals(@standardConverters[searchlist][i]^.source, source) and
             Hermes_FormatEquals(@standardConverters[searchlist][i]^.dest, dest) then
          begin
            Hermes_FormatCopy(source, @cnv^.source);
            Hermes_FormatCopy(dest, @cnv^.dest);

            cnv^.loopnormal := standardConverters[searchlist][i]^.loopnormal;
            cnv^.loopstretch := standardConverters[searchlist][i]^.loopstretch;

            cnv^.normal := standardConverters[searchlist][i]^.normal;
            cnv^.stretch := standardConverters[searchlist][i]^.stretch;

            cnv^.dither := standardConverters[searchlist][i]^.dither;
            cnv^.ditherstretch := standardConverters[searchlist][i]^.ditherstretch;

            Result := True;
            exit;
          end;


  { Otherwise find a generic converter }
{  DebugMSG('looking for a generic converter!');}


  cnv^.loopnormal := nil;
  cnv^.loopstretch := nil;
  cnv^.dither := nil;
  cnv^.ditherstretch := nil;
  cnv^.flags := cnv^.flags or HERMES_CONVERT_GENERIC;


  { Generic routines implement whole converters not scanline converters,
    assign placeholders }

  cnv^.normal := @NotApplicable;
  cnv^.stretch := @NotApplicable;

  found := False;

{
        Converting rules:

        C -> C
        C -> A

        A -> O, A -> A
        A -> C

        O -> O , A, C are the same
}

  if source^.has_colorkey and dest^.has_colorkey then { Ck -> Ck }
    case source^.bits of
      32: case dest^.bits of
        32: begin
          cnv^.loopnormal := @ConvertP_Generic32_C_Generic32_C;
          cnv^.loopstretch := @ConvertP_Generic32_C_Generic32_C_S;
          found := True;
        end;
        24: begin
          cnv^.loopnormal := @ConvertP_Generic32_C_Generic24_C;
          found := True;
        end;
        16: begin
          cnv^.loopnormal := @ConvertP_Generic32_C_Generic16_C;
          cnv^.loopstretch := @ConvertP_Generic32_C_Generic16_C_S;
          found := True;
        end;
        8: begin
          cnv^.loopnormal := @ConvertP_Generic32_C_Generic8_C;
          found := True;
        end;
      end;
      24: case dest^.bits of
        32: begin
          cnv^.loopnormal := @ConvertP_Generic24_C_Generic32_C;
          found := True;
        end;
        24: begin
          cnv^.loopnormal := @ConvertP_Generic24_C_Generic24_C;
          found := True;
        end;
        16: begin
          cnv^.loopnormal := @ConvertP_Generic24_C_Generic16_C;
          found := True;
        end;
        8: begin
          cnv^.loopnormal := @ConvertP_Generic24_C_Generic8_C;
          found := True;
        end;
      end;
      16: case dest^.bits of
        32: begin
          cnv^.loopnormal := @ConvertP_Generic16_C_Generic32_C;
          found := True;
        end;
        24: begin
          cnv^.loopnormal := @ConvertP_Generic16_C_Generic24_C;
          found := True;
        end;
        16: begin
          cnv^.loopnormal := @ConvertP_Generic16_C_Generic16_C;
          found := True;
        end;
        8: begin
          cnv^.loopnormal := @ConvertP_Generic16_C_Generic8_C;
          found := True;
        end;
      end;
    end
  else
    if source^.has_colorkey and (dest^.a <> 0) then { Ck -> A }
      case source^.bits of
        32: case dest^.bits of
          32: begin
            cnv^.loopnormal := @ConvertP_Generic32_C_Generic32_A;
            cnv^.loopstretch := @ConvertP_Generic32_C_Generic32_A_S;
            found := True;
          end;
          24: begin
            cnv^.loopnormal := @ConvertP_Generic32_C_Generic24_A;
            found := True;
          end;
          16: begin
            cnv^.loopnormal := @ConvertP_Generic32_C_Generic16_A;
            cnv^.loopstretch := @ConvertP_Generic32_C_Generic16_A_S;
            found := True;
          end;
          8: begin
            cnv^.loopnormal := @ConvertP_Generic32_C_Generic8_A;
            found := True;
          end;
        end;
        24: case dest^.bits of
          32: begin
            cnv^.loopnormal := @ConvertP_Generic24_C_Generic32_A;
            found := True;
          end;
          24: begin
            cnv^.loopnormal := @ConvertP_Generic24_C_Generic24_A;
            found := True;
          end;
          16: begin
            cnv^.loopnormal := @ConvertP_Generic24_C_Generic16_A;
            found := True;
          end;
          8: begin
            cnv^.loopnormal := @ConvertP_Generic24_C_Generic8_A;
            found := True;
          end;
        end;
        16: case dest^.bits of
          32: begin
            cnv^.loopnormal := @ConvertP_Generic16_C_Generic32_A;
            found := True;
          end;
          24: begin
            cnv^.loopnormal := @ConvertP_Generic16_C_Generic24_A;
            found := True;
          end;
          16: begin
            cnv^.loopnormal := @ConvertP_Generic16_C_Generic16_A;
            found := True;
          end;
          8: begin
            cnv^.loopnormal := @ConvertP_Generic16_C_Generic8_A;
            found := True;
          end;
        end;
      end
    else
      if (source^.a <> 0) and dest^.has_colorkey then { A -> Ck }
        case source^.bits of
          32: case dest^.bits of
            32: begin
              cnv^.loopnormal := @ConvertP_Generic32_A_Generic32_C;
              cnv^.loopstretch := @ConvertP_Generic32_A_Generic32_C_S;
              found := True;
            end;
            24: begin
              cnv^.loopnormal := @ConvertP_Generic32_A_Generic24_C;
              found := True;
            end;
            16: begin
              cnv^.loopnormal := @ConvertP_Generic32_A_Generic16_C;
              cnv^.loopnormal := @ConvertP_Generic32_A_Generic16_C_S;
              found := True;
            end;
            8: begin
              cnv^.loopnormal := @ConvertP_Generic32_A_Generic8_C;
              found := True;
            end;
          end;
          24: case dest^.bits of
            32: begin
              cnv^.loopnormal := @ConvertP_Generic24_A_Generic32_C;
              found := True;
            end;
            24: begin
              cnv^.loopnormal := @ConvertP_Generic24_A_Generic24_C;
              found := True;
            end;
            16: begin
              cnv^.loopnormal := @ConvertP_Generic24_A_Generic16_C;
              found := True;
            end;
            8: begin
              cnv^.loopnormal := @ConvertP_Generic24_A_Generic8_C;
              found := True;
            end;
          end;
          16: case dest^.bits of
            32: begin
              cnv^.loopnormal := @ConvertP_Generic16_A_Generic32_C;
              found := True;
            end;
            24: begin
              cnv^.loopnormal := @ConvertP_Generic16_A_Generic24_C;
              found := True;
            end;
            16: begin
              cnv^.loopnormal := @ConvertP_Generic16_A_Generic16_C;
              found := True;
            end;
            8: begin
              cnv^.loopnormal := @ConvertP_Generic16_A_Generic8_C;
              found := True;
            end;
          end;
        end
      else
        if (source^.a <> 0) and (dest^.a <> 0) then { A -> A }
          case source^.bits of
            32: case dest^.bits of
              32: begin
                cnv^.loopnormal := @ConvertP_Generic32_A_Generic32_A;
                cnv^.loopstretch := @ConvertP_Generic32_A_Generic32_A_S;
                found := True;
              end;
              24: begin
                cnv^.loopnormal := @ConvertP_Generic32_A_Generic24_A;
                found := True;
              end;
              16: begin
                cnv^.loopnormal := @ConvertP_Generic32_A_Generic16_A;
                cnv^.loopstretch := @ConvertP_Generic32_A_Generic16_A_S;
                found := True;
              end;
              8: begin
                cnv^.loopnormal := @ConvertP_Generic32_A_Generic8_A;
                found := True;
              end;
            end;
            24: case dest^.bits of
              32: begin
                cnv^.loopnormal := @ConvertP_Generic24_A_Generic32_A;
                found := True;
              end;
              24: begin
                cnv^.loopnormal := @ConvertP_Generic24_A_Generic24_A;
                found := True;
              end;
              16: begin
                cnv^.loopnormal := @ConvertP_Generic24_A_Generic16_A;
                found := True;
              end;
              8: begin
                cnv^.loopnormal := @ConvertP_Generic24_A_Generic8_A;
                found := True;
              end;
            end;
            16: case dest^.bits of
              32: begin
                cnv^.loopnormal := @ConvertP_Generic16_A_Generic32_A;
                found := True;
              end;
              24: begin
                cnv^.loopnormal := @ConvertP_Generic16_A_Generic24_A;
                found := True;
              end;
              16: begin
                cnv^.loopnormal := @ConvertP_Generic16_A_Generic16_A;
                found := True;
              end;
              8: begin
                cnv^.loopnormal := @ConvertP_Generic16_A_Generic8_A;
                found := True;
              end;
            end;
          end
        else { O->O, O->A, A->O, Ck->O, O->Ck }
          case source^.bits of
            32: case dest^.bits of
              32: begin
                cnv^.loopnormal := @ConvertP_Generic32_Generic32;
                cnv^.loopstretch := @ConvertP_Generic32_Generic32_S;
                found := True;
              end;
              24: begin
                cnv^.loopnormal := @ConvertP_Generic32_Generic24;
                cnv^.loopstretch := @ConvertP_Generic32_Generic24_S;
                found := True;
              end;
              16: begin
                cnv^.loopnormal := @ConvertP_Generic32_Generic16;
                cnv^.loopstretch := @ConvertP_Generic32_Generic16_S;
                found := True;
              end;
              8: begin
                cnv^.loopnormal := @ConvertP_Generic32_Generic8;
                cnv^.loopstretch := @ConvertP_Generic32_Generic8_S;
                found := True;
              end;
            end;
            24: case dest^.bits of
              32: begin
                cnv^.loopnormal := @ConvertP_Generic24_Generic32;
                cnv^.loopstretch := @ConvertP_Generic24_Generic32_S;
                found := True;
              end;
              24: begin
                cnv^.loopnormal := @ConvertP_Generic24_Generic24;
                cnv^.loopstretch := @ConvertP_Generic24_Generic24_S;
                found := True;
              end;
              16: begin
                cnv^.loopnormal := @ConvertP_Generic24_Generic16;
                cnv^.loopstretch := @ConvertP_Generic24_Generic16_S;
                found := True;
              end;
              8: begin
                cnv^.loopnormal := @ConvertP_Generic24_Generic8;
                cnv^.loopstretch := @ConvertP_Generic24_Generic8_S;
                found := True;
              end;
            end;
            16: case dest^.bits of
              32: begin
                cnv^.loopnormal := @ConvertP_Generic16_Generic32;
                cnv^.loopstretch := @ConvertP_Generic16_Generic32_S;
                found := True;
              end;
              24: begin
                cnv^.loopnormal := @ConvertP_Generic16_Generic24;
                cnv^.loopstretch := @ConvertP_Generic16_Generic24_S;
                found := True;
              end;
              16: begin
                cnv^.loopnormal := @ConvertP_Generic16_Generic16;
                cnv^.loopstretch := @ConvertP_Generic16_Generic16_S;
                found := True;
              end;
              8: begin
                cnv^.loopnormal := @ConvertP_Generic16_Generic8;
                cnv^.loopstretch := @ConvertP_Generic16_Generic8_S;
                found := True;
              end;
            end;
          end;

  if found then
  begin
    Hermes_FormatCopy(source, @cnv^.source);
    Hermes_FormatCopy(dest, @cnv^.dest);

    Result := True;
    exit;
  end;

  DebugMSG('no converter found!!!');
  { No converter found, fail }
  Result := False;
end;

function Hermes_ConverterPalette(handle: THermesConverterHandle; sourcepal, destpal: THermesPaletteHandle): Boolean;
var
  cnv: PHermesConverter;
begin
{  DebugMSG('Hermes_ConverterPalette('+C2Str(sourcepal)+','+C2Str(destpal)+')');}
  Result := False;
  if handle = nil then
    exit;

  cnv := PHermesConverter(handle);

  { Fail silently if not indexed colour format }
  if not cnv^.source.indexed then
  begin
    cnv^.lookup := nil;
    Result := True;
    exit;
  end;

  cnv^.lookup := Hermes_PaletteGetTable(sourcepal, @cnv^.dest);

  if cnv^.lookup = nil then
    exit;

  Result := True;
end;

function Hermes_ConverterCopy(handle: THermesConverterHandle; s_pixels: Pointer;
                              s_x, s_y, s_width, s_height, s_pitch: Integer;
                              d_pixels: Pointer; d_x, d_y, d_width,
                              d_height, d_pitch: Integer): Boolean;
var
  cnv: PHermesConverter;
  iface: THermesConverterInterface;
begin
  Result := False;
  if handle = nil then
    exit;
//  cnv := ConverterList[handle];
  cnv := PHermesConverter(handle);
//  if cnv = nil then
//    exit;

  { Returns success if height or width is zero. This is debatable.. ! }
  if (s_width <= 0) or (s_height <= 0) or (d_width <= 0) or (d_height <= 0) then
  begin
    Result := True;
    exit;
  end;

  iface.s_pixels := s_pixels;
  iface.s_width := s_width;
  iface.s_height := s_height;
  iface.s_add := s_pitch - s_width * (cnv^.source.bits shr 3);
  iface.s_pitch := s_pitch;

  iface.d_pixels := d_pixels;
  iface.d_width := d_width;
  iface.d_height := d_height;
  iface.d_add := d_pitch - d_width*(cnv^.dest.bits shr 3);
  iface.d_pitch := d_pitch;

  Inc(iface.s_pixels, s_y * s_pitch + s_x * (cnv^.source.bits shr 3));
  Inc(iface.d_pixels, d_y * d_pitch + d_x * (cnv^.dest.bits shr 3));

  iface.s_has_colorkey := cnv^.source.has_colorkey;
  iface.d_has_colorkey := cnv^.dest.has_colorkey;
  iface.s_colorkey := cnv^.source.colorkey;
  iface.d_colorkey := cnv^.dest.colorkey;

  iface.lookup := cnv^.lookup;


  { for generic converters, do some extra setup (find shifts, etc.)
    TODO: Move that out of here and in the request routine ! }
  if (cnv^.flags and HERMES_CONVERT_GENERIC) <> 0 then
  begin
    Hermes_Calculate_Generic_Info(Hermes_Topbit(cnv^.source.r),
                                  Hermes_Topbit(cnv^.source.g),
                                  Hermes_Topbit(cnv^.source.b),
                                  Hermes_Topbit(cnv^.source.a),
                                  Hermes_Topbit(cnv^.dest.r),
                                  Hermes_Topbit(cnv^.dest.g),
                                  Hermes_Topbit(cnv^.dest.b),
                                  Hermes_Topbit(cnv^.dest.a),
                                  @iface.info);
    iface.mask_r := cnv^.dest.r;
    iface.mask_g := cnv^.dest.g;
    iface.mask_b := cnv^.dest.b;
    iface.mask_a := cnv^.dest.a;
  end;

  { Check for dithering. This should not be in here but in request as well }
  if (cnv^.flags and HERMES_CONVERT_DITHER) <> 0 then
  begin
    { if there is a ditherer, use it else fall back to normal }
    if cnv^.dither <> nil then
      cnv^.loopnormal := cnv^.dither;
  end;

  { Normal conversion }
  if (s_width = d_width) and (s_height = d_height) then
  begin
    if (cnv^.normal = nil) or (cnv^.loopnormal = nil) then
      exit;
    { Optimization
    if (iface.s_add = 0) and (iface.d_add = 0) then
    begin
      iface.s_width := iface.s_width * s_height;
      iface.d_width := iface.d_width * d_height;
      iface.s_height := 1;
      iface.d_height := 1;
    end;}
    iface.func := cnv^.normal;
    cnv^.loopnormal(@iface);
    Result := True;
    exit;
  end
  { Stretch conversion }
  else
  begin
    if (cnv^.stretch = nil) or (cnv^.loopstretch = nil) then
      exit;
    iface.func := cnv^.stretch;
    cnv^.loopstretch(@iface);
  end;
  Result := True;
end;
