<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <fcntl.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_OUTLINE_H
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <cstdio>
#include <cstdlib>
#include <cstring>

#include "opentype-sanitiser.h"
#include "ots-memory-stream.h"

namespace {

void DumpBitmap(const FT_Bitmap *bitmap) {
  for (int i = 0; i < bitmap->rows * bitmap->width; ++i) {
    if (bitmap->buffer[i] > 192) {
      std::fprintf(stderr, "#");
    } else if (bitmap->buffer[i] > 128) {
      std::fprintf(stderr, "*");
    } else if (bitmap->buffer[i] > 64) {
      std::fprintf(stderr, "+");
    } else if (bitmap->buffer[i] > 32) {
      std::fprintf(stderr, ".");
    } else {
      std::fprintf(stderr, " ");
    }

    if ((i + 1) % bitmap->width == 0) {
      std::fprintf(stderr, "\n");
    }
  }
}

int CompareBitmaps(const FT_Bitmap *orig, const FT_Bitmap *trans) {
  int ret = 0;

  if (orig->width == trans->width &&
      orig->rows == trans->rows) {
    for (int i = 0; i < orig->rows * orig->width; ++i) {
      if (orig->buffer[i] != trans->buffer[i]) {
        std::fprintf(stderr, "bitmap data doesn't match!\n");
        ret = 1;
        break;
      }
    }
  } else {
    std::fprintf(stderr, "bitmap metrics doesn't match! (%d, %d), (%d, %d)\n",
                 orig->width, orig->rows, trans->width, trans->rows);
    ret = 1;
  }

  if (ret) {
    std::fprintf(stderr, "EXPECTED:\n");
    DumpBitmap(orig);
    std::fprintf(stderr, "\nACTUAL:\n");
    DumpBitmap(trans);
    std::fprintf(stderr, "\n\n");
  }

  delete[] orig->buffer;
  delete[] trans->buffer;
  return ret;
}

int GetBitmap(FT_Library library, FT_Outline *outline, FT_Bitmap *bitmap) {
  FT_BBox bbox;
  FT_Outline_Get_CBox(outline, &bbox);

  bbox.xMin &= ~63;
  bbox.yMin &= ~63;
  bbox.xMax = (bbox.xMax + 63) & ~63;
  bbox.yMax = (bbox.yMax + 63) & ~63;
  FT_Outline_Translate(outline, -bbox.xMin, -bbox.yMin);

  const int w = (bbox.xMax - bbox.xMin) >> 6;
  const int h = (bbox.yMax - bbox.yMin) >> 6;

  if (w == 0 || h == 0) {
    return -1;  // white space
  }
  if (w < 0 || h < 0) {
    std::fprintf(stderr, "bad width/height\n");
    return 1;  // error
  }

  uint8_t *buf = new uint8_t[w * h];
  std::memset(buf, 0x0, w * h);

  bitmap->width = w;
  bitmap->rows = h;
  bitmap->pitch = w;
  bitmap->buffer = buf;
  bitmap->pixel_mode = FT_PIXEL_MODE_GRAY;
  bitmap->num_grays = 256;
  if (FT_Outline_Get_Bitmap(library, outline, bitmap)) {
    std::fprintf(stderr, "can't get outline\n");
    delete[] buf;
    return 1;  // error.
  }

  return 0;
}

int LoadChar(FT_Face face, bool use_bitmap, int pt, FT_ULong c) {
  static const int kDpi = 72;

  FT_Matrix matrix;
  matrix.xx = matrix.yy = 1 << 16;
  matrix.xy = matrix.yx = 0 << 16;

  FT_Int32 flags = FT_LOAD_DEFAULT | FT_LOAD_TARGET_NORMAL;
  if (!use_bitmap) {
    // Since the transcoder drops embedded bitmaps from the transcoded one,
    // we have to use FT_LOAD_NO_BITMAP flag for the original face.
    flags |= FT_LOAD_NO_BITMAP;
  }

  FT_Error error = FT_Set_Char_Size(face, pt * (1 << 6), 0, kDpi, 0);
  if (error) {
    std::fprintf(stderr, "Failed to set the char size!\n");
    return 1;
  }

  FT_Set_Transform(face, &matrix, 0);

  error = FT_Load_Char(face, c, flags);
  if (error) return -1;  // no such glyf in the font.

  if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) {
    std::fprintf(stderr, "bad format\n");
    return 1;
  }

  return 0;
}

int LoadCharThenCompare(FT_Library library,
                        FT_Face orig_face, FT_Face trans_face,
                        int pt, FT_ULong c) {
  FT_Bitmap orig_bitmap, trans_bitmap;

  // Load original bitmap.
  int ret = LoadChar(orig_face, false, pt, c);
  if (ret) return ret;  // 1: error, -1: no such glyph

  FT_Outline *outline = &orig_face->glyph->outline;
  ret = GetBitmap(library, outline, &orig_bitmap);
  if (ret) return ret;  // white space?

  // Load transformed bitmap.
  ret = LoadChar(trans_face, true, pt, c);
  if (ret == -1) {
    std::fprintf(stderr, "the glyph is not found on the transcoded font\n");
  }
  if (ret) return 1;  // -1 should be treated as error.
  outline = &trans_face->glyph->outline;
  ret = GetBitmap(library, outline, &trans_bitmap);
  if (ret) return ret;  // white space?

  return CompareBitmaps(&orig_bitmap, &trans_bitmap);
}

int SideBySide(FT_Library library, const char *file_name,
               uint8_t *orig_font, size_t orig_len,
               uint8_t *trans_font, size_t trans_len) {
  FT_Face orig_face;
  FT_Error error
      = FT_New_Memory_Face(library, orig_font, orig_len, 0, &orig_face);
  if (error) {
    std::fprintf(stderr, "Failed to open the original font: %s!\n", file_name);
    return 1;
  }

  FT_Face trans_face;
  error = FT_New_Memory_Face(library, trans_font, trans_len, 0, &trans_face);
  if (error) {
    std::fprintf(stderr, "Failed to open the transcoded font: %s!\n",
                 file_name);
    return 1;
  }

  static const int kPts[] = {100, 20, 18, 16, 12, 10, 8};  // pt
  static const size_t kPtsLen = sizeof(kPts) / sizeof(kPts[0]);

  static const int kUnicodeRanges[] = {
    0x0020, 0x007E,  // Basic Latin (ASCII)
    0x00A1, 0x017F,  // Latin-1
    0x1100, 0x11FF,  // Hangul
    0x3040, 0x309F,  // Japanese HIRAGANA letters
    0x3130, 0x318F,  // Hangul
    0x4E00, 0x4F00,  // CJK Kanji/Hanja
    0xAC00, 0xAD00,  // Hangul
  };
  static const size_t kUnicodeRangesLen
      = sizeof(kUnicodeRanges) / sizeof(kUnicodeRanges[0]);

  for (size_t i = 0; i < kPtsLen; ++i) {
    for (size_t j = 0; j < kUnicodeRangesLen; j += 2) {
      for (int k = 0; k <= kUnicodeRanges[j + 1] - kUnicodeRanges[j]; ++k) {
        int ret = LoadCharThenCompare(library, orig_face, trans_face,
                                      kPts[i],
                                      kUnicodeRanges[j] + k);
        if (ret > 0) {
          std::fprintf(stderr, "Glyph mismatch! (file: %s, U+%04x, %dpt)!\n",
                       file_name, kUnicodeRanges[j] + k, kPts[i]);
          return 1;
        }
      }
    }
  }

  return 0;
}

}  // namespace

int main(int argc, char **argv) {
  if (argc != 2) {
    std::fprintf(stderr, "Usage: %s ttf_or_otf_filename\n", argv[0]);
    return 1;
  }

  // load the font to memory.
  const int fd = ::open(argv[1], O_RDONLY);
  if (fd < 0) {
    ::perror("open");
    return 1;
  }

  struct stat st;
  ::fstat(fd, &st);
  const off_t orig_len = st.st_size;

  uint8_t *orig_font = new uint8_t[orig_len];
  if (::read(fd, orig_font, orig_len) != orig_len) {
    std::fprintf(stderr, "Failed to read file!\n");
    return 1;
  }
  ::close(fd);

  // check if FreeType2 can open the original font.
  FT_Library library;
  FT_Error error = FT_Init_FreeType(&library);
  if (error) {
    std::fprintf(stderr, "Failed to initialize FreeType2!\n");
    return 1;
  }
  FT_Face dummy;
  error = FT_New_Memory_Face(library, orig_font, orig_len, 0, &dummy);
  if (error) {
    std::fprintf(stderr, "Failed to open the original font with FT2! %s\n",
                 argv[1]);
    return 1;
  }

  // transcode the original font.
  static const size_t kPadLen = 20 * 1024;
  uint8_t *trans_font = new uint8_t[orig_len + kPadLen];
  ots::MemoryStream output(trans_font, orig_len + kPadLen);
  ots::OTSContext context;

  bool result = context.Process(&output, orig_font, orig_len);
  if (!result) {
    std::fprintf(stderr, "Failed to sanitise file! %s\n", argv[1]);
    return 1;
  }
  const size_t trans_len = output.Tell();

  // perform side-by-side tests.
  return SideBySide(library, argv[1],
                    orig_font, orig_len,
                    trans_font, trans_len);
}
<commit_msg>[tests] -Werror -Wsign-compare<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <fcntl.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_OUTLINE_H
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <cstdio>
#include <cstdlib>
#include <cstring>

#include "opentype-sanitiser.h"
#include "ots-memory-stream.h"

