anchor.diff:    //Keep 1px padding on all sides
     MaxSoFar := MaxSoFar + 2;
     Result := ((MaxSoFar div 32) + 1)*32;
  +end;
  +
  +
  +procedure TForm1.Make4xInterpImages(RT: TRXType; aID_1, aID_2: Integer; aBaseDir: string; aExportType: TInterpExportType);
  +var
  +  origSpritesDir, interpSpritesDir: string;
  +  I: Integer;
  +  NeedAlpha: Boolean;
  +  StartupInfo: TStartupInfo;
  +  ProcessInfo: TProcessInformation;
  +begin
  +  MakeInterpImagesPair(RT, aID_1, aID_2, -1, -1, 0, 0, False, aBaseDir, aExportType);
  +
  +  origSpritesDir := aBaseDir + 'original_frames\';
  +  interpSpritesDir := aBaseDir + 'interpolated_frames\';
  +
  +  KMCopyFile(origSpritesDir + format('%d.png', [2]), interpSpritesDir + format('%.15d.png', [9]));
  +  KMDeleteFolderContent(origSpritesDir);
  +
  +  for I := 1 to 9 do
  +    KMCopyFile(interpSpritesDir + format('%.15d.png', [I]), origSpritesDir + format('%d.png', [I]));
  +
  +  KMDeleteFolderContent(interpSpritesDir);
  +
  +  NeedAlpha := aExportType in [ietBase, ietNormal];
  +
  +  //Interpolate
  +  ZeroMemory(@StartupInfo, SizeOf(StartupInfo));
  +  StartupInfo.cb := SizeOf(StartupInfo);
  +  StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
  +  StartupInfo.wShowWindow := SW_HIDE;
  +  CreateProcess(nil, PChar(GetDainParams(aBaseDir, NeedAlpha, 4)), nil, nil, false, 0, nil, PChar(fDainFolder), StartupInfo, ProcessInfo);
  +  WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
  +  //ShellExecute(0, nil, 'cmd.exe', PChar(DainParams), PChar(DainFolder), SW_SHOWNORMAL);
   end;
   
   
  
positive.code:     Exit;
    end;
  
    dirBase := fWorkDir + 'base\';
    dirShad := fWorkDir + 'shad\';
    dirTeam := fWorkDir + 'team\';
  
    StrList := TStringList.Create;
  
    outDirLocal := fOutDir+IntToStr(Byte(RT)+1)+'\';
    ForceDirectories(outDirLocal);
  
    //Import and reprocess
  
