<commit_before>/*
 * C++ Mandelbrot.
 *
 * Usage: mandelbrot_cpp <image_width> <image_height> <max_iterations> <repetitions (1+)> <center x> <center y> <section height> <gradient filename> <output filename>
 * Example: mandelbrot_cpp 320 200 20 1 -0.5 0.0 2.0 blue.gradient mandelbrot.raw
 *
 * Gradient file example:
 *   0.0: 0.0, 0.0, 0.0
 *   0.5: 0.0, 0.0, 1.0
 *   1.0: 1.0, 1.0, 1.0
 *
 * Tobias Brückner, 2019
 */

#include <algorithm>
#include <chrono>
#include <cmath>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <limits>
#include <numeric>
#include <regex>
#include <sstream>
#include <string>
#include <tuple>
#include <vector>

enum class Error {
    AllocMemory = 1,
    EvalArgs,
    LoadGradient,
    SaveImage,
    GetTime
};

// Compare two float values for "enough" equality.
bool equal_enough(float a, float b) noexcept
{
    constexpr float epsilon = std::numeric_limits<float>::epsilon();

    a = std::abs(a);
    b = std::abs(b);

    return std::abs(a - b) <= std::max(a, b) * epsilon;
}

struct PixelColor {
    unsigned char r, g, b;
};

struct GradientColor {
    float pos;
    float r, g, b;
    bool operator==(const float p) const noexcept { return equal_enough(p, pos); }
    bool operator<(const GradientColor& col) const noexcept { return pos < col.pos; }
};

struct Gradient {
    std::vector<GradientColor> colors;
};

void die(Error error)
{
    std::cerr << "Error: " << static_cast<int>(error) << std::endl;
    std::exit(static_cast<int>(error));
}

Gradient load_gradient(const std::string& filename)
{
    Gradient gradient;
    gradient.colors.push_back(GradientColor{0.0f, 0.0f, 0.0f, 0.0f});
    gradient.colors.push_back(GradientColor{1.0f, 1.0f, 1.0f, 1.0f});

    std::ifstream in{filename};
    std::string line;

    if (!in.is_open())
        die(Error::LoadGradient);

    const std::regex re{R"(([0-9]*\.?[0-9]+):\s*([0-9]*\.?[0-9]+),\s*([0-9]*\.?[0-9]+),\s*([0-9]*\.?[0-9]+))"};
    std::smatch m;

    while (std::getline(in, line)) {
        if (std::regex_match(line, m, re)) {
            if (m.size() == 4+1) {
                float pos = std::stof(m[1]);
                auto col = std::find(gradient.colors.begin(), gradient.colors.end(), pos);

                if (col != gradient.colors.end())
                    *col = GradientColor{pos, std::stof(m[2]), std::stof(m[3]), std::stof(m[4])};
                else
                    gradient.colors.push_back({pos, std::stof(m[2]), std::stof(m[3]), std::stof(m[4])});
            }
        }
    }

    std::sort(gradient.colors.begin(), gradient.colors.end());
    return gradient;
}

void color_from_gradient_range(const GradientColor& left, const GradientColor& right, const float pos, PixelColor& pixel_color) noexcept
{
    const float pos2 = (pos - left.pos) / (right.pos - left.pos);

    pixel_color.r = static_cast<unsigned char>(255.0f * (((right.r - left.r) * pos2) + left.r));
    pixel_color.g = static_cast<unsigned char>(255.0f * (((right.g - left.g) * pos2) + left.g));
    pixel_color.b = static_cast<unsigned char>(255.0f * (((right.b - left.b) * pos2) + left.b));
}

void color_from_gradient(const Gradient& gradient, const float pos, PixelColor& pixel_color) noexcept
{
    const auto end = gradient.colors.cend();

    const auto it = std::adjacent_find(gradient.colors.cbegin(), end,
        [&](const GradientColor& left, const GradientColor& right) { return left.pos <= pos && pos <= right.pos; });

    if (it != end)
        color_from_gradient_range(*it, *(it+1), pos, pixel_color);
}

void mandelbrot_calc(const int image_width, const int image_height, const int max_iterations, const double center_x, const double center_y, const double height,
                     std::vector<int>& histogram, std::vector<int>& iterations_per_pixel, std::vector<float>& smoothed_distances_to_next_iteration_per_pixel) noexcept
{
    const double width = height * (static_cast<double>(image_width) / static_cast<double>(image_height));

    const double x_left   = center_x - width / 2.0;
 // const double x_right  = center_x + width / 2.0;
    const double y_top    = center_y + height / 2.0;
 // const double y_bottom = center_y - height / 2.0;

    constexpr double bailout = 20.0;
    constexpr double bailout_squared = bailout * bailout;
    const double log_log_bailout = std::log(std::log(bailout));
    const double log_2 = std::log(2.0);

    double final_magnitude = 0.0;

    std::fill(histogram.begin(), histogram.end(), 0);

    for (int pixel_y = 0; pixel_y < image_height; ++pixel_y) {
        const double y0 = y_top - height * (static_cast<double>(pixel_y) / static_cast<double>(image_height));

        for (int pixel_x = 0; pixel_x < image_width; ++pixel_x) {
            const double x0 = x_left + width * (static_cast<double>(pixel_x) / static_cast<double>(image_width));

            double x = 0.0;
            double y = 0.0;

            // iteration, will be from 1 to max_iterations once the loop is done
            int iter = 0;

            while (iter < max_iterations) {
                const double x_squared = x*x;
                const double y_squared = y*y;

                if (x_squared + y_squared >= bailout_squared) {
                    final_magnitude = std::sqrt(x_squared + y_squared);
                    break;
                }

                const double xtemp = x_squared - y_squared + x0;
                y = 2.0*x*y + y0;
                x = xtemp;

                ++iter;
            }

            const int pixel = pixel_y * image_width + pixel_x;

            if (iter < max_iterations) {
                smoothed_distances_to_next_iteration_per_pixel[static_cast<std::size_t>(pixel)] = 1.0f - std::min(1.0f, static_cast<float>((std::log(std::log(final_magnitude)) - log_log_bailout) / log_2));
                ++histogram[static_cast<std::size_t>(iter)];  // no need to count histogram[max_iterations]
            }

            iterations_per_pixel[static_cast<std::size_t>(pixel)] = iter;  // 1 .. max_iterations
        }
    }
}

void mandelbrot_colorize(const int max_iterations, const Gradient& gradient,
                         std::vector<PixelColor>& image_data, const std::vector<int>& histogram, const std::vector<int>& iterations_per_pixel, const std::vector<float>& smoothed_distances_to_next_iteration_per_pixel, std::vector<float>& normalized_colors) noexcept
{
    // Sum all iterations, not counting the last one at position histogram[max_iterations] (which
    // are points in the Mandelbrot Set).
    const float total_iterations = std::accumulate(std::next(histogram.cbegin()), std::prev(histogram.cend()), 0);

    // Normalize the colors (0.0 .. 1.0) based on how often they are used in the image, not counting
    // histogram[max_iterations] (which are points in the Mandelbrot Set).
    int running_total = 0;

    for (std::size_t i = 1; i < static_cast<std::size_t>(max_iterations); ++i) {
        running_total += histogram[i];
        normalized_colors[i] = running_total / total_iterations;
    }

    auto iter = iterations_per_pixel.cbegin();  // in range of 1 .. max_iterations
    auto smoothed_distance_to_next_iteration = smoothed_distances_to_next_iteration_per_pixel.cbegin();  // in range of 0 .. <1.0

    for (auto& pixel : image_data) {
        if (*iter == max_iterations) {
            // pixels with max. iterations (aka. inside the Mandelbrot Set) are always black
            pixel.r = 0;
            pixel.g = 0;
            pixel.b = 0;
        } else {
            // we use the color of the previous iteration in order to cover the full gradient range
            const float color_of_previous_iter = normalized_colors[static_cast<std::size_t>(*iter - 1)];
            const float color_of_current_iter  = normalized_colors[static_cast<std::size_t>(*iter)];
            const float pos_in_gradient = color_of_previous_iter + *smoothed_distance_to_next_iteration * (color_of_current_iter - color_of_previous_iter);

            color_from_gradient(gradient, pos_in_gradient, pixel);
        }

        ++iter;
        ++smoothed_distance_to_next_iteration;
    }
}

bool save_image(const std::string& filename, const std::vector<PixelColor>& image_data)
{
    std::ofstream out{filename};

    if (!out.is_open())
        return false;

    for (auto p : image_data)
        out << p.r << p.g << p.b;

    return true;
}

double mean(const std::vector<double>& values)
{
    return std::accumulate(values.begin(), values.end(), 0.0) / values.size();
}

double median(const std::vector<double>& values)
{
    std::vector<double> sorted_values{values};
    std::sort(sorted_values.begin(), sorted_values.end());

    if (sorted_values.size() % 2)
        return sorted_values[(sorted_values.size() - 1) / 2];
    else
        return (sorted_values[sorted_values.size() / 2 - 1] + sorted_values[sorted_values.size() / 2]) / 2.0;
}

void show_summary(const std::vector<double>& durations)
{
    if (durations.size() == 1) {
        std::cout << durations[0] << " s" << std::endl;
    } else {
        std::vector<double> sorted_values{durations};
        std::sort(sorted_values.begin(), sorted_values.end());

        std::cout << "mean: " << mean(sorted_values) << " s, median: " << median(sorted_values) << " s (repetitions=" << sorted_values.size() << ") ["
                  << sorted_values[0];

        for (auto p = std::next(sorted_values.cbegin()); p != sorted_values.cend(); ++p)
            std::cout << ", " << *p;

        std::cout << "]" << std::endl;
    }
}

template <typename T>
T eval_arg(const char* s, T min, T max)
{
    T value;
    std::istringstream in{s};

    if (!(in >> value) || value < min || value > max)
        die(Error::EvalArgs);

    return value;
}

std::tuple<int, int, int, int, double, double, double, std::string, std::string> eval_args(const int argc, char const* argv[])
{
    if (argc < 10)
        die(Error::EvalArgs);

    auto image_width    = eval_arg(argv[1], 1, 100000);
    auto image_height   = eval_arg(argv[2], 1, 100000);
    auto max_iterations = eval_arg(argv[3], 1, 1000000000);
    auto repetitions    = eval_arg(argv[4], 1, 1000000);
    auto center_x       = eval_arg(argv[5], -100.0, 100.0);
    auto center_y       = eval_arg(argv[6], -100.0, 100.0);
    auto height         = eval_arg(argv[7], -100.0, 100.0);
    auto colors         = std::string{argv[8]};
    auto filename       = std::string{argv[9]};

    return std::make_tuple(image_width, image_height, max_iterations, repetitions, center_x, center_y, height, colors, filename);
}

void go(const int image_width, const int image_height, const int max_iterations, const double center_x, const double center_y, const double height, const Gradient& gradient, std::vector<PixelColor>& image_data, std::vector<double>& durations, const int repetitions) noexcept
{
    // histogram & normalized_colors: for simplicity we only use indices [1] .. [max_iterations], [0] is unused
    std::vector<int> histogram(static_cast<std::size_t>(max_iterations + 1));
    std::vector<float> normalized_colors(static_cast<std::size_t>(max_iterations + 1));
    std::vector<int> iterations_per_pixel(static_cast<std::size_t>(image_width * image_height));
    std::vector<float> smoothed_distances_to_next_iteration_per_pixel(static_cast<std::size_t>(image_width * image_height));

    for (int i = 0; i < repetitions; ++i) {
        const auto t1 = std::chrono::high_resolution_clock::now();
        mandelbrot_calc(image_width, image_height, max_iterations, center_x, center_y, height, histogram, iterations_per_pixel, smoothed_distances_to_next_iteration_per_pixel);
        mandelbrot_colorize(max_iterations, gradient, image_data, histogram, iterations_per_pixel, smoothed_distances_to_next_iteration_per_pixel, normalized_colors);
        const auto t2 = std::chrono::high_resolution_clock::now();

        durations.push_back(std::chrono::duration<double>{t2 - t1}.count());
    }
}

