<commit_before>/*=========================================================================

  Program:   ORFEO Toolbox
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See OTBCopyright.txt for details.

  Some parts of this code are derived from ITK. See ITKCopyright.txt
  for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#if defined(_MSC_VER)
#pragma warning ( disable : 4786 )
#endif

#ifdef __BORLANDC__
#define ITK_LEAN_AND_MEAN
#endif

//  Software Guide : BeginCommandLineArgs
//    INPUTS: {QB_Toulouse_Ortho_PAN.tif}, {QB_Toulouse_Ortho_XS.tif}
//    OUTPUTS: {QB_Toulouse_Ortho_PXS.tif}
//    OUTPUTS: {pretty_QB_Toulouse_Ortho_PXS.png}
//    OUTPUTS: {pretty_QB_Toulouse_Ortho_PAN.png}
//    OUTPUTS: {pretty_QB_Toulouse_Ortho_XS.png}
//  Software Guide : EndCommandLineArgs

// Software Guide : BeginLatex
//
// Here we are illustrating the use of the
// \doxygen{otb}{SimpleRcsPanSharpeningFusionImageFilter} for PAN-sharpening.
// This example takes a PAN and the corresponding XS images as input. These
// images are supposed to be registered.
//
// The fusion operation is defined as
//
// \begin{equation}
// \frac{XS}{\mathrm{Filtered}(PAN)} PAN
// \end{equation}
//
// Software Guide : EndLatex

// Software Guide : BeginLatex
//
// We start by including the required header and declaring the main function:
//
// Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
#include "otbImage.h"
#include "otbVectorImage.h"
#include "otbImageFileReader.h"
#include "otbStreamingImageFileWriter.h"
#include "otbSimpleRcsPanSharpeningFusionImageFilter.h"
// Software Guide : EndCodeSnippet

#include "otbPrintableImageFilter.h"
#include "itkRescaleIntensityImageFilter.h"

// Software Guide : BeginCodeSnippet
int main( int argc, char* argv[] )
{
// Software Guide : EndCodeSnippet

  if( argc < 4 )
  {
    std::cerr << "Missing Parameters " << std::endl;
    std::cerr << "Usage: " << argv[0];
    std::cerr << " inputPanchromatiqueImage inputMultiSpectralImage outputImage outputImagePrinted panchroPrinted msPrinted" << std::endl;
    return 1;
  }

  // Software Guide : BeginLatex
  //
  // We declare the different image type used here as well as the image reader.
  // Note that, the reader for the PAN image is templated by an
  // \doxygen{otb}{Image} while the XS reader uses an
  // \doxygen{otb}{VectorImage}.
  //
  // Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  typedef otb::Image<double, 2>     ImageType;
  typedef otb::VectorImage<double, 2>     VectorImageType;
  typedef otb::ImageFileReader<ImageType>  ReaderType;
  typedef otb::ImageFileReader<VectorImageType>  ReaderVectorType;
  typedef otb::VectorImage<unsigned int, 2>     VectorIntImageType;

  ReaderVectorType::Pointer     	readerXS=ReaderVectorType::New();
  ReaderType::Pointer     	readerPAN=ReaderType::New();
  // Software Guide : EndCodeSnippet

  // Software Guide : BeginLatex
  //
  // We pass the filenames to the readers
  //
  // Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  readerPAN->SetFileName(argv[1]);
  readerXS->SetFileName(argv[2]);
  // Software Guide : EndCodeSnippet

  // Software Guide : BeginLatex
  //
  // We declare the fusion filter an set its inputs using the readers:
  //
  // Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  typedef otb::SimpleRcsPanSharpeningFusionImageFilter
      <ImageType,VectorImageType,VectorIntImageType> FusionFilterType;
  FusionFilterType::Pointer fusion = FusionFilterType::New();
  fusion->SetPanInput(readerPAN->GetOutput());
  fusion->SetXsInput(readerXS->GetOutput());
  // Software Guide : EndCodeSnippet

  // Software Guide : BeginLatex
  //
  // And finally, we declare the writer and call its \code{Update()} method to
  // trigger the full pipeline execution.
  //
  // Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  typedef otb::StreamingImageFileWriter<VectorIntImageType>  WriterType;
  WriterType::Pointer	    	writer=WriterType::New();
  writer->SetFileName(argv[3]);
  writer->SetInput(fusion->GetOutput());
  writer->Update();
  // Software Guide : EndCodeSnippet

  typedef otb::PrintableImageFilter<VectorIntImageType> PrintableImageType;
  PrintableImageType::Pointer printable = PrintableImageType::New();

  typedef otb::VectorImage<unsigned char, 2>     VectorCharImageType;
  typedef otb::StreamingImageFileWriter<VectorCharImageType>  PNGWriterType;
  PNGWriterType::Pointer pngwriter = PNGWriterType::New();

  printable->SetInput(fusion->GetOutput());
  printable->SetChannel(3);
  printable->SetChannel(2);
  printable->SetChannel(1);
  pngwriter->SetFileName(argv[4]);
  pngwriter->SetInput(printable->GetOutput());
  pngwriter->Update();

  typedef otb::PrintableImageFilter<VectorImageType> PrintableImageType2;
  PrintableImageType2::Pointer printable2 = PrintableImageType2::New();
  printable2->SetInput(readerXS->GetOutput());
  printable2->SetChannel(3);
  printable2->SetChannel(2);
  printable2->SetChannel(1);
  pngwriter->SetFileName(argv[6]);
  pngwriter->SetInput(printable2->GetOutput());
  pngwriter->Update();

  typedef otb::Image<unsigned char, 2>     CharImageType;
  typedef itk::RescaleIntensityImageFilter <ImageType,CharImageType> RescalerType;
  RescalerType::Pointer rescaler = RescalerType::New();
  typedef otb::StreamingImageFileWriter<CharImageType>  PNGWriterType2;
  PNGWriterType2::Pointer pngwriter2 = PNGWriterType2::New();
  rescaler->SetInput(readerPAN->GetOutput());
  pngwriter2->SetFileName(argv[5]);
  pngwriter2->SetInput(rescaler->GetOutput());
  pngwriter2->Update();

  // Software Guide : BeginLatex
  //
  // Figure~\ref{fig:PANSHARP_FILTER} shows the result of applying
  // this PAN sharpening filter to a Quickbird image.
  // \begin{figure}
  // \center
  // \includegraphics[width=0.44\textwidth]{pretty_QB_Toulouse_Ortho_PAN.eps}
  // \includegraphics[width=0.44\textwidth]{pretty_QB_Toulouse_Ortho_XS.eps}
  // \includegraphics[width=0.44\textwidth]{pretty_QB_Toulouse_Ortho_PXS.eps}
  // \itkcaption[Pan sharpening]{Result of applying
  // the \doxygen{otb}{SimpleRcsPanSharpeningFusionImageFilter} to
  // orthorectified Quickbird
  // image. From left to right : original PAN image, original XS image and the
  // result of the PAN sharpening}
  // \label{fig:PANSHARP_FILTER}
  // \end{figure}
  //
  // Software Guide : EndLatex

  return EXIT_SUCCESS;

}

<commit_msg>Images avant le texte pour le SoftwareGuide<commit_after>/*=========================================================================

  Program:   ORFEO Toolbox
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See OTBCopyright.txt for details.

  Some parts of this code are derived from ITK. See ITKCopyright.txt
  for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#if defined(_MSC_VER)
#pragma warning ( disable : 4786 )
#endif

#ifdef __BORLANDC__
#define ITK_LEAN_AND_MEAN
#endif

//  Software Guide : BeginCommandLineArgs
//    INPUTS: {QB_Toulouse_Ortho_PAN.tif}, {QB_Toulouse_Ortho_XS.tif}
//    OUTPUTS: {QB_Toulouse_Ortho_PXS.tif}
//    OUTPUTS: {pretty_QB_Toulouse_Ortho_PXS.png}
//    OUTPUTS: {pretty_QB_Toulouse_Ortho_PAN.png}
//    OUTPUTS: {pretty_QB_Toulouse_Ortho_XS.png}
//  Software Guide : EndCommandLineArgs

// Software Guide : BeginLatex
//
// Here we are illustrating the use of the
// \doxygen{otb}{SimpleRcsPanSharpeningFusionImageFilter} for PAN-sharpening.
// This example takes a PAN and the corresponding XS images as input. These
// images are supposed to be registered.
//
// The fusion operation is defined as
//
// \begin{equation}
// \frac{XS}{\mathrm{Filtered}(PAN)} PAN
// \end{equation}
//
// Software Guide : EndLatex

// Software Guide : BeginLatex
//
// Figure~\ref{fig:PANSHARP_FILTER} shows the result of applying
// this PAN sharpening filter to a Quickbird image.
// \begin{figure}
// \center
// \includegraphics[width=0.44\textwidth]{pretty_QB_Toulouse_Ortho_PAN.eps}
// \includegraphics[width=0.44\textwidth]{pretty_QB_Toulouse_Ortho_XS.eps}
// \includegraphics[width=0.44\textwidth]{pretty_QB_Toulouse_Ortho_PXS.eps}
// \itkcaption[Pan sharpening]{Result of applying
// the \doxygen{otb}{SimpleRcsPanSharpeningFusionImageFilter} to
// orthorectified Quickbird
// image. From left to right : original PAN image, original XS image and the
// result of the PAN sharpening}
// \label{fig:PANSHARP_FILTER}
// \end{figure}
//
// Software Guide : EndLatex

// Software Guide : BeginLatex
//
// We start by including the required header and declaring the main function:
//
// Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
#include "otbImage.h"
#include "otbVectorImage.h"
#include "otbImageFileReader.h"
#include "otbStreamingImageFileWriter.h"
#include "otbSimpleRcsPanSharpeningFusionImageFilter.h"
// Software Guide : EndCodeSnippet

#include "otbPrintableImageFilter.h"
#include "itkRescaleIntensityImageFilter.h"

// Software Guide : BeginCodeSnippet
int main( int argc, char* argv[] )
{
// Software Guide : EndCodeSnippet

  if( argc < 4 )
  {
    std::cerr << "Missing Parameters " << std::endl;
    std::cerr << "Usage: " << argv[0];
    std::cerr << " inputPanchromatiqueImage inputMultiSpectralImage outputImage outputImagePrinted panchroPrinted msPrinted" << std::endl;
    return 1;
  }

  // Software Guide : BeginLatex
  //
  // We declare the different image type used here as well as the image reader.
  // Note that, the reader for the PAN image is templated by an
  // \doxygen{otb}{Image} while the XS reader uses an
  // \doxygen{otb}{VectorImage}.
  //
  // Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  typedef otb::Image<double, 2>     ImageType;
  typedef otb::VectorImage<double, 2>     VectorImageType;
  typedef otb::ImageFileReader<ImageType>  ReaderType;
  typedef otb::ImageFileReader<VectorImageType>  ReaderVectorType;
  typedef otb::VectorImage<unsigned int, 2>     VectorIntImageType;

  ReaderVectorType::Pointer     	readerXS=ReaderVectorType::New();
  ReaderType::Pointer     	readerPAN=ReaderType::New();
  // Software Guide : EndCodeSnippet

  // Software Guide : BeginLatex
  //
  // We pass the filenames to the readers
  //
  // Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  readerPAN->SetFileName(argv[1]);
  readerXS->SetFileName(argv[2]);
  // Software Guide : EndCodeSnippet

  // Software Guide : BeginLatex
  //
  // We declare the fusion filter an set its inputs using the readers:
  //
  // Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  typedef otb::SimpleRcsPanSharpeningFusionImageFilter
      <ImageType,VectorImageType,VectorIntImageType> FusionFilterType;
  FusionFilterType::Pointer fusion = FusionFilterType::New();
  fusion->SetPanInput(readerPAN->GetOutput());
  fusion->SetXsInput(readerXS->GetOutput());
  // Software Guide : EndCodeSnippet

  // Software Guide : BeginLatex
  //
  // And finally, we declare the writer and call its \code{Update()} method to
  // trigger the full pipeline execution.
  //
  // Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  typedef otb::StreamingImageFileWriter<VectorIntImageType>  WriterType;
  WriterType::Pointer	    	writer=WriterType::New();
  writer->SetFileName(argv[3]);
  writer->SetInput(fusion->GetOutput());
  writer->Update();
  // Software Guide : EndCodeSnippet

  typedef otb::PrintableImageFilter<VectorIntImageType> PrintableImageType;
  PrintableImageType::Pointer printable = PrintableImageType::New();

  typedef otb::VectorImage<unsigned char, 2>     VectorCharImageType;
  typedef otb::StreamingImageFileWriter<VectorCharImageType>  PNGWriterType;
  PNGWriterType::Pointer pngwriter = PNGWriterType::New();

  printable->SetInput(fusion->GetOutput());
  printable->SetChannel(3);
  printable->SetChannel(2);
  printable->SetChannel(1);
  pngwriter->SetFileName(argv[4]);
  pngwriter->SetInput(printable->GetOutput());
  pngwriter->Update();

  typedef otb::PrintableImageFilter<VectorImageType> PrintableImageType2;
  PrintableImageType2::Pointer printable2 = PrintableImageType2::New();
  printable2->SetInput(readerXS->GetOutput());
  printable2->SetChannel(3);
  printable2->SetChannel(2);
  printable2->SetChannel(1);
  pngwriter->SetFileName(argv[6]);
  pngwriter->SetInput(printable2->GetOutput());
  pngwriter->Update();

  typedef otb::Image<unsigned char, 2>     CharImageType;
  typedef itk::RescaleIntensityImageFilter <ImageType,CharImageType> RescalerType;
  RescalerType::Pointer rescaler = RescalerType::New();
  typedef otb::StreamingImageFileWriter<CharImageType>  PNGWriterType2;
  PNGWriterType2::Pointer pngwriter2 = PNGWriterType2::New();
  rescaler->SetInput(readerPAN->GetOutput());
  pngwriter2->SetFileName(argv[5]);
  pngwriter2->SetInput(rescaler->GetOutput());
  pngwriter2->Update();

  return EXIT_SUCCESS;

}

<|endoftext|>


<commit_before>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#include "itkFirstOrderTextureFeaturesImageFilter.h"
#include "itkFlatStructuringElement.h"
#include "itkAdditiveGaussianNoiseImageFilter.h"
#include "itkFilterWatcher.h"

#include "gtest/gtest.h"

namespace
{

template<typename T>
void print_feature( const T &p, std::ostream  &out = std::cout )
{
out << "mean: " << p[0] << std::endl;
out << "minimum: " << p[1] << std::endl;
out << "maximum: " << p[2] << std::endl;
out << "variance: " << p[3] << std::endl;
out << "standard deviation: " << p[4] << std::endl;
out << "skewness: " << p[5] << std::endl;
out << "kurtosis: " << p[6] << std::endl;
out << "entropy: " << p[7] << std::endl;
}

}

TEST(TextTureFeatures, FirstOrder_Test1)
{
  const unsigned int ImageDimension = 2;
  typedef itk::Image<float, ImageDimension >                    ImageType;
  typedef itk::Image<itk::FixedArray<float,8>, ImageDimension > OImageType;
  typedef itk::FlatStructuringElement< ImageDimension >         KernelType;

  unsigned int r = 50u;
  unsigned int d = r*2 + 1;
  ImageType::SizeType imageSize = {{ d, d }};
  ImageType::SpacingValueType imageSpacing[]  = { 1.0f, 1.0f };

  ImageType::Pointer image = ImageType::New();

  image->SetRegions( ImageType::RegionType(imageSize) );
  image->SetSpacing( imageSpacing );
  image->Allocate();
  image->FillBuffer(0);

  typedef itk::AdditiveGaussianNoiseImageFilter< ImageType > ImageNoiseType;
  ImageNoiseType::Pointer noiseFilter = ImageNoiseType::New();
  noiseFilter->SetSeed(124);
  noiseFilter->SetMean(0.0);
  noiseFilter->SetStandardDeviation(.1);
  noiseFilter->SetInput(image);

  typedef itk::FirstOrderTextureFeaturesImageFilter< ImageType, OImageType, KernelType > TextureFilterType;

  KernelType::SizeType radius;
  radius.Fill( r );
  KernelType kernel = KernelType::Box( radius );
  TextureFilterType::Pointer filter = TextureFilterType::New();
  filter->SetKernel( kernel );
  filter->SetInput( noiseFilter->GetOutput() );

  FilterWatcher watcher(filter, "filter");

  ImageType::SizeType requestSize = {{10,10}};
  ImageType::IndexType requestIndex = {{45,45}};
  ImageType::RegionType request(requestIndex, requestSize);
  filter->GetOutput()->SetRequestedRegion( request );
  filter->Update();

  OImageType::ConstPointer output = filter->GetOutput();

  {
  ImageType::IndexType idx = {{r,r}};
  const OImageType::PixelType &p = output->GetPixel(idx);

  print_feature( p );

  // The following is for a Gaussian sample with std dev of .1
  // The expected value was analytically computed, while the
  // tolerances were estimated based on 10,000 different sample set
  // distributions in numpy.
  EXPECT_NEAR( 0.0, p[0], 0.002) << "mean";
  EXPECT_GT(-.3, p[1]) << "min";
  EXPECT_LT(.3, p[2]) << "max";
  EXPECT_NEAR(0.01, p[3], .001) << "variance";
  EXPECT_NEAR(0.1, p[4], .01) << "standard deviation";
  EXPECT_NEAR(0, p[5], .1) << "skewness";
  EXPECT_NEAR(0, p[6], .2) << "kurtosis";
  EXPECT_NEAR(13.3, p[7], .1) << "entropy";
  }
}

TEST(TextTureFeatures, FirstOrder_Test2)
{
  const unsigned int ImageDimension = 2;
  typedef itk::Image<float, ImageDimension >                    ImageType;
  typedef itk::Image<itk::FixedArray<float,8>, ImageDimension > OImageType;
  typedef itk::FlatStructuringElement< ImageDimension >         KernelType;

  unsigned int r = 50u;
  unsigned int d = r*2 + 1;
  ImageType::SizeType imageSize = {{ d, d }};
  ImageType::SpacingValueType imageSpacing[]  = { 1.0f, 1.0f };

  ImageType::Pointer image = ImageType::New();

  image->SetRegions( ImageType::RegionType(imageSize) );
  image->SetSpacing( imageSpacing );
  image->Allocate();
  image->FillBuffer(0);

  typedef itk::AdditiveGaussianNoiseImageFilter< ImageType > ImageNoiseType;
  ImageNoiseType::Pointer noiseFilter = ImageNoiseType::New();
  noiseFilter->SetSeed(124);
  noiseFilter->SetMean(100.0);
  noiseFilter->SetStandardDeviation(1);
  noiseFilter->SetInput(image);

  typedef itk::FirstOrderTextureFeaturesImageFilter< ImageType, OImageType, KernelType > TextureFilterType;

  KernelType::SizeType radius;
  radius.Fill( r );
  KernelType kernel = KernelType::Box( radius );
  TextureFilterType::Pointer filter = TextureFilterType::New();
  filter->SetKernel( kernel );
  filter->SetInput( noiseFilter->GetOutput() );

  FilterWatcher watcher(filter, "filter");

  ImageType::SizeType requestSize = {{10,10}};
  ImageType::IndexType requestIndex = {{45,45}};
  ImageType::RegionType request(requestIndex, requestSize);
  filter->GetOutput()->SetRequestedRegion( request );
  filter->Update();

  OImageType::ConstPointer output = filter->GetOutput();

  {
  ImageType::IndexType idx = {{r,r}};
  const OImageType::PixelType &p = output->GetPixel(idx);

  print_feature( p );

  // The following is for a Gaussian sample with std dev of .1
  // The expected value was analytically computed, while the
  // tolerances were estimated based on 10,000 different sample set
  // distributions in numpy.
  EXPECT_NEAR( 100, p[0], 0.02) << "mean";
  EXPECT_GT(97, p[1]) << "min";
  EXPECT_LT(103, p[2]) << "max";
  EXPECT_NEAR(1, p[3], .1) << "variance";
  EXPECT_NEAR(1, p[4], .1) << "standard deviation";
  EXPECT_NEAR(0, p[5], .1) << "skewness";
  EXPECT_NEAR(0, p[6], .2) << "kurtosis";
  EXPECT_NEAR(13.3, p[7], .2) << "entropy";
  }
}
<commit_msg>BUG: Fix typeo for Google Test test case name<commit_after>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#include "itkFirstOrderTextureFeaturesImageFilter.h"
#include "itkFlatStructuringElement.h"
#include "itkAdditiveGaussianNoiseImageFilter.h"
#include "itkFilterWatcher.h"

#include "gtest/gtest.h"

namespace
{

template<typename T>
void print_feature( const T &p, std::ostream  &out = std::cout )
{
out << "mean: " << p[0] << std::endl;
out << "minimum: " << p[1] << std::endl;
out << "maximum: " << p[2] << std::endl;
out << "variance: " << p[3] << std::endl;
out << "standard deviation: " << p[4] << std::endl;
out << "skewness: " << p[5] << std::endl;
out << "kurtosis: " << p[6] << std::endl;
out << "entropy: " << p[7] << std::endl;
}

}

TEST(TextureFeatures, FirstOrder_Test1)
{
  const unsigned int ImageDimension = 2;
  typedef itk::Image<float, ImageDimension >                    ImageType;
  typedef itk::Image<itk::FixedArray<float,8>, ImageDimension > OImageType;
  typedef itk::FlatStructuringElement< ImageDimension >         KernelType;

  unsigned int r = 50u;
  unsigned int d = r*2 + 1;
  ImageType::SizeType imageSize = {{ d, d }};
  ImageType::SpacingValueType imageSpacing[]  = { 1.0f, 1.0f };

  ImageType::Pointer image = ImageType::New();

  image->SetRegions( ImageType::RegionType(imageSize) );
  image->SetSpacing( imageSpacing );
  image->Allocate();
  image->FillBuffer(0);

  typedef itk::AdditiveGaussianNoiseImageFilter< ImageType > ImageNoiseType;
  ImageNoiseType::Pointer noiseFilter = ImageNoiseType::New();
  noiseFilter->SetSeed(124);
  noiseFilter->SetMean(0.0);
  noiseFilter->SetStandardDeviation(.1);
  noiseFilter->SetInput(image);

  typedef itk::FirstOrderTextureFeaturesImageFilter< ImageType, OImageType, KernelType > TextureFilterType;

  KernelType::SizeType radius;
  radius.Fill( r );
  KernelType kernel = KernelType::Box( radius );
  TextureFilterType::Pointer filter = TextureFilterType::New();
  filter->SetKernel( kernel );
  filter->SetInput( noiseFilter->GetOutput() );

  FilterWatcher watcher(filter, "filter");

  ImageType::SizeType requestSize = {{10,10}};
  ImageType::IndexType requestIndex = {{45,45}};
  ImageType::RegionType request(requestIndex, requestSize);
  filter->GetOutput()->SetRequestedRegion( request );
  filter->Update();

  OImageType::ConstPointer output = filter->GetOutput();

  {
  ImageType::IndexType idx = {{r,r}};
  const OImageType::PixelType &p = output->GetPixel(idx);

  print_feature( p );

  // The following is for a Gaussian sample with std dev of .1
  // The expected value was analytically computed, while the
  // tolerances were estimated based on 10,000 different sample set
  // distributions in numpy.
  EXPECT_NEAR( 0.0, p[0], 0.002) << "mean";
  EXPECT_GT(-.3, p[1]) << "min";
  EXPECT_LT(.3, p[2]) << "max";
  EXPECT_NEAR(0.01, p[3], .001) << "variance";
  EXPECT_NEAR(0.1, p[4], .01) << "standard deviation";
  EXPECT_NEAR(0, p[5], .1) << "skewness";
  EXPECT_NEAR(0, p[6], .2) << "kurtosis";
  EXPECT_NEAR(13.3, p[7], .1) << "entropy";
  }
}

TEST(TextureFeatures, FirstOrder_Test2)
{
  const unsigned int ImageDimension = 2;
  typedef itk::Image<float, ImageDimension >                    ImageType;
  typedef itk::Image<itk::FixedArray<float,8>, ImageDimension > OImageType;
  typedef itk::FlatStructuringElement< ImageDimension >         KernelType;

  unsigned int r = 50u;
  unsigned int d = r*2 + 1;
  ImageType::SizeType imageSize = {{ d, d }};
  ImageType::SpacingValueType imageSpacing[]  = { 1.0f, 1.0f };

  ImageType::Pointer image = ImageType::New();

  image->SetRegions( ImageType::RegionType(imageSize) );
  image->SetSpacing( imageSpacing );
  image->Allocate();
  image->FillBuffer(0);

  typedef itk::AdditiveGaussianNoiseImageFilter< ImageType > ImageNoiseType;
  ImageNoiseType::Pointer noiseFilter = ImageNoiseType::New();
  noiseFilter->SetSeed(124);
  noiseFilter->SetMean(100.0);
  noiseFilter->SetStandardDeviation(1);
  noiseFilter->SetInput(image);

  typedef itk::FirstOrderTextureFeaturesImageFilter< ImageType, OImageType, KernelType > TextureFilterType;

  KernelType::SizeType radius;
  radius.Fill( r );
  KernelType kernel = KernelType::Box( radius );
  TextureFilterType::Pointer filter = TextureFilterType::New();
  filter->SetKernel( kernel );
  filter->SetInput( noiseFilter->GetOutput() );

  FilterWatcher watcher(filter, "filter");

  ImageType::SizeType requestSize = {{10,10}};
  ImageType::IndexType requestIndex = {{45,45}};
  ImageType::RegionType request(requestIndex, requestSize);
  filter->GetOutput()->SetRequestedRegion( request );
  filter->Update();

  OImageType::ConstPointer output = filter->GetOutput();

  {
  ImageType::IndexType idx = {{r,r}};
  const OImageType::PixelType &p = output->GetPixel(idx);

  print_feature( p );

  // The following is for a Gaussian sample with std dev of .1
  // The expected value was analytically computed, while the
  // tolerances were estimated based on 10,000 different sample set
  // distributions in numpy.
  EXPECT_NEAR( 100, p[0], 0.02) << "mean";
  EXPECT_GT(97, p[1]) << "min";
  EXPECT_LT(103, p[2]) << "max";
  EXPECT_NEAR(1, p[3], .1) << "variance";
  EXPECT_NEAR(1, p[4], .1) << "standard deviation";
  EXPECT_NEAR(0, p[5], .1) << "skewness";
  EXPECT_NEAR(0, p[6], .2) << "kurtosis";
  EXPECT_NEAR(13.3, p[7], .2) << "entropy";
  }
}
<|endoftext|>


<commit_before>// Copyright 2019 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "firebase_test_framework.h"  // NOLINT

#include <cstdio>
#include <cstring>
#include <string>
#include <vector>

#include "firebase/future.h"

namespace firebase {
namespace internal {
// Borrow Firebase's internal Base64 encoder and decoder.
extern bool Base64Encode(const std::string& input, std::string* output);
extern bool Base64Decode(const std::string& input, std::string* output);
}  // namespace internal
}  // namespace firebase

namespace firebase_test_framework {

int FirebaseTest::argc_ = 0;
char** FirebaseTest::argv_ = nullptr;
bool FirebaseTest::found_config_ = false;

FirebaseTest::FirebaseTest() : app_(nullptr) {}

FirebaseTest::~FirebaseTest() { assert(app_ == nullptr); }

void FirebaseTest::SetUp() {}

void FirebaseTest::TearDown() {
  if (HasFailure()) {
    app_framework::SetPreserveFullLog(false);
    app_framework::LogError(
        "Test %s failed.\nFull test log:\n%s",
        ::testing::UnitTest::GetInstance()->current_test_info()->name(),
        "========================================================");
    app_framework::SetPreserveFullLog(true);
    app_framework::AddToFullLog(
        "========================================================\n");
    app_framework::OutputFullLog();
  } else {
    app_framework::ClearFullLog();
  }
}

void FirebaseTest::FindFirebaseConfig(const char* try_directory) {
#if !defined(__ANDROID__) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
  static const char kDefaultGoogleServicesPath[] = "google-services.json";

  if (!found_config_) {
    if (try_directory[0] != '\0' && app_framework::FileExists(try_directory)) {
      app_framework::ChangeToFileDirectory(try_directory);
    } else if (app_framework::FileExists(kDefaultGoogleServicesPath)) {
      // It's in the current directory, don't do anything.
    } else {
      // Try the directory the binary is in.
      app_framework::ChangeToFileDirectory(argv_[0]);
    }
    // Only do this once.
    found_config_ = true;
  }
#endif  //  !defined(__ANDROID__) && !(defined(TARGET_OS_IPHONE) &&
        //  TARGET_OS_IPHONE)
  (void)try_directory;
}

void FirebaseTest::InitializeApp() {
  if (app_) return;  // Already initialized.

  app_framework::LogDebug("Initialize Firebase App.");

#if defined(__ANDROID__)
  app_ = ::firebase::App::Create(app_framework::GetJniEnv(),
                                 app_framework::GetActivity());
#else
  app_ = ::firebase::App::Create();
#endif  // defined(__ANDROID__)
}

void FirebaseTest::TerminateApp() {
  if (!app_) return;  // Already terminated.

  app_framework::LogDebug("Shutdown Firebase App.");
  delete app_;
  app_ = nullptr;
}

bool FirebaseTest::RunFlakyBlockBase(bool (*flaky_block)(void* context),
                                     void* context, const char* name) {
  // Run flaky_block(context). If it returns true, all is well, return true.
  // If it returns false, something flaky failed; wait a moment and try again.
  const int kRetryDelaysMs[] = {// Roughly exponential backoff for the retries.
                                100, 1000, 5000, 10000, 30000};
  const int kNumAttempts =
      1 + (sizeof(kRetryDelaysMs) / sizeof(kRetryDelaysMs[0]));

  int attempt = 0;

  while (attempt < kNumAttempts) {
    bool result = flaky_block(context);
    if (result || (attempt == kNumAttempts - 1)) {
      return result;
    }
    app_framework::LogDebug("RunFlakyBlock%s%s: Attempt %d failed",
                            *name ? " " : "", name, attempt + 1);
    int delay_ms = kRetryDelaysMs[attempt];
    app_framework::ProcessEvents(delay_ms);
    attempt++;
  }
  return false;
}

firebase::FutureBase FirebaseTest::RunWithRetryBase(
    firebase::FutureBase (*run_future)(void* context), void* context,
    const char* name, int expected_error) {
  // Run run_future(context), which returns a Future, then wait for that Future
  // to complete. If the Future returns Invalid, or if its error() does
  // not match expected_error, pause a moment and try again.
  //
  // In most cases, this will return the Future once it's been completed.
  // However, if it reaches the last attempt, it will return immediately once
  // the operation begins. This is because at this point we want to return the
  // results whether or not the operation succeeds.
  const int kRetryDelaysMs[] = {// Roughly exponential backoff for the retries.
                                100, 1000, 5000, 10000, 30000};
  const int kNumAttempts =
      1 + (sizeof(kRetryDelaysMs) / sizeof(kRetryDelaysMs[0]));

  int attempt = 0;
  firebase::FutureBase future;

  while (attempt < kNumAttempts) {
    future = run_future(context);
    if (attempt == kNumAttempts - 1) {
      // This is the last attempt, return immediately.
      break;
    }

    // Wait for completion, then check status and error.
    while (future.status() == firebase::kFutureStatusPending) {
      app_framework::ProcessEvents(100);
    }
    if (future.status() != firebase::kFutureStatusComplete) {
      app_framework::LogDebug(
          "RunWithRetry%s%s: Attempt %d returned invalid status",
          *name ? " " : "", name, attempt + 1);
    } else if (future.error() != expected_error) {
      app_framework::LogDebug(
          "RunWithRetry%s%s: Attempt %d returned error %d, expected %d",
          *name ? " " : "", name, attempt + 1, future.error(), expected_error);
    } else {
      // Future is completed and the error matches what's expected, no need to
      // retry further.
      break;
    }
    int delay_ms = kRetryDelaysMs[attempt];
    app_framework::LogDebug(
        "RunWithRetry%s%s: Pause %d milliseconds before retrying.",
        *name ? " " : "", name, delay_ms);
    app_framework::ProcessEvents(delay_ms);
    attempt++;
  }
  return future;
}

bool FirebaseTest::WaitForCompletion(const firebase::FutureBase& future,
                                     const char* name, int expected_error) {
  app_framework::LogDebug("WaitForCompletion %s", name);
  while (future.status() == firebase::kFutureStatusPending) {
    app_framework::ProcessEvents(100);
  }
  EXPECT_EQ(future.status(), firebase::kFutureStatusComplete)
      << name << " returned an invalid status.";
  EXPECT_EQ(future.error(), expected_error)
      << name << " returned error " << future.error() << ": "
      << future.error_message();
  return (future.status() == firebase::kFutureStatusComplete &&
          future.error() == expected_error);
}

bool FirebaseTest::WaitForCompletionAnyResult(
    const firebase::FutureBase& future, const char* name) {
  app_framework::LogDebug("WaitForCompletion %s", name);
  while (future.status() == firebase::kFutureStatusPending) {
    app_framework::ProcessEvents(100);
  }
  EXPECT_EQ(future.status(), firebase::kFutureStatusComplete)
      << name << " returned an invalid status.";
  return (future.status() == firebase::kFutureStatusComplete);
}

static void VariantToStringInternal(const firebase::Variant& variant,
                                    std::ostream& out,
                                    const std::string& indent) {
  if (variant.is_null()) {
    out << "null";
  } else if (variant.is_int64()) {
    out << variant.int64_value();
  } else if (variant.is_double()) {
    out << variant.double_value();
  } else if (variant.is_bool()) {
    out << (variant.bool_value() ? "true" : "false");
  } else if (variant.is_string()) {
    out << variant.string_value();
  } else if (variant.is_blob()) {
    out << "blob[" << variant.blob_size() << "] = <";
    char hex[3];
    for (size_t i = 0; i < variant.blob_size(); ++i) {
      snprintf(hex, sizeof(hex), "%02x", variant.blob_data()[i]);
      if (i != 0) out << " ";
      out << hex;
    }
    out << ">";
  } else if (variant.is_vector()) {
    out << "[" << std::endl;
    const auto& v = variant.vector();
    for (auto it = v.begin(); it != v.end(); ++it) {
      out << indent + "  ";
      VariantToStringInternal(*it, out, indent + "  ");
      auto next_it = it;
      next_it++;
      if (next_it != v.end()) out << ",";
      out << std::endl;
    }
    out << "]";
  } else if (variant.is_map()) {
    out << "[" << std::endl;
    const auto& m = variant.map();
    for (auto it = m.begin(); it != m.end(); ++it) {
      out << indent + "  ";
      VariantToStringInternal(it->first, out, indent + "  ");
      out << ": ";
      VariantToStringInternal(it->second, out, indent + "  ");
      auto next_it = it;
      next_it++;
      if (next_it != m.end()) out << ",";
      out << std::endl;
    }
    out << "]";
  } else {
    out << "<unsupported type>";
  }
}

std::string FirebaseTest::VariantToString(const firebase::Variant& variant) {
  std::ostringstream out;
  VariantToStringInternal(variant, out, "");
  return out.str();
}

bool FirebaseTest::IsUserInteractionAllowed() {
  // In the trivial case, just check whether we are logging to file. If not,
  // assume interaction is allowed.
  return !app_framework::IsLoggingToFile();
}

bool FirebaseTest::Base64Encode(const std::string& input, std::string* output) {
  return ::firebase::internal::Base64Encode(input, output);
}

bool FirebaseTest::Base64Decode(const std::string& input, std::string* output) {
  return ::firebase::internal::Base64Decode(input, output);
}

class LogTestEventListener : public testing::EmptyTestEventListener {
 public:
  void OnTestPartResult(
      const testing::TestPartResult& test_part_result) override {
    if (test_part_result.failed() && test_part_result.message()) {
      app_framework::AddToFullLog(test_part_result.message());
      app_framework::AddToFullLog("\n");
    }
  };
};

}  // namespace firebase_test_framework

namespace firebase {
// gtest requires that the operator<< be in the same namespace as the item you
// are outputting.
std::ostream& operator<<(std::ostream& os, const Variant& v) {
  return os << firebase_test_framework::FirebaseTest::VariantToString(v);
}
}  // namespace firebase

namespace {

std::vector<std::string> ArgcArgvToVector(int argc, char* argv[]) {
  std::vector<std::string> args_vector;
  for (int i = 0; i < argc; ++i) {
    args_vector.push_back(argv[i]);
  }
  return args_vector;
}

char** VectorToArgcArgv(const std::vector<std::string>& args_vector,
                        int* argc) {
  // Ensure that `argv` ends with a null terminator. This is a POSIX requirement
  // (see https://man7.org/linux/man-pages/man2/execve.2.html) and googletest
  // relies on it. Without this null terminator, the
  // `ParseGoogleTestFlagsOnlyImpl()` function in googletest accesses invalid
  // memory and causes an Address Sanitizer failure.
  char** argv = new char*[args_vector.size() + 1];
  for (int i = 0; i < args_vector.size(); ++i) {
    const char* arg = args_vector[i].c_str();
    char* arg_copy = new char[std::strlen(arg) + 1];
    std::strcpy(arg_copy, arg);
    argv[i] = arg_copy;
  }
  argv[args_vector.size()] = nullptr;
  *argc = static_cast<int>(args_vector.size());
  return argv;
}

/**
 * Makes changes to argc and argv before passing them to `InitGoogleTest`.
 *
 * This function is a convenience function for developers to edit during
 * development/debugging to customize the the arguments specified to googletest
 * when directly specifying command-line arguments is not available, such as on
 * Android and iOS. For example, to debug a specific test, add the
 * --gtest_filter argument, and to list all tests add the --gtest_list_tests
 * argument.
 *
 * @param argc A pointer to the `argc` that will be specified to
 * `InitGoogleTest`; the integer to which this pointer points will be updated
 * with the new length of `argv`.
 * @param argv The `argv` that contains the arguments that would have otherwise
 * been specified to `InitGoogleTest()`; they will not be modified.
 *
 * @return The new `argv` to be specified to `InitGoogleTest()`.
 */
