<commit_before>// Copyright (c) 2012 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/pulse/pulse_output.h"

#include "base/bind.h"
#include "base/message_loop.h"
#include "media/audio/audio_parameters.h"
#include "media/audio/audio_util.h"
#if defined(OS_LINUX)
#include "media/audio/linux/audio_manager_linux.h"
#elif defined(OS_OPENBSD)
#include "media/audio/openbsd/audio_manager_openbsd.h"
#endif
#include "media/base/data_buffer.h"
#include "media/base/seekable_buffer.h"

namespace media {

static pa_sample_format_t BitsToPASampleFormat(int bits_per_sample) {
  switch (bits_per_sample) {
    // Unsupported sample formats shown for reference.  I am assuming we want
    // signed and little endian because that is what we gave to ALSA.
    case 8:
      return PA_SAMPLE_U8;
      // Also 8-bits: PA_SAMPLE_ALAW and PA_SAMPLE_ULAW
    case 16:
      return PA_SAMPLE_S16LE;
      // Also 16-bits: PA_SAMPLE_S16BE (big endian).
    case 24:
      return PA_SAMPLE_S24LE;
      // Also 24-bits: PA_SAMPLE_S24BE (big endian).
      // Other cases: PA_SAMPLE_24_32LE (in LSB of 32-bit field, little endian),
      //          and PA_SAMPLE_24_32BE (in LSB of 32-bit field, big endian),
    case 32:
      return PA_SAMPLE_S32LE;
      // Also 32-bits: PA_SAMPLE_S32BE (big endian),
      //               PA_SAMPLE_FLOAT32LE (floating point little endian),
      //           and PA_SAMPLE_FLOAT32BE (floating point big endian).
    default:
      return PA_SAMPLE_INVALID;
  }
}

static pa_channel_position ChromiumToPAChannelPosition(Channels channel) {
  switch (channel) {
    // PulseAudio does not differentiate between left/right and
    // stereo-left/stereo-right, both translate to front-left/front-right.
    case LEFT:
    case STEREO_LEFT:
      return PA_CHANNEL_POSITION_FRONT_LEFT;
    case RIGHT:
    case STEREO_RIGHT:
      return PA_CHANNEL_POSITION_FRONT_RIGHT;
    case CENTER:
      return PA_CHANNEL_POSITION_FRONT_CENTER;
    case LFE:
      return PA_CHANNEL_POSITION_LFE;
    case BACK_LEFT:
      return PA_CHANNEL_POSITION_REAR_LEFT;
    case BACK_RIGHT:
      return PA_CHANNEL_POSITION_REAR_RIGHT;
    case LEFT_OF_CENTER:
      return PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
    case RIGHT_OF_CENTER:
      return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
    case BACK_CENTER:
      return PA_CHANNEL_POSITION_REAR_CENTER;
    case SIDE_LEFT:
      return PA_CHANNEL_POSITION_SIDE_LEFT;
    case SIDE_RIGHT:
      return PA_CHANNEL_POSITION_SIDE_RIGHT;
    case CHANNELS_MAX:
      return PA_CHANNEL_POSITION_INVALID;
  }
  NOTREACHED() << "Invalid channel " << channel;
  return PA_CHANNEL_POSITION_INVALID;
}

static pa_channel_map ChannelLayoutToPAChannelMap(
    ChannelLayout channel_layout) {
  // Initialize channel map.
  pa_channel_map channel_map;
  pa_channel_map_init(&channel_map);

  channel_map.channels = ChannelLayoutToChannelCount(channel_layout);

  // All channel maps have the same size array of channel positions.
  for (unsigned int channel = 0; channel != CHANNELS_MAX; ++channel) {
    int channel_position = kChannelOrderings[channel_layout][channel];
    if (channel_position > -1) {
      channel_map.map[channel_position] = ChromiumToPAChannelPosition(
          static_cast<Channels>(channel));
    } else {
      // PulseAudio expects unused channels in channel maps to be filled with
      // PA_CHANNEL_POSITION_MONO.
      channel_map.map[channel_position] = PA_CHANNEL_POSITION_MONO;
    }
  }

  // Fill in the rest of the unused channels.
  for (unsigned int channel = CHANNELS_MAX; channel != PA_CHANNELS_MAX;
       ++channel) {
    channel_map.map[channel] = PA_CHANNEL_POSITION_MONO;
  }

  return channel_map;
}

static size_t MicrosecondsToBytes(
    uint32 microseconds, uint32 sample_rate, size_t bytes_per_frame) {
  return microseconds * sample_rate * bytes_per_frame /
      base::Time::kMicrosecondsPerSecond;
}

// static
void PulseAudioOutputStream::ContextStateCallback(pa_context* context,
                                                  void* state_addr) {
  pa_context_state_t* state = static_cast<pa_context_state_t*>(state_addr);
  *state = pa_context_get_state(context);
}

// static
void PulseAudioOutputStream::WriteRequestCallback(pa_stream* playback_handle,
                                                  size_t length,
                                                  void* stream_addr) {
  PulseAudioOutputStream* stream =
      reinterpret_cast<PulseAudioOutputStream*>(stream_addr);

  DCHECK_EQ(stream->manager_->GetMessageLoop(), MessageLoop::current());

  stream->write_callback_handled_ = true;

  // Fulfill write request.
  stream->FulfillWriteRequest(length);
}

PulseAudioOutputStream::PulseAudioOutputStream(const AudioParameters& params,
                                               AudioManagerPulse* manager)
    : channel_layout_(params.channel_layout),
      channel_count_(ChannelLayoutToChannelCount(channel_layout_)),
      sample_format_(BitsToPASampleFormat(params.bits_per_sample)),
      sample_rate_(params.sample_rate),
      bytes_per_frame_(params.channels * params.bits_per_sample / 8),
      manager_(manager),
      pa_context_(NULL),
      pa_mainloop_(NULL),
      playback_handle_(NULL),
      packet_size_(params.GetPacketSize()),
      frames_per_packet_(packet_size_ / bytes_per_frame_),
      client_buffer_(NULL),
      volume_(1.0f),
      stream_stopped_(true),
      write_callback_handled_(false),
      ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
      source_callback_(NULL) {
  DCHECK_EQ(manager_->GetMessageLoop(), MessageLoop::current());

  // TODO(slock): Sanity check input values.
}

PulseAudioOutputStream::~PulseAudioOutputStream() {
  // All internal structures should already have been freed in Close(),
  // which calls AudioManagerPulse::Release which deletes this object.
  DCHECK(!playback_handle_);
  DCHECK(!pa_context_);
  DCHECK(!pa_mainloop_);
}

bool PulseAudioOutputStream::Open() {
  DCHECK_EQ(manager_->GetMessageLoop(), MessageLoop::current());

  // TODO(slock): Possibly move most of this to an OpenPlaybackDevice function
  // in a new class 'pulse_util', like alsa_util.

  // Create a mainloop API and connect to the default server.
  pa_mainloop_ = pa_mainloop_new();
  pa_mainloop_api* pa_mainloop_api = pa_mainloop_get_api(pa_mainloop_);
  pa_context_ = pa_context_new(pa_mainloop_api, "Chromium");
  pa_context_state_t pa_context_state = PA_CONTEXT_UNCONNECTED;
  pa_context_connect(pa_context_, NULL, PA_CONTEXT_NOFLAGS, NULL);

  // Wait until PulseAudio is ready.
  pa_context_set_state_callback(pa_context_, &ContextStateCallback,
                                &pa_context_state);
  while (pa_context_state != PA_CONTEXT_READY) {
    pa_mainloop_iterate(pa_mainloop_, 1, NULL);
    if (pa_context_state == PA_CONTEXT_FAILED ||
        pa_context_state == PA_CONTEXT_TERMINATED) {
      Reset();
      return false;
    }
  }

  // Set sample specifications.
  pa_sample_spec pa_sample_specifications;
  pa_sample_specifications.format = sample_format_;
  pa_sample_specifications.rate = sample_rate_;
  pa_sample_specifications.channels = channel_count_;

  // Get channel mapping and open playback stream.
  pa_channel_map* map = NULL;
  pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap(
      channel_layout_);
  if (source_channel_map.channels != 0) {
    // The source data uses a supported channel map so we will use it rather
    // than the default channel map (NULL).
    map = &source_channel_map;
  }
  playback_handle_ = pa_stream_new(pa_context_, "Playback",
                                   &pa_sample_specifications, map);

  // Initialize client buffer.
  uint32 output_packet_size = frames_per_packet_ * bytes_per_frame_;
  client_buffer_.reset(new media::SeekableBuffer(0, output_packet_size));

  // Set write callback.
  pa_stream_set_write_callback(playback_handle_, &WriteRequestCallback, this);

  // Set server-side buffer attributes.
  // (uint32_t)-1 is the default and recommended value from PulseAudio's
  // documentation, found at:
  // http://freedesktop.org/software/pulseaudio/doxygen/structpa__buffer__attr.html.
  pa_buffer_attr pa_buffer_attributes;
  pa_buffer_attributes.maxlength = static_cast<uint32_t>(-1);
  pa_buffer_attributes.tlength = output_packet_size;
  pa_buffer_attributes.prebuf = static_cast<uint32_t>(-1);
  pa_buffer_attributes.minreq = static_cast<uint32_t>(-1);
  pa_buffer_attributes.fragsize = static_cast<uint32_t>(-1);

  // Connect playback stream.
  pa_stream_connect_playback(playback_handle_, NULL,
                             &pa_buffer_attributes,
                             (pa_stream_flags_t)
                             (PA_STREAM_INTERPOLATE_TIMING |
                             PA_STREAM_ADJUST_LATENCY |
                             PA_STREAM_AUTO_TIMING_UPDATE),
                             NULL, NULL);

  if (!playback_handle_) {
    Reset();
    return false;
  }

  return true;
}

void PulseAudioOutputStream::Reset() {
  stream_stopped_ = true;

  // Close the stream.
  if (playback_handle_) {
    pa_stream_flush(playback_handle_, NULL, NULL);
    pa_stream_disconnect(playback_handle_);

    // Release PulseAudio structures.
    pa_stream_unref(playback_handle_);
    playback_handle_ = NULL;
  }
  if (pa_context_) {
    pa_context_unref(pa_context_);
    pa_context_ = NULL;
  }
  if (pa_mainloop_) {
    pa_mainloop_free(pa_mainloop_);
    pa_mainloop_ = NULL;
  }

  // Release internal buffer.
  client_buffer_.reset();
}

void PulseAudioOutputStream::Close() {
  DCHECK_EQ(manager_->GetMessageLoop(), MessageLoop::current());

  Reset();

  // Signal to the manager that we're closed and can be removed.
  // This should be the last call in the function as it deletes "this".
  manager_->ReleaseOutputStream(this);
}

void PulseAudioOutputStream::WaitForWriteRequest() {
  DCHECK_EQ(manager_->GetMessageLoop(), MessageLoop::current());

  if (stream_stopped_)
    return;

  // Iterate the PulseAudio mainloop.  If PulseAudio doesn't request a write,
  // post a task to iterate the mainloop again.
  write_callback_handled_ = false;
  pa_mainloop_iterate(pa_mainloop_, 1, NULL);
  if (!write_callback_handled_) {
    manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
        &PulseAudioOutputStream::WaitForWriteRequest,
        weak_factory_.GetWeakPtr()));
  }
}

bool PulseAudioOutputStream::BufferPacketFromSource() {
  uint32 buffer_delay = client_buffer_->forward_bytes();
  pa_usec_t pa_latency_micros;
  int negative;
  pa_stream_get_latency(playback_handle_, &pa_latency_micros, &negative);
  uint32 hardware_delay = MicrosecondsToBytes(pa_latency_micros,
                                              sample_rate_,
                                              bytes_per_frame_);
  // TODO(slock): Deal with negative latency (negative == 1).  This has yet
  // to happen in practice though.
  scoped_refptr<media::DataBuffer> packet =
      new media::DataBuffer(packet_size_);
  size_t packet_size = RunDataCallback(packet->GetWritableData(),
                                       packet->GetBufferSize(),
                                       AudioBuffersState(buffer_delay,
                                                         hardware_delay));

  if (packet_size == 0)
    return false;

  media::AdjustVolume(packet->GetWritableData(),
                      packet_size,
                      channel_count_,
                      bytes_per_frame_ / channel_count_,
                      volume_);
  packet->SetDataSize(packet_size);
  // Add the packet to the buffer.
  client_buffer_->Append(packet);
  return true;
}

void PulseAudioOutputStream::FulfillWriteRequest(size_t requested_bytes) {
  // If we have enough data to fulfill the request, we can finish the write.
  if (stream_stopped_)
    return;

  // Request more data from the source until we can fulfill the request or
  // fail to receive anymore data.
  bool buffering_successful = true;
  while (client_buffer_->forward_bytes() < requested_bytes &&
         buffering_successful) {
    buffering_successful = BufferPacketFromSource();
  }

  size_t bytes_written = 0;
  if (client_buffer_->forward_bytes() > 0) {
    // Try to fulfill the request by writing as many of the requested bytes to
    // the stream as we can.
    WriteToStream(requested_bytes, &bytes_written);
  }

  if (bytes_written < requested_bytes) {
    // We weren't able to buffer enough data to fulfill the request.  Try to
    // fulfill the rest of the request later.
    manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
        &PulseAudioOutputStream::FulfillWriteRequest,
        weak_factory_.GetWeakPtr(),
        requested_bytes - bytes_written));
  } else {
    // Continue playback.
    manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
        &PulseAudioOutputStream::WaitForWriteRequest,
        weak_factory_.GetWeakPtr()));
  }
}