int main(int argc, char const* argv[])
{
    auto [image_width, image_height, max_iterations, repetitions, center_x, center_y, height, gradient_filename, filename] = eval_args(argc, argv);
    auto gradient = load_gradient(gradient_filename);

    std::vector<PixelColor> image_data(static_cast<unsigned long>(image_width * image_height));
    std::vector<double> durations;

    go(image_width, image_height, max_iterations, center_x, center_y, height, gradient, image_data, durations, repetitions);

    if (!save_image(filename, image_data))
        die(Error::SaveImage);

    show_summary(durations);
}
<commit_msg>Remove compiler cast warning<commit_after>/*
 * C++ Mandelbrot.
 *
 * Usage: mandelbrot_cpp <image_width> <image_height> <max_iterations> <repetitions (1+)> <center x> <center y> <section height> <gradient filename> <output filename>
 * Example: mandelbrot_cpp 320 200 20 1 -0.5 0.0 2.0 blue.gradient mandelbrot.raw
 *
 * Gradient file example:
 *   0.0: 0.0, 0.0, 0.0
 *   0.5: 0.0, 0.0, 1.0
 *   1.0: 1.0, 1.0, 1.0
 *
 * Tobias Brückner, 2019
 */

#include <algorithm>
#include <chrono>
#include <cmath>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <limits>
#include <numeric>
#include <regex>
#include <sstream>
#include <string>
#include <tuple>
#include <vector>

enum class Error {
    AllocMemory = 1,
    EvalArgs,
    LoadGradient,
    SaveImage,
    GetTime
};

// Compare two float values for "enough" equality.
bool equal_enough(float a, float b) noexcept
{
    constexpr float epsilon = std::numeric_limits<float>::epsilon();

    a = std::abs(a);
    b = std::abs(b);

    return std::abs(a - b) <= std::max(a, b) * epsilon;
}

struct PixelColor {
    unsigned char r, g, b;
};

struct GradientColor {
    float pos;
    float r, g, b;
    bool operator==(const float p) const noexcept { return equal_enough(p, pos); }
    bool operator<(const GradientColor& col) const noexcept { return pos < col.pos; }
};

struct Gradient {
    std::vector<GradientColor> colors;
};

void die(Error error)
{
    std::cerr << "Error: " << static_cast<int>(error) << std::endl;
    std::exit(static_cast<int>(error));
}

Gradient load_gradient(const std::string& filename)
{
    Gradient gradient;
    gradient.colors.push_back(GradientColor{0.0f, 0.0f, 0.0f, 0.0f});
    gradient.colors.push_back(GradientColor{1.0f, 1.0f, 1.0f, 1.0f});

    std::ifstream in{filename};
    std::string line;

    if (!in.is_open())
        die(Error::LoadGradient);

    const std::regex re{R"(([0-9]*\.?[0-9]+):\s*([0-9]*\.?[0-9]+),\s*([0-9]*\.?[0-9]+),\s*([0-9]*\.?[0-9]+))"};
    std::smatch m;

    while (std::getline(in, line)) {
        if (std::regex_match(line, m, re)) {
            if (m.size() == 4+1) {
                float pos = std::stof(m[1]);
                auto col = std::find(gradient.colors.begin(), gradient.colors.end(), pos);

                if (col != gradient.colors.end())
                    *col = GradientColor{pos, std::stof(m[2]), std::stof(m[3]), std::stof(m[4])};
                else
                    gradient.colors.push_back({pos, std::stof(m[2]), std::stof(m[3]), std::stof(m[4])});
            }
        }
    }

    std::sort(gradient.colors.begin(), gradient.colors.end());
    return gradient;
}

void color_from_gradient_range(const GradientColor& left, const GradientColor& right, const float pos, PixelColor& pixel_color) noexcept
{
    const float pos2 = (pos - left.pos) / (right.pos - left.pos);

    pixel_color.r = static_cast<unsigned char>(255.0f * (((right.r - left.r) * pos2) + left.r));
    pixel_color.g = static_cast<unsigned char>(255.0f * (((right.g - left.g) * pos2) + left.g));
    pixel_color.b = static_cast<unsigned char>(255.0f * (((right.b - left.b) * pos2) + left.b));
}

void color_from_gradient(const Gradient& gradient, const float pos, PixelColor& pixel_color) noexcept
{
    const auto end = gradient.colors.cend();

    const auto it = std::adjacent_find(gradient.colors.cbegin(), end,
        [&](const GradientColor& left, const GradientColor& right) { return left.pos <= pos && pos <= right.pos; });

    if (it != end)
        color_from_gradient_range(*it, *(it+1), pos, pixel_color);
}

void mandelbrot_calc(const int image_width, const int image_height, const int max_iterations, const double center_x, const double center_y, const double height,
                     std::vector<int>& histogram, std::vector<int>& iterations_per_pixel, std::vector<float>& smoothed_distances_to_next_iteration_per_pixel) noexcept
{
    const double width = height * (static_cast<double>(image_width) / static_cast<double>(image_height));

    const double x_left   = center_x - width / 2.0;
 // const double x_right  = center_x + width / 2.0;
    const double y_top    = center_y + height / 2.0;
 // const double y_bottom = center_y - height / 2.0;

    constexpr double bailout = 20.0;
    constexpr double bailout_squared = bailout * bailout;
    const double log_log_bailout = std::log(std::log(bailout));
    const double log_2 = std::log(2.0);

    double final_magnitude = 0.0;

    std::fill(histogram.begin(), histogram.end(), 0);

    for (int pixel_y = 0; pixel_y < image_height; ++pixel_y) {
        const double y0 = y_top - height * (static_cast<double>(pixel_y) / static_cast<double>(image_height));

        for (int pixel_x = 0; pixel_x < image_width; ++pixel_x) {
            const double x0 = x_left + width * (static_cast<double>(pixel_x) / static_cast<double>(image_width));

            double x = 0.0;
            double y = 0.0;

            // iteration, will be from 1 to max_iterations once the loop is done
            int iter = 0;

            while (iter < max_iterations) {
                const double x_squared = x*x;
                const double y_squared = y*y;

                if (x_squared + y_squared >= bailout_squared) {
                    final_magnitude = std::sqrt(x_squared + y_squared);
                    break;
                }

                const double xtemp = x_squared - y_squared + x0;
                y = 2.0*x*y + y0;
                x = xtemp;

                ++iter;
            }

            const int pixel = pixel_y * image_width + pixel_x;

            if (iter < max_iterations) {
                smoothed_distances_to_next_iteration_per_pixel[static_cast<std::size_t>(pixel)] = 1.0f - std::min(1.0f, static_cast<float>((std::log(std::log(final_magnitude)) - log_log_bailout) / log_2));
                ++histogram[static_cast<std::size_t>(iter)];  // no need to count histogram[max_iterations]
            }

            iterations_per_pixel[static_cast<std::size_t>(pixel)] = iter;  // 1 .. max_iterations
        }
    }
}

void mandelbrot_colorize(const int max_iterations, const Gradient& gradient,
                         std::vector<PixelColor>& image_data, const std::vector<int>& histogram, const std::vector<int>& iterations_per_pixel, const std::vector<float>& smoothed_distances_to_next_iteration_per_pixel, std::vector<float>& normalized_colors) noexcept
{
    // Sum all iterations, not counting the last one at position histogram[max_iterations] (which
    // are points in the Mandelbrot Set).
    const float total_iterations = static_cast<float>(std::accumulate(std::next(histogram.cbegin()), std::prev(histogram.cend()), 0));

    // Normalize the colors (0.0 .. 1.0) based on how often they are used in the image, not counting
    // histogram[max_iterations] (which are points in the Mandelbrot Set).
    int running_total = 0;

    for (std::size_t i = 1; i < static_cast<std::size_t>(max_iterations); ++i) {
        running_total += histogram[i];
        normalized_colors[i] = running_total / total_iterations;
    }

    auto iter = iterations_per_pixel.cbegin();  // in range of 1 .. max_iterations
    auto smoothed_distance_to_next_iteration = smoothed_distances_to_next_iteration_per_pixel.cbegin();  // in range of 0 .. <1.0

    for (auto& pixel : image_data) {
        if (*iter == max_iterations) {
            // pixels with max. iterations (aka. inside the Mandelbrot Set) are always black
            pixel.r = 0;
            pixel.g = 0;
            pixel.b = 0;
        } else {
            // we use the color of the previous iteration in order to cover the full gradient range
            const float color_of_previous_iter = normalized_colors[static_cast<std::size_t>(*iter - 1)];
            const float color_of_current_iter  = normalized_colors[static_cast<std::size_t>(*iter)];
            const float pos_in_gradient = color_of_previous_iter + *smoothed_distance_to_next_iteration * (color_of_current_iter - color_of_previous_iter);

            color_from_gradient(gradient, pos_in_gradient, pixel);
        }

        ++iter;
        ++smoothed_distance_to_next_iteration;
    }
}

bool save_image(const std::string& filename, const std::vector<PixelColor>& image_data)
{
    std::ofstream out{filename};

    if (!out.is_open())
        return false;

    for (auto p : image_data)
        out << p.r << p.g << p.b;

    return true;
}

double mean(const std::vector<double>& values)
{
    return std::accumulate(values.begin(), values.end(), 0.0) / values.size();
}

double median(const std::vector<double>& values)
{
    std::vector<double> sorted_values{values};
    std::sort(sorted_values.begin(), sorted_values.end());

    if (sorted_values.size() % 2)
        return sorted_values[(sorted_values.size() - 1) / 2];
    else
        return (sorted_values[sorted_values.size() / 2 - 1] + sorted_values[sorted_values.size() / 2]) / 2.0;
}

void show_summary(const std::vector<double>& durations)
{
    if (durations.size() == 1) {
        std::cout << durations[0] << " s" << std::endl;
    } else {
        std::vector<double> sorted_values{durations};
        std::sort(sorted_values.begin(), sorted_values.end());

        std::cout << "mean: " << mean(sorted_values) << " s, median: " << median(sorted_values) << " s (repetitions=" << sorted_values.size() << ") ["
                  << sorted_values[0];

        for (auto p = std::next(sorted_values.cbegin()); p != sorted_values.cend(); ++p)
            std::cout << ", " << *p;

        std::cout << "]" << std::endl;
    }
}

template <typename T>
T eval_arg(const char* s, T min, T max)
{
    T value;
    std::istringstream in{s};

    if (!(in >> value) || value < min || value > max)
        die(Error::EvalArgs);

    return value;
}