namespace {

void DumpBitmap(const FT_Bitmap *bitmap) {
  for (unsigned int i = 0; i < bitmap->rows * bitmap->width; ++i) {
    if (bitmap->buffer[i] > 192) {
      std::fprintf(stderr, "#");
    } else if (bitmap->buffer[i] > 128) {
      std::fprintf(stderr, "*");
    } else if (bitmap->buffer[i] > 64) {
      std::fprintf(stderr, "+");
    } else if (bitmap->buffer[i] > 32) {
      std::fprintf(stderr, ".");
    } else {
      std::fprintf(stderr, " ");
    }

    if ((i + 1) % bitmap->width == 0) {
      std::fprintf(stderr, "\n");
    }
  }
}

int CompareBitmaps(const FT_Bitmap *orig, const FT_Bitmap *trans) {
  int ret = 0;

  if (orig->width == trans->width &&
      orig->rows == trans->rows) {
    for (unsigned int i = 0; i < orig->rows * orig->width; ++i) {
      if (orig->buffer[i] != trans->buffer[i]) {
        std::fprintf(stderr, "bitmap data doesn't match!\n");
        ret = 1;
        break;
      }
    }
  } else {
    std::fprintf(stderr, "bitmap metrics doesn't match! (%d, %d), (%d, %d)\n",
                 orig->width, orig->rows, trans->width, trans->rows);
    ret = 1;
  }

  if (ret) {
    std::fprintf(stderr, "EXPECTED:\n");
    DumpBitmap(orig);
    std::fprintf(stderr, "\nACTUAL:\n");
    DumpBitmap(trans);
    std::fprintf(stderr, "\n\n");
  }

  delete[] orig->buffer;
  delete[] trans->buffer;
  return ret;
}

int GetBitmap(FT_Library library, FT_Outline *outline, FT_Bitmap *bitmap) {
  FT_BBox bbox;
  FT_Outline_Get_CBox(outline, &bbox);

  bbox.xMin &= ~63;
  bbox.yMin &= ~63;
  bbox.xMax = (bbox.xMax + 63) & ~63;
  bbox.yMax = (bbox.yMax + 63) & ~63;
  FT_Outline_Translate(outline, -bbox.xMin, -bbox.yMin);

  const int w = (bbox.xMax - bbox.xMin) >> 6;
  const int h = (bbox.yMax - bbox.yMin) >> 6;

  if (w == 0 || h == 0) {
    return -1;  // white space
  }
  if (w < 0 || h < 0) {
    std::fprintf(stderr, "bad width/height\n");
    return 1;  // error
  }

  uint8_t *buf = new uint8_t[w * h];
  std::memset(buf, 0x0, w * h);

  bitmap->width = w;
  bitmap->rows = h;
  bitmap->pitch = w;
  bitmap->buffer = buf;
  bitmap->pixel_mode = FT_PIXEL_MODE_GRAY;
  bitmap->num_grays = 256;
  if (FT_Outline_Get_Bitmap(library, outline, bitmap)) {
    std::fprintf(stderr, "can't get outline\n");
    delete[] buf;
    return 1;  // error.
  }

  return 0;
}

int LoadChar(FT_Face face, bool use_bitmap, int pt, FT_ULong c) {
  static const int kDpi = 72;

  FT_Matrix matrix;
  matrix.xx = matrix.yy = 1 << 16;
  matrix.xy = matrix.yx = 0 << 16;

  FT_Int32 flags = FT_LOAD_DEFAULT | FT_LOAD_TARGET_NORMAL;
  if (!use_bitmap) {
    // Since the transcoder drops embedded bitmaps from the transcoded one,
    // we have to use FT_LOAD_NO_BITMAP flag for the original face.
    flags |= FT_LOAD_NO_BITMAP;
  }

  FT_Error error = FT_Set_Char_Size(face, pt * (1 << 6), 0, kDpi, 0);
  if (error) {
    std::fprintf(stderr, "Failed to set the char size!\n");
    return 1;
  }

  FT_Set_Transform(face, &matrix, 0);

  error = FT_Load_Char(face, c, flags);
  if (error) return -1;  // no such glyf in the font.

  if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) {
    std::fprintf(stderr, "bad format\n");
    return 1;
  }

  return 0;
}

int LoadCharThenCompare(FT_Library library,
                        FT_Face orig_face, FT_Face trans_face,
                        int pt, FT_ULong c) {
  FT_Bitmap orig_bitmap, trans_bitmap;

  // Load original bitmap.
  int ret = LoadChar(orig_face, false, pt, c);
  if (ret) return ret;  // 1: error, -1: no such glyph

  FT_Outline *outline = &orig_face->glyph->outline;
  ret = GetBitmap(library, outline, &orig_bitmap);
  if (ret) return ret;  // white space?

  // Load transformed bitmap.
  ret = LoadChar(trans_face, true, pt, c);
  if (ret == -1) {
    std::fprintf(stderr, "the glyph is not found on the transcoded font\n");
  }
  if (ret) return 1;  // -1 should be treated as error.
  outline = &trans_face->glyph->outline;
  ret = GetBitmap(library, outline, &trans_bitmap);
  if (ret) return ret;  // white space?

  return CompareBitmaps(&orig_bitmap, &trans_bitmap);
}

int SideBySide(FT_Library library, const char *file_name,
               uint8_t *orig_font, size_t orig_len,
               uint8_t *trans_font, size_t trans_len) {
  FT_Face orig_face;
  FT_Error error
      = FT_New_Memory_Face(library, orig_font, orig_len, 0, &orig_face);
  if (error) {
    std::fprintf(stderr, "Failed to open the original font: %s!\n", file_name);
    return 1;
  }

  FT_Face trans_face;
  error = FT_New_Memory_Face(library, trans_font, trans_len, 0, &trans_face);
  if (error) {
    std::fprintf(stderr, "Failed to open the transcoded font: %s!\n",
                 file_name);
    return 1;
  }

  static const int kPts[] = {100, 20, 18, 16, 12, 10, 8};  // pt
  static const size_t kPtsLen = sizeof(kPts) / sizeof(kPts[0]);

  static const int kUnicodeRanges[] = {
    0x0020, 0x007E,  // Basic Latin (ASCII)
    0x00A1, 0x017F,  // Latin-1
    0x1100, 0x11FF,  // Hangul
    0x3040, 0x309F,  // Japanese HIRAGANA letters
    0x3130, 0x318F,  // Hangul
    0x4E00, 0x4F00,  // CJK Kanji/Hanja
    0xAC00, 0xAD00,  // Hangul
  };
  static const size_t kUnicodeRangesLen
      = sizeof(kUnicodeRanges) / sizeof(kUnicodeRanges[0]);

  for (size_t i = 0; i < kPtsLen; ++i) {
    for (size_t j = 0; j < kUnicodeRangesLen; j += 2) {
      for (int k = 0; k <= kUnicodeRanges[j + 1] - kUnicodeRanges[j]; ++k) {
        int ret = LoadCharThenCompare(library, orig_face, trans_face,
                                      kPts[i],
                                      kUnicodeRanges[j] + k);
        if (ret > 0) {
          std::fprintf(stderr, "Glyph mismatch! (file: %s, U+%04x, %dpt)!\n",
                       file_name, kUnicodeRanges[j] + k, kPts[i]);
          return 1;
        }
      }
    }
  }

  return 0;
}

}  // namespace

int main(int argc, char **argv) {
  if (argc != 2) {
    std::fprintf(stderr, "Usage: %s ttf_or_otf_filename\n", argv[0]);
    return 1;
  }

  // load the font to memory.
  const int fd = ::open(argv[1], O_RDONLY);
  if (fd < 0) {
    ::perror("open");
    return 1;
  }

  struct stat st;
  ::fstat(fd, &st);
  const off_t orig_len = st.st_size;

  uint8_t *orig_font = new uint8_t[orig_len];
  if (::read(fd, orig_font, orig_len) != orig_len) {
    std::fprintf(stderr, "Failed to read file!\n");
    return 1;
  }
  ::close(fd);

  // check if FreeType2 can open the original font.
  FT_Library library;
  FT_Error error = FT_Init_FreeType(&library);
  if (error) {
    std::fprintf(stderr, "Failed to initialize FreeType2!\n");
    return 1;
  }
  FT_Face dummy;
  error = FT_New_Memory_Face(library, orig_font, orig_len, 0, &dummy);
  if (error) {
    std::fprintf(stderr, "Failed to open the original font with FT2! %s\n",
                 argv[1]);
    return 1;
  }

  // transcode the original font.
  static const size_t kPadLen = 20 * 1024;
  uint8_t *trans_font = new uint8_t[orig_len + kPadLen];
  ots::MemoryStream output(trans_font, orig_len + kPadLen);
  ots::OTSContext context;

  bool result = context.Process(&output, orig_font, orig_len);
  if (!result) {
    std::fprintf(stderr, "Failed to sanitise file! %s\n", argv[1]);
    return 1;
  }
  const size_t trans_len = output.Tell();

  // perform side-by-side tests.
  return SideBySide(library, argv[1],
                    orig_font, orig_len,
                    trans_font, trans_len);
}
<|endoftext|>


<commit_before>#pragma once

#include <thread>

#include "lib/chrono.hpp"
#include "lib/core/service.hpp"

#include "app/drivers/audio_driver.hpp"
#include "app/services/runtime.hpp"

namespace otto::stubs {
  struct NoProcessAudioDriver final : drivers::IAudioDriver {
    void set_callback(Callback&& cb) override
    {
      callback = std::move(cb);
    }
    void start() override {}
    void stop() override {}
    [[nodiscard]] std::size_t buffer_size() const override
    {
      return 64;
    }
    [[nodiscard]] std::size_t sample_rate() const override
    {
      return 44100;
    }

    Callback callback;
  };

  struct DummyAudioDriver final : drivers::IAudioDriver {
    DummyAudioDriver()
    {
      buffers_.resize(buffer_size() * 4);
    }
    void set_callback(Callback&& cb) override
    {
      callback_ = std::move(cb);
    }
    void start() override
    {
      thread_ = std::jthread([this](std::stop_token stopper) {
        auto input_buf = util::stereo_audio_buffer(
          util::audio_buffer(std::span(buffers_.data() + 0 * buffer_size(), buffer_size()), nullptr),
          util::audio_buffer(std::span(buffers_.data() + 1 * buffer_size(), buffer_size()), nullptr));
        auto output_buf = util::stereo_audio_buffer(
          util::audio_buffer(std::span(buffers_.data() + 2 * buffer_size(), buffer_size()), nullptr),
          util::audio_buffer(std::span(buffers_.data() + 3 * buffer_size(), buffer_size()), nullptr));
        CallbackData cbd = {
          .input = input_buf,
          .output = output_buf,
        };
        while (runtime->should_run() && !stopper.stop_requested()) {
          callback_(cbd);
          std::this_thread::sleep_for(chrono::nanoseconds((1ns / 1s) * buffer_size() / sample_rate()));
        }
      });
    }
    void stop() override
    {
      thread_.request_stop();
      thread_.join();
    }
    [[nodiscard]] std::size_t buffer_size() const override
    {
      return 64;
    }
    [[nodiscard]] std::size_t sample_rate() const override
    {
      return 44100;
    }

  private:
    std::vector<float> buffers_;
    [[no_unique_address]] core::ServiceAccessor<services::Runtime> runtime;
    Callback callback_;
    std::jthread thread_;
  };

} // namespace otto::stubs
<commit_msg>Fix DummyAudioDriver::stop<commit_after>#pragma once

#include <thread>