char** EditMainArgsForGoogleTest(int* argc, char* argv[]) {
  // Put the args into a vector of strings because modifying string objects in
  // a vector is far easier than modifying a char** array.
  const std::vector<std::string> original_args = ArgcArgvToVector(*argc, argv);
  std::vector<std::string> modified_args(original_args);

  // Add elements to the `modified_args` vector to specify to googletest.
  // e.g. modified_args.push_back("--gtest_list_tests");
  // e.g. modified_args.push_back("--gtest_filter=MyTestFixture.MyTest");

  // Create a new `argv` with the elements from the `modified_args` vector and
  // write the new count back to `argc`. The memory leaks produced by
  // `VectorToArgcArgv` acceptable because they last for the entire application.
  // Calling `VectorToArgcArgv` also fixes an invalid memory access performed by
  // googletest by adding the required null element to the end of `argv`.
  return VectorToArgcArgv(modified_args, argc);
}

}  // namespace

extern "C" int common_main(int argc, char* argv[]) {
  argv = EditMainArgsForGoogleTest(&argc, argv);
  ::testing::InitGoogleTest(&argc, argv);
  firebase_test_framework::FirebaseTest::SetArgs(argc, argv);
  app_framework::SetLogLevel(app_framework::kDebug);
  // Anything below the given log level will be preserved, and printed out in
  // the event of test failure.
  app_framework::SetPreserveFullLog(true);
  ::testing::TestEventListeners& listeners =
      ::testing::UnitTest::GetInstance()->listeners();
  listeners.Append(new firebase_test_framework::LogTestEventListener());
  int result = RUN_ALL_TESTS();

  return result;
}
<commit_msg>Only run one test<commit_after>// Copyright 2019 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "firebase_test_framework.h"  // NOLINT