std::tuple<int, int, int, int, double, double, double, std::string, std::string> eval_args(const int argc, char const* argv[])
{
    if (argc < 10)
        die(Error::EvalArgs);

    auto image_width    = eval_arg(argv[1], 1, 100000);
    auto image_height   = eval_arg(argv[2], 1, 100000);
    auto max_iterations = eval_arg(argv[3], 1, 1000000000);
    auto repetitions    = eval_arg(argv[4], 1, 1000000);
    auto center_x       = eval_arg(argv[5], -100.0, 100.0);
    auto center_y       = eval_arg(argv[6], -100.0, 100.0);
    auto height         = eval_arg(argv[7], -100.0, 100.0);
    auto colors         = std::string{argv[8]};
    auto filename       = std::string{argv[9]};

    return std::make_tuple(image_width, image_height, max_iterations, repetitions, center_x, center_y, height, colors, filename);
}

void go(const int image_width, const int image_height, const int max_iterations, const double center_x, const double center_y, const double height, const Gradient& gradient, std::vector<PixelColor>& image_data, std::vector<double>& durations, const int repetitions) noexcept
{
    // histogram & normalized_colors: for simplicity we only use indices [1] .. [max_iterations], [0] is unused
    std::vector<int> histogram(static_cast<std::size_t>(max_iterations + 1));
    std::vector<float> normalized_colors(static_cast<std::size_t>(max_iterations + 1));
    std::vector<int> iterations_per_pixel(static_cast<std::size_t>(image_width * image_height));
    std::vector<float> smoothed_distances_to_next_iteration_per_pixel(static_cast<std::size_t>(image_width * image_height));

    for (int i = 0; i < repetitions; ++i) {
        const auto t1 = std::chrono::high_resolution_clock::now();
        mandelbrot_calc(image_width, image_height, max_iterations, center_x, center_y, height, histogram, iterations_per_pixel, smoothed_distances_to_next_iteration_per_pixel);
        mandelbrot_colorize(max_iterations, gradient, image_data, histogram, iterations_per_pixel, smoothed_distances_to_next_iteration_per_pixel, normalized_colors);
        const auto t2 = std::chrono::high_resolution_clock::now();

        durations.push_back(std::chrono::duration<double>{t2 - t1}.count());
    }
}

int main(int argc, char const* argv[])
{
    auto [image_width, image_height, max_iterations, repetitions, center_x, center_y, height, gradient_filename, filename] = eval_args(argc, argv);
    auto gradient = load_gradient(gradient_filename);

    std::vector<PixelColor> image_data(static_cast<unsigned long>(image_width * image_height));
    std::vector<double> durations;

    go(image_width, image_height, max_iterations, center_x, center_y, height, gradient, image_data, durations, repetitions);

    if (!save_image(filename, image_data))
        die(Error::SaveImage);

    show_summary(durations);
}
<|endoftext|>


<commit_before>#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "model.h"
#include "model_io.h"
#include "figurepainter.h"
#include <fstream>
#include <QFileDialog>
#include <QInputDialog>
#include <QMessageBox>
#include <QShortcut>
#include <QScreen>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    modelWidget(nullptr),
    redoExtraShortcut(QKeySequence("Ctrl+Shift+Z"), this),
    zoomInExtraShortcut(QKeySequence("Ctrl+="), this)
{
    ui->setupUi(this);
    connect(&redoExtraShortcut  , &QShortcut::activated, ui->actionRedo  , &QAction::trigger);
    connect(&zoomInExtraShortcut, &QShortcut::activated, ui->actionZoomIn, &QAction::trigger);
    setModelWidget(new Ui::ModelWidget());
}

MainWindow::~MainWindow() {
    delete ui;
}

void MainWindow::on_actionNew_triggered() {
    setModelWidget(new Ui::ModelWidget());
    currentFileName = QString();
}

void MainWindow::setModelWidget(Ui::ModelWidget *newWidget) {
    if (this->modelWidget) {
        ui->wrapperFrame->layout()->removeWidget(this->modelWidget);
        delete this->modelWidget;
    }

    this->modelWidget = newWidget;
    ui->wrapperFrame->layout()->addWidget(this->modelWidget);
    ui->actionSaveAs->setEnabled(true);
    ui->actionUndo->setEnabled(modelWidget->canUndo());
    connect(modelWidget, &Ui::ModelWidget::canUndoChanged, [this]() {
        ui->actionUndo->setEnabled(modelWidget->canUndo());
    });
    ui->actionRedo->setEnabled(modelWidget->canRedo());
    connect(modelWidget, &Ui::ModelWidget::canRedoChanged, [this]() {
        ui->actionRedo->setEnabled(modelWidget->canRedo());
    });
    connect(modelWidget, &Ui::ModelWidget::scaleFactorChanged, [this]() {
        ui->actionZoomOut->setEnabled(modelWidget->scaleFactor() > SCALE_FACTOR_STEP + 1e-8);
    });
    modelWidget->setGridStep(ui->actionShowGrid->isChecked() ? defaultGridStep : 0);

    QScreen *screen = QApplication::screens().at(0);
    modelWidget->setScaleFactor(screen->logicalDotsPerInch() / 96.0);
}

void MainWindow::on_actionOpen_triggered() {
    QString filename = QFileDialog::getOpenFileName(
                           this,
                           "Select file with a model",
                           QDir::currentPath(),
                           "Models (*.model)"
                       );
    if (filename == "") {
        return;
    }

    std::unique_ptr<Ui::ModelWidget> modelWidget(new Ui::ModelWidget());
    std::ifstream file(filename.toStdString());
    try {
        Model model;
        file >> model;
        modelWidget->setModel(std::move(model));
    } catch (model_format_error &e) {
        QMessageBox::critical(this, "Error while opening model", e.what());
        return;
    }

    setModelWidget(modelWidget.release());
    currentFileName = filename;
}

void MainWindow::on_actionSave_triggered() {
    if (currentFileName == QString()) {
        ui->actionSaveAs->trigger();
        return;
    }
    Model &model = this->modelWidget->getModel();
    std::ofstream file(currentFileName.toStdString());
    file << model;
}

void MainWindow::on_actionSaveAs_triggered() {
    QString filename = QFileDialog::getSaveFileName(
                           this,
                           "Select file to save in",
                           QDir::currentPath(),
                           "Models (*.model);;SVG (*.svg);;PNG (*.png)"
                       );
    if (filename == "") {
        return;
    }
    Model &model = this->modelWidget->getModel();
    std::ofstream file(filename.toStdString());
    if (filename.toLower().endsWith(".svg")) {
        exportModelToSvg(model, file);
    } else if (filename.toLower().endsWith(".png")) {
        file.close();
        exportModelToImageFile(model, filename);
    } else {
        file << model;
        currentFileName = filename;
    }
}

void MainWindow::on_actionUndo_triggered() {
    // Extra check is added for symmetry with actionRedo
    if (!modelWidget->canUndo()) { return; }
    modelWidget->undo();
}

void MainWindow::on_actionRedo_triggered() {
    // Extra check is added because there is extra shortcut
    if (!modelWidget->canRedo()) { return; }
    modelWidget->redo();
}

void MainWindow::on_actionShowGrid_triggered() {
    if (ui->actionShowGrid->isChecked()) {
        defaultGridStep = QInputDialog::getInt(this, "Grid step", "Specify grid step in pixels", defaultGridStep, 1, int(1e9));
    }
    modelWidget->setGridStep(ui->actionShowGrid->isChecked() ? defaultGridStep : 0);
}

void MainWindow::on_actionZoomIn_triggered() {
    modelWidget->setScaleFactor(modelWidget->scaleFactor() + SCALE_FACTOR_STEP);
}

void MainWindow::on_actionZoomOut_triggered() {
    modelWidget->setScaleFactor(modelWidget->scaleFactor() - SCALE_FACTOR_STEP);
}