#include "lib/chrono.hpp"
#include "lib/core/service.hpp"

#include "app/drivers/audio_driver.hpp"
#include "app/services/runtime.hpp"

namespace otto::stubs {
  struct NoProcessAudioDriver final : drivers::IAudioDriver {
    void set_callback(Callback&& cb) override
    {
      callback = std::move(cb);
    }
    void start() override {}
    void stop() override {}
    [[nodiscard]] std::size_t buffer_size() const override
    {
      return 64;
    }
    [[nodiscard]] std::size_t sample_rate() const override
    {
      return 44100;
    }

    Callback callback;
  };

  struct DummyAudioDriver final : drivers::IAudioDriver {
    DummyAudioDriver()
    {
      buffers_.resize(buffer_size() * 4);
    }
    void set_callback(Callback&& cb) override
    {
      callback_ = std::move(cb);
    }
    void start() override
    {
      thread_ = std::jthread([this](std::stop_token stopper) {
        auto input_buf = util::stereo_audio_buffer(
          util::audio_buffer(std::span(buffers_.data() + 0 * buffer_size(), buffer_size()), nullptr),
          util::audio_buffer(std::span(buffers_.data() + 1 * buffer_size(), buffer_size()), nullptr));
        auto output_buf = util::stereo_audio_buffer(
          util::audio_buffer(std::span(buffers_.data() + 2 * buffer_size(), buffer_size()), nullptr),
          util::audio_buffer(std::span(buffers_.data() + 3 * buffer_size(), buffer_size()), nullptr));
        CallbackData cbd = {
          .input = input_buf,
          .output = output_buf,
        };
        while (runtime->should_run() && !stopper.stop_requested()) {
          callback_(cbd);
          std::this_thread::sleep_for(chrono::nanoseconds((1ns / 1s) * buffer_size() / sample_rate()));
        }
      });
    }
    void stop() override
    {
      thread_.request_stop();
      if (thread_.joinable()) thread_.join();
    }
    [[nodiscard]] std::size_t buffer_size() const override
    {
      return 64;
    }
    [[nodiscard]] std::size_t sample_rate() const override
    {
      return 44100;
    }

  private:
    std::vector<float> buffers_;
    [[no_unique_address]] core::ServiceAccessor<services::Runtime> runtime;
    Callback callback_;
    std::jthread thread_;
  };

} // namespace otto::stubs
<|endoftext|>


<commit_before>/*
	Copyright (c) 2003 - 2005, Arvid Norberg, Daniel Wallin
	All rights reserved.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions
	are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of Rasterbar Software nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <cassert>
#include <boost/timer.hpp>
#include <iostream>
#include <vector>
#include <utility>
#include <set>

#include "libtorrent/buffer.hpp"
#include "libtorrent/chained_buffer.hpp"

#include "test.hpp"

using libtorrent::buffer;
using libtorrent::chained_buffer;

/*
template<class T>
T const& min_(T const& x, T const& y)
{
	return x < y ? x : y;
}

void test_speed()
{
	buffer b;

	char data[32];

	srand(0);

	boost::timer t;

	int const iterations = 5000000;
	int const step = iterations / 20;

	for (int i = 0; i < iterations; ++i)
	{
		int x = rand();

		if (i % step == 0) std::cerr << ".";

		std::size_t n = rand() % 32;
		n = 32;

		if (x % 2)
		{
			b.insert(data, data + n);
		}
		else
		{
			b.erase(min_(b.size(), n));
		}
	}

	float t1 = t.elapsed();
	std::cerr << "buffer elapsed: " << t.elapsed() << "\n";

	std::vector<char> v;

	srand(0);
	t.restart();

	for (int i = 0; i < iterations; ++i)
	{
		int x = rand();

		if (i % step == 0) std::cerr << ".";

		std::size_t n = rand() % 32;
		n = 32;

		if (x % 2)
		{
			v.insert(v.end(), data, data + n);
		}
		else
		{
			v.erase(v.begin(), v.begin() + min_(v.size(), n));
		}
	}

	float t2 = t.elapsed();
	std::cerr << "std::vector elapsed: " << t.elapsed() << "\n";

	assert(t1 < t2);
}
*/

// -- test buffer --

void test_buffer()
{
	char data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

	buffer b;

	TEST_CHECK(b.size() == 0);
	TEST_CHECK(b.capacity() == 0);
	TEST_CHECK(b.empty());

	b.resize(10);
	TEST_CHECK(b.size() == 10);
	TEST_CHECK(b.capacity() == 10);

	std::memcpy(b.begin(), data, 10);
	b.reserve(50);
	TEST_CHECK(std::memcmp(b.begin(), data, 10) == 0);
	TEST_CHECK(b.capacity() == 50);

	b.erase(b.begin() + 6, b.end());
	TEST_CHECK(std::memcmp(b.begin(), data, 6) == 0);
	TEST_CHECK(b.capacity() == 50);
	TEST_CHECK(b.size() == 6);

	b.insert(b.begin(), data + 5, data + 10);
	TEST_CHECK(b.capacity() == 50);
	TEST_CHECK(b.size() == 11);
	TEST_CHECK(std::memcmp(b.begin(), data + 5, 5) == 0);

	b.clear();
	TEST_CHECK(b.size() == 0);
	TEST_CHECK(b.capacity() == 50);

	b.insert(b.end(), data, data + 10);
	TEST_CHECK(b.size() == 10);
	TEST_CHECK(std::memcmp(b.begin(), data, 10) == 0);

	b.erase(b.begin(), b.end());
	TEST_CHECK(b.capacity() == 50);
	TEST_CHECK(b.size() == 0);

	buffer().swap(b);
	TEST_CHECK(b.capacity() == 0);

}

// -- test chained buffer --

std::set<char*> buffer_list;

void free_buffer(char* m)
{
	std::set<char*>::iterator i = buffer_list.find(m);
	TEST_CHECK(i != buffer_list.end());

	buffer_list.erase(i);
	std::free(m);
}

char* allocate_buffer(int size)
{
	char* mem = (char*)std::malloc(size);
	buffer_list.insert(mem);
	return mem;
}

template <class T>
int copy_buffers(T const& b, char* target)
{
	int copied = 0;
	for (typename T::const_iterator i = b.begin()
		, end(b.end()); i != end; ++i)
	{
		memcpy(target, asio::buffer_cast<char const*>(*i), asio::buffer_size(*i));
		target += asio::buffer_size(*i);
		copied += asio::buffer_size(*i);
	}
	return copied;
}

bool compare_chained_buffer(chained_buffer& b, char const* mem, int size)
{
	std::vector<char> flat(size);
	std::list<asio::const_buffer> const& iovec2 = b.build_iovec(size);
	int copied = copy_buffers(iovec2, &flat[0]);
	TEST_CHECK(copied == size);
	return std::memcmp(&flat[0], mem, size) == 0;
}

void test_chained_buffer()
{
	char data[] = "foobar";
	{
		chained_buffer b;

		TEST_CHECK(b.empty());
		TEST_CHECK(b.capacity() == 0);
		TEST_CHECK(b.size() == 0);
		TEST_CHECK(b.space_in_last_buffer() == 0);
		TEST_CHECK(buffer_list.empty());

		char* b1 = allocate_buffer(512);
		std::memcpy(b1, data, 6);
		b.append_buffer(b1, 512, 6, (void(*)(char*))&free_buffer);
		TEST_CHECK(buffer_list.size() == 1);

		TEST_CHECK(b.capacity() == 512);
		TEST_CHECK(b.size() == 6);
		TEST_CHECK(!b.empty());
		TEST_CHECK(b.space_in_last_buffer() == 512 - 6);

		b.pop_front(3);

		TEST_CHECK(b.capacity() == 512);
		TEST_CHECK(b.size() == 3);
		TEST_CHECK(!b.empty());
		TEST_CHECK(b.space_in_last_buffer() == 512 - 6);

		bool ret = b.append(data, 6);

		TEST_CHECK(ret == true);
		TEST_CHECK(b.capacity() == 512);
		TEST_CHECK(b.size() == 9);
		TEST_CHECK(!b.empty());
		TEST_CHECK(b.space_in_last_buffer() == 512 - 12);

		ret = b.append(data, 1024);

		TEST_CHECK(ret == false);

		char* b2 = allocate_buffer(512);
		std::memcpy(b2, data, 6);
		b.append_buffer(b2, 512, 6, (void(*)(char*))&free_buffer);
		TEST_CHECK(buffer_list.size() == 2);

		char* b3 = allocate_buffer(512);
		std::memcpy(b3, data, 6);
		b.append_buffer(b3, 512, 6, (void(*)(char*))&free_buffer);
		TEST_CHECK(buffer_list.size() == 3);

		TEST_CHECK(b.capacity() == 512 * 3);
		TEST_CHECK(b.size() == 21);
		TEST_CHECK(!b.empty());
		TEST_CHECK(b.space_in_last_buffer() == 512 - 6);

		TEST_CHECK(compare_chained_buffer(b, "barfoobar", 9));

		for (int i = 1; i < 21; ++i)
			TEST_CHECK(compare_chained_buffer(b, "barfoobarfoobarfoobar", i));

		b.pop_front(5 + 6);

		TEST_CHECK(buffer_list.size() == 2);
		TEST_CHECK(b.capacity() == 512 * 2);
		TEST_CHECK(b.size() == 10);
		TEST_CHECK(!b.empty());
		TEST_CHECK(b.space_in_last_buffer() == 512 - 6);

		char const* str = "obarfooba";
		TEST_CHECK(compare_chained_buffer(b, str, 9));

		for (int i = 0; i < 9; ++i)
		{
			b.pop_front(1);
			++str;
			TEST_CHECK(compare_chained_buffer(b, str, 8 - i));
			TEST_CHECK(b.size() == 9 - i);
		}

		char* b4 = allocate_buffer(20);
		std::memcpy(b4, data, 6);
		std::memcpy(b4 + 6, data, 6);
		b.append_buffer(b4, 20, 12, (void(*)(char*))&free_buffer);
		TEST_CHECK(b.space_in_last_buffer() == 8);

		ret = b.append(data, 6);
		TEST_CHECK(ret == true);
		TEST_CHECK(b.space_in_last_buffer() == 2);
		std::cout << b.space_in_last_buffer() << std::endl;
		ret = b.append(data, 2);
		TEST_CHECK(ret == true);
		TEST_CHECK(b.space_in_last_buffer() == 0);
		std::cout << b.space_in_last_buffer() << std::endl;

		char* b5 = allocate_buffer(20);
		std::memcpy(b4, data, 6);
		b.append_buffer(b5, 20, 6, (void(*)(char*))&free_buffer);

		b.pop_front(22);
		TEST_CHECK(b.size() == 5);
	}
	TEST_CHECK(buffer_list.empty());
}