negative.code:     begin
        //Filenames are 1-based, and skip the first one since it's the original
        suffixPath := 'interpolated_frames\' + format('%.15d.png', [SubStep+1+1]);
        ProcessInterpImage(InterpOffset+SubStep, suffixPath, outPrefix, aBkgRGB, OverallMaxX, OverallMinX, OverallMaxY, OverallMinY);
      end;
    end;
  
    FreeAndNil(StrList);
  end;
  
  
  procedure TForm1.ProcessInterpImage(outIndex: Integer; inSuffixPath, outPrefixPath: string; aBkgRGB: Cardinal; OverallMaxX, OverallMinX, OverallMaxY, OverallMinY: Integer);
  var
    pngWidth, pngHeight, newWidth, newHeight: Word;
    pngBase, pngShad, pngTeam, pngCrop, pngCropMask: TKMCardinalArray;
    I, X, Y, MinX, MinY, MaxX, MaxY: Integer;
    NoShadMinX, NoShadMinY, NoShadMaxX, NoShadMaxY: Integer;
    needsMask: Boolean;
    StrList: TStringList;
    dirBase, dirShad, dirTeam: string;
  begin
    dirBase := fWorkDir + 'base\';
    dirShad := fWorkDir + 'shad\';
    dirTeam := fWorkDir + 'team\';
  
    //Clear all our buffers so they get rezeroed
    SetLength(pngBase, 0);
    SetLength(pngShad, 0);
    SetLength(pngTeam, 0);
    SetLength(pngCrop, 0);
    SetLength(pngCropMask, 0);
  
    LoadFromPng(dirBase + inSuffixPath, pngWidth, pngHeight, pngBase);
  
    if FileExists(dirShad + inSuffixPath) then
      LoadFromPng(dirShad + inSuffixPath, pngWidth, pngHeight, pngShad);
  
    if FileExists(dirTeam + inSuffixPath) then
      LoadFromPng(dirTeam + inSuffixPath, pngWidth, pngHeight, pngTeam);
  
    //Determine Min/Max X/Y
    MinX := MaxInt;
    MinY := MaxInt;
    MaxX := -1;
    MaxY := -1;
    NoShadMinX := pngWidth-1;
    NoShadMinY := pngHeight-1;
    NoShadMaxX := 0;
    NoShadMaxY := 0;
    needsMask := False;
    for Y := 0 to pngHeight-1 do
    begin
      for X := 0 to pngWidth-1 do
      begin
        if (pngBase[Y*pngWidth + X] shr 24 > 50) or
        ((Length(pngShad) > 0) and ((pngShad[Y*pngWidth + X] and $FF) > 10)) then
        begin
          MinX := Min(MinX, X);
          MinY := Min(MinY, Y);
          MaxX := Max(MaxX, X);
          MaxY := Max(MaxY, Y);
        end;
        //Tighter "no shadow" bounds. Also ignore areas where alpha < 50%
        if pngBase[Y*pngWidth + X] shr 24 >= $7F then
        begin
          NoShadMinX := Min(NoShadMinX, X);
          NoShadMinY := Min(NoShadMinY, Y);
          NoShadMaxX := Max(NoShadMaxX, X);
          NoShadMaxY := Max(NoShadMaxY, Y);
        end;
        if (Length(pngTeam) > 0) and ((pngTeam[Y*pngWidth + X] and $FF) > 0) then
          needsMask := True;
      end;
    end;
  
    //Apply overall bounds to crop out the base background sprite
    MinX := Max(MinX, OverallMinX + (pngWidth div 2));
    MinY := Max(MinY, OverallMinY + (pngHeight div 2) + CANVAS_Y_OFFSET);
    MaxX := Min(MaxX, OverallMaxX + (pngWidth div 2));
    MaxY := Min(MaxY, OverallMaxY + (pngHeight div 2) + CANVAS_Y_OFFSET);
  
    //Crop
    if (MaxX > MinX) and (MaxY > MinY) then
    begin
      newWidth := MaxX - MinX + 1;
      newHeight := MaxY - MinY + 1;
      SetLength(pngCrop, newWidth*newHeight);
      SetLength(pngCropMask, newWidth*newHeight);
      I := 0;
      for Y := MinY to MaxY do
      begin
        for X := MinX to MaxX do
        begin
          //Background is black with alpha from the shadow mask
          if Length(pngShad) > 0 then
            pngCrop[I] := (pngShad[Y*pngWidth + X] shl 24) or aBkgRGB;
  
          //Layer base sprite on top
          pngCrop[I] := BlendRGBA(pngCrop[I], pngBase[Y*pngWidth + X]);
  
          if Length(pngTeam) > 0 then
            pngCropMask[I] := pngTeam[Y*pngWidth + X];
  
          Inc(I);
        end;
      end;
  
      //Save offsets .txt file
      StrList := TStringList.Create;
      StrList.Append(IntToStr(MinX - pngWidth div 2));
      StrList.Append(IntToStr(MinY - CANVAS_Y_OFFSET - pngHeight div 2));
      StrList.Append(IntToStr(NoShadMinX - MinX));
      StrList.Append(IntToStr(NoShadMinY - MinY));
      StrList.Append(IntToStr(newWidth-1 - (MaxX - NoShadMaxX)));
      StrList.Append(IntToStr(newHeight-1 - (MaxY - NoShadMaxY)));
  
      StrList.SaveToFile(outPrefixPath+format('%d.txt', [outIndex]));
      SaveToPng(newWidth, newHeight, pngCrop, outPrefixPath+format('%d.png', [outIndex]));
      if needsMask and (Length(pngTeam) > 0) then
        SaveToPng(newWidth, newHeight, pngCropMask, outPrefixPath+format('%dm.png', [outIndex]));
  
      FreeAndNil(StrList);
    end;
  end;
  
  
  
  procedure TForm1.DoInterpUnit(aUT: TKMUnitType; aAction: TKMUnitActionType; aDir: TKMDirection; var aPicOffset: Integer; aDryRun: Boolean);
  