void MainWindow::on_actionExit_triggered() {
    QApplication::exit();
}
<commit_msg>mainwindow: now extension is forced in actionSaveAs() (#49)<commit_after>#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "model.h"
#include "model_io.h"
#include "figurepainter.h"
#include <fstream>
#include <QFileDialog>
#include <QInputDialog>
#include <QMessageBox>
#include <QShortcut>
#include <QScreen>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    modelWidget(nullptr),
    redoExtraShortcut(QKeySequence("Ctrl+Shift+Z"), this),
    zoomInExtraShortcut(QKeySequence("Ctrl+="), this)
{
    ui->setupUi(this);
    connect(&redoExtraShortcut  , &QShortcut::activated, ui->actionRedo  , &QAction::trigger);
    connect(&zoomInExtraShortcut, &QShortcut::activated, ui->actionZoomIn, &QAction::trigger);
    setModelWidget(new Ui::ModelWidget());
}

MainWindow::~MainWindow() {
    delete ui;
}

void MainWindow::on_actionNew_triggered() {
    setModelWidget(new Ui::ModelWidget());
    currentFileName = QString();
}

void MainWindow::setModelWidget(Ui::ModelWidget *newWidget) {
    if (this->modelWidget) {
        ui->wrapperFrame->layout()->removeWidget(this->modelWidget);
        delete this->modelWidget;
    }

    this->modelWidget = newWidget;
    ui->wrapperFrame->layout()->addWidget(this->modelWidget);
    ui->actionSaveAs->setEnabled(true);
    ui->actionUndo->setEnabled(modelWidget->canUndo());
    connect(modelWidget, &Ui::ModelWidget::canUndoChanged, [this]() {
        ui->actionUndo->setEnabled(modelWidget->canUndo());
    });
    ui->actionRedo->setEnabled(modelWidget->canRedo());
    connect(modelWidget, &Ui::ModelWidget::canRedoChanged, [this]() {
        ui->actionRedo->setEnabled(modelWidget->canRedo());
    });
    connect(modelWidget, &Ui::ModelWidget::scaleFactorChanged, [this]() {
        ui->actionZoomOut->setEnabled(modelWidget->scaleFactor() > SCALE_FACTOR_STEP + 1e-8);
    });
    modelWidget->setGridStep(ui->actionShowGrid->isChecked() ? defaultGridStep : 0);

    QScreen *screen = QApplication::screens().at(0);
    modelWidget->setScaleFactor(screen->logicalDotsPerInch() / 96.0);
}

void MainWindow::on_actionOpen_triggered() {
    QString filename = QFileDialog::getOpenFileName(
                           this,
                           "Select file with a model",
                           QDir::currentPath(),
                           "Models (*.model)"
                       );
    if (filename == "") {
        return;
    }

    std::unique_ptr<Ui::ModelWidget> modelWidget(new Ui::ModelWidget());
    std::ifstream file(filename.toStdString());
    try {
        Model model;
        file >> model;
        modelWidget->setModel(std::move(model));
    } catch (model_format_error &e) {
        QMessageBox::critical(this, "Error while opening model", e.what());
        return;
    }

    setModelWidget(modelWidget.release());
    currentFileName = filename;
}

void MainWindow::on_actionSave_triggered() {
    if (currentFileName == QString()) {
        ui->actionSaveAs->trigger();
        return;
    }
    Model &model = this->modelWidget->getModel();
    std::ofstream file(currentFileName.toStdString());
    file << model;
}

QString forceFileExtension(QString filename, QString selectedFilter) {
    QString extension = selectedFilter.mid(selectedFilter.indexOf("(*.") + 3);
    extension.chop(1); // chop trailing )
    extension = ("." + extension).toLower();
    if (!filename.endsWith(extension)) {
        filename += extension;
    }
    return filename;
}

void MainWindow::on_actionSaveAs_triggered() {
    QString selectedFilter;
    QString filename = QFileDialog::getSaveFileName(
                           this,
                           "Select file to save in",
                           QDir::currentPath(),
                           "Models (*.model);;SVG (*.svg);;PNG (*.png)",
                           &selectedFilter
                       );
    if (filename == "") {
        return;
    }
    filename = forceFileExtension(filename, selectedFilter);
    Model &model = this->modelWidget->getModel();
    std::ofstream file(filename.toStdString());
    if (filename.toLower().endsWith(".svg")) {
        exportModelToSvg(model, file);
    } else if (filename.toLower().endsWith(".png")) {
        file.close();
        exportModelToImageFile(model, filename);
    } else {
        file << model;
        currentFileName = filename;
    }
}

void MainWindow::on_actionUndo_triggered() {
    // Extra check is added for symmetry with actionRedo
    if (!modelWidget->canUndo()) { return; }
    modelWidget->undo();
}

void MainWindow::on_actionRedo_triggered() {
    // Extra check is added because there is extra shortcut
    if (!modelWidget->canRedo()) { return; }
    modelWidget->redo();
}

void MainWindow::on_actionShowGrid_triggered() {
    if (ui->actionShowGrid->isChecked()) {
        defaultGridStep = QInputDialog::getInt(this, "Grid step", "Specify grid step in pixels", defaultGridStep, 1, int(1e9));
    }
    modelWidget->setGridStep(ui->actionShowGrid->isChecked() ? defaultGridStep : 0);
}

void MainWindow::on_actionZoomIn_triggered() {
    modelWidget->setScaleFactor(modelWidget->scaleFactor() + SCALE_FACTOR_STEP);
}

void MainWindow::on_actionZoomOut_triggered() {
    modelWidget->setScaleFactor(modelWidget->scaleFactor() - SCALE_FACTOR_STEP);
}

void MainWindow::on_actionExit_triggered() {
    QApplication::exit();
}
<|endoftext|>


<commit_before>#include "aboutdialog.h"

#include <QPushButton>
#include <QLabel>
#include <QVBoxLayout>
#include <QHBoxLayout>

AboutDialog::AboutDialog(QWidget * parent)
    : QDialog(parent),
      okButton(std::make_shared<QPushButton>(tr("OK"))),
      textLabel(std::make_shared<QLabel>(getDialogContent())),
      iconLabel(std::make_shared<QLabel>()),
      mainLayout(std::make_shared<QHBoxLayout>()),
      iconLayout(std::make_shared<QVBoxLayout>()),
      labelLayout(std::make_shared<QVBoxLayout>()),
      buttonLayout(std::make_shared<QHBoxLayout>())
{
    connect(okButton.get(), SIGNAL(clicked()), this, SLOT(accept()));

    setupLayouts();
    addLayouts();

    setWindowTitle(tr("About Track and Field Management"));
    setFixedHeight(sizeHint().height());
}

void AboutDialog::setupLayouts()
{
    iconLabel->setPixmap(QPixmap(":/images/about.png"));
    iconLayout->addWidget(iconLabel.get());
    iconLayout->addStretch();

    buttonLayout->addStretch();
    buttonLayout->addWidget(okButton.get());
    buttonLayout->addStretch();

    labelLayout->addWidget(textLabel.get());
}

void AboutDialog::addLayouts()
{
    mainLayout->addLayout(iconLayout.get());
    mainLayout->addLayout(labelLayout.get());
    labelLayout->addLayout(buttonLayout.get());
    setLayout(mainLayout.get());
}

QString AboutDialog::getDialogContent() const
{
    return tr("About\n"
              "\n"
              "This is Track and Field Management system\n"
              "A program which lets you manage your track and field competitions\n"
              "\n"
              "This program is licensed under the LGPLv2.1\n"
              "Please refer to http://www.gnu.org/licenses/lgpl-2.1.html for questions about the license\n"
              "\n"
              "Copyright \x00A9 2013 Martin Ertsaas\n");
}
<commit_msg>gui: Make the about page a rich text, and make the links clickable.<commit_after>#include "aboutdialog.h"

#include <QDebug>
#include <QPushButton>
#include <QLabel>
#include <QVBoxLayout>
#include <QHBoxLayout>

AboutDialog::AboutDialog(QWidget * parent)
    : QDialog(parent),
      okButton(std::make_shared<QPushButton>(tr("OK"))),
      textLabel(std::make_shared<QLabel>(getDialogContent())),
      iconLabel(std::make_shared<QLabel>()),
      mainLayout(std::make_shared<QHBoxLayout>()),
      iconLayout(std::make_shared<QVBoxLayout>()),
      labelLayout(std::make_shared<QVBoxLayout>()),
      buttonLayout(std::make_shared<QHBoxLayout>())
{
    textLabel->setTextFormat(Qt::RichText);
    textLabel->setOpenExternalLinks(true);

    connect(okButton.get(), SIGNAL(clicked()), this, SLOT(accept()));

    setupLayouts();
    addLayouts();

    setWindowTitle(tr("About Track and Field Management"));
    setFixedHeight(sizeHint().height());
}

void AboutDialog::setupLayouts()
{
    iconLabel->setPixmap(QPixmap(":/images/about.png"));
    iconLayout->addWidget(iconLabel.get());
    iconLayout->addStretch();

    buttonLayout->addStretch();
    buttonLayout->addWidget(okButton.get());
    buttonLayout->addStretch();

    labelLayout->addWidget(textLabel.get());
}

void AboutDialog::addLayouts()
{
    mainLayout->addLayout(iconLayout.get());
    mainLayout->addLayout(labelLayout.get());
    labelLayout->addLayout(buttonLayout.get());
    setLayout(mainLayout.get());
}

QString AboutDialog::getDialogContent() const
{
    return tr("
About
"
              "
This is Track and Field Management system
"
              "A program which lets you manage your track and field competitions"
              "
This program is licensed under the LGPLv2.1
"
              "Please refer to http://www.gnu.org/licenses/lgpl-2.1.html for questions about the license"
              "
Copyright © 2013 Martin Ertsaas");
}
<|endoftext|>


<commit_before>/*
 -----------------------------------------------------------------------------
 This source file is part of OGRE
 (Object-oriented Graphics Rendering Engine)
 For the latest info, see http://www.ogre3d.org/

 Copyright (c) 2000-2012 Torus Knot Software Ltd

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 -----------------------------------------------------------------------------
 */

#include "OgreStableHeaders.h"
#include "OgreGLES2UniformCacheImp.h"

namespace Ogre {

    GLES2UniformCacheImp::GLES2UniformCacheImp(void)
    {
        clearCache();
    }

    void GLES2UniformCacheImp::clearCache()
    {
        mUniformValueMap.reserve(16);
        mUniformValueMap.clear();
    }

    GLES2UniformCacheImp::~GLES2UniformCacheImp(void)
    {
        mUniformValueMap.clear();
    }

    bool GLES2UniformCacheImp::updateUniform(GLint location, const void *value, GLsizei length)
    {
        uint32 current = mUniformValueMap[location];
        uint32 hash = Ogre::FastHash(static_cast<const char*>(value), length);
        // First check if the uniform name is in the map. If not, this is new so insert it into the map.
        if (!current || (current != hash))
        {
            // Haven't cached this state yet or the value has changed
            mUniformValueMap[location] = hash;
			return true;
        }

        return false;
    }
}
<commit_msg>[OGRE-301] Fix Android build with ES 2 state cache enabled.<commit_after>/*
 -----------------------------------------------------------------------------
 This source file is part of OGRE
 (Object-oriented Graphics Rendering Engine)
 For the latest info, see http://www.ogre3d.org/

 Copyright (c) 2000-2012 Torus Knot Software Ltd

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 -----------------------------------------------------------------------------
 */

#include "OgreStableHeaders.h"
#include "OgreCommon.h"
#include "OgreGLES2UniformCacheImp.h"

namespace Ogre {

    GLES2UniformCacheImp::GLES2UniformCacheImp(void)
    {
        clearCache();
    }

    void GLES2UniformCacheImp::clearCache()
    {
        mUniformValueMap.clear();
    }

    GLES2UniformCacheImp::~GLES2UniformCacheImp(void)
    {
        mUniformValueMap.clear();
    }

    bool GLES2UniformCacheImp::updateUniform(GLint location, const void *value, GLsizei length)
    {
        uint32 current = mUniformValueMap[location];
        uint32 hash = Ogre::FastHash(static_cast<const char*>(value), length);
        // First check if the uniform name is in the map. If not, this is new so insert it into the map.
        if (!current || (current != hash))
        {
            // Haven't cached this state yet or the value has changed
            mUniformValueMap[location] = hash;
			return true;
        }

        return false;
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2006, 2007 Apple Computer, Inc.
 * Copyright (c) 2006, 2007, 2008, 2009, 2012 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 "core/platform/graphics/FontPlatformData.h"

#include "RuntimeEnabledFeatures.h"
#include "SkPaint.h"
#include "SkTypeface.h"
#include "SkTypeface_win.h"
#include "core/platform/graphics/FontCache.h"
#include "core/platform/graphics/GraphicsContext.h"
#include "core/platform/graphics/skia/SkiaFontWin.h"
#include "platform/LayoutTestSupport.h"
#include "platform/SharedBuffer.h"
#include "platform/win/HWndDC.h"
#include "public/platform/Platform.h"
#include "public/platform/win/WebSandboxSupport.h"
#include "wtf/PassOwnPtr.h"
#include "wtf/StdLibExtras.h"
#include <mlang.h>
#include <objidl.h>
#include <windows.h>

namespace WebCore {

void FontPlatformData::setupPaint(SkPaint* paint, GraphicsContext* context) const
{
    const float ts = m_textSize >= 0 ? m_textSize : 12;
    paint->setTextSize(SkFloatToScalar(m_textSize));
    paint->setTypeface(typeface());
    paint->setFakeBoldText(m_fakeBold);
    paint->setTextSkewX(m_fakeItalic ? -SK_Scalar1 / 4 : 0);
    if (RuntimeEnabledFeatures::subpixelFontScalingEnabled())
        paint->setSubpixelText(true);

    // Only set painting flags when we're actually painting.
    if (context) {
        int textFlags = paintTextFlags();
        if (!context->couldUseLCDRenderedText()) {
            textFlags &= ~SkPaint::kLCDRenderText_Flag;
            // If we *just* clear our request for LCD, then GDI seems to
            // sometimes give us AA text, and sometimes give us BW text. Since the
            // original intent was LCD, we want to force AA (rather than BW), so we
            // add a special bit to tell Skia to do its best to avoid the BW: by
            // drawing LCD offscreen and downsampling that to AA.
            textFlags |= SkPaint::kGenA8FromLCD_Flag;
        }

        static const uint32_t textFlagsMask = SkPaint::kAntiAlias_Flag |
            SkPaint::kLCDRenderText_Flag |
            SkPaint::kGenA8FromLCD_Flag;

        SkASSERT(!(textFlags & ~textFlagsMask));
        uint32_t flags = paint->getFlags();
        flags &= ~textFlagsMask;
        flags |= textFlags;
        paint->setFlags(flags);
    }
}

// Lookup the current system settings for font smoothing.
// We cache these values for performance, but if the browser has a way to be
// notified when these change, we could re-query them at that time.
static uint32_t getDefaultGDITextFlags()
{
    static bool gInited;
    static uint32_t gFlags;
    if (!gInited) {
        BOOL enabled;
        gFlags = 0;
        if (SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &enabled, 0) && enabled) {
            gFlags |= SkPaint::kAntiAlias_Flag;

            UINT smoothType;
            if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &smoothType, 0)) {
                if (FE_FONTSMOOTHINGCLEARTYPE == smoothType)
                    gFlags |= SkPaint::kLCDRenderText_Flag;
            }
        }
        gInited = true;
    }
    return gFlags;
}

static bool isWebFont(const LOGFONT& lf)
{
    // web-fonts have artifical names constructed to always be
    // 1. 24 characters, followed by a '\0'
    // 2. the last two characters are '=='
    return '=' == lf.lfFaceName[22] && '=' == lf.lfFaceName[23] && '\0' == lf.lfFaceName[24];
}

static int computePaintTextFlags(const LOGFONT& lf)
{
    int textFlags = 0;
    switch (lf.lfQuality) {
    case NONANTIALIASED_QUALITY:
        textFlags = 0;
        break;
    case ANTIALIASED_QUALITY:
        textFlags = SkPaint::kAntiAlias_Flag;
        break;
    case CLEARTYPE_QUALITY:
        textFlags = (SkPaint::kAntiAlias_Flag | SkPaint::kLCDRenderText_Flag);
        break;
    default:
        textFlags = getDefaultGDITextFlags();
        break;
    }

    // only allow features that SystemParametersInfo allows
    textFlags &= getDefaultGDITextFlags();

    /*
     *  FontPlatformData(...) will read our logfont, and try to honor the the lfQuality
     *  setting (computing the corresponding SkPaint flags for AA and LCD). However, it
     *  will limit the quality based on its query of SPI_GETFONTSMOOTHING. This could mean
     *  we end up drawing the text in BW, even though our lfQuality requested antialiasing.
     *
     *  Many web-fonts are so poorly hinted that they are terrible to read when drawn in BW.
     *  In these cases, we have decided to FORCE these fonts to be drawn with at least grayscale AA,
     *  even when the System (getDefaultGDITextFlags) tells us to draw only in BW.
     */
    if (isWebFont(lf) && !isRunningLayoutTest())
        textFlags |= SkPaint::kAntiAlias_Flag;
    return textFlags;
}

PassRefPtr<SkTypeface> CreateTypefaceFromHFont(HFONT hfont, int* size, int* paintTextFlags)
{
    LOGFONT info;
    GetObject(hfont, sizeof(info), &info);
    if (size) {
        int height = info.lfHeight;
        if (height < 0)
            height = -height;
        *size = height;
    }
    if (paintTextFlags)
        *paintTextFlags = computePaintTextFlags(info);
    return adoptRef(SkCreateTypefaceFromLOGFONT(info));
}

FontPlatformData::FontPlatformData(WTF::HashTableDeletedValueType)
    : m_font(0)
    , m_textSize(-1)
    , m_fakeBold(false)
    , m_fakeItalic(false)
    , m_orientation(Horizontal)
    , m_scriptCache(0)
    , m_typeface(adoptRef(SkTypeface::RefDefault()))
    , m_paintTextFlags(0)
    , m_isHashTableDeletedValue(true)
{
}

FontPlatformData::FontPlatformData()
    : m_font(0)
    , m_textSize(0)
    , m_fakeBold(false)
    , m_fakeItalic(false)
    , m_orientation(Horizontal)
    , m_scriptCache(0)
    , m_typeface(adoptRef(SkTypeface::RefDefault()))
    , m_paintTextFlags(0)
    , m_isHashTableDeletedValue(false)
{
}

#if ENABLE(GDI_FONTS_ON_WINDOWS)
FontPlatformData::FontPlatformData(HFONT font, float size, FontOrientation orientation)
    : m_font(RefCountedHFONT::create(font))
    , m_textSize(size)
    , m_fakeBold(false)
    , m_fakeItalic(false)
    , m_orientation(orientation)
    , m_scriptCache(0)
    , m_typeface(CreateTypefaceFromHFont(font, 0, &m_paintTextFlags))
    , m_isHashTableDeletedValue(false)
{
}
#endif

// FIXME: this constructor is needed for SVG fonts but doesn't seem to do much
FontPlatformData::FontPlatformData(float size, bool bold, bool oblique)
    : m_font(0)
    , m_textSize(size)
    , m_fakeBold(false)
    , m_fakeItalic(false)
    , m_orientation(Horizontal)
    , m_scriptCache(0)
    , m_typeface(adoptRef(SkTypeface::RefDefault()))
    , m_paintTextFlags(0)
    , m_isHashTableDeletedValue(false)
{
}

FontPlatformData::FontPlatformData(const FontPlatformData& data)
    : m_font(data.m_font)
    , m_textSize(data.m_textSize)
    , m_fakeBold(data.m_fakeBold)
    , m_fakeItalic(data.m_fakeItalic)
    , m_orientation(data.m_orientation)
    , m_scriptCache(0)
    , m_typeface(data.m_typeface)
    , m_paintTextFlags(data.m_paintTextFlags)
    , m_isHashTableDeletedValue(false)
{
}

FontPlatformData::FontPlatformData(const FontPlatformData& data, float textSize)
    : m_font(data.m_font)
    , m_textSize(textSize)
    , m_fakeBold(data.m_fakeBold)
    , m_fakeItalic(data.m_fakeItalic)
    , m_orientation(data.m_orientation)
    , m_scriptCache(0)
    , m_typeface(data.m_typeface)
    , m_paintTextFlags(data.m_paintTextFlags)
    , m_isHashTableDeletedValue(false)
{
}

FontPlatformData::FontPlatformData(PassRefPtr<SkTypeface> tf, const char* family, float textSize, bool fakeBold, bool fakeItalic, FontOrientation orientation)
    : m_font(0)
    , m_textSize(textSize)
    , m_fakeBold(fakeBold)
    , m_fakeItalic(fakeItalic)
    , m_orientation(orientation)
    , m_scriptCache(0)
    , m_typeface(tf)
    , m_isHashTableDeletedValue(false)
{
    // FIXME: This can be removed together with m_font once the last few
    // uses of hfont() has been eliminated.
    LOGFONT logFont;
    SkLOGFONTFromTypeface(m_typeface.get(), &logFont);
    logFont.lfHeight = -textSize;
    HFONT hFont = CreateFontIndirect(&logFont);
    if (hFont)
        m_font = RefCountedHFONT::create(hFont);
    m_paintTextFlags = computePaintTextFlags(logFont);
}

FontPlatformData& FontPlatformData::operator=(const FontPlatformData& data)
{
    if (this != &data) {
        m_font = data.m_font;
        m_textSize = data.m_textSize;
        m_fakeBold = data.m_fakeBold;
        m_fakeItalic = data.m_fakeItalic;
        m_orientation = data.m_orientation;
        m_typeface = data.m_typeface;
        m_paintTextFlags = data.m_paintTextFlags;

        // The following fields will get re-computed if necessary.
        ScriptFreeCache(&m_scriptCache);
        m_scriptCache = 0;
        m_scriptFontProperties.clear();
    }
    return *this;
}

FontPlatformData::~FontPlatformData()
{
    ScriptFreeCache(&m_scriptCache);
    m_scriptCache = 0;
}

String FontPlatformData::fontFamilyName() const
{
    HWndDC dc(0);
    HGDIOBJ oldFont = static_cast<HFONT>(SelectObject(dc, hfont()));
    WCHAR name[LF_FACESIZE];
    unsigned resultLength = GetTextFace(dc, LF_FACESIZE, name);
    if (resultLength > 0)
        resultLength--; // ignore the null terminator
    SelectObject(dc, oldFont);
    return String(name, resultLength);
}

bool FontPlatformData::isFixedPitch() const
{
#if ENABLE(GDI_FONTS_ON_WINDOWS)
    // TEXTMETRICS have this. Set m_treatAsFixedPitch based off that.
    HWndDC dc(0);
    HGDIOBJ oldFont = SelectObject(dc, hfont());

    // Yes, this looks backwards, but the fixed pitch bit is actually set if the font
    // is *not* fixed pitch. Unbelievable but true.
    TEXTMETRIC textMetric = { 0 };
    if (!GetTextMetrics(dc, &textMetric)) {
        if (ensureFontLoaded(hfont())) {
            // Retry GetTextMetrics.
            // FIXME: Handle gracefully the error if this call also fails.
            // See http://crbug.com/6401.
            if (!GetTextMetrics(dc, &textMetric))
                LOG_ERROR("Unable to get the text metrics after second attempt");
        }
    }

    bool treatAsFixedPitch = !(textMetric.tmPitchAndFamily & TMPF_FIXED_PITCH);

    SelectObject(dc, oldFont);

    return treatAsFixedPitch;
#else
    return typeface() && typeface()->isFixedPitch();
#endif
}

FontPlatformData::RefCountedHFONT::~RefCountedHFONT()
{
    DeleteObject(m_hfont);
}

SCRIPT_FONTPROPERTIES* FontPlatformData::scriptFontProperties() const
{
    if (!m_scriptFontProperties) {
        m_scriptFontProperties = adoptPtr(new SCRIPT_FONTPROPERTIES);
        memset(m_scriptFontProperties.get(), 0, sizeof(SCRIPT_FONTPROPERTIES));
        m_scriptFontProperties->cBytes = sizeof(SCRIPT_FONTPROPERTIES);
        HRESULT result = ScriptGetFontProperties(0, scriptCache(), m_scriptFontProperties.get());
        if (result == E_PENDING) {
            HWndDC dc(0);
            HGDIOBJ oldFont = SelectObject(dc, hfont());
            HRESULT hr = ScriptGetFontProperties(dc, scriptCache(), m_scriptFontProperties.get());
            if (S_OK != hr) {
                if (FontPlatformData::ensureFontLoaded(hfont())) {
                    // FIXME: Handle gracefully the error if this call also fails.
                    hr = ScriptGetFontProperties(dc, scriptCache(), m_scriptFontProperties.get());
                    if (S_OK != hr) {
                        LOG_ERROR("Unable to get the font properties after second attempt");
                    }
                }
            }

            SelectObject(dc, oldFont);
        }
    }
    return m_scriptFontProperties.get();
}

#ifndef NDEBUG
String FontPlatformData::description() const
{
    return String();
}
#endif

bool FontPlatformData::ensureFontLoaded(HFONT font)
{
    WebKit::WebSandboxSupport* sandboxSupport = WebKit::Platform::current()->sandboxSupport();
    // if there is no sandbox, then we can assume the font
    // was able to be loaded successfully already
    return sandboxSupport ? sandboxSupport->ensureFontLoaded(font) : true;
}

}
<commit_msg>Use Skia API on Windows to get font family name<commit_after>/*
 * Copyright (C) 2006, 2007 Apple Computer, Inc.
 * Copyright (c) 2006, 2007, 2008, 2009, 2012 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 "core/platform/graphics/FontPlatformData.h"

#include "RuntimeEnabledFeatures.h"
#include "SkPaint.h"
#include "SkTypeface.h"
#include "SkTypeface_win.h"
#include "core/platform/graphics/FontCache.h"
#include "core/platform/graphics/GraphicsContext.h"
#include "core/platform/graphics/skia/SkiaFontWin.h"
#include "platform/LayoutTestSupport.h"
#include "platform/SharedBuffer.h"
#include "platform/win/HWndDC.h"
#include "public/platform/Platform.h"
#include "public/platform/win/WebSandboxSupport.h"
#include "wtf/PassOwnPtr.h"
#include "wtf/StdLibExtras.h"
#include <mlang.h>
#include <objidl.h>
#include <windows.h>

namespace WebCore {

void FontPlatformData::setupPaint(SkPaint* paint, GraphicsContext* context) const
{
    const float ts = m_textSize >= 0 ? m_textSize : 12;
    paint->setTextSize(SkFloatToScalar(m_textSize));
    paint->setTypeface(typeface());
    paint->setFakeBoldText(m_fakeBold);
    paint->setTextSkewX(m_fakeItalic ? -SK_Scalar1 / 4 : 0);
    if (RuntimeEnabledFeatures::subpixelFontScalingEnabled())
        paint->setSubpixelText(true);

    // Only set painting flags when we're actually painting.
    if (context) {
        int textFlags = paintTextFlags();
        if (!context->couldUseLCDRenderedText()) {
            textFlags &= ~SkPaint::kLCDRenderText_Flag;
            // If we *just* clear our request for LCD, then GDI seems to
            // sometimes give us AA text, and sometimes give us BW text. Since the
            // original intent was LCD, we want to force AA (rather than BW), so we
            // add a special bit to tell Skia to do its best to avoid the BW: by
            // drawing LCD offscreen and downsampling that to AA.
            textFlags |= SkPaint::kGenA8FromLCD_Flag;
        }

        static const uint32_t textFlagsMask = SkPaint::kAntiAlias_Flag |
            SkPaint::kLCDRenderText_Flag |
            SkPaint::kGenA8FromLCD_Flag;

        SkASSERT(!(textFlags & ~textFlagsMask));
        uint32_t flags = paint->getFlags();
        flags &= ~textFlagsMask;
        flags |= textFlags;
        paint->setFlags(flags);
    }
}

// Lookup the current system settings for font smoothing.
// We cache these values for performance, but if the browser has a way to be
// notified when these change, we could re-query them at that time.
static uint32_t getDefaultGDITextFlags()
{
    static bool gInited;
    static uint32_t gFlags;
    if (!gInited) {
        BOOL enabled;
        gFlags = 0;
        if (SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &enabled, 0) && enabled) {
            gFlags |= SkPaint::kAntiAlias_Flag;

            UINT smoothType;
            if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &smoothType, 0)) {
                if (FE_FONTSMOOTHINGCLEARTYPE == smoothType)
                    gFlags |= SkPaint::kLCDRenderText_Flag;
            }
        }
        gInited = true;
    }
    return gFlags;
}

static bool isWebFont(const LOGFONT& lf)
{
    // web-fonts have artifical names constructed to always be
    // 1. 24 characters, followed by a '\0'
    // 2. the last two characters are '=='
    return '=' == lf.lfFaceName[22] && '=' == lf.lfFaceName[23] && '\0' == lf.lfFaceName[24];
}

static int computePaintTextFlags(const LOGFONT& lf)
{
    int textFlags = 0;
    switch (lf.lfQuality) {
    case NONANTIALIASED_QUALITY:
        textFlags = 0;
        break;
    case ANTIALIASED_QUALITY:
        textFlags = SkPaint::kAntiAlias_Flag;
        break;
    case CLEARTYPE_QUALITY:
        textFlags = (SkPaint::kAntiAlias_Flag | SkPaint::kLCDRenderText_Flag);
        break;
    default:
        textFlags = getDefaultGDITextFlags();
        break;
    }

    // only allow features that SystemParametersInfo allows
    textFlags &= getDefaultGDITextFlags();

    /*
     *  FontPlatformData(...) will read our logfont, and try to honor the the lfQuality
     *  setting (computing the corresponding SkPaint flags for AA and LCD). However, it
     *  will limit the quality based on its query of SPI_GETFONTSMOOTHING. This could mean
     *  we end up drawing the text in BW, even though our lfQuality requested antialiasing.
     *
     *  Many web-fonts are so poorly hinted that they are terrible to read when drawn in BW.
     *  In these cases, we have decided to FORCE these fonts to be drawn with at least grayscale AA,
     *  even when the System (getDefaultGDITextFlags) tells us to draw only in BW.
     */
    if (isWebFont(lf) && !isRunningLayoutTest())
        textFlags |= SkPaint::kAntiAlias_Flag;
    return textFlags;
}