int test_main()
{
	test_buffer();
	test_chained_buffer();
	return 0;
}

<commit_msg>test_buffer fix<commit_after>/*
	Copyright (c) 2003 - 2005, Arvid Norberg, Daniel Wallin
	All rights reserved.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions
	are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of Rasterbar Software nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <cassert>
#include <boost/timer.hpp>
#include <iostream>
#include <vector>
#include <utility>
#include <set>

#include "libtorrent/buffer.hpp"
#include "libtorrent/chained_buffer.hpp"

#include "test.hpp"

using libtorrent::buffer;
using libtorrent::chained_buffer;

/*
template<class T>
T const& min_(T const& x, T const& y)
{
	return x < y ? x : y;
}

void test_speed()
{
	buffer b;

	char data[32];

	srand(0);

	boost::timer t;

	int const iterations = 5000000;
	int const step = iterations / 20;

	for (int i = 0; i < iterations; ++i)
	{
		int x = rand();

		if (i % step == 0) std::cerr << ".";

		std::size_t n = rand() % 32;
		n = 32;

		if (x % 2)
		{
			b.insert(data, data + n);
		}
		else
		{
			b.erase(min_(b.size(), n));
		}
	}

	float t1 = t.elapsed();
	std::cerr << "buffer elapsed: " << t.elapsed() << "\n";

	std::vector<char> v;

	srand(0);
	t.restart();

	for (int i = 0; i < iterations; ++i)
	{
		int x = rand();

		if (i % step == 0) std::cerr << ".";

		std::size_t n = rand() % 32;
		n = 32;

		if (x % 2)
		{
			v.insert(v.end(), data, data + n);
		}
		else
		{
			v.erase(v.begin(), v.begin() + min_(v.size(), n));
		}
	}

	float t2 = t.elapsed();
	std::cerr << "std::vector elapsed: " << t.elapsed() << "\n";

	assert(t1 < t2);
}
*/

// -- test buffer --

void test_buffer()
{
	char data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

	buffer b;

	TEST_CHECK(b.size() == 0);
	TEST_CHECK(b.capacity() == 0);
	TEST_CHECK(b.empty());

	b.resize(10);
	TEST_CHECK(b.size() == 10);
	TEST_CHECK(b.capacity() == 10);

	std::memcpy(b.begin(), data, 10);
	b.reserve(50);
	TEST_CHECK(std::memcmp(b.begin(), data, 10) == 0);
	TEST_CHECK(b.capacity() == 50);

	b.erase(b.begin() + 6, b.end());
	TEST_CHECK(std::memcmp(b.begin(), data, 6) == 0);
	TEST_CHECK(b.capacity() == 50);
	TEST_CHECK(b.size() == 6);

	b.insert(b.begin(), data + 5, data + 10);
	TEST_CHECK(b.capacity() == 50);
	TEST_CHECK(b.size() == 11);
	TEST_CHECK(std::memcmp(b.begin(), data + 5, 5) == 0);

	b.clear();
	TEST_CHECK(b.size() == 0);
	TEST_CHECK(b.capacity() == 50);

	b.insert(b.end(), data, data + 10);
	TEST_CHECK(b.size() == 10);
	TEST_CHECK(std::memcmp(b.begin(), data, 10) == 0);

	b.erase(b.begin(), b.end());
	TEST_CHECK(b.capacity() == 50);
	TEST_CHECK(b.size() == 0);

	buffer().swap(b);
	TEST_CHECK(b.capacity() == 0);

}

// -- test chained buffer --

std::set<char*> buffer_list;

void free_buffer(char* m)
{
	std::set<char*>::iterator i = buffer_list.find(m);
	TEST_CHECK(i != buffer_list.end());

	buffer_list.erase(i);
	std::free(m);
}

char* allocate_buffer(int size)
{
	char* mem = (char*)std::malloc(size);
	buffer_list.insert(mem);
	return mem;
}

template <class T>
int copy_buffers(T const& b, char* target)
{
	int copied = 0;
	for (typename T::const_iterator i = b.begin()
		, end(b.end()); i != end; ++i)
	{
		memcpy(target, asio::buffer_cast<char const*>(*i), asio::buffer_size(*i));
		target += asio::buffer_size(*i);
		copied += asio::buffer_size(*i);
	}
	return copied;
}

bool compare_chained_buffer(chained_buffer& b, char const* mem, int size)
{
	if (size == 0) return true;
	std::vector<char> flat(size);
	std::list<asio::const_buffer> const& iovec2 = b.build_iovec(size);
	int copied = copy_buffers(iovec2, &flat[0]);
	TEST_CHECK(copied == size);
	return std::memcmp(&flat[0], mem, size) == 0;
}

void test_chained_buffer()
{
	char data[] = "foobar";
	{
		chained_buffer b;

		TEST_CHECK(b.empty());
		TEST_CHECK(b.capacity() == 0);
		TEST_CHECK(b.size() == 0);
		TEST_CHECK(b.space_in_last_buffer() == 0);
		TEST_CHECK(buffer_list.empty());

		char* b1 = allocate_buffer(512);
		std::memcpy(b1, data, 6);
		b.append_buffer(b1, 512, 6, (void(*)(char*))&free_buffer);
		TEST_CHECK(buffer_list.size() == 1);

		TEST_CHECK(b.capacity() == 512);
		TEST_CHECK(b.size() == 6);
		TEST_CHECK(!b.empty());
		TEST_CHECK(b.space_in_last_buffer() == 512 - 6);

		b.pop_front(3);

		TEST_CHECK(b.capacity() == 512);
		TEST_CHECK(b.size() == 3);
		TEST_CHECK(!b.empty());
		TEST_CHECK(b.space_in_last_buffer() == 512 - 6);

		bool ret = b.append(data, 6);

		TEST_CHECK(ret == true);
		TEST_CHECK(b.capacity() == 512);
		TEST_CHECK(b.size() == 9);
		TEST_CHECK(!b.empty());
		TEST_CHECK(b.space_in_last_buffer() == 512 - 12);

		ret = b.append(data, 1024);

		TEST_CHECK(ret == false);

		char* b2 = allocate_buffer(512);
		std::memcpy(b2, data, 6);
		b.append_buffer(b2, 512, 6, (void(*)(char*))&free_buffer);
		TEST_CHECK(buffer_list.size() == 2);

		char* b3 = allocate_buffer(512);
		std::memcpy(b3, data, 6);
		b.append_buffer(b3, 512, 6, (void(*)(char*))&free_buffer);
		TEST_CHECK(buffer_list.size() == 3);

		TEST_CHECK(b.capacity() == 512 * 3);
		TEST_CHECK(b.size() == 21);
		TEST_CHECK(!b.empty());
		TEST_CHECK(b.space_in_last_buffer() == 512 - 6);

		TEST_CHECK(compare_chained_buffer(b, "barfoobar", 9));

		for (int i = 1; i < 21; ++i)
			TEST_CHECK(compare_chained_buffer(b, "barfoobarfoobarfoobar", i));

		b.pop_front(5 + 6);

		TEST_CHECK(buffer_list.size() == 2);
		TEST_CHECK(b.capacity() == 512 * 2);
		TEST_CHECK(b.size() == 10);
		TEST_CHECK(!b.empty());
		TEST_CHECK(b.space_in_last_buffer() == 512 - 6);

		char const* str = "obarfooba";
		TEST_CHECK(compare_chained_buffer(b, str, 9));

		for (int i = 0; i < 9; ++i)
		{
			b.pop_front(1);
			++str;
			TEST_CHECK(compare_chained_buffer(b, str, 8 - i));
			TEST_CHECK(b.size() == 9 - i);
		}

		char* b4 = allocate_buffer(20);
		std::memcpy(b4, data, 6);
		std::memcpy(b4 + 6, data, 6);
		b.append_buffer(b4, 20, 12, (void(*)(char*))&free_buffer);
		TEST_CHECK(b.space_in_last_buffer() == 8);

		ret = b.append(data, 6);
		TEST_CHECK(ret == true);
		TEST_CHECK(b.space_in_last_buffer() == 2);
		std::cout << b.space_in_last_buffer() << std::endl;
		ret = b.append(data, 2);
		TEST_CHECK(ret == true);
		TEST_CHECK(b.space_in_last_buffer() == 0);
		std::cout << b.space_in_last_buffer() << std::endl;

		char* b5 = allocate_buffer(20);
		std::memcpy(b4, data, 6);
		b.append_buffer(b5, 20, 6, (void(*)(char*))&free_buffer);

		b.pop_front(22);
		TEST_CHECK(b.size() == 5);
	}
	TEST_CHECK(buffer_list.empty());
}

int test_main()
{
	test_buffer();
	test_chained_buffer();
	return 0;
}

<|endoftext|>


<commit_before>#include "gl/context.h"
#include "gl/environment.h"

#include <gtest/gtest.h>

using namespace gxy;

struct Fixture : public testing::Test
{
  int width{42};
  int height{89};
  std::string title{"badger"};

  gl::environment env{};
  gl::context ctx{env, width, height, title.c_str()};
};

TEST_F(Fixture, OpenGLMajorVersion)
{
  int major{};
  ::glGetIntegerv(GL_MAJOR_VERSION, &major);

  ASSERT_EQ(4, major);
}

TEST_F(Fixture, OpenGLMinorVersion)
{
  int minor{};
  ::glGetIntegerv(GL_MINOR_VERSION, &minor);

  ASSERT_EQ(1, minor);
}

<commit_msg>Removed namespace scope from gl methods (stupid macros).<commit_after>#include "gl/context.h"
#include "gl/environment.h"

#include <gtest/gtest.h>

using namespace gxy;

struct Fixture : public testing::Test
{
  int width{42};
  int height{89};
  std::string title{"badger"};

  gl::environment env{};
  gl::context ctx{env, width, height, title.c_str()};
};

TEST_F(Fixture, OpenGLMajorVersion)
{
  int major{};
  glGetIntegerv(GL_MAJOR_VERSION, &major);

  ASSERT_EQ(4, major);
}

TEST_F(Fixture, OpenGLMinorVersion)
{
  int minor{};
  glGetIntegerv(GL_MINOR_VERSION, &minor);

  ASSERT_EQ(1, minor);
}

<|endoftext|>


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