void PulseAudioOutputStream::WriteToStream(size_t bytes_to_write,
                                           size_t* bytes_written) {
  *bytes_written = 0;
  while (*bytes_written < bytes_to_write) {
    const uint8* chunk;
    size_t chunk_size;

    // Stop writing if there is no more data available.
    if (!client_buffer_->GetCurrentChunk(&chunk, &chunk_size))
      break;

    // Write data to stream.
    pa_stream_write(playback_handle_, chunk, chunk_size,
                    NULL, 0LL, PA_SEEK_RELATIVE);
    client_buffer_->Seek(chunk_size);
    *bytes_written += chunk_size;
  }
}

void PulseAudioOutputStream::Start(AudioSourceCallback* callback) {
  DCHECK_EQ(manager_->GetMessageLoop(), MessageLoop::current());
  CHECK(callback);
  DLOG_IF(ERROR, !playback_handle_)
      << "Open() has not been called successfully";
  if (!playback_handle_)
    return;

  source_callback_ = callback;

  // Clear buffer, it might still have data in it.
  client_buffer_->Clear();
  stream_stopped_ = false;

  // Start playback.
  manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
      &PulseAudioOutputStream::WaitForWriteRequest,
      weak_factory_.GetWeakPtr()));
}

void PulseAudioOutputStream::Stop() {
  DCHECK_EQ(manager_->GetMessageLoop(), MessageLoop::current());

  stream_stopped_ = true;
}

void PulseAudioOutputStream::SetVolume(double volume) {
  DCHECK_EQ(manager_->GetMessageLoop(), MessageLoop::current());

  volume_ = static_cast<float>(volume);
}

void PulseAudioOutputStream::GetVolume(double* volume) {
  DCHECK_EQ(manager_->GetMessageLoop(), MessageLoop::current());

  *volume = volume_;
}

uint32 PulseAudioOutputStream::RunDataCallback(
    uint8* dest, uint32 max_size, AudioBuffersState buffers_state) {
  if (source_callback_)
    return source_callback_->OnMoreData(dest, max_size, buffers_state);

  return 0;
}

}  // namespace media
<commit_msg>Fix PulseAudio compile failures when use_pulseaudio=1.<commit_after>// Copyright (c) 2012 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/pulse/pulse_output.h"

#include "base/bind.h"
#include "base/message_loop.h"
#include "media/audio/audio_parameters.h"
#include "media/audio/audio_util.h"
#if defined(OS_LINUX)
#include "media/audio/linux/audio_manager_linux.h"
#elif defined(OS_OPENBSD)
#include "media/audio/openbsd/audio_manager_openbsd.h"
#endif
#include "media/base/data_buffer.h"
#include "media/base/seekable_buffer.h"

namespace media {

static pa_sample_format_t BitsToPASampleFormat(int bits_per_sample) {
  switch (bits_per_sample) {
    // Unsupported sample formats shown for reference.  I am assuming we want
    // signed and little endian because that is what we gave to ALSA.
    case 8:
      return PA_SAMPLE_U8;
      // Also 8-bits: PA_SAMPLE_ALAW and PA_SAMPLE_ULAW
    case 16:
      return PA_SAMPLE_S16LE;
      // Also 16-bits: PA_SAMPLE_S16BE (big endian).
    case 24:
      return PA_SAMPLE_S24LE;
      // Also 24-bits: PA_SAMPLE_S24BE (big endian).
      // Other cases: PA_SAMPLE_24_32LE (in LSB of 32-bit field, little endian),
      //          and PA_SAMPLE_24_32BE (in LSB of 32-bit field, big endian),
    case 32:
      return PA_SAMPLE_S32LE;
      // Also 32-bits: PA_SAMPLE_S32BE (big endian),
      //               PA_SAMPLE_FLOAT32LE (floating point little endian),
      //           and PA_SAMPLE_FLOAT32BE (floating point big endian).
    default:
      return PA_SAMPLE_INVALID;
  }
}

static pa_channel_position ChromiumToPAChannelPosition(Channels channel) {
  switch (channel) {
    // PulseAudio does not differentiate between left/right and
    // stereo-left/stereo-right, both translate to front-left/front-right.
    case LEFT:
    case STEREO_LEFT:
      return PA_CHANNEL_POSITION_FRONT_LEFT;
    case RIGHT:
    case STEREO_RIGHT:
      return PA_CHANNEL_POSITION_FRONT_RIGHT;
    case CENTER:
      return PA_CHANNEL_POSITION_FRONT_CENTER;
    case LFE:
      return PA_CHANNEL_POSITION_LFE;
    case BACK_LEFT:
      return PA_CHANNEL_POSITION_REAR_LEFT;
    case BACK_RIGHT:
      return PA_CHANNEL_POSITION_REAR_RIGHT;
    case LEFT_OF_CENTER:
      return PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
    case RIGHT_OF_CENTER:
      return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
    case BACK_CENTER:
      return PA_CHANNEL_POSITION_REAR_CENTER;
    case SIDE_LEFT:
      return PA_CHANNEL_POSITION_SIDE_LEFT;
    case SIDE_RIGHT:
      return PA_CHANNEL_POSITION_SIDE_RIGHT;
    case CHANNELS_MAX:
      return PA_CHANNEL_POSITION_INVALID;
  }
  NOTREACHED() << "Invalid channel " << channel;
  return PA_CHANNEL_POSITION_INVALID;
}

static pa_channel_map ChannelLayoutToPAChannelMap(
    ChannelLayout channel_layout) {
  // Initialize channel map.
  pa_channel_map channel_map;
  pa_channel_map_init(&channel_map);

  channel_map.channels = ChannelLayoutToChannelCount(channel_layout);

  // All channel maps have the same size array of channel positions.
  for (unsigned int channel = 0; channel != CHANNELS_MAX; ++channel) {
    int channel_position = kChannelOrderings[channel_layout][channel];
    if (channel_position > -1) {
      channel_map.map[channel_position] = ChromiumToPAChannelPosition(
          static_cast<Channels>(channel));
    } else {
      // PulseAudio expects unused channels in channel maps to be filled with
      // PA_CHANNEL_POSITION_MONO.
      channel_map.map[channel_position] = PA_CHANNEL_POSITION_MONO;
    }
  }

  // Fill in the rest of the unused channels.
  for (unsigned int channel = CHANNELS_MAX; channel != PA_CHANNELS_MAX;
       ++channel) {
    channel_map.map[channel] = PA_CHANNEL_POSITION_MONO;
  }

  return channel_map;
}

static size_t MicrosecondsToBytes(
    uint32 microseconds, uint32 sample_rate, size_t bytes_per_frame) {
  return microseconds * sample_rate * bytes_per_frame /
      base::Time::kMicrosecondsPerSecond;
}

// static
void PulseAudioOutputStream::ContextStateCallback(pa_context* context,
                                                  void* state_addr) {
  pa_context_state_t* state = static_cast<pa_context_state_t*>(state_addr);
  *state = pa_context_get_state(context);
}

// static
void PulseAudioOutputStream::WriteRequestCallback(pa_stream* playback_handle,
                                                  size_t length,
                                                  void* stream_addr) {
  PulseAudioOutputStream* stream =
      reinterpret_cast<PulseAudioOutputStream*>(stream_addr);

  DCHECK(stream->manager_->GetMessageLoop()->BelongsToCurrentThread());

  stream->write_callback_handled_ = true;

  // Fulfill write request.
  stream->FulfillWriteRequest(length);
}

PulseAudioOutputStream::PulseAudioOutputStream(const AudioParameters& params,
                                               AudioManagerPulse* manager)
    : channel_layout_(params.channel_layout()),
      channel_count_(ChannelLayoutToChannelCount(channel_layout_)),
      sample_format_(BitsToPASampleFormat(params.bits_per_sample())),
      sample_rate_(params.sample_rate()),
      bytes_per_frame_(params.GetBytesPerFrame()),
      manager_(manager),
      pa_context_(NULL),
      pa_mainloop_(NULL),
      playback_handle_(NULL),
      packet_size_(params.GetBytesPerBuffer()),
      frames_per_packet_(packet_size_ / bytes_per_frame_),
      client_buffer_(NULL),
      volume_(1.0f),
      stream_stopped_(true),
      write_callback_handled_(false),
      ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
      source_callback_(NULL) {
  DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());

  // TODO(slock): Sanity check input values.
}

PulseAudioOutputStream::~PulseAudioOutputStream() {
  // All internal structures should already have been freed in Close(),
  // which calls AudioManagerPulse::Release which deletes this object.
  DCHECK(!playback_handle_);
  DCHECK(!pa_context_);
  DCHECK(!pa_mainloop_);
}

bool PulseAudioOutputStream::Open() {
  DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());

  // TODO(slock): Possibly move most of this to an OpenPlaybackDevice function
  // in a new class 'pulse_util', like alsa_util.

  // Create a mainloop API and connect to the default server.
  pa_mainloop_ = pa_mainloop_new();
  pa_mainloop_api* pa_mainloop_api = pa_mainloop_get_api(pa_mainloop_);
  pa_context_ = pa_context_new(pa_mainloop_api, "Chromium");
  pa_context_state_t pa_context_state = PA_CONTEXT_UNCONNECTED;
  pa_context_connect(pa_context_, NULL, PA_CONTEXT_NOFLAGS, NULL);

  // Wait until PulseAudio is ready.
  pa_context_set_state_callback(pa_context_, &ContextStateCallback,
                                &pa_context_state);
  while (pa_context_state != PA_CONTEXT_READY) {
    pa_mainloop_iterate(pa_mainloop_, 1, NULL);
    if (pa_context_state == PA_CONTEXT_FAILED ||
        pa_context_state == PA_CONTEXT_TERMINATED) {
      Reset();
      return false;
    }
  }

  // Set sample specifications.
  pa_sample_spec pa_sample_specifications;
  pa_sample_specifications.format = sample_format_;
  pa_sample_specifications.rate = sample_rate_;
  pa_sample_specifications.channels = channel_count_;

  // Get channel mapping and open playback stream.
  pa_channel_map* map = NULL;
  pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap(
      channel_layout_);
  if (source_channel_map.channels != 0) {
    // The source data uses a supported channel map so we will use it rather
    // than the default channel map (NULL).
    map = &source_channel_map;
  }
  playback_handle_ = pa_stream_new(pa_context_, "Playback",
                                   &pa_sample_specifications, map);

  // Initialize client buffer.
  uint32 output_packet_size = frames_per_packet_ * bytes_per_frame_;
  client_buffer_.reset(new media::SeekableBuffer(0, output_packet_size));

  // Set write callback.
  pa_stream_set_write_callback(playback_handle_, &WriteRequestCallback, this);

  // Set server-side buffer attributes.
  // (uint32_t)-1 is the default and recommended value from PulseAudio's
  // documentation, found at:
  // http://freedesktop.org/software/pulseaudio/doxygen/structpa__buffer__attr.html.
  pa_buffer_attr pa_buffer_attributes;
  pa_buffer_attributes.maxlength = static_cast<uint32_t>(-1);
  pa_buffer_attributes.tlength = output_packet_size;
  pa_buffer_attributes.prebuf = static_cast<uint32_t>(-1);
  pa_buffer_attributes.minreq = static_cast<uint32_t>(-1);
  pa_buffer_attributes.fragsize = static_cast<uint32_t>(-1);

  // Connect playback stream.
  pa_stream_connect_playback(playback_handle_, NULL,
                             &pa_buffer_attributes,
                             (pa_stream_flags_t)
                             (PA_STREAM_INTERPOLATE_TIMING |
                             PA_STREAM_ADJUST_LATENCY |
                             PA_STREAM_AUTO_TIMING_UPDATE),
                             NULL, NULL);

  if (!playback_handle_) {
    Reset();
    return false;
  }

  return true;
}

void PulseAudioOutputStream::Reset() {
  stream_stopped_ = true;

  // Close the stream.
  if (playback_handle_) {
    pa_stream_flush(playback_handle_, NULL, NULL);
    pa_stream_disconnect(playback_handle_);

    // Release PulseAudio structures.
    pa_stream_unref(playback_handle_);
    playback_handle_ = NULL;
  }
  if (pa_context_) {
    pa_context_unref(pa_context_);
    pa_context_ = NULL;
  }
  if (pa_mainloop_) {
    pa_mainloop_free(pa_mainloop_);
    pa_mainloop_ = NULL;
  }

  // Release internal buffer.
  client_buffer_.reset();
}

void PulseAudioOutputStream::Close() {
  DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());

  Reset();

  // Signal to the manager that we're closed and can be removed.
  // This should be the last call in the function as it deletes "this".
  manager_->ReleaseOutputStream(this);
}

void PulseAudioOutputStream::WaitForWriteRequest() {
  DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());

  if (stream_stopped_)
    return;

  // Iterate the PulseAudio mainloop.  If PulseAudio doesn't request a write,
  // post a task to iterate the mainloop again.
  write_callback_handled_ = false;
  pa_mainloop_iterate(pa_mainloop_, 1, NULL);
  if (!write_callback_handled_) {
    manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
        &PulseAudioOutputStream::WaitForWriteRequest,
        weak_factory_.GetWeakPtr()));
  }
}