PassRefPtr<SkTypeface> CreateTypefaceFromHFont(HFONT hfont, int* size, int* paintTextFlags)
{
    LOGFONT info;
    GetObject(hfont, sizeof(info), &info);
    if (size) {
        int height = info.lfHeight;
        if (height < 0)
            height = -height;
        *size = height;
    }
    if (paintTextFlags)
        *paintTextFlags = computePaintTextFlags(info);
    return adoptRef(SkCreateTypefaceFromLOGFONT(info));
}

FontPlatformData::FontPlatformData(WTF::HashTableDeletedValueType)
    : m_font(0)
    , m_textSize(-1)
    , m_fakeBold(false)
    , m_fakeItalic(false)
    , m_orientation(Horizontal)
    , m_scriptCache(0)
    , m_typeface(adoptRef(SkTypeface::RefDefault()))
    , m_paintTextFlags(0)
    , m_isHashTableDeletedValue(true)
{
}

FontPlatformData::FontPlatformData()
    : m_font(0)
    , m_textSize(0)
    , m_fakeBold(false)
    , m_fakeItalic(false)
    , m_orientation(Horizontal)
    , m_scriptCache(0)
    , m_typeface(adoptRef(SkTypeface::RefDefault()))
    , m_paintTextFlags(0)
    , m_isHashTableDeletedValue(false)
{
}

