<commit_before>/*
 *  Copyright (c) 2017, The OpenThread Authors.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. 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.
 *  3. Neither the name of the copyright holder 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 HOLDER 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.
 */

/**
 * @file
 *   This file implements a simple CLI for the CoAP service.
 */

#include "cli_udp.hpp"

#include <openthread/message.h>
#include <openthread/udp.h>

#include "cli/cli.hpp"
#include "cli/cli_server.hpp"
#include "common/encoding.hpp"

using ot::Encoding::BigEndian::HostSwap16;

namespace ot {
namespace Cli {

const struct UdpExample::Command UdpExample::sCommands[] = {
    {"help", &UdpExample::ProcessHelp},       {"bind", &UdpExample::ProcessBind}, {"close", &UdpExample::ProcessClose},
    {"connect", &UdpExample::ProcessConnect}, {"open", &UdpExample::ProcessOpen}, {"send", &UdpExample::ProcessSend}};

UdpExample::UdpExample(Interpreter &aInterpreter)
    : mInterpreter(aInterpreter)
{
    memset(&mSocket, 0, sizeof(mSocket));
}

otError UdpExample::ProcessHelp(int argc, char *argv[])
{
    OT_UNUSED_VARIABLE(argc);
    OT_UNUSED_VARIABLE(argv);

    for (unsigned int i = 0; i < OT_ARRAY_LENGTH(sCommands); i++)
    {
        mInterpreter.mServer->OutputFormat("%s\r\n", sCommands[i].mName);
    }

    return OT_ERROR_NONE;
}

otError UdpExample::ProcessBind(int argc, char *argv[])
{
    otError    error;
    otSockAddr sockaddr;
    long       value;

    VerifyOrExit(argc == 2, error = OT_ERROR_INVALID_ARGS);

    memset(&sockaddr, 0, sizeof(sockaddr));

    error = otIp6AddressFromString(argv[0], &sockaddr.mAddress);
    SuccessOrExit(error);

    error = Interpreter::ParseLong(argv[1], value);
    SuccessOrExit(error);

    sockaddr.mPort = static_cast<uint16_t>(value);

    error = otUdpBind(&mSocket, &sockaddr);

exit:
    return error;
}

otError UdpExample::ProcessConnect(int argc, char *argv[])
{
    otError    error;
    otSockAddr sockaddr;
    long       value;

    VerifyOrExit(argc == 2, error = OT_ERROR_INVALID_ARGS);

    memset(&sockaddr, 0, sizeof(sockaddr));

    error = otIp6AddressFromString(argv[0], &sockaddr.mAddress);
    SuccessOrExit(error);

    error = Interpreter::ParseLong(argv[1], value);
    SuccessOrExit(error);

    sockaddr.mPort = static_cast<uint16_t>(value);

    error = otUdpConnect(&mSocket, &sockaddr);

exit:
    return error;
}

otError UdpExample::ProcessClose(int argc, char *argv[])
{
    OT_UNUSED_VARIABLE(argc);
    OT_UNUSED_VARIABLE(argv);

    return otUdpClose(&mSocket);
}

otError UdpExample::ProcessOpen(int argc, char *argv[])
{
    OT_UNUSED_VARIABLE(argc);
    OT_UNUSED_VARIABLE(argv);

    return otUdpOpen(mInterpreter.mInstance, &mSocket, HandleUdpReceive, this);
}

otError UdpExample::ProcessSend(int argc, char *argv[])
{
    otError       error;
    otMessageInfo messageInfo;
    otMessage *   message              = NULL;
    int           curArg               = 0;
    unsigned long autoGenMessageLength = 0;

    memset(&messageInfo, 0, sizeof(messageInfo));

    VerifyOrExit(argc == 1 || argc == 3 || argc == 4, error = OT_ERROR_INVALID_ARGS);

    if (argc == 4)
    {
        if (strcmp(argv[curArg++], "-s") == 0)
        {
            Interpreter::ParseUnsignedLong(argv[curArg++], autoGenMessageLength);
        }
        else
        {
            ExitNow(error = OT_ERROR_INVALID_ARGS);
        }
    }

    if (argc >= 3)
    {
        long value;

        error = otIp6AddressFromString(argv[curArg++], &messageInfo.mPeerAddr);
        SuccessOrExit(error);

        error = Interpreter::ParseLong(argv[curArg++], value);
        SuccessOrExit(error);

        messageInfo.mPeerPort = static_cast<uint16_t>(value);
    }

    message = otUdpNewMessage(mInterpreter.mInstance, NULL);
    VerifyOrExit(message != NULL, error = OT_ERROR_NO_BUFS);

    if (autoGenMessageLength != 0)
    {
        error = WriteCharToBuffer(message, static_cast<uint16_t>(autoGenMessageLength));
    }
    else
    {
        error = otMessageAppend(message, argv[curArg], static_cast<uint16_t>(strlen(argv[curArg])));
    }
    SuccessOrExit(error);

    error = otUdpSend(&mSocket, message, &messageInfo);

exit:

    if (error != OT_ERROR_NONE && message != NULL)
    {
        otMessageFree(message);
    }

    return error;
}

otError UdpExample::WriteCharToBuffer(otMessage *aMessage, uint16_t aMessageSize)
{
    otError error     = OT_ERROR_NONE;
    uint8_t character = 0x30; // 0

    for (uint16_t index = 0; index < aMessageSize; index++)
    {
        SuccessOrExit(error = otMessageAppend(aMessage, &character, 1));
        character++;

        switch (character)
        {
        case 0x3A:            // 9
            character = 0x41; // A
            break;
        case 0x5B:            // Z
            character = 0x61; // a
            break;
        case 0x7B:            // z
            character = 0x30; // 0
            break;
        }
    }

exit:
    return error;
}

otError UdpExample::Process(int argc, char *argv[])
{
    otError error = OT_ERROR_PARSE;

    if (argc < 1)
    {
        ProcessHelp(0, NULL);
        error = OT_ERROR_INVALID_ARGS;
    }
    else
    {
        for (size_t i = 0; i < OT_ARRAY_LENGTH(sCommands); i++)
        {
            if (strcmp(argv[0], sCommands[i].mName) == 0)
            {
                error = (this->*sCommands[i].mCommand)(argc - 1, argv + 1);
                break;
            }
        }
    }
    return error;
}

void UdpExample::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
{
    static_cast<UdpExample *>(aContext)->HandleUdpReceive(aMessage, aMessageInfo);
}

void UdpExample::HandleUdpReceive(otMessage *aMessage, const otMessageInfo *aMessageInfo)
{
    uint8_t buf[1500];
    int     length;

    mInterpreter.mServer->OutputFormat("%d bytes from ", otMessageGetLength(aMessage) - otMessageGetOffset(aMessage));
    mInterpreter.OutputIp6Address(aMessageInfo->mPeerAddr);
    mInterpreter.mServer->OutputFormat(" %d ", aMessageInfo->mPeerPort);

    length      = otMessageRead(aMessage, otMessageGetOffset(aMessage), buf, sizeof(buf) - 1);
    buf[length] = '\0';

    mInterpreter.mServer->OutputFormat("%s\r\n", buf);
}

} // namespace Cli
} // namespace ot
<commit_msg>[cli] add error check to `udp send` command (#4148)<commit_after>/*
 *  Copyright (c) 2017, The OpenThread Authors.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. 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.
 *  3. Neither the name of the copyright holder 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 HOLDER 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.
 */

/**
 * @file
 *   This file implements a simple CLI for the CoAP service.
 */

#include "cli_udp.hpp"

#include <openthread/message.h>
#include <openthread/udp.h>

#include "cli/cli.hpp"
#include "cli/cli_server.hpp"
#include "common/encoding.hpp"

using ot::Encoding::BigEndian::HostSwap16;

namespace ot {
namespace Cli {

const struct UdpExample::Command UdpExample::sCommands[] = {
    {"help", &UdpExample::ProcessHelp},       {"bind", &UdpExample::ProcessBind}, {"close", &UdpExample::ProcessClose},
    {"connect", &UdpExample::ProcessConnect}, {"open", &UdpExample::ProcessOpen}, {"send", &UdpExample::ProcessSend}};

UdpExample::UdpExample(Interpreter &aInterpreter)
    : mInterpreter(aInterpreter)
{
    memset(&mSocket, 0, sizeof(mSocket));
}

otError UdpExample::ProcessHelp(int argc, char *argv[])
{
    OT_UNUSED_VARIABLE(argc);
    OT_UNUSED_VARIABLE(argv);

    for (unsigned int i = 0; i < OT_ARRAY_LENGTH(sCommands); i++)
    {
        mInterpreter.mServer->OutputFormat("%s\r\n", sCommands[i].mName);
    }

    return OT_ERROR_NONE;
}

otError UdpExample::ProcessBind(int argc, char *argv[])
{
    otError    error;
    otSockAddr sockaddr;
    long       value;

    VerifyOrExit(argc == 2, error = OT_ERROR_INVALID_ARGS);

    memset(&sockaddr, 0, sizeof(sockaddr));

    error = otIp6AddressFromString(argv[0], &sockaddr.mAddress);
    SuccessOrExit(error);

    error = Interpreter::ParseLong(argv[1], value);
    SuccessOrExit(error);

    sockaddr.mPort = static_cast<uint16_t>(value);

    error = otUdpBind(&mSocket, &sockaddr);

exit:
    return error;
}

otError UdpExample::ProcessConnect(int argc, char *argv[])
{
    otError    error;
    otSockAddr sockaddr;
    long       value;

    VerifyOrExit(argc == 2, error = OT_ERROR_INVALID_ARGS);

    memset(&sockaddr, 0, sizeof(sockaddr));

    error = otIp6AddressFromString(argv[0], &sockaddr.mAddress);
    SuccessOrExit(error);

    error = Interpreter::ParseLong(argv[1], value);
    SuccessOrExit(error);

    sockaddr.mPort = static_cast<uint16_t>(value);

    error = otUdpConnect(&mSocket, &sockaddr);

exit:
    return error;
}

otError UdpExample::ProcessClose(int argc, char *argv[])
{
    OT_UNUSED_VARIABLE(argc);
    OT_UNUSED_VARIABLE(argv);

    return otUdpClose(&mSocket);
}

otError UdpExample::ProcessOpen(int argc, char *argv[])
{
    OT_UNUSED_VARIABLE(argc);
    OT_UNUSED_VARIABLE(argv);

    return otUdpOpen(mInterpreter.mInstance, &mSocket, HandleUdpReceive, this);
}

otError UdpExample::ProcessSend(int argc, char *argv[])
{
    otError       error;
    otMessageInfo messageInfo;
    otMessage *   message              = NULL;
    int           curArg               = 0;
    unsigned long autoGenMessageLength = 0;

    memset(&messageInfo, 0, sizeof(messageInfo));

    VerifyOrExit(argc == 1 || argc == 3 || argc == 4, error = OT_ERROR_INVALID_ARGS);

    if (argc == 4)
    {
        if (strcmp(argv[curArg++], "-s") == 0)
        {
            error = Interpreter::ParseUnsignedLong(argv[curArg++], autoGenMessageLength);
            SuccessOrExit(error);
        }
        else
        {
            ExitNow(error = OT_ERROR_INVALID_ARGS);
        }
    }

    if (argc >= 3)
    {
        long value;

        error = otIp6AddressFromString(argv[curArg++], &messageInfo.mPeerAddr);
        SuccessOrExit(error);

        error = Interpreter::ParseLong(argv[curArg++], value);
        SuccessOrExit(error);

        messageInfo.mPeerPort = static_cast<uint16_t>(value);
    }

    message = otUdpNewMessage(mInterpreter.mInstance, NULL);
    VerifyOrExit(message != NULL, error = OT_ERROR_NO_BUFS);

    if (autoGenMessageLength != 0)
    {
        error = WriteCharToBuffer(message, static_cast<uint16_t>(autoGenMessageLength));
    }
    else
    {
        error = otMessageAppend(message, argv[curArg], static_cast<uint16_t>(strlen(argv[curArg])));
    }
    SuccessOrExit(error);

    error = otUdpSend(&mSocket, message, &messageInfo);

exit:

    if (error != OT_ERROR_NONE && message != NULL)
    {
        otMessageFree(message);
    }

    return error;
}

otError UdpExample::WriteCharToBuffer(otMessage *aMessage, uint16_t aMessageSize)
{
    otError error     = OT_ERROR_NONE;
    uint8_t character = 0x30; // 0

    for (uint16_t index = 0; index < aMessageSize; index++)
    {
        SuccessOrExit(error = otMessageAppend(aMessage, &character, 1));
        character++;

        switch (character)
        {
        case 0x3A:            // 9
            character = 0x41; // A
            break;
        case 0x5B:            // Z
            character = 0x61; // a
            break;
        case 0x7B:            // z
            character = 0x30; // 0
            break;
        }
    }

exit:
    return error;
}

otError UdpExample::Process(int argc, char *argv[])
{
    otError error = OT_ERROR_PARSE;

    if (argc < 1)
    {
        ProcessHelp(0, NULL);
        error = OT_ERROR_INVALID_ARGS;
    }
    else
    {
        for (size_t i = 0; i < OT_ARRAY_LENGTH(sCommands); i++)
        {
            if (strcmp(argv[0], sCommands[i].mName) == 0)
            {
                error = (this->*sCommands[i].mCommand)(argc - 1, argv + 1);
                break;
            }
        }
    }
    return error;
}

void UdpExample::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
{
    static_cast<UdpExample *>(aContext)->HandleUdpReceive(aMessage, aMessageInfo);
}

void UdpExample::HandleUdpReceive(otMessage *aMessage, const otMessageInfo *aMessageInfo)
{
    uint8_t buf[1500];
    int     length;

    mInterpreter.mServer->OutputFormat("%d bytes from ", otMessageGetLength(aMessage) - otMessageGetOffset(aMessage));
    mInterpreter.OutputIp6Address(aMessageInfo->mPeerAddr);
    mInterpreter.mServer->OutputFormat(" %d ", aMessageInfo->mPeerPort);

    length      = otMessageRead(aMessage, otMessageGetOffset(aMessage), buf, sizeof(buf) - 1);
    buf[length] = '\0';

    mInterpreter.mServer->OutputFormat("%s\r\n", buf);
}

} // namespace Cli
} // namespace ot
<|endoftext|>


<commit_before>#include "softmax_with_loss_op.h"
#include "softmax_shared.h"

namespace caffe2 {

REGISTER_CPU_OPERATOR(SoftmaxWithLoss, SoftmaxWithLossOp<float, CPUContext>);
REGISTER_CPU_OPERATOR(
    SoftmaxWithLossGradient,
    SoftmaxWithLossGradientOp<float, CPUContext>);

// Input: X (logits), T (labels); Output: P (probs), Y
OPERATOR_SCHEMA(SoftmaxWithLoss).NumOutputs(2).SetDoc(R"DOC(
Combined Softmax and Cross-Entropy loss operator.
The operator computes the softmax normalized values for each layer in the batch
of the given input, after which cross-entropy loss is computed. This operator is
numerically more stable than separate Softmax and CrossEntropy ops.
The inputs are a 2-D tensor (Tensor<float>) of size
(batch_size x input_feature_dimensions) and tensor of labels (ground truth).
Output is tensor with the probability for each label for each example (N x D)
and averaged loss (scalar). Use parameter spatial=1 to enable spatial softmax.
Spatial softmax also supports special \"don't care\" label (-1) that is ignored
when computing the loss.

Optional third input blob can be used to weight the samples for the loss.
For the spatial version, weighting is by x,y position of the input.
)DOC");
// Input: X, T, P, dY; Output: dX
OPERATOR_SCHEMA(SoftmaxWithLossGradient).NumOutputs(1);

#define DONT_CARE (-1)

template <>
bool SoftmaxWithLossOp<float, CPUContext>::RunOnDevice() {
  auto& X = Input(0); // Logits
  auto& T = Input(1); // Labels / targets
  auto* P = Output(0); // Probabilities from softmax
  auto* avg_loss = Output(1); // Average loss
  int N = X.dim32(0);
  int D = X.dim32(1);

  P->ResizeLike(X);

  if (sum_multiplier_.size() != D) {
    sum_multiplier_.Resize(D);
    math::Set<float, CPUContext>(
        D, 1.f, sum_multiplier_.mutable_data<float>(), &context_);
  }

  float* Pdata = P->mutable_data<float>();
  const float* weights = (InputSize() > 2 ? Input(2).data<float>() : nullptr);

  if (!spatial_mode_) {
    DCHECK_EQ(X.ndim(), 2);
    DCHECK((T.ndim() == 1) || (T.ndim() == 2 && T.dim32(1) == 1));
    DCHECK_EQ(T.dim32(0), N);

    if (sum_multiplier_.size() != D) {
      sum_multiplier_.Resize(D);
      math::Set<float, CPUContext>(
          D, 1.f, sum_multiplier_.mutable_data<float>(), &context_);
    }

    Tensor<CPUContext> scalef;
    scalef.Resize(N); // TOOD: what's the role of scale?

    SoftmaxCPU(context_, N, D, X, Pdata, scalef, sum_multiplier_);

    // Then compute cross entropy
    const int* label_data = T.data<int>();
    float loss_sum = 0.0;
    float weight_sum = 0.0;
    for (int i = 0; i < N; ++i) {
      CAFFE_ENFORCE(
          label_data[i] < D,
          "Label seems incorrect: label value larger than number of classes: ",
          label_data[i],
          " vs ",
          D);
      float weight = weights ? weights[i] : 1.0;
      float l = -log(std::max(Pdata[i * D + label_data[i]], 1e-20f)) * weight;
      loss_sum += l;
      weight_sum += weight;
    }

    avg_loss->Resize(vector<TIndex>());
    float* avg_loss_data = avg_loss->mutable_data<float>();
    avg_loss_data[0] = loss_sum * scale_ / weight_sum;
  } else {
    // Spatial mode, compute softmax for each x, y location
    DCHECK_EQ(X.ndim(), 4);
    DCHECK_EQ(T.ndim(), 3);

    int H = X.dim32(2);
    int W = X.dim32(3);

    const float* Xdata = X.data<float>();

    for (int i = 0; i < N; ++i) {
      for (int y = 0; y < H; ++y) {
        for (int x = 0; x < W; ++x) {
          // Subtract max on each cell for numerical reasons
          float max_val = (-1e20f);
          for (int c = 0; c < D; ++c) {
            // TODO optimize
            int idx = i * (H * W * D) + c * (H * W) + y * W + x;
            max_val = std::max(max_val, Xdata[idx]);
          }

          // Exponentiate
          float expsum = 0.0f;
          for (int c = 0; c < D; ++c) {
            int idx = i * (H * W * D) + c * (H * W) + y * W + x;
            float expx = exp(Xdata[idx] - max_val);
            Pdata[idx] = expx;
            expsum += expx;
          }

          // Normalize
          for (int c = 0; c < D; ++c) {
            int idx = i * (H * W * D) + c * (H * W) + y * W + x;
            Pdata[idx] /= expsum;
          }
        }
      }
    }

    // Compute the avg cross-entropy loss
    avg_loss->Resize(vector<TIndex>());
    float* avg_loss_data = avg_loss->mutable_data<float>();
    const int* label_data = T.data<int>();

    float sum_label_xent = 0.0f;
    float total_weight = 0.0;

    for (int y = 0; y < H; y++) {
      for (int x = 0; x < W; x++) {
        for (int i = 0; i < N; i++) {
          int label_idx = i * H * W + y * W + x;
          int label = label_data[label_idx];
          if (label != DONT_CARE) {
            int idx = i * (H * W * D) + label * (H * W) + y * W + x;
            float w = weights ? weights[label_idx] : 1.0;
            total_weight += w;
            sum_label_xent += -log(std::max(Pdata[idx], 1e-20f)) * w;
          }
        }
      }
    }
    *avg_loss_data = sum_label_xent / total_weight;
  } // if spatial
  return true;
}

template <>
bool SoftmaxWithLossGradientOp<float, CPUContext>::RunOnDevice() {
  auto& X = Input(0); // Logits
  auto& T = Input(1); // Labels / targets
  // Input(2) is weights if given
  auto& P = Input(InputSize() - 2); // Probabilities from softmax
  auto& d_avg_loss = Input(InputSize() - 1); // Gradient w.r.t. avg loss
  auto* dX = Output(0);
  const float* weights = (InputSize() > 4 ? Input(2).data<float>() : nullptr);

  int N = X.dim32(0);
  int D = X.dim32(1);
  dX->ResizeLike(X);
  DCHECK_EQ(T.dim32(0), N);

  if (!spatial_mode_) {
    DCHECK_EQ(X.ndim(), 2);
    DCHECK((T.ndim() == 1) || (T.ndim() == 2 && T.dim32(1) == 1));

    const float* Pdata = P.data<float>();
    float* dX_data = dX->mutable_data<float>();
    const int* label_data = T.data<int>();

    // Copy softmax probabilities into dX. All but the neuron
    // corresponding to the correct label has gradient equaling e(x_j)
    // which is the probability under softmax.
    context_.Copy<float, CPUContext, CPUContext>(P.size(), Pdata, dX_data);

    // Compute gradient for the matching labels.
    float total_weight = 0.0f;
    if (weights) {
      for (int i = 0; i < N; ++i) {
        int idx = i * D + label_data[i];
        float weight = weights[i];
        dX_data[idx] = Pdata[idx] - 1.0;
        for (int d = 0; d < D; d++) {
          int idx = i * D + d;
          dX_data[idx] *= weight;
        }

        total_weight += weight;
      }
    } else {
      for (int i = 0; i < N; ++i) {
        int idx = i * D + label_data[i];
        dX_data[idx] = Pdata[idx] - 1.0f;
      }
      total_weight = N;
    }

    // Scale by d_avg_loss / N
    math::Scale<float, CPUContext>(
        dX->size(),
        scale_ / total_weight * d_avg_loss.data<float>()[0],
        dX->data<float>(),
        dX_data,
        &context_);
  } else {
    // Spatial mode, compute softmax for each x, y location
    DCHECK_EQ(X.ndim(), 4);
    DCHECK_EQ(T.ndim(), 3);

    int H = X.dim32(2);
    int W = X.dim32(3);

    const float* Pdata = P.data<float>();
    float* dX_data = dX->mutable_data<float>();
    const int* label_data = T.data<int>();

    // Copy softmax probabilities into dX. All but the neuron
    // corresponding to the correct label has gradient equaling e(x_j)
    // which is the probability under softmax.
    context_.Copy<float, CPUContext, CPUContext>(P.size(), Pdata, dX_data);

    float total_weight = 0.0f;
    for (int y = 0; y < H; ++y) {
      for (int x = 0; x < W; ++x) {
        for (int i = 0; i < N; ++i) {
          int label_idx = i * H * W + y * W + x;
          int label = label_data[label_idx];

          if (label != DONT_CARE) {
            int idx = i * (H * W * D) + label * (H * W) + y * W + x;

            dX_data[idx] = (dX_data[idx] - 1.0);

            if (weights != nullptr) {
              float weight = weights[label_idx];
              for (int c = 0; c < D; ++c) {
                int k = i * (H * W * D) + c * (H * W) + y * W + x;
                dX_data[k] *= weight;
              }
              total_weight += weight;
            } else {
              total_weight += 1.0;
            }
          } else {

            // Set gradient to zero for coordinates where we have dont care
            for (int c = 0; c < D; ++c) {
              int idx = i * (H * W * D) + c * (H * W) + y * W + x;
              dX_data[idx] = 0;
            }
          }
        }
      }
    }

    math::Scale<float, CPUContext>(
        dX->size(),
        scale_ / total_weight,
        dX->data<float>(),
        dX_data,
        &context_);
    math::Scale<float, CPUContext>(
        dX->size(),
        d_avg_loss.data<float>(),
        dX->data<float>(),
        dX->mutable_data<float>(),
        &context_);
  }
  return true;
}

namespace {
class GetSoftmaxWithLossGradient : public GradientMakerBase {
  using GradientMakerBase::GradientMakerBase;
  vector<OperatorDef> GetGradientDefs() override {
    vector<string> blob_names{
        {I(0), I(1), O(0), GO(1)},
    };

    // Add weight blob, if given
    if (def_.input_size() == 3) {
      blob_names.emplace(blob_names.begin() + 2, I(2));
    }
    return SingleGradientDef(
        "SoftmaxWithLossGradient", "", blob_names, vector<string>{GI(0)});
  }
};

REGISTER_GRADIENT(SoftmaxWithLoss, GetSoftmaxWithLossGradient);
}
} // namespace caffe2
<commit_msg>caffe2/caffe2/operators/softmax_with_loss_op.cc: avoid shadowing warnings<commit_after>#include "softmax_with_loss_op.h"
#include "softmax_shared.h"

namespace caffe2 {

REGISTER_CPU_OPERATOR(SoftmaxWithLoss, SoftmaxWithLossOp<float, CPUContext>);
REGISTER_CPU_OPERATOR(
    SoftmaxWithLossGradient,
    SoftmaxWithLossGradientOp<float, CPUContext>);

// Input: X (logits), T (labels); Output: P (probs), Y
OPERATOR_SCHEMA(SoftmaxWithLoss).NumOutputs(2).SetDoc(R"DOC(
Combined Softmax and Cross-Entropy loss operator.
The operator computes the softmax normalized values for each layer in the batch
of the given input, after which cross-entropy loss is computed. This operator is
numerically more stable than separate Softmax and CrossEntropy ops.
The inputs are a 2-D tensor (Tensor<float>) of size
(batch_size x input_feature_dimensions) and tensor of labels (ground truth).
Output is tensor with the probability for each label for each example (N x D)
and averaged loss (scalar). Use parameter spatial=1 to enable spatial softmax.
Spatial softmax also supports special \"don't care\" label (-1) that is ignored
when computing the loss.

Optional third input blob can be used to weight the samples for the loss.
For the spatial version, weighting is by x,y position of the input.
)DOC");
// Input: X, T, P, dY; Output: dX
OPERATOR_SCHEMA(SoftmaxWithLossGradient).NumOutputs(1);

#define DONT_CARE (-1)

template <>
bool SoftmaxWithLossOp<float, CPUContext>::RunOnDevice() {
  auto& X = Input(0); // Logits
  auto& T = Input(1); // Labels / targets
  auto* P = Output(0); // Probabilities from softmax
  auto* avg_loss = Output(1); // Average loss
  int N = X.dim32(0);
  int D = X.dim32(1);

  P->ResizeLike(X);

  if (sum_multiplier_.size() != D) {
    sum_multiplier_.Resize(D);
    math::Set<float, CPUContext>(
        D, 1.f, sum_multiplier_.mutable_data<float>(), &context_);
  }

  float* Pdata = P->mutable_data<float>();
  const float* weights = (InputSize() > 2 ? Input(2).data<float>() : nullptr);

  if (!spatial_mode_) {
    DCHECK_EQ(X.ndim(), 2);
    DCHECK((T.ndim() == 1) || (T.ndim() == 2 && T.dim32(1) == 1));
    DCHECK_EQ(T.dim32(0), N);

    if (sum_multiplier_.size() != D) {
      sum_multiplier_.Resize(D);
      math::Set<float, CPUContext>(
          D, 1.f, sum_multiplier_.mutable_data<float>(), &context_);
    }

    Tensor<CPUContext> scalef;
    scalef.Resize(N); // TOOD: what's the role of scale?

    SoftmaxCPU(context_, N, D, X, Pdata, scalef, sum_multiplier_);

    // Then compute cross entropy
    const int* label_data = T.data<int>();
    float loss_sum = 0.0;
    float weight_sum = 0.0;
    for (int i = 0; i < N; ++i) {
      CAFFE_ENFORCE(
          label_data[i] < D,
          "Label seems incorrect: label value larger than number of classes: ",
          label_data[i],
          " vs ",
          D);
      float weight = weights ? weights[i] : 1.0;
      float l = -log(std::max(Pdata[i * D + label_data[i]], 1e-20f)) * weight;
      loss_sum += l;
      weight_sum += weight;
    }

    avg_loss->Resize(vector<TIndex>());
    float* avg_loss_data = avg_loss->mutable_data<float>();
    avg_loss_data[0] = loss_sum * scale_ / weight_sum;
  } else {
    // Spatial mode, compute softmax for each x, y location
    DCHECK_EQ(X.ndim(), 4);
    DCHECK_EQ(T.ndim(), 3);

    int H = X.dim32(2);
    int W = X.dim32(3);

    const float* Xdata = X.data<float>();

    for (int i = 0; i < N; ++i) {
      for (int y = 0; y < H; ++y) {
        for (int x = 0; x < W; ++x) {
          // Subtract max on each cell for numerical reasons
          float max_val = (-1e20f);
          for (int c = 0; c < D; ++c) {
            // TODO optimize
            int idx = i * (H * W * D) + c * (H * W) + y * W + x;
            max_val = std::max(max_val, Xdata[idx]);
          }

          // Exponentiate
          float expsum = 0.0f;
          for (int c = 0; c < D; ++c) {
            int idx = i * (H * W * D) + c * (H * W) + y * W + x;
            float expx = exp(Xdata[idx] - max_val);
            Pdata[idx] = expx;
            expsum += expx;
          }

          // Normalize
          for (int c = 0; c < D; ++c) {
            int idx = i * (H * W * D) + c * (H * W) + y * W + x;
            Pdata[idx] /= expsum;
          }
        }
      }
    }

    // Compute the avg cross-entropy loss
    avg_loss->Resize(vector<TIndex>());
    float* avg_loss_data = avg_loss->mutable_data<float>();
    const int* label_data = T.data<int>();

    float sum_label_xent = 0.0f;
    float total_weight = 0.0;

    for (int y = 0; y < H; y++) {
      for (int x = 0; x < W; x++) {
        for (int i = 0; i < N; i++) {
          int label_idx = i * H * W + y * W + x;
          int label = label_data[label_idx];
          if (label != DONT_CARE) {
            int idx = i * (H * W * D) + label * (H * W) + y * W + x;
            float w = weights ? weights[label_idx] : 1.0;
            total_weight += w;
            sum_label_xent += -log(std::max(Pdata[idx], 1e-20f)) * w;
          }
        }
      }
    }
    *avg_loss_data = sum_label_xent / total_weight;
  } // if spatial
  return true;
}

template <>
bool SoftmaxWithLossGradientOp<float, CPUContext>::RunOnDevice() {
  auto& X = Input(0); // Logits
  auto& T = Input(1); // Labels / targets
  // Input(2) is weights if given
  auto& P = Input(InputSize() - 2); // Probabilities from softmax
  auto& d_avg_loss = Input(InputSize() - 1); // Gradient w.r.t. avg loss
  auto* dX = Output(0);
  const float* weights = (InputSize() > 4 ? Input(2).data<float>() : nullptr);

  int N = X.dim32(0);
  int D = X.dim32(1);
  dX->ResizeLike(X);
  DCHECK_EQ(T.dim32(0), N);

  if (!spatial_mode_) {
    DCHECK_EQ(X.ndim(), 2);
    DCHECK((T.ndim() == 1) || (T.ndim() == 2 && T.dim32(1) == 1));

    const float* Pdata = P.data<float>();
    float* dX_data = dX->mutable_data<float>();
    const int* label_data = T.data<int>();

    // Copy softmax probabilities into dX. All but the neuron
    // corresponding to the correct label has gradient equaling e(x_j)
    // which is the probability under softmax.
    context_.Copy<float, CPUContext, CPUContext>(P.size(), Pdata, dX_data);

    // Compute gradient for the matching labels.
    float total_weight = 0.0f;
    if (weights) {
      for (int i = 0; i < N; ++i) {
        int idx = i * D + label_data[i];
        float weight = weights[i];
        dX_data[idx] = Pdata[idx] - 1.0;
        for (int d = 0; d < D; d++) {
          int k = i * D + d;
          dX_data[k] *= weight;
        }

        total_weight += weight;
      }
    } else {
      for (int i = 0; i < N; ++i) {
        int idx = i * D + label_data[i];
        dX_data[idx] = Pdata[idx] - 1.0f;
      }
      total_weight = N;
    }

    // Scale by d_avg_loss / N
    math::Scale<float, CPUContext>(
        dX->size(),
        scale_ / total_weight * d_avg_loss.data<float>()[0],
        dX->data<float>(),
        dX_data,
        &context_);
  } else {
    // Spatial mode, compute softmax for each x, y location
    DCHECK_EQ(X.ndim(), 4);
    DCHECK_EQ(T.ndim(), 3);

    int H = X.dim32(2);
    int W = X.dim32(3);

    const float* Pdata = P.data<float>();
    float* dX_data = dX->mutable_data<float>();
    const int* label_data = T.data<int>();

    // Copy softmax probabilities into dX. All but the neuron
    // corresponding to the correct label has gradient equaling e(x_j)
    // which is the probability under softmax.
    context_.Copy<float, CPUContext, CPUContext>(P.size(), Pdata, dX_data);

    float total_weight = 0.0f;
    for (int y = 0; y < H; ++y) {
      for (int x = 0; x < W; ++x) {
        for (int i = 0; i < N; ++i) {
          int label_idx = i * H * W + y * W + x;
          int label = label_data[label_idx];

          if (label != DONT_CARE) {
            int idx = i * (H * W * D) + label * (H * W) + y * W + x;

            dX_data[idx] = (dX_data[idx] - 1.0);

            if (weights != nullptr) {
              float weight = weights[label_idx];
              for (int c = 0; c < D; ++c) {
                int k = i * (H * W * D) + c * (H * W) + y * W + x;
                dX_data[k] *= weight;
              }
              total_weight += weight;
            } else {
              total_weight += 1.0;
            }
          } else {

            // Set gradient to zero for coordinates where we have dont care
            for (int c = 0; c < D; ++c) {
              int idx = i * (H * W * D) + c * (H * W) + y * W + x;
              dX_data[idx] = 0;
            }
          }
        }
      }
    }

    math::Scale<float, CPUContext>(
        dX->size(),
        scale_ / total_weight,
        dX->data<float>(),
        dX_data,
        &context_);
    math::Scale<float, CPUContext>(
        dX->size(),
        d_avg_loss.data<float>(),
        dX->data<float>(),
        dX->mutable_data<float>(),
        &context_);
  }
  return true;
}

namespace {
class GetSoftmaxWithLossGradient : public GradientMakerBase {
  using GradientMakerBase::GradientMakerBase;
  vector<OperatorDef> GetGradientDefs() override {
    vector<string> blob_names{
        {I(0), I(1), O(0), GO(1)},
    };

    // Add weight blob, if given
    if (def_.input_size() == 3) {
      blob_names.emplace(blob_names.begin() + 2, I(2));
    }
    return SingleGradientDef(
        "SoftmaxWithLossGradient", "", blob_names, vector<string>{GI(0)});
  }
};

REGISTER_GRADIENT(SoftmaxWithLoss, GetSoftmaxWithLossGradient);
}
} // namespace caffe2
<|endoftext|>


<commit_before>// This file is part of the HörTech Open Master Hearing Aid (openMHA)
// Copyright © 2006 2007 2008 2013 2016 HörTech gGmbH
//
// openMHA is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// openMHA is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License, version 3 for more details.
//
// You should have received a copy of the GNU Affero General Public License,
// version 3 along with openMHA.  If not, see <http://www.gnu.org/licenses/>.

#include "mhapluginloader.h"
#include "mha_algo_comm.hh"
#include <exception>
#include <getopt.h>
#include <stdarg.h>
#include <fstream>

std::string conv2latex(std::string s,bool iscolored=false)
{
    MHAParser::strreplace(s,"\\","$$\\backslash$");
    MHAParser::strreplace(s,"{","\\{");
    MHAParser::strreplace(s,"}","\\}");
    MHAParser::strreplace(s,"(c)","\\copyright{}");
    MHAParser::strreplace(s,"_","\\_");
    MHAParser::strreplace(s,"&","\\&");
    MHAParser::strreplace(s,"#","\\#");
    MHAParser::strreplace(s,"$","\\$");
    MHAParser::strreplace(s,"\\$\\$","$");
    MHAParser::strreplace(s,"<","$<$");
    MHAParser::strreplace(s,">","$>$");
    MHAParser::strreplace(s,"%","\\%");
    MHAParser::strreplace(s,"~","\\verb!~!");
    MHAParser::strreplace(s,"^","\\verb!^!");
    if( iscolored )
        s = "\\color{monitorcolor}{"+s+"}";
    return s;
}

class latex_doc_t {
public:
    latex_doc_t(const std::string& plugname,const std::string& plugin_macro);
    std::string get_latex_doc();
    std::string get_main_category();
    std::vector<std::string> get_categories();
private:
    std::string strdom(mha_domain_t d);
    std::string get_ac(MHAKernel::algo_comm_class_t& ac,std::string txt);
    std::string parsername(std::string s);
    std::string get_parser_var(MHAParser::base_t* p,std::string name);
    std::string get_parser_tab(MHAParser::base_t* p,std::string prefix);
    std::string plugname;
    std::string latex_plugname;
    MHAKernel::algo_comm_class_t ac;
    PluginLoader::mhapluginloader_t loader;
    std::string plugin_macro;
};

latex_doc_t::latex_doc_t(const std::string& plugname_,const std::string& plugin_macro_)
    : plugname(plugname_),
      latex_plugname(conv2latex(plugname)),
      loader(ac.get_c_handle(),plugname),
      plugin_macro(plugin_macro_)
{
}

std::string latex_doc_t::get_latex_doc()
{
    std::string retv("");
    retv += "\\" + plugin_macro + "{" + latex_plugname + "}\n\\label{plug:" + plugname + "}\n\n";
    retv += "\\index{" + latex_plugname + " (MHA plugin)}\n";
    retv += "\\index{plugin!" + latex_plugname + "}\n";
    std::vector<std::string> cats = loader.get_categories();
    for( unsigned int k=0;k<cats.size();k++){
        retv += "\\index{" + conv2latex(cats[k]) + " (plugin category)!" + latex_plugname + "}\n";
    }
    if( loader.has_parser() )
        retv += conv2latex(loader.parse("?help")) + "\n\n";
    std::string detailed_doc(loader.get_documentation());
    if( detailed_doc.size() )
        retv += "\\paragraph{Detailed description}\n\n" + loader.get_documentation() + "\n\n";
    retv += "\\paragraph{Supported domains}\n\nThe MHA plugin {\\tt "
        + latex_plugname + "} supportes these signal domains:\n\\begin{itemize}\n";
    mha_domain_t indom, outdom;
    unsigned int n_domains(0);
    for( indom = 0; indom < MHA_DOMAIN_MAX; indom++ )
        for( outdom = 0; outdom < MHA_DOMAIN_MAX; outdom++ )
            if( loader.has_process(indom,outdom) ){
                retv += " \\item " + strdom(indom) + " to " + strdom(outdom) + "\n";
                n_domains++;
            }
    if( n_domains == 0 )
        retv += " \\item This plugin has no processing callbacks.\n";
    retv += "\\end{itemize}\n\n";
    if( cats.size() > 0 ){
        retv += "\n\n\\paragraph{Categories}\n\n \\emph{" + cats[0] + "} ";
        for( unsigned int k=1;k<cats.size();k++){
            retv += conv2latex(cats[k]) + " ";
        }
        retv += "\n\n";
    }
    if( loader.has_parser() ){
        retv += get_parser_tab(&loader,"");
    }
    return retv;
}

std::string latex_doc_t::get_main_category()
{
    return loader.get_categories()[0];
}

std::vector<std::string> latex_doc_t::get_categories()
{
    return loader.get_categories();
}

std::string latex_doc_t::strdom( mha_domain_t d )
{
    switch( d ){
    case MHA_WAVEFORM : return "waveform";
    case MHA_SPECTRUM : return "spectrum";
    default: return "unknwon";
    }
}

std::string latex_doc_t::get_ac(MHAKernel::algo_comm_class_t& ac,std::string txt)
{
    std::string retv("");
    std::string stmp;
    std::vector<std::string> vstmp;
    stmp = ac.local_get_entries();
    if( stmp.size() ){
        retv += "AC variables " + txt + ":\n";
        MHAParser::StrCnv::str2val(std::string("[")+stmp+std::string("]"),vstmp);
        for( unsigned int k=0;k<vstmp.size();k++)
            retv += "  " + vstmp[k] + "\n";
        retv += "\n";
    }else{
        retv += "empty AC space " + txt + ".\n";
    }
    return retv;
}

std::string latex_doc_t::parsername(std::string s)
{
    if( s.rfind(".") < s.size() )
        s.erase(0,s.rfind(".")+1);
    return s;
}

std::string latex_doc_t::get_parser_var(MHAParser::base_t* p,std::string name)
{
    std::string retv("");
    bool is_parser(false);
    bool is_monitor(false);
    if( p->parse(name+"?type") == "parser" )
        is_parser = true;
    if( !is_parser )
        is_monitor = p->parse(name+"?perm")=="monitor";
    retv += conv2latex(parsername(name),is_monitor) + " & ";
    retv += conv2latex(p->parse(name+"?type"),is_monitor) + " & ";
    retv += conv2latex(p->parse(name+"?help"));
    std::string cmds = p->parse(name+"?cmds");
    if( cmds.find("range") < cmds.size() ){
        std::string range = p->parse(name+"?range");
        if( range.size() )
            retv += "\n\n{\\bf Range:} " + conv2latex(range);
    }
    retv += " & ";
    if( is_parser )
        retv += "(see below)";
    else{
        if( is_monitor )
            retv += conv2latex("(monitor)",is_monitor);
        else
            retv += conv2latex(p->parse(name+"?val"));
    }
    retv += "\\\\\n";
    return retv;
}

std::string latex_doc_t::get_parser_tab(MHAParser::base_t* p,std::string prefix)
{
    std::string retv;
    if( p->parse(prefix+"?type") == "parser" ){
        std::vector<std::string> entries;
        MHAParser::StrCnv::str2val(p->parse(prefix+"?entries"),entries);
        if( entries.size() ){
            unsigned int k;
            if( prefix.size() )
                retv += "\n\nVariables of sub-parser {\\tt " + conv2latex(parsername(prefix)) + "}:\n\n";
            else
                retv += "\n\\paragraph{Configuration variables}\n\n";
            retv += "{\\small\\begin{tabular}{|l|l|p{6cm}|l|}\n\\hline\n";
            retv += "{\\bf\\normalsize Name}&{\\bf\\normalsize Type}&{\\bf\\normalsize Description}&{\\bf\\normalsize Default}\\\\\\hline\n";
            for(k=0;k<entries.size();k++){
                retv += get_parser_var(p,prefix+"."+entries[k]);
                retv += "\\hline\n";
            }
            retv += "\\end{tabular}}\n";
            for(k=0;k<entries.size();k++){
                if( p->parse(prefix+"."+entries[k]+"?type")=="parser")
                    retv += get_parser_tab(p,prefix+"."+entries[k]);
            }
        }
    }else{
        if( prefix.size() == 0 ){
            retv += "\\paragraph{Configuration}\n\nThe plugin represents a variable node in the MHA configuration hierarchy.\n\n";
            retv += "{\\small\\begin{tabular}{|ll|p{5cm}|l|}\n\\hline\n";
            retv += "&{\\bf\\normalsize Type}&{\\bf\\normalsize Description}&{\\bf\\normalsize Default}\\\\\\hline\n";
            retv += get_parser_var(p,prefix);
            retv += "\\hline\n\\end{tabular}}\n";
        }
    }
    return retv;
}

void create_latex_doc(std::map<std::string,std::string>& doc,const std::string& plugname,const std::string& plugin_macro)
{
    latex_doc_t ldoc(plugname,plugin_macro);
    doc[ldoc.get_main_category()] += ldoc.get_latex_doc();
}

int main(int argc,char** argv)
{
    std::string ofname("generatemhaplugindoc.tex");
    std::string category_macro("section");
    std::string plugin_macro("subsection");
    int option;
    static struct option long_options[] = {
        {"ofname",         1, NULL, 'o'},
        {"category-macro", 1, NULL, 'c'},
        {"plugin-macro",   1, NULL, 'p'},
        {"help",           0, NULL, 'h'},
        {NULL,             0, NULL, 0  }
    };
    static char short_options[] = "o:c:p:h";
    while( (option = getopt_long(argc,argv,short_options,long_options,NULL)) > -1 ){
        switch( option ){
        case 'o' :
            ofname = optarg;
            break;
        case 'c' :
            category_macro = optarg;
            break;
        case 'p' :
            plugin_macro = optarg;
            break;
        case 'h' :
            std::cout <<
                "Options:\n"
                "  --ofname=name | -o name : set output file name\n"
                "  --category-macro=macro | -c macro : set LaTeX macro for categories\n"
                "  --plugin-macro=macro | -p macro : set LaTeX macro for plugins\n"
                "  --help | -h : print this help screen\n";
            exit(1);
            // break not needed, would be unreachable
        }
    }
    std::map<std::string,std::string> ldoc;
    for( int karg=optind;karg<argc;karg++ ){
        try{
            create_latex_doc(ldoc,argv[karg],plugin_macro);
        }
        catch(std::exception& e){
            std::cerr << argv[karg] << ": " << e.what() << std::endl;
        }
    }
    std::ofstream ofile(ofname.c_str());
    std::map<std::string,std::string>::const_iterator ldoc_i;
    for( ldoc_i=ldoc.begin();ldoc_i != ldoc.end(); ++ldoc_i ){
        if( ldoc_i->first != "other" ){
            ofile << "\\" << category_macro << "{Plugin category '" << conv2latex(ldoc_i->first) << "'}\n" << std::endl;
            ofile << ldoc_i->second << std::endl;
        }
    }
    for( ldoc_i=ldoc.begin();ldoc_i != ldoc.end(); ++ldoc_i ){
        if( ldoc_i->first == "other" ){
            ofile << "\\" << category_macro << "{Uncategorized plugins}\n" << std::endl;
            ofile << ldoc_i->second << std::endl;
        }
    }
    return 0;
}

// Local Variables:
// c-basic-offset: 4
// indent-tabs-mode: nil
// coding: utf-8-unix
// End:
<commit_msg>Adjust document hierarchy in plugin subsections.<commit_after>// This file is part of the HörTech Open Master Hearing Aid (openMHA)
// Copyright © 2006 2007 2008 2013 2016 HörTech gGmbH
//
// openMHA is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// openMHA is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License, version 3 for more details.
//
// You should have received a copy of the GNU Affero General Public License,
// version 3 along with openMHA.  If not, see <http://www.gnu.org/licenses/>.

#include "mhapluginloader.h"
#include "mha_algo_comm.hh"
#include <exception>
#include <getopt.h>
#include <stdarg.h>
#include <fstream>

std::string conv2latex(std::string s,bool iscolored=false)
{
    MHAParser::strreplace(s,"\\","$$\\backslash$");
    MHAParser::strreplace(s,"{","\\{");
    MHAParser::strreplace(s,"}","\\}");
    MHAParser::strreplace(s,"(c)","\\copyright{}");
    MHAParser::strreplace(s,"_","\\_");
    MHAParser::strreplace(s,"&","\\&");
    MHAParser::strreplace(s,"#","\\#");
    MHAParser::strreplace(s,"$","\\$");
    MHAParser::strreplace(s,"\\$\\$","$");
    MHAParser::strreplace(s,"<","$<$");
    MHAParser::strreplace(s,">","$>$");
    MHAParser::strreplace(s,"%","\\%");
    MHAParser::strreplace(s,"~","\\verb!~!");
    MHAParser::strreplace(s,"^","\\verb!^!");
    if( iscolored )
        s = "\\color{monitorcolor}{"+s+"}";
    return s;
}

class latex_doc_t {
public:
    latex_doc_t(const std::string& plugname,const std::string& plugin_macro);
    std::string get_latex_doc();
    std::string get_main_category();
    std::vector<std::string> get_categories();
private:
    std::string strdom(mha_domain_t d);
    std::string get_ac(MHAKernel::algo_comm_class_t& ac,std::string txt);
    std::string parsername(std::string s);
    std::string get_parser_var(MHAParser::base_t* p,std::string name);
    std::string get_parser_tab(MHAParser::base_t* p,std::string prefix);
    std::string plugname;
    std::string latex_plugname;
    MHAKernel::algo_comm_class_t ac;
    PluginLoader::mhapluginloader_t loader;
    std::string plugin_macro;
};

latex_doc_t::latex_doc_t(const std::string& plugname_,const std::string& plugin_macro_)
    : plugname(plugname_),
      latex_plugname(conv2latex(plugname)),
      loader(ac.get_c_handle(),plugname),
      plugin_macro(plugin_macro_)
{
}

std::string latex_doc_t::get_latex_doc()
{
    std::string plugin_subsection_macro = "\\paragraph";
    if (plugin_macro == "\\subsection")
        plugin_subsection_macro = "\\subsubsection";
    if (plugin_macro == "\\section")
        plugin_subsection_macro = "\\subsection";

    std::string retv("");
    retv += "\\" + plugin_macro + "{" + latex_plugname + "}\n\\label{plug:" + plugname + "}\n\n";
    retv += "\\index{" + latex_plugname + " (MHA plugin)}\n";
    retv += "\\index{plugin!" + latex_plugname + "}\n";
    std::vector<std::string> cats = loader.get_categories();
    for( unsigned int k=0;k<cats.size();k++){
        retv += "\\index{" + conv2latex(cats[k]) + " (plugin category)!" + latex_plugname + "}\n";
    }
    if( loader.has_parser() )
        retv += conv2latex(loader.parse("?help")) + "\n\n";
    std::string detailed_doc(loader.get_documentation());
    if( detailed_doc.size() )
        retv += "\\paragraph{Detailed description}\n\n" + loader.get_documentation() + "\n\n";
    retv += "\\paragraph{Supported domains}\n\nThe MHA plugin {\\tt "
        + latex_plugname + "} supportes these signal domains:\n\\begin{itemize}\n";
    mha_domain_t indom, outdom;
    unsigned int n_domains(0);
    for( indom = 0; indom < MHA_DOMAIN_MAX; indom++ )
        for( outdom = 0; outdom < MHA_DOMAIN_MAX; outdom++ )
            if( loader.has_process(indom,outdom) ){
                retv += " \\item " + strdom(indom) + " to " + strdom(outdom) + "\n";
                n_domains++;
            }
    if( n_domains == 0 )
        retv += " \\item This plugin has no processing callbacks.\n";
    retv += "\\end{itemize}\n\n";
    if( cats.size() > 0 ){
        retv += "\n\n\\paragraph{Categories}\n\n \\emph{" + cats[0] + "} ";
        for( unsigned int k=1;k<cats.size();k++){
            retv += conv2latex(cats[k]) + " ";
        }
        retv += "\n\n";
    }
    if( loader.has_parser() ){
        retv += get_parser_tab(&loader,"");
    }
    return retv;
}

std::string latex_doc_t::get_main_category()
{
    return loader.get_categories()[0];
}

std::vector<std::string> latex_doc_t::get_categories()
{
    return loader.get_categories();
}

std::string latex_doc_t::strdom( mha_domain_t d )
{
    switch( d ){
    case MHA_WAVEFORM : return "waveform";
    case MHA_SPECTRUM : return "spectrum";
    default: return "unknwon";
    }
}

std::string latex_doc_t::get_ac(MHAKernel::algo_comm_class_t& ac,std::string txt)
{
    std::string retv("");
    std::string stmp;
    std::vector<std::string> vstmp;
    stmp = ac.local_get_entries();
    if( stmp.size() ){
        retv += "AC variables " + txt + ":\n";
        MHAParser::StrCnv::str2val(std::string("[")+stmp+std::string("]"),vstmp);
        for( unsigned int k=0;k<vstmp.size();k++)
            retv += "  " + vstmp[k] + "\n";
        retv += "\n";
    }else{
        retv += "empty AC space " + txt + ".\n";
    }
    return retv;
}

std::string latex_doc_t::parsername(std::string s)
{
    if( s.rfind(".") < s.size() )
        s.erase(0,s.rfind(".")+1);
    return s;
}

std::string latex_doc_t::get_parser_var(MHAParser::base_t* p,std::string name)
{
    std::string retv("");
    bool is_parser(false);
    bool is_monitor(false);
    if( p->parse(name+"?type") == "parser" )
        is_parser = true;
    if( !is_parser )
        is_monitor = p->parse(name+"?perm")=="monitor";
    retv += conv2latex(parsername(name),is_monitor) + " & ";
    retv += conv2latex(p->parse(name+"?type"),is_monitor) + " & ";
    retv += conv2latex(p->parse(name+"?help"));
    std::string cmds = p->parse(name+"?cmds");
    if( cmds.find("range") < cmds.size() ){
        std::string range = p->parse(name+"?range");
        if( range.size() )
            retv += "\n\n{\\bf Range:} " + conv2latex(range);
    }
    retv += " & ";
    if( is_parser )
        retv += "(see below)";
    else{
        if( is_monitor )
            retv += conv2latex("(monitor)",is_monitor);
        else
            retv += conv2latex(p->parse(name+"?val"));
    }
    retv += "\\\\\n";
    return retv;
}

std::string latex_doc_t::get_parser_tab(MHAParser::base_t* p,std::string prefix)
{
    std::string retv;
    if( p->parse(prefix+"?type") == "parser" ){
        std::vector<std::string> entries;
        MHAParser::StrCnv::str2val(p->parse(prefix+"?entries"),entries);
        if( entries.size() ){
            unsigned int k;
            if( prefix.size() )
                retv += "\n\nVariables of sub-parser {\\tt " + conv2latex(parsername(prefix)) + "}:\n\n";
            else
                retv += "\n\\paragraph{Configuration variables}\n\n";
            retv += "{\\small\\begin{tabular}{|l|l|p{6cm}|l|}\n\\hline\n";
            retv += "{\\bf\\normalsize Name}&{\\bf\\normalsize Type}&{\\bf\\normalsize Description}&{\\bf\\normalsize Default}\\\\\\hline\n";
            for(k=0;k<entries.size();k++){
                retv += get_parser_var(p,prefix+"."+entries[k]);
                retv += "\\hline\n";
            }
            retv += "\\end{tabular}}\n";
            for(k=0;k<entries.size();k++){
                if( p->parse(prefix+"."+entries[k]+"?type")=="parser")
                    retv += get_parser_tab(p,prefix+"."+entries[k]);
            }
        }
    }else{
        if( prefix.size() == 0 ){
            retv += "\\paragraph{Configuration}\n\nThe plugin represents a variable node in the MHA configuration hierarchy.\n\n";
            retv += "{\\small\\begin{tabular}{|ll|p{5cm}|l|}\n\\hline\n";
            retv += "&{\\bf\\normalsize Type}&{\\bf\\normalsize Description}&{\\bf\\normalsize Default}\\\\\\hline\n";
            retv += get_parser_var(p,prefix);
            retv += "\\hline\n\\end{tabular}}\n";
        }
    }
    return retv;
}

void create_latex_doc(std::map<std::string,std::string>& doc,const std::string& plugname,const std::string& plugin_macro)
{
    latex_doc_t ldoc(plugname,plugin_macro);
    doc[ldoc.get_main_category()] += ldoc.get_latex_doc();
}

int main(int argc,char** argv)
{
    std::string ofname("generatemhaplugindoc.tex");
    std::string category_macro("section");
    std::string plugin_macro("subsection");
    int option;
    static struct option long_options[] = {
        {"ofname",         1, NULL, 'o'},
        {"category-macro", 1, NULL, 'c'},
        {"plugin-macro",   1, NULL, 'p'},
        {"help",           0, NULL, 'h'},
        {NULL,             0, NULL, 0  }
    };
    static char short_options[] = "o:c:p:h";
    while( (option = getopt_long(argc,argv,short_options,long_options,NULL)) > -1 ){
        switch( option ){
        case 'o' :
            ofname = optarg;
            break;
        case 'c' :
            category_macro = optarg;
            break;
        case 'p' :
            plugin_macro = optarg;
            break;
        case 'h' :
            std::cout <<
                "Options:\n"
                "  --ofname=name | -o name : set output file name\n"
                "  --category-macro=macro | -c macro : set LaTeX macro for categories\n"
                "  --plugin-macro=macro | -p macro : set LaTeX macro for plugins\n"
                "  --help | -h : print this help screen\n";
            exit(1);
            // break not needed, would be unreachable
        }
    }
    std::map<std::string,std::string> ldoc;
    for( int karg=optind;karg<argc;karg++ ){
        try{
            create_latex_doc(ldoc,argv[karg],plugin_macro);
        }
        catch(std::exception& e){
            std::cerr << argv[karg] << ": " << e.what() << std::endl;
        }
    }
    std::ofstream ofile(ofname.c_str());
    std::map<std::string,std::string>::const_iterator ldoc_i;
    for( ldoc_i=ldoc.begin();ldoc_i != ldoc.end(); ++ldoc_i ){
        if( ldoc_i->first != "other" ){
            ofile << "\\" << category_macro << "{Plugin category '" << conv2latex(ldoc_i->first) << "'}\n" << std::endl;
            ofile << ldoc_i->second << std::endl;
        }
    }
    for( ldoc_i=ldoc.begin();ldoc_i != ldoc.end(); ++ldoc_i ){
        if( ldoc_i->first == "other" ){
            ofile << "\\" << category_macro << "{Uncategorized plugins}\n" << std::endl;
            ofile << ldoc_i->second << std::endl;
        }
    }
    return 0;
}

// Local Variables:
// c-basic-offset: 4
// indent-tabs-mode: nil
// coding: utf-8-unix
// End:
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_canvas.hxx"

#include <tools/prex.h>
#include <X11/extensions/Xrender.h>
#include <X11/Xlib.h>
#include <tools/postx.h>

#include "cairo_xlib_cairo.hxx"

#include <vcl/sysdata.hxx>
#include <vcl/bitmap.hxx>
#include <vcl/virdev.hxx>
#include <basegfx/vector/b2isize.hxx>

namespace cairo
{

#include <cairo-xlib.h>
#include <cairo-xlib-xrender.h>

    // TODO(F3): svp headless case!

    bool IsCairoWorking( OutputDevice* pOutDev )
    {
        if( !pOutDev )
            return false;

        Display* pDisplay = (Display*)pOutDev->GetSystemGfxData().pDisplay;
        int nDummy;
        return XQueryExtension( pDisplay, "RENDER", &nDummy, &nDummy, &nDummy );
    }

    X11SysData::X11SysData() :
        pDisplay(NULL),
        hDrawable(0),
        pVisual(NULL),
        nScreen(0),
        nDepth(-1),
        aColormap(-1),
        pRenderFormat(NULL)
    {}

    X11SysData::X11SysData( const SystemGraphicsData& pSysDat ) :
        pDisplay(pSysDat.pDisplay),
        hDrawable(pSysDat.hDrawable),
        pVisual(pSysDat.pVisual),
        nScreen(pSysDat.nScreen),
        nDepth(pSysDat.nDepth),
        aColormap(pSysDat.aColormap),
        pRenderFormat(pSysDat.pXRenderFormat)
    {}

    X11SysData::X11SysData( const SystemEnvData& pSysDat ) :
        pDisplay(pSysDat.pDisplay),
        hDrawable(pSysDat.aWindow),
        pVisual(pSysDat.pVisual),
        nScreen(pSysDat.nScreen),
        nDepth(pSysDat.nDepth),
        aColormap(pSysDat.aColormap),
        pRenderFormat(NULL)
    {}

    X11Pixmap::~X11Pixmap()
    {
        if( mpDisplay && mhDrawable )
            XFreePixmap( (Display*)mpDisplay, mhDrawable );
    }

    /**
     * Surface::Surface:   Create Canvas surface with existing data
     * @param pSysData Platform native system environment data (struct SystemEnvData in vcl/inc/sysdata.hxx)
     * @param pSurface Cairo surface
     *
     * pSysData contains the platform native Drawable reference
     * This constructor only stores data, it does no processing.
     * It is used by e.g. Surface::getSimilar()
     *
     * Set the mpSurface as pSurface
     **/
    X11Surface::X11Surface( const X11SysData&            rSysData,
                            const X11PixmapSharedPtr&    rPixmap,
                            const CairoSurfaceSharedPtr& pSurface ) :
        maSysData(rSysData),
        mpPixmap(rPixmap),
        mpSurface(pSurface)
    {}

    /**
     * Surface::Surface:     Create generic Canvas surface using given Cairo Surface
     *
     * @param pSurface Cairo Surface
     *
     * This constructor only stores data, it does no processing.
     * It is used with e.g. cairo_image_surface_create_for_data()
     * Unlike other constructors, mpSysData is set to NULL
     *
     * Set the mpSurface as pSurface
     **/
    X11Surface::X11Surface( const CairoSurfaceSharedPtr& pSurface ) :
        maSysData(),
        mpPixmap(),
        mpSurface(pSurface)
    {}

    /**
     * Surface::Surface:   Create Canvas surface from Window reference.
     * @param pSysData Platform native system environment data (struct SystemEnvData in vcl/inc/sysdata.hxx)
     * @param x horizontal location of the new surface
     * @param y vertical location of the new surface
     * @param width width of the new surface
     * @param height height of the new surface
     *
     * pSysData contains the platform native Window reference.
     *
     * pSysData is used to create a surface on the Window
     *
     * Set the mpSurface to the new surface or NULL
     **/
    X11Surface::X11Surface( const X11SysData& rSysData, int x, int y, int width, int height ) :
        maSysData(rSysData),
        mpPixmap(),
        mpSurface(
            cairo_xlib_surface_create( (Display*)rSysData.pDisplay,
                                       rSysData.hDrawable,
                                       (Visual*)rSysData.pVisual,
                                       width + x, height + y ),
            &cairo_surface_destroy)
    {
        cairo_surface_set_device_offset(mpSurface.get(), x, y );
    }

    /**
     * Surface::Surface:   Create platfrom native Canvas surface from BitmapSystemData
     * @param pSysData Platform native system environment data (struct SystemEnvData in vcl/inc/sysdata.hxx)
     * @param pBmpData Platform native image data (struct BitmapSystemData in vcl/inc/bitmap.hxx)
     * @param width width of the new surface
     * @param height height of the new surface
     *
     * The pBmpData provides the imagedata that the created surface should contain.
     *
     * Set the mpSurface to the new surface or NULL
     **/
    X11Surface::X11Surface( const X11SysData&       rSysData,
                            const BitmapSystemData& rData ) :
        maSysData( rSysData ),
        mpPixmap(),
        mpSurface(
            cairo_xlib_surface_create( (Display*)rSysData.pDisplay,
                                       (Drawable)rData.aPixmap,
                                       (Visual*) rSysData.pVisual,
                                       rData.mnWidth, rData.mnHeight ),
            &cairo_surface_destroy)
    {
    }

    /**
     * Surface::getCairo:  Create Cairo (drawing object) for the Canvas surface
     *
     * @return new Cairo or NULL
     **/
    CairoSharedPtr X11Surface::getCairo() const
    {
        return CairoSharedPtr( cairo_create(mpSurface.get()),
                               &cairo_destroy );
    }

    /**
     * Surface::getSimilar:  Create new similar Canvas surface
     * @param aContent format of the new surface (cairo_content_t from cairo/src/cairo.h)
     * @param width width of the new surface
     * @param height height of the new surface
     *
     * Creates a new Canvas surface. This normally creates platform native surface, even though
     * generic function is used.
     *
     * Cairo surface from aContent (cairo_content_t)
     *
     * @return new surface or NULL
     **/
    SurfaceSharedPtr X11Surface::getSimilar( Content aContent, int width, int height ) const
    {
        Pixmap hPixmap;

        if( maSysData.pDisplay && maSysData.hDrawable )
        {
            XRenderPictFormat* pFormat;
            int nFormat;

            switch (aContent)
            {
                case CAIRO_CONTENT_ALPHA:
                    nFormat = PictStandardA8;
                    break;
                case CAIRO_CONTENT_COLOR:
                    nFormat = PictStandardRGB24;
                    break;
                case CAIRO_CONTENT_COLOR_ALPHA:
                default:
                    nFormat = PictStandardARGB32;
                    break;
            }

            pFormat = XRenderFindStandardFormat( (Display*)maSysData.pDisplay, nFormat );
            hPixmap = XCreatePixmap( (Display*)maSysData.pDisplay, maSysData.hDrawable,
                                     width > 0 ? width : 1, height > 0 ? height : 1,
                                     pFormat->depth );

            X11SysData aSysData(maSysData);
            aSysData.pRenderFormat = pFormat;
            return SurfaceSharedPtr(
                new X11Surface( aSysData,
                                X11PixmapSharedPtr(
                                    new X11Pixmap(hPixmap, maSysData.pDisplay)),
                                CairoSurfaceSharedPtr(
                                    cairo_xlib_surface_create_with_xrender_format(
                                        (Display*)maSysData.pDisplay,
                                        hPixmap,
                                        ScreenOfDisplay((Display *)maSysData.pDisplay, maSysData.nScreen),
                                        pFormat, width, height ),
                                    &cairo_surface_destroy) ));
        }
        else
            return SurfaceSharedPtr(
                new X11Surface( maSysData,
                                X11PixmapSharedPtr(),
                                CairoSurfaceSharedPtr(
                                    cairo_surface_create_similar( mpSurface.get(), aContent, width, height ),
                                    &cairo_surface_destroy )));
    }

    boost::shared_ptr<VirtualDevice> X11Surface::createVirtualDevice() const
    {
        SystemGraphicsData aSystemGraphicsData;

        aSystemGraphicsData.nSize = sizeof(SystemGraphicsData);
        aSystemGraphicsData.hDrawable = getDrawable();
        aSystemGraphicsData.pXRenderFormat = getRenderFormat();

        return boost::shared_ptr<VirtualDevice>(
            new VirtualDevice( &aSystemGraphicsData, getDepth() ));
    }

    /**
     * Surface::Resize:  Resizes the Canvas surface.
     * @param width new width of the surface
     * @param height new height of the surface
     *
     * Only used on X11.
     *
     * @return The new surface or NULL
     **/
    void X11Surface::Resize( int width, int height )
    {
        cairo_xlib_surface_set_size( mpSurface.get(), width, height );
    }

    void X11Surface::flush() const
    {
        XSync( (Display*)maSysData.pDisplay, false );
    }

    /**
     * Surface::getDepth:  Get the color depth of the Canvas surface.
     *
     * @return color depth
     **/
    int X11Surface::getDepth() const
    {
        if( maSysData.pRenderFormat )
            return ((XRenderPictFormat*) maSysData.pRenderFormat)->depth;

        return -1;
    }

    SurfaceSharedPtr createSurface( const CairoSurfaceSharedPtr& rSurface )
    {
        return SurfaceSharedPtr(new X11Surface(rSurface));
    }

    static X11SysData getSysData( const Window& rWindow )
    {
        const SystemEnvData* pSysData = GetSysData(&rWindow);

        if( !pSysData )
            return X11SysData();
        else
            return X11SysData(*pSysData);
    }

    static X11SysData getSysData( const VirtualDevice& rVirDev )
    {
        return X11SysData( rVirDev.GetSystemGfxData() );
    }

    SurfaceSharedPtr createSurface( const OutputDevice& rRefDevice,
                                    int x, int y, int width, int height )
    {
        if( rRefDevice.GetOutDevType() == OUTDEV_WINDOW )
            return SurfaceSharedPtr(new X11Surface(getSysData((const Window&)rRefDevice),
                                                   x,y,width,height));
        else if( rRefDevice.GetOutDevType() == OUTDEV_VIRDEV )
            return SurfaceSharedPtr(new X11Surface(getSysData((const VirtualDevice&)rRefDevice),
                                                   x,y,width,height));
        else
            return SurfaceSharedPtr();
    }

    SurfaceSharedPtr createBitmapSurface( const OutputDevice&     rRefDevice,
                                          const BitmapSystemData& rData,
                                          const Size&             rSize )
    {
        OSL_TRACE( "requested size: %d x %d available size: %d x %d",
                   rSize.Width(), rSize.Height(), rData.mnWidth, rData.mnHeight );
        if ( rData.mnWidth == rSize.Width() && rData.mnHeight == rSize.Height() )
        {
            if( rRefDevice.GetOutDevType() == OUTDEV_WINDOW )
                return SurfaceSharedPtr(new X11Surface(getSysData((const Window&)rRefDevice), rData ));
            else if( rRefDevice.GetOutDevType() == OUTDEV_VIRDEV )
                return SurfaceSharedPtr(new X11Surface(getSysData((const VirtualDevice&)rRefDevice), rData ));
        }

        return SurfaceSharedPtr();
    }
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>gtk3: fix cairo canvas crash for non X (or svp) backend<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_canvas.hxx"

#include <tools/prex.h>
#include <X11/extensions/Xrender.h>
#include <X11/Xlib.h>
#include <tools/postx.h>

#include "cairo_xlib_cairo.hxx"

#include <vcl/sysdata.hxx>
#include <vcl/bitmap.hxx>
#include <vcl/virdev.hxx>
#include <basegfx/vector/b2isize.hxx>

namespace cairo
{

#include <cairo-xlib.h>
#include <cairo-xlib-xrender.h>

    // TODO(F3): svp headless case!

    bool IsCairoWorking( OutputDevice* pOutDev )
    {
        if( !pOutDev )
            return false;

        Display* pDisplay = (Display*)pOutDev->GetSystemGfxData().pDisplay;
        if( !pDisplay )
            return false;

        int nDummy;
        return XQueryExtension( pDisplay, "RENDER", &nDummy, &nDummy, &nDummy );
    }

    X11SysData::X11SysData() :
        pDisplay(NULL),
        hDrawable(0),
        pVisual(NULL),
        nScreen(0),
        nDepth(-1),
        aColormap(-1),
        pRenderFormat(NULL)
    {}

    X11SysData::X11SysData( const SystemGraphicsData& pSysDat ) :
        pDisplay(pSysDat.pDisplay),
        hDrawable(pSysDat.hDrawable),
        pVisual(pSysDat.pVisual),
        nScreen(pSysDat.nScreen),
        nDepth(pSysDat.nDepth),
        aColormap(pSysDat.aColormap),
        pRenderFormat(pSysDat.pXRenderFormat)
    {}

    X11SysData::X11SysData( const SystemEnvData& pSysDat ) :
        pDisplay(pSysDat.pDisplay),
        hDrawable(pSysDat.aWindow),
        pVisual(pSysDat.pVisual),
        nScreen(pSysDat.nScreen),
        nDepth(pSysDat.nDepth),
        aColormap(pSysDat.aColormap),
        pRenderFormat(NULL)
    {}

    X11Pixmap::~X11Pixmap()
    {
        if( mpDisplay && mhDrawable )
            XFreePixmap( (Display*)mpDisplay, mhDrawable );
    }

    /**
     * Surface::Surface:   Create Canvas surface with existing data
     * @param pSysData Platform native system environment data (struct SystemEnvData in vcl/inc/sysdata.hxx)
     * @param pSurface Cairo surface
     *
     * pSysData contains the platform native Drawable reference
     * This constructor only stores data, it does no processing.
     * It is used by e.g. Surface::getSimilar()
     *
     * Set the mpSurface as pSurface
     **/
    X11Surface::X11Surface( const X11SysData&            rSysData,
                            const X11PixmapSharedPtr&    rPixmap,
                            const CairoSurfaceSharedPtr& pSurface ) :
        maSysData(rSysData),
        mpPixmap(rPixmap),
        mpSurface(pSurface)
    {}

    /**
     * Surface::Surface:     Create generic Canvas surface using given Cairo Surface
     *
     * @param pSurface Cairo Surface
     *
     * This constructor only stores data, it does no processing.
     * It is used with e.g. cairo_image_surface_create_for_data()
     * Unlike other constructors, mpSysData is set to NULL
     *
     * Set the mpSurface as pSurface
     **/
    X11Surface::X11Surface( const CairoSurfaceSharedPtr& pSurface ) :
        maSysData(),
        mpPixmap(),
        mpSurface(pSurface)
    {}

    /**
     * Surface::Surface:   Create Canvas surface from Window reference.
     * @param pSysData Platform native system environment data (struct SystemEnvData in vcl/inc/sysdata.hxx)
     * @param x horizontal location of the new surface
     * @param y vertical location of the new surface
     * @param width width of the new surface
     * @param height height of the new surface
     *
     * pSysData contains the platform native Window reference.
     *
     * pSysData is used to create a surface on the Window
     *
     * Set the mpSurface to the new surface or NULL
     **/
    X11Surface::X11Surface( const X11SysData& rSysData, int x, int y, int width, int height ) :
        maSysData(rSysData),
        mpPixmap(),
        mpSurface(
            cairo_xlib_surface_create( (Display*)rSysData.pDisplay,
                                       rSysData.hDrawable,
                                       (Visual*)rSysData.pVisual,
                                       width + x, height + y ),
            &cairo_surface_destroy)
    {
        cairo_surface_set_device_offset(mpSurface.get(), x, y );
    }

    /**
     * Surface::Surface:   Create platfrom native Canvas surface from BitmapSystemData
     * @param pSysData Platform native system environment data (struct SystemEnvData in vcl/inc/sysdata.hxx)
     * @param pBmpData Platform native image data (struct BitmapSystemData in vcl/inc/bitmap.hxx)
     * @param width width of the new surface
     * @param height height of the new surface
     *
     * The pBmpData provides the imagedata that the created surface should contain.
     *
     * Set the mpSurface to the new surface or NULL
     **/
    X11Surface::X11Surface( const X11SysData&       rSysData,
                            const BitmapSystemData& rData ) :
        maSysData( rSysData ),
        mpPixmap(),
        mpSurface(
            cairo_xlib_surface_create( (Display*)rSysData.pDisplay,
                                       (Drawable)rData.aPixmap,
                                       (Visual*) rSysData.pVisual,
                                       rData.mnWidth, rData.mnHeight ),
            &cairo_surface_destroy)
    {
    }

    /**
     * Surface::getCairo:  Create Cairo (drawing object) for the Canvas surface
     *
     * @return new Cairo or NULL
     **/
    CairoSharedPtr X11Surface::getCairo() const
    {
        return CairoSharedPtr( cairo_create(mpSurface.get()),
                               &cairo_destroy );
    }

    /**
     * Surface::getSimilar:  Create new similar Canvas surface
     * @param aContent format of the new surface (cairo_content_t from cairo/src/cairo.h)
     * @param width width of the new surface
     * @param height height of the new surface
     *
     * Creates a new Canvas surface. This normally creates platform native surface, even though
     * generic function is used.
     *
     * Cairo surface from aContent (cairo_content_t)
     *
     * @return new surface or NULL
     **/
    SurfaceSharedPtr X11Surface::getSimilar( Content aContent, int width, int height ) const
    {
        Pixmap hPixmap;

        if( maSysData.pDisplay && maSysData.hDrawable )
        {
            XRenderPictFormat* pFormat;
            int nFormat;

            switch (aContent)
            {
                case CAIRO_CONTENT_ALPHA:
                    nFormat = PictStandardA8;
                    break;
                case CAIRO_CONTENT_COLOR:
                    nFormat = PictStandardRGB24;
                    break;
                case CAIRO_CONTENT_COLOR_ALPHA:
                default:
                    nFormat = PictStandardARGB32;
                    break;
            }

            pFormat = XRenderFindStandardFormat( (Display*)maSysData.pDisplay, nFormat );
            hPixmap = XCreatePixmap( (Display*)maSysData.pDisplay, maSysData.hDrawable,
                                     width > 0 ? width : 1, height > 0 ? height : 1,
                                     pFormat->depth );

            X11SysData aSysData(maSysData);
            aSysData.pRenderFormat = pFormat;
            return SurfaceSharedPtr(
                new X11Surface( aSysData,
                                X11PixmapSharedPtr(
                                    new X11Pixmap(hPixmap, maSysData.pDisplay)),
                                CairoSurfaceSharedPtr(
                                    cairo_xlib_surface_create_with_xrender_format(
                                        (Display*)maSysData.pDisplay,
                                        hPixmap,
                                        ScreenOfDisplay((Display *)maSysData.pDisplay, maSysData.nScreen),
                                        pFormat, width, height ),
                                    &cairo_surface_destroy) ));
        }
        else
            return SurfaceSharedPtr(
                new X11Surface( maSysData,
                                X11PixmapSharedPtr(),
                                CairoSurfaceSharedPtr(
                                    cairo_surface_create_similar( mpSurface.get(), aContent, width, height ),
                                    &cairo_surface_destroy )));
    }

    boost::shared_ptr<VirtualDevice> X11Surface::createVirtualDevice() const
    {
        SystemGraphicsData aSystemGraphicsData;

        aSystemGraphicsData.nSize = sizeof(SystemGraphicsData);
        aSystemGraphicsData.hDrawable = getDrawable();
        aSystemGraphicsData.pXRenderFormat = getRenderFormat();

        return boost::shared_ptr<VirtualDevice>(
            new VirtualDevice( &aSystemGraphicsData, getDepth() ));
    }

    /**
     * Surface::Resize:  Resizes the Canvas surface.
     * @param width new width of the surface
     * @param height new height of the surface
     *
     * Only used on X11.
     *
     * @return The new surface or NULL
     **/
    void X11Surface::Resize( int width, int height )
    {
        cairo_xlib_surface_set_size( mpSurface.get(), width, height );
    }

    void X11Surface::flush() const
    {
        XSync( (Display*)maSysData.pDisplay, false );
    }

    /**
     * Surface::getDepth:  Get the color depth of the Canvas surface.
     *
     * @return color depth
     **/
    int X11Surface::getDepth() const
    {
        if( maSysData.pRenderFormat )
            return ((XRenderPictFormat*) maSysData.pRenderFormat)->depth;

        return -1;
    }

    SurfaceSharedPtr createSurface( const CairoSurfaceSharedPtr& rSurface )
    {
        return SurfaceSharedPtr(new X11Surface(rSurface));
    }

    static X11SysData getSysData( const Window& rWindow )
    {
        const SystemEnvData* pSysData = GetSysData(&rWindow);

        if( !pSysData )
            return X11SysData();
        else
            return X11SysData(*pSysData);
    }

    static X11SysData getSysData( const VirtualDevice& rVirDev )
    {
        return X11SysData( rVirDev.GetSystemGfxData() );
    }

    SurfaceSharedPtr createSurface( const OutputDevice& rRefDevice,
                                    int x, int y, int width, int height )
    {
        if( rRefDevice.GetOutDevType() == OUTDEV_WINDOW )
            return SurfaceSharedPtr(new X11Surface(getSysData((const Window&)rRefDevice),
                                                   x,y,width,height));
        else if( rRefDevice.GetOutDevType() == OUTDEV_VIRDEV )
            return SurfaceSharedPtr(new X11Surface(getSysData((const VirtualDevice&)rRefDevice),
                                                   x,y,width,height));
        else
            return SurfaceSharedPtr();
    }

    SurfaceSharedPtr createBitmapSurface( const OutputDevice&     rRefDevice,
                                          const BitmapSystemData& rData,
                                          const Size&             rSize )
    {
        OSL_TRACE( "requested size: %d x %d available size: %d x %d",
                   rSize.Width(), rSize.Height(), rData.mnWidth, rData.mnHeight );
        if ( rData.mnWidth == rSize.Width() && rData.mnHeight == rSize.Height() )
        {
            if( rRefDevice.GetOutDevType() == OUTDEV_WINDOW )
                return SurfaceSharedPtr(new X11Surface(getSysData((const Window&)rRefDevice), rData ));
            else if( rRefDevice.GetOutDevType() == OUTDEV_VIRDEV )
                return SurfaceSharedPtr(new X11Surface(getSysData((const VirtualDevice&)rRefDevice), rData ));
        }

        return SurfaceSharedPtr();
    }
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>