#include <cstdio>
#include <cstring>
#include <string>
#include <vector>

#include "firebase/future.h"

namespace firebase {
namespace internal {
// Borrow Firebase's internal Base64 encoder and decoder.
extern bool Base64Encode(const std::string& input, std::string* output);
extern bool Base64Decode(const std::string& input, std::string* output);
}  // namespace internal
}  // namespace firebase

namespace firebase_test_framework {

int FirebaseTest::argc_ = 0;
char** FirebaseTest::argv_ = nullptr;
bool FirebaseTest::found_config_ = false;

FirebaseTest::FirebaseTest() : app_(nullptr) {}

FirebaseTest::~FirebaseTest() { assert(app_ == nullptr); }

void FirebaseTest::SetUp() {}

void FirebaseTest::TearDown() {
  if (HasFailure()) {
    app_framework::SetPreserveFullLog(false);
    app_framework::LogError(
        "Test %s failed.\nFull test log:\n%s",
        ::testing::UnitTest::GetInstance()->current_test_info()->name(),
        "========================================================");
    app_framework::SetPreserveFullLog(true);
    app_framework::AddToFullLog(
        "========================================================\n");
    app_framework::OutputFullLog();
  } else {
    app_framework::ClearFullLog();
  }
}

void FirebaseTest::FindFirebaseConfig(const char* try_directory) {
#if !defined(__ANDROID__) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
  static const char kDefaultGoogleServicesPath[] = "google-services.json";

  if (!found_config_) {
    if (try_directory[0] != '\0' && app_framework::FileExists(try_directory)) {
      app_framework::ChangeToFileDirectory(try_directory);
    } else if (app_framework::FileExists(kDefaultGoogleServicesPath)) {
      // It's in the current directory, don't do anything.
    } else {
      // Try the directory the binary is in.
      app_framework::ChangeToFileDirectory(argv_[0]);
    }
    // Only do this once.
    found_config_ = true;
  }
#endif  //  !defined(__ANDROID__) && !(defined(TARGET_OS_IPHONE) &&
        //  TARGET_OS_IPHONE)
  (void)try_directory;
}

void FirebaseTest::InitializeApp() {
  if (app_) return;  // Already initialized.

  app_framework::LogDebug("Initialize Firebase App.");

#if defined(__ANDROID__)
  app_ = ::firebase::App::Create(app_framework::GetJniEnv(),
                                 app_framework::GetActivity());
#else
  app_ = ::firebase::App::Create();
#endif  // defined(__ANDROID__)
}

void FirebaseTest::TerminateApp() {
  if (!app_) return;  // Already terminated.

  app_framework::LogDebug("Shutdown Firebase App.");
  delete app_;
  app_ = nullptr;
}

bool FirebaseTest::RunFlakyBlockBase(bool (*flaky_block)(void* context),
                                     void* context, const char* name) {
  // Run flaky_block(context). If it returns true, all is well, return true.
  // If it returns false, something flaky failed; wait a moment and try again.
  const int kRetryDelaysMs[] = {// Roughly exponential backoff for the retries.
                                100, 1000, 5000, 10000, 30000};
  const int kNumAttempts =
      1 + (sizeof(kRetryDelaysMs) / sizeof(kRetryDelaysMs[0]));

  int attempt = 0;

  while (attempt < kNumAttempts) {
    bool result = flaky_block(context);
    if (result || (attempt == kNumAttempts - 1)) {
      return result;
    }
    app_framework::LogDebug("RunFlakyBlock%s%s: Attempt %d failed",
                            *name ? " " : "", name, attempt + 1);
    int delay_ms = kRetryDelaysMs[attempt];
    app_framework::ProcessEvents(delay_ms);
    attempt++;
  }
  return false;
}

firebase::FutureBase FirebaseTest::RunWithRetryBase(
    firebase::FutureBase (*run_future)(void* context), void* context,
    const char* name, int expected_error) {
  // Run run_future(context), which returns a Future, then wait for that Future
  // to complete. If the Future returns Invalid, or if its error() does
  // not match expected_error, pause a moment and try again.
  //
  // In most cases, this will return the Future once it's been completed.
  // However, if it reaches the last attempt, it will return immediately once
  // the operation begins. This is because at this point we want to return the
  // results whether or not the operation succeeds.
  const int kRetryDelaysMs[] = {// Roughly exponential backoff for the retries.
                                100, 1000, 5000, 10000, 30000};
  const int kNumAttempts =
      1 + (sizeof(kRetryDelaysMs) / sizeof(kRetryDelaysMs[0]));

  int attempt = 0;
  firebase::FutureBase future;

  while (attempt < kNumAttempts) {
    future = run_future(context);
    if (attempt == kNumAttempts - 1) {
      // This is the last attempt, return immediately.
      break;
    }

    // Wait for completion, then check status and error.
    while (future.status() == firebase::kFutureStatusPending) {
      app_framework::ProcessEvents(100);
    }
    if (future.status() != firebase::kFutureStatusComplete) {
      app_framework::LogDebug(
          "RunWithRetry%s%s: Attempt %d returned invalid status",
          *name ? " " : "", name, attempt + 1);
    } else if (future.error() != expected_error) {
      app_framework::LogDebug(
          "RunWithRetry%s%s: Attempt %d returned error %d, expected %d",
          *name ? " " : "", name, attempt + 1, future.error(), expected_error);
    } else {
      // Future is completed and the error matches what's expected, no need to
      // retry further.
      break;
    }
    int delay_ms = kRetryDelaysMs[attempt];
    app_framework::LogDebug(
        "RunWithRetry%s%s: Pause %d milliseconds before retrying.",
        *name ? " " : "", name, delay_ms);
    app_framework::ProcessEvents(delay_ms);
    attempt++;
  }
  return future;
}

bool FirebaseTest::WaitForCompletion(const firebase::FutureBase& future,
                                     const char* name, int expected_error) {
  app_framework::LogDebug("WaitForCompletion %s", name);
  while (future.status() == firebase::kFutureStatusPending) {
    app_framework::ProcessEvents(100);
  }
  EXPECT_EQ(future.status(), firebase::kFutureStatusComplete)
      << name << " returned an invalid status.";
  EXPECT_EQ(future.error(), expected_error)
      << name << " returned error " << future.error() << ": "
      << future.error_message();
  return (future.status() == firebase::kFutureStatusComplete &&
          future.error() == expected_error);
}

bool FirebaseTest::WaitForCompletionAnyResult(
    const firebase::FutureBase& future, const char* name) {
  app_framework::LogDebug("WaitForCompletion %s", name);
  while (future.status() == firebase::kFutureStatusPending) {
    app_framework::ProcessEvents(100);
  }
  EXPECT_EQ(future.status(), firebase::kFutureStatusComplete)
      << name << " returned an invalid status.";
  return (future.status() == firebase::kFutureStatusComplete);
}

static void VariantToStringInternal(const firebase::Variant& variant,
                                    std::ostream& out,
                                    const std::string& indent) {
  if (variant.is_null()) {
    out << "null";
  } else if (variant.is_int64()) {
    out << variant.int64_value();
  } else if (variant.is_double()) {
    out << variant.double_value();
  } else if (variant.is_bool()) {
    out << (variant.bool_value() ? "true" : "false");
  } else if (variant.is_string()) {
    out << variant.string_value();
  } else if (variant.is_blob()) {
    out << "blob[" << variant.blob_size() << "] = <";
    char hex[3];
    for (size_t i = 0; i < variant.blob_size(); ++i) {
      snprintf(hex, sizeof(hex), "%02x", variant.blob_data()[i]);
      if (i != 0) out << " ";
      out << hex;
    }
    out << ">";
  } else if (variant.is_vector()) {
    out << "[" << std::endl;
    const auto& v = variant.vector();
    for (auto it = v.begin(); it != v.end(); ++it) {
      out << indent + "  ";
      VariantToStringInternal(*it, out, indent + "  ");
      auto next_it = it;
      next_it++;
      if (next_it != v.end()) out << ",";
      out << std::endl;
    }
    out << "]";
  } else if (variant.is_map()) {
    out << "[" << std::endl;
    const auto& m = variant.map();
    for (auto it = m.begin(); it != m.end(); ++it) {
      out << indent + "  ";
      VariantToStringInternal(it->first, out, indent + "  ");
      out << ": ";
      VariantToStringInternal(it->second, out, indent + "  ");
      auto next_it = it;
      next_it++;
      if (next_it != m.end()) out << ",";
      out << std::endl;
    }
    out << "]";
  } else {
    out << "<unsupported type>";
  }
}

std::string FirebaseTest::VariantToString(const firebase::Variant& variant) {
  std::ostringstream out;
  VariantToStringInternal(variant, out, "");
  return out.str();
}

bool FirebaseTest::IsUserInteractionAllowed() {
  // In the trivial case, just check whether we are logging to file. If not,
  // assume interaction is allowed.
  return !app_framework::IsLoggingToFile();
}

bool FirebaseTest::Base64Encode(const std::string& input, std::string* output) {
  return ::firebase::internal::Base64Encode(input, output);
}

bool FirebaseTest::Base64Decode(const std::string& input, std::string* output) {
  return ::firebase::internal::Base64Decode(input, output);
}

class LogTestEventListener : public testing::EmptyTestEventListener {
 public:
  void OnTestPartResult(
      const testing::TestPartResult& test_part_result) override {
    if (test_part_result.failed() && test_part_result.message()) {
      app_framework::AddToFullLog(test_part_result.message());
      app_framework::AddToFullLog("\n");
    }
  };
};

}  // namespace firebase_test_framework