#if ENABLE(GDI_FONTS_ON_WINDOWS)
FontPlatformData::FontPlatformData(HFONT font, float size, FontOrientation orientation)
    : m_font(RefCountedHFONT::create(font))
    , m_textSize(size)
    , m_fakeBold(false)
    , m_fakeItalic(false)
    , m_orientation(orientation)
    , m_scriptCache(0)
    , m_typeface(CreateTypefaceFromHFont(font, 0, &m_paintTextFlags))
    , m_isHashTableDeletedValue(false)
{
}
#endif

// FIXME: this constructor is needed for SVG fonts but doesn't seem to do much
FontPlatformData::FontPlatformData(float size, bool bold, bool oblique)
    : m_font(0)
    , m_textSize(size)
    , m_fakeBold(false)
    , m_fakeItalic(false)
    , m_orientation(Horizontal)
    , m_scriptCache(0)
    , m_typeface(adoptRef(SkTypeface::RefDefault()))
    , m_paintTextFlags(0)
    , m_isHashTableDeletedValue(false)
{
}

FontPlatformData::FontPlatformData(const FontPlatformData& data)
    : m_font(data.m_font)
    , m_textSize(data.m_textSize)
    , m_fakeBold(data.m_fakeBold)
    , m_fakeItalic(data.m_fakeItalic)
    , m_orientation(data.m_orientation)
    , m_scriptCache(0)
    , m_typeface(data.m_typeface)
    , m_paintTextFlags(data.m_paintTextFlags)
    , m_isHashTableDeletedValue(false)
{
}

FontPlatformData::FontPlatformData(const FontPlatformData& data, float textSize)
    : m_font(data.m_font)
    , m_textSize(textSize)
    , m_fakeBold(data.m_fakeBold)
    , m_fakeItalic(data.m_fakeItalic)
    , m_orientation(data.m_orientation)
    , m_scriptCache(0)
    , m_typeface(data.m_typeface)
    , m_paintTextFlags(data.m_paintTextFlags)
    , m_isHashTableDeletedValue(false)
{
}

FontPlatformData::FontPlatformData(PassRefPtr<SkTypeface> tf, const char* family, float textSize, bool fakeBold, bool fakeItalic, FontOrientation orientation)
    : m_font(0)
    , m_textSize(textSize)
    , m_fakeBold(fakeBold)
    , m_fakeItalic(fakeItalic)
    , m_orientation(orientation)
    , m_scriptCache(0)
    , m_typeface(tf)
    , m_isHashTableDeletedValue(false)
{
    // FIXME: This can be removed together with m_font once the last few
    // uses of hfont() has been eliminated.
    LOGFONT logFont;
    SkLOGFONTFromTypeface(m_typeface.get(), &logFont);
    logFont.lfHeight = -textSize;
    HFONT hFont = CreateFontIndirect(&logFont);
    if (hFont)
        m_font = RefCountedHFONT::create(hFont);
    m_paintTextFlags = computePaintTextFlags(logFont);
}

FontPlatformData& FontPlatformData::operator=(const FontPlatformData& data)
{
    if (this != &data) {
        m_font = data.m_font;
        m_textSize = data.m_textSize;
        m_fakeBold = data.m_fakeBold;
        m_fakeItalic = data.m_fakeItalic;
        m_orientation = data.m_orientation;
        m_typeface = data.m_typeface;
        m_paintTextFlags = data.m_paintTextFlags;

        // The following fields will get re-computed if necessary.
        ScriptFreeCache(&m_scriptCache);
        m_scriptCache = 0;
        m_scriptFontProperties.clear();
    }
    return *this;
}

FontPlatformData::~FontPlatformData()
{
    ScriptFreeCache(&m_scriptCache);
    m_scriptCache = 0;
}

String FontPlatformData::fontFamilyName() const
{
#if ENABLE(GDI_FONTS_ON_WINDOWS)
    HWndDC dc(0);
    HGDIOBJ oldFont = static_cast<HFONT>(SelectObject(dc, hfont()));
    WCHAR name[LF_FACESIZE];
    unsigned resultLength = GetTextFace(dc, LF_FACESIZE, name);
    if (resultLength > 0)
        resultLength--; // ignore the null terminator
    SelectObject(dc, oldFont);
    return String(name, resultLength);
#else
    // FIXME: This returns the requested name, perhaps a better solution would be to
    // return the list of names provided by SkTypeface::createFamilyNameIterator.
    ASSERT(typeface());
    SkString familyName;
    typeface()->getFamilyName(&familyName);
    return String::fromUTF8(familyName.c_str());
#endif
}

bool FontPlatformData::isFixedPitch() const
{
#if ENABLE(GDI_FONTS_ON_WINDOWS)
    // TEXTMETRICS have this. Set m_treatAsFixedPitch based off that.
    HWndDC dc(0);
    HGDIOBJ oldFont = SelectObject(dc, hfont());

    // Yes, this looks backwards, but the fixed pitch bit is actually set if the font
    // is *not* fixed pitch. Unbelievable but true.
    TEXTMETRIC textMetric = { 0 };
    if (!GetTextMetrics(dc, &textMetric)) {
        if (ensureFontLoaded(hfont())) {
            // Retry GetTextMetrics.
            // FIXME: Handle gracefully the error if this call also fails.
            // See http://crbug.com/6401.
            if (!GetTextMetrics(dc, &textMetric))
                LOG_ERROR("Unable to get the text metrics after second attempt");
        }
    }

    bool treatAsFixedPitch = !(textMetric.tmPitchAndFamily & TMPF_FIXED_PITCH);

    SelectObject(dc, oldFont);

    return treatAsFixedPitch;
#else
    return typeface() && typeface()->isFixedPitch();
#endif
}

FontPlatformData::RefCountedHFONT::~RefCountedHFONT()
{
    DeleteObject(m_hfont);
}