bool PulseAudioOutputStream::BufferPacketFromSource() {
  uint32 buffer_delay = client_buffer_->forward_bytes();
  pa_usec_t pa_latency_micros;
  int negative;
  pa_stream_get_latency(playback_handle_, &pa_latency_micros, &negative);
  uint32 hardware_delay = MicrosecondsToBytes(pa_latency_micros,
                                              sample_rate_,
                                              bytes_per_frame_);
  // TODO(slock): Deal with negative latency (negative == 1).  This has yet
  // to happen in practice though.
  scoped_refptr<media::DataBuffer> packet =
      new media::DataBuffer(packet_size_);
  size_t packet_size = RunDataCallback(packet->GetWritableData(),
                                       packet->GetBufferSize(),
                                       AudioBuffersState(buffer_delay,
                                                         hardware_delay));

  if (packet_size == 0)
    return false;

  media::AdjustVolume(packet->GetWritableData(),
                      packet_size,
                      channel_count_,
                      bytes_per_frame_ / channel_count_,
                      volume_);
  packet->SetDataSize(packet_size);
  // Add the packet to the buffer.
  client_buffer_->Append(packet);
  return true;
}

void PulseAudioOutputStream::FulfillWriteRequest(size_t requested_bytes) {
  // If we have enough data to fulfill the request, we can finish the write.
  if (stream_stopped_)
    return;

  // Request more data from the source until we can fulfill the request or
  // fail to receive anymore data.
  bool buffering_successful = true;
  size_t forward_bytes = static_cast<size_t>(client_buffer_->forward_bytes());
  while (forward_bytes < requested_bytes && buffering_successful) {
    buffering_successful = BufferPacketFromSource();
  }

  size_t bytes_written = 0;
  if (client_buffer_->forward_bytes() > 0) {
    // Try to fulfill the request by writing as many of the requested bytes to
    // the stream as we can.
    WriteToStream(requested_bytes, &bytes_written);
  }

  if (bytes_written < requested_bytes) {
    // We weren't able to buffer enough data to fulfill the request.  Try to
    // fulfill the rest of the request later.
    manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
        &PulseAudioOutputStream::FulfillWriteRequest,
        weak_factory_.GetWeakPtr(),
        requested_bytes - bytes_written));
  } else {
    // Continue playback.
    manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
        &PulseAudioOutputStream::WaitForWriteRequest,
        weak_factory_.GetWeakPtr()));
  }
}

void PulseAudioOutputStream::WriteToStream(size_t bytes_to_write,
                                           size_t* bytes_written) {
  *bytes_written = 0;
  while (*bytes_written < bytes_to_write) {
    const uint8* chunk;
    int chunk_size;

    // Stop writing if there is no more data available.
    if (!client_buffer_->GetCurrentChunk(&chunk, &chunk_size))
      break;

    // Write data to stream.
    pa_stream_write(playback_handle_, chunk, chunk_size,
                    NULL, 0LL, PA_SEEK_RELATIVE);
    client_buffer_->Seek(chunk_size);
    *bytes_written += chunk_size;
  }
}

void PulseAudioOutputStream::Start(AudioSourceCallback* callback) {
  DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());
  CHECK(callback);
  DLOG_IF(ERROR, !playback_handle_)
      << "Open() has not been called successfully";
  if (!playback_handle_)
    return;

  source_callback_ = callback;

  // Clear buffer, it might still have data in it.
  client_buffer_->Clear();
  stream_stopped_ = false;

  // Start playback.
  manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
      &PulseAudioOutputStream::WaitForWriteRequest,
      weak_factory_.GetWeakPtr()));
}

void PulseAudioOutputStream::Stop() {
  DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());

  stream_stopped_ = true;
}

void PulseAudioOutputStream::SetVolume(double volume) {
  DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());

  volume_ = static_cast<float>(volume);
}

void PulseAudioOutputStream::GetVolume(double* volume) {
  DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());

  *volume = volume_;
}

uint32 PulseAudioOutputStream::RunDataCallback(
    uint8* dest, uint32 max_size, AudioBuffersState buffers_state) {
  if (source_callback_)
    return source_callback_->OnMoreData(dest, max_size, buffers_state);

  return 0;
}

}  // namespace media
<|endoftext|>


<commit_before>//
// Copyright (c) 2009 Advanced Micro Devices, Inc. All rights reserved.
//
#pragma once

#include <memory>
#include "acl.h"
#include "rocprogram.hpp"
#include "top.hpp"
#include "rocprintf.hpp"

#ifndef WITHOUT_HSA_BACKEND

namespace roc {

#define MAX_INFO_STRING_LEN 0x40

enum ROC_ARG_TYPE {
  ROC_ARGTYPE_ERROR = 0,
  ROC_ARGTYPE_POINTER,
  ROC_ARGTYPE_VALUE,
  ROC_ARGTYPE_REFERENCE,
  ROC_ARGTYPE_IMAGE,
  ROC_ARGTYPE_SAMPLER,
  ROC_ARGTYPE_HIDDEN_GLOBAL_OFFSET_X,
  ROC_ARGTYPE_HIDDEN_GLOBAL_OFFSET_Y,
  ROC_ARGTYPE_HIDDEN_GLOBAL_OFFSET_Z,
  ROC_ARGTYPE_HIDDEN_PRINTF_BUFFER,
  ROC_ARGTYPE_HIDDEN_DEFAULT_QUEUE,
  ROC_ARGTYPE_HIDDEN_COMPLETION_ACTION,
  ROC_ARGTYPE_HIDDEN_NONE,
  ROC_ARGMAX_ARG_TYPES
};

enum ROC_ADDRESS_QUALIFIER {
  ROC_ADDRESS_ERROR = 0,
  ROC_ADDRESS_GLOBAL,
  ROC_ADDRESS_CONSTANT,
  ROC_ADDRESS_LOCAL,
  ROC_MAX_ADDRESS_QUALIFIERS
};

enum ROC_DATA_TYPE {
  ROC_DATATYPE_ERROR = 0,
  ROC_DATATYPE_B1,
  ROC_DATATYPE_B8,
  ROC_DATATYPE_B16,
  ROC_DATATYPE_B32,
  ROC_DATATYPE_B64,
  ROC_DATATYPE_S8,
  ROC_DATATYPE_S16,
  ROC_DATATYPE_S32,
  ROC_DATATYPE_S64,
  ROC_DATATYPE_U8,
  ROC_DATATYPE_U16,
  ROC_DATATYPE_U32,
  ROC_DATATYPE_U64,
  ROC_DATATYPE_F16,
  ROC_DATATYPE_F32,
  ROC_DATATYPE_F64,
  ROC_DATATYPE_STRUCT,
  ROC_DATATYPE_OPAQUE,
  ROC_DATATYPE_MAX_TYPES
};

enum ROC_ACCESS_TYPE {
  ROC_ACCESS_TYPE_NONE = 0,
  ROC_ACCESS_TYPE_RO,
  ROC_ACCESS_TYPE_WO,
  ROC_ACCESS_TYPE_RW
};

class Kernel : public device::Kernel {
 public:
  struct Argument {
    uint index_;                      //!< Argument's index in the OCL signature
    std::string name_;                //!< Argument's name
    std::string typeName_;            //!< Argument's type name
    uint size_;                       //!< Size in bytes
    uint alignment_;                  //!< Argument's alignment
    uint pointeeAlignment_;           //!< Alignment of the data pointed to
    ROC_ARG_TYPE type_;               //!< Type of the argument
    ROC_ADDRESS_QUALIFIER addrQual_;  //!< Address qualifier of the argument
    ROC_DATA_TYPE dataType_;          //!< The type of data
    ROC_ACCESS_TYPE access_;          //!< Access type for the argument
  };

  Kernel(std::string name, Program* prog, const uint64_t& kernelCodeHandle,
         const uint32_t workgroupGroupSegmentByteSize,
         const uint32_t workitemPrivateSegmentByteSize, const uint32_t kernargSegmentByteSize,
         const uint32_t kernargSegmentAlignment);

  const uint64_t& KernelCodeHandle() { return kernelCodeHandle_; }

  const uint32_t WorkgroupGroupSegmentByteSize() const { return workgroupGroupSegmentByteSize_; }

  const uint32_t workitemPrivateSegmentByteSize() const { return workitemPrivateSegmentByteSize_; }

  const uint64_t KernargSegmentByteSize() const { return kernargSegmentByteSize_ + 48; }

  const uint8_t KernargSegmentAlignment() const { return kernargSegmentAlignment_; }

  ~Kernel();

  //! Initializes the metadata required for this kernel
  virtual bool init() = 0;

  const Program* program() const { return static_cast<const Program*>(program_); }

  //! Returns the kernel argument list
  const std::vector<Argument*>& hsailArgs() const { return hsailArgList_; }

  //! Returns a pointer to the hsail argument at the specified index
  Argument* hsailArgAt(size_t index) const {
    for (auto arg : hsailArgList_)
      if (arg->index_ == index) return arg;
    assert(!"Should not reach here");
    return nullptr;
  }

  //! Return printf info array
  const std::vector<PrintfInfo>& printfInfo() const { return printf_; }

  //! Return TRUE if kernel is internal blit kernel
  bool isInternalKernel() const { return (flags_.internalKernel_) ? true : false; }

  //! set internal kernel flag
  void setInternalKernelFlag(bool flag) { flags_.internalKernel_ = flag; }

  //! Return TRUE if kernel uses images
  bool imageEnable() const { return (flags_.imageEnable_) ? true : false; }

  //! Return TRUE if kernel wirtes images
  bool imageWrite() const { return (flags_.imageWrite_) ? true : false; }

 protected:
  union Flags {
    struct {
      uint internalKernel_ : 1;  //!< Is a blit kernel?
      uint imageEnable_    : 1;  //!< Kernel uses images
      uint imageWrite_     : 1;  //!< Kernel writes images
    };
    uint value_;
    Flags() : value_(0) {}
  } flags_;

  Program* program_;                //!< The roc::Program context
  std::vector<Argument*> hsailArgList_;  //!< Vector list of HSAIL Arguments
  uint64_t kernelCodeHandle_;            //!< Kernel code handle (aka amd_kernel_code_t)
  const uint32_t workgroupGroupSegmentByteSize_;
  const uint32_t workitemPrivateSegmentByteSize_;
  const uint32_t kernargSegmentByteSize_;
  const uint32_t kernargSegmentAlignment_;
  size_t kernelDirectiveOffset_;
  std::vector<PrintfInfo> printf_;
};

#if defined(WITH_COMPILER_LIB)
class HSAILKernel : public roc::Kernel {
 public:
  HSAILKernel(std::string name, Program* prog, const uint64_t& kernelCodeHandle,
              const uint32_t workgroupGroupSegmentByteSize,
              const uint32_t workitemPrivateSegmentByteSize,
              const uint32_t kernargSegmentByteSize,
              const uint32_t kernargSegmentAlignment)
   : roc::Kernel(name, prog, kernelCodeHandle, workgroupGroupSegmentByteSize,
                 workitemPrivateSegmentByteSize, kernargSegmentByteSize, kernargSegmentAlignment) {
  }

  //! Initializes the metadata required for this kernel
  virtual bool init() final;

 private:
  //! Populates hsailArgList_
  void initArguments(const aclArgData* aclArg);

  //! Initializes HSAIL Printf metadata and info
  void initPrintf(const aclPrintfFmt* aclPrintf);
};
#endif // defined(WITH_COMPILER_LIB)

#if defined(WITH_LIGHTNING_COMPILER)
class LightningKernel : public roc::Kernel {
 public:
  LightningKernel(std::string name, Program* prog, const uint64_t& kernelCodeHandle,
                  const uint32_t workgroupGroupSegmentByteSize,
                  const uint32_t workitemPrivateSegmentByteSize,
                  const uint32_t kernargSegmentByteSize,
                  const uint32_t kernargSegmentAlignment)
   : roc::Kernel(name, prog, kernelCodeHandle, workgroupGroupSegmentByteSize,
                 workitemPrivateSegmentByteSize, kernargSegmentByteSize, kernargSegmentAlignment) {
  }
  //! Initializes the metadata required for this kernel
  virtual bool init() final;

private:
  //! Initializes Hsail Argument metadata and info for LC
  void initArguments(const KernelMD& kernelMD);

  //! Initializes HSAIL Printf metadata and info for LC
  void initPrintf(const std::vector<std::string>& printfInfoStrings);
};
#endif // defined(WITH_LIGHTNING_COMPILER)

}  // namespace roc

#endif  // WITHOUT_HSA_BACKEND
<commit_msg>P4 to Git Change 1549971 by skudchad@skudchad_rocm on 2018/05/03 18:39:37<commit_after>//
// Copyright (c) 2009 Advanced Micro Devices, Inc. All rights reserved.
//
#pragma once

#include <memory>
#include "acl.h"
#include "rocprogram.hpp"
#include "top.hpp"
#include "rocprintf.hpp"

#ifndef WITHOUT_HSA_BACKEND