namespace firebase {
// gtest requires that the operator<< be in the same namespace as the item you
// are outputting.
std::ostream& operator<<(std::ostream& os, const Variant& v) {
  return os << firebase_test_framework::FirebaseTest::VariantToString(v);
}
}  // namespace firebase

namespace {

std::vector<std::string> ArgcArgvToVector(int argc, char* argv[]) {
  std::vector<std::string> args_vector;
  for (int i = 0; i < argc; ++i) {
    args_vector.push_back(argv[i]);
  }
  return args_vector;
}

char** VectorToArgcArgv(const std::vector<std::string>& args_vector,
                        int* argc) {
  // Ensure that `argv` ends with a null terminator. This is a POSIX requirement
  // (see https://man7.org/linux/man-pages/man2/execve.2.html) and googletest
  // relies on it. Without this null terminator, the
  // `ParseGoogleTestFlagsOnlyImpl()` function in googletest accesses invalid
  // memory and causes an Address Sanitizer failure.
  char** argv = new char*[args_vector.size() + 1];
  for (int i = 0; i < args_vector.size(); ++i) {
    const char* arg = args_vector[i].c_str();
    char* arg_copy = new char[std::strlen(arg) + 1];
    std::strcpy(arg_copy, arg);
    argv[i] = arg_copy;
  }
  argv[args_vector.size()] = nullptr;
  *argc = static_cast<int>(args_vector.size());
  return argv;
}

/**
 * Makes changes to argc and argv before passing them to `InitGoogleTest`.
 *
 * This function is a convenience function for developers to edit during
 * development/debugging to customize the the arguments specified to googletest
 * when directly specifying command-line arguments is not available, such as on
 * Android and iOS. For example, to debug a specific test, add the
 * --gtest_filter argument, and to list all tests add the --gtest_list_tests
 * argument.
 *
 * @param argc A pointer to the `argc` that will be specified to
 * `InitGoogleTest`; the integer to which this pointer points will be updated
 * with the new length of `argv`.
 * @param argv The `argv` that contains the arguments that would have otherwise
 * been specified to `InitGoogleTest()`; they will not be modified.
 *
 * @return The new `argv` to be specified to `InitGoogleTest()`.
 */
char** EditMainArgsForGoogleTest(int* argc, char* argv[]) {
  // Put the args into a vector of strings because modifying string objects in
  // a vector is far easier than modifying a char** array.
  const std::vector<std::string> original_args = ArgcArgvToVector(*argc, argv);
  std::vector<std::string> modified_args(original_args);

  // Add elements to the `modified_args` vector to specify to googletest.
  modified_args.push_back("--gtest_catch_exceptions=0");
  modified_args.push_back("--gtest_filter=TransactionTest*");

  // Create a new `argv` with the elements from the `modified_args` vector and
  // write the new count back to `argc`. The memory leaks produced by
  // `VectorToArgcArgv` acceptable because they last for the entire application.
  // Calling `VectorToArgcArgv` also fixes an invalid memory access performed by
  // googletest by adding the required null element to the end of `argv`.
  return VectorToArgcArgv(modified_args, argc);
}

}  // namespace