SCRIPT_FONTPROPERTIES* FontPlatformData::scriptFontProperties() const
{
    if (!m_scriptFontProperties) {
        m_scriptFontProperties = adoptPtr(new SCRIPT_FONTPROPERTIES);
        memset(m_scriptFontProperties.get(), 0, sizeof(SCRIPT_FONTPROPERTIES));
        m_scriptFontProperties->cBytes = sizeof(SCRIPT_FONTPROPERTIES);
        HRESULT result = ScriptGetFontProperties(0, scriptCache(), m_scriptFontProperties.get());
        if (result == E_PENDING) {
            HWndDC dc(0);
            HGDIOBJ oldFont = SelectObject(dc, hfont());
            HRESULT hr = ScriptGetFontProperties(dc, scriptCache(), m_scriptFontProperties.get());
            if (S_OK != hr) {
                if (FontPlatformData::ensureFontLoaded(hfont())) {
                    // FIXME: Handle gracefully the error if this call also fails.
                    hr = ScriptGetFontProperties(dc, scriptCache(), m_scriptFontProperties.get());
                    if (S_OK != hr) {
                        LOG_ERROR("Unable to get the font properties after second attempt");
                    }
                }
            }

            SelectObject(dc, oldFont);
        }
    }
    return m_scriptFontProperties.get();
}

#ifndef NDEBUG
String FontPlatformData::description() const
{
    return String();
}
#endif

bool FontPlatformData::ensureFontLoaded(HFONT font)
{
    WebKit::WebSandboxSupport* sandboxSupport = WebKit::Platform::current()->sandboxSupport();
    // if there is no sandbox, then we can assume the font
    // was able to be loaded successfully already
    return sandboxSupport ? sandboxSupport->ensureFontLoaded(font) : true;
}

}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: accumulation.hxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: kz $ $Date: 2005-01-21 16:57:34 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library 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 for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _SLIDESHOW_ACCUMULATION_HXX
#define _SLIDESHOW_ACCUMULATION_HXX

#include <sal/types.h>
#include <rtl/ustring.hxx>

namespace presentation
{
    namespace internal
    {
        /** Generic accumulation.

            This template handles value accumulation across repeated
            effect runs: returned is the end value times the repeat
            count, plus the current value.

            @param rEndValue
            End value of the simple animation.

            @param nRepeatCount
            Number of completed repeats (i.e. 0 during the first
            effect run)

            @param rCurrValue
            Current animation value
         */
        template< typename ValueType > ValueType accumulate( const ValueType&   rEndValue,
                                                             sal_uInt32         nRepeatCount,
                                                             const ValueType&   rCurrValue )
        {
            return nRepeatCount*rEndValue + rCurrValue;
        }

        /// Specialization for non-addable enums/constant values
        template<> sal_Int16 accumulate< sal_Int16 >( const sal_Int16&,
                                                      sal_uInt32,
                                                      const sal_Int16&  rCurrValue )
        {
            // always return rCurrValue, it's forbidden to add enums/constant values...
            return rCurrValue;
        }

        /// Specialization for non-addable strings
        template<> ::rtl::OUString accumulate< ::rtl::OUString >( const ::rtl::OUString&,
                                                                  sal_uInt32,
                                                                  const ::rtl::OUString&    rCurrValue )
        {
            // always return rCurrValue, it's impossible to add strings...
            return rCurrValue;
        }

        /// Specialization for non-addable bools
        template<> bool accumulate< bool >( const bool&,
                                            sal_uInt32,
                                            const bool&     bCurrValue )
        {
            // always return bCurrValue, SMIL spec requires to ignore
            // cumulative behaviour for bools.
            return bCurrValue;
        }
    }
}

#endif /* _SLIDESHOW_ACCUMULATION_HXX */
<commit_msg>INTEGRATION: CWS ooo19126 (1.2.36); FILE MERGED 2005/09/05 17:41:09 rt 1.2.36.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: accumulation.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-07 20:34:01 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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 for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _SLIDESHOW_ACCUMULATION_HXX
#define _SLIDESHOW_ACCUMULATION_HXX

#include <sal/types.h>
#include <rtl/ustring.hxx>

namespace presentation
{
    namespace internal
    {
        /** Generic accumulation.

            This template handles value accumulation across repeated
            effect runs: returned is the end value times the repeat
            count, plus the current value.

            @param rEndValue
            End value of the simple animation.

            @param nRepeatCount
            Number of completed repeats (i.e. 0 during the first
            effect run)

            @param rCurrValue
            Current animation value
         */
        template< typename ValueType > ValueType accumulate( const ValueType&   rEndValue,
                                                             sal_uInt32         nRepeatCount,
                                                             const ValueType&   rCurrValue )
        {
            return nRepeatCount*rEndValue + rCurrValue;
        }

        /// Specialization for non-addable enums/constant values
        template<> sal_Int16 accumulate< sal_Int16 >( const sal_Int16&,
                                                      sal_uInt32,
                                                      const sal_Int16&  rCurrValue )
        {
            // always return rCurrValue, it's forbidden to add enums/constant values...
            return rCurrValue;
        }

        /// Specialization for non-addable strings
        template<> ::rtl::OUString accumulate< ::rtl::OUString >( const ::rtl::OUString&,
                                                                  sal_uInt32,
                                                                  const ::rtl::OUString&    rCurrValue )
        {
            // always return rCurrValue, it's impossible to add strings...
            return rCurrValue;
        }

        /// Specialization for non-addable bools
        template<> bool accumulate< bool >( const bool&,
                                            sal_uInt32,
                                            const bool&     bCurrValue )
        {
            // always return bCurrValue, SMIL spec requires to ignore
            // cumulative behaviour for bools.
            return bCurrValue;
        }
    }
}

#endif /* _SLIDESHOW_ACCUMULATION_HXX */
<|endoftext|>


<commit_before>#include "stdafx.h"
#include "ExceptionHandler.h"
#include "Helper.h"
#include <StackWalker\StackWalker.h>

// https://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273/
#define CXX_EXCEPTION_MAGIC 0xE06D7363
struct CXX_EXCEPTION_DETAIL {
	DWORD _0;
	DWORD _1;
	DWORD _2;
	DWORD catchableTypesPtr;
};

struct CXX_EXCEPTION_CATCHABLE_TYPES {
	DWORD count;
	DWORD catchableTypeHead;
};

struct CXX_EXCEPTION_CATCHABLE_TYPE {
	DWORD _0;
	DWORD typeInfo;
	DWORD _2;
	DWORD _3;
	DWORD _4;
	DWORD _5;
	DWORD _6;
};

static std::string join(const std::vector<std::string>& vec, const char* delim) {
	std::stringstream res;
	copy(vec.begin(), vec.end(), std::ostream_iterator<std::string>(res, delim));
	return res.str();
}

class MyStackWalker : public StackWalker {
protected:
	std::vector<std::string>& _arr;

	virtual void OnOutput(LPCSTR szText) {
		_arr.push_back(szText);
		StackWalker::OnOutput(szText);
	}

	virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName) {}
	virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion) {}
	virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) {}
	virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) {
		CHAR buffer[STACKWALK_MAX_NAMELEN];
		if ((eType != lastEntry) && (entry.offset != 0)) {
			if (entry.name[0] == 0)
				strcpy_s(entry.name, "?");
			if (entry.undName[0] != 0)
				strcpy_s(entry.name, entry.undName);
			if (entry.undFullName[0] != 0)
				strcpy_s(entry.name, entry.undFullName);
			if (entry.lineFileName[0] == 0) {
				strcpy_s(entry.lineFileName, "?");
				if (entry.moduleName[0] == 0)
					strcpy_s(entry.moduleName, "?");
				_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%p (%s): %s: %s\n", (LPVOID)entry.offset, entry.moduleName, entry.lineFileName, entry.name);
			} else
				_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%p (%s): %s (%d): %s\n", (LPVOID)entry.offset, entry.moduleName, entry.lineFileName, entry.lineNumber, entry.name);
			OnOutput(buffer);
		}
	}

public:
	MyStackWalker(std::vector<std::string>& arr) : StackWalker(), _arr(arr) {}
};

static const wchar_t * GetFriendlyExceptionCode(DWORD code) {
	switch (code) {
		case CXX_EXCEPTION_MAGIC:
			return L"CxxException";
		case EXCEPTION_ACCESS_VIOLATION:
			return L"AccessViolation";
		case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
			return L"ArrayBoundsExceeded";
		case EXCEPTION_BREAKPOINT:
			return L"Breakpoint";
		case EXCEPTION_DATATYPE_MISALIGNMENT:
			return L"DatatypeMisalignment";
		case EXCEPTION_FLT_DENORMAL_OPERAND:
			return L"FltDenormalOperand";
		case EXCEPTION_FLT_DIVIDE_BY_ZERO:
			return L"FltDivideByZero";
		case EXCEPTION_FLT_INEXACT_RESULT:
			return L"FltInexactResult";
		case EXCEPTION_FLT_INVALID_OPERATION:
			return L"FltInvalidOperation";
		case EXCEPTION_FLT_OVERFLOW:
			return L"FltOverflow";
		case EXCEPTION_FLT_STACK_CHECK:
			return L"FltStackCheck";
		case EXCEPTION_FLT_UNDERFLOW:
			return L"FltUnderflow";
		case EXCEPTION_ILLEGAL_INSTRUCTION:
			return L"IllegalInstruction";
		case EXCEPTION_IN_PAGE_ERROR:
			return L"InPageError";
		case EXCEPTION_INT_DIVIDE_BY_ZERO:
			return L"IntDivideByZero";
		case EXCEPTION_INT_OVERFLOW:
			return L"IntOverflow";
		case EXCEPTION_INVALID_DISPOSITION:
			return L"InvalidDisposition";
		case EXCEPTION_NONCONTINUABLE_EXCEPTION:
			return L"NoncontinuableException";
		case EXCEPTION_PRIV_INSTRUCTION:
			return L"PrivInstruction";
		case EXCEPTION_SINGLE_STEP:
			return L"SingleStep";
		case EXCEPTION_STACK_OVERFLOW:
			return L"StackOverflow";
	}
	return 0;
}