namespace roc {

#define MAX_INFO_STRING_LEN 0x40

enum ROC_ARG_TYPE {
  ROC_ARGTYPE_ERROR = 0,
  ROC_ARGTYPE_POINTER,
  ROC_ARGTYPE_VALUE,
  ROC_ARGTYPE_REFERENCE,
  ROC_ARGTYPE_IMAGE,
  ROC_ARGTYPE_SAMPLER,
  ROC_ARGTYPE_HIDDEN_GLOBAL_OFFSET_X,
  ROC_ARGTYPE_HIDDEN_GLOBAL_OFFSET_Y,
  ROC_ARGTYPE_HIDDEN_GLOBAL_OFFSET_Z,
  ROC_ARGTYPE_HIDDEN_PRINTF_BUFFER,
  ROC_ARGTYPE_HIDDEN_DEFAULT_QUEUE,
  ROC_ARGTYPE_HIDDEN_COMPLETION_ACTION,
  ROC_ARGTYPE_HIDDEN_NONE,
  ROC_ARGMAX_ARG_TYPES
};

enum ROC_ADDRESS_QUALIFIER {
  ROC_ADDRESS_ERROR = 0,
  ROC_ADDRESS_GLOBAL,
  ROC_ADDRESS_CONSTANT,
  ROC_ADDRESS_LOCAL,
  ROC_MAX_ADDRESS_QUALIFIERS
};

enum ROC_DATA_TYPE {
  ROC_DATATYPE_ERROR = 0,
  ROC_DATATYPE_B1,
  ROC_DATATYPE_B8,
  ROC_DATATYPE_B16,
  ROC_DATATYPE_B32,
  ROC_DATATYPE_B64,
  ROC_DATATYPE_S8,
  ROC_DATATYPE_S16,
  ROC_DATATYPE_S32,
  ROC_DATATYPE_S64,
  ROC_DATATYPE_U8,
  ROC_DATATYPE_U16,
  ROC_DATATYPE_U32,
  ROC_DATATYPE_U64,
  ROC_DATATYPE_F16,
  ROC_DATATYPE_F32,
  ROC_DATATYPE_F64,
  ROC_DATATYPE_STRUCT,
  ROC_DATATYPE_OPAQUE,
  ROC_DATATYPE_MAX_TYPES
};

enum ROC_ACCESS_TYPE {
  ROC_ACCESS_TYPE_NONE = 0,
  ROC_ACCESS_TYPE_RO,
  ROC_ACCESS_TYPE_WO,
  ROC_ACCESS_TYPE_RW
};

class Kernel : public device::Kernel {
 public:
  struct Argument {
    uint index_;                      //!< Argument's index in the OCL signature
    std::string name_;                //!< Argument's name
    std::string typeName_;            //!< Argument's type name
    uint size_;                       //!< Size in bytes
    uint alignment_;                  //!< Argument's alignment
    uint pointeeAlignment_;           //!< Alignment of the data pointed to
    ROC_ARG_TYPE type_;               //!< Type of the argument
    ROC_ADDRESS_QUALIFIER addrQual_;  //!< Address qualifier of the argument
    ROC_DATA_TYPE dataType_;          //!< The type of data
    ROC_ACCESS_TYPE access_;          //!< Access type for the argument
  };

  Kernel(std::string name, Program* prog, const uint64_t& kernelCodeHandle,
         const uint32_t workgroupGroupSegmentByteSize,
         const uint32_t workitemPrivateSegmentByteSize, const uint32_t kernargSegmentByteSize,
         const uint32_t kernargSegmentAlignment);

  const uint64_t& KernelCodeHandle() { return kernelCodeHandle_; }

  const uint32_t WorkgroupGroupSegmentByteSize() const { return workgroupGroupSegmentByteSize_; }

  const uint32_t workitemPrivateSegmentByteSize() const { return workitemPrivateSegmentByteSize_; }

  const uint64_t KernargSegmentByteSize() const { return kernargSegmentByteSize_; }

  const uint8_t KernargSegmentAlignment() const { return kernargSegmentAlignment_; }

  ~Kernel();

  //! Initializes the metadata required for this kernel
  virtual bool init() = 0;

  const Program* program() const { return static_cast<const Program*>(program_); }

  //! Returns the kernel argument list
  const std::vector<Argument*>& hsailArgs() const { return hsailArgList_; }

  //! Returns a pointer to the hsail argument at the specified index
  Argument* hsailArgAt(size_t index) const {
    for (auto arg : hsailArgList_)
      if (arg->index_ == index) return arg;
    assert(!"Should not reach here");
    return nullptr;
  }

  //! Return printf info array
  const std::vector<PrintfInfo>& printfInfo() const { return printf_; }

  //! Return TRUE if kernel is internal blit kernel
  bool isInternalKernel() const { return (flags_.internalKernel_) ? true : false; }

  //! set internal kernel flag
  void setInternalKernelFlag(bool flag) { flags_.internalKernel_ = flag; }

  //! Return TRUE if kernel uses images
  bool imageEnable() const { return (flags_.imageEnable_) ? true : false; }

  //! Return TRUE if kernel wirtes images
  bool imageWrite() const { return (flags_.imageWrite_) ? true : false; }

 protected:
  union Flags {
    struct {
      uint internalKernel_ : 1;  //!< Is a blit kernel?
      uint imageEnable_    : 1;  //!< Kernel uses images
      uint imageWrite_     : 1;  //!< Kernel writes images
    };
    uint value_;
    Flags() : value_(0) {}
  } flags_;

  Program* program_;                //!< The roc::Program context
  std::vector<Argument*> hsailArgList_;  //!< Vector list of HSAIL Arguments
  uint64_t kernelCodeHandle_;            //!< Kernel code handle (aka amd_kernel_code_t)
  const uint32_t workgroupGroupSegmentByteSize_;
  const uint32_t workitemPrivateSegmentByteSize_;
  const uint32_t kernargSegmentByteSize_;
  const uint32_t kernargSegmentAlignment_;
  size_t kernelDirectiveOffset_;
  std::vector<PrintfInfo> printf_;
};

#if defined(WITH_COMPILER_LIB)
class HSAILKernel : public roc::Kernel {
 public:
  HSAILKernel(std::string name, Program* prog, const uint64_t& kernelCodeHandle,
              const uint32_t workgroupGroupSegmentByteSize,
              const uint32_t workitemPrivateSegmentByteSize,
              const uint32_t kernargSegmentByteSize,
              const uint32_t kernargSegmentAlignment)
   : roc::Kernel(name, prog, kernelCodeHandle, workgroupGroupSegmentByteSize,
                 workitemPrivateSegmentByteSize, kernargSegmentByteSize, kernargSegmentAlignment) {
  }

  //! Initializes the metadata required for this kernel
  virtual bool init() final;

 private:
  //! Populates hsailArgList_
  void initArguments(const aclArgData* aclArg);

  //! Initializes HSAIL Printf metadata and info
  void initPrintf(const aclPrintfFmt* aclPrintf);
};
#endif // defined(WITH_COMPILER_LIB)

#if defined(WITH_LIGHTNING_COMPILER)
class LightningKernel : public roc::Kernel {
 public:
  LightningKernel(std::string name, Program* prog, const uint64_t& kernelCodeHandle,
                  const uint32_t workgroupGroupSegmentByteSize,
                  const uint32_t workitemPrivateSegmentByteSize,
                  const uint32_t kernargSegmentByteSize,
                  const uint32_t kernargSegmentAlignment)
   : roc::Kernel(name, prog, kernelCodeHandle, workgroupGroupSegmentByteSize,
                 workitemPrivateSegmentByteSize, kernargSegmentByteSize, kernargSegmentAlignment) {
  }
  //! Initializes the metadata required for this kernel
  virtual bool init() final;

private:
  //! Initializes Hsail Argument metadata and info for LC
  void initArguments(const KernelMD& kernelMD);

  //! Initializes HSAIL Printf metadata and info for LC
  void initPrintf(const std::vector<std::string>& printfInfoStrings);
};
#endif // defined(WITH_LIGHTNING_COMPILER)

}  // namespace roc

#endif  // WITHOUT_HSA_BACKEND
<|endoftext|>


<commit_before>
#include "UnrealEnginePythonPrivatePCH.h"

#include "UEPySImage.h"

#define sw_image StaticCastSharedRef<SImage>(self->s_leaf_widget.s_widget.s_widget)

static PyObject *py_ue_simage_set_brush(ue_PySImage *self, PyObject * args) {
	PyObject *py_brush;
	if (!PyArg_ParseTuple(args, "O:set_brush", &py_brush)) {
		return NULL;
	}

	FSlateBrush *brush = ue_py_check_struct<FSlateBrush>(py_brush);
	if (!brush)
		return PyErr_Format(PyExc_Exception, "argument is not a FSlateBrush");

	self->brush = *brush;

	sw_image->SetImage(&self->brush);

	Py_INCREF(self);
	return (PyObject *)self;
}

static PyObject *py_ue_simage_set_texture(ue_PySImage *self, PyObject * args) {
	PyObject *py_texture;
	PyObject *py_linear_color = nullptr;
	if (!PyArg_ParseTuple(args, "O|O:set_texture", &py_texture)) {
		return NULL;
	}

	UTexture2D *texture = ue_py_check_type<UTexture2D>(py_texture);
	if (!texture)
		return PyErr_Format(PyExc_Exception, "argument is not a Texture");

	FLinearColor tint(1, 1, 1, 1);

	if (py_linear_color) {
		ue_PyFLinearColor *py_color = py_ue_is_flinearcolor(py_linear_color);
		if (!py_color) {
			return PyErr_Format(PyExc_Exception, "argument is not a FLinearColor");
		}
		tint = py_color->color;
	}

	self->brush = FSlateImageBrush(texture, FVector2D(texture->GetSurfaceWidth(), texture->GetSurfaceHeight()), tint);

	sw_image->SetImage(&self->brush);

	Py_INCREF(self);
	return (PyObject *)self;
}

static PyMethodDef ue_PySImage_methods[] = {
	{ "set_brush", (PyCFunction)py_ue_simage_set_brush, METH_VARARGS, "" },
	{ "set_image", (PyCFunction)py_ue_simage_set_brush, METH_VARARGS, "" },
	{ "set_texture", (PyCFunction)py_ue_simage_set_texture, METH_VARARGS, "" },
	{ NULL }  /* Sentinel */
};

PyTypeObject ue_PySImageType = {
	PyVarObject_HEAD_INIT(NULL, 0)
	"unreal_engine.SImage", /* tp_name */
	sizeof(ue_PySImage), /* tp_basicsize */
	0,                         /* tp_itemsize */
	0,       /* tp_dealloc */
	0,                         /* tp_print */
	0,                         /* tp_getattr */
	0,                         /* tp_setattr */
	0,                         /* tp_reserved */
	0,                         /* tp_repr */
	0,                         /* tp_as_number */
	0,                         /* tp_as_sequence */
	0,                         /* tp_as_mapping */
	0,                         /* tp_hash  */
	0,                         /* tp_call */
	0,                         /* tp_str */
	0,                         /* tp_getattro */
	0,                         /* tp_setattro */
	0,                         /* tp_as_buffer */
	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /* tp_flags */
	"Unreal Engine SImage",           /* tp_doc */
	0,                         /* tp_traverse */
	0,                         /* tp_clear */
	0,                         /* tp_richcompare */
	0,                         /* tp_weaklistoffset */
	0,                         /* tp_iter */
	0,                         /* tp_iternext */
	ue_PySImage_methods,             /* tp_methods */
};

static int ue_py_simage_init(ue_PySImage *self, PyObject *args, PyObject *kwargs) {
	ue_py_slate_setup_farguments(SImage);

	ue_py_slate_farguments_struct("color_and_opacity", ColorAndOpacity, FSlateColor);
	ue_py_slate_farguments_optional_struct_ptr("image", Image, FSlateBrush);
	ue_py_slate_farguments_event("on_mouse_button_down", OnMouseButtonDown, FPointerEventHandler, OnMouseEvent);

	ue_py_snew(SImage, s_leaf_widget.s_widget);
	return 0;
}

void ue_python_init_simage(PyObject *ue_module) {

	ue_PySImageType.tp_init = (initproc)ue_py_simage_init;

	ue_PySImageType.tp_base = &ue_PySLeafWidgetType;

	if (PyType_Ready(&ue_PySImageType) < 0)
		return;

	Py_INCREF(&ue_PySImageType);
	PyModule_AddObject(ue_module, "SImage", (PyObject *)&ue_PySImageType);
}
<commit_msg>fixed SImage set_texture<commit_after>
#include "UnrealEnginePythonPrivatePCH.h"

#include "UEPySImage.h"

#define sw_image StaticCastSharedRef<SImage>(self->s_leaf_widget.s_widget.s_widget)

static PyObject *py_ue_simage_set_brush(ue_PySImage *self, PyObject * args) {
	PyObject *py_brush;
	if (!PyArg_ParseTuple(args, "O:set_brush", &py_brush)) {
		return NULL;
	}

	FSlateBrush *brush = ue_py_check_struct<FSlateBrush>(py_brush);
	if (!brush)
		return PyErr_Format(PyExc_Exception, "argument is not a FSlateBrush");

	self->brush = *brush;

	sw_image->SetImage(&self->brush);

	Py_INCREF(self);
	return (PyObject *)self;
}

static PyObject *py_ue_simage_set_texture(ue_PySImage *self, PyObject * args) {
	PyObject *py_texture;
	PyObject *py_linear_color = nullptr;
	if (!PyArg_ParseTuple(args, "O|O:set_texture", &py_texture, &py_linear_color)) {
		return NULL;
	}

	UTexture2D *texture = ue_py_check_type<UTexture2D>(py_texture);
	if (!texture)
		return PyErr_Format(PyExc_Exception, "argument is not a Texture");

	FLinearColor tint(1, 1, 1, 1);

	if (py_linear_color) {
		ue_PyFLinearColor *py_color = py_ue_is_flinearcolor(py_linear_color);
		if (!py_color) {
			return PyErr_Format(PyExc_Exception, "argument is not a FLinearColor");
		}
		tint = py_color->color;
	}

	self->brush = FSlateImageBrush(texture, FVector2D(texture->GetSurfaceWidth(), texture->GetSurfaceHeight()), tint);

	sw_image->SetImage(&self->brush);

	Py_INCREF(self);
	return (PyObject *)self;
}