Program:   Medical Imaging & Interaction Toolkit
Module:    $RCSfile$
Language:  C++
Date:      $Date$
Version:   $Revision$

Copyright (c) German Cancer Research Center, Division of Medical and
Biological Informatics. All rights reserved.
See MITKCopyright.txt or http://www.mitk.org/copyright.html 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.

=========================================================================*/

#include "mitkShowSegmentationAsSurface.h"
#include "mitkManualSegmentationToSurfaceFilter.h"
#include "mitkDataTreeNodeFactory.h"
#include "mitkMaterialProperty.h"

#include <vtkPolyDataNormals.h>

namespace mitk
{

ShowSegmentationAsSurface::ShowSegmentationAsSurface()
:m_UIDGeneratorSurfaces("Surface_"),
 m_AddToTree(false)
{
}

ShowSegmentationAsSurface::~ShowSegmentationAsSurface()
{
}

void ShowSegmentationAsSurface::Initialize(const NonBlockingAlgorithm* other)
{
  Superclass::Initialize(other);

  bool syncVisibility(false);

  if (other)
  {
    other->GetParameter("Sync visibility", syncVisibility);
  }

  SetParameter("Sync visibility", syncVisibility );
  SetParameter("Smooth", true );
  SetParameter("Wireframe", false );
}

bool ShowSegmentationAsSurface::ReadyToRun()
{
  try
  {
    Image::Pointer image;
    GetPointerParameter("Input", image);

    return image.IsNotNull() && GetGroupNode();
  }
  catch (std::invalid_argument&)
  {
    return false;
  }
}

bool ShowSegmentationAsSurface::ThreadedUpdateFunction()
{
  Image::Pointer image;
  GetPointerParameter("Input", image);

  bool smooth(true);
  GetParameter("Smooth", smooth);

  ManualSegmentationToSurfaceFilter::Pointer surfaceFilter = ManualSegmentationToSurfaceFilter::New();
  surfaceFilter->SetInput( image );
  surfaceFilter->SetThreshold( 1 ); //expects binary image with zeros and ones

  if (smooth)
  {
    surfaceFilter->SetMedianKernelSize(3, 3, 3); // apply median to segmentation before marching cubes
    surfaceFilter->SetGaussianStandardDeviation( 1.5 );
    surfaceFilter->SetDecimate( ImageToSurfaceFilter::DecimatePro );
    surfaceFilter->SetTargetReduction( 0.8 );
  }

  surfaceFilter->SetUseGaussianImageSmooth(smooth); // apply gaussian to thresholded image ?
  surfaceFilter->SetMedianFilter3D(smooth); // apply median to segmentation before marching cubes ?
  surfaceFilter->SetSmooth(smooth); // smooth wireframe ?
  surfaceFilter->UpdateLargestPossibleRegion();

  // calculate normals for nicer display
  m_Surface = surfaceFilter->GetOutput();

  vtkPolyData* polyData = m_Surface->GetVtkPolyData();
  polyData->SetVerts(0);
  polyData->SetLines(0);

  if (smooth)
  {
    vtkPolyDataNormals* normalsGen = vtkPolyDataNormals::New();

    normalsGen->SetInput( polyData );
    normalsGen->Update();

    m_Surface->SetVtkPolyData( normalsGen->GetOutput() );

    normalsGen->Delete();
  }
  else
  {
    m_Surface->SetVtkPolyData( polyData );
  }

  return true;
}

void ShowSegmentationAsSurface::ThreadedUpdateSuccessful()
{
  m_Node = LookForPointerTargetBelowGroupNode("Surface representation");

  m_AddToTree = m_Node.IsNull();

  if (m_AddToTree)
  {
    m_Node = DataTreeNode::New();

    DataTreeNodeFactory::SetDefaultSurfaceProperties(m_Node);

    bool wireframe(false);
    GetParameter("Wireframe", wireframe );
    if (wireframe)
    {
      MaterialProperty* mp = dynamic_cast<MaterialProperty*>( m_Node->GetProperty("material").GetPointer() );
      if (mp)
      {
        mp->SetRepresentation( MaterialProperty::Wireframe );
      }
    }

    m_Node->SetProperty("opacity", new FloatProperty(0.3) );
    m_Node->SetProperty("linewidth", new IntProperty(1) );
    m_Node->SetProperty("scalar visibility", new BoolProperty(false) );

    std::string uid = m_UIDGeneratorSurfaces.GetUID();
    m_Node->SetProperty( "FILENAME", new StringProperty( uid + ".vtk" ) ); // undocumented feature of Image::WriteXMLData
    m_Node->SetProperty( "name", new StringProperty("surface representation") );

    // synchronize this object's color with the parent's color
    //surfaceNode->SetProperty( "color", parentNode->GetProperty( "color" ) );
    //surfaceNode->SetProperty( "visible", parentNode->GetProperty( "visible" ) );
  }

  m_Node->SetData( m_Surface );

  if (m_AddToTree)
  {

    DataTreeNode* groupNode = GetGroupNode();
    if (groupNode)
    {
      groupNode->SetProperty( "Surface representation", new SmartPointerProperty(m_Node) );
      BaseProperty* colorProp = groupNode->GetProperty("color");
      if (colorProp)
        m_Node->ReplaceProperty("color", colorProp);
      else
        m_Node->SetProperty("color", new ColorProperty(1.0, 1.0, 0.0));

      bool showResult(true);
      GetParameter("Show result", showResult );

      bool syncVisibility(false);
      GetParameter("Sync visibility", syncVisibility );

      Image::Pointer image;
      GetPointerParameter("Input", image);

      BaseProperty* organTypeProp = image->GetProperty("organ type");
      if (organTypeProp)
        m_Surface->SetProperty("organ type", organTypeProp);

      BaseProperty* visibleProp = groupNode->GetProperty("visible");
      if (visibleProp && syncVisibility)
        m_Node->ReplaceProperty("visible", visibleProp);
      else
        m_Node->SetProperty("visible", new BoolProperty(showResult));
     }

    InsertBelowGroupNode(m_Node);
  }

  Superclass::ThreadedUpdateSuccessful();
}

} // namespace