extern "C" int common_main(int argc, char* argv[]) {
  argv = EditMainArgsForGoogleTest(&argc, argv);
  ::testing::InitGoogleTest(&argc, argv);
  firebase_test_framework::FirebaseTest::SetArgs(argc, argv);
  app_framework::SetLogLevel(app_framework::kDebug);
  // Anything below the given log level will be preserved, and printed out in
  // the event of test failure.
  app_framework::SetPreserveFullLog(true);
  ::testing::TestEventListeners& listeners =
      ::testing::UnitTest::GetInstance()->listeners();
  listeners.Append(new firebase_test_framework::LogTestEventListener());
  int result = RUN_ALL_TESTS();

  return result;
}
<|endoftext|>


<commit_before>#include "ModuleImporter.h"
#include "Application.h"
#include "ImportMesh.h"
#include "ImportMaterial.h"
#include "ImportScript.h"
#include "CompMaterial.h"
#include "CompTransform.h"
#include "ModuleFS.h"
#include "ModuleInput.h"
#include "Scene.h"
#include "ModuleGUI.h"
#include "ModuleWindow.h"
#include "JSONSerialization.h"

#include <direct.h>
#include <mono/jit/jit.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/mono-config.h>

ModuleImporter::ModuleImporter(bool start_enabled) : Module(start_enabled)
{
	Awake_enabled = true;
	Start_enabled = true;
	preUpdate_enabled = true;

	name = "Importer";
}

ModuleImporter::~ModuleImporter()
{
	RELEASE(iMesh);
	RELEASE(iMaterial);
	RELEASE(iScript);
}

bool ModuleImporter::Init(JSON_Object* node)
{
	perf_timer.Start();

	// Will contain exe path
	HMODULE hModule = GetModuleHandle(NULL);
	if (hModule != NULL)
	{
		// When passing NULL to GetModuleHandle, it returns handle of exe itself
		GetModuleFileName(hModule, directoryExe, (sizeof(directoryExe)));
	}
	iMesh = new ImportMesh();
	iMaterial = new ImportMaterial();
	iScript = new ImportScript();

	Awake_t = perf_timer.ReadMs();
	return true;
}

bool ModuleImporter::Start()
{
	perf_timer.Start();

	struct aiLogStream stream;
	stream = aiGetPredefinedLogStream(aiDefaultLogStream_DEBUGGER, nullptr);
	aiAttachLogStream(&stream);

	// Now InitSystem Domain Mono
	if (InitSystemScript())
	{
		LOG("Culverin Assembly Init SUCCESS.");
	}
	else
	{
		LOG("Culverin Assembly Init FAIL.");
	}

	Start_t = perf_timer.ReadMs();
	return true;
}

bool ModuleImporter::InitSystemScript()
{
	char my_path[FILENAME_MAX];
	// Fill my_path char array with the path of the .dll
	_getcwd(my_path, FILENAME_MAX);

	// Set where Mono Directory is placed in mono_path
	mono_path = my_path;
	mono_path += "/Mono";

	// Use the standard configuration ----
	mono_config_parse(NULL);

	// Get the correct dirs --------------
	std::string lib = mono_path;
	lib += "/lib";
	std::string etc = mono_path;
	etc += "/etc";

	// Setup the default directories for mono use here for now the directories of your Mono installation
	mono_set_dirs(lib.c_str(), etc.c_str());
	domain = mono_jit_init_version("/Game", "v4.0.30319");

	MonoAssembly* culverin_assembly = mono_domain_assembly_open(domain, "./ScriptManager/AssemblyReference/CulverinEditor.dll");
	if (culverin_assembly)
	{
		culverin_mono_image = mono_assembly_get_image(culverin_assembly);
		return true;
	}
	return false;
}

update_status ModuleImporter::PreUpdate(float dt)
{
	perf_timer.Start();

	if (App->input->GetKey(SDL_SCANCODE_B) == KEY_DOWN)
	{
		//ImportMesh* imp = new ImportMesh();
		//imp->Load("Baker_house.rin");
	}

	preUpdate_t = perf_timer.ReadMs();
	return UPDATE_CONTINUE;
}

GameObject* ModuleImporter::ProcessNode(aiNode* node, const aiScene* scene, GameObject* obj)
{
	static int count = 0;
	GameObject* objChild = new GameObject(obj);
	objChild->SetName(App->GetCharfromConstChar(node->mName.C_Str()));

	CompTransform* trans = (CompTransform*)objChild->AddComponent(C_TRANSFORM);
	ProcessTransform(node, trans);

	// Process all the Node's MESHES
	for (uint i = 0; i < node->mNumMeshes; i++)
	{
		GameObject* newObj = nullptr;

		if (node->mNumMeshes > 1)
		{
			newObj = new GameObject(obj);
			std::string newName = "Submesh" + std::to_string(i);
			newObj->SetName(App->GetCharfromConstChar(newName.c_str()));
			CompTransform* newTrans = (CompTransform*)newObj->AddComponent(C_TRANSFORM);
			ProcessTransform(node, newTrans);
		}

		else
		{
			newObj = objChild;
		}

		aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
		iMesh->Import(scene, mesh, newObj, node->mName.C_Str());
	}

	// Process children
	for (uint i = 0; i < node->mNumChildren; i++)
	{
		ProcessNode(node->mChildren[i], scene, objChild);
	}

	return objChild;
}