static PyMethodDef ue_PySImage_methods[] = {
	{ "set_brush", (PyCFunction)py_ue_simage_set_brush, METH_VARARGS, "" },
	{ "set_image", (PyCFunction)py_ue_simage_set_brush, METH_VARARGS, "" },
	{ "set_texture", (PyCFunction)py_ue_simage_set_texture, METH_VARARGS, "" },
	{ NULL }  /* Sentinel */
};

PyTypeObject ue_PySImageType = {
	PyVarObject_HEAD_INIT(NULL, 0)
	"unreal_engine.SImage", /* tp_name */
	sizeof(ue_PySImage), /* tp_basicsize */
	0,                         /* tp_itemsize */
	0,       /* tp_dealloc */
	0,                         /* tp_print */
	0,                         /* tp_getattr */
	0,                         /* tp_setattr */
	0,                         /* tp_reserved */
	0,                         /* tp_repr */
	0,                         /* tp_as_number */
	0,                         /* tp_as_sequence */
	0,                         /* tp_as_mapping */
	0,                         /* tp_hash  */
	0,                         /* tp_call */
	0,                         /* tp_str */
	0,                         /* tp_getattro */
	0,                         /* tp_setattro */
	0,                         /* tp_as_buffer */
	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /* tp_flags */
	"Unreal Engine SImage",           /* tp_doc */
	0,                         /* tp_traverse */
	0,                         /* tp_clear */
	0,                         /* tp_richcompare */
	0,                         /* tp_weaklistoffset */
	0,                         /* tp_iter */
	0,                         /* tp_iternext */
	ue_PySImage_methods,             /* tp_methods */
};

static int ue_py_simage_init(ue_PySImage *self, PyObject *args, PyObject *kwargs) {
	ue_py_slate_setup_farguments(SImage);

	ue_py_slate_farguments_struct("color_and_opacity", ColorAndOpacity, FSlateColor);
	ue_py_slate_farguments_optional_struct_ptr("image", Image, FSlateBrush);
	ue_py_slate_farguments_event("on_mouse_button_down", OnMouseButtonDown, FPointerEventHandler, OnMouseEvent);

	ue_py_snew(SImage, s_leaf_widget.s_widget);
	return 0;
}