<commit_msg>FIX (#1060) Surfaces have no name<commit_after>/*=========================================================================

Program:   Medical Imaging & Interaction Toolkit
Module:    $RCSfile$
Language:  C++
Date:      $Date$
Version:   $Revision$

Copyright (c) German Cancer Research Center, Division of Medical and
Biological Informatics. All rights reserved.
See MITKCopyright.txt or http://www.mitk.org/copyright.html 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.

=========================================================================*/

#include "mitkShowSegmentationAsSurface.h"
#include "mitkManualSegmentationToSurfaceFilter.h"
#include "mitkDataTreeNodeFactory.h"
#include "mitkMaterialProperty.h"

#include <vtkPolyDataNormals.h>

namespace mitk
{

ShowSegmentationAsSurface::ShowSegmentationAsSurface()
:m_UIDGeneratorSurfaces("Surface_"),
 m_AddToTree(false)
{
}

ShowSegmentationAsSurface::~ShowSegmentationAsSurface()
{
}

void ShowSegmentationAsSurface::Initialize(const NonBlockingAlgorithm* other)
{
  Superclass::Initialize(other);

  bool syncVisibility(false);

  if (other)
  {
    other->GetParameter("Sync visibility", syncVisibility);
  }

  SetParameter("Sync visibility", syncVisibility );
  SetParameter("Smooth", true );
  SetParameter("Wireframe", false );
}

bool ShowSegmentationAsSurface::ReadyToRun()
{
  try
  {
    Image::Pointer image;
    GetPointerParameter("Input", image);

    return image.IsNotNull() && GetGroupNode();
  }
  catch (std::invalid_argument&)
  {
    return false;
  }
}

bool ShowSegmentationAsSurface::ThreadedUpdateFunction()
{
  Image::Pointer image;
  GetPointerParameter("Input", image);

  bool smooth(true);
  GetParameter("Smooth", smooth);

  ManualSegmentationToSurfaceFilter::Pointer surfaceFilter = ManualSegmentationToSurfaceFilter::New();
  surfaceFilter->SetInput( image );
  surfaceFilter->SetThreshold( 1 ); //expects binary image with zeros and ones

  if (smooth)
  {
    surfaceFilter->SetMedianKernelSize(3, 3, 3); // apply median to segmentation before marching cubes
    surfaceFilter->SetGaussianStandardDeviation( 1.5 );
    surfaceFilter->SetDecimate( ImageToSurfaceFilter::DecimatePro );
    surfaceFilter->SetTargetReduction( 0.8 );
  }

  surfaceFilter->SetUseGaussianImageSmooth(smooth); // apply gaussian to thresholded image ?
  surfaceFilter->SetMedianFilter3D(smooth); // apply median to segmentation before marching cubes ?
  surfaceFilter->SetSmooth(smooth); // smooth wireframe ?
  surfaceFilter->UpdateLargestPossibleRegion();

  // calculate normals for nicer display
  m_Surface = surfaceFilter->GetOutput();

  vtkPolyData* polyData = m_Surface->GetVtkPolyData();
  polyData->SetVerts(0);
  polyData->SetLines(0);

  if (smooth)
  {
    vtkPolyDataNormals* normalsGen = vtkPolyDataNormals::New();

    normalsGen->SetInput( polyData );
    normalsGen->Update();

    m_Surface->SetVtkPolyData( normalsGen->GetOutput() );

    normalsGen->Delete();
  }
  else
  {
    m_Surface->SetVtkPolyData( polyData );
  }

  return true;
}

void ShowSegmentationAsSurface::ThreadedUpdateSuccessful()
{
  m_Node = LookForPointerTargetBelowGroupNode("Surface representation");

  m_AddToTree = m_Node.IsNull();

  if (m_AddToTree)
  {
    m_Node = DataTreeNode::New();

    DataTreeNodeFactory::SetDefaultSurfaceProperties(m_Node);

    bool wireframe(false);
    GetParameter("Wireframe", wireframe );
    if (wireframe)
    {
      MaterialProperty* mp = dynamic_cast<MaterialProperty*>( m_Node->GetProperty("material").GetPointer() );
      if (mp)
      {
        mp->SetRepresentation( MaterialProperty::Wireframe );
      }
    }

    m_Node->SetProperty("opacity", new FloatProperty(0.3) );
    m_Node->SetProperty("linewidth", new IntProperty(1) );
    m_Node->SetProperty("scalar visibility", new BoolProperty(false) );

    std::string uid = m_UIDGeneratorSurfaces.GetUID();
    m_Node->SetProperty( "FILENAME", new StringProperty( uid + ".vtk" ) ); // undocumented feature of Image::WriteXMLData
    std::string groupNodesName ("surface");

    DataTreeNode* groupNode = GetGroupNode();
    if (groupNode)
    {
      groupNode->GetName( groupNodesName );
    }
    m_Node->SetProperty( "name", new StringProperty(groupNodesName) );

    // synchronize this object's color with the parent's color
    //surfaceNode->SetProperty( "color", parentNode->GetProperty( "color" ) );
    //surfaceNode->SetProperty( "visible", parentNode->GetProperty( "visible" ) );
  }

  m_Node->SetData( m_Surface );

  if (m_AddToTree)
  {

    DataTreeNode* groupNode = GetGroupNode();
    if (groupNode)
    {
      groupNode->SetProperty( "Surface representation", new SmartPointerProperty(m_Node) );
      BaseProperty* colorProp = groupNode->GetProperty("color");
      if (colorProp)
        m_Node->ReplaceProperty("color", colorProp);
      else
        m_Node->SetProperty("color", new ColorProperty(1.0, 1.0, 0.0));

      bool showResult(true);
      GetParameter("Show result", showResult );

      bool syncVisibility(false);
      GetParameter("Sync visibility", syncVisibility );

      Image::Pointer image;
      GetPointerParameter("Input", image);

      BaseProperty* organTypeProp = image->GetProperty("organ type");
      if (organTypeProp)
        m_Surface->SetProperty("organ type", organTypeProp);

      BaseProperty* visibleProp = groupNode->GetProperty("visible");
      if (visibleProp && syncVisibility)
        m_Node->ReplaceProperty("visible", visibleProp);
      else
        m_Node->SetProperty("visible", new BoolProperty(showResult));
     }

    InsertBelowGroupNode(m_Node);
  }

  Superclass::ThreadedUpdateSuccessful();
}

} // namespace

<|endoftext|>


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

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

#include "mitkTestFixture.h"

#include "mitkTestingMacros.h"
#include "mitkConstants.h"
#include "mitkTypes.h" // for Equal method
#include "mitkPoint.h"
#include "itkPoint.h"
#include "vtkPoints.h"
#include "vtkSmartPointer.h"

#include <iostream>

using namespace mitk;

class mitkPointTypeConversionTestSuite : public mitk::TestFixture
{

  CPPUNIT_TEST_SUITE(mitkPointTypeConversionTestSuite);

  MITK_TEST(Mitk2Itk_PointCompatibility);
  MITK_TEST(Itk2Mitk_PointCompatibility);

  MITK_TEST(Vtk2Mitk_PointCompatibility);

  MITK_TEST(Mitk2Pod_PointCompatibility);
  MITK_TEST(Pod2Mitk_PointCompatibility);

  MITK_TEST(Point2Vector);

  CPPUNIT_TEST_SUITE_END();

private:

  vtkSmartPointer<vtkPoints>   a_vtkPoints;
  ScalarType originalValues[3];
  ScalarType valuesToCopy[3];

public:

  void setUp(void)
  {
    FillVector3D(originalValues, 1.0, 2.0, 3.0);
    FillVector3D(valuesToCopy,   4.0, 5.0, 6.0);

    vtkSmartPointer<vtkPoints>   a_vtkPoints = vtkSmartPointer<vtkPoints>::New();
    a_vtkPoints->Initialize();
  }

  void tearDown(void)
  {
    a_vtkPoints = NULL;
  }

  /**
   * @brief Convenience method to test if one vector has been assigned successfully to the other.
   *
   * More specifically, tests if v1 = v2 was performed correctly.
   *
   * @param v1    The vector v1 of the assignment v1 = v2
   * @param v2    The vector v2 of the assignment v1 = v2
   * @param v1Name        The type name of v1 (e.g.: mitk::Vector3D). Necessary for the correct test output.
   * @param v2Name        The type name of v2 (e.g.: mitk::Vector3D). Necessary for the correct test output.
  *  @param eps   defines the allowed tolerance when testing for equality.
   */
  template <typename T1, typename T2>
  void TestForEquality(T1 v1, T2 v2, std::string v1Name, std::string v2Name, ScalarType eps = mitk::eps)
  {
    CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE("\nAssigning " + v2Name  + " to " + v1Name + ":\n both are equal", EqualArray(v1, v2, 3, eps));
  }

  void Mitk2Itk_PointCompatibility()
  {
    itk::Point<ScalarType, 3> itkPoint3D = originalValues;
    mitk::Point3D             point3D    = valuesToCopy;

    itkPoint3D = point3D;

    TestForEquality(itkPoint3D, point3D, "itk::Point", "mitk:Point");
  }

  void Itk2Mitk_PointCompatibility()
  {
    mitk::Point3D point3D                = originalValues;
    itk::Point<ScalarType, 3> itkPoint3D = valuesToCopy;

    point3D = itkPoint3D;

    TestForEquality(point3D, itkPoint3D, "mitk:Point", "itk::Point");
  }

  void Vtk2Mitk_PointCompatibility()
  {
    mitk::Point3D point3D = originalValues;
    a_vtkPoints->InsertNextPoint(valuesToCopy);
    double vtkPoint[3];
    a_vtkPoints->GetPoint(0, vtkPoint);

    point3D = vtkPoint;

    TestForEquality(point3D, vtkPoint, "mitk:Point", "vtkPoint");

  }

  void Mitk2Pod_PointCompatibility()
  {
    ScalarType podPoint[] = {1.0, 2.0, 3.0};
    mitk::Point3D point3D = valuesToCopy;

    point3D.ToArray(podPoint);

    TestForEquality(podPoint, point3D, "POD point", "mitk::Point");

  }

  void Pod2Mitk_PointCompatibility()
  {
    itk::Point<double, 3> point3D = originalValues;
    ScalarType podPoint[] = {4.0, 5.0, 6.0};

    point3D = podPoint;

    TestForEquality(point3D, podPoint, "mitk::Point3D", "POD point");
  }

  void Point2Vector()
  {
    itk::Point<double, 3> point3D   = valuesToCopy;
    itk::Vector<double, 3> vector3D = originalValues;

    point3D = vector3D;

    TestForEquality(point3D, vector3D, "mitk::Point", "mitk::Vector");
  }

};

MITK_TEST_SUITE_REGISTRATION(mitkPointTypeConversion)

<commit_msg>Fixed errors in Point conversion test, now it works!<commit_after>/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

#include "mitkTestFixture.h"

#include "mitkTestingMacros.h"
#include "mitkConstants.h"
#include "mitkTypes.h" // for Equal method
#include "mitkPoint.h"
#include "itkPoint.h"
#include "vtkPoints.h"
#include "vtkSmartPointer.h"

#include <iostream>

using namespace mitk;

class mitkPointTypeConversionTestSuite : public mitk::TestFixture
{

  CPPUNIT_TEST_SUITE(mitkPointTypeConversionTestSuite);

  MITK_TEST(Mitk2Itk_PointCompatibility);
  MITK_TEST(Itk2Mitk_PointCompatibility);

  MITK_TEST(Vtk2Mitk_PointCompatibility);

  MITK_TEST(Mitk2Pod_PointCompatibility);
  MITK_TEST(Pod2Mitk_PointCompatibility);

  MITK_TEST(Vector2Point);

  CPPUNIT_TEST_SUITE_END();

private:

  vtkSmartPointer<vtkPoints>   a_vtkPoints;
  ScalarType originalValues[3];
  ScalarType valuesToCopy[3];

public:

  void setUp(void)
  {
    FillVector3D(originalValues, 1.0, 2.0, 3.0);
    FillVector3D(valuesToCopy,   4.0, 5.0, 6.0);

    a_vtkPoints = vtkSmartPointer<vtkPoints>::New();
    a_vtkPoints->Initialize();
  }

  void tearDown(void)
  {
 //   a_vtkPoints = NULL;
  }

  /**
   * @brief Convenience method to test if one vector has been assigned successfully to the other.
   *
   * More specifically, tests if v1 = v2 was performed correctly.
   *
   * @param v1    The vector v1 of the assignment v1 = v2
   * @param v2    The vector v2 of the assignment v1 = v2
   * @param v1Name        The type name of v1 (e.g.: mitk::Vector3D). Necessary for the correct test output.
   * @param v2Name        The type name of v2 (e.g.: mitk::Vector3D). Necessary for the correct test output.
  *  @param eps   defines the allowed tolerance when testing for equality.
   */
  template <typename T1, typename T2>
  void TestForEquality(T1 v1, T2 v2, std::string v1Name, std::string v2Name, ScalarType eps = mitk::eps)
  {
    CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE("\nAssigning " + v2Name  + " to " + v1Name + ":\n both are equal", EqualArray(v1, v2, 3, eps));
  }

  void Mitk2Itk_PointCompatibility()
  {
    itk::Point<ScalarType, 3> itkPoint3D = originalValues;
    mitk::Point3D             point3D    = valuesToCopy;

    itkPoint3D = point3D;

    TestForEquality(itkPoint3D, point3D, "itk::Point", "mitk:Point");
  }

  void Itk2Mitk_PointCompatibility()
  {
    mitk::Point3D point3D                = originalValues;
    itk::Point<ScalarType, 3> itkPoint3D = valuesToCopy;

    point3D = itkPoint3D;

    TestForEquality(point3D, itkPoint3D, "mitk:Point", "itk::Point");
  }

  void Vtk2Mitk_PointCompatibility()
  {
    mitk::Point3D point3D = originalValues;
    a_vtkPoints->InsertNextPoint(valuesToCopy);
    double vtkPoint[3];
    a_vtkPoints->GetPoint(0, vtkPoint);

    point3D = vtkPoint;

    TestForEquality(point3D, vtkPoint, "mitk:Point", "vtkPoint");

  }

  void Mitk2Pod_PointCompatibility()
  {
    ScalarType podPoint[] = {1.0, 2.0, 3.0};
    mitk::Point3D point3D = valuesToCopy;

    point3D.ToArray(podPoint);

    TestForEquality(podPoint, point3D, "POD point", "mitk::Point");

  }

  void Pod2Mitk_PointCompatibility()
  {
    itk::Point<double, 3> point3D = originalValues;
    ScalarType podPoint[] = {4.0, 5.0, 6.0};

    point3D = podPoint;

    TestForEquality(point3D, podPoint, "mitk::Point3D", "POD point");
  }

  void Vector2Point()
  {
    itk::Point<double, 3> point3D   = valuesToCopy;
    itk::Vector<double, 3> vector3D = originalValues;

    point3D = vector3D;

    TestForEquality(point3D, vector3D, "mitk::Point", "mitk::Vector");
  }

};

MITK_TEST_SUITE_REGISTRATION(mitkPointTypeConversion)

<|endoftext|>


<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/audio/mac/audio_manager_mac.h"
#include "media/audio/mac/audio_output_mac.h"

#include "base/basictypes.h"
#include "base/logging.h"
#include "media/audio/audio_util.h"

// Overview of operation:
// 1) An object of PCMQueueOutAudioOutputStream is created by the AudioManager
// factory: audio_man->MakeAudioStream(). This just fills some structure.
// 2) Next some thread will call Open(), at that point the underliying OS
// queue is created and the audio buffers allocated.
// 3) Then some thread will call Start(source) At this point the source will be
// called to fill the initial buffers in the context of that same thread.
// Then the OS queue is started which will create its own thread which
// periodically will call the source for more data as buffers are being
// consumed.
// 4) At some point some thread will call Stop(), which we handle by directly
// stoping the OS queue.
// 5) One more callback to the source could be delivered in in the context of
// the queue's own thread. Data, if any will be discared.
// 6) The same thread that called stop will call Close() where we cleanup
// and notifiy the audio manager, which likley will destroy this object.

#if !defined(MAC_OS_X_VERSION_10_6) || \
    MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
enum {
  kAudioQueueErr_EnqueueDuringReset = -66632
};
#endif

PCMQueueOutAudioOutputStream::PCMQueueOutAudioOutputStream(
    AudioManagerMac* manager, int channels, int sampling_rate,
    char bits_per_sample)
        : format_(),
          audio_queue_(NULL),
          buffer_(),
          source_(NULL),
          manager_(manager),
          silence_bytes_(0),
          volume_(1),
          pending_bytes_(0) {
  // We must have a manager.
  DCHECK(manager_);
  // A frame is one sample across all channels. In interleaved audio the per
  // frame fields identify the set of n |channels|. In uncompressed audio, a
  // packet is always one frame.
  format_.mSampleRate = sampling_rate;
  format_.mFormatID = kAudioFormatLinearPCM;
  format_.mFormatFlags = kLinearPCMFormatFlagIsPacked |
                         kLinearPCMFormatFlagIsSignedInteger;
  format_.mBitsPerChannel = bits_per_sample;
  format_.mChannelsPerFrame = channels;
  format_.mFramesPerPacket = 1;
  format_.mBytesPerPacket = (format_.mBitsPerChannel * channels) / 8;
  format_.mBytesPerFrame = format_.mBytesPerPacket;

  // Silence buffer has a duration of 6ms to simulate the behavior of Windows.
  // This value is choosen by experiments and macs cannot keep up with
  // anything less than 6ms.
  silence_bytes_ = format_.mBytesPerFrame * sampling_rate * 6 / 1000;
}

PCMQueueOutAudioOutputStream::~PCMQueueOutAudioOutputStream() {
}

void PCMQueueOutAudioOutputStream::HandleError(OSStatus err) {
  // source_ can be set to NULL from another thread. We need to cache its
  // pointer while we operate here. Note that does not mean that the source
  // has been destroyed.
  AudioSourceCallback* source = source_;
  if (source)
    source->OnError(this, static_cast<int>(err));
  NOTREACHED() << "error code " << err;
}

bool PCMQueueOutAudioOutputStream::Open(size_t packet_size) {
  if (0 == packet_size) {
    // TODO(cpu) : Impelement default buffer computation.
    return false;
  }
  // Create the actual queue object and let the OS use its own thread to
  // run its CFRunLoop.
  OSStatus err = AudioQueueNewOutput(&format_, RenderCallback, this, NULL,
                                     kCFRunLoopCommonModes, 0, &audio_queue_);
  if (err != noErr) {
    HandleError(err);
    return false;
  }
  // Allocate the hardware-managed buffers.
  for (size_t ix = 0; ix != kNumBuffers; ++ix) {
    err = AudioQueueAllocateBuffer(audio_queue_, packet_size, &buffer_[ix]);
    if (err != noErr) {
      HandleError(err);
      return false;
    }
  }
  // Set initial volume here.
  err = AudioQueueSetParameter(audio_queue_, kAudioQueueParam_Volume, 1.0);
  if (err != noErr) {
    HandleError(err);
    return false;
  }
  return true;
}

void PCMQueueOutAudioOutputStream::Close() {
  // It is valid to call Close() before calling Open(), thus audio_queue_
  // might be NULL.
  if (audio_queue_) {
    OSStatus err = 0;
    for (size_t ix = 0; ix != kNumBuffers; ++ix) {
      if (buffer_[ix]) {
        err = AudioQueueFreeBuffer(audio_queue_, buffer_[ix]);
        if (err != noErr) {
          HandleError(err);
          break;
        }
      }
    }
    err = AudioQueueDispose(audio_queue_, true);
    if (err != noErr)
      HandleError(err);
  }
  // Inform the audio manager that we have been closed. This can cause our
  // destruction.
  manager_->ReleaseStream(this);
}

void PCMQueueOutAudioOutputStream::Stop() {
  // We request a synchronous stop, so the next call can take some time. In
  // the windows implementation we block here as well.
  source_ = NULL;
  // We set the source to null to signal to the data queueing thread it can stop
  // queueing data, however at most one callback might still be in flight which
  // could attempt to enqueue right after the next call. Rather that trying to
  // use a lock we rely on the internal Mac queue lock so the enqueue might
  // succeed or might fail but it won't crash or leave the queue itself in an
  // inconsistent state.
  OSStatus err = AudioQueueStop(audio_queue_, true);
  if (err != noErr)
    HandleError(err);
}

void PCMQueueOutAudioOutputStream::SetVolume(double left_level,
                                             double ) {
  if (!audio_queue_)
    return;
  volume_ = static_cast<float>(left_level);
  OSStatus err = AudioQueueSetParameter(audio_queue_,
                                        kAudioQueueParam_Volume,
                                        left_level);
  if (err != noErr) {
    HandleError(err);
  }
}

void PCMQueueOutAudioOutputStream::GetVolume(double* left_level,
                                             double* right_level) {
  if (!audio_queue_)
    return;
  *left_level = volume_;
  *right_level = volume_;
}

// Reorder PCM from AAC layout to Core Audio layout.
// TODO(fbarchard): Switch layout when ffmpeg is updated.
const int kNumSurroundChannels = 6;
template<class Format>
static void SwizzleLayout(Format *b, size_t filled) {
  Format aac[kNumSurroundChannels];
  for (size_t i = 0; i < filled; i += sizeof(aac), b += kNumSurroundChannels) {
    memcpy(aac, b, sizeof(aac));
    b[0] = aac[1];  // L
    b[1] = aac[2];  // R
    b[2] = aac[0];  // C
    b[3] = aac[5];  // LFE
    b[4] = aac[3];  // Ls
    b[5] = aac[4];  // Rs
  }
}

// Note to future hackers of this function: Do not add locks here because we
// call out to third party source that might do crazy things including adquire
// external locks or somehow re-enter here because its legal for them to call
// some audio functions.
void PCMQueueOutAudioOutputStream::RenderCallback(void* p_this,
                                                  AudioQueueRef queue,
                                                  AudioQueueBufferRef buffer) {
  PCMQueueOutAudioOutputStream* audio_stream =
      static_cast<PCMQueueOutAudioOutputStream*>(p_this);
  // Call the audio source to fill the free buffer with data. Not having a
  // source means that the queue has been closed. This is not an error.
  AudioSourceCallback* source = audio_stream->source_;
  if (!source)
    return;

  // Adjust the number of pending bytes by subtracting the amount played.
  audio_stream->pending_bytes_ -= buffer->mAudioDataByteSize;
  size_t capacity = buffer->mAudioDataBytesCapacity;
  size_t filled = source->OnMoreData(audio_stream, buffer->mAudioData,
                                     capacity, audio_stream->pending_bytes_);

  // In order to keep the callback running, we need to provide a positive amount
  // of data to the audio queue. To simulate the behavior of Windows, we write
  // a buffer of silence.
  if (!filled) {
    CHECK(audio_stream->silence_bytes_ <= static_cast<int>(capacity));
    filled = audio_stream->silence_bytes_;
    memset(buffer->mAudioData, 0, filled);
  } else if (filled > capacity) {
    // User probably overran our buffer.
    audio_stream->HandleError(0);
    return;
  }

  // Handle channel order for PCM 5.1 audio.
  if (audio_stream->format_.mChannelsPerFrame == 6) {
    if (audio_stream->format_.mBitsPerChannel == 8) {
      SwizzleLayout(reinterpret_cast<uint8*>(buffer->mAudioData), filled);
    } else if (audio_stream->format_.mBitsPerChannel == 16) {
      SwizzleLayout(reinterpret_cast<int16*>(buffer->mAudioData), filled);
    } else if (audio_stream->format_.mBitsPerChannel == 32) {
      SwizzleLayout(reinterpret_cast<int32*>(buffer->mAudioData), filled);
    }
  }

  buffer->mAudioDataByteSize = filled;
  // Incremnet bytes by amount filled into audio buffer.
  audio_stream->pending_bytes_ += filled;
  if (NULL == queue)
    return;
  // Queue the audio data to the audio driver.
  OSStatus err = AudioQueueEnqueueBuffer(queue, buffer, 0, NULL);
  if (err != noErr) {
    if (err == kAudioQueueErr_EnqueueDuringReset) {
      // This is the error you get if you try to enqueue a buffer and the
      // queue has been closed. Not really a problem if indeed the queue
      // has been closed.
      if (!audio_stream->source_)
        return;
    }
    audio_stream->HandleError(err);
  }
}

void PCMQueueOutAudioOutputStream::Start(AudioSourceCallback* callback) {
  DCHECK(callback);
  OSStatus err = noErr;
  source_ = callback;
  pending_bytes_ = 0;
  // Ask the source to pre-fill all our buffers before playing.
  for (size_t ix = 0; ix != kNumBuffers; ++ix) {
    buffer_[ix]->mAudioDataByteSize = 0;
    RenderCallback(this, NULL, buffer_[ix]);
  }
  // Queue the buffers to the audio driver, sounds starts now.
  for (size_t ix = 0; ix != kNumBuffers; ++ix) {
    err = AudioQueueEnqueueBuffer(audio_queue_, buffer_[ix], 0, NULL);
    if (err != noErr) {
      HandleError(err);
      return;
    }
  }
  err  = AudioQueueStart(audio_queue_, NULL);
  if (err != noErr) {
    HandleError(err);
    return;
  }
}

<commit_msg>Support 8 and 32 bit formats for Mac Channel Swizzler.<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/audio/mac/audio_manager_mac.h"
#include "media/audio/mac/audio_output_mac.h"

#include "base/basictypes.h"
#include "base/logging.h"
#include "media/audio/audio_util.h"

// Overview of operation:
// 1) An object of PCMQueueOutAudioOutputStream is created by the AudioManager
// factory: audio_man->MakeAudioStream(). This just fills some structure.
// 2) Next some thread will call Open(), at that point the underliying OS
// queue is created and the audio buffers allocated.
// 3) Then some thread will call Start(source) At this point the source will be
// called to fill the initial buffers in the context of that same thread.
// Then the OS queue is started which will create its own thread which
// periodically will call the source for more data as buffers are being
// consumed.
// 4) At some point some thread will call Stop(), which we handle by directly
// stoping the OS queue.
// 5) One more callback to the source could be delivered in in the context of
// the queue's own thread. Data, if any will be discared.
// 6) The same thread that called stop will call Close() where we cleanup
// and notifiy the audio manager, which likley will destroy this object.

#if !defined(MAC_OS_X_VERSION_10_6) || \
    MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
enum {
  kAudioQueueErr_EnqueueDuringReset = -66632
};
#endif

PCMQueueOutAudioOutputStream::PCMQueueOutAudioOutputStream(
    AudioManagerMac* manager, int channels, int sampling_rate,
    char bits_per_sample)
        : format_(),
          audio_queue_(NULL),
          buffer_(),
          source_(NULL),
          manager_(manager),
          silence_bytes_(0),
          volume_(1),
          pending_bytes_(0) {
  // We must have a manager.
  DCHECK(manager_);
  // A frame is one sample across all channels. In interleaved audio the per
  // frame fields identify the set of n |channels|. In uncompressed audio, a
  // packet is always one frame.
  format_.mSampleRate = sampling_rate;
  format_.mFormatID = kAudioFormatLinearPCM;
  format_.mFormatFlags = kLinearPCMFormatFlagIsPacked |
                         kLinearPCMFormatFlagIsSignedInteger;
  format_.mBitsPerChannel = bits_per_sample;
  format_.mChannelsPerFrame = channels;
  format_.mFramesPerPacket = 1;
  format_.mBytesPerPacket = (format_.mBitsPerChannel * channels) / 8;
  format_.mBytesPerFrame = format_.mBytesPerPacket;

  // Silence buffer has a duration of 6ms to simulate the behavior of Windows.
  // This value is choosen by experiments and macs cannot keep up with
  // anything less than 6ms.
  silence_bytes_ = format_.mBytesPerFrame * sampling_rate * 6 / 1000;
}

PCMQueueOutAudioOutputStream::~PCMQueueOutAudioOutputStream() {
}

void PCMQueueOutAudioOutputStream::HandleError(OSStatus err) {
  // source_ can be set to NULL from another thread. We need to cache its
  // pointer while we operate here. Note that does not mean that the source
  // has been destroyed.
  AudioSourceCallback* source = source_;
  if (source)
    source->OnError(this, static_cast<int>(err));
  NOTREACHED() << "error code " << err;
}

bool PCMQueueOutAudioOutputStream::Open(size_t packet_size) {
  if (0 == packet_size) {
    // TODO(cpu) : Impelement default buffer computation.
    return false;
  }
  // Create the actual queue object and let the OS use its own thread to
  // run its CFRunLoop.
  OSStatus err = AudioQueueNewOutput(&format_, RenderCallback, this, NULL,
                                     kCFRunLoopCommonModes, 0, &audio_queue_);
  if (err != noErr) {
    HandleError(err);
    return false;
  }
  // Allocate the hardware-managed buffers.
  for (size_t ix = 0; ix != kNumBuffers; ++ix) {
    err = AudioQueueAllocateBuffer(audio_queue_, packet_size, &buffer_[ix]);
    if (err != noErr) {
      HandleError(err);
      return false;
    }
  }
  // Set initial volume here.
  err = AudioQueueSetParameter(audio_queue_, kAudioQueueParam_Volume, 1.0);
  if (err != noErr) {
    HandleError(err);
    return false;
  }
  return true;
}

void PCMQueueOutAudioOutputStream::Close() {
  // It is valid to call Close() before calling Open(), thus audio_queue_
  // might be NULL.
  if (audio_queue_) {
    OSStatus err = 0;
    for (size_t ix = 0; ix != kNumBuffers; ++ix) {
      if (buffer_[ix]) {
        err = AudioQueueFreeBuffer(audio_queue_, buffer_[ix]);
        if (err != noErr) {
          HandleError(err);
          break;
        }
      }
    }
    err = AudioQueueDispose(audio_queue_, true);
    if (err != noErr)
      HandleError(err);
  }
  // Inform the audio manager that we have been closed. This can cause our
  // destruction.
  manager_->ReleaseStream(this);
}

void PCMQueueOutAudioOutputStream::Stop() {
  // We request a synchronous stop, so the next call can take some time. In
  // the windows implementation we block here as well.
  source_ = NULL;
  // We set the source to null to signal to the data queueing thread it can stop
  // queueing data, however at most one callback might still be in flight which
  // could attempt to enqueue right after the next call. Rather that trying to
  // use a lock we rely on the internal Mac queue lock so the enqueue might
  // succeed or might fail but it won't crash or leave the queue itself in an
  // inconsistent state.
  OSStatus err = AudioQueueStop(audio_queue_, true);
  if (err != noErr)
    HandleError(err);
}

void PCMQueueOutAudioOutputStream::SetVolume(double left_level,
                                             double ) {
  if (!audio_queue_)
    return;
  volume_ = static_cast<float>(left_level);
  OSStatus err = AudioQueueSetParameter(audio_queue_,
                                        kAudioQueueParam_Volume,
                                        left_level);
  if (err != noErr) {
    HandleError(err);
  }
}

void PCMQueueOutAudioOutputStream::GetVolume(double* left_level,
                                             double* right_level) {
  if (!audio_queue_)
    return;
  *left_level = volume_;
  *right_level = volume_;
}

// Reorder PCM from AAC layout to Core Audio layout.
// TODO(fbarchard): Switch layout when ffmpeg is updated.
namespace {
template<class Format>
static void SwizzleLayout(Format *b, size_t filled) {
  static const int kNumSurroundChannels = 6;
  Format aac[kNumSurroundChannels];
  for (size_t i = 0; i < filled; i += sizeof(aac), b += kNumSurroundChannels) {
    memcpy(aac, b, sizeof(aac));
    b[0] = aac[1];  // L
    b[1] = aac[2];  // R
    b[2] = aac[0];  // C
    b[3] = aac[5];  // LFE
    b[4] = aac[3];  // Ls
    b[5] = aac[4];  // Rs
  }
}
}  // namespace

// Note to future hackers of this function: Do not add locks here because we
// call out to third party source that might do crazy things including adquire
// external locks or somehow re-enter here because its legal for them to call
// some audio functions.
void PCMQueueOutAudioOutputStream::RenderCallback(void* p_this,
                                                  AudioQueueRef queue,
                                                  AudioQueueBufferRef buffer) {
  PCMQueueOutAudioOutputStream* audio_stream =
      static_cast<PCMQueueOutAudioOutputStream*>(p_this);
  // Call the audio source to fill the free buffer with data. Not having a
  // source means that the queue has been closed. This is not an error.
  AudioSourceCallback* source = audio_stream->source_;
  if (!source)
    return;

  // Adjust the number of pending bytes by subtracting the amount played.
  audio_stream->pending_bytes_ -= buffer->mAudioDataByteSize;
  size_t capacity = buffer->mAudioDataBytesCapacity;
  size_t filled = source->OnMoreData(audio_stream, buffer->mAudioData,
                                     capacity, audio_stream->pending_bytes_);

  // In order to keep the callback running, we need to provide a positive amount
  // of data to the audio queue. To simulate the behavior of Windows, we write
  // a buffer of silence.
  if (!filled) {
    CHECK(audio_stream->silence_bytes_ <= static_cast<int>(capacity));
    filled = audio_stream->silence_bytes_;
    memset(buffer->mAudioData, 0, filled);
  } else if (filled > capacity) {
    // User probably overran our buffer.
    audio_stream->HandleError(0);
    return;
  }

  // Handle channel order for 5.1 audio.
  if (audio_stream->format_.mChannelsPerFrame == 6) {
    if (audio_stream->format_.mBitsPerChannel == 8) {
      SwizzleLayout(reinterpret_cast<uint8*>(buffer->mAudioData), filled);
    } else if (audio_stream->format_.mBitsPerChannel == 16) {
      SwizzleLayout(reinterpret_cast<int16*>(buffer->mAudioData), filled);
    } else if (audio_stream->format_.mBitsPerChannel == 32) {
      SwizzleLayout(reinterpret_cast<int32*>(buffer->mAudioData), filled);
    }
  }

  buffer->mAudioDataByteSize = filled;
  // Incremnet bytes by amount filled into audio buffer.
  audio_stream->pending_bytes_ += filled;
  if (NULL == queue)
    return;
  // Queue the audio data to the audio driver.
  OSStatus err = AudioQueueEnqueueBuffer(queue, buffer, 0, NULL);
  if (err != noErr) {
    if (err == kAudioQueueErr_EnqueueDuringReset) {
      // This is the error you get if you try to enqueue a buffer and the
      // queue has been closed. Not really a problem if indeed the queue
      // has been closed.
      if (!audio_stream->source_)
        return;
    }
    audio_stream->HandleError(err);
  }
}

void PCMQueueOutAudioOutputStream::Start(AudioSourceCallback* callback) {
  DCHECK(callback);
  OSStatus err = noErr;
  source_ = callback;
  pending_bytes_ = 0;
  // Ask the source to pre-fill all our buffers before playing.
  for (size_t ix = 0; ix != kNumBuffers; ++ix) {
    buffer_[ix]->mAudioDataByteSize = 0;
    RenderCallback(this, NULL, buffer_[ix]);
  }
  // Queue the buffers to the audio driver, sounds starts now.
  for (size_t ix = 0; ix != kNumBuffers; ++ix) {
    err = AudioQueueEnqueueBuffer(audio_queue_, buffer_[ix], 0, NULL);
    if (err != noErr) {
      HandleError(err);
      return;
    }
  }
  err  = AudioQueueStart(audio_queue_, NULL);
  if (err != noErr) {
    HandleError(err);
    return;
  }
}

<|endoftext|>