GameObject* ModuleImporter::ProcessNode(aiNode* node, const aiScene* scene, GameObject* obj, std::vector<ReImport>& resourcesToReimport)
{
	static int count = 0;
	GameObject* objChild = new GameObject(obj);
	objChild->SetName(App->GetCharfromConstChar(node->mName.C_Str()));

	CompTransform* trans = (CompTransform*)objChild->AddComponent(C_TRANSFORM);
	ProcessTransform(node, trans);

	// Process all the Node's MESHES
	for (uint i = 0; i < node->mNumMeshes; i++)
	{
		GameObject* newObj = nullptr;

		if (node->mNumMeshes > 1)
		{
			newObj = new GameObject(obj);
			std::string newName = "Submesh" + std::to_string(i);
			newObj->SetName(App->GetCharfromConstChar(newName.c_str()));
			CompTransform* newTrans = (CompTransform*)newObj->AddComponent(C_TRANSFORM);
			ProcessTransform(node, newTrans);
		}

		else
		{
			newObj = objChild;
		}

		aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
		bool isReimported = false;
		for (int i = 0; i < resourcesToReimport.size(); i++)
		{
			if (strcmp(node->mName.C_Str(), resourcesToReimport[i].nameMesh) == 0)
			{
				iMesh->Import(scene, mesh, newObj, node->mName.C_Str(), resourcesToReimport[i].uuid);
				isReimported = true;
			}
		}
		if (isReimported == false)
		{
			iMesh->Import(scene, mesh, newObj, node->mName.C_Str(), 0);
		}
	}

	// Process children
	for (uint i = 0; i < node->mNumChildren; i++)
	{
		ProcessNode(node->mChildren[i], scene, objChild, resourcesToReimport);
	}

	return objChild;
}

void ModuleImporter::ProcessTransform(aiNode* node, CompTransform* trans)
{
	aiVector3D aiPos;
	aiQuaternion aiRot;
	aiVector3D aiScale;
	aiMatrix4x4 aiMatrix;
	float4x4 matrix;

	aiMatrix = node->mTransformation;

	node->mTransformation.Decompose(aiScale, aiRot, aiPos);

	trans->SetPos(float3(aiPos.x, aiPos.y, aiPos.z));
	trans->SetRot(Quat(aiRot.x, aiRot.y, aiRot.z, aiRot.w));
	trans->SetScale(float3(aiScale.x, aiScale.y, aiScale.z));

	trans->Enable();
}

void ModuleImporter::ProcessTransform(CompTransform* trans)
{
	//Set all variables to zero/identity
	trans->ResetMatrix();
	trans->Enable();
}

bool ModuleImporter::CleanUp()
{
	aiDetachAllLogStreams();
	return true;
}

MonoDomain* ModuleImporter::GetDomain()
{
	return domain;
}

MonoImage* ModuleImporter::GetCulverinImage()
{
	return culverin_mono_image;
}

std::string ModuleImporter::GetMonoPath() const
{
	return mono_path;
}

bool ModuleImporter::Import(const char* file, Resource::Type type)
{
	bool ret = true;

	switch (type)
	{
	case Resource::Type::MESH:
	{
		LOG("IMPORTING MODEL, File Path: %s", file);

		//Clear vector of textures, but dont import same textures!
		//iMesh->PrepareToImport();
		const aiScene* scene = aiImportFile(file, aiProcessPreset_TargetRealtime_MaxQuality);
		if (scene != nullptr)
		{
			GameObject* obj = ProcessNode(scene->mRootNode, scene, nullptr);
			obj->SetName(App->GetCharfromConstChar(App->fs->FixName_directory(file).c_str()));

			//Now Save Serialitzate OBJ -> Prefab
			std::string Newdirectory = ((Project*)App->gui->winManager[WindowName::PROJECT])->GetDirectory();
			Newdirectory += "\\" + App->fs->FixName_directory(file);
			App->Json_seria->SavePrefab(*obj, ((Project*)App->gui->winManager[WindowName::PROJECT])->GetDirectory(), Newdirectory.c_str());

			//App->scene->gameobjects.push_back(obj);
			App->scene->DeleteGameObject(obj);
		}
		else
		{
			ret = false;
			LOG("Cannot import this fbx.");
		}
		aiReleaseImport(scene);
		break;
	}
	case Resource::Type::MATERIAL:
	{
		LOG("IMPORTING TEXTURE, File Path: %s", file);
		iMaterial->Import(file);

		break;
	}
	case Resource::Type::SCRIPT:
	{
		LOG("IMPORTING SCRIPT, File Path: %s", file);
		iScript->Import(file);

		break;
	}
	case Resource::Type::UNKNOWN:
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "UNKNOWN file type dropped on window",
			file, App->window->window);
		LOG("UNKNOWN FILE TYPE, File Path: %s", file);
		break;
	}

	default:
		break;
	}

	return true;
}

bool ModuleImporter::Import(const char* file, Resource::Type type, std::vector<ReImport>& resourcesToReimport)
{
	bool ret = true;

	switch (type)
	{
	case Resource::Type::MESH:
	{
		LOG("IMPORTING MODEL, File Path: %s", file);

		//Clear vector of textures, but dont import same textures!
		//iMesh->PrepareToImport();
		const aiScene* scene = aiImportFile(file, aiProcessPreset_TargetRealtime_MaxQuality);
		if (scene != nullptr)
		{
			GameObject* obj = ProcessNode(scene->mRootNode, scene, nullptr, resourcesToReimport);
			obj->SetName(App->GetCharfromConstChar(App->fs->FixName_directory(file).c_str()));

			//Now Save Serialitzate OBJ -> Prefab
			std::string Newdirectory = ((Project*)App->gui->winManager[WindowName::PROJECT])->GetDirectory();
			Newdirectory += "\\" + App->fs->FixName_directory(file);
			App->Json_seria->SavePrefab(*obj, ((Project*)App->gui->winManager[WindowName::PROJECT])->GetDirectory(), Newdirectory.c_str());

			App->scene->gameobjects.push_back(obj); // Need cleen
			App->scene->DeleteGameObject(obj);
		}
		else
		{
			ret = false;
			LOG("Cannot import this fbx.");
		}
		aiReleaseImport(scene);
		break;
	}
	case Resource::Type::MATERIAL:
	{
		LOG("IMPORTING TEXTURE, File Path: %s", file);
		//
		bool isReImport = false;
		for (int i = 0; i < resourcesToReimport.size(); i++)
		{
			if (strcmp(file, resourcesToReimport[i].directoryObj) == 0)
			{
				iMaterial->Import(file, resourcesToReimport[i].uuid);
				isReImport = true;
				break;
			}
		}
		if (isReImport == false)
		{
			iMaterial->Import(file);
		}
		break;
	}
	case Resource::Type::SCRIPT:
	{
		LOG("IMPORTING SCRIPT, File Path: %s", file);
		//
		bool isReImport = false;
		for (int i = 0; i < resourcesToReimport.size(); i++)
		{
			if (strcmp(file, resourcesToReimport[i].directoryObj) == 0)
			{
				iScript->Import(file, resourcesToReimport[i].uuid);
				isReImport = true;
				break;
			}
		}
		if (isReImport == false)
		{
			iScript->Import(file);
		}
		break;
	}
	case Resource::Type::UNKNOWN:
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "UNKNOWN file type dropped on window",
			file, App->window->window);
		LOG("UNKNOWN FILE TYPE, File Path: %s", file);
		break;
	}

	default:
		break;
	}

	return true;
}
<commit_msg>Removed invalid functions<commit_after>#include "ModuleImporter.h"
#include "Application.h"
#include "ImportMesh.h"
#include "ImportMaterial.h"
#include "ImportScript.h"
#include "CompMaterial.h"
#include "CompTransform.h"
#include "ModuleFS.h"
#include "ModuleInput.h"
#include "Scene.h"
#include "ModuleGUI.h"
#include "ModuleWindow.h"
#include "JSONSerialization.h"

#include <direct.h>
#include <mono/jit/jit.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/mono-config.h>

ModuleImporter::ModuleImporter(bool start_enabled) : Module(start_enabled)
{
	Awake_enabled = true;
	Start_enabled = true;
	preUpdate_enabled = true;

	name = "Importer";
}

ModuleImporter::~ModuleImporter()
{
	RELEASE(iMesh);
	RELEASE(iMaterial);
	RELEASE(iScript);
}

bool ModuleImporter::Init(JSON_Object* node)
{
	perf_timer.Start();

	// Will contain exe path
	HMODULE hModule = GetModuleHandle(NULL);
	if (hModule != NULL)
	{
		// When passing NULL to GetModuleHandle, it returns handle of exe itself
		GetModuleFileName(hModule, directoryExe, (sizeof(directoryExe)));
	}
	iMesh = new ImportMesh();
	iMaterial = new ImportMaterial();
	iScript = new ImportScript();

	Awake_t = perf_timer.ReadMs();
	return true;
}

bool ModuleImporter::Start()
{
	perf_timer.Start();

	struct aiLogStream stream;
	stream = aiGetPredefinedLogStream(aiDefaultLogStream_DEBUGGER, nullptr);
	aiAttachLogStream(&stream);

	// Now InitSystem Domain Mono
	if (iScript->InitScriptingSystem())
	{
		LOG("Culverin Assembly Init SUCCESS.");
	}
	else
	{
		LOG("Culverin Assembly Init FAIL.");
	}

	Start_t = perf_timer.ReadMs();
	return true;
}

update_status ModuleImporter::PreUpdate(float dt)
{
	perf_timer.Start();

	if (App->input->GetKey(SDL_SCANCODE_B) == KEY_DOWN)
	{
		//ImportMesh* imp = new ImportMesh();
		//imp->Load("Baker_house.rin");
	}

	preUpdate_t = perf_timer.ReadMs();
	return UPDATE_CONTINUE;
}

GameObject* ModuleImporter::ProcessNode(aiNode* node, const aiScene* scene, GameObject* obj)
{
	static int count = 0;
	GameObject* objChild = new GameObject(obj);
	objChild->SetName(App->GetCharfromConstChar(node->mName.C_Str()));

	CompTransform* trans = (CompTransform*)objChild->AddComponent(C_TRANSFORM);
	ProcessTransform(node, trans);

	// Process all the Node's MESHES
	for (uint i = 0; i < node->mNumMeshes; i++)
	{
		GameObject* newObj = nullptr;

		if (node->mNumMeshes > 1)
		{
			newObj = new GameObject(obj);
			std::string newName = "Submesh" + std::to_string(i);
			newObj->SetName(App->GetCharfromConstChar(newName.c_str()));
			CompTransform* newTrans = (CompTransform*)newObj->AddComponent(C_TRANSFORM);
			ProcessTransform(node, newTrans);
		}

		else
		{
			newObj = objChild;
		}

		aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
		iMesh->Import(scene, mesh, newObj, node->mName.C_Str());
	}

	// Process children
	for (uint i = 0; i < node->mNumChildren; i++)
	{
		ProcessNode(node->mChildren[i], scene, objChild);
	}

	return objChild;
}

GameObject* ModuleImporter::ProcessNode(aiNode* node, const aiScene* scene, GameObject* obj, std::vector<ReImport>& resourcesToReimport)
{
	static int count = 0;
	GameObject* objChild = new GameObject(obj);
	objChild->SetName(App->GetCharfromConstChar(node->mName.C_Str()));

	CompTransform* trans = (CompTransform*)objChild->AddComponent(C_TRANSFORM);
	ProcessTransform(node, trans);

	// Process all the Node's MESHES
	for (uint i = 0; i < node->mNumMeshes; i++)
	{
		GameObject* newObj = nullptr;

		if (node->mNumMeshes > 1)
		{
			newObj = new GameObject(obj);
			std::string newName = "Submesh" + std::to_string(i);
			newObj->SetName(App->GetCharfromConstChar(newName.c_str()));
			CompTransform* newTrans = (CompTransform*)newObj->AddComponent(C_TRANSFORM);
			ProcessTransform(node, newTrans);
		}

		else
		{
			newObj = objChild;
		}

		aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
		bool isReimported = false;
		for (int i = 0; i < resourcesToReimport.size(); i++)
		{
			if (strcmp(node->mName.C_Str(), resourcesToReimport[i].nameMesh) == 0)
			{
				iMesh->Import(scene, mesh, newObj, node->mName.C_Str(), resourcesToReimport[i].uuid);
				isReimported = true;
			}
		}
		if (isReimported == false)
		{
			iMesh->Import(scene, mesh, newObj, node->mName.C_Str(), 0);
		}
	}

	// Process children
	for (uint i = 0; i < node->mNumChildren; i++)
	{
		ProcessNode(node->mChildren[i], scene, objChild, resourcesToReimport);
	}

	return objChild;
}