void ue_python_init_simage(PyObject *ue_module) {

	ue_PySImageType.tp_init = (initproc)ue_py_simage_init;

	ue_PySImageType.tp_base = &ue_PySLeafWidgetType;

	if (PyType_Ready(&ue_PySImageType) < 0)
		return;

	Py_INCREF(&ue_PySImageType);
	PyModule_AddObject(ue_module, "SImage", (PyObject *)&ue_PySImageType);
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013 Google Inc. 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 Google Inc. 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 "config.h"
#include "bindings/v8/CustomElementConstructorBuilder.h"

#include "HTMLNames.h"
#include "SVGNames.h"
#include "V8Document.h"
#include "V8HTMLElementWrapperFactory.h"
#include "V8SVGElementWrapperFactory.h"
#include "bindings/v8/CustomElementBinding.h"
#include "bindings/v8/DOMWrapperWorld.h"
#include "bindings/v8/Dictionary.h"
#include "bindings/v8/ExceptionState.h"
#include "bindings/v8/UnsafePersistent.h"
#include "bindings/v8/V8Binding.h"
#include "bindings/v8/V8HiddenValue.h"
#include "bindings/v8/V8PerContextData.h"
#include "core/dom/Document.h"
#include "core/dom/custom/CustomElementCallbackDispatcher.h"
#include "core/dom/custom/CustomElementDefinition.h"
#include "core/dom/custom/CustomElementDescriptor.h"
#include "core/dom/custom/CustomElementException.h"
#include "wtf/Assertions.h"

namespace WebCore {

static void constructCustomElement(const v8::FunctionCallbackInfo<v8::Value>&);

CustomElementConstructorBuilder::CustomElementConstructorBuilder(NewScriptState* scriptState, const Dictionary* options)
    : m_scriptState(scriptState)
    , m_options(options)
    , m_wrapperType(0)
{
    ASSERT(m_scriptState->context() == v8::Isolate::GetCurrent()->GetCurrentContext());
}

bool CustomElementConstructorBuilder::isFeatureAllowed() const
{
    return m_scriptState->world().isMainWorld();
}

bool CustomElementConstructorBuilder::validateOptions(const AtomicString& type, QualifiedName& tagName, ExceptionState& exceptionState)
{
    ASSERT(m_prototype.IsEmpty());

    ScriptValue prototypeScriptValue;
    if (m_options->get("prototype", prototypeScriptValue) && !prototypeScriptValue.isNull()) {
        if (!prototypeScriptValue.isObject()) {
            CustomElementException::throwException(CustomElementException::PrototypeNotAnObject, type, exceptionState);
            return false;
        }
        m_prototype = prototypeScriptValue.v8Value().As<v8::Object>();
    } else {
        m_prototype = v8::Object::New(m_scriptState->isolate());
        v8::Local<v8::Object> basePrototype = m_scriptState->perContextData()->prototypeForType(&V8HTMLElement::wrapperTypeInfo);
        if (!basePrototype.IsEmpty())
            m_prototype->SetPrototype(basePrototype);
    }

    AtomicString extends;
    bool extendsProvidedAndNonNull = m_options->get("extends", extends);

    if (!m_scriptState->perContextData()) {
        // FIXME: This should generate an InvalidContext exception at a later point.
        CustomElementException::throwException(CustomElementException::ContextDestroyedCheckingPrototype, type, exceptionState);
        return false;
    }

    AtomicString namespaceURI = HTMLNames::xhtmlNamespaceURI;
    if (hasValidPrototypeChainFor(&V8SVGElement::wrapperTypeInfo))
        namespaceURI = SVGNames::svgNamespaceURI;

    AtomicString localName;

    if (extendsProvidedAndNonNull) {
        localName = extends.lower();

        if (!Document::isValidName(localName)) {
            CustomElementException::throwException(CustomElementException::ExtendsIsInvalidName, type, exceptionState);
            return false;
        }
        if (CustomElement::isValidName(localName)) {
            CustomElementException::throwException(CustomElementException::ExtendsIsCustomElementName, type, exceptionState);
            return false;
        }
    } else {
        if (namespaceURI == SVGNames::svgNamespaceURI) {
            CustomElementException::throwException(CustomElementException::ExtendsIsInvalidName, type, exceptionState);
            return false;
        }
        localName = type;
    }

    if (!extendsProvidedAndNonNull)
        m_wrapperType = &V8HTMLElement::wrapperTypeInfo;
    else if (namespaceURI == HTMLNames::xhtmlNamespaceURI)
        m_wrapperType = findWrapperTypeForHTMLTagName(localName);
    else
        m_wrapperType = findWrapperTypeForSVGTagName(localName);

    ASSERT(m_wrapperType);
    tagName = QualifiedName(nullAtom, localName, namespaceURI);
    return m_wrapperType;
}

PassRefPtr<CustomElementLifecycleCallbacks> CustomElementConstructorBuilder::createCallbacks()
{
    ASSERT(!m_prototype.IsEmpty());

    RefPtr<ExecutionContext> executionContext = m_scriptState->executionContext();

    v8::TryCatch exceptionCatcher;
    exceptionCatcher.SetVerbose(true);

    v8::Isolate* isolate = v8::Isolate::GetCurrent();
    v8::Handle<v8::Function> created = retrieveCallback(isolate, "createdCallback");
    v8::Handle<v8::Function> attached = retrieveCallback(isolate, "attachedCallback");
    v8::Handle<v8::Function> detached = retrieveCallback(isolate, "detachedCallback");
    v8::Handle<v8::Function> attributeChanged = retrieveCallback(isolate, "attributeChangedCallback");

    m_callbacks = V8CustomElementLifecycleCallbacks::create(executionContext.get(), m_prototype, created, attached, detached, attributeChanged);
    return m_callbacks.get();
}

v8::Handle<v8::Function> CustomElementConstructorBuilder::retrieveCallback(v8::Isolate* isolate, const char* name)
{
    v8::Handle<v8::Value> value = m_prototype->Get(v8String(isolate, name));
    if (value.IsEmpty() || !value->IsFunction())
        return v8::Handle<v8::Function>();
    return value.As<v8::Function>();
}

bool CustomElementConstructorBuilder::createConstructor(Document* document, CustomElementDefinition* definition, ExceptionState& exceptionState)
{
    ASSERT(!m_prototype.IsEmpty());
    ASSERT(m_constructor.IsEmpty());
    ASSERT(document);

    v8::Isolate* isolate = m_scriptState->isolate();

    if (!prototypeIsValid(definition->descriptor().type(), exceptionState))
        return false;

    v8::Local<v8::FunctionTemplate> constructorTemplate = v8::FunctionTemplate::New(isolate);
    constructorTemplate->SetCallHandler(constructCustomElement);
    m_constructor = constructorTemplate->GetFunction();
    if (m_constructor.IsEmpty()) {
        CustomElementException::throwException(CustomElementException::ContextDestroyedRegisteringDefinition, definition->descriptor().type(), exceptionState);
        return false;
    }

    const CustomElementDescriptor& descriptor = definition->descriptor();

    v8::Handle<v8::String> v8TagName = v8String(isolate, descriptor.localName());
    v8::Handle<v8::Value> v8Type;
    if (descriptor.isTypeExtension())
        v8Type = v8String(isolate, descriptor.type());
    else
        v8Type = v8::Null(isolate);

    m_constructor->SetName(v8Type->IsNull() ? v8TagName : v8Type.As<v8::String>());

    V8HiddenValue::setHiddenValue(isolate, m_constructor, V8HiddenValue::customElementDocument(isolate), toV8(document, m_scriptState->context()->Global(), isolate));
    V8HiddenValue::setHiddenValue(isolate, m_constructor, V8HiddenValue::customElementNamespaceURI(isolate), v8String(isolate, descriptor.namespaceURI()));
    V8HiddenValue::setHiddenValue(isolate, m_constructor, V8HiddenValue::customElementTagName(isolate), v8TagName);
    V8HiddenValue::setHiddenValue(isolate, m_constructor, V8HiddenValue::customElementType(isolate), v8Type);

    v8::Handle<v8::String> prototypeKey = v8String(isolate, "prototype");
    ASSERT(m_constructor->HasOwnProperty(prototypeKey));
    // This sets the property *value*; calling Set is safe because
    // "prototype" is a non-configurable data property so there can be
    // no side effects.
    m_constructor->Set(prototypeKey, m_prototype);
    // This *configures* the property. ForceSet of a function's
    // "prototype" does not affect the value, but can reconfigure the
    // property.
    m_constructor->ForceSet(prototypeKey, m_prototype, v8::PropertyAttribute(v8::ReadOnly | v8::DontEnum | v8::DontDelete));

    V8HiddenValue::setHiddenValue(isolate, m_prototype, V8HiddenValue::customElementIsInterfacePrototypeObject(isolate), v8::True(isolate));
    m_prototype->ForceSet(v8String(isolate, "constructor"), m_constructor, v8::DontEnum);

    return true;
}

bool CustomElementConstructorBuilder::prototypeIsValid(const AtomicString& type, ExceptionState& exceptionState) const
{
    if (m_prototype->InternalFieldCount() || !V8HiddenValue::getHiddenValue(m_scriptState->isolate(), m_prototype, V8HiddenValue::customElementIsInterfacePrototypeObject(m_scriptState->isolate())).IsEmpty()) {
        CustomElementException::throwException(CustomElementException::PrototypeInUse, type, exceptionState);
        return false;
    }

    if (m_prototype->GetPropertyAttributes(v8String(m_scriptState->isolate(), "constructor")) & v8::DontDelete) {
        CustomElementException::throwException(CustomElementException::ConstructorPropertyNotConfigurable, type, exceptionState);
        return false;
    }

    return true;
}

bool CustomElementConstructorBuilder::didRegisterDefinition(CustomElementDefinition* definition) const
{
    ASSERT(!m_constructor.IsEmpty());

    return m_callbacks->setBinding(definition, CustomElementBinding::create(m_scriptState->isolate(), m_prototype, m_wrapperType));
}

ScriptValue CustomElementConstructorBuilder::bindingsReturnValue() const
{
    return ScriptValue(m_constructor, m_scriptState->isolate());
}

bool CustomElementConstructorBuilder::hasValidPrototypeChainFor(const WrapperTypeInfo* type) const
{
    v8::Handle<v8::Object> elementPrototype = m_scriptState->perContextData()->prototypeForType(type);
    if (elementPrototype.IsEmpty())
        return false;

    v8::Handle<v8::Value> chain = m_prototype;
    while (!chain.IsEmpty() && chain->IsObject()) {
        if (chain == elementPrototype)
            return true;
        chain = chain.As<v8::Object>()->GetPrototype();
    }

    return false;
}

static void constructCustomElement(const v8::FunctionCallbackInfo<v8::Value>& info)
{
    v8::Isolate* isolate = info.GetIsolate();

    if (!info.IsConstructCall()) {
        throwTypeError("DOM object constructor cannot be called as a function.", isolate);
        return;
    }

    if (info.Length() > 0) {
        throwTypeError("This constructor should be called without arguments.", isolate);
        return;
    }

    Document* document = V8Document::toNative(V8HiddenValue::getHiddenValue(info.GetIsolate(), info.Callee(), V8HiddenValue::customElementDocument(isolate)).As<v8::Object>());
    V8TRYCATCH_FOR_V8STRINGRESOURCE_VOID(V8StringResource<>, namespaceURI, V8HiddenValue::getHiddenValue(isolate, info.Callee(), V8HiddenValue::customElementNamespaceURI(isolate)));
    V8TRYCATCH_FOR_V8STRINGRESOURCE_VOID(V8StringResource<>, tagName, V8HiddenValue::getHiddenValue(isolate, info.Callee(), V8HiddenValue::customElementTagName(isolate)));
    v8::Handle<v8::Value> maybeType = V8HiddenValue::getHiddenValue(info.GetIsolate(), info.Callee(), V8HiddenValue::customElementType(isolate));
    V8TRYCATCH_FOR_V8STRINGRESOURCE_VOID(V8StringResource<>, type, maybeType);

    ExceptionState exceptionState(ExceptionState::ConstructionContext, "CustomElement", info.Holder(), info.GetIsolate());
    CustomElementCallbackDispatcher::CallbackDeliveryScope deliveryScope;
    RefPtr<Element> element = document->createElementNS(namespaceURI, tagName, maybeType->IsNull() ? nullAtom : type, exceptionState);
    if (exceptionState.throwIfNeeded())
        return;
    v8SetReturnValueFast(info, element.release(), document);
}

} // namespace WebCore
<commit_msg>Remove unused include.<commit_after>/*
 * Copyright (C) 2013 Google Inc. 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 Google Inc. 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 "config.h"
#include "bindings/v8/CustomElementConstructorBuilder.h"

#include "HTMLNames.h"
#include "SVGNames.h"
#include "V8Document.h"
#include "V8HTMLElementWrapperFactory.h"
#include "V8SVGElementWrapperFactory.h"
#include "bindings/v8/CustomElementBinding.h"
#include "bindings/v8/DOMWrapperWorld.h"
#include "bindings/v8/Dictionary.h"
#include "bindings/v8/ExceptionState.h"
#include "bindings/v8/V8Binding.h"
#include "bindings/v8/V8HiddenValue.h"
#include "bindings/v8/V8PerContextData.h"
#include "core/dom/Document.h"
#include "core/dom/custom/CustomElementCallbackDispatcher.h"
#include "core/dom/custom/CustomElementDefinition.h"
#include "core/dom/custom/CustomElementDescriptor.h"
#include "core/dom/custom/CustomElementException.h"
#include "wtf/Assertions.h"

namespace WebCore {

static void constructCustomElement(const v8::FunctionCallbackInfo<v8::Value>&);

CustomElementConstructorBuilder::CustomElementConstructorBuilder(NewScriptState* scriptState, const Dictionary* options)
    : m_scriptState(scriptState)
    , m_options(options)
    , m_wrapperType(0)
{
    ASSERT(m_scriptState->context() == v8::Isolate::GetCurrent()->GetCurrentContext());
}

bool CustomElementConstructorBuilder::isFeatureAllowed() const
{
    return m_scriptState->world().isMainWorld();
}

bool CustomElementConstructorBuilder::validateOptions(const AtomicString& type, QualifiedName& tagName, ExceptionState& exceptionState)
{
    ASSERT(m_prototype.IsEmpty());

    ScriptValue prototypeScriptValue;
    if (m_options->get("prototype", prototypeScriptValue) && !prototypeScriptValue.isNull()) {
        if (!prototypeScriptValue.isObject()) {
            CustomElementException::throwException(CustomElementException::PrototypeNotAnObject, type, exceptionState);
            return false;
        }
        m_prototype = prototypeScriptValue.v8Value().As<v8::Object>();
    } else {
        m_prototype = v8::Object::New(m_scriptState->isolate());
        v8::Local<v8::Object> basePrototype = m_scriptState->perContextData()->prototypeForType(&V8HTMLElement::wrapperTypeInfo);
        if (!basePrototype.IsEmpty())
            m_prototype->SetPrototype(basePrototype);
    }

    AtomicString extends;
    bool extendsProvidedAndNonNull = m_options->get("extends", extends);

    if (!m_scriptState->perContextData()) {
        // FIXME: This should generate an InvalidContext exception at a later point.
        CustomElementException::throwException(CustomElementException::ContextDestroyedCheckingPrototype, type, exceptionState);
        return false;
    }

    AtomicString namespaceURI = HTMLNames::xhtmlNamespaceURI;
    if (hasValidPrototypeChainFor(&V8SVGElement::wrapperTypeInfo))
        namespaceURI = SVGNames::svgNamespaceURI;

    AtomicString localName;

    if (extendsProvidedAndNonNull) {
        localName = extends.lower();

        if (!Document::isValidName(localName)) {
            CustomElementException::throwException(CustomElementException::ExtendsIsInvalidName, type, exceptionState);
            return false;
        }
        if (CustomElement::isValidName(localName)) {
            CustomElementException::throwException(CustomElementException::ExtendsIsCustomElementName, type, exceptionState);
            return false;
        }
    } else {
        if (namespaceURI == SVGNames::svgNamespaceURI) {
            CustomElementException::throwException(CustomElementException::ExtendsIsInvalidName, type, exceptionState);
            return false;
        }
        localName = type;
    }

    if (!extendsProvidedAndNonNull)
        m_wrapperType = &V8HTMLElement::wrapperTypeInfo;
    else if (namespaceURI == HTMLNames::xhtmlNamespaceURI)
        m_wrapperType = findWrapperTypeForHTMLTagName(localName);
    else
        m_wrapperType = findWrapperTypeForSVGTagName(localName);

    ASSERT(m_wrapperType);
    tagName = QualifiedName(nullAtom, localName, namespaceURI);
    return m_wrapperType;
}

PassRefPtr<CustomElementLifecycleCallbacks> CustomElementConstructorBuilder::createCallbacks()
{
    ASSERT(!m_prototype.IsEmpty());

    RefPtr<ExecutionContext> executionContext = m_scriptState->executionContext();

    v8::TryCatch exceptionCatcher;
    exceptionCatcher.SetVerbose(true);

    v8::Isolate* isolate = v8::Isolate::GetCurrent();
    v8::Handle<v8::Function> created = retrieveCallback(isolate, "createdCallback");
    v8::Handle<v8::Function> attached = retrieveCallback(isolate, "attachedCallback");
    v8::Handle<v8::Function> detached = retrieveCallback(isolate, "detachedCallback");
    v8::Handle<v8::Function> attributeChanged = retrieveCallback(isolate, "attributeChangedCallback");

    m_callbacks = V8CustomElementLifecycleCallbacks::create(executionContext.get(), m_prototype, created, attached, detached, attributeChanged);
    return m_callbacks.get();
}

v8::Handle<v8::Function> CustomElementConstructorBuilder::retrieveCallback(v8::Isolate* isolate, const char* name)
{
    v8::Handle<v8::Value> value = m_prototype->Get(v8String(isolate, name));
    if (value.IsEmpty() || !value->IsFunction())
        return v8::Handle<v8::Function>();
    return value.As<v8::Function>();
}

bool CustomElementConstructorBuilder::createConstructor(Document* document, CustomElementDefinition* definition, ExceptionState& exceptionState)
{
    ASSERT(!m_prototype.IsEmpty());
    ASSERT(m_constructor.IsEmpty());
    ASSERT(document);

    v8::Isolate* isolate = m_scriptState->isolate();

    if (!prototypeIsValid(definition->descriptor().type(), exceptionState))
        return false;

    v8::Local<v8::FunctionTemplate> constructorTemplate = v8::FunctionTemplate::New(isolate);
    constructorTemplate->SetCallHandler(constructCustomElement);
    m_constructor = constructorTemplate->GetFunction();
    if (m_constructor.IsEmpty()) {
        CustomElementException::throwException(CustomElementException::ContextDestroyedRegisteringDefinition, definition->descriptor().type(), exceptionState);
        return false;
    }

    const CustomElementDescriptor& descriptor = definition->descriptor();

    v8::Handle<v8::String> v8TagName = v8String(isolate, descriptor.localName());
    v8::Handle<v8::Value> v8Type;
    if (descriptor.isTypeExtension())
        v8Type = v8String(isolate, descriptor.type());
    else
        v8Type = v8::Null(isolate);

    m_constructor->SetName(v8Type->IsNull() ? v8TagName : v8Type.As<v8::String>());

    V8HiddenValue::setHiddenValue(isolate, m_constructor, V8HiddenValue::customElementDocument(isolate), toV8(document, m_scriptState->context()->Global(), isolate));
    V8HiddenValue::setHiddenValue(isolate, m_constructor, V8HiddenValue::customElementNamespaceURI(isolate), v8String(isolate, descriptor.namespaceURI()));
    V8HiddenValue::setHiddenValue(isolate, m_constructor, V8HiddenValue::customElementTagName(isolate), v8TagName);
    V8HiddenValue::setHiddenValue(isolate, m_constructor, V8HiddenValue::customElementType(isolate), v8Type);

    v8::Handle<v8::String> prototypeKey = v8String(isolate, "prototype");
    ASSERT(m_constructor->HasOwnProperty(prototypeKey));
    // This sets the property *value*; calling Set is safe because
    // "prototype" is a non-configurable data property so there can be
    // no side effects.
    m_constructor->Set(prototypeKey, m_prototype);
    // This *configures* the property. ForceSet of a function's
    // "prototype" does not affect the value, but can reconfigure the
    // property.
    m_constructor->ForceSet(prototypeKey, m_prototype, v8::PropertyAttribute(v8::ReadOnly | v8::DontEnum | v8::DontDelete));

    V8HiddenValue::setHiddenValue(isolate, m_prototype, V8HiddenValue::customElementIsInterfacePrototypeObject(isolate), v8::True(isolate));
    m_prototype->ForceSet(v8String(isolate, "constructor"), m_constructor, v8::DontEnum);

    return true;
}

bool CustomElementConstructorBuilder::prototypeIsValid(const AtomicString& type, ExceptionState& exceptionState) const
{
    if (m_prototype->InternalFieldCount() || !V8HiddenValue::getHiddenValue(m_scriptState->isolate(), m_prototype, V8HiddenValue::customElementIsInterfacePrototypeObject(m_scriptState->isolate())).IsEmpty()) {
        CustomElementException::throwException(CustomElementException::PrototypeInUse, type, exceptionState);
        return false;
    }

    if (m_prototype->GetPropertyAttributes(v8String(m_scriptState->isolate(), "constructor")) & v8::DontDelete) {
        CustomElementException::throwException(CustomElementException::ConstructorPropertyNotConfigurable, type, exceptionState);
        return false;
    }

    return true;
}

bool CustomElementConstructorBuilder::didRegisterDefinition(CustomElementDefinition* definition) const
{
    ASSERT(!m_constructor.IsEmpty());

    return m_callbacks->setBinding(definition, CustomElementBinding::create(m_scriptState->isolate(), m_prototype, m_wrapperType));
}

ScriptValue CustomElementConstructorBuilder::bindingsReturnValue() const
{
    return ScriptValue(m_constructor, m_scriptState->isolate());
}

bool CustomElementConstructorBuilder::hasValidPrototypeChainFor(const WrapperTypeInfo* type) const
{
    v8::Handle<v8::Object> elementPrototype = m_scriptState->perContextData()->prototypeForType(type);
    if (elementPrototype.IsEmpty())
        return false;

    v8::Handle<v8::Value> chain = m_prototype;
    while (!chain.IsEmpty() && chain->IsObject()) {
        if (chain == elementPrototype)
            return true;
        chain = chain.As<v8::Object>()->GetPrototype();
    }

    return false;
}

static void constructCustomElement(const v8::FunctionCallbackInfo<v8::Value>& info)
{
    v8::Isolate* isolate = info.GetIsolate();

    if (!info.IsConstructCall()) {
        throwTypeError("DOM object constructor cannot be called as a function.", isolate);
        return;
    }

    if (info.Length() > 0) {
        throwTypeError("This constructor should be called without arguments.", isolate);
        return;
    }

    Document* document = V8Document::toNative(V8HiddenValue::getHiddenValue(info.GetIsolate(), info.Callee(), V8HiddenValue::customElementDocument(isolate)).As<v8::Object>());
    V8TRYCATCH_FOR_V8STRINGRESOURCE_VOID(V8StringResource<>, namespaceURI, V8HiddenValue::getHiddenValue(isolate, info.Callee(), V8HiddenValue::customElementNamespaceURI(isolate)));
    V8TRYCATCH_FOR_V8STRINGRESOURCE_VOID(V8StringResource<>, tagName, V8HiddenValue::getHiddenValue(isolate, info.Callee(), V8HiddenValue::customElementTagName(isolate)));
    v8::Handle<v8::Value> maybeType = V8HiddenValue::getHiddenValue(info.GetIsolate(), info.Callee(), V8HiddenValue::customElementType(isolate));
    V8TRYCATCH_FOR_V8STRINGRESOURCE_VOID(V8StringResource<>, type, maybeType);

    ExceptionState exceptionState(ExceptionState::ConstructionContext, "CustomElement", info.Holder(), info.GetIsolate());
    CustomElementCallbackDispatcher::CallbackDeliveryScope deliveryScope;
    RefPtr<Element> element = document->createElementNS(namespaceURI, tagName, maybeType->IsNull() ? nullAtom : type, exceptionState);
    if (exceptionState.throwIfNeeded())
        return;
    v8SetReturnValueFast(info, element.release(), document);
}

} // namespace WebCore
<|endoftext|>


<commit_before>#include "SpriteComponent.h"
#include "../../Logger.h"
#include "../../Assets/TextureManager.h"
#include "../../Graphics/GraphicsManager.h"
#include "../TransformComponent.h"
#include "../../Objects/FreeCamera.h"
#include "../CameraComponent.h"
#include "../../Objects/Object.h"
#include "../../Graphics/SpriteBatch.h"

namespace star
{
	SpriteComponent::SpriteComponent(const tstring& filepath,const tstring& spriteName, bool bIsHUDElement, int widthSegments, int heightSegments)
		: BaseComponent()
		, m_Width(0)
		, m_WidthSegments(widthSegments)
		, m_CurrentWidthSegment(0)
		, m_Heigth(0)
		, m_HeightSegments(heightSegments)
		, m_CurrentHeightSegment(0)
		, m_Shader()
		, m_FilePath(filepath)
		, m_SpriteName(spriteName)
		, m_bIsHudElement(bIsHUDElement)
	{
	}

	void SpriteComponent::InitializeComponent()
	{
		#ifdef DESKTOP
				Filepath texshaderVertex(_T("WinShaders/"), _T("Texture_Shader.vert"));
				Filepath texshaderFrag(_T("WinShaders/"), _T("Texture_Shader.frag"));
		#else
				Filepath texshaderVertex(_T("AndroidShaders/"), _T("BaseTexShader.vert"));
				Filepath texshaderFrag(_T("AndroidShaders/"), _T("BaseTexShader.frag"));
		#endif

				if(!m_Shader.Init(texshaderVertex.GetFullPath(),texshaderFrag.GetFullPath()))
				{
					Logger::GetInstance()->Log(star::LogLevel::Info, _T("Making Shader Failed"));
				}

		TextureManager::GetInstance()->LoadTexture(m_FilePath.GetFullPath(),m_SpriteName);
		m_Width = TextureManager::GetInstance()->GetTextureDimensions(m_SpriteName).x / m_WidthSegments;
		m_Heigth =  TextureManager::GetInstance()->GetTextureDimensions(m_SpriteName).y / m_HeightSegments;

		CreateVertices();
		CreateIndices();
	}

	SpriteComponent::~SpriteComponent()
	{
		// [COMMENT] can you explain me why you wouldn't want
		// to destroy the texture once this spritecomponent
		// is deleted. (you can do that via skype)
		// [COMMENT] @ Simon: We should discuss if RemoveTexture should be called here.
		// Really depends of the structure of the TextureManager

		//TextureManager::GetInstance()->DeleteTexture(m_SpriteName);
	}

	void SpriteComponent::CreateVertices()
	{
		m_Vertices[0] = (GLfloat)m_Width;
        m_Vertices[1] = (GLfloat)m_Heigth;
        m_Vertices[2] = 0;
        m_Vertices[3] = (GLfloat)m_Width;
        m_Vertices[4] = 0;
        m_Vertices[5] = 0;
        m_Vertices[6] = 0;
        m_Vertices[7] = (GLfloat)m_Heigth;
        m_Vertices[8] = 0;
        m_Vertices[9] = 0;
        m_Vertices[10] = 0;
        m_Vertices[11] = 0;
	}

	void SpriteComponent::CreateIndices()
	{
		float startX = static_cast<float>(m_CurrentWidthSegment) / static_cast<float>(m_WidthSegments);
		float endX = 1.0f / m_WidthSegments;
		float startY = static_cast<float>(m_CurrentHeightSegment) / static_cast<float>(m_HeightSegments);
		float endY = 1.0f / m_HeightSegments;

		m_UvCoords[0] = startX + endX;
		m_UvCoords[1] = startY + endY;
		m_UvCoords[2] = startX + endX;
		m_UvCoords[3] = startY;
		m_UvCoords[4] = startX;
		m_UvCoords[5] = startY + endY;
		m_UvCoords[6] = startX;
		m_UvCoords[7] = startY;
	}

	void SpriteComponent::Draw()
	{
		SpriteBatch::GetInstance()->AddSpriteToQueue(this, m_bIsHudElement);
	}

	const tstring& SpriteComponent::GetFilePath() const
	{
		return m_FilePath.GetPath();
	}

	const tstring& SpriteComponent::GetName() const
	{
		return m_SpriteName;
	}

	int32 SpriteComponent::GetWidth() const
	{
		return (m_Width/m_WidthSegments);
	}

	int32 SpriteComponent::GetHeight() const
	{
		return (m_Heigth/m_HeightSegments);
	}

	std::vector<GLfloat> SpriteComponent::GetVertices() const
	{
		std::vector<GLfloat> vertices;
		vertices.clear();

		for(int i = 0; i < 12; ++i)
		{
			vertices.push_back(m_Vertices[i]);
		}
		return vertices;
	}

	std::vector<GLfloat> SpriteComponent::GetUVCoords() const
	{
		std::vector<GLfloat> uvCoords;
		uvCoords.clear();

		for(int i = 0; i < 8; ++i)
		{
			uvCoords.push_back(m_UvCoords[i]);
		}
		return uvCoords;
	}

	void SpriteComponent::SetCurrentSegment(int widthSegment, int heightSegment)
	{
		m_CurrentWidthSegment = widthSegment;
		m_CurrentHeightSegment = heightSegment;

		CreateIndices();
	}

	void SpriteComponent::SetTexture( const tstring& filepath, const tstring& spriteName, bool bIsHUDElement /*= false*/, int widthSegments /*= 1*/, int heightSegments /*= 1*/ )
	{
		m_Width=(0);
		m_WidthSegments=(widthSegments);
		m_CurrentWidthSegment=(0);
		m_Heigth=(0);
		m_HeightSegments=(heightSegments);
		m_CurrentHeightSegment=(0);
		m_FilePath=(filepath);
		m_SpriteName=(spriteName);
		m_bIsHudElement=(bIsHUDElement);

		TextureManager::GetInstance()->LoadTexture(m_FilePath.GetFullPath(),m_SpriteName);
		m_Width = TextureManager::GetInstance()->GetTextureDimensions(m_SpriteName).x / m_WidthSegments;
		m_Heigth =  TextureManager::GetInstance()->GetTextureDimensions(m_SpriteName).y / m_HeightSegments;

		CreateVertices();
		CreateIndices();
	}

}
<commit_msg>sprite fixed as well<commit_after>#include "SpriteComponent.h"
#include "../../Logger.h"
#include "../../Assets/TextureManager.h"
#include "../../Graphics/GraphicsManager.h"
#include "../TransformComponent.h"
#include "../../Objects/FreeCamera.h"
#include "../CameraComponent.h"
#include "../../Objects/Object.h"
#include "../../Graphics/SpriteBatch.h"