static void GenerateErrorMessage(EXCEPTION_POINTERS* e, std::wostringstream& msg) {
	const wchar_t* code = GetFriendlyExceptionCode(e->ExceptionRecord->ExceptionCode);
	if (code) {
		msg << code;
	} else {
		msg << "0x" << std::uppercase << std::setfill(L'0') << std::setw(2 * sizeof(DWORD)) << std::hex << e->ExceptionRecord->ExceptionCode;
	}

	if (e->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && e->ExceptionRecord->NumberParameters == 2) {
		ULONG_PTR flag = e->ExceptionRecord->ExceptionInformation[0];
		ULONG_PTR ptr = e->ExceptionRecord->ExceptionInformation[1];
		msg << "(";
		switch (flag) {
			case 0: msg << "Read"; break;
			case 1: msg << "Write"; break;
			case 8: msg << "Execute"; break;
			default:
				msg << flag;
		}
		msg << "," << "0x" << std::uppercase << std::setfill(L'0') << std::setw(2 * sizeof(ULONG_PTR)) << std::hex << ptr << ")";

	} else if (e->ExceptionRecord->ExceptionCode == CXX_EXCEPTION_MAGIC &&
#ifdef __x86_64
		e->ExceptionRecord->NumberParameters == 4
#else
		e->ExceptionRecord->NumberParameters == 3
#endif
		) { // https://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273/
		msg << "(";

		ULONG_PTR offset = 0;
#ifdef __x86_64
		offset = e->ExceptionRecord->ExceptionInformation[3];
#endif

		CXX_EXCEPTION_DETAIL *detail = (CXX_EXCEPTION_DETAIL*)e->ExceptionRecord->ExceptionInformation[2];
		CXX_EXCEPTION_CATCHABLE_TYPES *types = (CXX_EXCEPTION_CATCHABLE_TYPES *)(offset + detail->catchableTypesPtr);
		CXX_EXCEPTION_CATCHABLE_TYPE *type = (CXX_EXCEPTION_CATCHABLE_TYPE *)(offset + (&types->catchableTypeHead)[0]);
		std::type_info* info = (std::type_info*) (offset + type->typeInfo);
		std::string className = info->name();

		msg << "0x" << std::uppercase << std::setfill(L'0') << std::setw(2 * sizeof(ULONG_PTR)) << std::hex << (LPVOID)e->ExceptionRecord->ExceptionInformation[1];
		msg << ", " << ANSIToUtf16(className) << ")";

		if (className.compare(typeid(class std::runtime_error).name()) == 0) {
			std::runtime_error* err = (std::runtime_error*)e->ExceptionRecord->ExceptionInformation[1];
			msg << L": " << Utf8ToUtf16(err->what());
		}
	} else if (e->ExceptionRecord->NumberParameters > 0) {
		msg << "(";
		for (DWORD i = 0; i < e->ExceptionRecord->NumberParameters; i++) {
			ULONG_PTR value = e->ExceptionRecord->ExceptionInformation[i];
			if (i != 0) msg << ",";
			msg << "0x" << std::uppercase << std::setfill(L'0') << std::setw(2 * sizeof(ULONG_PTR)) << std::hex << value;
		}
		msg << ")";
	}
}

static LONG CALLBACK MyUnhandledExceptionFilter(EXCEPTION_POINTERS* e) {
	std::vector<std::string> stack;
	MyStackWalker sw(stack);
	sw.ShowCallstack(GetCurrentThread(), e->ContextRecord);

	std::wostringstream msg;
	GenerateErrorMessage(e, msg);
	msg << L"\n\n" << ANSIToUtf16(join(stack, ""));

	std::wstring message = msg.str();

	int button = IDCANCEL;
	HRESULT result = TaskDialog(NULL, NULL, L"NekoScript", L"Unhandled Native Exception", message.c_str(), TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON, TD_ERROR_ICON, &button);
	if (result != S_OK) {
		button = MessageBoxW(NULL, message.c_str(), L"NekoScript: Unhandled Native Exception", MB_ICONERROR | MB_RETRYCANCEL | MB_DEFBUTTON2);
	}

	if (button == IDRETRY) {
		return EXCEPTION_CONTINUE_SEARCH;
	}
	return EXCEPTION_EXECUTE_HANDLER;
}

void InstallExceptionHandler() {
	SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
}<commit_msg>Add an header file to pass the compile on VS2015 platform(v140)<commit_after>#include "stdafx.h"
#include "ExceptionHandler.h"
#include "Helper.h"
#include <StackWalker\StackWalker.h>
#include <iterator>

// https://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273/
#define CXX_EXCEPTION_MAGIC 0xE06D7363
struct CXX_EXCEPTION_DETAIL {
	DWORD _0;
	DWORD _1;
	DWORD _2;
	DWORD catchableTypesPtr;
};

struct CXX_EXCEPTION_CATCHABLE_TYPES {
	DWORD count;
	DWORD catchableTypeHead;
};

struct CXX_EXCEPTION_CATCHABLE_TYPE {
	DWORD _0;
	DWORD typeInfo;
	DWORD _2;
	DWORD _3;
	DWORD _4;
	DWORD _5;
	DWORD _6;
};

static std::string join(const std::vector<std::string>& vec, const char* delim) {
	std::stringstream res;
	copy(vec.begin(), vec.end(), std::ostream_iterator<std::string>(res, delim));
	return res.str();
}

class MyStackWalker : public StackWalker {
protected:
	std::vector<std::string>& _arr;

	virtual void OnOutput(LPCSTR szText) {
		_arr.push_back(szText);
		StackWalker::OnOutput(szText);
	}

	virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName) {}
	virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion) {}
	virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) {}
	virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) {
		CHAR buffer[STACKWALK_MAX_NAMELEN];
		if ((eType != lastEntry) && (entry.offset != 0)) {
			if (entry.name[0] == 0)
				strcpy_s(entry.name, "?");
			if (entry.undName[0] != 0)
				strcpy_s(entry.name, entry.undName);
			if (entry.undFullName[0] != 0)
				strcpy_s(entry.name, entry.undFullName);
			if (entry.lineFileName[0] == 0) {
				strcpy_s(entry.lineFileName, "?");
				if (entry.moduleName[0] == 0)
					strcpy_s(entry.moduleName, "?");
				_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%p (%s): %s: %s\n", (LPVOID)entry.offset, entry.moduleName, entry.lineFileName, entry.name);
			} else
				_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%p (%s): %s (%d): %s\n", (LPVOID)entry.offset, entry.moduleName, entry.lineFileName, entry.lineNumber, entry.name);
			OnOutput(buffer);
		}
	}

public:
	MyStackWalker(std::vector<std::string>& arr) : StackWalker(), _arr(arr) {}
};

static const wchar_t * GetFriendlyExceptionCode(DWORD code) {
	switch (code) {
		case CXX_EXCEPTION_MAGIC:
			return L"CxxException";
		case EXCEPTION_ACCESS_VIOLATION:
			return L"AccessViolation";
		case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
			return L"ArrayBoundsExceeded";
		case EXCEPTION_BREAKPOINT:
			return L"Breakpoint";
		case EXCEPTION_DATATYPE_MISALIGNMENT:
			return L"DatatypeMisalignment";
		case EXCEPTION_FLT_DENORMAL_OPERAND:
			return L"FltDenormalOperand";
		case EXCEPTION_FLT_DIVIDE_BY_ZERO:
			return L"FltDivideByZero";
		case EXCEPTION_FLT_INEXACT_RESULT:
			return L"FltInexactResult";
		case EXCEPTION_FLT_INVALID_OPERATION:
			return L"FltInvalidOperation";
		case EXCEPTION_FLT_OVERFLOW:
			return L"FltOverflow";
		case EXCEPTION_FLT_STACK_CHECK:
			return L"FltStackCheck";
		case EXCEPTION_FLT_UNDERFLOW:
			return L"FltUnderflow";
		case EXCEPTION_ILLEGAL_INSTRUCTION:
			return L"IllegalInstruction";
		case EXCEPTION_IN_PAGE_ERROR:
			return L"InPageError";
		case EXCEPTION_INT_DIVIDE_BY_ZERO:
			return L"IntDivideByZero";
		case EXCEPTION_INT_OVERFLOW:
			return L"IntOverflow";
		case EXCEPTION_INVALID_DISPOSITION:
			return L"InvalidDisposition";
		case EXCEPTION_NONCONTINUABLE_EXCEPTION:
			return L"NoncontinuableException";
		case EXCEPTION_PRIV_INSTRUCTION:
			return L"PrivInstruction";
		case EXCEPTION_SINGLE_STEP:
			return L"SingleStep";
		case EXCEPTION_STACK_OVERFLOW:
			return L"StackOverflow";
	}
	return 0;
}

static void GenerateErrorMessage(EXCEPTION_POINTERS* e, std::wostringstream& msg) {
	const wchar_t* code = GetFriendlyExceptionCode(e->ExceptionRecord->ExceptionCode);
	if (code) {
		msg << code;
	} else {
		msg << "0x" << std::uppercase << std::setfill(L'0') << std::setw(2 * sizeof(DWORD)) << std::hex << e->ExceptionRecord->ExceptionCode;
	}

	if (e->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && e->ExceptionRecord->NumberParameters == 2) {
		ULONG_PTR flag = e->ExceptionRecord->ExceptionInformation[0];
		ULONG_PTR ptr = e->ExceptionRecord->ExceptionInformation[1];
		msg << "(";
		switch (flag) {
			case 0: msg << "Read"; break;
			case 1: msg << "Write"; break;
			case 8: msg << "Execute"; break;
			default:
				msg << flag;
		}
		msg << "," << "0x" << std::uppercase << std::setfill(L'0') << std::setw(2 * sizeof(ULONG_PTR)) << std::hex << ptr << ")";

	} else if (e->ExceptionRecord->ExceptionCode == CXX_EXCEPTION_MAGIC &&
#ifdef __x86_64
		e->ExceptionRecord->NumberParameters == 4
#else
		e->ExceptionRecord->NumberParameters == 3
#endif
		) { // https://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273/
		msg << "(";

		ULONG_PTR offset = 0;
#ifdef __x86_64
		offset = e->ExceptionRecord->ExceptionInformation[3];
#endif

		CXX_EXCEPTION_DETAIL *detail = (CXX_EXCEPTION_DETAIL*)e->ExceptionRecord->ExceptionInformation[2];
		CXX_EXCEPTION_CATCHABLE_TYPES *types = (CXX_EXCEPTION_CATCHABLE_TYPES *)(offset + detail->catchableTypesPtr);
		CXX_EXCEPTION_CATCHABLE_TYPE *type = (CXX_EXCEPTION_CATCHABLE_TYPE *)(offset + (&types->catchableTypeHead)[0]);
		std::type_info* info = (std::type_info*) (offset + type->typeInfo);
		std::string className = info->name();

		msg << "0x" << std::uppercase << std::setfill(L'0') << std::setw(2 * sizeof(ULONG_PTR)) << std::hex << (LPVOID)e->ExceptionRecord->ExceptionInformation[1];
		msg << ", " << ANSIToUtf16(className) << ")";

		if (className.compare(typeid(class std::runtime_error).name()) == 0) {
			std::runtime_error* err = (std::runtime_error*)e->ExceptionRecord->ExceptionInformation[1];
			msg << L": " << Utf8ToUtf16(err->what());
		}
	} else if (e->ExceptionRecord->NumberParameters > 0) {
		msg << "(";
		for (DWORD i = 0; i < e->ExceptionRecord->NumberParameters; i++) {
			ULONG_PTR value = e->ExceptionRecord->ExceptionInformation[i];
			if (i != 0) msg << ",";
			msg << "0x" << std::uppercase << std::setfill(L'0') << std::setw(2 * sizeof(ULONG_PTR)) << std::hex << value;
		}
		msg << ")";
	}
}

static LONG CALLBACK MyUnhandledExceptionFilter(EXCEPTION_POINTERS* e) {
	std::vector<std::string> stack;
	MyStackWalker sw(stack);
	sw.ShowCallstack(GetCurrentThread(), e->ContextRecord);

	std::wostringstream msg;
	GenerateErrorMessage(e, msg);
	msg << L"\n\n" << ANSIToUtf16(join(stack, ""));

	std::wstring message = msg.str();

	int button = IDCANCEL;
	HRESULT result = TaskDialog(NULL, NULL, L"NekoScript", L"Unhandled Native Exception", message.c_str(), TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON, TD_ERROR_ICON, &button);
	if (result != S_OK) {
		button = MessageBoxW(NULL, message.c_str(), L"NekoScript: Unhandled Native Exception", MB_ICONERROR | MB_RETRYCANCEL | MB_DEFBUTTON2);
	}

	if (button == IDRETRY) {
		return EXCEPTION_CONTINUE_SEARCH;
	}
	return EXCEPTION_EXECUTE_HANDLER;
}

void InstallExceptionHandler() {
	SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
}<|endoftext|>