void ModuleImporter::ProcessTransform(aiNode* node, CompTransform* trans)
{
	aiVector3D aiPos;
	aiQuaternion aiRot;
	aiVector3D aiScale;
	aiMatrix4x4 aiMatrix;
	float4x4 matrix;

	aiMatrix = node->mTransformation;

	node->mTransformation.Decompose(aiScale, aiRot, aiPos);

	trans->SetPos(float3(aiPos.x, aiPos.y, aiPos.z));
	trans->SetRot(Quat(aiRot.x, aiRot.y, aiRot.z, aiRot.w));
	trans->SetScale(float3(aiScale.x, aiScale.y, aiScale.z));

	trans->Enable();
}

void ModuleImporter::ProcessTransform(CompTransform* trans)
{
	//Set all variables to zero/identity
	trans->ResetMatrix();
	trans->Enable();
}

bool ModuleImporter::CleanUp()
{
	aiDetachAllLogStreams();
	return true;
}

bool ModuleImporter::Import(const char* file, Resource::Type type)
{
	bool ret = true;

	switch (type)
	{
	case Resource::Type::MESH:
	{
		LOG("IMPORTING MODEL, File Path: %s", file);

		//Clear vector of textures, but dont import same textures!
		//iMesh->PrepareToImport();
		const aiScene* scene = aiImportFile(file, aiProcessPreset_TargetRealtime_MaxQuality);
		if (scene != nullptr)
		{
			GameObject* obj = ProcessNode(scene->mRootNode, scene, nullptr);
			obj->SetName(App->GetCharfromConstChar(App->fs->FixName_directory(file).c_str()));

			//Now Save Serialitzate OBJ -> Prefab
			std::string Newdirectory = ((Project*)App->gui->winManager[WindowName::PROJECT])->GetDirectory();
			Newdirectory += "\\" + App->fs->FixName_directory(file);
			App->Json_seria->SavePrefab(*obj, ((Project*)App->gui->winManager[WindowName::PROJECT])->GetDirectory(), Newdirectory.c_str());

			//App->scene->gameobjects.push_back(obj);
			App->scene->DeleteGameObject(obj);
		}
		else
		{
			ret = false;
			LOG("Cannot import this fbx.");
		}
		aiReleaseImport(scene);
		break;
	}
	case Resource::Type::MATERIAL:
	{
		LOG("IMPORTING TEXTURE, File Path: %s", file);
		iMaterial->Import(file);

		break;
	}
	case Resource::Type::SCRIPT:
	{
		LOG("IMPORTING SCRIPT, File Path: %s", file);
		iScript->Import(file);

		break;
	}
	case Resource::Type::UNKNOWN:
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "UNKNOWN file type dropped on window",
			file, App->window->window);
		LOG("UNKNOWN FILE TYPE, File Path: %s", file);
		break;
	}

	default:
		break;
	}

	return true;
}

bool ModuleImporter::Import(const char* file, Resource::Type type, std::vector<ReImport>& resourcesToReimport)
{
	bool ret = true;

	switch (type)
	{
	case Resource::Type::MESH:
	{
		LOG("IMPORTING MODEL, File Path: %s", file);

		//Clear vector of textures, but dont import same textures!
		//iMesh->PrepareToImport();
		const aiScene* scene = aiImportFile(file, aiProcessPreset_TargetRealtime_MaxQuality);
		if (scene != nullptr)
		{
			GameObject* obj = ProcessNode(scene->mRootNode, scene, nullptr, resourcesToReimport);
			obj->SetName(App->GetCharfromConstChar(App->fs->FixName_directory(file).c_str()));

			//Now Save Serialitzate OBJ -> Prefab
			std::string Newdirectory = ((Project*)App->gui->winManager[WindowName::PROJECT])->GetDirectory();
			Newdirectory += "\\" + App->fs->FixName_directory(file);
			App->Json_seria->SavePrefab(*obj, ((Project*)App->gui->winManager[WindowName::PROJECT])->GetDirectory(), Newdirectory.c_str());

			App->scene->gameobjects.push_back(obj); // Need cleen
			App->scene->DeleteGameObject(obj);
		}
		else
		{
			ret = false;
			LOG("Cannot import this fbx.");
		}
		aiReleaseImport(scene);
		break;
	}
	case Resource::Type::MATERIAL:
	{
		LOG("IMPORTING TEXTURE, File Path: %s", file);
		//
		bool isReImport = false;
		for (int i = 0; i < resourcesToReimport.size(); i++)
		{
			if (strcmp(file, resourcesToReimport[i].directoryObj) == 0)
			{
				iMaterial->Import(file, resourcesToReimport[i].uuid);
				isReImport = true;
				break;
			}
		}
		if (isReImport == false)
		{
			iMaterial->Import(file);
		}
		break;
	}
	case Resource::Type::SCRIPT:
	{
		LOG("IMPORTING SCRIPT, File Path: %s", file);
		//
		bool isReImport = false;
		for (int i = 0; i < resourcesToReimport.size(); i++)
		{
			if (strcmp(file, resourcesToReimport[i].directoryObj) == 0)
			{
				iScript->Import(file, resourcesToReimport[i].uuid);
				isReImport = true;
				break;
			}
		}
		if (isReImport == false)
		{
			iScript->Import(file);
		}
		break;
	}
	case Resource::Type::UNKNOWN:
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "UNKNOWN file type dropped on window",
			file, App->window->window);
		LOG("UNKNOWN FILE TYPE, File Path: %s", file);
		break;
	}

	default:
		break;
	}

	return true;
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Cloudflare, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#if KJ_HAS_ZLIB

#include "gzip.h"
#include <kj/debug.h>

namespace kj {

namespace _ {  // private

GzipOutputContext::GzipOutputContext(kj::Maybe<int> compressionLevel) {
  int initResult;

  KJ_IF_MAYBE(level, compressionLevel) {
    compressing = true;
    initResult =
      deflateInit2(&ctx, *level, Z_DEFLATED,
                   15 + 16,  // windowBits = 15 (maximum) + magic value 16 to ask for gzip.
                   8,        // memLevel = 8 (the default)
                   Z_DEFAULT_STRATEGY);
  } else {
    compressing = false;
    initResult = inflateInit2(&ctx, 15 + 16);
  }

  if (initResult != Z_OK) {
    fail(initResult);
  }
}

GzipOutputContext::~GzipOutputContext() noexcept(false) {
  compressing ? deflateEnd(&ctx) : inflateEnd(&ctx);
}

void GzipOutputContext::setInput(const void* in, size_t size) {
  ctx.next_in = const_cast<byte*>(reinterpret_cast<const byte*>(in));
  ctx.avail_in = size;
}

kj::Tuple<bool, kj::ArrayPtr<const byte>> GzipOutputContext::pumpOnce(int flush) {
  ctx.next_out = buffer;
  ctx.avail_out = sizeof(buffer);

  auto result = compressing ? deflate(&ctx, flush) : inflate(&ctx, flush);
  if (result != Z_OK && result != Z_BUF_ERROR && result != Z_STREAM_END) {
    fail(result);
  }

  // - Z_STREAM_END means we have finished the stream successfully.
  // - Z_BUF_ERROR means we didn't have any more input to process
  //   (but still have to make a call to write to potentially flush data).
  return kj::tuple(result == Z_OK, kj::arrayPtr(buffer, sizeof(buffer) - ctx.avail_out));
}

void GzipOutputContext::fail(int result) {
  auto header = compressing ? "gzip compression failed" : "gzip decompression failed";
  if (ctx.msg == nullptr) {
    KJ_FAIL_REQUIRE(header, result);
  } else {
    KJ_FAIL_REQUIRE(header, ctx.msg);
  }
}

}  // namespace _ (private)

GzipInputStream::GzipInputStream(InputStream& inner)
    : inner(inner) {
  // windowBits = 15 (maximum) + magic value 16 to ask for gzip.
  KJ_ASSERT(inflateInit2(&ctx, 15 + 16) == Z_OK);
}

GzipInputStream::~GzipInputStream() noexcept(false) {
  inflateEnd(&ctx);
}

size_t GzipInputStream::tryRead(void* out, size_t minBytes, size_t maxBytes) {
  if (maxBytes == 0) return size_t(0);

  return readImpl(reinterpret_cast<byte*>(out), minBytes, maxBytes, 0);
}

size_t GzipInputStream::readImpl(
    byte* out, size_t minBytes, size_t maxBytes, size_t alreadyRead) {
  if (ctx.avail_in == 0) {
    size_t amount = inner.tryRead(buffer, 1, sizeof(buffer));
    if (amount == 0) {
      if (!atValidEndpoint) {
        KJ_FAIL_REQUIRE("gzip compressed stream ended prematurely");
      }
      return alreadyRead;
    } else {
      ctx.next_in = buffer;
      ctx.avail_in = amount;
    }
  }

  ctx.next_out = reinterpret_cast<byte*>(out);
  ctx.avail_out = maxBytes;

  auto inflateResult = inflate(&ctx, Z_NO_FLUSH);
  atValidEndpoint = inflateResult == Z_STREAM_END;
  if (inflateResult == Z_OK || inflateResult == Z_STREAM_END) {
    if (atValidEndpoint && ctx.avail_in > 0) {
      // There's more data available. Assume start of new content.
      KJ_ASSERT(inflateReset(&ctx) == Z_OK);
    }

    size_t n = maxBytes - ctx.avail_out;
    if (n >= minBytes) {
      return n + alreadyRead;
    } else {
      return readImpl(out + n, minBytes - n, maxBytes - n, alreadyRead + n);
    }
  } else {
    if (ctx.msg == nullptr) {
      KJ_FAIL_REQUIRE("gzip decompression failed", inflateResult);
    } else {
      KJ_FAIL_REQUIRE("gzip decompression failed", ctx.msg);
    }
  }
}

// =======================================================================================

GzipOutputStream::GzipOutputStream(OutputStream& inner, int compressionLevel)
    : inner(inner), ctx(compressionLevel) {}

GzipOutputStream::GzipOutputStream(OutputStream& inner, decltype(DECOMPRESS))
    : inner(inner), ctx(nullptr) {}

GzipOutputStream::~GzipOutputStream() noexcept(false) {
  pump(Z_FINISH);
}

void GzipOutputStream::write(const void* in, size_t size) {
  ctx.setInput(in, size);
  pump(Z_NO_FLUSH);
}

void GzipOutputStream::pump(int flush) {
  bool ok;
  do {
    auto result = ctx.pumpOnce(flush);
    ok = get<0>(result);
    auto chunk = get<1>(result);
    inner.write(chunk.begin(), chunk.size());
  } while (ok);
}

// =======================================================================================

GzipAsyncInputStream::GzipAsyncInputStream(AsyncInputStream& inner)
    : inner(inner) {
  // windowBits = 15 (maximum) + magic value 16 to ask for gzip.
  KJ_ASSERT(inflateInit2(&ctx, 15 + 16) == Z_OK);
}

GzipAsyncInputStream::~GzipAsyncInputStream() noexcept(false) {
  inflateEnd(&ctx);
}

Promise<size_t> GzipAsyncInputStream::tryRead(void* out, size_t minBytes, size_t maxBytes) {
  if (maxBytes == 0) return size_t(0);

  return readImpl(reinterpret_cast<byte*>(out), minBytes, maxBytes, 0);
}

Promise<size_t> GzipAsyncInputStream::readImpl(
    byte* out, size_t minBytes, size_t maxBytes, size_t alreadyRead) {
  if (ctx.avail_in == 0) {
    return inner.tryRead(buffer, 1, sizeof(buffer))
        .then([this,out,minBytes,maxBytes,alreadyRead](size_t amount) -> Promise<size_t> {
      if (amount == 0) {
        if (!atValidEndpoint) {
          return KJ_EXCEPTION(DISCONNECTED, "gzip compressed stream ended prematurely");
        }
        return alreadyRead;
      } else {
        ctx.next_in = buffer;
        ctx.avail_in = amount;
        return readImpl(out, minBytes, maxBytes, alreadyRead);
      }
    });
  }

  ctx.next_out = reinterpret_cast<byte*>(out);
  ctx.avail_out = maxBytes;

  auto inflateResult = inflate(&ctx, Z_NO_FLUSH);
  atValidEndpoint = inflateResult == Z_STREAM_END;
  if (inflateResult == Z_OK || inflateResult == Z_STREAM_END) {
    if (atValidEndpoint && ctx.avail_in > 0) {
      // There's more data available. Assume start of new content.
      KJ_ASSERT(inflateReset(&ctx) == Z_OK);
    }

    size_t n = maxBytes - ctx.avail_out;
    if (n >= minBytes) {
      return n + alreadyRead;
    } else {
      return readImpl(out + n, minBytes - n, maxBytes - n, alreadyRead + n);
    }
  } else {
    if (ctx.msg == nullptr) {
      KJ_FAIL_REQUIRE("gzip decompression failed", inflateResult);
    } else {
      KJ_FAIL_REQUIRE("gzip decompression failed", ctx.msg);
    }
  }
}

// =======================================================================================

GzipAsyncOutputStream::GzipAsyncOutputStream(AsyncOutputStream& inner, int compressionLevel)
    : inner(inner), ctx(compressionLevel) {}

GzipAsyncOutputStream::GzipAsyncOutputStream(AsyncOutputStream& inner, decltype(DECOMPRESS))
    : inner(inner), ctx(nullptr) {}

Promise<void> GzipAsyncOutputStream::write(const void* in, size_t size) {
  ctx.setInput(in, size);
  return pump(Z_NO_FLUSH);
}

Promise<void> GzipAsyncOutputStream::write(ArrayPtr<const ArrayPtr<const byte>> pieces) {
  if (pieces.size() == 0) return kj::READY_NOW;
  return write(pieces[0].begin(), pieces[0].size())
      .then([this,pieces]() {
    return write(pieces.slice(1, pieces.size()));
  });
}

kj::Promise<void> GzipAsyncOutputStream::pump(int flush) {
  auto result = ctx.pumpOnce(flush);
  auto ok = get<0>(result);
  auto chunk = get<1>(result);
  auto promise = inner.write(chunk.begin(), chunk.size());
  if (ok) {
    promise = promise.then([this, flush]() { return pump(flush); });
  }
  return promise;
}

}  // namespace kj