namespace star
{
	SpriteComponent::SpriteComponent(const tstring& filepath,const tstring& spriteName, bool bIsHUDElement, int widthSegments, int heightSegments)
		: BaseComponent()
		, m_Width(0)
		, m_WidthSegments(widthSegments)
		, m_CurrentWidthSegment(0)
		, m_Heigth(0)
		, m_HeightSegments(heightSegments)
		, m_CurrentHeightSegment(0)
		, m_Shader()
		, m_FilePath(filepath)
		, m_SpriteName(spriteName)
		, m_bIsHudElement(bIsHUDElement)
	{
	}

	void SpriteComponent::InitializeComponent()
	{
		#ifdef DESKTOP
				Filepath texshaderVertex(_T("WinShaders/"), _T("Texture_Shader.vert"));
				Filepath texshaderFrag(_T("WinShaders/"), _T("Texture_Shader.frag"));
		#else
				Filepath texshaderVertex(_T("AndroidShaders/"), _T("BaseTexShader.vert"));
				Filepath texshaderFrag(_T("AndroidShaders/"), _T("BaseTexShader.frag"));
		#endif

				if(!m_Shader.Init(texshaderVertex.GetFullPath(),texshaderFrag.GetFullPath()))
				{
					Logger::GetInstance()->Log(star::LogLevel::Info, _T("Making Shader Failed"));
				}

		TextureManager::GetInstance()->LoadTexture(m_FilePath.GetFullPath(),m_SpriteName);
		m_Width = TextureManager::GetInstance()->GetTextureDimensions(m_SpriteName).x / m_WidthSegments;
		m_Heigth =  TextureManager::GetInstance()->GetTextureDimensions(m_SpriteName).y / m_HeightSegments;

		CreateVertices();
		CreateIndices();
	}

	SpriteComponent::~SpriteComponent()
	{
		// [COMMENT] can you explain me why you wouldn't want
		// to destroy the texture once this spritecomponent
		// is deleted. (you can do that via skype)
		// [COMMENT] @ Simon: We should discuss if RemoveTexture should be called here.
		// Really depends of the structure of the TextureManager

		//TextureManager::GetInstance()->DeleteTexture(m_SpriteName);
	}

	void SpriteComponent::CreateVertices()
	{
		m_Vertices[0] = (GLfloat)m_Width;
        m_Vertices[1] = (GLfloat)m_Heigth;
        m_Vertices[2] = 0;
        m_Vertices[3] = (GLfloat)m_Width;
        m_Vertices[4] = 0;
        m_Vertices[5] = 0;
        m_Vertices[6] = 0;
        m_Vertices[7] = (GLfloat)m_Heigth;
        m_Vertices[8] = 0;
        m_Vertices[9] = 0;
        m_Vertices[10] = 0;
        m_Vertices[11] = 0;
	}

	void SpriteComponent::CreateIndices()
	{
		float startX = static_cast<float>(m_CurrentWidthSegment) / static_cast<float>(m_WidthSegments);
		float endX = 1.0f / m_WidthSegments;
		float startY = static_cast<float>(m_CurrentHeightSegment) / static_cast<float>(m_HeightSegments);
		float endY = 1.0f / m_HeightSegments;

		m_UvCoords[0] = startX + endX;
		m_UvCoords[1] = startY + endY;
		m_UvCoords[2] = startX + endX;
		m_UvCoords[3] = startY;
		m_UvCoords[4] = startX;
		m_UvCoords[5] = startY + endY;
		m_UvCoords[6] = startX;
		m_UvCoords[7] = startY;
	}

	void SpriteComponent::Draw()
	{
		SpriteBatch::GetInstance()->AddSpriteToQueue(this, m_bIsHudElement);
	}

	const tstring& SpriteComponent::GetFilePath() const
	{
		return m_FilePath.GetPath();
	}

	const tstring& SpriteComponent::GetName() const
	{
		return m_SpriteName;
	}

	int32 SpriteComponent::GetWidth() const
	{
		return (m_Width/m_WidthSegments);
	}

	int32 SpriteComponent::GetHeight() const
	{
		return (m_Heigth/m_HeightSegments);
	}

	std::vector<GLfloat> SpriteComponent::GetVertices() const
	{
		std::vector<GLfloat> vertices;
		vertices.clear();

		for(int i = 0; i < 12; ++i)
		{
			vertices.push_back(m_Vertices[i]);
		}
		return vertices;
	}

	std::vector<GLfloat> SpriteComponent::GetUVCoords() const
	{
		std::vector<GLfloat> uvCoords;
		uvCoords.clear();

		for(int i = 0; i < 8; ++i)
		{
			uvCoords.push_back(m_UvCoords[i]);
		}
		return uvCoords;
	}

	void SpriteComponent::SetCurrentSegment(int widthSegment, int heightSegment)
	{
		m_CurrentWidthSegment = widthSegment;
		m_CurrentHeightSegment = m_HeightSegments - heightSegment - 1;

		CreateIndices();
	}

	void SpriteComponent::SetTexture( const tstring& filepath, const tstring& spriteName, bool bIsHUDElement /*= false*/, int widthSegments /*= 1*/, int heightSegments /*= 1*/ )
	{
		m_Width = 0;
		m_WidthSegments = widthSegments;
		m_CurrentWidthSegment = 0;
		m_Heigth = 0;
		m_HeightSegments = heightSegments;
		m_CurrentHeightSegment = 0;
		m_FilePath = filepath;
		m_SpriteName = spriteName;
		m_bIsHudElement = bIsHUDElement;

		TextureManager::GetInstance()->LoadTexture(m_FilePath.GetFullPath(),m_SpriteName);
		m_Width = TextureManager::GetInstance()->GetTextureDimensions(m_SpriteName).x / m_WidthSegments;
		m_Heigth =  TextureManager::GetInstance()->GetTextureDimensions(m_SpriteName).y / m_HeightSegments;

		CreateVertices();
		CreateIndices();
	}

}
<|endoftext|>


<commit_before>// This file is a part of the OpenSurgSim project.
// Copyright 2013, SimQuest Solutions Inc.
//
// 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.

/// \file CubicSolverTests.cpp
/// Tests for the cubic solver function.

#include <gtest/gtest.h>

#include "SurgSim/Math/CubicSolver.h"

namespace SurgSim
{

namespace Math
{

TEST(CubicSolverTests, DegenerateCases)
{
	double roots[3];
	bool found;

	{
		SCOPED_TRACE("0.x^3 + x^2 + x + 1 = 0 (no solution on R: none on [0..1])");
		EXPECT_NO_THROW(found = findSmallestRootInRange01(0.0, 1.0, 1.0, 1.0, roots));
		EXPECT_EQ(false, found);
	}

	{
		SCOPED_TRACE("0.x^3 + 1x^2 + 4x - 12 = 0 (2 solutions on R: -6 and 2, none on [0..1])");
		EXPECT_NO_THROW(found = findSmallestRootInRange01(0.0, 1.0, 4.0, -12.0, roots));
		EXPECT_EQ(false, found);
	}

	{
		SCOPED_TRACE("0.x^3 + x^2 + 0x + 0 = 0 (1 solution on R: 0, 1 on [0..1])");
		EXPECT_NO_THROW(found = findSmallestRootInRange01(0.0, 1.0, 0.0, 0.0, roots));
		EXPECT_EQ(true, found);
		EXPECT_DOUBLE_EQ(0.0, roots[0]);
	}

	{
		SCOPED_TRACE("0.x^3 + x^2 + x - 2 = 0 (2 solutions on R: -2 and 1, 1 on [0..1])");
		EXPECT_NO_THROW(found = findSmallestRootInRange01(0.0, 1.0, 1.0, -2.0, roots));
		EXPECT_EQ(true, found);
		EXPECT_DOUBLE_EQ(1.0, roots[0]);
	}

	{
		SCOPED_TRACE("0.x^3 + 2x^2 + x - 1 = 0 (2 solutions on R: -1 and 0.5, 1 on [0..1])");
		EXPECT_NO_THROW(found = findSmallestRootInRange01(0.0, 2.0, 1.0, -1.0, roots));
		EXPECT_EQ(true, found);
		EXPECT_DOUBLE_EQ(0.5, roots[0]);
	}

};

TEST(CubicSolverTests, dNullCase)
{
	double roots[3];
	bool found;

	{
		SCOPED_TRACE("x^3 + x^2 + x + 0 = 0");
		EXPECT_NO_THROW(found = findSmallestRootInRange01(1.0, 1.0, 1.0, 0.0, roots));
		EXPECT_EQ(true, found);
		EXPECT_DOUBLE_EQ(0.0, roots[0]);
	}
}

TEST(CubicSolverTests, DerivativeNullDeterminantCases)
{
	double roots[3];
	bool found;

	{
		SCOPED_TRACE("P(x) = -x^3 + 3x^2 - 3x + 1 = 0 => P'(x) = -3x^2 + 6x - 3 => discriminant = 36 - 4*(-3)*(-3) = 0");
		EXPECT_NO_THROW(found = findSmallestRootInRange01(-1.0, 3.0, -3.0, 1.0, roots));
		EXPECT_EQ(true, found);
		EXPECT_DOUBLE_EQ(1.0, roots[0]);
	}

	{
		SCOPED_TRACE("P(x) = -x^3 + 3x^2 - 3x - 2 = 0 => P'(x) = -3x^2 + 6x - 3 => discriminant = 36 - 4*(-3)*(-3) = 0");
		EXPECT_NO_THROW(found = findSmallestRootInRange01(-1.0, 3.0, -3.0, -2.0, roots));
		EXPECT_EQ(false, found);
	}
};

TEST(CubicSolverTests, DerivativeNegativeDeterminantCases)
{
	using namespace CubicSolver;

	double roots[3];
	bool found;

	{
		SCOPED_TRACE("P(x) = x^3 + x^2 + x + 1 = 0 => P'(x) = 3x^2 + 2x + 1 => discriminant = 4 - 4*(3)*(1) = -8");
		// P(0) = 1
		// P(1) = 4
		// P is monotonic and 0 is not in [P(0)..P(1)], so the unique solution is not within [0..1]
		EXPECT_NO_THROW(found = findSmallestRootInRange01(1.0, 1.0, 1.0, 1.0, roots));
		EXPECT_EQ(false, found);
	}

	{
		SCOPED_TRACE("P(x) = x^3 - x^2 + x - 0.5 = 0 => P'(x) = 3x^2 - 2x + 1 => discriminant = 4 - 4*(3)*(1) = -8");
		// P(0) = -0.5
		// P(1) = 0.5
		// P is monotonic and 0 is in [P(0)..P(1)], so the unique solution is within [0..1]
		EXPECT_NO_THROW(found = findSmallestRootInRange01(1.0, -1.0, 1.0, -0.5, roots));
		EXPECT_EQ(true, found);
		EXPECT_TRUE(roots[0] >= 0.0 && roots[0] <= 1.0);
		double eval = evaluatePolynomial(1.0, -1.0, 1.0, -0.5, roots[0]);
		EXPECT_TRUE(isZero(eval)) << "P(" << roots[0] << ") = " << eval;
	}
};

TEST(CubicSolverTests, DerivativePositiveDeterminantCases)
{
	using namespace CubicSolver;

	double roots[3];
	bool found;

	{
		SCOPED_TRACE("P(x) = -x^3 + x^2 + x + 1 = 0 => P'(x) = -3x^2 + 2x + 1 => discriminant = 4 - 4*(-3)*(1) = 16");
		// P' has 2 roots: -1/3 and 1
		// P'(-1/3) = 0                                         P'(1) = 0
		// P (-1/3) = 1 > -1/27 + 1/9 - 1/3 + 1 > 0   P(0) = 1  P (1) = 2
		// P is monotonic in 3 intervals [-Inf -1/3[, [-1/3 1] and ]1 +Inf[
		// Therefore P is monotonic in [0..1] and above 0 on this interval, so there is no root in [0..1]
		EXPECT_NO_THROW(found = findSmallestRootInRange01(-1.0, 1.0, 1.0, 1.0, roots));
		EXPECT_EQ(false, found);
	}

	{
		SCOPED_TRACE("P(x) = -x^3 + x^2 + x - 0.5 = 0 => P'(x) = -3x^2 + 2x + 1 => discriminant = 4 - 4*(-3)*(1) = 16");
		// P' has 2 roots: -1/3 and 1
		// P'(-1/3) = 0                                          P'(1) = 0
		// P (-1/3) = -1/27 + 1/9 - 1/3 -0.5 < 0   P(0) = -0.5   P (1) = 0.5 > 0
		// P is monotonic in 3 intervals [-Inf -1/3[, [-1/3 1] and ]1 +Inf[
		// Therefore P is monotonic in [0..1] and cross 0 on this interval, so there is 1 root in [0..1]
		EXPECT_NO_THROW(found = findSmallestRootInRange01(-1.0, 1.0, 1.0, -0.5, roots));
		EXPECT_EQ(true, found);
		EXPECT_TRUE(roots[0] >= 0.0 && roots[0] <= 1.0);
		double eval = evaluatePolynomial(-1.0, 1.0, 1.0, -0.5, roots[0]);
		EXPECT_TRUE(isZero(eval)) << "P(" << roots[0] << ") = " << eval;
	}
};

}; // namespace Math
}; // namespace SurgSim
<commit_msg>Fix doxygen warnings<commit_after>// This file is a part of the OpenSurgSim project.
// Copyright 2013, SimQuest Solutions Inc.
//
// 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.

/// \file CubicSolverTests.cpp
/// Tests for the cubic solver function.

#include <gtest/gtest.h>

#include "SurgSim/Math/CubicSolver.h"

namespace SurgSim
{

namespace Math
{

TEST(CubicSolverTests, DegenerateCases)
{
	double roots[3];
	bool found;

	{
		SCOPED_TRACE("0.x^3 + x^2 + x + 1 = 0 (no solution on R: none on [0..1])");
		EXPECT_NO_THROW(found = findSmallestRootInRange01(0.0, 1.0, 1.0, 1.0, roots));
		EXPECT_EQ(false, found);
	}

	{
		SCOPED_TRACE("0.x^3 + 1x^2 + 4x - 12 = 0 (2 solutions on R: -6 and 2, none on [0..1])");
		EXPECT_NO_THROW(found = findSmallestRootInRange01(0.0, 1.0, 4.0, -12.0, roots));
		EXPECT_EQ(false, found);
	}

	{
		SCOPED_TRACE("0.x^3 + x^2 + 0x + 0 = 0 (1 solution on R: 0, 1 on [0..1])");
		EXPECT_NO_THROW(found = findSmallestRootInRange01(0.0, 1.0, 0.0, 0.0, roots));
		EXPECT_EQ(true, found);
		EXPECT_DOUBLE_EQ(0.0, roots[0]);
	}

	{
		SCOPED_TRACE("0.x^3 + x^2 + x - 2 = 0 (2 solutions on R: -2 and 1, 1 on [0..1])");
		EXPECT_NO_THROW(found = findSmallestRootInRange01(0.0, 1.0, 1.0, -2.0, roots));
		EXPECT_EQ(true, found);
		EXPECT_DOUBLE_EQ(1.0, roots[0]);
	}

	{
		SCOPED_TRACE("0.x^3 + 2x^2 + x - 1 = 0 (2 solutions on R: -1 and 0.5, 1 on [0..1])");
		EXPECT_NO_THROW(found = findSmallestRootInRange01(0.0, 2.0, 1.0, -1.0, roots));
		EXPECT_EQ(true, found);
		EXPECT_DOUBLE_EQ(0.5, roots[0]);
	}

};

TEST(CubicSolverTests, dNullCase)
{
	double roots[3];
	bool found;

	{
		SCOPED_TRACE("x^3 + x^2 + x + 0 = 0");
		EXPECT_NO_THROW(found = findSmallestRootInRange01(1.0, 1.0, 1.0, 0.0, roots));
		EXPECT_EQ(true, found);
		EXPECT_DOUBLE_EQ(0.0, roots[0]);
	}
}

TEST(CubicSolverTests, DerivativeNullDeterminantCases)
{
	double roots[3];
	bool found;

	{
		SCOPED_TRACE("P(x) = -x^3 + 3x^2 - 3x + 1 = 0 => P'(x) = -3x^2 + 6x - 3 => discriminant = 36 - 4*(3)*(3) = 0");
		EXPECT_NO_THROW(found = findSmallestRootInRange01(-1.0, 3.0, -3.0, 1.0, roots));
		EXPECT_EQ(true, found);
		EXPECT_DOUBLE_EQ(1.0, roots[0]);
	}

	{
		SCOPED_TRACE("P(x) = -x^3 + 3x^2 - 3x - 2 = 0 => P'(x) = -3x^2 + 6x - 3 => discriminant = 36 - 4*(3)*(3) = 0");
		EXPECT_NO_THROW(found = findSmallestRootInRange01(-1.0, 3.0, -3.0, -2.0, roots));
		EXPECT_EQ(false, found);
	}
};

TEST(CubicSolverTests, DerivativeNegativeDeterminantCases)
{
	using namespace CubicSolver;

	double roots[3];
	bool found;

	{
		SCOPED_TRACE("P(x) = x^3 + x^2 + x + 1 = 0 => P'(x) = 3x^2 + 2x + 1 => discriminant = 4 - 4*(3)*(1) = -8");
		// P(0) = 1
		// P(1) = 4
		// P is monotonic and 0 is not in [P(0)..P(1)], so the unique solution is not within [0..1]
		EXPECT_NO_THROW(found = findSmallestRootInRange01(1.0, 1.0, 1.0, 1.0, roots));
		EXPECT_EQ(false, found);
	}

	{
		SCOPED_TRACE("P(x) = x^3 - x^2 + x - 0.5 = 0 => P'(x) = 3x^2 - 2x + 1 => discriminant = 4 - 4*(3)*(1) = -8");
		// P(0) = -0.5
		// P(1) = 0.5
		// P is monotonic and 0 is in [P(0)..P(1)], so the unique solution is within [0..1]
		EXPECT_NO_THROW(found = findSmallestRootInRange01(1.0, -1.0, 1.0, -0.5, roots));
		EXPECT_EQ(true, found);
		EXPECT_TRUE(roots[0] >= 0.0 && roots[0] <= 1.0);
		double eval = evaluatePolynomial(1.0, -1.0, 1.0, -0.5, roots[0]);
		EXPECT_TRUE(isZero(eval)) << "P(" << roots[0] << ") = " << eval;
	}
};

TEST(CubicSolverTests, DerivativePositiveDeterminantCases)
{
	using namespace CubicSolver;

	double roots[3];
	bool found;

	{
		SCOPED_TRACE("P(x) = -x^3 + x^2 + x + 1 = 0 => P'(x) = -3x^2 + 2x + 1 => discriminant = 4 - 4*(-3)*(1) = 16");
		// P' has 2 roots: -1/3 and 1
		// P'(-1/3) = 0                                         P'(1) = 0
		// P (-1/3) = 1 > -1/27 + 1/9 - 1/3 + 1 > 0   P(0) = 1  P (1) = 2
		// P is monotonic in 3 intervals [-Inf -1/3[, [-1/3 1] and ]1 +Inf[
		// Therefore P is monotonic in [0..1] and above 0 on this interval, so there is no root in [0..1]
		EXPECT_NO_THROW(found = findSmallestRootInRange01(-1.0, 1.0, 1.0, 1.0, roots));
		EXPECT_EQ(false, found);
	}

	{
		SCOPED_TRACE("P(x) = -x^3 + x^2 + x - 0.5 = 0 => P'(x) = -3x^2 + 2x + 1 => discriminant = 4 - 4*(-3)*(1) = 16");
		// P' has 2 roots: -1/3 and 1
		// P'(-1/3) = 0                                          P'(1) = 0
		// P (-1/3) = -1/27 + 1/9 - 1/3 -0.5 < 0   P(0) = -0.5   P (1) = 0.5 > 0
		// P is monotonic in 3 intervals [-Inf -1/3[, [-1/3 1] and ]1 +Inf[
		// Therefore P is monotonic in [0..1] and cross 0 on this interval, so there is 1 root in [0..1]
		EXPECT_NO_THROW(found = findSmallestRootInRange01(-1.0, 1.0, 1.0, -0.5, roots));
		EXPECT_EQ(true, found);
		EXPECT_TRUE(roots[0] >= 0.0 && roots[0] <= 1.0);
		double eval = evaluatePolynomial(-1.0, 1.0, 1.0, -0.5, roots[0]);
		EXPECT_TRUE(isZero(eval)) << "P(" << roots[0] << ") = " << eval;
	}
};

}; // namespace Math
}; // namespace SurgSim
<|endoftext|>