#endif  // KJ_HAS_ZLIB
<commit_msg>Don't make zero-length writes from gzip streams.<commit_after>// Copyright (c) 2017 Cloudflare, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#if KJ_HAS_ZLIB

#include "gzip.h"
#include <kj/debug.h>

namespace kj {

namespace _ {  // private

GzipOutputContext::GzipOutputContext(kj::Maybe<int> compressionLevel) {
  int initResult;

  KJ_IF_MAYBE(level, compressionLevel) {
    compressing = true;
    initResult =
      deflateInit2(&ctx, *level, Z_DEFLATED,
                   15 + 16,  // windowBits = 15 (maximum) + magic value 16 to ask for gzip.
                   8,        // memLevel = 8 (the default)
                   Z_DEFAULT_STRATEGY);
  } else {
    compressing = false;
    initResult = inflateInit2(&ctx, 15 + 16);
  }

  if (initResult != Z_OK) {
    fail(initResult);
  }
}

GzipOutputContext::~GzipOutputContext() noexcept(false) {
  compressing ? deflateEnd(&ctx) : inflateEnd(&ctx);
}

void GzipOutputContext::setInput(const void* in, size_t size) {
  ctx.next_in = const_cast<byte*>(reinterpret_cast<const byte*>(in));
  ctx.avail_in = size;
}

kj::Tuple<bool, kj::ArrayPtr<const byte>> GzipOutputContext::pumpOnce(int flush) {
  ctx.next_out = buffer;
  ctx.avail_out = sizeof(buffer);

  auto result = compressing ? deflate(&ctx, flush) : inflate(&ctx, flush);
  if (result != Z_OK && result != Z_BUF_ERROR && result != Z_STREAM_END) {
    fail(result);
  }

  // - Z_STREAM_END means we have finished the stream successfully.
  // - Z_BUF_ERROR means we didn't have any more input to process
  //   (but still have to make a call to write to potentially flush data).
  return kj::tuple(result == Z_OK, kj::arrayPtr(buffer, sizeof(buffer) - ctx.avail_out));
}

void GzipOutputContext::fail(int result) {
  auto header = compressing ? "gzip compression failed" : "gzip decompression failed";
  if (ctx.msg == nullptr) {
    KJ_FAIL_REQUIRE(header, result);
  } else {
    KJ_FAIL_REQUIRE(header, ctx.msg);
  }
}

}  // namespace _ (private)

GzipInputStream::GzipInputStream(InputStream& inner)
    : inner(inner) {
  // windowBits = 15 (maximum) + magic value 16 to ask for gzip.
  KJ_ASSERT(inflateInit2(&ctx, 15 + 16) == Z_OK);
}

GzipInputStream::~GzipInputStream() noexcept(false) {
  inflateEnd(&ctx);
}

size_t GzipInputStream::tryRead(void* out, size_t minBytes, size_t maxBytes) {
  if (maxBytes == 0) return size_t(0);

  return readImpl(reinterpret_cast<byte*>(out), minBytes, maxBytes, 0);
}

size_t GzipInputStream::readImpl(
    byte* out, size_t minBytes, size_t maxBytes, size_t alreadyRead) {
  if (ctx.avail_in == 0) {
    size_t amount = inner.tryRead(buffer, 1, sizeof(buffer));
    if (amount == 0) {
      if (!atValidEndpoint) {
        KJ_FAIL_REQUIRE("gzip compressed stream ended prematurely");
      }
      return alreadyRead;
    } else {
      ctx.next_in = buffer;
      ctx.avail_in = amount;
    }
  }

  ctx.next_out = reinterpret_cast<byte*>(out);
  ctx.avail_out = maxBytes;

  auto inflateResult = inflate(&ctx, Z_NO_FLUSH);
  atValidEndpoint = inflateResult == Z_STREAM_END;
  if (inflateResult == Z_OK || inflateResult == Z_STREAM_END) {
    if (atValidEndpoint && ctx.avail_in > 0) {
      // There's more data available. Assume start of new content.
      KJ_ASSERT(inflateReset(&ctx) == Z_OK);
    }

    size_t n = maxBytes - ctx.avail_out;
    if (n >= minBytes) {
      return n + alreadyRead;
    } else {
      return readImpl(out + n, minBytes - n, maxBytes - n, alreadyRead + n);
    }
  } else {
    if (ctx.msg == nullptr) {
      KJ_FAIL_REQUIRE("gzip decompression failed", inflateResult);
    } else {
      KJ_FAIL_REQUIRE("gzip decompression failed", ctx.msg);
    }
  }
}

// =======================================================================================

GzipOutputStream::GzipOutputStream(OutputStream& inner, int compressionLevel)
    : inner(inner), ctx(compressionLevel) {}

GzipOutputStream::GzipOutputStream(OutputStream& inner, decltype(DECOMPRESS))
    : inner(inner), ctx(nullptr) {}

GzipOutputStream::~GzipOutputStream() noexcept(false) {
  pump(Z_FINISH);
}

void GzipOutputStream::write(const void* in, size_t size) {
  ctx.setInput(in, size);
  pump(Z_NO_FLUSH);
}

void GzipOutputStream::pump(int flush) {
  bool ok;
  do {
    auto result = ctx.pumpOnce(flush);
    ok = get<0>(result);
    auto chunk = get<1>(result);
    if (chunk.size() > 0) {
      inner.write(chunk.begin(), chunk.size());
    }
  } while (ok);
}

// =======================================================================================

GzipAsyncInputStream::GzipAsyncInputStream(AsyncInputStream& inner)
    : inner(inner) {
  // windowBits = 15 (maximum) + magic value 16 to ask for gzip.
  KJ_ASSERT(inflateInit2(&ctx, 15 + 16) == Z_OK);
}

GzipAsyncInputStream::~GzipAsyncInputStream() noexcept(false) {
  inflateEnd(&ctx);
}

Promise<size_t> GzipAsyncInputStream::tryRead(void* out, size_t minBytes, size_t maxBytes) {
  if (maxBytes == 0) return size_t(0);

  return readImpl(reinterpret_cast<byte*>(out), minBytes, maxBytes, 0);
}

Promise<size_t> GzipAsyncInputStream::readImpl(
    byte* out, size_t minBytes, size_t maxBytes, size_t alreadyRead) {
  if (ctx.avail_in == 0) {
    return inner.tryRead(buffer, 1, sizeof(buffer))
        .then([this,out,minBytes,maxBytes,alreadyRead](size_t amount) -> Promise<size_t> {
      if (amount == 0) {
        if (!atValidEndpoint) {
          return KJ_EXCEPTION(DISCONNECTED, "gzip compressed stream ended prematurely");
        }
        return alreadyRead;
      } else {
        ctx.next_in = buffer;
        ctx.avail_in = amount;
        return readImpl(out, minBytes, maxBytes, alreadyRead);
      }
    });
  }

  ctx.next_out = reinterpret_cast<byte*>(out);
  ctx.avail_out = maxBytes;

  auto inflateResult = inflate(&ctx, Z_NO_FLUSH);
  atValidEndpoint = inflateResult == Z_STREAM_END;
  if (inflateResult == Z_OK || inflateResult == Z_STREAM_END) {
    if (atValidEndpoint && ctx.avail_in > 0) {
      // There's more data available. Assume start of new content.
      KJ_ASSERT(inflateReset(&ctx) == Z_OK);
    }

    size_t n = maxBytes - ctx.avail_out;
    if (n >= minBytes) {
      return n + alreadyRead;
    } else {
      return readImpl(out + n, minBytes - n, maxBytes - n, alreadyRead + n);
    }
  } else {
    if (ctx.msg == nullptr) {
      KJ_FAIL_REQUIRE("gzip decompression failed", inflateResult);
    } else {
      KJ_FAIL_REQUIRE("gzip decompression failed", ctx.msg);
    }
  }
}

// =======================================================================================

GzipAsyncOutputStream::GzipAsyncOutputStream(AsyncOutputStream& inner, int compressionLevel)
    : inner(inner), ctx(compressionLevel) {}

GzipAsyncOutputStream::GzipAsyncOutputStream(AsyncOutputStream& inner, decltype(DECOMPRESS))
    : inner(inner), ctx(nullptr) {}

Promise<void> GzipAsyncOutputStream::write(const void* in, size_t size) {
  ctx.setInput(in, size);
  return pump(Z_NO_FLUSH);
}

Promise<void> GzipAsyncOutputStream::write(ArrayPtr<const ArrayPtr<const byte>> pieces) {
  if (pieces.size() == 0) return kj::READY_NOW;
  return write(pieces[0].begin(), pieces[0].size())
      .then([this,pieces]() {
    return write(pieces.slice(1, pieces.size()));
  });
}

kj::Promise<void> GzipAsyncOutputStream::pump(int flush) {
  auto result = ctx.pumpOnce(flush);
  auto ok = get<0>(result);
  auto chunk = get<1>(result);

  if (chunk.size() == 0) {
    if (ok) {
      return pump(flush);
    } else {
      return kj::READY_NOW;
    }
  } else {
    auto promise = inner.write(chunk.begin(), chunk.size());
    if (ok) {
      promise = promise.then([this, flush]() { return pump(flush); });
    }
    return promise;
  }
}

}  // namespace kj

#endif  // KJ_HAS_ZLIB
<|endoftext|>